rule()
rule defines a single testable requirement inside a specification(). Unlike BDD
steps, rules contain direct assertions — no Given/When/Then ceremony. Rules also
support async callbacks, quoted value extraction, and named parameters.
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("Math Operations", () => {
rule("Adding '5' and '3' returns '8'", (ctx) => {
const [a, b, expected] = ctx.rule.values;
expect(a + b).toBe(expected);
});
});
Reference
rule(title, fn)
Registers a rule that maps to a Vitest it() call.
function rule(title: string, fn: (ctx: RuleCtx) => void | Promise<void>): void
Parameters
-
title:string— The rule description. Can contain:- Quoted values:
'value'→ extracted toctx.rule.values[](and alsoctx.step.values[]) - Named parameters:
<name:value>→ extracted toctx.rule.params(and alsoctx.step.params) - Tags and descriptions (same multi-line parsing as
feature()/scenario())
- Quoted values:
-
fn:(ctx: RuleCtx) => void | Promise<void>— The rule implementation. Supportsasync.
The ctx Parameter
| Property | Type | Description |
|---|---|---|
ctx.specification | SpecificationContext | { filename, title, description, tags } |
ctx.rule | RuleContext | { title, description, tags, specification, values, valuesRaw, params, paramsRaw } |
ctx.step | StepContext | Step-level data (same values/params as ctx.rule, plus table, docString) |
RuleContext Properties
| Property | Type | Description |
|---|---|---|
title | string | The rule title |
description | string | Description text (from multi-line title) |
tags | string[] | Tags extracted from the title |
specification | SpecificationContext | Reference to the parent specification context |
values | any[] | Type-coerced quoted values from the title |
valuesRaw | string[] | Raw string values before coercion |
params | Record<string, any> | Type-coerced named parameters from <name:value> |
paramsRaw | Record<string, string> | Raw string named parameters |
Returns
void — Rules are registered as Vitest it() calls.
Caveats
- Rules support
async— unlikefeature,scenario, andspecificationcallbacks. - Rules must be nested inside a
specification()call. - Both
ctx.ruleandctx.stepprovide extracted values/params. Usectx.rulefor rule-level data access (clearer intent). - Do not use Given/When/Then steps inside a rule — use direct assertions.
Modifiers
rule.skip(title, fn)
Skip this rule. It appears as pending in test output.
rule.skip("Feature not yet implemented", () => {
// This test body is not executed
});
rule.only(title, fn)
Run only this rule.
rule.only("Debugging this specific rule", () => {
expect(calculate()).toBe(42);
});
Usage
Basic: Direct assertion
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("Email Validation", () => {
rule("Email addresses must contain @", () => {
expect(validateEmail("invalid")).toBe(false);
expect(validateEmail("user@example.com")).toBe(true);
});
rule("Empty strings are rejected", () => {
expect(validateEmail("")).toBe(false);
});
});
Quoted values with ctx.rule.values
Values in single quotes are auto-extracted and type-coerced.
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("Calculator", () => {
rule("Adding '5' and '3' returns '8'", (ctx) => {
const [a, b, expected] = ctx.rule.values;
// a = 5 (number), b = 3 (number), expected = 8 (number)
expect(a + b).toBe(expected);
});
rule("Concatenating 'hello' and 'world' returns 'helloworld'", (ctx) => {
const [a, b, expected] = ctx.rule.values;
// a = "hello" (string), b = "world" (string)
expect(a + b).toBe(expected);
});
rule("Boolean 'true' is truthy", (ctx) => {
const [value] = ctx.rule.values;
// value = true (boolean)
expect(value).toBe(true);
});
});
Named parameters with ctx.rule.params
Named parameters use <name:value> syntax for self-documenting rule titles.
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("String Operations", () => {
rule("Reversing <input:hello> returns <expected:olleh>", (ctx) => {
const { input, expected } = ctx.rule.params;
expect(reverse(input)).toBe(expected);
});
rule("Uppercasing <input:hello> returns <expected:HELLO>", (ctx) => {
expect(ctx.rule.params.input.toUpperCase()).toBe(ctx.rule.params.expected);
});
});
Async rules
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("API Endpoints", () => {
rule("GET /api/users returns a list of users", async () => {
const response = await fetch("/api/users");
const data = await response.json();
expect(Array.isArray(data)).toBe(true);
expect(data.length).toBeGreaterThan(0);
});
rule("POST /api/users creates a new user", async () => {
const response = await fetch("/api/users", {
method: "POST",
body: JSON.stringify({ name: "Alice", email: "alice@example.com" }),
});
expect(response.status).toBe(201);
});
});
Combining values and params
import { specification, rule } from "@swedevtools/livedoc-vitest";
specification("Price Calculation", () => {
rule("Item priced at '$100' with <tax:10>% tax costs '$110'", (ctx) => {
const [price, , total] = ctx.rule.values; // 100, 110
const taxRate = ctx.rule.params.tax; // 10
expect(price + (price * taxRate / 100)).toBe(total);
});
});
See Also
specification()— parent container for rulesruleOutline()— data-driven variant with Examples table- Data Extraction APIs — full reference for value extraction and type coercion
- Context Object — full reference for
ctx.ruleandctx.specification - Steps:
given/when/then— BDD alternative for structured step sequences