feat: add admin email configuration to app settings
delete: remove outdated Spatie media library decision document docs: create new decision document for Spatie media library usage docs: add testing strategy document for Pest4 and Dusk test: implement login tests with Dusk for user authentication chore: add .gitignore files for console, screenshots, and source directories
This commit is contained in:
parent
aa39707e10
commit
ff5ab3aa58
14 changed files with 189 additions and 1491 deletions
1
.github/copilot-instructions.md
vendored
1
.github/copilot-instructions.md
vendored
|
|
@ -15,6 +15,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||
- laravel/prompts (PROMPTS) - v0
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
- livewire/livewire (LIVEWIRE) - v3
|
||||
- laravel/dusk (DUSK) - v8
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -23,3 +23,4 @@ yarn-error.log
|
|||
/.zed
|
||||
.vite
|
||||
*.deleted
|
||||
.env.dusk.local
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ This application is a Laravel application and its main Laravel ecosystems packag
|
|||
- laravel/prompts (PROMPTS) - v0
|
||||
- livewire/flux (FLUXUI_FREE) - v2
|
||||
- livewire/livewire (LIVEWIRE) - v3
|
||||
- laravel/dusk (DUSK) - v8
|
||||
- laravel/mcp (MCP) - v0
|
||||
- laravel/pint (PINT) - v1
|
||||
- laravel/sail (SAIL) - v1
|
||||
|
|
|
|||
|
|
@ -3,13 +3,18 @@
|
|||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Filament\Panel;
|
||||
use Illuminate\Container\Attributes\Log;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Log as FacadesLog;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
|
||||
class User extends Authenticatable
|
||||
class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable, TwoFactorAuthenticatable;
|
||||
|
|
@ -61,4 +66,14 @@ class User extends Authenticatable
|
|||
->map(fn ($word) => Str::substr($word, 0, 1))
|
||||
->implode('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user can access Filament admin panel.
|
||||
*/
|
||||
public function canAccessPanel(Panel $panel): bool
|
||||
{
|
||||
// FacadesLog::info('Checking admin access for user: ' . $this->email . ' against admin email: ' . config('app.admin_email'));
|
||||
|
||||
return $this->email === config('app.admin_email');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/boost": "^1.8",
|
||||
"laravel/dusk": "^8.3",
|
||||
"laravel/pail": "^1.2.2",
|
||||
"laravel/pint": "^1.24",
|
||||
"laravel/sail": "^1.41",
|
||||
|
|
|
|||
1481
composer.lock
generated
1481
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -123,4 +123,6 @@ return [
|
|||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
'admin_email' => env('ADMIN_EMAIL', ''),
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
# 2026-01-02
|
||||
|
||||
```bash
|
||||
php artisan storage:link
|
||||
composer require "spatie/laravel-medialibrary"
|
||||
composer require filament/spatie-laravel-media-library-plugin:"^4.0" -W
|
||||
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-config"
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
24
docs/decisions/004-spatie-media-library.md
Normal file
24
docs/decisions/004-spatie-media-library.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# 2026-01-02
|
||||
|
||||
```bash
|
||||
php artisan storage:link
|
||||
composer require "spatie/laravel-medialibrary"
|
||||
composer require filament/spatie-laravel-media-library-plugin:"^4.0" -W
|
||||
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-config"
|
||||
php artisan migrate
|
||||
```
|
||||
I had a lot of fun and silly games working out if I even needed both Spatie media library and the filament plugin they have as it states itself a sub directory of filament itself.
|
||||
|
||||
I managed to remove the spatie fulament plugin and for the app to work for a while but eventually found a part of it not implemented in filament core. It took a while but there seems to be an overlap somewhere. So I re-installed the spatie filament module to find all the errors went away, when previously they did not
|
||||
|
||||
PHP and I guess composer would seem to aggressively cache things very very hard and I just didnt understand how very hard that is sometimes.
|
||||
|
||||
Suffice to say, spatie media library seems to be 'the way' as far as media is concerned for filament.
|
||||
|
||||
I looked at curator, also by the author of the tiptap filament integration to find it seems dependant on tailwind3, limiting me to Laravel 11 I believe, which was not what I wanted.
|
||||
|
||||
I used filament 'actions' in order to augment the form for 'entries' so as to add 'pick from gallery' functionality together with custom javascript to insert records found in media library for both rich content and featured images
|
||||
|
||||
There may be a better way of doing this but for now, I believe this is a solution I have craved for a while now with filament and have at least gained a bit better understanding of so called 'actions' and will re-visit this whole section in the docs.
|
||||
|
||||
|
||||
38
docs/decisions/005-testing.md
Normal file
38
docs/decisions/005-testing.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# 2026-01-05
|
||||
|
||||
I would very much like to start using Pest4 as it has a lot of nice things in it like browser testing with some of the latest screen shot visual testing and more
|
||||
|
||||
for now I know Dusk and have been able to use this in previous projects before Pest4
|
||||
|
||||
so initially at least I intend to use Dusk and build on to migrate to Pest4 where I can
|
||||
|
||||
# test isolation and setup
|
||||
|
||||
in the past I now realize, I was introducing brittle tests by first doing setup to then rely on a configuration for further tests
|
||||
|
||||
I think this is a pattern that some follow but as a generalist, devops and hybrid person I can and do make mistakes. I suppose we all do.
|
||||
|
||||
Mine was to not fully embrace test isolation
|
||||
|
||||
Each test therefor needs to be set up from scratch so to speak
|
||||
|
||||
I then have each subsequent test re-setting up say, a user, a database table, whatever is required but each time from a blank slate
|
||||
|
||||
That way, when a test fails, it should be obvious what has been broken without having to traverse through a bunch of setup and dependencies to find I broke a setup step, not a test.
|
||||
|
||||
Let us hope so anyway. That is the approach I am going with with Dusk for now. Lets see how I get on and if I get the setup code as non repeated by abstraction as I can
|
||||
|
||||
I think testing and particulary automated testing is essential for success.
|
||||
|
||||
It has been disappointing to find in my time working with others in the past that there seems resistance to testing and to automation of testing for reasons I believe not technical but that which time does not allow for me to digress
|
||||
|
||||
## confusing config
|
||||
|
||||
In config files: Just use the key name without any prefix
|
||||
|
||||
In your code: Use config('filename.keyname')
|
||||
|
||||
The filename becomes the first part automatically - you never type it inside the config file itself.
|
||||
|
||||
|
||||
|
||||
98
tests/Browser/LoginTest.php
Normal file
98
tests/Browser/LoginTest.php
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Browser;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseTruncation;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class LoginTest extends DuskTestCase
|
||||
{
|
||||
use DatabaseTruncation;
|
||||
|
||||
/**
|
||||
* Dusk test panel.
|
||||
*/
|
||||
private function createTestUser(string $email): User
|
||||
{
|
||||
return User::factory()->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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
2
tests/Browser/console/.gitignore
vendored
Normal file
2
tests/Browser/console/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
2
tests/Browser/screenshots/.gitignore
vendored
Normal file
2
tests/Browser/screenshots/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
2
tests/Browser/source/.gitignore
vendored
Normal file
2
tests/Browser/source/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
Loading…
Add table
Add a link
Reference in a new issue