Testing a Plugin
A plugin is held to the same testing bar as the core. Your tests live inside your plugin folder, run alongside the core's suites, and must follow the platform's assertion rules — which exist to make sure a passing test actually proves something.
The test types
All plugin tests live under plugins/{slug}/tests/ — never outside it:
- API tests (
tests/api/*.test.ts) — Vitest suites exercising your routes against a real Postgres. - Integration tests (
tests/integration/*.sh) — full-stack shell scripts exercising the UI → nginx → API → DB path. - Frontend tests (
tests/fe/*.test.tsx) — Vitest suites for your React components. - Unit tests — for plugin logic testable in isolation.
Discovery is already configured: the core's Vitest and integration runners include plugins/*/tests/..., so your tests run in CI alongside core tests (plugin integration suites appear prefixed plugin:<slug>/).
Infrastructure you can rely on
CI stands up Postgres with the core schema and all plugin migrations already applied before tests run, and the plugin loader has seeded your entity and statuses at API startup — so your real entity and its statuses are available in the test DB. Use them: plugin tests use the plugin's real entity (seeded by the loader), not the core's ensureTestEntity fixture, which is for core cascade tests only. Auth helpers for register/login come from the core test helpers.
The assertion rules (non-negotiable)
The platform's test rules apply to you in full — they exist so tests fail when the code is broken:
- No
toBeDefined()/toBeTruthy()alone — always follow with a value, type, or format assertion. - Assert against known seed data, not just shapes; use exact counts, not loose ranges.
- No assertions inside conditionals (they silently skip).
- Scope frontend assertions to specific elements (element IDs /
within()), not broad text matches.
Repeatable and clean
Tests must be runnable repeatedly without tearing down the database — so use unique-per-run identifiers (append a run id / timestamp) and clean up after yourself. The default plugin provides a shared cleanup helper (tests/helpers.ts) that deletes from all its tables in the correct FK order; copy that pattern.
The short version: put your tests in your plugin folder, lean on the seeded real entity, and write assertions strong enough to fail when something's actually wrong. That's what keeps a green suite meaningful.