parfum/tests/Feature/Livewire/Studio/Master/Outlet/CreateTest.php
Yoga Pangestu 977b191ccb refactor(outlet): menjadikan kode nya clean code
- menyesuaikan testing
- menghapus route untuk delete karena tidak lewat route
- menyesuaikan tipe prop dan lainnya
2025-12-09 13:32:33 +07:00

426 lines
15 KiB
PHP

<?php
require_once __DIR__.'/TestHelpers.php';
use App\Enums\Day;
use App\Enums\OutletStatus;
use App\Livewire\Studio\Master\Outlet\Create;
use App\Livewire\Studio\Master\Outlet\Index;
use App\Models\Employee;
use App\Models\Facility;
use App\Models\Outlet;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Gate;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->user = User::factory()
->active()
->has(Employee::factory())
->create();
});
function mountCreateComponent(User $user)
{
return Livewire::actingAs($user)->test(Create::class);
}
it('renders page successfully', function () {
mountCreateComponent($this->user)
->assertViewIs('livewire.studio.master.outlet.form')
->assertViewHas('pageTitle', 'Tambah Outlet');
});
it('mounts days and form opening_hours correctly', function () {
$component = mountCreateComponent($this->user);
$days = Day::cases();
expect($component->days)->toHaveCount(count($days));
foreach ($days as $day) {
expect(array_key_exists($day->value, $component->form->opening_hours))->toBeTrue();
expect($component->form->opening_hours[$day->value])->toBe(['open_time' => null, 'close_time' => null]);
}
});
it('prevents create when user is unauthorized', function () {
Gate::define('create outlet', fn () => false);
mountCreateComponent($this->user)
->call('save')
->assertForbidden();
});
it('saves outlet successfully when authorized', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$facilities = Facility::factory()->count(5)->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [];
for ($i = 1; $i <= 5; $i++) {
$images[] = makeLivewireFile("image{$i}.jpg");
}
$openingHours = collect(Day::cases())
->mapWithKeys(fn ($day) => [
$day->value => [
'day' => $day->value,
'open_time' => fake()->time('H:i'),
'close_time' => fake()->time('H:i'),
],
])
->toArray();
$data = array_merge($outlet, [
'opening_hours' => $openingHours,
'facilities' => collect($facilities)->pluck('name')->toArray(),
'featured_image' => [$featuredImage],
'images' => $images,
]);
mountCreateComponent($this->user)
->set('form.name', $data['name'])
->set('form.phone_number', $data['phone_number'])
->set('form.address', $data['address'])
->set('form.landmark', $data['landmark'])
->set('form.maps_url', $data['maps_url'])
->set('form.opened_date', $data['opened_date'])
->set('form.status', $data['status']->value)
->set('form.opening_hours', $data['opening_hours'])
->set('form.facilities', $data['facilities'])
->set('form.featured_image', $data['featured_image'])
->set('form.images', $data['images'])
->call('save')
->assertHasNoErrors()
->assertRedirect(Index::class);
$outlet = Outlet::first();
$this->assertDatabaseHas('outlets', [
'id' => $outlet->id,
'name' => $data['name'],
'phone_number' => $data['phone_number'],
]);
$this->assertCount(1, $outlet->getMedia('featured_image'));
$this->assertCount(5, $outlet->getMedia('images'));
expect($outlet->getFirstMedia('featured_image')->getPath())->toBeFile();
foreach ($outlet->getMedia('images') as $media) {
expect($media->getPath())->toBeFile();
}
});
it('validates required fields', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
mountCreateComponent($this->user)
->call('save')
->assertHasErrors([
'form.name',
'form.phone_number',
'form.address',
'form.landmark',
'form.opened_date',
'form.featured_image',
'form.images',
]);
});
it('validates name field max length', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
mountCreateComponent($this->user)
->set('form.name', str_repeat('a', 101))
->call('save')
->assertHasErrors(['form.name']);
});
it('validates facilities minimum count', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
mountCreateComponent($this->user)
->set('form.facilities', [])
->call('save')
->assertHasErrors(['form.facilities']);
});
it('validates featured image max count', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$featuredImages = [
makeLivewireFile('featured1.jpg'),
makeLivewireFile('featured2.jpg'),
];
mountCreateComponent($this->user)
->set('form.featured_image', $featuredImages)
->call('save')
->assertHasErrors(['form.featured_image']);
});
it('validates images max count', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$images = [];
for ($i = 1; $i <= 6; $i++) {
$images[] = makeLivewireFile("image{$i}.jpg");
}
mountCreateComponent($this->user)
->set('form.images', $images)
->call('save')
->assertHasErrors(['form.images']);
});
it('has default facilities value', function () {
$component = mountCreateComponent($this->user);
expect($component->form->facilities)->toBe(['Parkir']);
});
it('initializes opening hours for all days of week', function () {
$component = mountCreateComponent($this->user);
foreach (Day::cases() as $day) {
expect(array_key_exists($day->value, $component->form->opening_hours))->toBeTrue();
expect($component->form->opening_hours[$day->value])->toHaveKeys(['open_time', 'close_time']);
expect($component->form->opening_hours[$day->value]['open_time'])->toBeNull();
expect($component->form->opening_hours[$day->value]['close_time'])->toBeNull();
}
});
it('creates outlet with opening hours for all days', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [makeLivewireFile('image1.jpg')];
$openingHours = collect(Day::cases())
->mapWithKeys(fn ($day) => [
$day->value => [
'open_time' => '09:00',
'close_time' => '21:00',
],
])
->toArray();
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.opened_date', $outlet['opened_date'])
->set('form.status', $outlet['status']->value)
->set('form.opening_hours', $openingHours)
->set('form.facilities', ['Parkir'])
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasNoErrors();
$createdOutlet = Outlet::first();
expect($createdOutlet->openingHours)->toHaveCount(count(Day::cases()));
// Verify opening hours data
foreach ($createdOutlet->openingHours as $openingHour) {
expect($openingHour->open_time)->toBe('09:00');
expect($openingHour->close_time)->toBe('21:00');
}
});
it('creates outlet with facilities', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [makeLivewireFile('image1.jpg')];
$facilities = ['Parkir', 'WiFi', 'AC'];
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.opened_date', $outlet['opened_date'])
->set('form.status', $outlet['status']->value)
->set('form.facilities', $facilities)
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasNoErrors();
$createdOutlet = Outlet::first();
expect($createdOutlet->facilities->pluck('name')->toArray())->toBe($facilities);
});
it('sets closed_date when status is TERMINATED', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [makeLivewireFile('image1.jpg')];
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.opened_date', $outlet['opened_date'])
->set('form.status', OutletStatus::TERMINATED->value)
->set('form.facilities', ['Parkir'])
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasNoErrors();
$createdOutlet = Outlet::first();
expect($createdOutlet->status->value)->toBe(OutletStatus::TERMINATED->value);
expect($createdOutlet->closed_date)->not->toBeNull();
});
it('does not set closed_date for non-terminated status', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [makeLivewireFile('image1.jpg')];
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.opened_date', $outlet['opened_date'])
->set('form.status', OutletStatus::OPERATIONAL->value)
->set('form.facilities', ['Parkir'])
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasNoErrors();
$createdOutlet = Outlet::first();
expect($createdOutlet->closed_date)->toBeNull();
});
it('can add and remove facilities dynamically', function () {
$component = mountCreateComponent($this->user);
// Initial state
expect($component->form->facilities)->toBe(['Parkir']);
// Add facility
$component->call('addFacility');
expect($component->form->facilities)->toBe(['Parkir', '']);
// Add another facility
$component->call('addFacility');
expect($component->form->facilities)->toBe(['Parkir', '', '']);
// Remove middle facility
$component->call('removeFacility', 1);
expect($component->form->facilities)->toBe(['Parkir', '']);
// Remove first facility (should keep at least one empty)
$component->call('removeFacility', 0);
expect($component->form->facilities)->toBe(['']);
});
it('validates maps_url regex pattern', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [makeLivewireFile('image1.jpg')];
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.maps_url', 'invalid-url')
->set('form.opened_date', $outlet['opened_date'])
->set('form.facilities', ['Parkir'])
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasErrors(['form.maps_url']);
// Valid Google Maps URL should pass
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.maps_url', 'https://maps.app.goo.gl/example')
->set('form.opened_date', $outlet['opened_date'])
->set('form.facilities', ['Parkir'])
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasNoErrors();
});
it('handles facility removal edge cases', function () {
$component = mountCreateComponent($this->user);
// Set custom facilities
$component->set('form.facilities', ['Parkir', 'WiFi', 'AC']);
// Remove middle facility
$component->call('removeFacility', 1);
expect($component->form->facilities)->toBe(['Parkir', 'AC']);
// Try to remove non-existent index (should not crash)
$component->call('removeFacility', 99);
expect($component->form->facilities)->toBe(['Parkir', 'AC']);
});
it('creates outlet with soft delete capability', function () {
$permission = Permission::create(['name' => 'create outlet']);
$this->user->givePermissionTo($permission);
$outlet = Outlet::factory()->operational()->raw();
$featuredImage = makeLivewireFile('featured.jpg');
$images = [makeLivewireFile('image1.jpg')];
mountCreateComponent($this->user)
->set('form.name', $outlet['name'])
->set('form.phone_number', $outlet['phone_number'])
->set('form.address', $outlet['address'])
->set('form.landmark', $outlet['landmark'])
->set('form.opened_date', $outlet['opened_date'])
->set('form.facilities', ['Parkir'])
->set('form.featured_image', [$featuredImage])
->set('form.images', $images)
->call('save')
->assertHasNoErrors();
$createdOutlet = Outlet::first();
expect($createdOutlet)->toBeInstanceOf(Outlet::class);
// Verify soft delete trait
$createdOutlet->delete();
expect(Outlet::withTrashed()->find($createdOutlet->id))->not->toBeNull();
expect(Outlet::find($createdOutlet->id))->toBeNull();
});