Keyword-driven Automation with Playwright and TypeScript

Write native Step Keywords with Playwright and TypeScript, unit-test them locally, then build and run them on a Step cluster — reusing one Keyword library for both a functional test and a load test.

Get Step SaaS for free to follow this tutorial
Illustration for Playwright with TypeScript

This tutorial walks you through a complete keyword-driven Playwright project written in TypeScript: you will clone a ready-made sample, understand how its Keywords are built, run them locally as unit tests, then compile and run them on a Step cluster.

Note:

This tutorial covers the keyword-driven approach, where your Playwright code is written as native Step Keywords — functions that Step calls directly, with their own inputs, outputs and shared session. This is what lets a single Keyword library be reused across different Plans, and it gives you per-Keyword reporting in Step.

Playwright can also be integrated the other way round: keep an existing Playwright test suite as it is and have Step run it through the Execute Keyword, invoking a command such as npx playwright test or npm test. That command-based approach is better suited to importing an existing suite unchanged, and will be covered in a separate tutorial.

Prerequisites

  • Node.js 24 or later, and npm, installed on your local machine
  • Step CLI installed — see the Step CLI documentation
  • Git, to clone the sample repository
  • Access to a Step cluster. Get started quickly by setting up a SaaS cluster in the Step Portal — Node.js agents are available by default and auto-provisioned, so nothing else needs configuring. Alternatively, follow the Installation page to set up your own on-premise cluster, where a Node agent has to be registered.

Test scenario

The sample automates a purchase on our demo online store. The user journey is split into two Keywords:

  • findProduct — opens a browser, searches for a product, and reports its price as a Keyword output
  • checkout — reuses the browser opened by findProduct to add the product to the cart and complete a guest checkout

The same two Keywords are then reused by two different Plans: a functional test case and a load test simulating concurrent users. This is one of the main benefits of the Keyword-driven approach — you automate the journey once and reuse it across testing purposes.

Checkout the sample

Clone the sample project from GitHub:

  git clone https://github.com/exense/step-samples.git
  

Navigate to the sample directory:

  cd step-samples/automation-packages/playwright-typescript
  

The project is organized as follows:

  playwright-typescript/
├── automation-package.yaml              # Automation Package descriptor: Plans, Parameters, Keyword declarations
└── nodejs-keywords/
    ├── src/
    │   └── keywords-typescript.ts       # Keyword implementations (TypeScript source)
    ├── tests/
    │   └── keywords-typescript.test.ts  # Local unit test of the two Keywords
    ├── keywords/                        # Compiled JavaScript, generated by the build
    ├── package.json
    ├── tsconfig.json                    # Keyword build: compiles src/ to keywords/
    └── tsconfig.test.json               # Type-checks the tests, which are not part of the build
  

Install the dependencies

  cd nodejs-keywords
npm install
  
Note: The postinstall script runs playwright install chromium, which downloads the browser binary Playwright drives. The first install therefore takes noticeably longer than a plain npm install.

Understanding the Keywords

The Keywords are implemented in nodejs-keywords/src/keywords-typescript.ts. Each one is an exported async function following the Step Keyword API signature:

  export async function findProduct(input, output, session, properties) {
    ...
}
  

Those four arguments are the entire contract between Step and your automation code.

Inputs and properties

The sample deliberately splits the data it receives in two:

  • input carries the business data of the call — input.url and input.productName — provided per Keyword call in the Plan
  • properties carries the technical configuration — here properties['headless'] — declared once for the whole package and overridable in Step
  const headless = properties['headless'] === 'true';
const browser = await chromium.launch({ headless });
  
Note: Property values always arrive as strings. The explicit === 'true' comparison matters: a plain truthiness check would read the string "false" as true.

Sharing a browser between Keywords

session is a store shared by all Keywords running in the same session. findProduct registers the browser in it, and checkout picks the page back up:

  // in findProduct
session.set('browser', browser);
session.set('playwright', { context, page });

// in checkout
const { context, page } = session.get('playwright');
  

Registering the browser is also all the cleanup the sample needs: Step closes every session value that exposes a close() method when the session ends, and closing a browser closes its contexts and pages with it. The playwright entry is a plain object with nothing to close, so Step leaves it alone and the browser remains the single owner of the cleanup.

Reporting results

