Skip to main content

rule()

Docs for Vitest SDK v0.3.3

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

See more examples below.

Parameters​

  • title: string — The rule description. Can contain:

    • Quoted values: 'value' → extracted to ctx.rule.values[]
    • Named parameters: <name:value> → extracted to ctx.rule.params
    • Tags and descriptions (same multi-line parsing as feature()/scenario())
  • fn: (ctx: RuleCtx) => void | Promise<void> — The rule implementation. Supports async.

The ctx Parameter​

PropertyTypeDescription
ctx.specificationSpecificationContext{ filename, title, description, tags }
ctx.ruleRuleContext{ title, description, tags, specification, values, valuesRaw, params, paramsRaw }

RuleContext Properties​

PropertyTypeDescription
titlestringThe rule title
descriptionstringDescription text (from multi-line title)
tagsstring[]Tags extracted from the title
specificationSpecificationContextReference to the parent specification context
valuesany[]Type-coerced quoted values from the title
valuesRawstring[]Raw string values before coercion
paramsRecord<string, any>Type-coerced named parameters from <name:value>
paramsRawRecord<string, string>Raw string named parameters
attachmentsAttachment[]Evidence collected for this rule execution
attach(base64, options?)voidAdd base64 data; options: mimeType, kind, title
attachScreenshot(base64, title?)voidAdd a PNG screenshot
attachJSON(data, title?)voidAdd UTF-8 JSON data (objects are pretty-printed)

Returns​

void — Rules are registered as Vitest it() calls.

Caveats​

  • Rules support async — unlike feature, scenario, and specification callbacks.
  • Rules must be nested inside a specification() call.
  • Use ctx.rule for values, parameters, and attachments; ctx.step is only available inside BDD steps.
  • 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​

Attach evidence to a rule​

Attach evidence during the rule callback, not in the surrounding specification() declaration. Evidence is included in the rule's execution.attachments in exported TestRunV1 JSON and server payloads, including when the rule throws after attaching it.

rule("The response contains an order number", async (ctx) => {
const response = await getOrder();
ctx.rule.attachJSON(response, "Order response");
expect(response.orderNumber).toBeTruthy();
});

rule("The receipt image is generated", async (ctx) => {
const pngBase64 = await generateReceipt();
ctx.rule.attachScreenshot(pngBase64, "Receipt");
ctx.rule.attach(pngBase64, {
mimeType: "image/png",
kind: "image",
title: "Receipt image",
});
});

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​