Testing and quality
Use layered automated testing and deployment gates to keep a configurable platform safe to evolve.
FormPlatform Testing Guide
1. Current test architecture
FormPlatform uses layered verification:
| Layer | Location | Purpose | Dependencies |
|---|---|---|---|
| Frontend unit/contracts | `src/FormPlatform.Host/ClientApp/src/**/*.test.js` | pure runtime rules, public component contracts, regressions | Node.js, Vitest |
| Static checks | projects/scripts | JSON/XML/JS/C# structure, links, formatting, manifests | none/local SDK |
| PostgreSQL integration | `tests/FormPlatform.PostgreSql.Tests` | real ORM, transaction, reference, migration/checksum behavior | Docker |
| SQL Server integration | `tests/FormPlatform.SqlServer.Tests` | real SQL Server ORM, generated-key/paging, migration/checksum behavior | Docker |
| Playwright E2E | `tests/e2e` | browser, auth, routing, forms, modules, error UX | running Host, Chromium, test account |
| All-module build | `eng/build-all.ps1` | Host/SDK/Sample/Todo/ResourceBooking compatibility | Node, .NET, sibling repositories |
| Manual acceptance | browser/database/logs | Designer, printing, payment/OIDC, environment flows | target environment |
GitHub CI now enforces seven gates: frontend Vitest, build and stage every module, SDK/module compatibility, PostgreSQL Testcontainers, SQL Server Testcontainers, the real Host against isolated PostgreSQL for Playwright, and a final release-runtime integrity gate. The final gate depends on every earlier gate.
2. Prerequisites
Use .NET 10 SDK and Node.js 22. PostgreSQL tests require Docker. E2E requires a separately running FormPlatform, and ResourceBooking tests require that module and client extension.
Never point automation at development or production data. Testcontainers creates a temporary PostgreSQL. The E2E Host also needs a dedicated database and secrets.
3. PostgreSQL integration tests
dotnet test tests/FormPlatform.PostgreSql.Tests/FormPlatform.PostgreSql.Tests.csproj -c Release
`PostgreSqlFixture` starts `postgres:17-alpine` and implements `IDatabaseConnectionFactory`. Tests in the xUnit collection share a container, so isolate with unique rows/tables or cleanup.
Current coverage includes dynamic repository/provider behavior and migration once-only/checksum/Core-Commerce schema behavior.
3.1 Add ORM tests
Use real metadata, repository, dialect, and UnitOfWork rather than mocked SQL. Project explicit fields and assert that unrequested attributes are absent. Use unique IDs/names and never rely on test order.
3.2 SQL Server integration tests
dotnet test tests/FormPlatform.SqlServer.Tests/FormPlatform.SqlServer.Tests.csproj -c Release
`SqlServerFixture` starts an isolated SQL Server 2022 container. It verifies the SQL Server dialect's CRUD/filter/offset-paging/generated-key path and applies the complete Core and Commerce migration chain twice, including checksum protection. This is deliberately independent from a locally installed SQL Server.
3.3 Add migration tests
Verify empty-database execution, idempotent second execution, history module/id/checksum, checksum mismatch failure, and a new migration following old history. PostgreSQL is currently automated; MySQL and SQL Server still need provider CI or pre-release environment tests.
3.4 Testcontainers troubleshooting
Run `docker info`; inspect proxy/registry settings for image failures; allow first-run download time; avoid fixed port 5432; and prevent collisions because the fixture shares one container.
4. Release runtime integrity gate
After every earlier CI gate passes, the staged runtime is checked by `eng/validate-release-runtime.ps1`:
- Host, SDK, contracts, appsettings, SPA asset references, module manifests, and module entry assemblies must exist.
- `appsettings.json` must not contain non-empty connection strings, passwords, secrets, encryption keys, API keys, or private keys. Deployment secrets belong in environment variables, User Secrets, or the external deployment secrets document.
- `DataAccess:IncludeSqlParameterValues` must remain `false`.
- The existing binary module validator runs again, and `release-manifest.json` records SHA-256 hashes for every delivered file.
Run the same gate after `eng/prepare-ci-runtime.ps1` before manually publishing an artifact:
./eng/validate-release-runtime.ps1 -RuntimeDirectory .ci/runtime -WriteManifest
The resulting `formplatform-release-candidate` CI artifact is a tested, integrity-listed candidate; release approval should require the `release-readiness` job in branch protection.
5. Playwright E2E
cd tests/e2e
npm install --no-audit --no-fund
npm run install:browsers
Prepare a dedicated Host/database, apply migrations, create a role-appropriate test user, deploy tested modules, build the client, and start the Host.
$env:FORMPLATFORM_E2E_BASE_URL='http://localhost:5080'
$env:FORMPLATFORM_E2E_USER='e2e-admin'
$env:FORMPLATFORM_E2E_PASSWORD='use-a-secret-value'
cd tests/e2e
npm test
Debug with `npm run test:ui`, `npx playwright test specs/core.spec.js --headed --debug`, and `npx playwright show-trace ...`.
Current core flows cover public standalone Designer, validated client-extension discovery, authenticated Form Center, direct/refresh module routing, and preserving a mounted form after a server 409. Login tests may skip locally when credentials are absent, but missing CI credentials fail the run.
5.1 Stable E2E rules
Prefer roles, labels, and test IDs over Tailwind classes or DOM hierarchy. Mock only a boundary outside the test objective. A 409 route mock is valid for error UX; it is invalid for a real persistence test. Each test creates/cleans its own data.
Critical coverage includes Designer/Preview/Viewer, all identity modes, CRUD/failure preservation, pagination draft/final state, boolean/hidden fields, DataGrid editing/sort/export, AsyncSelect/Tree references, module direct routes, i18n/toasts/field errors, print exclusion, and Survey upload/download authorization.
6. All-module build
Expected layout:
workspace/FormPlatform
workspace/Todo
workspace/ResourceBooking
Run:
./eng/build-all.ps1 -Configuration Release
The script builds ClientApp, Host/SDK, Sample, Todo, and ResourceBooking client/server. Missing modules/manifests fail instead of being skipped. `-SkipClientBuild` provides a faster server compatibility check; path parameters can override sibling locations.
GitHub variables are `FORMPLATFORM_TODO_REPOSITORY` and `FORMPLATFORM_RESOURCE_BOOKING_REPOSITORY`; private repositories use a read-only `FORMPLATFORM_MODULES_TOKEN`.
`eng/prepare-ci-runtime.ps1` stages the Host, Vue assets, Sample, Todo, and ResourceBooking under `.ci/runtime`. Then run:
./eng/validate-module-compatibility.ps1 -RuntimeDirectory .ci/runtime
The Host's `--validate-modules` mode loads the real packages and validates manifest schema/name, module/assembly version equality, SDK/Platform ranges, SDK reference compatibility, and absence of obsolete Host references. It neither connects to a database nor starts HTTP. Playwright uses a `postgres:17-alpine` service and saves Host logs, reports, traces, and screenshots; PostgreSQL tests save TRX results. Require all four jobs in branch protection.
7. Client unit, contract, and static testing
ClientApp uses Vitest for substitutions, Action Context isolation, validation/cache/debounce, Pagination, Checkbox/Radio, DataGrid models, and exhaustive built-in contracts:
cd src/FormPlatform.Host/ClientApp
npm run test:unit
npm run test:unit:watch
See Client Component Contracts. Unit tests protect pure/runtime rules; Playwright remains preferred for real control interaction. The production build validates Vue templates, dynamic imports, and bundling.
Static checks include `node --check` for plain JS, JSON/XML parsing, `git diff --check`, Markdown relative-link checking, and manifest version/schema checking. Vue SFCs require Vite or a Vue parser; `node --check` is insufficient.
8. API/error contract tests
Assert status, problem content type, and stable fields: `code`, `messageKey`, `fallback`, `fieldErrors`, `status`, and `traceId`. A 500 must not reveal SQL, connection strings, secrets, or stack traces. Empty 401/403/404 responses should receive the same platform envelope. Client tests ensure field errors remain attached to the active editor/FormReader.
9. Manual acceptance
Environment-dependent checks remain necessary for OIDC callbacks, SMTP, PayPal/Stripe/WeChat sandboxes, print/PDF, browser local date/time, IIS/Nginx forwarding, large files, production database permissions, and backup/restore.
Minimum release smoke: clean startup with no migration/module errors, login/logout, Form Center, Designer, create/update a regular record, submit one Survey, query a DataGrid, open a module route, and inspect logs for unhandled failures.
10. Test data and security
Credentials for shared or persistent test environments are CI secrets, not spec literals. The fixed password in the E2E workflow belongs only to its per-job disposable database and must never be reused. Do not use personal data. Use payment/OIDC sandboxes. Failure traces may contain form content, so limit artifact access and retention. Keep sensitive SQL parameter logging disabled.
11. Strategy template
For each feature decide whether it needs a unit rule test, PostgreSQL integration, migration history test, API contract assertion, Playwright interaction, provider-specific migration run, allow/deny matrix, or external-service/manual smoke. A bug fix starts with a reproducer; after it passes, remove obsolete workarounds.
12. Recommended CI stages
1. Frontend Vitest and component contracts.
2. Client production build plus Host/SDK/all-module build.
3. Stage the deployable runtime and validate every SDK/module package through the Host.
4. PostgreSQL integration.
5. Isolated PostgreSQL/Host plus Playwright core.
6. Preserve runtime, TRX, Host log, and failure traces.
7. SQL Server migration/ORM Testcontainers suite.
8. Release runtime integrity gate and SHA-256 manifest.
9. Staging external-service smoke, then production approval.
Preserve the earliest root-cause log. A successful build alone does not prove migrations, module loading, or browser flows.