output is the channel back to Step. output.add('price', price) returns a Keyword output that the Plan can use downstream, and output.attach(...) attaches a file to the execution report — here a Playwright trace:

  const price = await page.getByRole('heading', { name: '$' }).textContent();
output.add('price', price);
  
Behind the scenes Both Keywords stop their trace and attach it inside a finally block, so the trace is attached when the Keyword fails too — which is exactly when it is most useful. The helper writes each trace into a private mkdtemp directory before attaching it: under load, several Keywords run concurrently on the same agent and share the OS temp directory, so a fixed file name would let one execution overwrite another’s trace.

For a complete description of the API, see the Step Keyword API documentation.

Testing the Keywords locally

Before deploying anything, you can run the Keywords straight from your machine. The unit test in nodejs-keywords/tests/keywords-typescript.test.ts uses the step-node-agent runner to invoke them exactly as an agent would:

  // The runner's first argument is the Keyword properties — the same channel
// Step feeds from its Parameters, with values as strings.
const keywordRunner = runner({ headless: 'true' });

const found = await keywordRunner.run('findProduct', {
    url: 'https://opencart-prf.stepcloud.ch/',
    productName: 'iMac',
});
assert.match(found.payload.price, /^\$\d+\.\d{2}$/);

await keywordRunner.run('checkout');
  

Run it with:

  npm test
  

This gives you immediate feedback on your Keywords without a round-trip to a Step cluster — the fastest way to iterate while developing.

Behind the scenes A few details worth copying into your own projects: run() throws when a Keyword reports an error, so reaching the next line already proves it succeeded — no assertion needed. The price is matched as a pattern rather than a literal, so a catalogue change on the live store does not break the test. And keywordRunner.close() in the after hook disposes the session, which closes the browser opened by findProduct.
Note: The test script chains three things: npm run build, then npm run typecheck, then the tests. The type-check step exists because tsx strips types without verifying them — without tsconfig.test.json, the test sources would never be checked by the compiler.

Understanding the Automation Package descriptor

The automation-package.yaml file at the root of the sample tells Step what the package contains.

The Keywords are declared as Node Keywords, both pointing at the nodejs-keywords/ directory:

  keywords:
  - Node:
      name: findProduct
      jsfile: nodejs-keywords/
  - Node:
      name: checkout
      jsfile: nodejs-keywords/
  

The technical configuration discussed above is declared as a package Parameter, which is how it reaches the Keywords as properties:

  parameters:
  # Set to "false" to watch the browser while the plan runs
  - key: headless
    value: "true"
  

Because it is a Parameter and not an input, the same knob can be redefined in Step — globally, or per project or environment — without touching a single Plan. Set it to "false" to watch the browser as the Plan runs; Step agents support both headed and headless execution.

The Plans then call the Keywords. The functional test case runs them once in sequence:

  - name: "Functional Test Case - OpenCart purchase flow"
  agents: auto_detect
  root:
    testSet:
      children:
        - testCase:
            nodeName: "OpenCart Test Case 01"
            children:
              - callKeyword:
                  keyword: "findProduct"
                  inputs:
                    - url: "https://opencart-prf.stepcloud.ch/"
                    - productName: "iMac"
              - callKeyword:
                  keyword: "checkout"
  

While the load test wraps the very same calls in a thread group and a session, so each simulated user runs the full journey in its own browser session:

  - name: "Load Test - OpenCart purchase flow"
  agents: auto_detect
  root:
    threadGroup:
      users: 5
      iterations: 10
      children:
        - session:
            children:
              - callKeyword:
                  keyword: "findProduct"
                  inputs:
                    - url: "https://opencart-prf.stepcloud.ch/"
                    - productName: "iMac"
              - callKeyword:
                  keyword: "checkout"
  

For the full syntax, refer to the Automation Package descriptor documentation.

Building the project

Step executes JavaScript, not TypeScript, so the sources have to be compiled before the package is sent to your cluster:

  cd step-samples/automation-packages/playwright-typescript/nodejs-keywords
npm run build
  

This runs tsc, compiling everything under src/ into the keywords/ directory — the location the jsfile property in the descriptor points to.

Note: Do not skip this step. keywords/ is generated and not tracked in Git, so a fresh clone has no compiled output at all. If you deploy without building, or change a .ts file without rebuilding, the agent will run stale JavaScript — or fail to find any.
Behind the scenes The .apignore file controls what actually gets uploaded. src/, tests/, tsconfig.test.json and node_modules/ are all excluded: the agent only needs package.json — it runs npm install itself — and the compiled keywords/ directory. Keeping node_modules/ out is what keeps the uploaded package small.

