- Deleted MarketplaceFeeRule class and its usage in settings migration. - Removed 'is_affiliate' field from orders and related factories. - Updated notifications table to use longText for body. - Adjusted role permissions by removing marketplace-related permissions. - Cleaned up admin settings page by removing marketplace settings section and related components. - Updated tests to reflect the removal of marketplace settings and ensure other settings remain unaffected.
593 lines
19 KiB
PHP
593 lines
19 KiB
PHP
<?php
|
|
|
|
use App\Models\User;
|
|
use App\Settings\HomepageSettings;
|
|
use App\Settings\HRSettings;
|
|
use App\Settings\SocialMediaSettings;
|
|
use App\Settings\SystemSettings;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Inertia\Testing\AssertableInertia as Assert;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| AUTHENTICATION
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('guests are redirected to the login page', function () {
|
|
$response = $this->get(route('admin.settings.index'));
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guest cannot update system settings', function () {
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'Unauthorized',
|
|
]);
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guest cannot update homepage settings', function () {
|
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
|
'hero_image_key' => 'unauthorized/image.jpg',
|
|
]);
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guest cannot update social media settings', function () {
|
|
$response = $this->put(route('admin.settings.update-social-media'), [
|
|
'instagram_url' => 'https://unauthorized.com',
|
|
]);
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
test('guest cannot update hr settings', function () {
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
]);
|
|
$response->assertRedirect(route('login'));
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| INDEX PAGE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('authenticated users can visit the settings page', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->get(route('admin.settings.index'));
|
|
$response->assertOk();
|
|
});
|
|
|
|
test('settings page renders with correct inertia component', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->get(route('admin.settings.index'));
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->component('admin/settings/index')
|
|
->has('system')
|
|
->has('homepage')
|
|
->has('socialMedia')
|
|
->has('hr')
|
|
);
|
|
});
|
|
|
|
test('settings page contains system data', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$system = app(SystemSettings::class);
|
|
|
|
$response = $this->get(route('admin.settings.index'));
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->where('system.app_name', $system->app_name)
|
|
->where('system.email', $system->email)
|
|
->where('system.phone', $system->phone)
|
|
);
|
|
});
|
|
|
|
test('settings page contains hr data', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$hr = app(HRSettings::class);
|
|
|
|
$response = $this->get(route('admin.settings.index'));
|
|
$response->assertInertia(fn (Assert $page) => $page
|
|
->where('hr.scheduled_check_in_time', $hr->scheduled_check_in_time)
|
|
->where('hr.scheduled_check_out_time', $hr->scheduled_check_out_time)
|
|
->where('hr.late_penalty_amount', $hr->late_penalty_amount)
|
|
->where('hr.absent_penalty_amount', $hr->absent_penalty_amount)
|
|
);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| UPDATE SYSTEM
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('user can update system settings with valid data', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST Collection Updated',
|
|
'about_app' => 'Tentang DST Collection',
|
|
'address' => 'Jl. Test No. 123',
|
|
'email' => 'test@dst.com',
|
|
'phone' => '081234567890',
|
|
]);
|
|
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect(route('admin.settings.index'));
|
|
|
|
$settings = app(SystemSettings::class);
|
|
expect($settings->app_name)->toBe('DST Collection Updated');
|
|
expect($settings->about_app)->toBe('Tentang DST Collection');
|
|
expect($settings->address)->toBe('Jl. Test No. 123');
|
|
expect($settings->email)->toBe('test@dst.com');
|
|
expect($settings->phone)->toBe('081234567890');
|
|
});
|
|
|
|
test('system app_name is required', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => '',
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('app_name');
|
|
});
|
|
|
|
test('system email must be valid email format', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST',
|
|
'email' => 'not-an-email',
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('email');
|
|
});
|
|
|
|
test('system app_name must not exceed 255 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => str_repeat('a', 256),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('app_name');
|
|
});
|
|
|
|
test('system about_app must not exceed 2000 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST',
|
|
'about_app' => str_repeat('a', 2001),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('about_app');
|
|
});
|
|
|
|
test('system address must not exceed 500 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST',
|
|
'address' => str_repeat('a', 501),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('address');
|
|
});
|
|
|
|
test('system phone must not exceed 20 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST',
|
|
'phone' => str_repeat('1', 21),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('phone');
|
|
});
|
|
|
|
test('system nullable fields can be empty', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST',
|
|
'about_app' => '',
|
|
'address' => '',
|
|
'email' => '',
|
|
'phone' => '',
|
|
]);
|
|
|
|
$response->assertSessionHasNoErrors();
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| UPDATE HOMEPAGE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('user can update homepage settings with valid data', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
|
'hero_image_key' => 'homepage/hero/2026/07/31/abc123/hero.jpg',
|
|
'about_image_key' => 'homepage/about/2026/07/31/def456/about.jpg',
|
|
'gallery_image_keys' => ['homepage/gallery/2026/07/31/g1/img.jpg', 'homepage/gallery/2026/07/31/g2/img.jpg'],
|
|
]);
|
|
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect(route('admin.settings.index'));
|
|
|
|
$settings = app(HomepageSettings::class);
|
|
expect($settings->hero_image_url)->toBe('homepage/hero/2026/07/31/abc123/hero.jpg');
|
|
expect($settings->about_image_url)->toBe('homepage/about/2026/07/31/def456/about.jpg');
|
|
expect($settings->gallery_images)->toBe(['homepage/gallery/2026/07/31/g1/img.jpg', 'homepage/gallery/2026/07/31/g2/img.jpg']);
|
|
});
|
|
|
|
test('homepage fields are nullable', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
|
'hero_image_key' => null,
|
|
'about_image_key' => null,
|
|
]);
|
|
|
|
$response->assertSessionHasNoErrors();
|
|
});
|
|
|
|
test('homepage hero_image_key must not exceed 2000 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
|
'hero_image_key' => str_repeat('a', 2001),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('hero_image_key');
|
|
});
|
|
|
|
test('homepage gallery_image_keys must be array', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
|
'gallery_image_keys' => 'not-an-array',
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('gallery_image_keys');
|
|
});
|
|
|
|
test('homepage gallery_image_keys items must not exceed 2000 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-homepage'), [
|
|
'gallery_image_keys' => [str_repeat('a', 2001)],
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('gallery_image_keys.0');
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| UPDATE SOCIAL MEDIA
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('user can update social media settings with valid data', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-social-media'), [
|
|
'instagram_url' => 'https://instagram.com/dst',
|
|
'facebook_url' => 'https://facebook.com/dst',
|
|
'tiktok_url' => 'https://tiktok.com/@dst',
|
|
]);
|
|
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect(route('admin.settings.index'));
|
|
|
|
$settings = app(SocialMediaSettings::class);
|
|
expect($settings->instagram_url)->toBe('https://instagram.com/dst');
|
|
expect($settings->facebook_url)->toBe('https://facebook.com/dst');
|
|
expect($settings->tiktok_url)->toBe('https://tiktok.com/@dst');
|
|
});
|
|
|
|
test('social media fields are nullable', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-social-media'), [
|
|
'instagram_url' => null,
|
|
'facebook_url' => null,
|
|
'tiktok_url' => null,
|
|
]);
|
|
|
|
$response->assertSessionHasNoErrors();
|
|
});
|
|
|
|
test('social media instagram_url must not exceed 500 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-social-media'), [
|
|
'instagram_url' => str_repeat('a', 501),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('instagram_url');
|
|
});
|
|
|
|
test('social media facebook_url must not exceed 500 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-social-media'), [
|
|
'facebook_url' => str_repeat('a', 501),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('facebook_url');
|
|
});
|
|
|
|
test('social media tiktok_url must not exceed 500 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-social-media'), [
|
|
'tiktok_url' => str_repeat('a', 501),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('tiktok_url');
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| UPDATE HR
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('user can update hr settings with valid data', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
'scheduled_check_out_time' => '18:00',
|
|
'late_penalty_amount' => 10000,
|
|
'absent_penalty_amount' => 50000,
|
|
]);
|
|
|
|
$response
|
|
->assertSessionHasNoErrors()
|
|
->assertRedirect(route('admin.settings.index'));
|
|
|
|
$settings = app(HRSettings::class);
|
|
expect($settings->scheduled_check_in_time)->toBe('09:00');
|
|
expect($settings->scheduled_check_out_time)->toBe('18:00');
|
|
expect($settings->late_penalty_amount)->toBe(10000);
|
|
expect($settings->absent_penalty_amount)->toBe(50000);
|
|
});
|
|
|
|
test('hr scheduled_check_in_time is required', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '',
|
|
'scheduled_check_out_time' => '18:00',
|
|
'late_penalty_amount' => 0,
|
|
'absent_penalty_amount' => 0,
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('scheduled_check_in_time');
|
|
});
|
|
|
|
test('hr scheduled_check_out_time is required', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
'scheduled_check_out_time' => '',
|
|
'late_penalty_amount' => 0,
|
|
'absent_penalty_amount' => 0,
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('scheduled_check_out_time');
|
|
});
|
|
|
|
test('hr late_penalty_amount is required', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
'scheduled_check_out_time' => '18:00',
|
|
'late_penalty_amount' => '',
|
|
'absent_penalty_amount' => 0,
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('late_penalty_amount');
|
|
});
|
|
|
|
test('hr absent_penalty_amount is required', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
'scheduled_check_out_time' => '18:00',
|
|
'late_penalty_amount' => 0,
|
|
'absent_penalty_amount' => '',
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('absent_penalty_amount');
|
|
});
|
|
|
|
test('hr penalty amounts must be at least 0', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
'scheduled_check_out_time' => '18:00',
|
|
'late_penalty_amount' => -1,
|
|
'absent_penalty_amount' => -1,
|
|
]);
|
|
|
|
$response->assertSessionHasErrors(['late_penalty_amount', 'absent_penalty_amount']);
|
|
});
|
|
|
|
test('hr penalty amounts must be integers', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:00',
|
|
'scheduled_check_out_time' => '18:00',
|
|
'late_penalty_amount' => 1000.50,
|
|
'absent_penalty_amount' => 2000.75,
|
|
]);
|
|
|
|
$response->assertSessionHasErrors(['late_penalty_amount', 'absent_penalty_amount']);
|
|
});
|
|
|
|
test('hr time fields must not exceed 5 characters', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$response = $this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '09:000',
|
|
'scheduled_check_out_time' => '18:000',
|
|
'late_penalty_amount' => 0,
|
|
'absent_penalty_amount' => 0,
|
|
]);
|
|
|
|
$response->assertSessionHasErrors(['scheduled_check_in_time', 'scheduled_check_out_time']);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| EDGE CASES & DATA INTEGRITY
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('system settings default values exist', function () {
|
|
$settings = app(SystemSettings::class);
|
|
|
|
expect($settings->app_name)->not->toBeEmpty();
|
|
expect($settings->about_app)->not->toBeNull();
|
|
expect($settings->address)->not->toBeNull();
|
|
expect($settings->email)->not->toBeNull();
|
|
expect($settings->phone)->not->toBeNull();
|
|
});
|
|
|
|
test('hr settings default values exist', function () {
|
|
$settings = app(HRSettings::class);
|
|
|
|
expect($settings->scheduled_check_in_time)->not->toBeEmpty();
|
|
expect($settings->scheduled_check_out_time)->not->toBeEmpty();
|
|
expect($settings->late_penalty_amount)->toBeInt();
|
|
expect($settings->absent_penalty_amount)->toBeInt();
|
|
});
|
|
|
|
test('updating one setting group does not affect others', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$systemBefore = app(SystemSettings::class);
|
|
$hrBefore = app(HRSettings::class);
|
|
|
|
$this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'Changed Name',
|
|
'about_app' => $systemBefore->about_app,
|
|
'address' => $systemBefore->address,
|
|
'email' => $systemBefore->email,
|
|
'phone' => $systemBefore->phone,
|
|
]);
|
|
|
|
$systemAfter = app(SystemSettings::class);
|
|
$hrAfter = app(HRSettings::class);
|
|
|
|
expect($systemAfter->app_name)->toBe('Changed Name');
|
|
expect($hrAfter->scheduled_check_in_time)->toBe($hrBefore->scheduled_check_in_time);
|
|
expect($hrAfter->late_penalty_amount)->toBe($hrBefore->late_penalty_amount);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| REALISTIC USER SCENARIOS
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
test('user updates all settings groups in sequence', function () {
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
// Update system
|
|
$this->put(route('admin.settings.update-system'), [
|
|
'app_name' => 'DST Updated',
|
|
'about_app' => 'About Updated',
|
|
'address' => 'Address Updated',
|
|
'email' => 'updated@dst.com',
|
|
'phone' => '089999999999',
|
|
])->assertSessionHasNoErrors();
|
|
|
|
// Update homepage
|
|
$this->put(route('admin.settings.update-homepage'), [
|
|
'hero_image_key' => 'homepage/hero/2026/07/31/new-hero.jpg',
|
|
'about_image_key' => 'homepage/about/2026/07/31/new-about.jpg',
|
|
])->assertSessionHasNoErrors();
|
|
|
|
// Update social media
|
|
$this->put(route('admin.settings.update-social-media'), [
|
|
'instagram_url' => 'https://instagram.com/newdst',
|
|
'facebook_url' => 'https://facebook.com/newdst',
|
|
'tiktok_url' => 'https://tiktok.com/@newdst',
|
|
])->assertSessionHasNoErrors();
|
|
|
|
// Update HR
|
|
$this->put(route('admin.settings.update-hr'), [
|
|
'scheduled_check_in_time' => '08:30',
|
|
'scheduled_check_out_time' => '17:30',
|
|
'late_penalty_amount' => 15000,
|
|
'absent_penalty_amount' => 75000,
|
|
])->assertSessionHasNoErrors();
|
|
|
|
// Verify all settings persisted
|
|
$system = app(SystemSettings::class);
|
|
$homepage = app(HomepageSettings::class);
|
|
$socialMedia = app(SocialMediaSettings::class);
|
|
$hr = app(HRSettings::class);
|
|
|
|
expect($system->app_name)->toBe('DST Updated');
|
|
expect($homepage->hero_image_url)->toBe('homepage/hero/2026/07/31/new-hero.jpg');
|
|
expect($socialMedia->instagram_url)->toBe('https://instagram.com/newdst');
|
|
expect($hr->scheduled_check_in_time)->toBe('08:30');
|
|
expect($hr->late_penalty_amount)->toBe(15000);
|
|
});
|