@korvol/stepwright
v0.1.3
Published
Step-based framework for writing resilient Playwright scripts with rich failure context
Downloads
393
Readme
@korvol/stepwright
A step-based framework for writing resilient Playwright scripts with rich failure context, video recording, and trace capture.
Features
- Fluent API - Chain steps, checkpoints, and configuration
- Video & Trace Recording - Capture video and Playwright traces with configurable modes
- Rich Failure Context - Automatic capture of screenshots, DOM, and logs on failure
- Checkpoints - Group steps and define retry strategies
- Background Tasks - Handle modals, auth redirects, and dynamic content
- Reporters - Console and JSON reporters included, extensible architecture
- Failure Cases - Generate structured failure data for AI-powered fixing with Fixwright
Installation
npm install @korvol/stepwright playwright
# or
pnpm add @korvol/stepwright playwrightQuick Start
import { Stepwright, ConsoleReporter } from '@korvol/stepwright';
interface MyData {
username: string;
loggedIn: boolean;
[key: string]: unknown;
}
const script = Stepwright.create<MyData>('Login Flow')
.config({
headless: false,
defaultTimeout: 30000,
screenshotOnFailure: true,
})
.video({ mode: 'on-failure' })
.trace({ mode: 'on-failure' })
.reporter(new ConsoleReporter())
.data({
username: '[email protected]',
loggedIn: false,
})
.checkpoint('Authentication', { required: true })
.step('Navigate to login', async (ctx) => {
await ctx.page.goto('https://example.com/login');
ctx.log('Navigated to login page');
})
.step('Enter credentials', async (ctx) => {
await ctx.page.fill('#username', ctx.data.username);
await ctx.page.fill('#password', 'password');
})
.step('Submit form', async (ctx) => {
await ctx.page.click('button[type="submit"]');
await ctx.page.waitForURL('**/dashboard');
ctx.data.loggedIn = true;
})
.endCheckpoint();
const result = await script.run();
console.log(result.success ? 'Passed!' : `Failed at: ${result.failedStep?.name}`);Configuration
Basic Options
Stepwright.create('My Script')
.config({
// Browser
browser: 'chromium', // 'chromium' | 'firefox' | 'webkit'
headless: true,
launchOptions: {}, // Playwright LaunchOptions
contextOptions: {}, // Playwright BrowserContextOptions
// Timeouts
defaultTimeout: 30000, // ms
stopOnFailure: true,
// Failure Artifacts
screenshotOnFailure: true,
domOnFailure: true,
// Output
runDir: '.stepwright/runs', // Base directory for run artifacts
artifactDir: '.stepwright/artifacts',
verbose: false,
})Video Recording
.video({
mode: 'on-failure', // 'off' | 'on' | 'on-failure' | 'retain-on-failure'
size: { width: 1280, height: 720 },
dir: './custom-videos', // Optional: override output directory
})Trace Recording
.trace({
mode: 'on-failure', // 'off' | 'on' | 'on-failure' | 'retain-on-failure'
screenshots: true,
snapshots: true,
sources: true,
dir: './custom-traces', // Optional: override output directory
})Reporters
import { ConsoleReporter, JSONReporter } from '@korvol/stepwright';
.reporter(new ConsoleReporter({
timestamps: true,
durations: true,
colors: true,
}))
.reporter(new JSONReporter({
outputPath: './results/output.json', // Optional: defaults to run directory
pretty: true,
}))API Reference
Stepwright
Stepwright.create<T>(name: string)
Create a new Stepwright instance with typed data.
.config(options: StepwrightConfig)
Configure the instance. Returns this for chaining.
.data(initialData: T)
Set initial shared data. Returns this for chaining.
.video(config: VideoConfig | VideoMode)
Configure video recording. Returns this for chaining.
.trace(config: TraceConfig | TraceMode)
Configure trace recording. Returns this for chaining.
.reporter(reporter: Reporter)
Add a reporter. Returns this for chaining.
.checkpoint(name: string, options?: CheckpointOptions)
Start a checkpoint group. Returns this for chaining.
.endCheckpoint()
End the current checkpoint. Returns this for chaining.
.step(name: string, fn: StepFunction, options?: StepOptions)
Add a step. Returns this for chaining.
.step('My Step', async (ctx) => {
// ctx.page - Playwright Page
// ctx.browser - Playwright Browser
// ctx.browserContext - Playwright BrowserContext
// ctx.data - Typed shared data
// ctx.log(message) - Log a message
// ctx.screenshot(name) - Take a screenshot
}, {
timeout: 60000,
critical: true,
retry: { times: 3, delay: 1000, backoff: 'exponential' },
}).background(name: string, task: BackgroundTaskDefinition)
Add a background task for handling dynamic content.
.run(options?: RunOptions)
Execute the script. Returns Promise<StepwrightResult<T>>.
StepwrightResult
interface StepwrightResult<T> {
success: boolean;
duration: number;
stepsCompleted: number;
totalSteps: number;
steps: StepResult[];
checkpoints?: CheckpointResult[];
data: T;
// On failure
failedStep?: {
name: string;
index: number;
checkpoint?: string;
error: Error;
artifacts: StepArtifacts;
};
// Artifact paths
runDirectory?: string;
trace?: string;
video?: string;
log?: string;
}Context (ctx)
Available in step functions:
| Property | Type | Description |
|----------|------|-------------|
| page | Page | Playwright Page |
| browser | Browser | Playwright Browser |
| browserContext | BrowserContext | Playwright BrowserContext |
| data | T | Typed shared data between steps |
| config | StepwrightConfig | Current configuration |
| step | CurrentStepInfo | Current step info |
| results | StepResult[] | Results of completed steps |
| log(msg) | Function | Log a message |
| screenshot(name?) | Function | Take a screenshot |
| getDOM(selector?) | Function | Get DOM content |
Generating Failure Cases
When a script fails, you can generate a structured failure case for AI-powered fixing with Fixwright:
import { Stepwright, generateFailureCase } from '@korvol/stepwright';
const result = await script.run();
if (!result.success && result.failedStep) {
const failureCase = generateFailureCase(result, {
scriptInfo: {
name: 'My Script',
path: 'scripts/my-script.ts',
repository: 'my-org/my-repo',
baseBranch: 'main',
commit: 'abc123',
},
artifactPaths: {
screenshot: result.failedStep.artifacts.screenshot,
dom: result.failedStep.artifacts.dom,
},
});
// Save or send to Fixwright
await fs.writeFile(`failures/${failureCase.id}.json`, JSON.stringify(failureCase));
}Run Directory Structure
When video/trace recording is enabled, artifacts are saved to:
.stepwright/
└── runs/
└── script-name-20240102-123456/
├── trace/ # Playwright trace (unzipped)
├── screenshots/ # Manual screenshots
├── video.webm # Video recording
├── stepwright.log # ctx.log messages
└── result.json # JSON reporter outputBackground Tasks
Handle dynamic content like modals and auth redirects:
.background('cookie-consent', {
trigger: (event) => event.type === 'dom' && event.mutation === 'added',
handler: async (event, ctx, queue) => {
const button = ctx.page.locator('[data-testid="accept-cookies"]');
if (await button.isVisible({ timeout: 500 })) {
await button.click();
ctx.log('Dismissed cookie consent');
}
},
debounce: 1000,
})Integration with Fixwright
Stepwright produces failure cases that Fixwright can automatically fix:
Stepwright (runner) Fixwright (fixer)
│ │
│ ──── FailureCase ────▶ │
│ │
[runs scripts] [analyzes failures]
[captures errors] [generates fixes]See @korvol/fixwright for AI-powered automatic fixing.
License
MIT
