complete 3.1 with qa test and future recommendations files also update claude md with init and laravel boost
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
use App\Models\AdminLog;
|
||||
use App\Models\User;
|
||||
use App\Models\WorkingHour;
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->admin = User::factory()->admin()->create();
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Access Tests
|
||||
// ===========================================
|
||||
|
||||
test('admin can access working hours configuration page', function () {
|
||||
$this->actingAs($this->admin)
|
||||
->get(route('admin.settings.working-hours'))
|
||||
->assertOk();
|
||||
});
|
||||
|
||||
test('non-admin cannot access working hours configuration page', function () {
|
||||
$client = User::factory()->individual()->create();
|
||||
|
||||
$this->actingAs($client)
|
||||
->get(route('admin.settings.working-hours'))
|
||||
->assertForbidden();
|
||||
});
|
||||
|
||||
test('unauthenticated user cannot access working hours configuration page', function () {
|
||||
$this->get(route('admin.settings.working-hours'))
|
||||
->assertRedirect(route('login'));
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Component Initialization Tests
|
||||
// ===========================================
|
||||
|
||||
test('component initializes with empty schedule when no working hours exist', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
$component = Volt::test('admin.settings.working-hours');
|
||||
|
||||
foreach (range(0, 6) as $day) {
|
||||
expect($component->get("schedule.{$day}.is_active"))->toBeFalse();
|
||||
expect($component->get("schedule.{$day}.start_time"))->toBe('09:00');
|
||||
expect($component->get("schedule.{$day}.end_time"))->toBe('17:00');
|
||||
}
|
||||
});
|
||||
|
||||
test('component initializes with existing working hours', function () {
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '08:00:00',
|
||||
'end_time' => '16:00:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
$component = Volt::test('admin.settings.working-hours');
|
||||
|
||||
expect($component->get('schedule.1.is_active'))->toBeTrue();
|
||||
expect($component->get('schedule.1.start_time'))->toBe('08:00');
|
||||
expect($component->get('schedule.1.end_time'))->toBe('16:00');
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Save Working Hours Tests
|
||||
// ===========================================
|
||||
|
||||
test('admin can save working hours configuration', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '17:00')
|
||||
->call('save')
|
||||
->assertHasNoErrors();
|
||||
|
||||
$workingHour = WorkingHour::where('day_of_week', 1)->first();
|
||||
|
||||
expect($workingHour)->not->toBeNull()
|
||||
->and($workingHour->is_active)->toBeTrue()
|
||||
->and($workingHour->start_time)->toContain('09:00')
|
||||
->and($workingHour->end_time)->toContain('17:00');
|
||||
});
|
||||
|
||||
test('admin can enable multiple days with different hours', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.0.is_active', true)
|
||||
->set('schedule.0.start_time', '09:00')
|
||||
->set('schedule.0.end_time', '17:00')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '08:00')
|
||||
->set('schedule.1.end_time', '16:00')
|
||||
->set('schedule.2.is_active', true)
|
||||
->set('schedule.2.start_time', '10:00')
|
||||
->set('schedule.2.end_time', '18:00')
|
||||
->call('save')
|
||||
->assertHasNoErrors();
|
||||
|
||||
expect(WorkingHour::where('is_active', true)->count())->toBe(3);
|
||||
|
||||
$monday = WorkingHour::where('day_of_week', 1)->first();
|
||||
expect($monday->start_time)->toContain('08:00');
|
||||
expect($monday->end_time)->toContain('16:00');
|
||||
});
|
||||
|
||||
test('admin can disable a day', function () {
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', false)
|
||||
->call('save')
|
||||
->assertHasNoErrors();
|
||||
|
||||
$workingHour = WorkingHour::where('day_of_week', 1)->first();
|
||||
expect($workingHour->is_active)->toBeFalse();
|
||||
});
|
||||
|
||||
test('existing working hours are updated not duplicated', function () {
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '08:00:00',
|
||||
'end_time' => '16:00:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '17:00')
|
||||
->call('save')
|
||||
->assertHasNoErrors();
|
||||
|
||||
expect(WorkingHour::where('day_of_week', 1)->count())->toBe(1);
|
||||
|
||||
$workingHour = WorkingHour::where('day_of_week', 1)->first();
|
||||
expect($workingHour->start_time)->toContain('09:00');
|
||||
expect($workingHour->end_time)->toContain('17:00');
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Validation Tests
|
||||
// ===========================================
|
||||
|
||||
test('validation fails when end time is before start time for active day', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '17:00')
|
||||
->set('schedule.1.end_time', '09:00')
|
||||
->call('save')
|
||||
->assertHasErrors(['schedule.1.end_time']);
|
||||
});
|
||||
|
||||
test('validation fails when end time equals start time for active day', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '09:00')
|
||||
->call('save')
|
||||
->assertHasErrors(['schedule.1.end_time']);
|
||||
});
|
||||
|
||||
test('inactive days do not require time validation', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', false)
|
||||
->set('schedule.1.start_time', '17:00')
|
||||
->set('schedule.1.end_time', '09:00')
|
||||
->call('save')
|
||||
->assertHasNoErrors();
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Audit Log Tests
|
||||
// ===========================================
|
||||
|
||||
test('audit log is created when working hours are saved', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.0.is_active', true)
|
||||
->set('schedule.0.start_time', '09:00')
|
||||
->set('schedule.0.end_time', '17:00')
|
||||
->call('save');
|
||||
|
||||
expect(AdminLog::where('target_type', 'working_hours')->count())->toBe(1);
|
||||
|
||||
$log = AdminLog::where('target_type', 'working_hours')->first();
|
||||
expect($log->admin_id)->toBe($this->admin->id);
|
||||
expect($log->action)->toBe('update');
|
||||
});
|
||||
|
||||
test('audit log contains old and new values', function () {
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '08:00:00',
|
||||
'end_time' => '16:00:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '17:00')
|
||||
->call('save');
|
||||
|
||||
$log = AdminLog::where('target_type', 'working_hours')->first();
|
||||
|
||||
expect($log->old_values)->toBeArray();
|
||||
expect($log->new_values)->toBeArray();
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Slot Display Tests
|
||||
// ===========================================
|
||||
|
||||
test('active day shows available slots badge', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '12:00')
|
||||
->assertSee(__('admin.slots_available', ['count' => 3]));
|
||||
});
|
||||
|
||||
test('inactive day does not show slots badge', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', false)
|
||||
->assertSee(__('admin.closed'));
|
||||
});
|
||||
|
||||
test('invalid time range shows no slots badge', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '17:00')
|
||||
->set('schedule.1.end_time', '09:00')
|
||||
->assertSee(__('admin.no_slots'));
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Time Format Display Tests
|
||||
// ===========================================
|
||||
|
||||
test('active day displays 12-hour time format', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '17:00')
|
||||
->assertSee('9:00 AM')
|
||||
->assertSee('5:00 PM');
|
||||
});
|
||||
|
||||
// ===========================================
|
||||
// Flash Message Tests
|
||||
// ===========================================
|
||||
|
||||
test('save completes without errors and data is persisted', function () {
|
||||
$this->actingAs($this->admin);
|
||||
|
||||
Volt::test('admin.settings.working-hours')
|
||||
->set('schedule.1.is_active', true)
|
||||
->set('schedule.1.start_time', '09:00')
|
||||
->set('schedule.1.end_time', '17:00')
|
||||
->call('save')
|
||||
->assertHasNoErrors();
|
||||
|
||||
// Verify data was saved
|
||||
expect(WorkingHour::where('day_of_week', 1)->exists())->toBeTrue();
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
use App\Models\WorkingHour;
|
||||
|
||||
test('getDayName returns correct English day names', function () {
|
||||
expect(WorkingHour::getDayName(0, 'en'))->toBe('Sunday');
|
||||
expect(WorkingHour::getDayName(1, 'en'))->toBe('Monday');
|
||||
expect(WorkingHour::getDayName(2, 'en'))->toBe('Tuesday');
|
||||
expect(WorkingHour::getDayName(3, 'en'))->toBe('Wednesday');
|
||||
expect(WorkingHour::getDayName(4, 'en'))->toBe('Thursday');
|
||||
expect(WorkingHour::getDayName(5, 'en'))->toBe('Friday');
|
||||
expect(WorkingHour::getDayName(6, 'en'))->toBe('Saturday');
|
||||
});
|
||||
|
||||
test('getDayName returns correct Arabic day names', function () {
|
||||
expect(WorkingHour::getDayName(0, 'ar'))->toBe('الأحد');
|
||||
expect(WorkingHour::getDayName(1, 'ar'))->toBe('الإثنين');
|
||||
expect(WorkingHour::getDayName(2, 'ar'))->toBe('الثلاثاء');
|
||||
expect(WorkingHour::getDayName(3, 'ar'))->toBe('الأربعاء');
|
||||
expect(WorkingHour::getDayName(4, 'ar'))->toBe('الخميس');
|
||||
expect(WorkingHour::getDayName(5, 'ar'))->toBe('الجمعة');
|
||||
expect(WorkingHour::getDayName(6, 'ar'))->toBe('السبت');
|
||||
});
|
||||
|
||||
test('getDayName defaults to English for unsupported locale', function () {
|
||||
expect(WorkingHour::getDayName(0, 'fr'))->toBe('Sunday');
|
||||
});
|
||||
|
||||
test('getSlots returns correct 1-hour slots', function () {
|
||||
$workingHour = WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '09:00:00',
|
||||
'end_time' => '12:00:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$slots = $workingHour->getSlots(60);
|
||||
|
||||
expect($slots)->toBe(['09:00', '10:00', '11:00']);
|
||||
});
|
||||
|
||||
test('getSlots returns correct 30-minute slots', function () {
|
||||
$workingHour = WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '09:00:00',
|
||||
'end_time' => '11:00:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$slots = $workingHour->getSlots(30);
|
||||
|
||||
expect($slots)->toBe(['09:00', '09:30', '10:00', '10:30']);
|
||||
});
|
||||
|
||||
test('getSlots returns empty array when duration exceeds available time', function () {
|
||||
$workingHour = WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '09:00:00',
|
||||
'end_time' => '09:30:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
expect($workingHour->getSlots(60))->toBe([]);
|
||||
});
|
||||
|
||||
test('getSlots returns empty array when start and end times are equal', function () {
|
||||
$workingHour = WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'start_time' => '09:00:00',
|
||||
'end_time' => '09:00:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
expect($workingHour->getSlots(60))->toBe([]);
|
||||
});
|
||||
|
||||
test('active scope returns only active working hours', function () {
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 0,
|
||||
'is_active' => true,
|
||||
]);
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 1,
|
||||
'is_active' => false,
|
||||
]);
|
||||
WorkingHour::factory()->create([
|
||||
'day_of_week' => 2,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
expect(WorkingHour::active()->count())->toBe(2);
|
||||
});
|
||||
|
||||
test('day_of_week is cast to integer', function () {
|
||||
$workingHour = WorkingHour::factory()->create([
|
||||
'day_of_week' => 3,
|
||||
]);
|
||||
|
||||
expect($workingHour->day_of_week)->toBeInt();
|
||||
});
|
||||
|
||||
test('is_active is cast to boolean', function () {
|
||||
$workingHour = WorkingHour::factory()->create([
|
||||
'is_active' => 1,
|
||||
]);
|
||||
|
||||
expect($workingHour->is_active)->toBeBool()
|
||||
->and($workingHour->is_active)->toBeTrue();
|
||||
});
|
||||
Reference in New Issue
Block a user