Contributing
Testing
Externa uses Pest 4 with the Laravel and browser plugins. Feature tests refresh the database; prefer focused suites that mirror the domain folders under tests/Feature.
Layout
tests/
Pest.php # binds TestCase + RefreshDatabase; grantAiPermissions()
TestCase.php
Unit/ # no RefreshDatabase by default
Feature/
Admin/ # users, groups, roles, files, activity
Ai/ # assistant, tools, webhook, attachments
Auth/ # Fortify / verification flows
Authorization/ # permission sync, super-admin gate, resolver
Collections/ # collections, fields, items, normalizer
Settings/ # account / profile settings
DashboardTest.php
NotificationsTest.php
CreateSuperAdminSeederTest.php
…
Browser/ # pest-plugin-browser (Playwright)
FileManagerBrowserTest.php
Configured in tests/Pest.php:
| Suite | Base | Traits |
|---|---|---|
Feature | Tests\TestCase | RefreshDatabase |
Browser | Tests\TestCase | RefreshDatabase |
Unit | Tests\TestCase | (none) |
Domain folders (what lives where)
| Folder | Covers |
|---|---|
Feature/Admin | User/group/role/permission management, file manager JSON, zip/duplicate async, thumbnails, activity log UI |
Feature/Api | Public CMS API (/api/v1) — public role grants, API keys, IP allowlist, per-action deny/allow |
Feature/Ai | Chat, attachments, tool permission matrix, import remote JSON, sync/webhook phases, multi-role smoke |
Feature/Collections | Collection CRUD, item data normalizer, management flows |
Feature/Auth | Login, registration, verification, 2FA-related behaviour |
Feature/Authorization | permissions:sync, effective permissions, Gate::before super-admin |
Feature/Settings | Profile / password / appearance settings pages |
Browser | Real browser interactions (file manager drive-like UX) |
Browser plugin notes
Dependencies: pestphp/pest-plugin-browser and playwright (npm).
- Browser tests live under
tests/Browser/, notFeature/. - They use the same factories/seeders patterns as feature tests (e.g. seed
PermissionSeeder, grant a disposable role with exact permission names). - Require a working Playwright install (
npx playwright installif browsers are missing). - Slower than HTTP feature tests — keep them for interactions that need real DOM/Inertia behaviour (drag/select, panels), not for every CRUD path.
Example helper pattern (see FileManagerBrowserTest.php): create a unique role per test, syncPermissions, assign to the user, then drive the UI.
How to run tests
From externa-core:
# Full gate used in CI-style scripts (clears config, Pint --test, then Pest)
composer test
# Direct Pest / Artisan
php artisan test
php artisan test --filter=FileManagerTest
php artisan test tests/Feature/Ai
php artisan test tests/Browser
# Frontend static checks (also part of composer ci:check)
npm run lint:check
npm run format:check
npm run types:check
composer test runs:
php artisan config:clearpint --parallel --testphp artisan test
PHP version
The app targets PHP 8.3+ (installation docs often recommend 8.4). Run tests with the same CLI PHP your Herd/FPM stack uses so platform checks match Composer.
Helpers
grantAiPermissions(User $user, array $permissions): User in tests/Pest.php creates a unique role, syncs the given permission names, and assigns it — safe for parallel/isolation-sensitive AI tests.
What to cover for new features
| Change | Minimum tests |
|---|---|
| New permission | Sync includes it; route/FormRequest 403 without it and 200/allowed with it; UI gate optional via browser or Inertia assert |
| New admin/files route | Feature test for happy path + permission denial; async path asserts job dispatched / worker-friendly outcome |
| New collections behaviour | Feature test for store/update/validation; normalizer cases for new field shapes |
| New field type | Normalizer + validation rules; create field via HTTP or AI tool; UI smoke if non-trivial |
| New AI tool | Permission denied string; success JSON with "ok": true; register presence in AppAssistant for a user who has the gate permissions |
| Webhook / import | Token rejection (empty/wrong); successful import summary; throttle not required in unit tests |
| Seeder / config defaults | Assert super-admin email/password from config/super_admin.php when touching seeders |
| Browser-only UX | One Browser test for the interaction that HTTP tests cannot express |
Prefer one focused Pest file next to the domain folder over a giant catch-all. Reuse factories and seed PermissionSeeder (or full DatabaseSeeder) instead of hand-inserting permission rows.
Related
- Contributing — lint/format before push
- Extending Externa — end-to-end checklists that include tests
- Operations — queue/scheduler needed when testing async jobs locally (
composer run devor a separatequeue:listen) - Post-v1 polish smoke — optional signed-off Pest/Playwright checklist (internal)