testing
SkillDev toolsLets your agent run tests to check code works as expected.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the testing skill
About this capability
Skill: testing
What this skill tells your AI
The instructions your AI receives, as published by udecode/plate in .agents/skills/testing/SKILL.md and read by ahel’s review.
Testing Goal
Push the suite toward three layers only:
- Pure unit tests for deterministic logic.
- Thin editor or plugin contract tests for real Plate or Slate wiring.
- Golden input or output tests for serializer and parser behavior.
Hard constraints:
- Bun-first and speed-first.
bun run testis the default iterative workflow. pnpm test:allis the full end-of-task and CI run. Do not use it as the default inner-loop command.- Keep the default iterative suite fast.
- No browser or e2e coverage in this program.
- Coverage is hotspot telemetry, not a vanity target. Do not chase repo-wide numbers blindly.
- Use coverage after each phase only to choose the next hotspot.
- No one-smoke-test-per-package sweep.
- Do not add broad smoke coverage for thin wrapper packages.
Coverage Strategy
- Coverage is for regression detection during breaking changes and rearchitecture, not for winning a percentage contest.
- Rank files from fresh
lcov. Trust file order more than package totals. - Work in passes:
- high-value contract pass: do every honest file with score
>= 6 - medium-value follow-up: rerun coverage, then do worthwhile
>= 5files while skipping crumbs, wrappers, and sludge - architecture-safety pass: stop following coverage blindly and harden the contracts you most refuse to break
- high-value contract pass: do every honest file with score
- File-first beats package sweeps once the obvious packages are already covered.
- Good architecture-safety targets:
- plugin resolution and composition
- normalization contracts
- parser and serializer behavior
- structural transforms and merge helpers
- history, diff, and change-tracking behavior
- public editor invariants
/reactis not permanently excluded. Exclude it only when the current pass explicitly says so.- React work should wait until non-React seams are exhausted only when that is the active phase goal, not because the skill hardcodes it forever.
- Stop when the remaining misses are mostly:
- thin wrappers
- DOM-only or provider-only seams
- giant low-ROI sludge files
- tiny uncovered crumbs
- code likely to be deleted or rewritten soon
Core Rules
- Assert public behavior through editor APIs, plugin APIs, hooks, transforms, or rendered output. Do not assert private state, call order, or implementation detail when public behavior already proves the contract.
- Prefer file-ranked batches over package sweeps. Package sweeps are for early broad passes, not the endgame.
- Do not use coverage to justify testing files with no meaningful contract.
- Bun globals come from
tooling/config/global.d.ts. Do not importdescribe,it,expect,mock,spyOn, or other globals frombun:test. - Use
*.spec.ts[x]for the fast lane and*.slow.ts[x]for the slow lane. - Keep helpers package-local. Never import a helper from another spec file.
- No spec should import another spec.
- Put compile-only type contracts in
type-tests/, not mixed into runtime specs. - Titles should describe behavior semantically, not echo raw option names.
- Prefer explicit assertions over snapshots by default.
- Delete skipped tests, commented-out tests, and dead placeholder cases. Do not preserve wishful thinking.
- No fake smoke tests.
- No app-registry imports in package tests.
- Do not add package
devDependenciesjust to support cross-package or app-shaped test setups. If a test needs other package kits, app aliases, or multi-package wiring, move it toapps/www/src/__tests__/package-integration. - When adding tests, inspect fast-suite outliers with
bun run test:profile.bun run test:slowestis the hard gate. If a fast-suite spec crosses the thresholds intooling/config/test-suites.mjs, rename it to*.slow.ts[x]. - Do not trust a fast laptop. Treat the local warning zone as real debt before CI proves you wrong:
60ms/testor120ms/fileis already a move candidate, especially for React-heavy specs. - For borderline cases, run
pnpm test:slowest -- --top 25 --rerun-each 3and move repeat offenders before they drift over the CI line. - Treat known third-party resource logs and serializer fallback warnings as test noise. Suppress them narrowly in the shared Bun setup at
tooling/config/bunTestSetup.ts, not by changing runtime code or sprinkling per-spec console mocks.
Seam Selection
- Use
createEditorfrom@platejs/slatefor pure Slate query, transform, interface, and history contracts. - Use
createSlateEditorfor non-React plugin or editor wiring:- plugin option stores
- selector extension
- pure plugin API composition
- pure transform composition
- parser and deserializer contracts
- HTML
insertData - DnD-style contracts
- Use
createPlateEditoronly when the contract is genuinely Plate-specific. - Use rendered React tests only when the contract is genuinely React-specific: hooks, providers, stores, DOM behavior, or rerender semantics.
- Remaining
createPlateEditorusage is a reviewed allowlist, not a future cleanup queue.
File Organization
- File-scoped specs live beside the implementation.
- Keep
__tests__/only for:- package-local helpers
- fixture banks
- intentionally split multi-file behavior suites
- intentionally kept integration suites
- Move lone file-scoped specs out of
__tests__/. - Collapse tiny split suites into one adjacent table-driven file when the only variation is fixture shape.
- Action helpers may create the editor and perform the transform, but assertions stay in the
it()body. - Rule-action helpers should return the editor and other setup results, not assert internally.
- For composition-heavy suites, extract focused helpers before adding more inline setup. Small helpers like
getSortedKeys(...)andcreateStoreEditor(...)beat repeated editor construction sludge.
Fixtures And Assertions
- Use JSX hyperscript only when tree shape or selection shape is the contract.
- Use plain object fixtures for option, state, and pure helper tests.
- Use a real editor object only when editor-root semantics matter.
NodeApiandElementApido not treat plain{ children: [...] }objects the same way as real editors. - Keep inputs and outputs small.
- Use
it.eachfor small behavior matrices. - In this Bun + Testing Library setup, prefer render-returned queries over
screen. - Snapshots are allowed only when serialized text, AST, or similar output is the contract and inline assertions would be worse.
- Whitespace-sensitive serializer outputs should prefer direct
toBe(...)string assertions. - Avoid
toHaveStylehere. Use direct style-property assertions instead. - After broad title renames on snapshot-backed suites, delete and regenerate the snapshot file.
bun test -uupdates and adds keys, but does not reliably prune dead ones.
Cleanup Heuristics
- Score files before cleanup waves instead of skimming randomly.
- Use fresh
lcovafter each pass. Do not keep working from a stale hotspot map. - Rewrite large hotspot specs before chasing broad title debt. Bigger signal first.
- Scan title debt across:
- plain string titles
it.each(...)format stringsString.rawtitles- snapshot keys derived from those titles
- Scan for commented-out
it,test, anddescribeblocks during dead-spec cleanup waves. - End cleanup waves with repo scans for:
- skipped tests
- commented-out tests
- cross-spec imports
- placeholder titles
- non-allowlisted
createPlateEditorseams
- Use
bun run test:profilefor the fast suite when deciding whether a spec belongs in the slow lane.pnpm test:slowestandpnpm checkenforce those thresholds. - For rule-override hotspots, extract one editor helper and table-drive repeated node-type cases instead of cloning the same transform assertions.
- For plugin-composition hotspots, extract helpers before adding more inline setup.
- Adapt upstream invariants when local runtime semantics differ. Keep the invariant, rewrite the fixture around the real public contract.
- Treat tiny one-branch crumbs as crumbs. Do not let a coverage number talk you into fake work.
- Penalize giant files with poor test ROI unless they hold a central contract you actually care about during the next rewrite.
Package Rules
autoformat
- Use package-local rule arrays.
- Use
KEYSor base plugins, not React plugin.key. - Add only the base plugins a rule actually needs.
- Collapse tiny mark or block suites into matrices when the contract is the same.
- Do not import
AutoformatKitor app registries in package tests. - If an
autoformatcase needs cross-package behavior likecode-blockwiring or app-owned integration setup, move it toapps/www/src/__tests__/package-integrationinstead of expandingpackages/autoformat/package.json.
markdown
- Configure
MarkdownPluginlocally in the package helper. - Do not import
MarkdownKitor any app registry fromapps/www. - Prefer direct string assertions for tiny whitespace-sensitive serializer outputs.
ai / streaming markdown
- Keep one mixed-document smoke case.
- Add a few explicit chunk-boundary tests.
- Do not hide streaming behavior behind snapshots or giant hand-written trees.
core
- Use
createSlateEditorfor:- pure plugin option stores
- selector extension
- plugin API composition
- transform composition
- parser and deserializer contracts
- HTML
insertData - DnD-style contracts
- Grow the compile-only type lane here first:
- plugin creation
- editor creation
- inference
- option merging
- API merging
- Port upstream Slate React invariants by behavior, not by file.
- When a core source test mounts
Platewhile the same run also loads public-package React entrypoints, treat duplicate-instance warnings as test noise. Suppress them in the test wrapper withsuppressInstanceWarninginstead of changing runtime warning logic. - For provider-only React specs in core, reuse the shared
packages/core/src/react/__tests__/TestPlate.tsxhelper instead of re-declaring localconst Plate = ...wrappers.
selection
moveSelectionandshiftSelectionstay on Plate. They are genuinely Plate-bound exceptions, not cleanup debt.
docx, docx-io, and app integration
- Keep app-owned cross-package integration tests under
apps/www/src/__tests__/package-integration. - Keep buckets local under that folder instead of scattering app-owned integration coverage through
src/lib. - Package tests must not pull app aliases, app kits, or registries into package graphs.
- Fixture-heavy
docxanddocx-iosuites are valid reasons to keep__tests__/.
slate
- Focus on pure editor, query, and transform behavior first.
- Keep runtime coverage on navigation, selection math, structural queries, transform edge cases, extension transforms, and
createEditorlegacy sync. - Keep a small compile-only type lane for public
@platejs/slatecontracts. - Use selective upstream mining. Pull invariants that cheaply improve local public-contract coverage; do not mirror upstream blindly.
- Add direct helper specs for custom Slate code when indirect coverage is lying.
- Use
lcovas package truth. Bun’s text coverage summary is noisy for targeted package runs. - Stop once the remaining misses are mostly deferred DOM wrappers plus low-risk non-DOM dust.
- Later utility and core work should mine these upstream
slate-reactinvariants:use-slate-selector: selector equality and stale-rerender preventionuse-slate: editor version and subscription behavioruse-selected: selection rerender and path stabilityeditable: value-change vs selection-change partitioningdecorations: decoration propagation and redecorate behaviorchunking: chunk or index invalidation only if remaining core gaps justify it
- Skip
react-editorDOM focus coverage unless a real Plate bug forces it. - Playwright example coverage stays out.
Reviewed Exceptions
createPlateEditor allowlist
Keep createPlateEditor when the contract is actually about:
- React or provider wiring
- rendered output or DOM behavior
- store rerender semantics
- Plate plugin conversion boundaries
- the known Plate-only selection APIs:
moveSelectionandshiftSelection
Do not treat these files as backlog just because they still use Plate.
__tests__/ allowlist
Keep __tests__/ when it holds:
- package-local helpers or fixture banks
- intentionally split multi-file suites like
withAutoformat - fixture-heavy integration suites like
docxanddocx-io - app-owned cross-package integration suites under
apps/www/src/__tests__/package-integration
Quick Reference
- Start with the smallest seam that proves the contract:
createEditor->createSlateEditor->createPlateEditor. - Work in passes:
>= 6first, rerun coverage, then worthwhile>= 5, then switch to architecture-safety targets. - Use
bun run testfor the fast default loop. Usepnpm test:allfor the full suite. - Use
bun run test:profileto inspect the fast loop andbun run test:slowestto enforce it. pnpm test:slowesthas two bands: warning zone for local drift and hard CI failure thresholds. Fix the warning zone before it becomes a PR failure.- Use Bun globals. Do not import them from
bun:test. - Keep specs beside the implementation. Use
__tests__/only for helpers, fixtures, or intentional split or integration suites. - Use plain objects for simple state. Use JSX hyperscript only when tree or selection shape is the contract.
- Prefer explicit assertions. Use render-returned queries over
screen, and use direct style-property assertions overtoHaveStyle. - Delete skips, commented tests, dead smoke tests, and stale snapshot files after broad title renames.
- Package tests must stay package-local. No app registries, no app kits, no cross-spec imports.
- For package-only coverage decisions, trust
lcov, not Bun’s broad text summary. - Keep exactly two lanes:
- fast lane:
*.spec.ts[x], run bypnpm test - slow lane:
*.slow.ts[x], run bypnpm test:slow
- fast lane:
- Use
pnpm test:allfor the full repo test run instead of relying on barebun test. - Stop before 100%. When the remaining misses are wrappers, DOM/provider dust, sludge, or crumbs, you are done.
Signals
- GitHub stars
- 17k
- Forks
- 996
- Last commit
- Sep 2026
- Hacker News mentions
- 20
Advanced
- Catalog kind
- skill
- Gateway key
testing-4- Source
- github.com/udecode/plate