diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a715f25..0077032 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -44,6 +44,13 @@ This application is a Laravel application and its main Laravel ecosystems packag ## Documentation Files - You must only create documentation files if explicitly requested by the user. +## Testing Approach - CRITICAL +- **THE APPLICATION IS 100% WORKING** - All functionality works perfectly in production. +- When writing or debugging browser tests (Laravel Dusk), focus ONLY on test syntax, selectors, and Dusk interaction methods. +- NEVER assume the application has bugs or suggest app fixes - the issue is always in the test code. +- Trust the existing functionality and work on getting the correct CSS selectors, XPath expressions, and Dusk methods. +- If manual interaction works but the test fails, the problem is the test implementation, not the app. + === boost rules === diff --git a/app/Filament/Resources/Entries/Schemas/EntryForm.php b/app/Filament/Resources/Entries/Schemas/EntryForm.php index 6c029f6..b7bdf36 100644 --- a/app/Filament/Resources/Entries/Schemas/EntryForm.php +++ b/app/Filament/Resources/Entries/Schemas/EntryForm.php @@ -43,8 +43,9 @@ class EntryForm ->dehydrated(false) ->hintAction( Action::make('featured_picker') - ->label('Pick from Gallery') + ->label('Featured Image from Gallery') ->icon('heroicon-m-photo') + ->extraAttributes(['id' => 'featured-picker-button']) ->schema([ Select::make('image_id') ->label('Select an existing image') diff --git a/tests/Browser/001_LoginDashAdminTest.php b/tests/Browser/001_LoginDashAdminTest.php new file mode 100644 index 0000000..b5d2bc3 --- /dev/null +++ b/tests/Browser/001_LoginDashAdminTest.php @@ -0,0 +1,60 @@ +createTestUser("login-test@example.com"); + + $this->browse(function (Browser $browser) use ($user) { + $this->loginUser($browser, $user); + $this->assertWithDebugPause($browser, fn($b) => + $b->assertPathIs('/dashboard'), + 1000 // Custom pause time for this test + ); + }); + } + + public function test_invalid_login(): void + { + $user = $this->createTestUser("invalid-email@example.com"); + + $this->browse(function (Browser $browser) use ($user) { + $this->loginUser($browser, $user); + $this->assertWithDebugPause($browser, fn($b) => + $b->visit('/admin') + ->waitForLocation('/admin') + ->assertPathIs('/admin') + ->assertSee('FORBIDDEN'), + 1000 // Custom pause time for this test + ); + }); + } + + public function test_access_admin_panel(): void + { + $user = $this->createTestUser("login-test@example.com"); + + $this->browse(function (Browser $browser) use ($user) { + $this->loginUser($browser, $user); + $this->assertWithDebugPause($browser, fn($b) => + $b->visit('/admin') + ->waitForLocation('/admin') + ->assertPathIs('/admin') + ->assertTitleContains('Dashboard') + ->assertDontSee('FORBIDDEN'), + 1000 // Custom pause time for this test + ); + }); + } +} \ No newline at end of file diff --git a/tests/Browser/002_UploadImageAdminTest.php b/tests/Browser/002_UploadImageAdminTest.php new file mode 100644 index 0000000..915078c --- /dev/null +++ b/tests/Browser/002_UploadImageAdminTest.php @@ -0,0 +1,46 @@ +createTestUser("login-test@example.com"); + + $filePath = base_path('tests/Browser/fixtures/robot.webp'); + + $this->browse(function (Browser $browser ) use ($user, $filePath) { + $this->loginUser($browser, $user); + $this->assertWithDebugPause( + $browser, + fn($b) => + $b->visit('/admin/media') + ->waitForLocation('/admin/media') + ->assertPathIs('/admin/media') + ->assertTitleContains('Media') + ->clickLink('New media') + ->waitForText('Create Media') + ->type('#form\\.name', 'test image') + ->assertVisible('.filepond--drop-label') + ->attach('.filepond--browser', $filePath) + ->pause(7000) + ->waitForText('Create') + ->waitFor('#key-bindings-1:not([disabled])') + ->click('#key-bindings-1') + ->assertSee('Collection name'), + 1000 // Custom pause time for this test + ); + }); + } +} diff --git a/tests/Browser/003_CreateEntryAdminTest.php b/tests/Browser/003_CreateEntryAdminTest.php new file mode 100644 index 0000000..dc05e3b --- /dev/null +++ b/tests/Browser/003_CreateEntryAdminTest.php @@ -0,0 +1,74 @@ +createTestUser("login-test@example.com"); + + $filePath = base_path('tests/Browser/fixtures/robot.webp'); + + $this->browse(function (Browser $browser) use ($user, $filePath) { + $this->loginUser($browser, $user); + $this->assertWithDebugPause( + $browser, + fn($b) => + $b->visit('/admin/media') + ->waitForLocation('/admin/media') + ->assertPathIs('/admin/media') + ->assertTitleContains('Media') + ->clickLink('New media') + ->waitForText('Create Media') + ->type('#form\\.name', 'test image') + ->assertVisible('.filepond--drop-label') + ->attach('.filepond--browser', $filePath) + ->pause(7000) + ->waitForText('Create') + ->waitFor('#key-bindings-1:not([disabled])') + ->click('#key-bindings-1') + ->assertSee('Collection name') + ->pause(5000) + + ->visit('/admin/entries') + ->waitForLocation('/admin/entries') + ->assertPathIs('/admin/entries') + ->assertTitleContains('Entries') + ->clickLink('New entry') + ->waitForText('Create Entry') + ->type('#form\\.title', 'TEST ENTRY') + ->keys('#form\\.title', '{tab}') + ->waitForText('Create') + + ->click('#key-bindings-1') + ->waitForText('Updated at') + ->assertSee('Updated at') + ->visit('/admin/entries/1/edit') + ->waitForText('Edit TEST ENTRY') + ->pause(2000) + ->waitForText('Featured Image') + ->click('#featured-picker-button') + ->waitForText('Select an existing image') + ->click('.fi-select-input-btn') + ->pause(2000) + ->click('li:first-child') + ->waitForText('Submit') + ->clickAtXPath('//button[contains(., "Submit")]') + + ->waitForText('Edit TEST ENTRY') + ->click('#key-bindings-1'), + // ->pause(20000), + 1000 // Custom pause time for this test + ); + }); + } +} diff --git a/tests/Browser/Concerns/AuthenticatesUsers.php b/tests/Browser/Concerns/AuthenticatesUsers.php new file mode 100644 index 0000000..640fbd0 --- /dev/null +++ b/tests/Browser/Concerns/AuthenticatesUsers.php @@ -0,0 +1,38 @@ +create([ + 'email' => $email, + 'password' => bcrypt('password'), + 'two_factor_secret' => null, + 'two_factor_recovery_codes' => null, + ]); + } + + private function loginUser(Browser $browser, User $user): void + { + $browser->visit('/login') + ->type('email', $user->email) + ->type('password', 'password') + ->press('Log in') + ->waitForLocation('/dashboard'); + } + + private function assertWithDebugPause(Browser $browser, callable $assertions, int $pauseMs = 10000): void + { + try { + $assertions($browser); + } catch (\Exception $e) { + $browser->pause($pauseMs); + throw $e; + } + } +} \ No newline at end of file diff --git a/tests/Browser/LoginTest.php b/tests/Browser/LoginTest.php deleted file mode 100644 index eecd256..0000000 --- a/tests/Browser/LoginTest.php +++ /dev/null @@ -1,98 +0,0 @@ -create([ - 'email' => $email, - 'password' => bcrypt('password'), - 'two_factor_secret' => null, - 'two_factor_recovery_codes' => null, - ]); - } - - private function loginUser(Browser $browser, User $user): void - { - $browser->visit('/login') - ->type('email', $user->email) - ->type('password', 'password') - ->press('Log in') - ->waitForLocation('/dashboard'); - } - - public function test_login(): void - { - $user = $this->createTestUser("login-test@example.com"); - - $this->browse(function (Browser $browser) use ($user) { - $this->loginUser($browser, $user); - try { - $browser->assertPathIs('/dashboard'); // Or wherever successful login redirects - } catch (\Exception $e) { - $browser->pause(10000); // Pause for 10 seconds on failure to debug - throw $e; - } - }); - } - - /* - public function test_invalid_login(): void - { - $user = $this->createTestUser("invalid-email@example.com"); - - - $this->browse(function (Browser $browser) use ($user) { - $this->loginUser($browser, $user); - try { - $browser->visit('/admin') - ->waitForLocation('/admin') - ->assertPathIs('/admin') - ->assertTitleContains('Dashboard') - ->assertDontSee('Forbidden') - ->pause(1000); - } catch (\Exception $e) { - $browser->pause(1000); // Pause for 1 second on failure to debug - throw $e; - } - }); - - - - - } -*/ - - public function test_access_admin_panel(): void - { - - $user = $this->createTestUser("login-test@example.com"); - - $this->browse(function (Browser $browser) use ($user) { - $this->loginUser($browser, $user); - try { - $browser->visit('/admin') - ->waitForLocation('/admin') - ->assertPathIs('/admin') - ->assertTitleContains('Dashboard') - ->assertDontSee('Forbidden') - ->pause(1000); - } catch (\Exception $e) { - $browser->pause(1000); // Pause for 1 second on failure to debug - throw $e; - } - }); - } -} diff --git a/tests/Browser/fixtures/robot.webp b/tests/Browser/fixtures/robot.webp new file mode 100644 index 0000000..3dd400b Binary files /dev/null and b/tests/Browser/fixtures/robot.webp differ diff --git a/tests/DuskTestCase.php b/tests/DuskTestCase.php new file mode 100644 index 0000000..020699d --- /dev/null +++ b/tests/DuskTestCase.php @@ -0,0 +1,48 @@ +addArguments(collect([ + $this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080', + '--disable-search-engine-choice-screen', + '--disable-smooth-scrolling', + ])->unless($this->hasHeadlessDisabled(), function (Collection $items) { + return $items->merge([ + '--disable-gpu', + '--headless=new', + ]); + })->all()); + + return RemoteWebDriver::create( + $_ENV['DUSK_DRIVER_URL'] ?? env('DUSK_DRIVER_URL') ?? 'http://localhost:9515', + DesiredCapabilities::chrome()->setCapability( + ChromeOptions::CAPABILITY, $options + ) + ); + } +} diff --git a/tests/Pest.php b/tests/Pest.php index b3a25b7..59833e1 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,9 @@ extend(Tests\DuskTestCase::class) +// ->use(Illuminate\Foundation\Testing\DatabaseMigrations::class) + ->in('Browser'); + /* |-------------------------------------------------------------------------- | Test Case