feat: product screenshots gallery

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Doar Mihai
2026-10-17 16:00:38 +03:00
co-authored by Claude Sonnet 5.5
parent bf365d029f
commit a3894d2f37
9 changed files with 314 additions and 2 deletions
+115
View File
@@ -0,0 +1,115 @@
<?php
use App\Models\Product;
use App\Models\ProductScreenshot;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
uses(RefreshDatabase::class);
function shotPayload(array $overrides = []): array
{
return array_merge([
'title' => 'Shot Product',
'description' => 'A product description',
'website_url' => 'https://example.com',
'category_id' => makeCategory()->id,
], $overrides);
}
function makeShot(Product $product, string $name = 'a.jpg'): ProductScreenshot
{
Storage::disk('public')->put("screenshots/{$product->id}-{$name}", 'x');
return $product->screenshots()->create(['path' => "screenshots/{$product->id}-{$name}"]);
}
test('submitter uploads several screenshots', function () {
Storage::fake('public');
$user = userWithRole('user');
$this->actingAs($user)->post(route('products.store'), shotPayload([
'screenshots' => [
UploadedFile::fake()->image('a.jpg'),
UploadedFile::fake()->image('b.png'),
UploadedFile::fake()->image('c.jpg'),
],
]))->assertRedirect();
$product = Product::firstOrFail();
expect($product->screenshots)->toHaveCount(3);
expect($product->screenshots->pluck('position')->all())->toBe([0, 1, 2]);
foreach ($product->screenshots as $shot) {
Storage::disk('public')->assertExists($shot->path);
}
});
test('screenshots must be images up to 2 MB', function () {
Storage::fake('public');
$user = userWithRole('user');
$this->actingAs($user)->post(route('products.store'), shotPayload([
'screenshots' => [UploadedFile::fake()->create('doc.pdf', 100, 'application/pdf')],
]))->assertSessionHasErrors('screenshots.0');
$this->actingAs($user)->post(route('products.store'), shotPayload([
'screenshots' => [UploadedFile::fake()->image('big.jpg')->size(3000)],
]))->assertSessionHasErrors('screenshots.0');
expect(Product::count())->toBe(0);
});
test('owner removes a screenshot on edit', function () {
Storage::fake('public');
$user = userWithRole('user');
$product = Product::factory()->pending()->create(['user_id' => $user->id]);
$shot = makeShot($product);
$this->actingAs($user)->put(route('products.update', $product), shotPayload([
'category_id' => $product->category_id,
'remove_screenshots' => [$shot->id],
]))->assertRedirect();
expect(ProductScreenshot::count())->toBe(0);
Storage::disk('public')->assertMissing($shot->path);
});
test('remove_screenshots ignores ids from another product', function () {
Storage::fake('public');
$user = userWithRole('user');
$product = Product::factory()->pending()->create(['user_id' => $user->id]);
$other = Product::factory()->pending()->create();
$foreign = makeShot($other, 'f.jpg');
$this->actingAs($user)->put(route('products.update', $product), shotPayload([
'category_id' => $product->category_id,
'remove_screenshots' => [$foreign->id],
]))->assertRedirect()->assertSessionHasNoErrors();
expect($foreign->fresh())->not->toBeNull();
Storage::disk('public')->assertExists($foreign->path);
});
test('deleting a product removes its screenshot files', function () {
Storage::fake('public');
$user = userWithRole('user');
$product = Product::factory()->pending()->create(['user_id' => $user->id]);
$shot = makeShot($product);
$this->actingAs($user)->delete(route('products.destroy', $product))->assertRedirect();
Storage::disk('public')->assertMissing($shot->path);
expect(ProductScreenshot::count())->toBe(0);
});
test('show page renders gallery only when screenshots exist', function () {
Storage::fake('public');
$product = Product::factory()->approved()->create();
$this->get(route('products.show', $product))->assertOk()->assertDontSee('data-gallery', false);
makeShot($product);
$this->get(route('products.show', $product))->assertOk()->assertSee('data-gallery', false);
});