specification()
Docs for Vitest SDK v0.3.3
specification is the top-level container in the Specification pattern — a simpler
alternative to BDD when you need to express technical rules, domain constraints, or
unit-level requirements without Given/When/Then ceremony.
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("Password Validation", () => {
rule("Password must be at least 8 characters", () => {
expect(isValidPassword("short")).toBe(false);
expect(isValidPassword("longenough")).toBe(true);
});
rule("Password must contain a number", () => {
expect(isValidPassword("noNumbers")).toBe(false);
expect(isValidPassword("has1number")).toBe(true);
});
});
Reference
specification(title, fn)
Registers a specification block that maps to a Vitest describe() call prefixed with "Specification: ".
function specification(title: string, fn: (ctx: SpecificationCtx) => void): void
Parameters
-
title:string— The specification title with optional tags and description. Parsed identically tofeature():- First line → specification title
- Lines starting with
@→ tags (space-separated) - Remaining lines → description text
-
fn:(ctx: SpecificationCtx) => void— Callback containingrule()and/orruleOutline()calls. Must not beasync.
The ctx Parameter
| Property | Type | Description |
|---|---|---|
ctx.specification | SpecificationContext | { filename, title, description, tags } |
Returns
void — Specifications are registered as side effects.
Caveats
- The callback must not be
async. Use async inside individualrule()callbacks. - Specifications are the outermost container in the specification pattern — they cannot be nested.
- Do not mix
scenario()inside aspecification()— userule()andruleOutline()instead.
Modifiers
specification.skip(title, fn)
Skip this specification. All rules inside are marked as pending.
specification.skip("Legacy Validation — migrating to new rules", () => {
rule("Old rule", () => { /* skipped */ });
});
specification.only(title, fn)
Run only this specification. All other specifications and features are skipped.
specification.only("Debugging this specification", () => {
rule("Focus rule", () => { /* only this runs */ });
});
Usage
Basic: Group related rules
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("Email Validation", () => {
rule("Email must contain an @ symbol", () => {
expect(isValidEmail("invalid")).toBe(false);
expect(isValidEmail("user@example.com")).toBe(true);
});
rule("Email must have a domain", () => {
expect(isValidEmail("user@")).toBe(false);
expect(isValidEmail("user@example.com")).toBe(true);
});
rule("Email domain must have a TLD", () => {
expect(isValidEmail("user@example")).toBe(false);
expect(isValidEmail("user@example.com")).toBe(true);
});
});
Tags and description
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification(`Tax Calculation Rules
@finance @tax @critical
Keeps tax results consistent for the domestic and
international orders covered by these rules.
`, (ctx) => {
rule("A 'CA' order of '100' has tax '7.25'", (ctx) => {
const [state, amount, expectedTax] = ctx.rule.values;
const tax = calculateTax({ state, amount });
expect(tax).toBe(expectedTax);
});
rule("A 'UK' order of '100' has tax '0'", (ctx) => {
const [country, amount, expectedTax] = ctx.rule.values;
const tax = calculateTax({ country, amount });
expect(tax).toBe(expectedTax);
});
});
Accessing specification metadata
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification(`Specification Metadata
@warehouse @v2
Readers can identify the subject and tags of this published specification.
`, (ctx) => {
rule("Read specification context", () => {
expect(ctx.specification.title).toBe("Specification Metadata");
expect(ctx.specification.tags).toEqual(["warehouse", "v2"]);
expect(ctx.specification.description).toContain("identify the subject and tags");
});
});
Mixing rule and ruleOutline
import { specification, rule, ruleOutline } from "@swedevtools/livedoc-vitest";
specification("Discount Engine", () => {
rule("No discount for orders under $50", () => {
expect(getDiscount(49)).toBe(0);
});
ruleOutline(`Discount tiers
Examples:
| spend | discount |
| 50 | 0 |
| 100 | 5 |
| 200 | 10 |
| 500 | 15 |
`, (ctx) => {
expect(getDiscount(ctx.example.spend)).toBe(ctx.example.discount);
});
});
See Also
rule()— individual test assertions within a specificationruleOutline()— data-driven rules with examples tablesfeature()— BDD alternative for user-story-driven tests- Context Object — full reference for
ctx.specification