Executing in Step

Before talking to your cluster, you need three things: its URL, the project to target, and an API key. For instructions on generating a key, refer to Generate an API Key.

From the root of the Automation Package, run:

  cd step-samples/automation-packages/playwright-typescript
step ap execute --stepUrl=https://<Hostname of your Step cluster> --projectName=Common --token=<Your API key>
  

This packages the project, sends it to your Step cluster, and immediately runs the Plans it contains. Use it for a quick end-to-end verification from your machine or from a CI pipeline.

Note: --projectName and --token are required on Step SaaS and on the Enterprise edition, which are multi-tenant and authenticated. On an open-source cluster you can omit both and pass --stepUrl alone. Treat the API key as a secret: in a CI pipeline, inject it from a secret store rather than writing it into the command.

Deploying to Step

Executing does not leave anything behind on the cluster. To make the package permanently available — so the Plans can be scheduled or launched from the Step UI — deploy it instead:

  cd step-samples/automation-packages/playwright-typescript
step ap deploy --stepUrl=https://<Hostname of your Step cluster> --projectName=Common --token=<Your API key>
  

Once deployed, both the functional test case and the load test appear in the Step UI, ready to be executed, scheduled, or included in larger Plans — and the headless Parameter can be overridden there without changing the package.

Troubleshooting

  • Build fails: make sure all dependencies were installed with npm install
  • TypeScript compilation errors: check your tsconfig.json configuration
  • Step CLI not found: verify the Step CLI is installed and available on your PATH
  • Authentication or authorization errors from the CLI: on SaaS and Enterprise clusters, check that --token holds a valid, unexpired API key and that --projectName names a project your user can access
  • Keyword not found at execution time: you most likely skipped npm run build, or the jsfile path in automation-package.yaml does not match the outDir in tsconfig.json
  • No agent available (on-premise clusters): Node.js Keywords require a Node agent registered on your cluster. On SaaS clusters this is handled for you.
  • Runtime errors after switching the project to ESM: keep the project CommonJS. The Step Node agent copies CommonJS helpers into the Keyword project and forks them, so adding "type": "module" to package.json breaks the runtime. tsconfig.json uses "module": "node20", which keeps the CommonJS emit while enabling modern module resolution.

Additional resources

Illustration for Using Step with Grafana
Using Step with Grafana

This article demonstrates how to connect Grafana to data generated by Step.

Illustration for Setting up system monitoring with a Step agent
Setting up system monitoring with a Step agent

This article demonstrates how to set up distributed system monitoring using Keyword executions, and analyze the results as measurements.

Illustration for NET tutorials: Microsoft Office automation with Step
NET tutorials: Microsoft Office automation with Step

This tutorial demonstrates how to automate interaction with Microsoft Office applications using the Office Interop Assembly.

Illustration for JUnit Plan Runner
JUnit Plan Runner

This article provides documentation for how to integrate JUnit tests into Step.

Illustration for How to monitor services availability and performance
How to monitor services availability and performance

This tutorial demonstrates how Step can be used to monitor services, availability and performance metrics.

Illustration for .NET tutorials: AutoIt with Step
.NET tutorials: AutoIt with Step

This tutorial demonstrates how to utilize the AutoIt C# binding to automate interactions with Windows applications.

Illustration for Android Testing using Step and Appium
Android Testing using Step and Appium

This article demonstrates the automation of mobile applications on Android using the Appium framework.

Illustration for Browser-based automation with Step and Selenium
Browser-based automation with Step and Selenium

This article defines three Keywords which will be used in browser-based automation scenarios, using Step and Selenium, as general drivers.

Illustration for Load Testing with Cypress
Load Testing with Cypress using the Step UI - advanced

This tutorial shows you how to efficiently set up a browser-based load test using existing Cypress tests in the Step automation platform.

Illustration for Adding and Configuring New Agents
Adding and Configuring New Agents

In this short tutorial, we show how to quickly implement a simple browser-based load test based on Cypress scripts in Step.

Illustration for Load Testing with Playwright
Load Testing with Playwright using the Step UI

This tutorial shows you how to set up a browser-based load test using existing Playwright tests in the Step UI.

