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:
- Navigates to a website
- Fills out a search form
- Extracts results
- Saves data for later use
Prerequisites
Make sure you have:
- Node.js 18+ installed
- A project with
@korvol/stepwrightinstalled - TypeScript configured (recommended)
Step-by-Step Tutorial
-
Create the script file
Create a new file
scripts/search-automation.ts:import { Stepwright } from '@korvol/stepwright';// We'll build our script here -
Define your data type
Type-safe data makes scripts more maintainable:
interface SearchData {query: string;results: Array<{title: string;url: string;}>;} -
Create the script instance
const script = Stepwright.create<SearchData>('Product Search').config({headless: false, // See what's happeningscreenshotOnFailure: true,defaultTimeout: 30000,}).data({query: 'typescript testing',results: [],}); -
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 loadawait ctx.page.waitForSelector('[data-testid="result"]');// Extract result dataconst 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(); -
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:
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
npx tsx scripts/search-automation.tsExpected 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: 4000msKey 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
- Handling Dynamic Content - Work with AJAX and SPAs
- CI/CD Integration - Run in pipelines
- Best Practices - Write maintainable scripts