create(); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Pocket note', 'original_filename' => 'note.mp3', 'file_path' => 'recordings/note.mp3', 'file_size_bytes' => 1024, 'transcription_status' => 'done', 'transcript' => 'hello world', ]); $this->actingAs($user) ->get(route('recordings.index')) ->assertOk() ->assertSeeLivewire(Index::class) ->assertSee('Pocket note'); } public function test_empty_index_shows_upload_dropzone(): void { $user = User::factory()->create(); $this->actingAs($user) ->get(route('recordings.index')) ->assertOk() ->assertSeeLivewire('upload-recordings') ->assertSee('Drop audio files here or click to browse') ->assertDontSee('No recordings yet.') ->assertDontSee('Upload your first MP3'); } public function test_search_filters_recordings(): void { $user = User::factory()->create(); $this->actingAs($user); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Office chat', 'original_filename' => 'office.mp3', 'file_path' => 'recordings/office.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'done', 'transcript' => "talking about the pocket recorder today\nsecond line stays hidden", ]); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Unrelated', 'original_filename' => 'other.mp3', 'file_path' => 'recordings/other.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'done', 'transcript' => 'nothing useful', ]); Livewire::test(Index::class) ->set('search', 'pocket recorder') ->assertSee('Office chat') ->assertSee('talking about the pocket recorder today') ->assertDontSee('second line stays hidden') ->assertDontSee('Unrelated'); } public function test_queue_pending_dispatches_jobs(): void { Bus::fake(); $user = User::factory()->create(); $this->actingAs($user); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Needs work', 'original_filename' => 'needs.mp3', 'file_path' => 'recordings/needs.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'pending', ]); Livewire::test(Index::class) ->call('queuePending'); Bus::assertDispatched(TranscribeRecording::class, 1); } public function test_index_polls_while_transcriptions_are_active(): void { $user = User::factory()->create(); $this->actingAs($user); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'In progress', 'original_filename' => 'active.mp3', 'file_path' => 'recordings/active.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'processing', 'transcription_progress' => 'Transcribing locally…', 'transcription_percent' => 40, 'transcription_driver' => 'local', 'transcription_started_at' => now(), ]); Livewire::test(Index::class) ->assertSee('wire:poll', false) ->assertSee('Transcribing locally…') ->assertSee('40%') ->assertSeeHtml('bg-amber-400'); } public function test_queued_recordings_do_not_show_fake_percent(): void { $user = User::factory()->create(); $this->actingAs($user); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Waiting in line', 'original_filename' => 'queued.mp3', 'file_path' => 'recordings/queued.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'pending', 'transcription_progress' => 'Queued — waiting to start…', 'transcription_percent' => null, 'transcription_driver' => 'local', 'transcription_started_at' => now(), ]); Livewire::test(Index::class) ->assertSee('Waiting in line') ->assertSee('Queued') ->assertSee('Queued — waiting to start…') ->assertDontSee('5%') ->assertSeeHtml('bg-zinc-400/15') ->assertDontSeeHtml('bg-amber-400'); } public function test_index_does_not_poll_when_all_transcriptions_are_idle(): void { $user = User::factory()->create(); $this->actingAs($user); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Finished', 'original_filename' => 'done.mp3', 'file_path' => 'recordings/done.mp3', 'file_size_bytes' => 100, 'transcription_status' => 'done', 'transcript' => 'all done', ]); Livewire::test(Index::class) ->assertDontSee('wire:poll', false); } public function test_user_can_delete_recording_from_index(): void { Storage::fake('local'); Storage::disk('local')->put('recordings/delete-me.mp3', 'bytes'); $user = User::factory()->create(); $this->actingAs($user); $recording = Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Delete from list', 'original_filename' => 'delete-me.mp3', 'file_path' => 'recordings/delete-me.mp3', 'file_size_bytes' => 5, 'transcription_status' => 'done', ]); Livewire::test(Index::class) ->assertSee('Delete from list') ->call('delete', $recording->id) ->assertDontSee('Delete from list'); $this->assertDatabaseMissing('recordings', ['id' => $recording->id]); Storage::disk('local')->assertMissing('recordings/delete-me.mp3'); } public function test_user_can_delete_all_recordings_from_index(): void { Storage::fake('local'); Storage::disk('local')->put('recordings/one.mp3', 'one'); Storage::disk('local')->put('recordings/two.mp3', 'two'); Storage::disk('local')->put('recordings/other.mp3', 'other'); $user = User::factory()->create(); $other = User::factory()->create(); $this->actingAs($user); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Mine one', 'original_filename' => 'one.mp3', 'file_path' => 'recordings/one.mp3', 'file_size_bytes' => 3, 'transcription_status' => 'done', ]); Recording::query()->create([ 'user_id' => $user->id, 'title' => 'Mine two', 'original_filename' => 'two.mp3', 'file_path' => 'recordings/two.mp3', 'file_size_bytes' => 3, 'transcription_status' => 'pending', ]); Recording::query()->create([ 'user_id' => $other->id, 'title' => 'Someone else', 'original_filename' => 'other.mp3', 'file_path' => 'recordings/other.mp3', 'file_size_bytes' => 5, 'transcription_status' => 'done', ]); Livewire::test(Index::class) ->assertSee('Delete all') ->call('deleteAll') ->assertDontSee('Mine one') ->assertDontSee('Mine two'); $this->assertDatabaseMissing('recordings', ['user_id' => $user->id]); $this->assertDatabaseHas('recordings', ['user_id' => $other->id, 'title' => 'Someone else']); Storage::disk('local')->assertMissing('recordings/one.mp3'); Storage::disk('local')->assertMissing('recordings/two.mp3'); Storage::disk('local')->assertExists('recordings/other.mp3'); } public function test_user_cannot_delete_another_users_recording_from_index(): void { Storage::fake('local'); $owner = User::factory()->create(); $intruder = User::factory()->create(); $recording = Recording::query()->create([ 'user_id' => $owner->id, 'title' => 'Keep me', 'original_filename' => 'keep.mp3', 'file_path' => 'recordings/keep.mp3', 'file_size_bytes' => 10, 'transcription_status' => 'done', ]); $this->actingAs($intruder); try { Livewire::test(Index::class) ->call('delete', $recording->id); $this->fail('Expected deleting another user\'s recording to fail.'); } catch (ModelNotFoundException) { // Owned-query findOrFail hides other users' recordings. } $this->assertDatabaseHas('recordings', ['id' => $recording->id]); } }