Move recordings UI to Livewire pages with Flux components.
Replace controller-driven Blade views with full-page Livewire index/show/create flows so Flux tables, toasts, and uploads work natively.
This commit is contained in:
@@ -52,7 +52,7 @@ class DiskSpaceTest extends TestCase
|
||||
|
||||
$this->get(route('recordings.index'))
|
||||
->assertOk()
|
||||
->assertSee('Disk space')
|
||||
->assertSee('Disk space used', false)
|
||||
->assertSee('free')
|
||||
->assertSee('role="progressbar"', false);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RecordingAudioStreamTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_owner_can_stream_recording_audio(): void
|
||||
{
|
||||
Storage::fake('local');
|
||||
Storage::disk('local')->put('recordings/note.mp3', 'fake-audio-bytes');
|
||||
|
||||
$user = User::factory()->create();
|
||||
$recording = Recording::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'title' => 'Note',
|
||||
'original_filename' => 'note.mp3',
|
||||
'file_path' => 'recordings/note.mp3',
|
||||
'file_size_bytes' => 16,
|
||||
'transcription_status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('recordings.audio', $recording))
|
||||
->assertOk()
|
||||
->assertHeader('content-type', 'audio/mpeg')
|
||||
->assertHeader('content-disposition', 'inline; filename=note.mp3');
|
||||
}
|
||||
|
||||
public function test_show_page_includes_audio_player(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$recording = Recording::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'title' => 'Playable',
|
||||
'original_filename' => 'playable.mp3',
|
||||
'file_path' => 'recordings/playable.mp3',
|
||||
'file_size_bytes' => 10,
|
||||
'transcription_status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('recordings.show', $recording))
|
||||
->assertOk()
|
||||
->assertSee('Play', false)
|
||||
->assertSee(route('recordings.audio', $recording), false)
|
||||
->assertSee('<audio', false);
|
||||
}
|
||||
|
||||
public function test_index_page_links_to_recording_show(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$recording = Recording::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'title' => 'List playable',
|
||||
'original_filename' => 'list.mp3',
|
||||
'file_path' => 'recordings/list.mp3',
|
||||
'file_size_bytes' => 10,
|
||||
'transcription_status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('recordings.index'))
|
||||
->assertOk()
|
||||
->assertSee('List playable')
|
||||
->assertSee(route('recordings.show', $recording), false);
|
||||
}
|
||||
|
||||
public function test_other_user_cannot_stream_recording_audio(): void
|
||||
{
|
||||
Storage::fake('local');
|
||||
Storage::disk('local')->put('recordings/secret.mp3', 'fake-audio-bytes');
|
||||
|
||||
$owner = User::factory()->create();
|
||||
$intruder = User::factory()->create();
|
||||
$recording = Recording::query()->create([
|
||||
'user_id' => $owner->id,
|
||||
'title' => 'Secret',
|
||||
'original_filename' => 'secret.mp3',
|
||||
'file_path' => 'recordings/secret.mp3',
|
||||
'file_size_bytes' => 16,
|
||||
'transcription_status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->actingAs($intruder)
|
||||
->get(route('recordings.audio', $recording))
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_missing_audio_file_returns_not_found(): void
|
||||
{
|
||||
Storage::fake('local');
|
||||
|
||||
$user = User::factory()->create();
|
||||
$recording = Recording::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'title' => 'Gone',
|
||||
'original_filename' => 'gone.mp3',
|
||||
'file_path' => 'recordings/gone.mp3',
|
||||
'file_size_bytes' => 10,
|
||||
'transcription_status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('recordings.audio', $recording))
|
||||
->assertNotFound();
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,14 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Jobs\TranscribeRecording;
|
||||
use App\Livewire\UploadRecordings;
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RecordingDuplicateUploadTest extends TestCase
|
||||
@@ -33,19 +35,18 @@ class RecordingDuplicateUploadTest extends TestCase
|
||||
$first = UploadedFile::fake()->createWithContent('meeting.mp3', 'identical-audio-bytes');
|
||||
$duplicate = UploadedFile::fake()->createWithContent('meeting-copy.mp3', 'identical-audio-bytes');
|
||||
|
||||
$this->post(route('recordings.store'), [
|
||||
'audio' => [$first],
|
||||
])->assertRedirect();
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [$first])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertSame(1, Recording::query()->count());
|
||||
Bus::assertDispatched(TranscribeRecording::class, 1);
|
||||
|
||||
$response = $this->post(route('recordings.store'), [
|
||||
'audio' => [$duplicate],
|
||||
]);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [$duplicate])
|
||||
->assertRedirect(route('recordings.create'))
|
||||
->assertSessionHas('error');
|
||||
|
||||
$response->assertRedirect(route('recordings.create'));
|
||||
$response->assertSessionHas('error');
|
||||
$this->assertSame(1, Recording::query()->count());
|
||||
Bus::assertDispatched(TranscribeRecording::class, 1);
|
||||
}
|
||||
@@ -59,18 +60,17 @@ class RecordingDuplicateUploadTest extends TestCase
|
||||
$two = UploadedFile::fake()->createWithContent('two.mp3', 'same-bytes');
|
||||
$three = UploadedFile::fake()->createWithContent('three.mp3', 'different-bytes');
|
||||
|
||||
$response = $this->post(route('recordings.store'), [
|
||||
'audio' => [$one, $two, $three],
|
||||
]);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [$one, $two, $three])
|
||||
->assertRedirect(route('recordings.index'))
|
||||
->assertSessionHas('success');
|
||||
|
||||
$response->assertRedirect(route('recordings.index'));
|
||||
$response->assertSessionHas('success');
|
||||
$this->assertStringContainsString('Skipped 1 duplicate', session('success'));
|
||||
$this->assertSame(2, Recording::query()->count());
|
||||
Bus::assertDispatched(TranscribeRecording::class, 2);
|
||||
}
|
||||
|
||||
public function test_upload_page_includes_existing_fingerprints_for_client_dedupe(): void
|
||||
public function test_upload_page_mentions_duplicate_skipping(): void
|
||||
{
|
||||
Recording::query()->create([
|
||||
'user_id' => $this->user->id,
|
||||
@@ -84,7 +84,7 @@ class RecordingDuplicateUploadTest extends TestCase
|
||||
|
||||
$this->get(route('recordings.create'))
|
||||
->assertOk()
|
||||
->assertSee('note.mp3:2048', false)
|
||||
->assertSeeLivewire('upload-recordings')
|
||||
->assertSee('Duplicate files', false);
|
||||
}
|
||||
|
||||
@@ -103,12 +103,11 @@ class RecordingDuplicateUploadTest extends TestCase
|
||||
'transcription_status' => 'done',
|
||||
]);
|
||||
|
||||
$response = $this->post(route('recordings.store'), [
|
||||
'audio' => [UploadedFile::fake()->createWithContent('legacy.mp3', 'legacy-audio')],
|
||||
]);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [UploadedFile::fake()->createWithContent('legacy.mp3', 'legacy-audio')])
|
||||
->assertRedirect(route('recordings.create'))
|
||||
->assertSessionHas('error');
|
||||
|
||||
$response->assertRedirect(route('recordings.create'));
|
||||
$response->assertSessionHas('error');
|
||||
$this->assertSame(1, Recording::query()->count());
|
||||
Bus::assertNothingDispatched();
|
||||
}
|
||||
@@ -120,9 +119,9 @@ class RecordingDuplicateUploadTest extends TestCase
|
||||
|
||||
$file = UploadedFile::fake()->createWithContent('hash-me.mp3', 'payload-for-hash');
|
||||
|
||||
$this->post(route('recordings.store'), [
|
||||
'audio' => [$file],
|
||||
])->assertRedirect();
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [$file])
|
||||
->assertRedirect();
|
||||
|
||||
$recording = Recording::query()->first();
|
||||
$this->assertNotNull($recording);
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Livewire\Recordings\Show;
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RecordingOwnershipTest extends TestCase
|
||||
@@ -74,8 +76,9 @@ class RecordingOwnershipTest extends TestCase
|
||||
'transcription_status' => 'done',
|
||||
]);
|
||||
|
||||
$this->actingAs($intruder)
|
||||
->delete(route('recordings.destroy', $recording))
|
||||
$this->actingAs($intruder);
|
||||
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->assertForbidden();
|
||||
|
||||
$this->assertDatabaseHas('recordings', ['id' => $recording->id]);
|
||||
|
||||
@@ -4,6 +4,9 @@ namespace Tests\Feature;
|
||||
|
||||
use App\Http\Requests\StoreRecordingRequest;
|
||||
use App\Jobs\TranscribeRecording;
|
||||
use App\Livewire\Recordings\Index;
|
||||
use App\Livewire\Recordings\Show;
|
||||
use App\Livewire\UploadRecordings;
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use App\Services\TranscriptionService;
|
||||
@@ -12,6 +15,7 @@ use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Laravel\Ai\Transcription;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RecordingUploadTest extends TestCase
|
||||
@@ -54,15 +58,14 @@ class RecordingUploadTest extends TestCase
|
||||
|
||||
$file = UploadedFile::fake()->create('meeting.mp3', 500, 'audio/mpeg');
|
||||
|
||||
$response = $this->post(route('recordings.store'), [
|
||||
'audio' => [$file],
|
||||
'title' => 'Team meeting',
|
||||
]);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('title', 'Team meeting')
|
||||
->set('audio', [$file])
|
||||
->assertRedirect(route('recordings.show', Recording::query()->first()));
|
||||
|
||||
$recording = Recording::query()->first();
|
||||
|
||||
$this->assertNotNull($recording);
|
||||
$response->assertRedirect(route('recordings.show', $recording));
|
||||
$this->assertSame('Team meeting', $recording->title);
|
||||
$this->assertSame('pending', $recording->transcription_status);
|
||||
$this->assertSame('local', $recording->transcription_driver);
|
||||
@@ -76,16 +79,13 @@ class RecordingUploadTest extends TestCase
|
||||
Storage::fake('local');
|
||||
Bus::fake();
|
||||
|
||||
$response = $this->post(route('recordings.store'), [
|
||||
'audio' => [
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [
|
||||
UploadedFile::fake()->createWithContent('one.mp3', str_repeat('a', 400)),
|
||||
UploadedFile::fake()->createWithContent('two.wav', str_repeat('b', 400)),
|
||||
UploadedFile::fake()->createWithContent('three.ogg', str_repeat('c', 400)),
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('recordings.index'));
|
||||
$response->assertSessionHas('success');
|
||||
])
|
||||
->assertRedirect(route('recordings.index'));
|
||||
$this->assertSame(3, Recording::query()->count());
|
||||
Bus::assertDispatched(TranscribeRecording::class, 3);
|
||||
|
||||
@@ -99,9 +99,9 @@ class RecordingUploadTest extends TestCase
|
||||
{
|
||||
$this->get(route('recordings.create'))
|
||||
->assertOk()
|
||||
->assertSee('Drop audio files here')
|
||||
->assertSee('Drop audio files here or click to browse')
|
||||
->assertSee('max 2 GB each', false)
|
||||
->assertSee('name="audio[]"', false);
|
||||
->assertSeeLivewire('upload-recordings');
|
||||
}
|
||||
|
||||
public function test_files_larger_than_two_gigabytes_are_rejected(): void
|
||||
@@ -113,11 +113,9 @@ class RecordingUploadTest extends TestCase
|
||||
->create('huge.mp3', 10, 'audio/mpeg')
|
||||
->size(StoreRecordingRequest::MAX_AUDIO_KILOBYTES + 1);
|
||||
|
||||
$this->from(route('recordings.create'))
|
||||
->post(route('recordings.store'), [
|
||||
'audio' => [$file],
|
||||
])
|
||||
->assertSessionHasErrors(['audio.0']);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [$file])
|
||||
->assertHasErrors(['audio.0']);
|
||||
|
||||
$this->assertSame(0, Recording::query()->count());
|
||||
Bus::assertNothingDispatched();
|
||||
@@ -133,15 +131,14 @@ class RecordingUploadTest extends TestCase
|
||||
['clip.ogg', 'audio/ogg', 'ogg-bytes'],
|
||||
['talk.m4a', 'audio/mp4', 'm4a-bytes'],
|
||||
] as [$name, $mime, $contents]) {
|
||||
$response = $this->post(route('recordings.store'), [
|
||||
'audio' => [UploadedFile::fake()->createWithContent($name, $contents)],
|
||||
'title' => $name,
|
||||
]);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('title', $name)
|
||||
->set('audio', [UploadedFile::fake()->createWithContent($name, $contents)])
|
||||
->assertRedirect();
|
||||
|
||||
$recording = Recording::query()->where('title', $name)->first();
|
||||
|
||||
$this->assertNotNull($recording, "Failed uploading {$name}");
|
||||
$response->assertRedirect(route('recordings.show', $recording));
|
||||
Storage::disk('local')->assertExists($recording->file_path);
|
||||
}
|
||||
|
||||
@@ -152,11 +149,9 @@ class RecordingUploadTest extends TestCase
|
||||
{
|
||||
Storage::fake('local');
|
||||
|
||||
$this->from(route('recordings.create'))
|
||||
->post(route('recordings.store'), [
|
||||
'audio' => [UploadedFile::fake()->create('notes.txt', 10, 'text/plain')],
|
||||
])
|
||||
->assertSessionHasErrors(['audio.0']);
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [UploadedFile::fake()->create('notes.txt', 10, 'text/plain')])
|
||||
->assertHasErrors(['audio.0']);
|
||||
}
|
||||
|
||||
public function test_user_can_queue_local_transcription(): void
|
||||
@@ -173,8 +168,8 @@ class RecordingUploadTest extends TestCase
|
||||
// Fake AI so the afterResponse job (sync) does not call a real provider.
|
||||
Transcription::fake(['Queued transcription text.']);
|
||||
|
||||
$this->post(route('recordings.transcribe', $recording))
|
||||
->assertRedirect();
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->call('startTranscription');
|
||||
|
||||
$recording->refresh();
|
||||
$this->assertSame('local', $recording->transcription_driver);
|
||||
@@ -332,8 +327,8 @@ class RecordingUploadTest extends TestCase
|
||||
'updated_at' => now()->subMinutes(10),
|
||||
]);
|
||||
|
||||
$this->post(route('recordings.transcribe', $recording))
|
||||
->assertRedirect();
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->call('startTranscription');
|
||||
|
||||
$recording->refresh();
|
||||
$this->assertSame('local', $recording->transcription_driver);
|
||||
@@ -355,9 +350,8 @@ class RecordingUploadTest extends TestCase
|
||||
'transcription_started_at' => now(),
|
||||
]);
|
||||
|
||||
$this->post(route('recordings.transcribe.cancel', $recording))
|
||||
->assertRedirect()
|
||||
->assertSessionHas('success');
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->call('cancelTranscription');
|
||||
|
||||
$recording->refresh();
|
||||
$this->assertSame('cancelled', $recording->transcription_status);
|
||||
@@ -380,9 +374,8 @@ class RecordingUploadTest extends TestCase
|
||||
'transcription_started_at' => now()->subMinute(),
|
||||
]);
|
||||
|
||||
$this->post(route('recordings.transcribe', $recording))
|
||||
->assertRedirect()
|
||||
->assertSessionHas('success');
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->call('startTranscription');
|
||||
|
||||
$recording->refresh();
|
||||
$this->assertSame('local', $recording->transcription_driver);
|
||||
@@ -464,8 +457,8 @@ class RecordingUploadTest extends TestCase
|
||||
'transcribed_at' => now()->subHour(),
|
||||
]);
|
||||
|
||||
$this->post(route('recordings.transcribe', $recording))
|
||||
->assertRedirect();
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->call('startTranscription');
|
||||
|
||||
$recording->refresh();
|
||||
$this->assertSame('Old transcript text.', $recording->transcript);
|
||||
@@ -497,10 +490,8 @@ class RecordingUploadTest extends TestCase
|
||||
'transcript' => 'Finished text',
|
||||
]);
|
||||
|
||||
$this->from(route('recordings.index'))
|
||||
->post(route('recordings.transcribe-pending'))
|
||||
->assertRedirect(route('recordings.index'))
|
||||
->assertSessionHas('success');
|
||||
Livewire::test(Index::class)
|
||||
->call('queuePending');
|
||||
|
||||
Bus::assertDispatched(TranscribeRecording::class, 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Recordings;
|
||||
|
||||
use App\Jobs\TranscribeRecording;
|
||||
use App\Livewire\Recordings\Index;
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class IndexTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_page_renders_as_livewire(): void
|
||||
{
|
||||
$user = User::factory()->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_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',
|
||||
]);
|
||||
|
||||
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')
|
||||
->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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Recordings;
|
||||
|
||||
use App\Livewire\Recordings\Show;
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ShowTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_show_page_renders_as_livewire(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$recording = Recording::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'title' => 'Show me',
|
||||
'original_filename' => 'show.mp3',
|
||||
'file_path' => 'recordings/show.mp3',
|
||||
'file_size_bytes' => 100,
|
||||
'transcription_status' => 'done',
|
||||
'transcript' => 'Finished text',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->get(route('recordings.show', $recording))
|
||||
->assertOk()
|
||||
->assertSeeLivewire(Show::class)
|
||||
->assertSee('Show me')
|
||||
->assertSee('Play', false);
|
||||
}
|
||||
|
||||
public function test_user_can_delete_own_recording(): 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 me',
|
||||
'original_filename' => 'delete-me.mp3',
|
||||
'file_path' => 'recordings/delete-me.mp3',
|
||||
'file_size_bytes' => 5,
|
||||
'transcription_status' => 'done',
|
||||
]);
|
||||
|
||||
Livewire::test(Show::class, ['recording' => $recording])
|
||||
->call('delete')
|
||||
->assertRedirect(route('recordings.index'));
|
||||
|
||||
$this->assertDatabaseMissing('recordings', ['id' => $recording->id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Jobs\TranscribeRecording;
|
||||
use App\Livewire\UploadRecordings;
|
||||
use App\Models\Recording;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UploadRecordingsLivewireTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected User $user;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
$this->actingAs($this->user);
|
||||
}
|
||||
|
||||
public function test_dropping_files_saves_and_queues_transcription(): void
|
||||
{
|
||||
Storage::fake('local');
|
||||
Bus::fake();
|
||||
|
||||
$file = UploadedFile::fake()->create('meeting.mp3', 500, 'audio/mpeg');
|
||||
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('title', 'Team meeting')
|
||||
->set('audio', [$file])
|
||||
->assertRedirect(route('recordings.show', Recording::query()->first()));
|
||||
|
||||
$recording = Recording::query()->first();
|
||||
|
||||
$this->assertNotNull($recording);
|
||||
$this->assertSame('Team meeting', $recording->title);
|
||||
$this->assertSame('pending', $recording->transcription_status);
|
||||
Storage::disk('local')->assertExists($recording->file_path);
|
||||
Bus::assertDispatched(TranscribeRecording::class);
|
||||
}
|
||||
|
||||
public function test_batch_upload_redirects_to_index(): void
|
||||
{
|
||||
Storage::fake('local');
|
||||
Bus::fake();
|
||||
|
||||
Livewire::test(UploadRecordings::class)
|
||||
->set('audio', [
|
||||
UploadedFile::fake()->createWithContent('one.mp3', str_repeat('a', 400)),
|
||||
UploadedFile::fake()->createWithContent('two.wav', str_repeat('b', 400)),
|
||||
])
|
||||
->assertRedirect(route('recordings.index'));
|
||||
|
||||
$this->assertSame(2, Recording::query()->count());
|
||||
Bus::assertDispatched(TranscribeRecording::class, 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user