Skip to main content

scenarioOutline()

Docs for Vitest SDK v0.3.3

scenarioOutline runs the same scenario multiple times with different data sets. The Examples table is embedded directly in the title string, and each row produces a separate test run.

import { feature, scenarioOutline, given, when, Then as then } from "@swedevtools/livedoc-vitest";

feature("Login Validation", () => {
scenarioOutline(`Validate credentials
Examples:
| username | password | result |
| alice | correct | success |
| alice | wrong | failure |
| unknown | any | failure |
`, (ctx) => {
given("a user enters '<username>' and '<password>'", () => {
credentials = { user: ctx.example.username, pass: ctx.example.password };
});
when("they submit the login form", () => {
result = login(credentials);
});
then("the result is '<result>'", () => {
expect(result.status).toBe(ctx.example.result);
});
});
});

Reference​

scenarioOutline(title, fn)​

Registers a data-driven scenario. The title must contain an Examples: table. The callback runs once per table row.

function scenarioOutline(title: string, fn: (ctx: ScenarioOutlineCtx) => void): void

See more examples below.

Parameters​

  • title: string — The scenario outline title followed by an Examples: table. Parsed as:

    • First line → scenario title (may include tags on @-prefixed lines and description on remaining lines, same as scenario())
    • Examples: block → pipe-delimited table where the first row is headers and subsequent rows are data
  • fn: (ctx: ScenarioOutlineCtx) => void — Callback containing step functions. Must not be async. Runs once per example row.

The ctx Parameter​

PropertyTypeDescription
ctx.featureFeatureContext{ filename, title, description, tags }
ctx.scenarioScenarioContext{ title, description, tags, given?, and[], steps }
ctx.exampleRecord<string, any>Current row data keyed by column header name

The Examples Table​

The table uses pipe-delimited syntax. Values are auto-coerced:

Input in TableCoerced Type
42number
3.14number
trueboolean
falseboolean
hellostring

Placeholders in Step Titles​

Use <columnName> in step titles. These are display-only — they are substituted with the current row's value in test output but have no effect on execution. Always access data via ctx.example.

// <username> is replaced with "alice" in the test report
given("a user named '<username>'", () => {
// Access the actual value through ctx.example
const name = ctx.example.username;
});

Returns​

void — Scenario outlines are registered as side effects.

Caveats​

  • Not async: The callback must be synchronous. Use async inside individual steps.
  • The Examples: keyword is required. Without it, no data rows are parsed and the outline runs zero times.
  • Column names become property names on ctx.example. Use valid JavaScript identifiers (e.g. unit_price, not unit price).
  • Values in ctx.example are type-coerced by default. If you need raw strings, convert explicitly.

Modifiers​

scenarioOutline.skip(title, fn)​

Skip this scenario outline entirely.

scenarioOutline.skip(`Not ready
Examples:
| input | expected |
| a | b |
`, () => { /* skipped */ });

scenarioOutline.only(title, fn)​

Run only this scenario outline.

scenarioOutline.only(`Debugging this outline
Examples:
| input | expected |
| a | b |
`, () => { /* only this runs */ });

Usage​

Basic: Simple data-driven scenario​

import { feature, scenarioOutline, given, when, Then as then } from "@swedevtools/livedoc-vitest";

feature("Calculator", () => {
scenarioOutline(`Addition
Examples:
| a | b | sum |
| 1 | 2 | 3 |
| 10 | 20 | 30 |
| -1 | 1 | 0 |
`, (ctx) => {
let result: number;

given("the first number is '<a>'", () => {
// ctx.example.a is already a number due to auto-coercion
});

when("I add '<a>' and '<b>'", () => {
result = ctx.example.a + ctx.example.b;
});

then("the result is '<sum>'", () => {
expect(result).toBe(ctx.example.sum);
});
});
});

Multiple example tables​

You can define multiple named Examples: blocks. All rows from all tables are merged and executed.

import { feature, scenarioOutline, given, when, Then as then } from "@swedevtools/livedoc-vitest";

feature("Shipping", () => {
scenarioOutline(`Shipping rates by region

Examples: Domestic
| weight | rate |
| 1 | 5.00 |
| 5 | 12.00 |
| 10 | 20.00 |

Examples: International
| weight | rate |
| 1 | 15.00 |
| 5 | 35.00 |
| 10 | 60.00 |
`, (ctx) => {
let shippingCost: number;

given("a package weighing '<weight>' kg", () => {
// weight is auto-coerced to number
});

when("the shipping rate is calculated", () => {
shippingCost = calculateShipping(ctx.example.weight);
});

then("the rate is '<rate>'", () => {
expect(shippingCost).toBe(ctx.example.rate);
});
});
});

The table labels (Domestic, International) are for documentation only — they appear in test output to explain the grouping but do not affect execution.

With tags and description​

import { feature, scenarioOutline, given, when, Then as then } from "@swedevtools/livedoc-vitest";

feature("Email Validation", () => {
scenarioOutline(`Validate email formats
@validation @email
Ensures that the email validator correctly identifies valid and invalid formats

Examples:
| email | valid |
| user@example.com | true |
| invalid | false |
| user@domain.org | true |
| @nodomain.com | false |
| user@.com | false |
`, (ctx) => {
let isValid: boolean;

when("validating '<email>'", () => {
isValid = validateEmail(ctx.example.email);
});

then("the result is '<valid>'", () => {
expect(isValid).toBe(ctx.example.valid);
});
});
});

Combining with background​

import { feature, scenarioOutline, background, given, when, Then as then } from "@swedevtools/livedoc-vitest";

feature("Pricing Engine", () => {
let engine: PricingEngine;

background("Engine is initialized", () => {
given("the pricing engine is running", () => {
engine = new PricingEngine();
});
});

scenarioOutline(`Discount tiers
Examples:
| spend | discount |
| 50 | 0 |
| 100 | 5 |
| 500 | 15 |
`, (ctx) => {
when("a customer spends '<spend>' dollars", () => {
engine.setSpend(ctx.example.spend);
});

then("the discount is '<discount>' percent", () => {
expect(engine.getDiscount()).toBe(ctx.example.discount);
});
});
});

See Also​