From 74600823104ed2307526ac088019539e7b020981 Mon Sep 17 00:00:00 2001 From: Dave Mills Date: Wed, 29 Jul 2026 16:14:08 +0100 Subject: [PATCH 1/2] add plan --- docs/plans/MISSING_SMOKE_CRUD_TESTS.md | 114 +++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/plans/MISSING_SMOKE_CRUD_TESTS.md diff --git a/docs/plans/MISSING_SMOKE_CRUD_TESTS.md b/docs/plans/MISSING_SMOKE_CRUD_TESTS.md new file mode 100644 index 00000000..bc8f73b4 --- /dev/null +++ b/docs/plans/MISSING_SMOKE_CRUD_TESTS.md @@ -0,0 +1,114 @@ +# Missing Smoke & CRUD Tests + +**Status:** Not Started + +Follow-up to [SMOKE_TESTS.md](SMOKE_TESTS.md) and [CRUD_TESTS.md](CRUD_TESTS.md). Audit date 2026-07-29: all 77 existing tests in `tests/Feature/Smoke/` and `tests/Feature/Crud/` pass. This plan adds the tests from the two original plans that are still missing **and still feasible against the current codebase**, and fixes two vacuous tests found during the audit. + +--- + +## Audit summary — why most "missing" items are not in this plan + +The codebase has moved on since both plans were written. Items from the original plans that are now obsolete (no test to write): + +| Original plan item | Why obsolete | +|---|---| +| Smoke: `cover page loads`, `results page loads`, `temp results page loads` | `CoverPage`/`ResultsPage`/temp-results routes removed; `/` now redirects to `/app` (`routes/web.php:14`). Adapted test added below. | +| Smoke: `data-collection-index`, `lisp-index`, `lisp-indicators`, `lisp-workshop` pages | Page classes deleted; `Pages/Lisp/` now contains only `OptionalModules` (already tested). | +| Smoke: `domains`, `themes`, `global-indicators` admin lists | `DomainResource`/`ThemeResource`/`GlobalIndicatorResource` no longer exist anywhere in app or packages. | +| CRUD: Domain (7 tests), Theme (7), GlobalIndicator (7) | Same — resources removed. | +| CRUD: User create/edit (4 tests) | Package redesign: Admin `UserResource` is list-only with an "invite users" action. Adapted tests already exist in `AdminPanelCrudTest.php`. | +| CRUD: XlsformModule `can create` + `create requires name` | `getHeaderActions()` (CreateAction) is commented out in the package's `ManageXlsformModule.php` — no create action exists to test. | +| CRUD: Program panel ProgramResource CRUD (6 tests) | Program panel redesigned around the `ManageProgram` tenant-profile page; adapted tests already exist in `ProgramPanelCrudTest.php`. | + +After this plan completes, update the **Status** lines of both original plan docs to **Completed**, noting the obsolete items above, and link a shared change log. + +--- + +## File 1 — `tests/Feature/Smoke/PublicRoutesTest.php` (1 test) + +| Test | Implementation | +|---|---| +| `root path redirects to app login` | `$this->get('/')->assertRedirect('app')` — adapted from the plan's "cover page loads"; `/` is now a redirect (`routes/web.php:14`). | + +Optional (not in original plan, flagged during audit): the only routable App-panel page with no smoke test is `HddsHints` (`/app/{team}/hdds-hints`, navigation hidden but discovered/routed). Add `hdds hints page loads` to `AppPanelTest.php` if it mounts cleanly without survey data; skip with a comment if it requires a deployed xlsform. + +--- + +## File 2 — `tests/Feature/Crud/AdminPanelCrudTest.php` (4 tests + 2 fixes) + +Actor/setup: existing `createSuperAdmin()` + `withAdminPanel()` `beforeEach` blocks. + +### Dataset (2 tests) + +Form fields (package `Datasets/Schemas/DatasetForm.php`): `name` required, `description` required. The `custom_key`/`label` block noted as failing in [fix-admin-crud-test-imports.md](../change-logs/fix-admin-crud-test-imports.md) is now commented out in the package, so plain create/edit works. + +| Test | Implementation | +|---|---| +| `create dataset requires name` | `livewire(CreateDataset::class)->fillForm(['name' => '', 'description' => 'x'])->call('create')->assertHasFormErrors(['name' => 'required'])` | +| `can edit dataset` | `Dataset::forceCreate(['name' => 'Editable Dataset', 'primary_key' => 'id'])` (same as existing edit-page-loads test), then `livewire(EditDataset::class, ['record' => $dataset->id])->fillForm(['name' => 'Updated Dataset', 'description' => 'x'])->call('save')->assertHasNoFormErrors()` + `assertDatabaseHas` | + +### Program (1 test) + +| Test | Implementation | +|---|---| +| `program list links to program panel` | Create a `Program`, then assert the table record URL: `$table = livewire(ListPrograms::class)->instance()->getTable(); expect($table->getRecordUrl($program))->toBe(url('program/'.$program->id));` — the `recordUrl` closure is at `app/Filament/Admin/Resources/ProgramResource.php:25`. | + +### XlsformModule (1 test) + +Table (package `XlsformModules/Tables/XlsformModuleTable.php`) exposes `EditAction` (row) and `DeleteBulkAction` (grouped bulk) — no row DeleteAction, no create. + +| Test | Implementation | +|---|---| +| `can delete xlsform module` | Create module via `XlsformModule::forceCreate([...])` (template chain as in existing `beforeEach`), then `livewire(ManageXlsformModule::class)->callTableBulkAction(DeleteBulkAction::class, [$module])` + `assertDatabaseMissing('xlsform_modules', ...)` | + +### Fixes — vacuous tests in the `XlsformModuleVersion` describe block + +Two tests currently pass without testing anything: + +1. `can create xlsform module version` (second one, [AdminPanelCrudTest.php:224](../../tests/Feature/Crud/AdminPanelCrudTest.php#L224)) — fills module fields (`xlsform_template_id`, `label`) on the **version** create action, then asserts `assertDatabaseHas('xlsform_modules', ['name' => 'test_module'])`, which passes because `beforeEach` already created that exact module. Delete it — the first `can create default xlsform module version` test already covers version creation. +2. `can delete xlsform module` ([AdminPanelCrudTest.php:236](../../tests/Feature/Crud/AdminPanelCrudTest.php#L236)) — creates an `XlsformModuleVersion` but asserts `assertDatabaseMissing('xlsform_modules', ...)`, which passes vacuously (wrong table). Rework as `can delete xlsform module version`: assert against `xlsform_module_versions`, and verify the delete action actually exists on the version table (if not, use `callTableBulkAction`). + +--- + +## File 3 — `tests/Feature/Crud/AppPanelCrudTest.php` (4 tests) + +### Farm (1 test) + +`FarmResource` table has `EditAction` (row) + `DeleteBulkAction` (toolbar) — plan's row-delete adapted to bulk delete. No `Farm` factory exists; build the parent chain manually: + +```php +$level = new LocationLevel(); $level->name = 'Village'; $level->owner_id = $this->team->id; $level->save(); +$location = Location::create(['location_level_id' => $level->id, 'name' => 'Test Village', 'code' => 'tv1']); // verify Location columns at implementation time +$farm = Farm::create(['owner_id' => $this->team->id, 'location_id' => $location->id, 'team_code' => 'farm-001']); +``` + +| Test | Implementation | +|---|---| +| `can bulk delete farm` | `withAppTenant($this->team)` + `livewire(ListFarms::class)->callTableBulkAction(DeleteBulkAction::class, [$farm])` + `assertDatabaseMissing('farms', ['id' => $farm->id])` | + +### ChoiceListEntry (3 tests) + +Existing `beforeEach` already builds the Template → Module → ModuleVersion → `smoke_test_list` ChoiceList chain. `ListChoiceListEntries::mount()` falls back to the first localisable choice list when `choiceListName` is not passed, so plain `livewire(ListChoiceListEntries::class)` resolves to `smoke_test_list`. + +Form ([ChoiceListEntryResource.php:117](../../app/Filament/App/Clusters/Localisations/Resources/ChoiceListEntryResource.php#L117)): `name` required, plus required per-locale `text` inputs generated from the survey's languages. In tests with no languages configured the repeater block should be empty — verify at implementation time; if a locale row is required, seed one `Locale` first. + +Edit/Delete row actions are visible only when `!$record->is_global_entry` — create the target entry with `is_global_entry => false` (team-owned). + +| Test | Implementation | +|---|---| +| `can create choice list entry via table action` | `livewire(ListChoiceListEntries::class)->callAction(CreateAction::class, data: ['name' => 'new_entry', ...])->assertHasNoActionErrors()` + `assertDatabaseHas` (create is a header action, not a row action) | +| `can edit choice list entry via table action` | Create entry, then `callTableAction(EditAction::class, $entry, data: ['name' => 'updated_entry'])` + DB check | +| `can delete choice list entry via table action` | Create entry, then `callTableAction(DeleteAction::class, $entry)` + `assertDatabaseMissing` | + +--- + +## Out of scope + +- `FarmEntityResource` (list/create/edit/import pages) — post-dates both plans; only the column-visibility test exists. Candidate for a separate smoke/CRUD plan. +- Package-level gaps (no create action on `ManageXlsformModule`, no user create/edit pages) — behaviour changes belong in the packages, not in this test plan. + +## Completion checklist + +1. All new/reworked tests pass: `./vendor/bin/pest tests/Feature/Smoke tests/Feature/Crud` +2. Update **Status** in `SMOKE_TESTS.md` and `CRUD_TESTS.md` to Completed, noting obsolete items. +3. Write change log in `docs/change-logs/` referencing this plan and the two originals. From fe75b8468578fd008e7420e1ff6cbc85c4a37c44 Mon Sep 17 00:00:00 2001 From: Dave Mills Date: Wed, 29 Jul 2026 16:28:18 +0100 Subject: [PATCH 2/2] Add missing smoke and CRUD tests, fix two vacuous tests Implements docs/plans/MISSING_SMOKE_CRUD_TESTS.md: - Smoke: root path redirects to app login (adapted from removed cover page) - Admin CRUD: dataset name-required + edit, program record URL, xlsform module bulk delete - Admin CRUD fixes: delete duplicate vacuous module-version create test; rework wrong-table delete test into 'can delete xlsform module version' - App CRUD: farm bulk delete, choice list entry create/edit/delete - TestCase: declare shared $choiceList property Smoke + CRUD suites: 77 -> 85 tests, all passing. Co-Authored-By: Claude Fable 5 --- docs/change-logs/missing-smoke-crud-tests.md | 46 +++++++ docs/plans/CRUD_TESTS.md | 2 +- docs/plans/MISSING_SMOKE_CRUD_TESTS.md | 2 +- docs/plans/SMOKE_TESTS.md | 2 +- tests/Feature/Crud/AdminPanelCrudTest.php | 82 +++++++---- tests/Feature/Crud/AppPanelCrudTest.php | 137 ++++++++++++++++--- tests/Feature/Smoke/AppPanelTest.php | 5 +- tests/Feature/Smoke/PublicRoutesTest.php | 8 +- tests/TestCase.php | 2 + 9 files changed, 230 insertions(+), 56 deletions(-) create mode 100644 docs/change-logs/missing-smoke-crud-tests.md diff --git a/docs/change-logs/missing-smoke-crud-tests.md b/docs/change-logs/missing-smoke-crud-tests.md new file mode 100644 index 00000000..4957cf14 --- /dev/null +++ b/docs/change-logs/missing-smoke-crud-tests.md @@ -0,0 +1,46 @@ +# Change Log: Missing Smoke & CRUD Tests + +Implements [docs/plans/MISSING_SMOKE_CRUD_TESTS.md](../plans/MISSING_SMOKE_CRUD_TESTS.md), the follow-up to [SMOKE_TESTS.md](../plans/SMOKE_TESTS.md) and [CRUD_TESTS.md](../plans/CRUD_TESTS.md). Adds the still-feasible tests from the two original plans and fixes two vacuous tests found during the 2026-07-29 audit. Both original plans are now marked **Completed**. + +## Test count + +`tests/Feature/Smoke` + `tests/Feature/Crud`: 77 → 85 tests, all passing (+9 new, 1 vacuous test deleted, 1 vacuous test reworked). + +## `tests/Feature/Smoke/PublicRoutesTest.php` + +- New: `root path redirects to app login` — `/` now redirects to `/app` (`routes/web.php`); adapted from the original plan's "cover page loads". +- Removed unused `CoverPage`/`ResultsPage` imports. + +## `tests/Feature/Smoke/AppPanelTest.php` + +- No `HddsHints` smoke test added (optional item flagged in the plan): its `mount()` aborts 404 unless the team has an HDDS module version linked to a deployed xlsform. Noted with a comment in the file. + +## `tests/Feature/Crud/AdminPanelCrudTest.php` + +New tests: + +- Dataset: `create dataset requires name`, `can edit dataset`. +- Program: `program list links to program panel` — asserts the rendered list contains the `program/{id}` record URL (the `recordUrl` override in `app/Filament/Admin/Resources/ProgramResource.php`). The plan's `getRecordUrl()` approach was swapped for `assertSeeHtml()` because `livewire()->instance()` is typed as the base Livewire component, which fails phpstan. +- XlsformModule: `can delete xlsform module` via the table's grouped `DeleteBulkAction` (the table has no row DeleteAction and no create action). + +Fixes to vacuous tests in the XlsformModuleVersion describe block: + +- Deleted the duplicate `can create xlsform module version` test — it filled module fields on the version create action and asserted a row that the `beforeEach` had already created; `can create default xlsform module version` covers version creation. +- Reworked `can delete xlsform module` into `can delete xlsform module version` — it asserted against the wrong table (`xlsform_modules`), passing vacuously; it now creates a version linked to the `beforeEach` module and asserts `xlsform_module_versions` after the row `DeleteAction`. + +## `tests/Feature/Crud/AppPanelCrudTest.php` + +New tests: + +- Farm: `can bulk delete farm` — builds the LocationLevel → Location → Farm chain manually (no Farm factory) and deletes via the toolbar `DeleteBulkAction` (the table has no row DeleteAction). +- ChoiceListEntry: `can create choice list entry via table header action`, `can edit choice list entry via table action`, `can delete choice list entry via table action`. All operate on a team-owned entry (`owner_id` set) because edit/delete row actions are hidden for global entries. Teams automatically get an English locale on creation (`Team::booted()`), so the `languageStrings` repeater requires exactly one label item — the create test passes it as form data; the edit test seeds it on the entry first. + +## `tests/TestCase.php` + +- Added `public ChoiceList $choiceList;` to the shared test-property list, following the existing pattern for `$team`/`$xlsformTemplate` etc. (phpstan resolves `$this->…` in Pest closures against `Tests\TestCase`). + +## Verification + +- `./vendor/bin/pest tests/Feature/Smoke tests/Feature/Crud` — 85 passed. +- `./vendor/bin/phpstan analyse tests/Feature/Smoke tests/Feature/Crud` — no new errors (8 pre-existing errors from stub files / unmatched ignore patterns are unchanged). +- `./vendor/bin/pint` — applied to changed files. diff --git a/docs/plans/CRUD_TESTS.md b/docs/plans/CRUD_TESTS.md index bed9274e..1df58d50 100644 --- a/docs/plans/CRUD_TESTS.md +++ b/docs/plans/CRUD_TESTS.md @@ -1,6 +1,6 @@ # CRUD Test Plan -**Status:** In Progress — first draft implemented 2026-05-12 (commit `b827ead`): 37 of ~69 planned tests exist in `tests/Feature/Crud/` (Admin 20/~49, App 12/~14, Program 4/~6), with the `withAppTenant()`/`withProgramTenant()` helpers in place. 5 Admin-panel tests were known-failing against current package behaviour — see [docs/change-logs/fix-admin-crud-test-imports.md](../change-logs/fix-admin-crud-test-imports.md). Remaining: ~29 Admin-panel resource cases. +**Status:** Completed — see [docs/change-logs/missing-smoke-crud-tests.md](../change-logs/missing-smoke-crud-tests.md). The still-feasible remaining cases were added via the follow-up plan [MISSING_SMOKE_CRUD_TESTS.md](MISSING_SMOKE_CRUD_TESTS.md). Items that became obsolete before completion: Domain/Theme/GlobalIndicator CRUD (resources removed), User create/edit (Admin UserResource is now list-only with an "invite users" action), XlsformModule create (the package's create action is disabled), and Program-panel ProgramResource CRUD (redesigned around the `ManageProgram` tenant-profile page — adapted tests exist in `ProgramPanelCrudTest.php`). ## Overview diff --git a/docs/plans/MISSING_SMOKE_CRUD_TESTS.md b/docs/plans/MISSING_SMOKE_CRUD_TESTS.md index bc8f73b4..6a35936c 100644 --- a/docs/plans/MISSING_SMOKE_CRUD_TESTS.md +++ b/docs/plans/MISSING_SMOKE_CRUD_TESTS.md @@ -1,6 +1,6 @@ # Missing Smoke & CRUD Tests -**Status:** Not Started +**Status:** Completed — see [docs/change-logs/missing-smoke-crud-tests.md](../change-logs/missing-smoke-crud-tests.md). All planned tests implemented (85 smoke/CRUD tests passing). The optional `HddsHints` smoke test was skipped: the page 404s without an HDDS module version linked to a deployed xlsform. The Program record-URL test uses `assertSeeHtml()` instead of `getRecordUrl()` for phpstan compatibility. Follow-up to [SMOKE_TESTS.md](SMOKE_TESTS.md) and [CRUD_TESTS.md](CRUD_TESTS.md). Audit date 2026-07-29: all 77 existing tests in `tests/Feature/Smoke/` and `tests/Feature/Crud/` pass. This plan adds the tests from the two original plans that are still missing **and still feasible against the current codebase**, and fixes two vacuous tests found during the audit. diff --git a/docs/plans/SMOKE_TESTS.md b/docs/plans/SMOKE_TESTS.md index e01d0491..d7eed700 100644 --- a/docs/plans/SMOKE_TESTS.md +++ b/docs/plans/SMOKE_TESTS.md @@ -1,6 +1,6 @@ # Smoke Test Plan -**Status:** In Progress — implemented 2026-05-11 (commit `9b15333`): 40 of the 43 planned tests exist across all 5 files in `tests/Feature/Smoke/`, and the `tests/Pest.php` helpers (`createSuperAdmin()`, `createAppUser()`, panel/tenant helpers, permission-cache `beforeEach`) are all in place. Remaining gap: ~3 App-panel resource smoke tests from the File 4 "Resources" block. +**Status:** Completed — see [docs/change-logs/missing-smoke-crud-tests.md](../change-logs/missing-smoke-crud-tests.md). The remaining gaps were closed via the follow-up plan [MISSING_SMOKE_CRUD_TESTS.md](MISSING_SMOKE_CRUD_TESTS.md); the only adaptation is that the "cover page loads" item became "root path redirects to app login" (the `/` route is now a redirect). Items that became obsolete before completion: cover/results/temp-results pages (routes removed), `data-collection-index`/`lisp-index`/`lisp-indicators`/`lisp-workshop` pages (page classes deleted), and `domains`/`themes`/`global-indicators` admin lists (resources removed). ## Overview diff --git a/tests/Feature/Crud/AdminPanelCrudTest.php b/tests/Feature/Crud/AdminPanelCrudTest.php index b869d50f..1d1bdbd3 100644 --- a/tests/Feature/Crud/AdminPanelCrudTest.php +++ b/tests/Feature/Crud/AdminPanelCrudTest.php @@ -1,21 +1,24 @@ get('/admin/programs')->assertOk(); }); + test('program list links to program panel', function () { + $program = Program::create(['name' => 'Linked Program']); + + livewire(ListPrograms::class) + ->assertSeeHtml(url('program/'.$program->id)); + }); + }); // --------------------------------------------------------------------------- @@ -125,11 +135,29 @@ $this->assertDatabaseHas('datasets', ['name' => 'Test Dataset']); }); + test('create dataset requires name', function () { + livewire(CreateDataset::class) + ->fillForm(['name' => '', 'description' => 'Test description']) + ->call('create') + ->assertHasFormErrors(['name' => 'required']); + }); + test('dataset edit page loads', function () { $dataset = Dataset::forceCreate(['name' => 'Editable Dataset', 'primary_key' => 'id']); $this->get("/admin/datasets/{$dataset->id}/edit")->assertOk(); }); + test('can edit dataset', function () { + $dataset = Dataset::forceCreate(['name' => 'Editable Dataset', 'primary_key' => 'id']); + + livewire(EditDataset::class, ['record' => $dataset->id]) + ->fillForm(['name' => 'Updated Dataset', 'description' => 'Updated description']) + ->call('save') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas('datasets', ['id' => $dataset->id, 'name' => 'Updated Dataset']); + }); + }); // --------------------------------------------------------------------------- @@ -160,8 +188,8 @@ $this->superAdmin = createSuperAdmin(); $this->actingAs($this->superAdmin); withAdminPanel(); - $this->xlsformTemplate = \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate::withoutEvents( - fn () => \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate::forceCreate(['title' => 'Test Template']) + $this->xlsformTemplate = XlsformTemplate::withoutEvents( + fn () => XlsformTemplate::forceCreate(['title' => 'Test Template']) ); }); @@ -169,6 +197,18 @@ $this->get('/admin/xlsform-modules')->assertOk(); }); + test('can delete xlsform module', function () { + $module = XlsformModule::forceCreate([ + 'xlsform_template_id' => $this->xlsformTemplate->id, + 'label' => 'Delete Module', + 'name' => 'delete_module', + ]); + + livewire(ManageXlsformModule::class) + ->callTableBulkAction(DeleteBulkAction::class, [$module]); + + $this->assertDatabaseMissing('xlsform_modules', ['id' => $module->id]); + }); }); @@ -180,8 +220,8 @@ $this->superAdmin = createSuperAdmin(); $this->actingAs($this->superAdmin); withAdminPanel(); - $this->xlsformTemplate = \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate::withoutEvents( - fn () => \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate::forceCreate(['title' => 'Test Template']) + $this->xlsformTemplate = XlsformTemplate::withoutEvents( + fn () => XlsformTemplate::forceCreate(['title' => 'Test Template']) ); $this->xlsformModule = XlsformModule::forceCreate([ 'xlsform_template_id' => $this->xlsformTemplate->id, @@ -209,7 +249,7 @@ ]); }); - test('create xlsform module requires name', function () { + test('create xlsform module requires name', function () { livewire(ManageXlsformModuleVersion::class) ->callAction(CreateAction::class, data: [ 'xlsform_template_id' => $this->xlsformTemplate->id, @@ -219,30 +259,16 @@ ->assertHasActionErrors(['name' => 'required']); }); - - - test('can create xlsform module version', function () { - livewire(ManageXlsformModuleVersion::class) - ->callAction(CreateAction::class, data: [ - 'xlsform_template_id' => $this->xlsformTemplate->id, - 'label' => 'Test Module', - 'name' => 'test_module', - ]) - ->assertHasNoActionErrors(); - - $this->assertDatabaseHas('xlsform_modules', ['name' => 'test_module']); - }); - - test('can delete xlsform module', function () { - - $module = XlsformModuleVersion::withoutEvents(fn () => XlsformModuleVersion::forceCreate([ + test('can delete xlsform module version', function () { + $version = XlsformModuleVersion::withoutEvents(fn () => XlsformModuleVersion::forceCreate([ + 'xlsform_module_id' => $this->xlsformModule->id, 'name' => 'delete_me', ])); livewire(ManageXlsformModuleVersion::class) - ->callTableAction(DeleteAction::class, $module); + ->callTableAction(DeleteAction::class, $version); - $this->assertDatabaseMissing('xlsform_modules', ['id' => $module->id]); + $this->assertDatabaseMissing('xlsform_module_versions', ['id' => $version->id]); }); }); diff --git a/tests/Feature/Crud/AppPanelCrudTest.php b/tests/Feature/Crud/AppPanelCrudTest.php index 1a1a905e..93f4782d 100644 --- a/tests/Feature/Crud/AppPanelCrudTest.php +++ b/tests/Feature/Crud/AppPanelCrudTest.php @@ -1,17 +1,28 @@ callTableAction(CreateAction::class, data: [ - 'name' => 'Region', + 'name' => 'Region', 'owner_id' => $this->team->id, ]) ->assertHasNoFormErrors(); @@ -76,7 +87,7 @@ test('location level view page loads', function () { withAppTenant($this->team); - $level = new LocationLevel(); + $level = new LocationLevel; $level->name = 'Test Level'; $level->owner_id = $this->team->id; $level->save(); @@ -86,7 +97,7 @@ test('can bulk delete location level', function () { withAppTenant($this->team); - $level = new LocationLevel(); + $level = new LocationLevel; $level->name = 'Delete Level'; $level->owner_id = $this->team->id; $level->save(); @@ -114,6 +125,33 @@ $this->get("/app/{$this->team->id}/location-levels/farms")->assertOk(); }); + test('can bulk delete farm', function () { + withAppTenant($this->team); + + $level = new LocationLevel; + $level->name = 'Village'; + $level->owner_id = $this->team->id; + $level->save(); + + $location = Location::create([ + 'owner_id' => $this->team->id, + 'location_level_id' => $level->id, + 'name' => 'Test Village', + 'code' => 'tv1', + ]); + + $farm = Farm::create([ + 'owner_id' => $this->team->id, + 'location_id' => $location->id, + 'team_code' => 'farm-001', + ]); + + livewire(ListFarms::class) + ->callTableBulkAction(DeleteBulkAction::class, [$farm]); + + $this->assertDatabaseMissing('farms', ['id' => $farm->id]); + }); + }); // --------------------------------------------------------------------------- @@ -125,24 +163,24 @@ $this->team = Team::factory()->create(); // ChoiceList requires the full parent chain: Template → Module → ModuleVersion - $xlsformTemplate = \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate::withoutEvents( - fn () => \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate::forceCreate(['title' => 'Test Template']) + $xlsformTemplate = XlsformTemplate::withoutEvents( + fn () => XlsformTemplate::forceCreate(['title' => 'Test Template']) ); - $xlsformModule = \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformModule::forceCreate([ + $xlsformModule = XlsformModule::forceCreate([ 'xlsform_template_id' => $xlsformTemplate->id, 'label' => 'Test Module', - 'name' => 'test_module', + 'name' => 'test_module', ]); - $xlsformModuleVersion = \Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformModuleVersion::forceCreate([ + $xlsformModuleVersion = XlsformModuleVersion::forceCreate([ 'xlsform_module_id' => $xlsformModule->id, - 'name' => 'v1', - 'is_default' => true, + 'name' => 'v1', + 'is_default' => true, ]); - \Stats4sd\FilamentOdkLink\Models\OdkLink\ChoiceList::forceCreate([ + $this->choiceList = ChoiceList::forceCreate([ 'xlsform_module_version_id' => $xlsformModuleVersion->id, - 'list_name' => 'smoke_test_list', - 'is_localisable' => true, - 'has_custom_handling' => false, + 'list_name' => 'smoke_test_list', + 'is_localisable' => true, + 'has_custom_handling' => false, ]); $this->user = createAppUser($this->team); @@ -153,10 +191,68 @@ $this->get("/app/{$this->team->id}/localisations/choice-list-entries")->assertOk(); }); -}); + test('can create choice list entry via table header action', function () { + withAppTenant($this->team); + + livewire(ListChoiceListEntries::class) + ->callTableAction(CreateAction::class, data: [ + 'name' => 'new_entry', + 'languageStrings' => [ + [ + 'locale_id' => $this->team->locales()->value('locales.id'), + 'language_string_type_id' => LanguageStringType::where('name', 'label')->firstOrFail()->id, + 'text' => 'New Entry', + ], + ], + ]) + ->assertHasNoTableActionErrors(); + $this->assertDatabaseHas('choice_list_entries', [ + 'choice_list_id' => $this->choiceList->id, + 'owner_id' => $this->team->id, + 'name' => 'new_entry', + ]); + }); + + test('can edit choice list entry via table action', function () { + withAppTenant($this->team); -describe('App Panel CRUD - Create New Team', function() { + $entry = ChoiceListEntry::create([ + 'choice_list_id' => $this->choiceList->id, + 'owner_id' => $this->team->id, + 'name' => 'editable_entry', + ]); + $entry->languageStrings()->create([ + 'locale_id' => $this->team->locales()->value('locales.id'), + 'language_string_type_id' => LanguageStringType::where('name', 'label')->firstOrFail()->id, + 'text' => 'Editable Entry', + ]); + + livewire(ListChoiceListEntries::class) + ->callTableAction(EditAction::class, $entry, data: ['name' => 'updated_entry']) + ->assertHasNoTableActionErrors(); + + $this->assertDatabaseHas('choice_list_entries', ['id' => $entry->id, 'name' => 'updated_entry']); + }); + + test('can delete choice list entry via table action', function () { + withAppTenant($this->team); + + $entry = ChoiceListEntry::create([ + 'choice_list_id' => $this->choiceList->id, + 'owner_id' => $this->team->id, + 'name' => 'deletable_entry', + ]); + + livewire(ListChoiceListEntries::class) + ->callTableAction(DeleteAction::class, $entry); + + $this->assertDatabaseMissing('choice_list_entries', ['id' => $entry->id]); + }); + +}); + +describe('App Panel CRUD - Create New Team', function () { beforeEach(function () { Http::fake(); @@ -165,7 +261,6 @@ $this->actingAs($this->superAdmin); }); - test('team create page loads', function () { $this->get("/app/{$this->team->id}/teams/create")->assertOk(); }); @@ -191,4 +286,4 @@ ->assertHasFormErrors(['name' => 'required']); }); -}); \ No newline at end of file +}); diff --git a/tests/Feature/Smoke/AppPanelTest.php b/tests/Feature/Smoke/AppPanelTest.php index 329bf805..4f1d0024 100644 --- a/tests/Feature/Smoke/AppPanelTest.php +++ b/tests/Feature/Smoke/AppPanelTest.php @@ -78,7 +78,7 @@ ->get("/app/{$this->team->id}/pilot-index") ->assertOk(); }); - + test('place adaptations index loads', function () { $this->actingAs($this->user) ->get("/app/{$this->team->id}/place-adaptations-index") @@ -123,4 +123,7 @@ ->assertOk(); }); + // HddsHints (/app/{team}/hdds-hints) has no smoke test: its mount() aborts 404 + // unless the team has an HDDS module version linked to a deployed xlsform. + }); diff --git a/tests/Feature/Smoke/PublicRoutesTest.php b/tests/Feature/Smoke/PublicRoutesTest.php index 8309cbdc..b0e6cd2c 100644 --- a/tests/Feature/Smoke/PublicRoutesTest.php +++ b/tests/Feature/Smoke/PublicRoutesTest.php @@ -1,10 +1,12 @@ get('/') + ->assertRedirect('app'); + }); + test('app login page loads', function () { $this->get('/app/login') ->assertOk(); diff --git a/tests/TestCase.php b/tests/TestCase.php index e723ae01..b1281f60 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,6 +5,7 @@ use App\Models\Team; use App\Models\User; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; +use Stats4sd\FilamentOdkLink\Models\OdkLink\ChoiceList; use Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformModule; use Stats4sd\FilamentOdkLink\Models\OdkLink\XlsformTemplate; use Stats4sd\FilamentTeamManagement\Models\Program; @@ -23,4 +24,5 @@ abstract class TestCase extends BaseTestCase public XlsformTemplate $xlsformTemplate; public XlsformModule $xlsformModule; + public ChoiceList $choiceList; }