Plugin Test Coverage
Use this skill when expanding Vitest and/or Playwright e2e coverage for a plugin under plugins/.
Reference implementations:
@sanity/document-internationalization— Vitest + e2e undere2e/tests/document-internationalization/sanity-plugin-internationalized-array— Vitest + e2e undere2e/tests/internationalized-array/
Also read e2e/README.md before writing Playwright specs.
When to use
- Filling unit/integration gaps for a published plugin
- Wiring a plugin into
dev/e2e-studioand adding Playwright specs - Porting the doc-i18n / internationalized-array coverage process to another plugin
Workflow
- Inventory use cases from README + source (authoring loops, config knobs, integrations).
- Map each use case to Vitest (pure logic, components with mocks) vs Playwright (studio UX that needs a real form).
- Fill Vitest gaps co-located under
plugins/<pkg>/src/— plugin assembly, context/providers, utils, components. - Wire e2e-studio —
definePluginexample file, register in both chromium/firefox workspaces. - Add helpers + specs —
e2e/helpers/<plugin>/,e2e/tests/<plugin>/. - Changesets — separate patch changeset per published package that gained
data-testids or runtime changes. Private e2e/studio files need none. - Verify —
pnpm format && pnpm lint && pnpm knip && pnpm build && pnpm test(and targeted e2e when secrets allow). - PR — draft,
🤖 botlabel, inventory + e2e test table in the description.
Skip one-off tooling (migrations, banners) unless they are part of the main authoring loop.
Vitest patterns
- Co-locate
*.test.ts(x)next to source; use jsdom via the packagevitest.config.ts. - Shared mocks/fixtures in
src/test/helpers.tsandsrc/test/component-helpers.tsx(ThemeWrapperfor@sanity/ui). - Plugin assembly tests: call the plugin factory, assert schema type names, document layout, form input wrappers, nested plugins.
- Context/provider tests: mock
useClient/useWorkspace/ pane hooks; useSuspense+actfor asyncReact.uselanguage resolution; call any module-levelclear()between tests. - Timeouts:
test('name', {timeout: 30_000}, async () => { … })— options object as second arg. - Keep the package-exports snapshot test; update with
pnpm test -uonly when exports intentionally change. - React Compiler is on — do not add unnecessary
useMemo/useCallback.
Run a single package:
pnpm --filter <package-name> test run
E2e structure
e2e/
├── tests/
│ ├── smoke.spec.ts # studio-wide only
│ └── <plugin-name>/
│ └── <plugin-name>.spec.ts
└── helpers/
└── <plugin-name>/
└── <helpers>.ts
Do not dump plugin specs at the top level of tests/.
E2e-studio wiring
- Add
dev/e2e-studio/src/<example>.tswithdefinePluginthat registers schema + the plugin under test. - Import and add it to both workspaces in
dev/e2e-studio/sanity.config.ts. - Ensure the workspace
package.jsonalready depends on the plugin (workspace:*). - Update the “Currently wired” list in
e2e/README.md.
Hard-won Playwright lessons
- Project
baseURLmust end with a trailing slash (…/chromium/,…/firefox/). - Navigate with relative intents:
intent/edit/id=…;type=…. Never host-absolute/intent/…(drops workspace basePath → “Workspace not found”). - Auth: Playwright
storageStateseeds__studio_auth_token_<projectId>; preflight/users/me. Never useSANITY_DEPLOY_TOKENfor e2e session auth. - Local pitfall: stale server on
:3333+reuseExistingServer→ signed-out studio. Kill and restart. - Prefer accessible role/name locators; add
data-testid+ a patch changeset only when selectors are flaky or ambiguous (e.g. duplicate add-button grids). - Seed documents via the Content Lake API; always
try/finallycleanup. - Video:
SANITY_E2E_VIDEO=onwhen debugging. - Document existence matters: some plugins only auto-seed after
_revexists — seed empty persisted docs, don’t rely on brand-new unsaved drafts.
PR checklist
- Separate changeset per published package touched
- Draft PR with
🤖 botlabel - Use-case inventory + e2e test table in the description
-
pnpm format/lint/knip/build/testgreen - CI e2e (chromium + firefox) green when studio wiring changed
Pointers
e2e/README.mdAGENTS.md— CI commands, changesets, Node version notes- Doc-i18n + internationalized-array as living examples