- menyesuaikan testing - menghapus route untuk delete karena tidak lewat route - menyesuaikan tipe prop dan lainnya
240 lines
7.9 KiB
PHP
240 lines
7.9 KiB
PHP
<?php
|
|
|
|
use App\Enums\OutletStatus;
|
|
use App\Livewire\Studio\Master\Outlet\Index;
|
|
use App\Models\Employee;
|
|
use App\Models\Facility;
|
|
use App\Models\OpeningHour;
|
|
use App\Models\Outlet;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Gate;
|
|
use Livewire\Features\SupportTesting\Testable;
|
|
use Livewire\Livewire;
|
|
use Spatie\Permission\Models\Permission;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$this->user = User::factory()
|
|
->active()
|
|
->has(Employee::factory())
|
|
->create();
|
|
});
|
|
|
|
function createOutlet(array $attributes = []): Outlet
|
|
{
|
|
return Outlet::factory()
|
|
->has(OpeningHour::factory()->count(7))
|
|
->has(Facility::factory()->count(5))
|
|
->state(fn () => array_merge([
|
|
'status' => fake()->randomElement(OutletStatus::cases()),
|
|
], $attributes))
|
|
->create();
|
|
}
|
|
|
|
function mountIndexComponent(User $user): Testable
|
|
{
|
|
return Livewire::actingAs($user)->test(Index::class);
|
|
}
|
|
|
|
it('renders page successfully', function () {
|
|
mountIndexComponent($this->user)
|
|
->assertViewIs('livewire.studio.master.outlet.index')
|
|
->assertViewHas('pageTitle', 'Outlet');
|
|
});
|
|
|
|
it('loads outlets as Eloquent collection', function () {
|
|
$outlet = createOutlet();
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect($component->get('outlets'))
|
|
->toBeInstanceOf(Collection::class)
|
|
->and($component->get('outlets')->first()->id)->toBe($outlet->id);
|
|
});
|
|
|
|
it('loads outlets with relationships', function () {
|
|
$outlet = createOutlet();
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
$loadedOutlet = $component->get('outlets')->first();
|
|
|
|
expect($loadedOutlet->openingHours)->toHaveCount(7)
|
|
->and($loadedOutlet->facilities)->toHaveCount(5);
|
|
});
|
|
|
|
it('displays all outlets', function () {
|
|
for ($i = 0; $i < 5; $i++) {
|
|
createOutlet();
|
|
}
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect($component->get('outlets'))->toHaveCount(5);
|
|
expect($component->get('outlets')->first())->toBeInstanceOf(Outlet::class);
|
|
});
|
|
|
|
it('prevents delete when user is unauthorized', function () {
|
|
$outlet = createOutlet();
|
|
Gate::define('delete outlet', fn () => false);
|
|
|
|
mountIndexComponent($this->user)
|
|
->call('delete', $outlet)
|
|
->assertForbidden();
|
|
});
|
|
|
|
it('deletes an outlet successfully when authorized', function () {
|
|
$permission = Permission::create(['name' => 'delete outlet']);
|
|
$this->user->givePermissionTo($permission);
|
|
|
|
$outlet = createOutlet();
|
|
|
|
mountIndexComponent($this->user)
|
|
->call('delete', $outlet)
|
|
->assertHasNoErrors();
|
|
|
|
$this->assertSoftDeleted('outlets', ['id' => $outlet->id]);
|
|
});
|
|
|
|
it('filters outlets by search query', function () {
|
|
$outlet1 = createOutlet(['name' => 'Outlet Jakarta']);
|
|
$outlet2 = createOutlet(['name' => 'Outlet Bandung']);
|
|
$outlet3 = createOutlet(['name' => 'Toko Surabaya']);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect($component->get('outlets'))->toHaveCount(3);
|
|
|
|
// Test filtering by Jakarta
|
|
$component->set('search', 'Jakarta');
|
|
$filteredComponent = mountIndexComponent($this->user)->set('search', 'Jakarta');
|
|
|
|
expect($filteredComponent->get('outlets'))->toHaveCount(1);
|
|
expect($filteredComponent->get('outlets')->first()->name)->toBe('Outlet Jakarta');
|
|
|
|
// Test filtering by Outlet
|
|
$filteredComponent2 = mountIndexComponent($this->user)->set('search', 'Outlet');
|
|
|
|
expect($filteredComponent2->get('outlets'))->toHaveCount(2);
|
|
});
|
|
|
|
it('filters outlets by status', function () {
|
|
$outlet1 = createOutlet(['status' => OutletStatus::OPERATIONAL]);
|
|
$outlet2 = createOutlet(['status' => OutletStatus::UNDER_CONSTRUCTION]);
|
|
$outlet3 = createOutlet(['status' => OutletStatus::TERMINATED]);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect($component->get('outlets'))->toHaveCount(3);
|
|
|
|
$filteredComponent = mountIndexComponent($this->user)->set('status', [OutletStatus::OPERATIONAL->value]);
|
|
|
|
expect($filteredComponent->get('outlets'))->toHaveCount(1);
|
|
expect($filteredComponent->get('outlets')->first()->name)->toBe($outlet1->name);
|
|
|
|
// Test filtering by multiple statuses
|
|
$filteredComponent2 = mountIndexComponent($this->user)->set('status', [
|
|
OutletStatus::OPERATIONAL->value,
|
|
OutletStatus::UNDER_CONSTRUCTION->value,
|
|
]);
|
|
|
|
expect($filteredComponent2->get('outlets'))->toHaveCount(2);
|
|
});
|
|
|
|
it('filters outlets by both search and status', function () {
|
|
$outlet1 = createOutlet(['name' => 'Outlet A', 'status' => OutletStatus::OPERATIONAL]);
|
|
$outlet2 = createOutlet(['name' => 'Outlet B', 'status' => OutletStatus::OPERATIONAL]);
|
|
$outlet3 = createOutlet(['name' => 'Outlet C', 'status' => OutletStatus::UNDER_CONSTRUCTION]);
|
|
|
|
$component = mountIndexComponent($this->user)
|
|
->set('search', 'Outlet')
|
|
->set('status', [OutletStatus::OPERATIONAL->value]);
|
|
|
|
expect($component->get('outlets'))->toHaveCount(2);
|
|
});
|
|
|
|
it('loads outlets with all required relationships', function () {
|
|
$outlet = createOutlet();
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
$loadedOutlet = $component->get('outlets')->first();
|
|
|
|
expect($loadedOutlet->openingHours)->toHaveCount(7)
|
|
->and($loadedOutlet->facilities)->toHaveCount(5)
|
|
->and($loadedOutlet->name)->toBe($outlet->name)
|
|
->and($loadedOutlet->phone_number)->toBe($outlet->phone_number);
|
|
});
|
|
|
|
it('loads outlets ordered by latest', function () {
|
|
$outlet1 = createOutlet(['name' => 'First']);
|
|
sleep(1);
|
|
$outlet2 = createOutlet(['name' => 'Second']);
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
expect($component->get('outlets')->first()->name)->toBe('Second');
|
|
expect($component->get('outlets')->last()->name)->toBe('First');
|
|
});
|
|
|
|
it('provides helper methods for view formatting', function () {
|
|
$outlet = createOutlet();
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
// Test that outlets are loaded with proper data structure
|
|
$loadedOutlet = $component->get('outlets')->first();
|
|
expect($loadedOutlet)->toBeInstanceOf(Outlet::class);
|
|
expect($loadedOutlet->openingHours)->toBeInstanceOf(Collection::class);
|
|
expect($loadedOutlet->facilities)->toBeInstanceOf(Collection::class);
|
|
});
|
|
|
|
it('handles empty search results gracefully', function () {
|
|
createOutlet(['name' => 'Existing Outlet']);
|
|
|
|
$component = mountIndexComponent($this->user)->set('search', 'NonExistentOutlet');
|
|
|
|
expect($component->get('outlets'))->toHaveCount(0);
|
|
});
|
|
|
|
it('maintains search state across component lifecycle', function () {
|
|
$outlet1 = createOutlet(['name' => 'Test Outlet']);
|
|
$outlet2 = createOutlet(['name' => 'Another Outlet']);
|
|
|
|
$component = mountIndexComponent($this->user)->set('search', 'Test');
|
|
|
|
expect($component->get('outlets'))->toHaveCount(1);
|
|
expect($component->get('search'))->toBe('Test');
|
|
|
|
// Re-mount should maintain state
|
|
$newComponent = mountIndexComponent($this->user)->set('search', 'Test');
|
|
|
|
expect($newComponent->get('outlets'))->toHaveCount(1);
|
|
});
|
|
|
|
it('handles status filter with empty array', function () {
|
|
createOutlet(['status' => OutletStatus::OPERATIONAL]);
|
|
createOutlet(['status' => OutletStatus::TERMINATED]);
|
|
|
|
$component = mountIndexComponent($this->user)->set('status', []);
|
|
|
|
expect($component->get('outlets'))->toHaveCount(2);
|
|
});
|
|
|
|
it('loads outlets efficiently with eager loading', function () {
|
|
$outlets = collect();
|
|
for ($i = 0; $i < 10; $i++) {
|
|
$outlets->push(createOutlet());
|
|
}
|
|
|
|
$component = mountIndexComponent($this->user);
|
|
|
|
// Verify relationships are loaded
|
|
$loadedOutlets = $component->get('outlets');
|
|
foreach ($loadedOutlets as $loadedOutlet) {
|
|
expect($loadedOutlet->relationLoaded('openingHours'))->toBeTrue();
|
|
expect($loadedOutlet->relationLoaded('facilities'))->toBeTrue();
|
|
}
|
|
});
|