Skip to content

Writing Your First Script

Writing Your First Script

This guide walks you through creating a complete Stepwright automation script from scratch.

What We’ll Build

We’ll create a script that:

  1. Navigates to a website
  2. Fills out a search form
  3. Extracts results
  4. Saves data for later use

Prerequisites

Make sure you have:

  • Node.js 18+ installed
  • A project with @korvol/stepwright installed
  • TypeScript configured (recommended)

Step-by-Step Tutorial

  1. Create the script file

    Create a new file scripts/search-automation.ts:

    import { Stepwright } from '@korvol/stepwright';
    // We'll build our script here
  2. Define your data type

    Type-safe data makes scripts more maintainable:

    interface SearchData {
    query: string;
    results: Array<{
    title: string;
    url: string;
    }>;
    }
  3. Create the script instance

    const script = Stepwright.create<SearchData>('Product Search')
    .config({
    headless: false, // See what's happening
    screenshotOnFailure: true,
    defaultTimeout: 30000,
    })
    .data({
    query: 'typescript testing',
    results: [],
    });
  4. Add your steps

    script
    .checkpoint('Search')
    .step('Navigate to search page', async (ctx) => {
    await ctx.page.goto('https://duckduckgo.com');
    ctx.log('Navigated to DuckDuckGo');
    })
    .step('Enter search query', async (ctx) => {
    const searchInput = ctx.page.locator('input[name="q"]');
    await searchInput.fill(ctx.data.query);
    ctx.log(`Entered query: ${ctx.data.query}`);
    })
    .step('Submit search', async (ctx) => {
    await ctx.page.keyboard.press('Enter');
    await ctx.page.waitForLoadState('networkidle');
    ctx.log('Search submitted');
    })
    .endCheckpoint()
    .checkpoint('Extract Results')
    .step('Extract search results', async (ctx) => {
    // Wait for results to load
    await ctx.page.waitForSelector('[data-testid="result"]');
    // Extract result data
    const resultElements = ctx.page.locator('[data-testid="result"]');
    const count = await resultElements.count();
    for (let i = 0; i < Math.min(count, 5); i++) {
    const result = resultElements.nth(i);
    const title = await result.locator('h2').textContent();
    const url = await result.locator('a').getAttribute('href');
    if (title && url) {
    ctx.data.results.push({ title, url });
    }
    }
    ctx.log(`Extracted ${ctx.data.results.length} results`);
    })
    .step('Take screenshot of results', async (ctx) => {
    await ctx.screenshot('search-results');
    ctx.log('Screenshot saved');
    })
    .endCheckpoint();
  5. Run and handle results

    async function main() {
    const result = await script.run();
    if (result.success) {
    console.log('Search completed successfully!');
    console.log('Results found:', result.data.results.length);
    for (const item of result.data.results) {
    console.log(`- ${item.title}`);
    console.log(` ${item.url}`);
    }
    } else {
    console.error('Script failed!');
    console.error('Failed step:', result.failedStep?.name);
    console.error('Error:', result.failedStep?.error.message);
    }
    console.log(`Duration: ${result.duration}ms`);
    }
    main().catch(console.error);

Complete Script

Here’s the full script:

scripts/search-automation.ts
import { Stepwright } from '@korvol/stepwright';
interface SearchData {
query: string;
results: Array<{
title: string;
url: string;
}>;
}
const script = Stepwright.create<SearchData>('Product Search')
.config({
headless: false,
screenshotOnFailure: true,
defaultTimeout: 30000,
})
.data({
query: 'typescript testing',
results: [],
})
.checkpoint('Search')
.step('Navigate to search page', async (ctx) => {
await ctx.page.goto('https://duckduckgo.com');
ctx.log('Navigated to DuckDuckGo');
})
.step('Enter search query', async (ctx) => {
const searchInput = ctx.page.locator('input[name="q"]');
await searchInput.fill(ctx.data.query);
ctx.log(`Entered query: ${ctx.data.query}`);
})
.step('Submit search', async (ctx) => {
await ctx.page.keyboard.press('Enter');
await ctx.page.waitForLoadState('networkidle');
ctx.log('Search submitted');
})
.endCheckpoint()
.checkpoint('Extract Results')
.step('Extract search results', async (ctx) => {
await ctx.page.waitForSelector('[data-testid="result"]');
const resultElements = ctx.page.locator('[data-testid="result"]');
const count = await resultElements.count();
for (let i = 0; i < Math.min(count, 5); i++) {
const result = resultElements.nth(i);
const title = await result.locator('h2').textContent();
const url = await result.locator('a').getAttribute('href');
if (title && url) {
ctx.data.results.push({ title, url });
}
}
ctx.log(`Extracted ${ctx.data.results.length} results`);
})
.step('Take screenshot of results', async (ctx) => {
await ctx.screenshot('search-results');
ctx.log('Screenshot saved');
})
.endCheckpoint();
async function main() {
const result = await script.run();
if (result.success) {
console.log('\nSearch completed successfully!');
console.log('Results found:', result.data.results.length);
for (const item of result.data.results) {
console.log(`\n- ${item.title}`);
console.log(` ${item.url}`);
}
} else {
console.error('\nScript failed!');
console.error('Failed step:', result.failedStep?.name);
console.error('Error:', result.failedStep?.error.message);
if (result.failedStep?.artifacts.screenshot) {
console.error('Screenshot:', result.failedStep.artifacts.screenshot);
}
}
console.log(`\nDuration: ${result.duration}ms`);
}
main().catch(console.error);

Running the Script

Terminal window
npx tsx scripts/search-automation.ts

Expected Output

[12:30:00] Starting: Product Search
[12:30:01] ✓ Navigate to search page (1.2s)
[12:30:02] ✓ Enter search query (0.3s)
[12:30:03] ✓ Submit search (1.5s)
[12:30:04] ✓ Extract search results (0.8s)
[12:30:04] ✓ Take screenshot of results (0.2s)
Search completed successfully!
Results found: 5
- TypeScript: JavaScript With Syntax For Types
https://www.typescriptlang.org/
- Testing TypeScript | TypeScript Documentation
https://www.typescriptlang.org/docs/handbook/...
Duration: 4000ms

Key Concepts Demonstrated

1. Type-Safe Data

interface SearchData {
query: string;
results: Array<...>;
}

TypeScript catches errors before runtime.

2. Checkpoints

.checkpoint('Search')
// related steps
.endCheckpoint()

Group related steps for logical organization and retry behavior.

3. Context Usage

.step('Example', async (ctx) => {
ctx.page // Playwright page
ctx.data // Shared data
ctx.log() // Logging
ctx.screenshot() // Screenshots
})

4. Error Handling

if (!result.success) {
console.error('Failed:', result.failedStep?.error.message);
console.error('Screenshot:', result.failedStep?.artifacts.screenshot);
}

Next Steps