test-style

star 0

Conventions for writing tests in silkdown so they double as documentation and produce explicit, easy-to-pinpoint failure messages. Auto-trigger when creating or editing any file matching packages/*/test/**/*.test.{ts,tsx} or e2e/**/*.spec.ts, when adding new vitest or Playwright tests, when reviewing existing tests for clarity, or when triaging a failing assertion that "doesn't tell you which thing broke". Also fires on phrases like "name this test", "test description", "split this test", "expect message", "assertion message", "expect.soft", "tests as documentation", "explicit failure", "vitest assert", "playwright test name", "what's a good test name".

magarcia By magarcia schedule Updated 5/5/2026

name: test-style description: Conventions for writing tests in silkdown so they double as documentation and produce explicit, easy-to-pinpoint failure messages. Auto-trigger when creating or editing any file matching packages//test/**/.test.{ts,tsx} or e2e/**/*.spec.ts, when adding new vitest or Playwright tests, when reviewing existing tests for clarity, or when triaging a failing assertion that "doesn't tell you which thing broke". Also fires on phrases like "name this test", "test description", "split this test", "expect message", "assertion message", "expect.soft", "tests as documentation", "explicit failure", "vitest assert", "playwright test name", "what's a good test name".

Test style — silkdown

Tests in this repo are read at least as often as they are run. They serve as the executable specification for the public API: a contributor (or AI agent) should be able to grep for the behaviour they're about to change, read the matching test names, and understand both what the system promises and why. When a test fails in CI, the message must point at the specific behaviour that broke — not at "expected true to be false".

Layered on top of tdd (which covers the loop) and react-best-practices (component testing rules). When in doubt, prefer integration / "social unit" tests that mount the real EditorView over isolated mocks.

Principles

  1. Test names describe behaviour, in full sentences<does X> when <Y>. Read-aloud check: a contributor with no context can predict the assertions from the title alone. → references/naming.md
  2. One behaviour per test — split compound names ("wraps and unwraps") into two tests. The cost is 5 lines; the gain is that CI tells you which half broke. → references/naming.md
  3. describe blocks name the behaviour area, not the filedescribe("inline reveal — bold (StrongEmphasis)"), not describe("tests"). The describe name is half the test sentence. → references/naming.md
  4. Every non-self-explanatory assertion gets a second-arg messageexpect(value, "what should be true").toBe(...). Required when multiple assertions in one test could fail ambiguously, when the literal compared value (null, true, a number) doesn't communicate intent, or when the diff would be a long DOM dump. → references/assertions.md
  5. assert(value, "msg") over if (!value) return; — the if-return pattern silently passes when fixtures are wrong. Use vitest's assert to fail loudly with a fixture-pointing message and narrow the type for the rest of the test. → references/assertions.md
  6. Don't drill through optional chains in the assertion targetexpect(link?.getAttribute("...")) hides the root cause. Assert link first, then drill in directly. → references/assertions.md
  7. Name magic offsets — pull them from the doc string itself or hoist to a named constant. A future reader sees cursorOnLine2 and knows the scenario. → references/structure.md
  8. Always destroy mounted views and restore stubbed globalsafterEach calls view.destroy(), restores window.open, runs cleanup() for testing-library. Leaks corrupt the next test. → references/structure.md
  9. e2e: assertion at every meaningful intermediate state — multi-press sequences must report which press broke things, not just the final state. → references/structure.md
  10. Don't mock @codemirror/* or @silkdown/core collaborators — repo tests are integration-style. Mocks are reserved for browser APIs (window.open) and external IO. → references/structure.md

Forbidden patterns (review-stoppers)

  • it("should ..."), it("works"), it("renders correctly"), it("test ...").
  • Compound names joined with and/,: "wraps and unwraps".
  • if (!fixture) return; — replaced with assert.
  • expect(maybeNull?.something).toBe(...) — assert maybeNull first.
  • expect(true).toBe(true) placeholder assertions.
  • Mocking @codemirror/* or @silkdown/core collaborators.

Pre-commit checklist

  • Title reads as a complete behavioural sentence.
  • Title would still be accurate if the implementation were rewritten.
  • Each assertion has either (a) a message, or (b) a self-explanatory failure value.
  • No if (!x) return; — replaced with assert.
  • No magic numeric offsets — named constants or computed from the doc.
  • If the test mounts an EditorView, afterEach calls view.destroy().
  • If the test stubs a global, afterEach restores it.
  • Two scenarios that diverge in one assertion are two its, not one.

Examples

Four shapes cover ~95% of tests in this repo. See references/examples.md:

  • Example A — pure-logic util test (URL policy / parser).
  • Example B — integration test with a real EditorView (decoration / widget).
  • Example C — React component test (view-stability invariant).
  • Example D — Playwright e2e with intermediate-state assertions on a keyboard sequence.
Install via CLI
npx skills add https://github.com/magarcia/silkdown --skill test-style
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator