withHeaders(['Accept-Language' => 'es']); Queue::fake(); } protected function projectWithGallery(): Project { $project = Project::factory()->published()->create([ 'title' => ['es' => 'Mi proyecto'], 'slug' => ['es' => 'mi-proyecto'], ]); $project->images()->createMany([ [ 'path' => 'projects/gallery/after.jpg', 'alt' => ['es' => 'El panel nuevo'], 'caption' => ['es' => 'Después'], 'position' => 2, ], [ 'path' => 'projects/gallery/before.jpg', 'alt' => ['es' => 'La hoja de cálculo que usaban'], 'caption' => ['es' => 'Antes'], 'position' => 1, ], ]); return $project; } #[Test] public function the_gallery_comes_back_in_the_order_it_was_arranged(): void { $this->projectWithGallery(); $gallery = $this->getJson('/api/public/v1/projects/mi-proyecto') ->assertOk() ->json('data.gallery'); // An unordered gallery is a different gallery every time it is read, // and "before" after "after" tells the wrong story. $this->assertSame(['Antes', 'Después'], array_column($gallery, 'caption')); } #[Test] public function each_image_carries_everything_needed_to_render_it(): void { $this->projectWithGallery(); Media::query()->where('path', 'projects/gallery/before.jpg')->sole()->forceFill([ 'width' => 1200, 'height' => 800, 'variants' => [ ['format' => 'webp', 'width' => 400, 'path' => 'projects/gallery/before-400.webp', 'size' => 8000], ], 'processed_at' => now(), ])->save(); $first = $this->getJson('/api/public/v1/projects/mi-proyecto') ->assertOk() ->json('data.gallery.0'); $this->assertStringStartsWith('http', $first['url']); $this->assertSame(1200, $first['width']); $this->assertSame('La hoja de cálculo que usaban', $first['alt']); $this->assertStringContainsString('400w', $first['srcset']['webp']); // A caption is not a description: one is shown to everyone, the other // is read by whoever cannot see the image. $this->assertSame('Antes', $first['caption']); } #[Test] public function a_listing_does_not_carry_every_screenshot_of_every_project(): void { $this->projectWithGallery(); $project = $this->getJson('/api/public/v1/projects') ->assertOk() ->json('data.0'); $this->assertArrayNotHasKey('gallery', $project); } #[Test] public function adding_a_gallery_image_registers_it_like_any_other_upload(): void { $project = Project::factory()->create(); $project->images()->create(['path' => 'projects/gallery/new.jpg']); // Nothing special about a gallery image: same media row, same derived // versions, same queue. $this->assertSame(1, Media::query()->where('path', 'projects/gallery/new.jpg')->count()); } #[Test] public function erasing_a_project_for_good_takes_its_gallery_with_it(): void { $project = $this->projectWithGallery(); $project->delete(); // A soft delete keeps it — that is the point of a soft delete. $this->assertSame(2, ImageAttachment::query()->count()); $project->forceDelete(); // A morph relation has no foreign key to cascade for it, so this is // handled explicitly and worth pinning down. $this->assertSame(0, ImageAttachment::query()->count()); } #[Test] public function the_gallery_editor_renders(): void { $this->seed(RolePermissionSeeder::class); $project = $this->projectWithGallery(); $this->actingAs($this->userWithRoles('admin')); $this->get(ProjectResource::getUrl('edit', ['record' => $project])) ->assertSuccessful(); } }