@probitas/runner
Test execution and orchestration engine for Probitas scenarios.
This package provides the ScenarioRunner class that executes
scenario definitions built with @probitas/builder. It handles the complete
test lifecycle including resource initialization, setup/cleanup hooks,
step execution with retry logic, and result reporting.
Links
- GitHub Repository
- @probitas/probitas - Main package (recommended for most users)
Related Packages
| Package | Description |
|---|---|
| @probitas/builder | Build scenario definitions to execute |
| @probitas/scenario | Core type definitions |
| @probitas/reporter | Output formatters for results |
| @probitas/cli | CLI that uses this runner |
Key Features
- Concurrent execution: Run multiple scenarios in parallel with configurable concurrency
- Failure control: Stop after N failures or continue through all tests
- Resource lifecycle: Automatic initialization and disposal of resources
- Retry logic: Built-in retry with linear/exponential backoff strategies
- Reporter integration: Pluggable reporters for custom output formatting
- Abort support: Cancel running tests via AbortSignal
Core Exports
ScenarioRunner- Main class for executing scenariosSkip- Exception class to skip scenarios conditionallyReporter- Interface for observing test execution eventsRunOptions- Configuration options for test runsRunResult- Aggregated results from a test runScenarioResult- Result from executing a single scenarioStepResult- Result from executing a single step
Error Types
TimeoutError- Thrown when a step exceeds its timeoutRetryExhaustedError- Thrown when all retry attempts fail
Installation
deno add jsr:@probitas/runnerClasses
#Runner
class RunnerTop-level test runner that orchestrates execution of multiple scenarios.
The Runner manages:
- Parallel execution with concurrency control (
maxConcurrency) - Early stopping on failures (
maxFailures) - Test lifecycle events through the Reporter interface
- Aggregated results in RunResult
Examples
const reporter = new ListReporter();
const runner = new Runner(reporter);
const result = await runner.run(scenarios, {
maxConcurrency: 4,
maxFailures: 1,
});
| Name | Description |
|---|---|
run() | Execute all scenarios and return aggregated results. |
Constructor
new Runner(reporter: Reporter)Create a new Runner with the given reporter.
Methods
run(): unknownExecute all scenarios and return aggregated results.
Emits events through the Reporter:
onRunStart- Before execution starts- For each scenario (concurrently):
- Delegates to ScenarioRunner for scenario-level execution
- ScenarioRunner emits scenario and step level events
onRunEnd- After all scenarios complete with RunResult
#Skip
class Skip extends ErrorErrorException class for conditionally skipping scenario execution.
Throw Skip from any step, setup, or resource fn to skip the
entire scenario. Skipped scenarios are counted separately in the
summary and don't count as failures.
Common use cases:
- Feature flags or environment checks
- Prerequisites not met
- Platform-specific tests
- Temporary test disabling with clear reason
Examples
Skip based on environment
import { scenario, Skip } from "@probitas/probitas";
scenario("Production Only")
.step("Check environment", () => {
if (Deno.env.get("ENV") !== "production") {
throw new Skip("Only runs in production");
}
})
.step("Production test", () => { ... })
.build();
Skip based on resource availability
scenario("Database Test")
.resource("db", async () => {
try {
return await Database.connect();
} catch {
throw new Skip("Database not available");
}
})
.step("Query data", (ctx) => ctx.resources.db.query(...))
.build();
Skip with feature flag
scenario("Beta Feature")
.step("Check feature flag", (ctx) => {
if (!ctx.store.get("betaEnabled")) {
throw new Skip("Beta feature not enabled");
}
})
.build();
| Name | Description |
|---|---|
reason | Human-readable reason for skipping. |
Constructor
new Skip(reason?: string)Create a Skip exception.
Properties
- readonly
reason?stringHuman-readable reason for skipping.
When provided, this reason is displayed in test reports. If not provided, defaults to
undefined(the error message will still show "Skipped").
Interfaces
#Reporter
interface Reporter| Name | Description |
|---|---|
onRunStart() | Called when the test run starts, before any scenarios execute. |
onRunEnd() | Called when test run completes |
onScenarioStart() | Called when a scenario begins execution. |
onScenarioEnd() | Called when scenario completes |
onStepStart() | Called when step starts |
onStepEnd() | Called when step completes (passed, failed, or skipped). |
Methods
onRunStart(scenarios: readonly ScenarioDefinition[]): void | Promise<void>Called when the test run starts, before any scenarios execute.
Parameters
scenariosreadonly ScenarioDefinition[]
onRunEnd(scenarios: readonly ScenarioDefinition[], result: RunResult): void | Promise<void>Called when test run completes
Parameters
scenariosreadonly ScenarioDefinition[]resultRunResult
onScenarioStart(scenario: ScenarioDefinition): void | Promise<void>Called when a scenario begins execution.
Parameters
scenarioScenarioDefinition
onScenarioEnd(scenario: ScenarioDefinition, result: ScenarioResult): void | Promise<void>Called when scenario completes
Parameters
scenarioScenarioDefinitionresultScenarioResult
onStepStart(scenario: ScenarioDefinition, step: StepDefinition): void | Promise<void>Called when step starts
Parameters
scenarioScenarioDefinitionstepStepDefinition
onStepEnd(scenario: ScenarioDefinition, step: StepDefinition, result: StepResult): void | Promise<void>Called when step completes (passed, failed, or skipped).
The result contains status-specific information:
- If status is "passed": contains
valuefrom step execution - If status is "failed" or "skipped": contains
errorinformation
Parameters
scenarioScenarioDefinitionstepStepDefinitionresultStepResult
#RunFilter
interface RunFilterFilter configuration for selecting scenarios to run.
Allows filtering scenarios by tags and/or name pattern. Both conditions must match if both are specified (AND logic).
Examples
Filter by tags
const filter: RunFilter = {
tags: ["api", "integration"] // Must have BOTH tags
};
Filter by name pattern
const filter: RunFilter = {
pattern: /login/i // Name must contain "login"
};
| Name | Description |
|---|---|
tags | Tags that scenarios must have (all must match) |
pattern | Pattern to match against scenario name |
Properties
- readonly
pattern?string | RegExpPattern to match against scenario name
#RunOptions
interface RunOptionsConfiguration options for the scenario runner.
Controls execution behavior including concurrency, failure handling, and reporting.
Examples
Sequential execution with fail-fast
const options: RunOptions = {
reporter: new ListReporter(),
maxConcurrency: 1, // Run one at a time
maxFailures: 1 // Stop after first failure
};
Parallel execution with limit
const options: RunOptions = {
reporter: new DotReporter(),
maxConcurrency: 4 // Run up to 4 scenarios at once
};
| Name | Description |
|---|---|
maxConcurrency | Maximum number of scenarios to run in parallel. |
maxFailures | Maximum number of failures before stopping the run. |
signal | Abort signal for external cancellation. |
Properties
- readonly
maxConcurrency?numberMaximum number of scenarios to run in parallel.
undefinedor0: Unlimited (all scenarios run in parallel)1: Sequential execution (one at a time)n: Run up to n scenarios concurrently
- readonly
maxFailures?numberMaximum number of failures before stopping the run.
undefined: Continue all scenarios regardless of failures1: Fail-fast (stop immediately on first failure)n: Stop after n failures
- readonly
signal?AbortSignalAbort signal for external cancellation.
When aborted, running scenarios complete but no new ones start.
#RunResult
interface RunResultSummary of all scenario executions in a test run.
Provides aggregate statistics and detailed results for the entire run.
Passed to Reporter.onRunEnd after all scenarios complete.
Examples
const summary: RunResult = {
total: 10,
passed: 8,
failed: 1,
skipped: 1,
duration: 5432,
scenarios: [...]
};
console.log(`${summary.passed}/${summary.total} passed`);
// → "8/10 passed"
| Name | Description |
|---|---|
total | Total number of scenarios in the run |
passed | Number of scenarios that passed |
failed | Number of scenarios that failed |
skipped | Number of scenarios that were skipped |
duration | Total execution time in milliseconds |
scenarios | Detailed result for each scenario |
Properties
- readonly
totalnumberTotal number of scenarios in the run
- readonly
passednumberNumber of scenarios that passed
- readonly
failednumberNumber of scenarios that failed
- readonly
skippednumberNumber of scenarios that were skipped
- readonly
durationnumberTotal execution time in milliseconds
Detailed result for each scenario
#ScenarioContext
interface ScenarioContextRuntime context for scenario execution.
Provides access to scenario metadata, accumulated results, shared storage,
and resources during scenario execution. This is the "live" counterpart
to the static ScenarioDefinition.
Examples
Accessing context in a step
scenario("Context Example")
.step("Use context", (ctx) => {
console.log(`Running: ${ctx.name}`);
console.log(`Tags: ${ctx.options.tags.join(", ")}`);
console.log(`Previous results: ${ctx.results.length}`);
})
.build();
| Name | Description |
|---|---|
name | Human-readable scenario name |
tags | — |
results | Array of all exec step results so far |
store | Shared key-value storage for cross-step communication |
resources | Named resources registered with `.resource()` |
signal | Abort signal (fires on timeout or manual cancellation) |
Properties
- readonly
namestringHuman-readable scenario name
- readonly
resultsunknown[]Array of all exec step results so far
- readonly
storeMap<string, unknown>Shared key-value storage for cross-step communication
- readonly
resourcesRecord<string, unknown>Named resources registered with
.resource() - readonly
signal?AbortSignalAbort signal (fires on timeout or manual cancellation)
Type Aliases
#ScenarioResult
type ScenarioResult = { status: "passed"; metadata: ScenarioMetadata; duration: number; steps: readonly StepResult[] } | { status: "failed" | "skipped"; metadata: ScenarioMetadata; duration: number; steps: readonly StepResult[]; error: unknown }Result from executing a complete scenario.
Contains the overall scenario status, timing, all step results,
and any error or skip reason. Passed to reporters and included
in the final RunResult.
#StepResult
type StepResult = { status: "passed"; metadata: StepMetadata; duration: number; value: unknown } | { status: "failed" | "skipped"; metadata: StepMetadata; duration: number; error: unknown }Result from executing a single step.
StepResult is a discriminated union type - the fields available depend on the status:
- "passed": Contains
value(return value from step function) - "failed": Contains
error(exception that was thrown) - "skipped": Contains
error(skip reason)
This type design ensures type-safe field access. Reporters should check
result.status to safely access status-specific fields.
