diff --git a/app/Livewire/Tasks/Index.php b/app/Livewire/Tasks/Index.php
index 9e7bee0..969f837 100644
--- a/app/Livewire/Tasks/Index.php
+++ b/app/Livewire/Tasks/Index.php
@@ -53,16 +53,10 @@ class Index extends Component
$this->startedKeys[] = $key;
}
- // Start = open the Runner modal (actual quiz/exercise) — NOT the Detail
- // modal which is reserved for the info-icon button. Use $this->js() to
- // emit a browser-side Livewire dispatch — server-side $this->dispatch()
- // with ->to(...) in Livewire 4 + wire-elements drops named payload args.
- $payload = json_encode([
- 'component' => 'tasks.modals.runner',
- 'arguments' => ['task' => $task],
- ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
-
- $this->js("Livewire.dispatch('openModal', {$payload})");
+ // Start navigates to the dedicated Run page (subject-specific quiz)
+ // instead of opening a modal — same data but per-subject questions
+ // and a structured URL.
+ $this->redirectRoute('tasks.run', ['key' => $key], navigate: true);
}
public function repeatTask(string $key): void
@@ -73,15 +67,7 @@ class Index extends Component
#[Computed]
public function tasks(): array
{
- return [
- ['key' => 't1', 'title' => 'Multiplikation bis 100', 'meta' => 'Mathematik · 12 Aufgaben · 15 Min.', 'icon' => '∑', 'color' => 'primary', 'points' => 120, 'status' => 'open', 'due' => 'Heute'],
- ['key' => 't2', 'title' => 'Wortarten: Nomen & Verben', 'meta' => 'Deutsch · Lese-Aufgabe · 10 Min.', 'icon' => 'A', 'color' => 'rose', 'points' => 80, 'status' => 'open', 'due' => 'Heute'],
- ['key' => 't3', 'title' => 'Vocab Review: Animals', 'meta' => 'Englisch · Karteikarten · 8 Min.', 'icon' => 'E', 'color' => 'violet', 'points' => 60, 'status' => 'open', 'due' => 'Heute'],
- ['key' => 't4', 'title' => 'Kreislauf des Wassers', 'meta' => 'Sachkunde · Video + Quiz · 12 Min.', 'icon' => '🌱', 'color' => 'green', 'points' => 100, 'status' => 'open', 'due' => 'Heute'],
- ['key' => 't5', 'title' => 'Geometrie: Flächen', 'meta' => 'Mathematik · Übung · 20 Min.', 'icon' => '∑', 'color' => 'primary', 'points' => 150, 'status' => 'open', 'due' => 'Morgen'],
- ['key' => 't6', 'title' => 'Gedicht lernen', 'meta' => 'Deutsch · Lese-Aufgabe · 15 Min.', 'icon' => 'A', 'color' => 'rose', 'points' => 90, 'status' => 'done', 'due' => 'Gestern'],
- ['key' => 't7', 'title' => 'Past Simple Übung', 'meta' => 'Englisch · Übung · 10 Min.', 'icon' => 'E', 'color' => 'violet', 'points' => 70, 'status' => 'done', 'due' => 'Gestern'],
- ];
+ return config('tasks', []);
}
#[Computed]
diff --git a/app/Livewire/Tasks/Run.php b/app/Livewire/Tasks/Run.php
new file mode 100644
index 0000000..1847cf4
--- /dev/null
+++ b/app/Livewire/Tasks/Run.php
@@ -0,0 +1,90 @@
+ 'tasks'])]
+#[Lazy]
+class Run extends Component
+{
+ public string $key = '';
+ public array $task = [];
+ public array $questions = [];
+ public int $currentStep = 1;
+ public int $totalSteps = 0;
+ public bool $finished = false;
+ public int $correctAnswers = 0;
+ public ?int $lastAnswerIndex = null;
+ public ?bool $lastAnswerCorrect = null;
+
+ public function mount(string $key): void
+ {
+ $task = $this->findTask($key);
+ abort_if($task === null, 404, "Task '{$key}' not found.");
+
+ $this->key = $key;
+ $this->task = $task;
+ $this->questions = config("task-questions.{$task['color']}", []);
+ $this->totalSteps = count($this->questions);
+
+ abort_if($this->totalSteps === 0, 404, "No questions configured for color '{$task['color']}'.");
+ }
+
+ public static function placeholder(array $params = []): View
+ {
+ return view('livewire.tasks.run-placeholder');
+ }
+
+ public function currentQuestion(): array
+ {
+ return $this->questions[$this->currentStep - 1] ?? $this->questions[0];
+ }
+
+ public function submitAnswer(int $optionIndex): void
+ {
+ $q = $this->currentQuestion();
+ $isCorrect = $optionIndex === ($q['correct'] ?? -1);
+
+ $this->lastAnswerIndex = $optionIndex;
+ $this->lastAnswerCorrect = $isCorrect;
+
+ if ($isCorrect) {
+ $this->correctAnswers++;
+ }
+ }
+
+ public function nextStep(): void
+ {
+ if ($this->currentStep >= $this->totalSteps) {
+ $this->finished = true;
+ return;
+ }
+ $this->currentStep++;
+ $this->lastAnswerIndex = null;
+ $this->lastAnswerCorrect = null;
+ }
+
+ public function restart(): void
+ {
+ $this->currentStep = 1;
+ $this->finished = false;
+ $this->correctAnswers = 0;
+ $this->lastAnswerIndex = null;
+ $this->lastAnswerCorrect = null;
+ }
+
+ /** Locate task by key — placeholder data (later DB-backed). */
+ protected function findTask(string $key): ?array
+ {
+ return collect(config('tasks', []))->firstWhere('key', $key);
+ }
+
+ public function render(): View
+ {
+ return view('livewire.tasks.run');
+ }
+}
diff --git a/config/task-questions.php b/config/task-questions.php
new file mode 100644
index 0000000..38bc770
--- /dev/null
+++ b/config/task-questions.php
@@ -0,0 +1,63 @@
+ of 4 answer choices
+ * - correct: int index (0..3) of correct option
+ *
+ * Subjects:
+ * primary -> Mathematik
+ * rose -> Deutsch
+ * violet -> Englisch
+ * green -> Sachkunde
+ * amber -> Musik / Sport / Philosophie
+ *
+ * Future: replace with DB-backed Question + Answer models, scoped by tenant.
+ */
+
+return [
+
+ 'primary' => [
+ ['question' => 'Was ergibt 12 × 8?', 'options' => ['64', '88', '96', '102'], 'correct' => 2],
+ ['question' => 'Was ergibt 15 × 7?', 'options' => ['95', '105', '115', '125'], 'correct' => 1],
+ ['question' => 'Was ergibt 9 × 11?', 'options' => ['91', '99', '108', '111'], 'correct' => 1],
+ ['question' => 'Was ergibt 14 × 6?', 'options' => ['72', '78', '84', '88'], 'correct' => 2],
+ ['question' => 'Was ergibt 144 ÷ 12?', 'options' => ['10', '11', '12', '13'], 'correct' => 2],
+ ],
+
+ 'rose' => [
+ ['question' => 'Welches Wort ist ein Nomen?', 'options' => ['laufen', 'schnell', 'Hund', 'rot'], 'correct' => 2],
+ ['question' => 'Welches Wort ist ein Verb?', 'options' => ['Tisch', 'springen', 'grün', 'Lehrer'], 'correct' => 1],
+ ['question' => 'Welches Wort ist ein Adjektiv?', 'options' => ['Schule', 'lesen', 'glücklich', 'gehen'], 'correct' => 2],
+ ['question' => 'Wie wird "Buch" im Plural geschrieben?', 'options' => ['Buchs', 'Buchen', 'Bücher', 'Buches'], 'correct' => 2],
+ ['question' => 'Welcher Artikel passt zu "Sonne"?', 'options' => ['der', 'die', 'das', 'den'], 'correct' => 1],
+ ],
+
+ 'violet' => [
+ ['question' => 'What is the past tense of "go"?', 'options' => ['goed', 'goes', 'gone', 'went'], 'correct' => 3],
+ ['question' => 'Translate "Katze" to English:', 'options' => ['dog', 'cat', 'cow', 'mouse'], 'correct' => 1],
+ ['question' => 'Which is the correct plural of "child"?', 'options' => ['childs', 'childes', 'children', 'childer'], 'correct' => 2],
+ ['question' => 'Pick the correct: "She ___ a book."', 'options' => ['read', 'reads', 'reading', 'reader'], 'correct' => 1],
+ ['question' => 'Translate "Apfel" to English:', 'options' => ['apple', 'orange', 'banana', 'lemon'], 'correct' => 0],
+ ],
+
+ 'green' => [
+ ['question' => 'Was passiert beim Verdampfen von Wasser?', 'options' => ['Es wird flüssig', 'Es wird zu Dampf', 'Es gefriert', 'Es verschwindet'], 'correct' => 1],
+ ['question' => 'Wie viele Beine hat eine Spinne?', 'options' => ['6', '8', '10', '12'], 'correct' => 1],
+ ['question' => 'Welches Tier ist KEIN Säugetier?', 'options' => ['Hund', 'Pferd', 'Forelle', 'Katze'], 'correct' => 2],
+ ['question' => 'In welcher Jahreszeit fallen die Blätter?', 'options' => ['Frühling', 'Sommer', 'Herbst', 'Winter'], 'correct' => 2],
+ ['question' => 'Welcher Planet ist der Sonne am nächsten?', 'options' => ['Erde', 'Mars', 'Merkur', 'Venus'], 'correct' => 2],
+ ],
+
+ 'amber' => [
+ ['question' => 'Wie viele Linien hat ein Notensystem?', 'options' => ['3', '4', '5', '6'], 'correct' => 2],
+ ['question' => 'Welche Note ist die Längste?', 'options' => ['Achtel', 'Viertel', 'Halbe', 'Ganze'], 'correct' => 3],
+ ['question' => 'Was bedeutet "forte" in der Musik?', 'options' => ['leise', 'laut', 'schnell', 'langsam'], 'correct' => 1],
+ ['question' => 'Wie viele Saiten hat eine normale Gitarre?', 'options' => ['4', '5', '6', '7'], 'correct' => 2],
+ ['question' => 'Was ist ein Komponist?', 'options' => ['Lehrer', 'Musik-Erfinder', 'Sänger', 'Instrumentenbauer'], 'correct' => 1],
+ ],
+
+];
diff --git a/config/tasks.php b/config/tasks.php
new file mode 100644
index 0000000..c8d1004
--- /dev/null
+++ b/config/tasks.php
@@ -0,0 +1,21 @@
+ 't1', 'title' => 'Multiplikation bis 100', 'meta' => 'Mathematik · 12 Aufgaben · 15 Min.', 'icon' => '∑', 'color' => 'primary', 'points' => 120, 'status' => 'open', 'due' => 'Heute'],
+ ['key' => 't2', 'title' => 'Wortarten: Nomen & Verben', 'meta' => 'Deutsch · Lese-Aufgabe · 10 Min.', 'icon' => 'A', 'color' => 'rose', 'points' => 80, 'status' => 'open', 'due' => 'Heute'],
+ ['key' => 't3', 'title' => 'Vocab Review: Animals', 'meta' => 'Englisch · Karteikarten · 8 Min.', 'icon' => 'E', 'color' => 'violet', 'points' => 60, 'status' => 'open', 'due' => 'Heute'],
+ ['key' => 't4', 'title' => 'Kreislauf des Wassers', 'meta' => 'Sachkunde · Video + Quiz · 12 Min.', 'icon' => '🌱', 'color' => 'green', 'points' => 100, 'status' => 'open', 'due' => 'Heute'],
+ ['key' => 't5', 'title' => 'Geometrie: Flächen', 'meta' => 'Mathematik · Übung · 20 Min.', 'icon' => '∑', 'color' => 'primary', 'points' => 150, 'status' => 'open', 'due' => 'Morgen'],
+ ['key' => 't6', 'title' => 'Gedicht lernen', 'meta' => 'Deutsch · Lese-Aufgabe · 15 Min.', 'icon' => 'A', 'color' => 'rose', 'points' => 90, 'status' => 'done', 'due' => 'Gestern'],
+ ['key' => 't7', 'title' => 'Past Simple Übung', 'meta' => 'Englisch · Übung · 10 Min.', 'icon' => 'E', 'color' => 'violet', 'points' => 70, 'status' => 'done', 'due' => 'Gestern'],
+];
diff --git a/resources/views/livewire/tasks/run-placeholder.blade.php b/resources/views/livewire/tasks/run-placeholder.blade.php
new file mode 100644
index 0000000..17f87a9
--- /dev/null
+++ b/resources/views/livewire/tasks/run-placeholder.blade.php
@@ -0,0 +1,22 @@
+
+ {{-- Top nav skeleton --}}
+
+
+ {{-- Task header card --}}
+
+
+ {{-- Question card --}}
+
+
+
+
+
+ @for($i = 0; $i < 4; $i++)
+
+ @endfor
+
+
+
diff --git a/resources/views/livewire/tasks/run.blade.php b/resources/views/livewire/tasks/run.blade.php
new file mode 100644
index 0000000..953f5e0
--- /dev/null
+++ b/resources/views/livewire/tasks/run.blade.php
@@ -0,0 +1,154 @@
+@php
+ $colorMap = [
+ 'primary' => ['bg' => 'bg-primary-soft', 'ink' => 'text-primary-ink', 'bar' => 'bg-primary-ink', 'ring' => 'focus-visible:ring-primary'],
+ 'rose' => ['bg' => 'bg-rose-soft', 'ink' => 'text-rose-ink', 'bar' => 'bg-rose-ink', 'ring' => 'focus-visible:ring-rose-ink'],
+ 'violet' => ['bg' => 'bg-violet-soft', 'ink' => 'text-violet-ink', 'bar' => 'bg-violet-ink', 'ring' => 'focus-visible:ring-violet-ink'],
+ 'green' => ['bg' => 'bg-green-soft', 'ink' => 'text-green-ink', 'bar' => 'bg-green-ink', 'ring' => 'focus-visible:ring-green-ink'],
+ 'amber' => ['bg' => 'bg-amber-soft', 'ink' => 'text-amber-ink', 'bar' => 'bg-amber-ink', 'ring' => 'focus-visible:ring-amber-ink'],
+ ];
+ $c = $colorMap[$task['color']] ?? $colorMap['primary'];
+ $progressPercent = $totalSteps > 0 ? (int) round(($currentStep / $totalSteps) * 100) : 0;
+@endphp
+
+
+
+ {{-- Breadcrumb / back nav --}}
+
+
+ {{-- Task header --}}
+
+
+ {{ $task['icon'] }}
+
+
+
{{ $task['meta'] }}
+
{{ $task['title'] }}
+
+
+
Belohnung
+
+{{ (int) $task['points'] }} Pkt.
+
+
+
+ @if(! $finished)
+ {{-- Progress --}}
+
+
+
+ Frage {{ $currentStep }} / {{ $totalSteps }}
+
+
+ {{ $correctAnswers }} richtig
+
+
+
+
+
+ {{-- Question --}}
+ @php $q = $this->currentQuestion(); @endphp
+
+
Aufgabe
+
+ {{ $q['question'] }}
+
+
+
+ @foreach($q['options'] as $i => $opt)
+ @php
+ $isLast = $lastAnswerIndex === $i;
+ $cls = 'group px-5 py-5 rounded-lg border-2 text-ink-1 font-semibold text-base text-left transition-all cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ' . $c['ring'];
+ $cls .= $lastAnswerIndex === null
+ ? ' bg-bg-soft border-line hover:border-primary hover:bg-primary-soft'
+ : ($isLast
+ ? ($lastAnswerCorrect ? ' bg-green-soft border-green-ink' : ' bg-rose-soft border-rose-ink')
+ : ' bg-bg-soft border-line opacity-60');
+ @endphp
+
+ @endforeach
+
+
+ @if($lastAnswerIndex !== null)
+
+
+ @if($currentStep < $totalSteps)
+ Nächste Frage
+
+ @else
+ Aufgabe abschließen
+
+ @endif
+
+
+ @endif
+
+ @else
+ @php
+ $passed = $correctAnswers >= (int) ceil($totalSteps / 2);
+ $percent = $totalSteps > 0 ? (int) round(($correctAnswers / $totalSteps) * 100) : 0;
+ $pointsEarned = $passed ? (int) $task['points'] : 0;
+ @endphp
+
+
+ @if($passed)
+
+ @else
+
+ @endif
+
+
+ @if($passed) Super gemacht! @else Fast geschafft @endif
+
+
+ Du hast {{ $correctAnswers }}
+ von {{ $totalSteps }} Aufgaben richtig.
+
+
{{ $percent }} % Erfolgsquote
+
+ @if($pointsEarned > 0)
+
+
+ +{{ $pointsEarned }} Pkt.
+ verdient
+
+ @endif
+
+
+
+
+ Nochmal
+
+
+
+ Fertig — zurück
+
+
+
+ @endif
+
+
diff --git a/routes/web.php b/routes/web.php
index 4f943f8..d6202a3 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -13,6 +13,9 @@ Route::middleware(['auth', 'license'])->group(function () {
Route::get('/dashboard', \App\Livewire\Dashboard\Index::class)->name('dashboard');
Route::get('/subjects', \App\Livewire\Subjects\Index::class)->name('subjects');
Route::get('/tasks', \App\Livewire\Tasks\Index::class)->name('tasks');
+ Route::get('/tasks/{key}/run', \App\Livewire\Tasks\Run::class)
+ ->whereAlphaNumeric('key')
+ ->name('tasks.run');
Route::get('/photo', \App\Livewire\PhotoHelp\Index::class)->name('photo');
Route::get('/rewards', \App\Livewire\Rewards\Index::class)->name('rewards');
Route::get('/progress', \App\Livewire\Progress\Index::class)->name('progress');
diff --git a/tests/Feature/Frontend/TaskQuestionBankTest.php b/tests/Feature/Frontend/TaskQuestionBankTest.php
new file mode 100644
index 0000000..1539822
--- /dev/null
+++ b/tests/Feature/Frontend/TaskQuestionBankTest.php
@@ -0,0 +1,31 @@
+toBeTrue();
+});
+
+it('config has buckets for all 5 subject colors', function () {
+ $bank = config('task-questions');
+ foreach (['primary', 'rose', 'violet', 'green', 'amber'] as $color) {
+ expect(array_key_exists($color, $bank))->toBeTrue("Question bank missing color '{$color}'");
+ expect($bank[$color])->toBeArray()->not->toBeEmpty();
+ }
+});
+
+it('each question has required keys (question, options, correct)', function () {
+ $bank = config('task-questions');
+ foreach ($bank as $color => $questions) {
+ foreach ($questions as $i => $q) {
+ expect($q)->toHaveKeys(['question', 'options', 'correct']);
+ expect($q['options'])->toHaveCount(4);
+ expect($q['correct'])->toBeInt()->toBeGreaterThanOrEqual(0)->toBeLessThan(4);
+ }
+ }
+});
+
+it('Math (primary) questions differ from German (rose)', function () {
+ $bank = config('task-questions');
+ $mathFirst = $bank['primary'][0]['question'];
+ $germanFirst = $bank['rose'][0]['question'];
+ expect($mathFirst)->not->toBe($germanFirst);
+});
diff --git a/tests/Feature/Livewire/TasksRunPageTest.php b/tests/Feature/Livewire/TasksRunPageTest.php
new file mode 100644
index 0000000..a949ade
--- /dev/null
+++ b/tests/Feature/Livewire/TasksRunPageTest.php
@@ -0,0 +1,130 @@
+seed(\Database\Seeders\RolesSeeder::class);
+
+ $tenant = Tenant::firstOrCreate(['name' => 'Test Schule'], ['type' => 'school', 'active' => true]);
+ $this->user = User::withoutGlobalScopes()->create([
+ 'name' => 'Run Tester', 'email' => 'run@test.example',
+ 'tenant_id' => $tenant->id, 'password' => bcrypt('password'),
+ ]);
+
+ $role = Role::where('name', 'child')->first();
+ DB::table('role_user')->insert(['user_id' => $this->user->id, 'role_id' => $role->id, 'created_at' => now(), 'updated_at' => now()]);
+
+ $license = License::withoutGlobalScopes()->create([
+ 'tenant_id' => $tenant->id, 'type' => 'school_flat',
+ 'expires_at' => now()->addYear(), 'active' => true,
+ ]);
+ DB::table('license_assignments')->insert(['license_id' => $license->id, 'user_id' => $this->user->id, 'created_at' => now(), 'updated_at' => now()]);
+
+ $this->actingAs($this->user);
+});
+
+it('route tasks.run registered with key param', function () {
+ expect(\Illuminate\Support\Facades\Route::has('tasks.run'))->toBeTrue();
+});
+
+it('GET /tasks/t1/run returns 200 for authenticated user', function () {
+ $this->get(route('tasks.run', ['key' => 't1']))->assertOk();
+});
+
+it('GET /tasks/{key}/run with unknown key returns 404', function () {
+ $this->get(route('tasks.run', ['key' => 'nonexistent']))->assertNotFound();
+});
+
+it('Run mount: math task (t1, primary) loads MATH questions', function () {
+ $component = Livewire::test(Run::class, ['key' => 't1']);
+ $component->assertSet('totalSteps', 5); // primary bank has 5 questions
+ $component->assertSee('Multiplikation bis 100');
+ $component->assertSee('Was ergibt 12 × 8?');
+ $component->assertDontSee('Welches Wort ist ein Nomen?');
+});
+
+it('Run mount: deutsch task (t2, rose) loads GERMAN questions', function () {
+ $component = Livewire::test(Run::class, ['key' => 't2']);
+ $component->assertSee('Welches Wort ist ein Nomen?');
+ $component->assertDontSee('Was ergibt 12 × 8?');
+});
+
+it('Run mount: english task (t3, violet) loads ENGLISH questions', function () {
+ $component = Livewire::test(Run::class, ['key' => 't3']);
+ $component->assertSee('past tense of "go"');
+ $component->assertDontSee('Welches Wort ist ein Nomen?');
+});
+
+it('Run mount: sachkunde task (t4, green) loads NATURE questions', function () {
+ $component = Livewire::test(Run::class, ['key' => 't4']);
+ $component->assertSee('Verdampfen von Wasser');
+});
+
+it('submitAnswer marks correct/incorrect + correctAnswers counter', function () {
+ Livewire::test(Run::class, ['key' => 't1'])
+ ->call('submitAnswer', 2) // primary q1: correct = 2 (96)
+ ->assertSet('lastAnswerIndex', 2)
+ ->assertSet('lastAnswerCorrect', true)
+ ->assertSet('correctAnswers', 1);
+});
+
+it('submitAnswer with wrong index sets lastAnswerCorrect=false, no point', function () {
+ Livewire::test(Run::class, ['key' => 't1'])
+ ->call('submitAnswer', 0)
+ ->assertSet('lastAnswerCorrect', false)
+ ->assertSet('correctAnswers', 0);
+});
+
+it('nextStep advances and clears lastAnswer state', function () {
+ Livewire::test(Run::class, ['key' => 't1'])
+ ->call('submitAnswer', 2)
+ ->call('nextStep')
+ ->assertSet('currentStep', 2)
+ ->assertSet('lastAnswerIndex', null)
+ ->assertSet('lastAnswerCorrect', null);
+});
+
+it('finishing last step sets finished=true', function () {
+ $run = Livewire::test(Run::class, ['key' => 't1']);
+ // primary bank has 5 questions
+ foreach (range(1, 5) as $step) {
+ $run->call('submitAnswer', 0)->call('nextStep');
+ }
+ $run->assertSet('finished', true);
+});
+
+it('restart resets all progress', function () {
+ Livewire::test(Run::class, ['key' => 't1'])
+ ->call('submitAnswer', 2)
+ ->call('nextStep')
+ ->call('restart')
+ ->assertSet('currentStep', 1)
+ ->assertSet('correctAnswers', 0)
+ ->assertSet('finished', false)
+ ->assertSet('lastAnswerIndex', null);
+});
+
+it('startTask in Index redirects to tasks.run route (not modal)', function () {
+ Livewire::test(\App\Livewire\Tasks\Index::class)
+ ->call('startTask', 't1')
+ ->assertRedirect(route('tasks.run', ['key' => 't1']));
+});
+
+it('unauthenticated user redirected to login', function () {
+ auth()->logout();
+ $this->get(route('tasks.run', ['key' => 't1']))->assertRedirect(route('login'));
+});
+
+it('Run placeholder view exists with task-run-skeleton testid', function () {
+ $view = Run::placeholder([]);
+ expect($view->getName())->toBe('livewire.tasks.run-placeholder');
+ expect($view->render())->toContain('task-run-skeleton');
+});
diff --git a/tests/Feature/Livewire/TasksStartButtonTest.php b/tests/Feature/Livewire/TasksStartButtonTest.php
index 1ed2362..7abc650 100644
--- a/tests/Feature/Livewire/TasksStartButtonTest.php
+++ b/tests/Feature/Livewire/TasksStartButtonTest.php
@@ -66,18 +66,10 @@ it('startTask action runs and adds key to startedKeys', function () {
->assertSet('startedKeys', ['t1']);
});
-it('startTask emits browser-side Livewire.dispatch via $this->js() targeting RUNNER modal (not detail)', function () {
- $tester = Livewire::test(TasksIndex::class)->call('startTask', 't1');
-
- $effects = $tester->effects ?? [];
- $scripts = $effects['xjs'] ?? $effects['js'] ?? [];
- $allScripts = implode("\n", is_array($scripts) ? array_map(fn($s) => is_array($s) ? json_encode($s) : (string) $s, $scripts) : [(string) $scripts]);
-
- expect($allScripts)->toContain("Livewire.dispatch('openModal'");
- // Start button opens RUNNER, not detail
- expect($allScripts)->toContain('tasks.modals.runner');
- expect($allScripts)->not->toContain('tasks.modals.detail');
- expect($allScripts)->toContain('t1');
+it('startTask redirects to tasks.run page (dedicated quiz, not modal)', function () {
+ Livewire::test(TasksIndex::class)
+ ->call('startTask', 't1')
+ ->assertRedirect(route('tasks.run', ['key' => 't1']));
});
it('Detail (info) button still opens tasks.modals.detail — separate from Start', function () {