dress/tests/Feature/Admin/Master/ProductTest.php

274 lines
8.7 KiB
PHP

<?php
use App\Models\Category;
use App\Models\Product;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use function Pest\Laravel\actingAs;
use function Pest\Laravel\assertDatabaseHas;
use function Pest\Laravel\assertSoftDeleted;
use function Pest\Laravel\deleteJson;
use function Pest\Laravel\get;
use function Pest\Laravel\patch;
use function Pest\Laravel\patchJson;
use function Pest\Laravel\postJson;
/*
|--------------------------------------------------------------------------
| Product Module Tests
|--------------------------------------------------------------------------
*/
describe('Product Module - Authorization', function () {
it('redirects to login when accessing product index unauthenticated', function () {
get(route('product.index'))
->assertRedirect(route('login'));
});
it('returns 403 when user has no permission to view products', function () {
actingAs(createUnauthorizedUser())
->get(route('product.index'))
->assertStatus(403);
});
});
describe('Product Module - Authorized Actions', function () {
beforeEach(function () {
$user = createAuthorizedUser([
'View:Product',
'Create:Product',
'Edit:Product',
'Delete:Product',
'DeleteAny:Product',
'ToggleStatus:Product',
]);
actingAs($user);
});
it('can access product index page', function () {
get(route('product.index'))
->assertOk()
->assertInertia(fn ($page) => $page
->component('admin/master/product/index')
->has('products')
->has('categories')
);
});
it('can access product create page', function () {
get(route('product.create'))
->assertOk()
->assertInertia(fn ($page) => $page
->component('admin/master/product/create')
->has('categories')
);
});
it('can store a new product', function () {
Storage::fake('public');
$categories = Category::factory()->count(2)->create(['is_active' => true]);
$data = [
'name' => 'New Awesome Product',
'description' => 'Product description',
'category_ids' => $categories->pluck('id')->toArray(),
'prices' => [
'purchase' => 10000,
'distributor' => 11000,
'agent' => 12000,
'reseller' => 13000,
'retail' => 15000,
],
'stock' => 10,
'thumbnail' => UploadedFile::fake()->image('thumbnail.jpg'),
];
postJson(route('product.store'), $data)
->assertRedirect(route('product.index'))
->assertSessionHas('success');
assertDatabaseHas('products', [
'name' => 'New Awesome Product',
'description' => 'Product description',
]);
$product = Product::where('name', 'New Awesome Product')->first();
// Check categories
expect($product->categories)->toHaveCount(2);
// Check prices
foreach ($data['prices'] as $type => $price) {
assertDatabaseHas('product_prices', [
'product_id' => $product->id,
'price_type' => $type,
'price' => $price,
]);
}
});
it('validates product creation', function () {
postJson(route('product.store'), [])
->assertStatus(422)
->assertJsonValidationErrors(['name', 'category_ids', 'prices', 'stock']);
});
it('can access product edit page', function () {
$product = Product::factory()->create();
get(route('product.edit', $product))
->assertOk()
->assertInertia(fn ($page) => $page
->component('admin/master/product/edit')
->has('product')
->has('categories')
);
});
it('can update a product', function () {
$product = Product::factory()->create(['name' => 'Old Product Name']);
$categories = Category::factory()->count(1)->create(['is_active' => true]);
$newData = [
'name' => 'Updated Product Name',
'description' => 'Updated description',
'category_ids' => $categories->pluck('id')->toArray(),
'prices' => [
'purchase' => 20000,
'distributor' => 21000,
'agent' => 22000,
'reseller' => 23000,
'retail' => 25000,
],
'stock' => 50,
];
patchJson(route('product.update', $product), $newData)
->assertRedirect(route('product.index'))
->assertSessionHas('success');
assertDatabaseHas('products', [
'id' => $product->id,
'name' => 'Updated Product Name',
'stock' => 50,
]);
});
it('can delete a product', function () {
$product = Product::factory()->create();
deleteJson(route('product.destroy', $product))
->assertRedirect()
->assertSessionHas('success');
assertSoftDeleted('products', ['id' => $product->id]);
});
it('can delete products in bulk', function () {
$products = Product::factory()->count(3)->create();
$ids = $products->pluck('id')->toArray();
deleteJson(route('product.bulkDestroy'), ['ids' => $ids])
->assertRedirect()
->assertSessionHas('success');
foreach ($ids as $id) {
assertSoftDeleted('products', ['id' => $id]);
}
});
it('can toggle product status', function () {
$product = Product::factory()->create(['is_active' => true]);
patchJson(route('product.toggleStatus', $product))
->assertRedirect()
->assertSessionHas('success');
expect($product->fresh()->is_active)->toBeFalse();
patchJson(route('product.toggleStatus', $product))
->assertRedirect();
expect($product->fresh()->is_active)->toBeTrue();
});
it('can manage product images', function () {
Storage::fake('public');
$product = Product::factory()->create();
$categories = Category::factory()->count(1)->create(['is_active' => true]);
// 1. Upload multiple images
$images = [
UploadedFile::fake()->image('image1.jpg'),
UploadedFile::fake()->image('image2.jpg'),
];
$data = [
'name' => $product->name,
'description' => 'Some description',
'category_ids' => $categories->pluck('id')->toArray(),
'prices' => [
'purchase' => 10000,
'distributor' => 11000,
'agent' => 12000,
'reseller' => 13000,
'retail' => 15000,
],
'images' => $images,
];
patch(route('product.update', $product), $data)
->assertRedirect()
->assertSessionHas('success');
$product->refresh();
expect($product->getMedia('images'))->toHaveCount(2);
// 2. Delete one image
$mediaIdToDelete = $product->getMedia('images')->first()->id;
$deleteData = array_merge($data, [
'delete_image_ids' => [$mediaIdToDelete],
'images' => [], // Don't upload new ones
]);
patch(route('product.update', $product), $deleteData)
->assertRedirect()
->assertSessionHas('success');
$product->refresh();
expect($product->getMedia('images'))->toHaveCount(1);
expect($product->getMedia('images')->first()->id)->not->toBe($mediaIdToDelete);
});
});
describe('Product Module - Unauthorized Actions', function () {
beforeEach(function () {
actingAs(createUnauthorizedUser());
});
it('cannot store a product without permission', function () {
postJson(route('product.store'), ['name' => 'Unauthorized'])
->assertStatus(403);
});
it('cannot update a product without permission', function () {
$product = Product::factory()->create();
patchJson(route('product.update', $product), ['name' => 'Unauthorized'])
->assertStatus(403);
});
it('cannot delete a product without permission', function () {
$product = Product::factory()->create();
deleteJson(route('product.destroy', $product))
->assertStatus(403);
});
it('cannot toggle product status without permission', function () {
$product = Product::factory()->create();
patchJson(route('product.toggleStatus', $product))
->assertStatus(403);
});
});