@probitas/scenario
Core scenario definition types and utilities.
This package provides the fundamental type definitions that represent scenario structures, along with utilities for loading scenario files and filtering scenarios using selectors. It serves as the foundation layer that other Probitas packages build upon.
Links
- GitHub Repository
- @probitas/probitas - Main package (recommended for most users)
Related Packages
| Package | Description |
|---|---|
| @probitas/builder | Uses these types to build scenarios |
| @probitas/runner | Executes scenario definitions |
| @probitas/discover | Discovers scenario files to load |
Type Definitions
The type system is designed around immutable data structures:
ScenarioDefinition- Complete scenario with name, options, and entriesStepDefinition- Individual step with function, options, and sourceSetupDefinition- Setup hook with cleanup function supportResourceDefinition- Named resource with fn functionStepContext- Context object passed to all functionsEntry- Discriminated union of step, setup, or resource
Options Types
ScenarioOptions- Scenario-level configuration (tags, default step options)StepOptions- Step execution settings (timeout, retry strategy)Source- File and line number for error reporting
Function Types
StepFunction- Signature for step execution functionsSetupFunction- Signature for setup hooks (returns cleanup)ResourceFunction- Signature for resource creation functionsSetupCleanup- Return type of setup functions
Loader Utilities
loadScenarios- Load scenario definitions from file pathsLoadScenariosOptions- Options for the loader
Selector Utilities
Selectors provide powerful filtering capabilities:
applySelectors- Filter scenarios using selector stringsparseSelector- Parse selector string into Selector objectsmatchesSelector- Check if a scenario matches a single selectorSelector- Parsed selector objectSelectorType- Type of selector ("tag" or "name")
Installation
deno add jsr:@probitas/scenarioInterfaces
#LoadScenariosOptions
interface LoadScenariosOptionsOptions for loading scenarios from files.
Examples
Handling import errors
const scenarios = await loadScenarios(files, {
onImportError: (file, err) => {
console.error(`Failed to load ${file}:`, err);
}
});
| Name | Description |
|---|---|
onImportError | Callback invoked when a scenario file fails to import. |
Properties
onImportError?(scenarioFile: string | URL, err: unknown) => unknownCallback invoked when a scenario file fails to import.
Use this for custom error handling or logging. If not provided, import errors are logged but otherwise silently ignored (the file is skipped and loading continues with other files).
#ScenarioDefinition
interface ScenarioDefinitionComplete, immutable definition of a scenario.
This is the core type produced by the builder and consumed by the runner. It contains everything needed to execute a scenario: its name, options, and ordered sequence of entries (steps, resources, setups).
Examples
Typical scenario structure
// Created by: scenario("Login Flow").step(...).build()
const definition: ScenarioDefinition = {
name: "Login Flow",
options: {
tags: ["auth", "smoke"],
stepOptions: { timeout: 30000, retry: { maxAttempts: 1, backoff: "linear" } }
},
entries: [
{ kind: "resource", value: { name: "api", fn: ... } },
{ kind: "step", value: { name: "Login", fn: ..., options: ... } },
{ kind: "step", value: { name: "Verify", fn: ..., options: ... } }
],
source: { file: "/tests/auth.probitas.ts", line: 5 }
};
| Name | Description |
|---|---|
name | Human-readable scenario name (displayed in reports and CLI) |
tags | Tags for filtering and organizing scenarios. |
steps | Ordered sequence of entries (resources → setups → steps) |
source | Source source where the scenario was defined |
Properties
- readonly
namestringHuman-readable scenario name (displayed in reports and CLI)
Ordered sequence of entries (resources → setups → steps)
Source source where the scenario was defined
#ScenarioMetadata
interface ScenarioMetadataSerializable scenario metadata (without executable functions).
Used by the JSON reporter and tooling to output scenario information without including non-serializable function references.
Examples
JSON reporter output
{
"name": "Login Flow",
"options": { "tags": ["auth"], "stepOptions": { ... } },
"entries": [
{ "kind": "step", "value": { "name": "Login", "options": { ... } } }
],
"source": { "file": "/tests/auth.probitas.ts", "line": 5 }
}
| Name | Description |
|---|---|
name | Scenario name |
tags | Tags for filtering and organizing scenarios. |
steps | Entry metadata (functions omitted for serialization) |
source | Source source where the scenario was defined |
Properties
- readonly
namestringScenario name
Entry metadata (functions omitted for serialization)
Source source where the scenario was defined
#ScenarioOptions
interface ScenarioOptionsConfiguration options for scenario execution.
Defines metadata and default behavior for an entire scenario.
Examples
const options: ScenarioOptions = {
tags: ["api", "integration", "slow"],
stepOptions: {
timeout: 60000,
retry: { maxAttempts: 2, backoff: "linear" }
}
};
| Name | Description |
|---|---|
tags | Tags for filtering and organizing scenarios. |
stepOptions | Default options applied to all steps in this scenario. |
Properties
Default options applied to all steps in this scenario.
Individual steps can override these defaults by specifying their own options in the
.step()call.
#Selector
interface SelectorParsed selector for filtering scenarios.
Created by parseSelector. Used by matchesSelector
and applySelectors to filter scenarios.
Examples
// Result of parseSelector("tag:api")
const selector: Selector = {
type: "tag",
value: /api/i,
negated: false
};
| Name | Description |
|---|---|
type | Type of match: "tag" for tags, "name" for scenario name |
value | Regular expression pattern for matching (case-insensitive) |
negated | If true, selector matches scenarios that do NOT match the pattern |
Properties
Type of match: "tag" for tags, "name" for scenario name
- readonly
valueRegExpRegular expression pattern for matching (case-insensitive)
- readonly
negatedbooleanIf true, selector matches scenarios that do NOT match the pattern
#Source
interface SourceSource source in a file for error reporting and debugging.
Captured automatically when defining scenarios, steps, and setups. Used by reporters to show meaningful stack traces.
Examples
// Example source object
const source: Source = {
file: "/project/tests/auth.probitas.ts",
line: 42
};
| Name | Description |
|---|---|
file | Absolute file path where the element was defined |
line | Line number in the file (1-indexed) |
column | Column number in the file (1-indexed) |
Properties
- readonly
filestringAbsolute file path where the element was defined
- readonly
line?numberLine number in the file (1-indexed)
- readonly
column?numberColumn number in the file (1-indexed)
#StepContext
interface StepContextExecution context provided to steps, resources, and setup hooks.
The context provides access to:
- Previous step results with full type inference
- All accumulated results as a typed tuple
- Named resources registered with
.resource() - Shared storage for cross-step communication
- Abort signal for timeout and cancellation handling
Examples
Accessing previous result
scenario("Chained Steps")
.step("First", () => ({ id: 123 }))
.step("Second", (ctx) => {
console.log(ctx.previous.id); // 123 (typed as number)
})
.build();
Using shared store
scenario("Store Example")
.setup((ctx) => {
ctx.store.set("startTime", Date.now());
})
.step("Check duration", (ctx) => {
const start = ctx.store.get("startTime") as number;
console.log(`Elapsed: ${Date.now() - start}ms`);
})
.build();
| Name | Description |
|---|---|
index | Current step index (0-based). |
previous | Result from the previous step. |
results | All accumulated results as a typed tuple. |
store | Shared key-value storage for cross-step communication. |
resources | Named resources registered with `.resource()`. |
signal | Abort signal that fires on timeout or manual cancellation. |
Properties
- readonly
indexnumberCurrent step index (0-based).
Useful for conditional logic based on position in the scenario.
- readonly
previousunknownResult from the previous step.
Fully typed based on what the previous step returned. For the first step, this is
unknown. - readonly
resultsreadonly unknown[]All accumulated results as a typed tuple.
Allows accessing any previous result by index:
ctx.results[0] // First step's result ctx.results[1] // Second step's result - readonly
storeMap<string, unknown>Shared key-value storage for cross-step communication.
Use this for data that doesn't fit the step result pattern, such as metadata or configuration set during setup.
- readonly
resourcesRecord<string, unknown>Named resources registered with
.resource().Resources are typed based on their registration:
.resource("db", () => createDbConnection()) .step((ctx) => ctx.resources.db.query(...)) - readonly
signal?AbortSignalAbort signal that fires on timeout or manual cancellation.
Pass this to fetch() or other APIs that support AbortSignal for proper timeout handling.
#StepDefinition
interface StepDefinition<T = unknown>Immutable definition of a scenario step.
Contains all information needed to execute a single step: the step function, its options, and debugging metadata.
| Name | Description |
|---|---|
kind | — |
name | Human-readable step name (displayed in reports) |
fn | Step function to execute |
timeout | Maximum execution time in milliseconds. |
retry | Retry configuration for handling transient failures |
source | Source source where the step was defined (for error messages) |
Properties
- readonly
kind"step" | "resource" | "setup" - readonly
namestringHuman-readable step name (displayed in reports)
Step function to execute
- readonly
timeoutnumberMaximum execution time in milliseconds.
If the step takes longer, a
TimeoutErroris thrown. Default: 30000 (30 seconds) - readonly
retry{ maxAttempts: number; backoff: "linear" | "exponential" }Retry configuration for handling transient failures
Source source where the step was defined (for error messages)
#StepOptions
interface StepOptionsConfiguration options for individual step execution.
Controls timeout and retry behavior for a step. These options can be set at:
- Step level (highest priority)
- Scenario level (applies to all steps in scenario)
- Default values (30s timeout, no retry)
Examples
const options: StepOptions = {
timeout: 60000, // 60 seconds
retry: {
maxAttempts: 3,
backoff: "exponential" // Waits 1s, 2s, 4s between retries
}
};
| Name | Description |
|---|---|
timeout | Maximum execution time in milliseconds. |
retry | Retry configuration for handling transient failures |
Properties
- readonly
timeout?numberMaximum execution time in milliseconds.
If the step takes longer, a
TimeoutErroris thrown. Default: 30000 (30 seconds) - readonly
retry?{ maxAttempts?: number; backoff?: "linear" | "exponential" }Retry configuration for handling transient failures
Functions
#applySelectors
function applySelectors(scenarios: ScenarioDefinition[], selectorInputs: readonly string[]): ScenarioDefinition[]Filter scenarios using selector strings with AND/OR/NOT logic.
This is the main entry point for scenario filtering. It combines multiple selector strings with the following logic:
- Multiple strings: OR condition (match any)
- Comma-separated in string: AND condition (match all)
!prefix: NOT condition (exclude matches)
Parameters
scenariosScenarioDefinition[]- Array of scenarios to filter
selectorInputsreadonly string[]- Selector strings from CLI
-sflags
- Selector strings from CLI
Returns
ScenarioDefinition[] — Filtered scenarios matching the selector criteria
Examples
OR logic - match any selector string
// Scenarios with "api" tag OR "db" tag
applySelectors(scenarios, ["tag:api", "tag:db"]);
AND logic - match all within comma-separated
// Scenarios with BOTH "api" AND "critical" tags
applySelectors(scenarios, ["tag:api,tag:critical"]);
Combined AND/OR/NOT
// (api AND critical) OR (db AND !slow)
applySelectors(scenarios, [
"tag:api,tag:critical",
"tag:db,!tag:slow"
]);
Exclude by name pattern
// All scenarios except those with "wip" in name
applySelectors(scenarios, ["!wip"]);
#loadScenarios
async function loadScenarios(scenarioFiles: readonly unknown[], options?: LoadScenariosOptions): Promise<ScenarioDefinition[]>Load scenario definitions from file paths.
Dynamically imports scenario files and extracts their default exports. Supports both single scenario exports and arrays of scenarios.
Parameters
scenarioFilesreadonly unknown[]- Absolute file paths or file:// URLs to load
options?LoadScenariosOptions- Optional error handling configuration
Returns
Promise<ScenarioDefinition[]> — Array of loaded scenario definitions
Examples
Loading discovered files
import { discoverScenarioFiles } from "@probitas/discover";
import { loadScenarios } from "@probitas/scenario";
const files = await discoverScenarioFiles({
includes: ["**\/*.probitas.ts"]
});
const scenarios = await loadScenarios(files);
Loading specific files
const scenarios = await loadScenarios([
"/project/tests/auth.probitas.ts",
"/project/tests/api.probitas.ts"
]);
#matchesSelector
function matchesSelector(scenario: ScenarioDefinition, selector: Selector): booleanCheck if a scenario matches a single selector.
Tests whether the scenario's name or tags match the selector's pattern. Does not apply negation - returns the raw match result.
Parameters
scenarioScenarioDefinition- Scenario definition to test
selectorSelector- Selector containing the pattern to match
Returns
boolean — `true` if the scenario matches the pattern (before negation)
Examples
const scenario = { name: "Login Test", options: { tags: ["auth"] } };
matchesSelector(scenario, { type: "tag", value: /auth/i, negated: false });
// → true
matchesSelector(scenario, { type: "name", value: /login/i, negated: false });
// → true
matchesSelector(scenario, { type: "tag", value: /api/i, negated: false });
// → false
#parseSelector
function parseSelector(input: string): Selector[]Parse a selector string into an array of Selector objects.
Selector syntax:
tag:pattern- Match scenarios with a tag matching the patternname:pattern- Match scenarios with a name matching the patternpattern- Shorthand forname:pattern!selector- Negate the selector (exclude matches)sel1,sel2- Combine selectors with AND logic
Parameters
inputstring- Selector string to parse
Returns
Selector[] — Array of parsed Selector objects (comma-separated = multiple selectors)
Examples
Basic selectors
parseSelector("tag:api");
// → [{ type: "tag", value: /api/i, negated: false }]
parseSelector("login"); // Shorthand for name:login
// → [{ type: "name", value: /login/i, negated: false }]
Negation
parseSelector("!tag:slow");
// → [{ type: "tag", value: /slow/i, negated: true }]
Combined selectors (AND logic)
parseSelector("tag:api,!tag:slow");
// → [
// { type: "tag", value: /api/i, negated: false },
// { type: "tag", value: /slow/i, negated: true }
// ]
Type Aliases
#SelectorType
type SelectorType = "tag" | "name"Type of selector for filtering scenarios.
"tag": Match against scenario tags"name": Match against scenario name
#SetupCleanup
type SetupCleanup = void | unknown | Disposable | AsyncDisposableCleanup handler returned by setup functions.
Setup functions can return various cleanup mechanisms that are automatically invoked after the scenario completes (regardless of success or failure).
Supported cleanup patterns:
void/undefined: No cleanup needed() => void: Synchronous cleanup function() => Promise<void>: Async cleanup functionDisposable: Object with[Symbol.dispose]()methodAsyncDisposable: Object with[Symbol.asyncDispose]()method
#StepFunction
type StepFunction<T = unknown> = (ctx: StepContext) => unknownFunction signature for step execution.
A step function receives the execution context and returns a value (sync or async) that becomes available to subsequent steps.
#StepMetadata
type StepMetadata = Omit<StepDefinition, "fn">Serializable step metadata (without the function).
Used for JSON output, tooling, and inspection without executing code.
