diff --git a/tests/Pest.php b/tests/Pest.php index cec77b86f..67bcbda81 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -31,6 +31,15 @@ beforeEach(function () { Server::flushIdentityMap(); }); +function loginAndSkipBoarding(string $email = 'test@example.com', string $password = 'password'): mixed +{ + return visit('/login') + ->fill('email', $email) + ->fill('password', $password) + ->click('Login') + ->click('Skip Setup'); +} + /* |-------------------------------------------------------------------------- | Expectations diff --git a/tests/Unit/Policies/GithubAppPolicyTest.php b/tests/Unit/Policies/GithubAppPolicyTest.php index 55ba7f3d3..5aedbf7b8 100644 --- a/tests/Unit/Policies/GithubAppPolicyTest.php +++ b/tests/Unit/Policies/GithubAppPolicyTest.php @@ -1,5 +1,6 @@ makePartial(); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = true; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: true); $policy = new GithubAppPolicy; expect($policy->view($user, $model))->toBeTrue(); @@ -32,12 +28,7 @@ it('allows team member to view non-system-wide github app', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('getAttribute')->with('teams')->andReturn($teams); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->view($user, $model))->toBeTrue(); @@ -51,12 +42,7 @@ it('denies non-team member to view non-system-wide github app', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('getAttribute')->with('teams')->andReturn($teams); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->view($user, $model))->toBeFalse(); @@ -82,12 +68,7 @@ it('allows user with system access to update system-wide github app', function ( $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('canAccessSystemResources')->andReturn(true); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = true; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: true); $policy = new GithubAppPolicy; expect($policy->update($user, $model))->toBeTrue(); @@ -97,12 +78,7 @@ it('denies user without system access to update system-wide github app', functio $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('canAccessSystemResources')->andReturn(false); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = true; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: true); $policy = new GithubAppPolicy; expect($policy->update($user, $model))->toBeFalse(); @@ -112,12 +88,7 @@ it('allows team admin to update non-system-wide github app', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->update($user, $model))->toBeTrue(); @@ -127,12 +98,7 @@ it('denies team member to update non-system-wide github app', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->update($user, $model))->toBeFalse(); @@ -142,12 +108,7 @@ it('allows user with system access to delete system-wide github app', function ( $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('canAccessSystemResources')->andReturn(true); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = true; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: true); $policy = new GithubAppPolicy; expect($policy->delete($user, $model))->toBeTrue(); @@ -157,12 +118,7 @@ it('denies user without system access to delete system-wide github app', functio $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('canAccessSystemResources')->andReturn(false); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = true; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: true); $policy = new GithubAppPolicy; expect($policy->delete($user, $model))->toBeFalse(); @@ -172,12 +128,7 @@ it('allows team admin to delete non-system-wide github app', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->delete($user, $model))->toBeTrue(); @@ -187,12 +138,7 @@ it('denies team member to delete non-system-wide github app', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->delete($user, $model))->toBeFalse(); @@ -201,12 +147,7 @@ it('denies team member to delete non-system-wide github app', function () { it('denies restore of github app', function () { $user = Mockery::mock(User::class)->makePartial(); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->restore($user, $model))->toBeFalse(); @@ -215,13 +156,17 @@ it('denies restore of github app', function () { it('denies force delete of github app', function () { $user = Mockery::mock(User::class)->makePartial(); - $model = new class - { - public $team_id = 1; - - public $is_system_wide = false; - }; + $model = mockGithubApp(teamId: 1, isSystemWide: false); $policy = new GithubAppPolicy; expect($policy->forceDelete($user, $model))->toBeFalse(); }); + +function mockGithubApp(int $teamId, bool $isSystemWide): GithubApp +{ + $githubApp = Mockery::mock(GithubApp::class)->makePartial(); + $githubApp->team_id = $teamId; + $githubApp->is_system_wide = $isSystemWide; + + return $githubApp; +} diff --git a/tests/Unit/Policies/SharedEnvironmentVariablePolicyTest.php b/tests/Unit/Policies/SharedEnvironmentVariablePolicyTest.php index f993978f9..d2a287a51 100644 --- a/tests/Unit/Policies/SharedEnvironmentVariablePolicyTest.php +++ b/tests/Unit/Policies/SharedEnvironmentVariablePolicyTest.php @@ -1,5 +1,6 @@ makePartial(); $user->shouldReceive('getAttribute')->with('teams')->andReturn($teams); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->view($user, $model))->toBeTrue(); @@ -35,10 +33,7 @@ it('denies non-team member to view shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('getAttribute')->with('teams')->andReturn($teams); - $model = new class - { - public $team_id = 2; - }; + $model = mockSharedEnvironmentVariable(teamId: 2); $policy = new SharedEnvironmentVariablePolicy; expect($policy->view($user, $model))->toBeFalse(); @@ -64,10 +59,7 @@ it('allows team admin to update shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->update($user, $model))->toBeTrue(); @@ -77,10 +69,7 @@ it('denies team member to update shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->update($user, $model))->toBeFalse(); @@ -90,10 +79,7 @@ it('allows team admin to delete shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->delete($user, $model))->toBeTrue(); @@ -103,10 +89,7 @@ it('denies team member to delete shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->delete($user, $model))->toBeFalse(); @@ -115,10 +98,7 @@ it('denies team member to delete shared environment variable', function () { it('denies restore of shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->restore($user, $model))->toBeFalse(); @@ -127,10 +107,7 @@ it('denies restore of shared environment variable', function () { it('denies force delete of shared environment variable', function () { $user = Mockery::mock(User::class)->makePartial(); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->forceDelete($user, $model))->toBeFalse(); @@ -140,10 +117,7 @@ it('allows team admin to manage environment', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(true); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->manageEnvironment($user, $model))->toBeTrue(); @@ -153,11 +127,16 @@ it('denies team member to manage environment', function () { $user = Mockery::mock(User::class)->makePartial(); $user->shouldReceive('isAdminOfTeam')->with(1)->andReturn(false); - $model = new class - { - public $team_id = 1; - }; + $model = mockSharedEnvironmentVariable(teamId: 1); $policy = new SharedEnvironmentVariablePolicy; expect($policy->manageEnvironment($user, $model))->toBeFalse(); }); + +function mockSharedEnvironmentVariable(int $teamId): SharedEnvironmentVariable +{ + $sharedEnvironmentVariable = Mockery::mock(SharedEnvironmentVariable::class)->makePartial(); + $sharedEnvironmentVariable->team_id = $teamId; + + return $sharedEnvironmentVariable; +} diff --git a/tests/v4/Browser/AuthorizationTest.php b/tests/v4/Browser/AuthorizationTest.php index 0f708687d..50751e431 100644 --- a/tests/v4/Browser/AuthorizationTest.php +++ b/tests/v4/Browser/AuthorizationTest.php @@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Hash; uses(RefreshDatabase::class); beforeEach(function () { - InstanceSettings::create(['id' => 0]); + InstanceSettings::create(['id' => 0, 'is_sponsorship_popup_enabled' => false]); // Create root/owner user $this->user = User::factory()->create([ @@ -96,15 +96,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== $this->member->teams()->attach(0, ['role' => 'member']); }); -function loginAndSkipOnboarding(): mixed -{ - return visit('/login') - ->fill('email', 'test@example.com') - ->fill('password', 'password') - ->click('Login') - ->click('Skip Setup'); -} - function loginAsMember(): mixed { return visit('/login') @@ -121,7 +112,7 @@ it('redirects unauthenticated users to login', function () { }); it('shows dashboard after successful login and onboarding skip', function () { - $page = loginAndSkipOnboarding(); + $page = loginAndSkipBoarding(); $page->assertSee('Dashboard') ->assertSee('Your self-hosted infrastructure') @@ -129,7 +120,7 @@ it('shows dashboard after successful login and onboarding skip', function () { }); it('displays all projects on dashboard', function () { - $page = loginAndSkipOnboarding(); + $page = loginAndSkipBoarding(); $page->assertSee('Projects') ->assertSee('My first project') @@ -140,7 +131,7 @@ it('displays all projects on dashboard', function () { }); it('displays all servers on dashboard', function () { - $page = loginAndSkipOnboarding(); + $page = loginAndSkipBoarding(); $page->assertSee('Servers') ->assertSee('localhost') @@ -151,7 +142,7 @@ it('displays all servers on dashboard', function () { }); it('allows authenticated users to access team settings', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); $page = visit('/team'); @@ -161,7 +152,7 @@ it('allows authenticated users to access team settings', function () { }); it('shows danger zone to team owner', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); $page = visit('/team'); @@ -194,7 +185,7 @@ it('prevents unauthenticated access to project show page', function () { }); it('authenticated user can navigate to server details', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); // Navigate to server show page using UUID $server = Server::first(); @@ -206,7 +197,7 @@ it('authenticated user can navigate to server details', function () { }); it('authenticated user can navigate to project details', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); // Navigate to project show page using UUID $project = Project::first(); @@ -225,7 +216,7 @@ it('prevents unauthenticated access to team members page', function () { }); it('authenticated user can access team members page', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); $page = visit('/team/members'); @@ -300,7 +291,7 @@ it('member does not see proxy controls on server page', function () { }); it('owner sees terminal and security links on server page', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); $server = Server::first(); $page = visit("/server/{$server->uuid}"); @@ -341,7 +332,7 @@ it('member does not see environment settings link on project page', function () }); it('owner sees add environment and settings on project page', function () { - loginAndSkipOnboarding(); + loginAndSkipBoarding(); $project = Project::where('uuid', 'project-1')->first(); $page = visit("/project/{$project->uuid}"); diff --git a/tests/v4/Browser/DashboardTest.php b/tests/v4/Browser/DashboardTest.php index b4a97f268..d50fceb0b 100644 --- a/tests/v4/Browser/DashboardTest.php +++ b/tests/v4/Browser/DashboardTest.php @@ -13,7 +13,7 @@ use Illuminate\Support\Facades\Hash; uses(RefreshDatabase::class); beforeEach(function () { - InstanceSettings::create(['id' => 0]); + InstanceSettings::create(['id' => 0, 'is_sponsorship_popup_enabled' => false]); $this->user = User::factory()->create([ 'id' => 0, @@ -99,15 +99,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== ]); }); -function loginAndSkipOnboarding(): mixed -{ - return visit('/login') - ->fill('email', 'test@example.com') - ->fill('password', 'password') - ->click('Login') - ->click('Skip Setup'); -} - it('redirects to login when not authenticated', function () { $page = visit('/'); @@ -128,7 +119,7 @@ it('shows onboarding after first login', function () { }); it('shows dashboard after skipping onboarding', function () { - $page = loginAndSkipOnboarding(); + $page = loginAndSkipBoarding(); $page->assertSee('Dashboard') ->assertSee('Your self-hosted infrastructure.') @@ -136,7 +127,7 @@ it('shows dashboard after skipping onboarding', function () { }); it('shows all projects on dashboard', function () { - $page = loginAndSkipOnboarding(); + $page = loginAndSkipBoarding(); $page->assertSee('Projects') ->assertSee('My first project') @@ -149,7 +140,7 @@ it('shows all projects on dashboard', function () { }); it('shows servers on dashboard', function () { - $page = loginAndSkipOnboarding(); + $page = loginAndSkipBoarding(); $page->assertSee('Servers') ->assertSee('localhost') diff --git a/tests/v4/Browser/LoginTest.php b/tests/v4/Browser/LoginTest.php index 7666e07e2..716acd20a 100644 --- a/tests/v4/Browser/LoginTest.php +++ b/tests/v4/Browser/LoginTest.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Hash; uses(RefreshDatabase::class); beforeEach(function () { - InstanceSettings::create(['id' => 0]); + InstanceSettings::create(['id' => 0, 'is_sponsorship_popup_enabled' => false]); }); it('shows registration page when no users exist', function () { diff --git a/tests/v4/Browser/RegistrationTest.php b/tests/v4/Browser/RegistrationTest.php index e2a232357..42a871257 100644 --- a/tests/v4/Browser/RegistrationTest.php +++ b/tests/v4/Browser/RegistrationTest.php @@ -7,7 +7,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase; uses(RefreshDatabase::class); beforeEach(function () { - InstanceSettings::create(['id' => 0]); + InstanceSettings::create(['id' => 0, 'is_sponsorship_popup_enabled' => false]); }); it('shows registration page when no users exist', function () { diff --git a/tests/v4/Browser/ResourceSettingsPersistenceTest.php b/tests/v4/Browser/ResourceSettingsPersistenceTest.php new file mode 100644 index 000000000..c451c7c04 --- /dev/null +++ b/tests/v4/Browser/ResourceSettingsPersistenceTest.php @@ -0,0 +1,226 @@ + 0, 'is_sponsorship_popup_enabled' => false]); + + $this->user = User::factory()->create([ + 'id' => 0, + 'name' => 'Root User', + 'email' => 'test@example.com', + 'password' => Hash::make('password'), + ]); + + PrivateKey::create([ + 'id' => 1, + 'uuid' => 'ssh-test', + 'team_id' => 0, + 'name' => 'Test Key', + 'description' => 'Test SSH key', + 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk +hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA +AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV +uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== +-----END OPENSSH PRIVATE KEY-----', + ]); + + $this->server = Server::create([ + 'id' => 0, + 'uuid' => 'localhost', + 'name' => 'localhost', + 'description' => 'Test docker container in development', + 'ip' => 'coolify-testing-host', + 'team_id' => 0, + 'private_key_id' => 1, + 'proxy' => [ + 'type' => ProxyTypes::TRAEFIK->value, + 'status' => ProxyStatus::EXITED->value, + ], + ]); + + $this->project = Project::create([ + 'uuid' => 'project-resource-persistence', + 'name' => 'Resource Persistence', + 'description' => 'Browser persistence tests', + 'team_id' => 0, + ]); + + $this->environment = $this->project->environments()->first(); + + StandaloneDocker::withoutEvents(function () { + $this->destination = StandaloneDocker::firstOrCreate( + ['server_id' => $this->server->id, 'network' => 'coolify'], + ['uuid' => 'docker-destination-1', 'name' => 'docker-destination-1'] + ); + }); + + $this->application = Application::factory()->create([ + 'uuid' => 'app-resource-persistence', + 'name' => 'App Before Browser Save', + 'git_repository' => 'https://github.com/coollabsio/coolify.git', + 'git_branch' => 'main', + 'build_pack' => 'nixpacks', + 'ports_exposes' => '3000', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + + $this->database = StandalonePostgresql::create([ + 'uuid' => 'db-resource-persistence', + 'name' => 'Database Before Browser Save', + 'description' => 'Initial database description', + 'postgres_user' => 'postgres', + 'postgres_password' => 'postgres-password', + 'postgres_db' => 'postgres', + 'image' => 'postgres:15-alpine', + 'status' => 'exited', + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); +}); + +it('saves application name and enables static site with nginx config', function () { + loginAndSkipBoarding(); + + $updatedName = 'App Saved '.(string) new Cuid2; + $applicationRoute = "/project/{$this->project->uuid}/environment/{$this->environment->uuid}/application/{$this->application->uuid}"; + + $page = visit($applicationRoute); + + dismissNotificationPrompt($page); + $page->screenshot(); + + $page->assertSee('General') + ->assertDontSee('Custom Nginx Configuration') + ->fill('name', $updatedName) + ->fill('customDockerRunOptions', '--read-only'); + + submitResourceForm($page); + dismissNotificationPrompt($page); + toggleCheckboxByWireModel($page, 'isStatic'); + $page->screenshot(); + + $page->assertSee('Custom Nginx Configuration') + ->assertSee('Is it a SPA (Single Page Application)?') + ->assertValue('name', $updatedName); + + $this->application->refresh(); + expect($this->application->name)->toBe($updatedName) + ->and($this->application->custom_docker_run_options)->toBe('--read-only') + ->and($this->application->settings->is_static)->toBeTrue(); + + $reloadedPage = visit($applicationRoute); + dismissNotificationPrompt($reloadedPage); + $reloadedPage->screenshot(); + + $reloadedPage->assertValue('name', $updatedName) + ->assertSee('Custom Nginx Configuration') + ->assertSee('Is it a SPA (Single Page Application)?'); +}); + +it('saves database name and enables ssl with mode selector', function () { + loginAndSkipBoarding(); + + $updatedDatabaseName = 'Database Saved '.(string) new Cuid2; + $databaseRoute = "/project/{$this->project->uuid}/environment/{$this->environment->uuid}/database/{$this->database->uuid}"; + + $page = visit($databaseRoute); + + dismissNotificationPrompt($page); + $page->screenshot(); + + $page->assertSee('General') + ->assertDontSee('SSL Mode') + ->fill('name', $updatedDatabaseName) + ->fill('description', 'Updated by browser test'); + + submitResourceForm($page); + dismissNotificationPrompt($page); + + toggleCheckboxByWireModel($page, 'enableSsl'); + + $page->assertSee('SSL Mode') + ->assertValue('name', $updatedDatabaseName); + $page->screenshot(); + + $this->database->refresh(); + expect($this->database->name)->toBe($updatedDatabaseName) + ->and($this->database->description)->toBe('Updated by browser test') + ->and($this->database->enable_ssl)->toBeTruthy(); + + $reloadedPage = visit($databaseRoute); + dismissNotificationPrompt($reloadedPage); + $reloadedPage->screenshot(); + + $reloadedPage->assertValue('name', $updatedDatabaseName) + ->assertSee('SSL Mode'); +}); + +function dismissNotificationPrompt($page): void +{ + $page->script(<<<'JS' + const closeButtonTexts = ['Accept and Close', 'Slice', 'Maybe next time']; + for (const text of closeButtonTexts) { + const closeButton = Array.from(document.querySelectorAll('button, a')) + .find((button) => button.textContent.includes(text)); + + if (! closeButton) { + continue; + } + + const modal = + closeButton.closest('.fixed') ?? + closeButton.closest('[role="dialog"]') ?? + closeButton.closest('div'); + + if (modal) { + modal.remove(); + } else { + closeButton.click(); + } + } + JS); +} + +function submitResourceForm($page): void +{ + $page->script(" + const form = document.querySelector('form[wire\\\\:submit=\"submit\"]'); + if (form) { + form.requestSubmit(); + } + "); + $page->wait(0.3); +} + +function toggleCheckboxByWireModel($page, string $wireModel): void +{ + $page->script(" + const checkbox = document.querySelector( + 'input[wire\\\\:model=\"{$wireModel}\"], input[wire\\\\:model\\\\.live=\"{$wireModel}\"]' + ); + if (checkbox) { + checkbox.click(); + } + "); + $page->wait(0.3); +}