Illustration for Basic Keyword Development
Basic Keyword Development

This article explains Keywords in Step and demonstrates how to create simple ones.

Illustration for Designing functional tests
Designing functional tests

This tutorial demonstrates the design, execution, and analysis of functional tests using the web interface of Step.

Illustration for Robotic Process Automation (RPA) with Selenium
Robotic Process Automation (RPA) with Selenium

This tutorial will demonstrate how to use Step and Selenium to automate various browser tasks.

Illustration for Robotic Process Automation (RPA) with Cypress
Robotic Process Automation (RPA) with Cypress

This tutorial demonstrates how to use Step and Cypress to automate various browser tasks.

Illustration for Synthetic Monitoring with Selenium
Synthetic Monitoring with Selenium

This tutorial demonstrates how Selenium automation tests can be turned into full synthetic monitoring using Step.

Illustration for Load Testing with Cypress
Load Testing with Cypress

In this tutorial, you'll learn how to reuse existing Cypress tests to quickly set up and run a browser-based load test using the automation as code approach.

Illustration for Load Testing with Cypress
Load Testing with Serenity BDD and Cucumber

In this tutorial, you'll learn how to reuse existing tests written with Serenity BDD and Cucumber for load testing.

Illustration for Synthetic Monitoring with Cypress
Synthetic Monitoring with Cypress

This tutorial demonstrates how Cypress automation tests can be turned into full synthetic monitoring using the automation as code approach.

Illustration for Load Testing with Cypress
Load Testing with Cypress using the Step UI

In this tutorial, you'll learn how to reuse existing Cypress tests to quickly set up and run a browser-based load test using the Step UI.

Illustration for Load Testing with Selenium
Load Testing with Selenium

This tutorial demonstrates how to leverage existing Selenium tests to set up and execute browser-based load tests, following a full code-based approach.

Illustration for Load Testing with Selenium
Load Testing with Selenium using the Step UI

This tutorial demonstrates how to set up a browser-based load test in the Step UI using existing Selenium tests.

Illustration for Synthetic Monitoring with Playwright
Synthetic Monitoring with Playwright

This tutorial demonstrates how Playwright automation tests can be turned into full synthetic monitoring using Step.

Illustration for Synthetic Monitoring with Cypress
Synthetic Monitoring with Cypress using the Step UI

This tutorial demonstrates how Cypress automation tests can be turned into full synthetic monitoring using the Step UI.

Illustration for Robotic Process Automation (RPA) with Playwright
Robotic Process Automation (RPA) with Playwright

This tutorial will demonstrate how to use Step and Playwright to automate various browser tasks.

Illustration grafana devops tutorial
Distributed load testing with JMeter

This tutorial shows how to distribute JMeter tests across multiple nodes.

Illustration for Load Testing with Playwright
Load Testing with Playwright for Java

In this tutorial, you'll learn how to reuse existing Playwright tests written in Java to quickly set up and run a browser-based load test using the automation as code approach.

Illustration for playwright synthetic monitoring in a devops workflow
DevOps Synthetic Monitoring with Playwright - Advanced

This tutorial demonstrates how Playwright tests can be reused for synthetic monitoring of a productive environment in a DevOps workflow

Illustration grafana devops tutorial
Distributed load testing with Grafana K6

This tutorial shows how to distribute Grafana K6 tests across multiple nodes.

Illustration for playwright synthetic monitoring in a devops workflow
DevOps Synthetic Monitoring with Playwright

This tutorial demonstrates how Playwright tests can be reused for synthetic monitoring of a productive environment in a DevOps workflow

Illustration for okhttp devops
Protocol-based load testing with okhttp

In this tutorial you'll learn how to quickly set up a protocol-based load test with okhttp

Illustration for playwright devops
Continuous end-to-end testing

Learn how to set up continuous end-to-end testing across several applications based on Playwright tests in your DevOps pipeline using Step

Illustration for playwright devops
Continuous load testing with Playwright

Learn how to quickly set up continuous browser-based load testing using Playwright tests in your DevOps pipeline

Illustration for Playwright with TypeScript
AI-Assisted Testing with the Step MCP Server

End-to-end walkthrough: author a test, run it, and analyse the results using plain English prompts in Claude via the Step MCP Server.

Want to hear our latest updates about automation?

Don't miss out on our regular blog posts - Subscribe now!

Image of a laptop device to incentivize users to subscribe