Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use await test.step(title, async () => { ... }) inside a Playwright Test test to give an operation a name in the HTML report and trace. The callback can contain any Playwright actions and assertions, can be nested, and its return value is returned by test.step.

Import test from @playwright/test, keep titles short and action-oriented, and await every step. The sections below show the basic pattern, return values, nested steps, step options, TestStepInfo, reporting, and fixes for common failures.

Basic test.step syntax

A step has a title and an asynchronous callback. Playwright records the title and the work performed in that callback as one named unit in the test report.

import { test, expect } from '@playwright/test';

test('checkout', async ({ page }) => {
  await test.step('Open the product page', async () => {
    await page.goto('/products/123');
  });

  await test.step('Add the product to the cart', async () => {
    await page.getByRole('button', { name: 'Add to cart' }).click();
    await expect(page.getByRole('status')).toContainText('Added');
  });
});

The first argument is the label shown in reports. The second is an async function. Because the outer test awaits each call, Playwright waits for the navigation, click, and assertion before moving to the next step. A step is reporting metadata; it is not required for the browser operation to execute.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

JavaScript version

const { test, expect } = require('@playwright/test');

test('checkout', async ({ page }) => {
  await test.step('Open the product page', async () => {
    await page.goto('/products/123');
  });

  await test.step('Add the product to the cart', async () => {
    await page.getByRole('button', { name: 'Add to cart' }).click();
    await expect(page.getByRole('status')).toContainText('Added');
  });
});

Use the same structure in JavaScript; only the import style changes.

What appears in reports

The named step appears in the Playwright HTML Report test-detail view, where you can expand the test and inspect its steps. Steps are also represented in traces. This makes a failure easier to locate than a long, unlabelled sequence of browser commands.

Run your tests with the Playwright Test runner, then open the generated report with the command appropriate to your project (for example, npx playwright show-report if your project uses the default HTML reporter). If you are using another reporter, check that reporter’s step support.

Use labels such as “Open the product page”, “Submit payment”, or “Verify confirmation email”. Avoid labels such as “Do stuff” and avoid wrapping every locator call in its own step. A step should communicate a meaningful action or checkpoint.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Return a value from a step

The callback’s return value becomes the return value of test.step. Await the step and assign the result when a named operation produces data needed later in the test.

const username = await test.step('Choose account', async () => {
  return 'alex';
});

expect(username).toBe('alex');

The value can be a string, object, or other value your test can use. Returning a value does not change how the step is displayed; it simply gives the caller the callback result.

Nested steps for workflows and helpers

A step can contain other steps. Use an outer step for a business-level operation and inner steps for the checkpoints that matter when diagnosing a failure.

await test.step('Complete checkout', async () => {
  await test.step('Enter shipping address', async () => {
    await page.getByLabel('Address').fill('1 Main Street');
    await page.getByLabel('City').fill('London');
  });

  await test.step('Confirm order', async () => {
    await page.getByRole('button', { name: 'Place order' }).click();
    await expect(page.getByRole('heading', { name: 'Thank you' })).toBeVisible();
  });
});

Keep nesting shallow enough that the report remains readable. A useful hierarchy is usually a test scenario, its major workflow stages, and only the checkpoints that distinguish one failure from another.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Step options and when to use them

The documented signature is test.step(title, body, options?). Each option solves a different reporting or execution problem.

Option Effect Documented availability
box When true, errors from inside the step point to the step call site in the report. This is useful when a reusable helper’s internal line is less helpful than the caller. Added in Playwright 1.39
location Supplies a custom source location shown in reports and the trace viewer. Added in Playwright 1.48
timeout Sets the maximum duration for that individual step in milliseconds. The documented default is 0, meaning no step-specific timeout. Added in Playwright 1.50
params Provides serializable parameters for reporters and the trace viewer. Added in Playwright 1.63
subtitle Adds a secondary label next to the step title in reports and the trace viewer. Added in Playwright 1.63

Use an options object as the third argument:

await test.step(
  'Create account',
  async () => {
    await page.getByLabel('Email').fill('alex@example.com');
    await page.getByRole('button', { name: 'Create account' }).click();
  },
  {
    box: true,
    subtitle: 'New customer flow',
    params: { plan: 'trial' },
    timeout: 15_000
  }
);

Check the API reference for the Playwright version installed in your project before using a newer option. If an older project rejects an option, upgrade Playwright or remove that option rather than changing the test’s behavior.

When box: true helps

Suppose a shared helper wraps several locators. Without boxing, a failure can lead the report to an internal helper line. With box: true, the reported location is the line where the test called the helper, which is often the most useful place to start debugging.

When to use timeout

A step timeout limits the whole callback, including all actions and assertions inside it. It is useful for detecting a workflow that hangs, but it does not replace locator or assertion timeouts. Set it only when a bounded duration makes sense for that particular workflow.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use TestStepInfo for conditional skips and attachments

The callback may receive a TestStepInfo argument. Its documented API supports conditional skipping and step-scoped attachments.

Skip a step conditionally

await test.step('Check desktop-only control', async step => {
  step.skip(isMobile, 'Not present in the mobile layout');
  await expect(
    page.getByRole('button', { name: 'Desktop action' })
  ).toBeVisible();
});

step.skip(condition, description) marks the step as skipped when the condition is true, so a mobile run does not fail because a desktop-only control is absent. The condition should describe an intentional product difference, not hide an unexpected failure.

Attach artifacts to the step

Use step.attach(name, options) to associate an attachment such as a screenshot or downloaded file with that step. Step-scoped attachments appear under the step; testInfo.attach() stores an attachment at the test level. Choose the step scope when the artifact explains one operation, and test scope when it describes the whole test.

Custom reporters and step events

A custom reporter can implement onStepBegin and onStepEnd. Playwright calls those hooks for executed steps while the test is running, before onTestEnd. Configure the reporter through the reporter option in the Playwright Test configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This lets a team stream step progress, record timings, or integrate step events with an existing test dashboard. Keep reporter work lightweight: expensive processing in a step hook can slow every test even though the browser actions themselves are unchanged.

If you only need interactive diagnosis, the built-in HTML Reporter and trace viewer are simpler than maintaining a custom reporter.

Organize steps in real tests

Use business actions as boundaries

Group the locators and assertions that implement one user-visible action. For example, “Add shipping address” is a better boundary than separate steps for filling the street, city, and postal-code fields, unless those fields are independently important checkpoints.

Keep titles stable

Stable titles make reports searchable and make changes in a workflow obvious. Put changing data in params or a subtitle where your Playwright version supports them instead of generating an entirely different title for every value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not use steps as control flow

A step does not make an operation retryable and does not replace assertions, fixtures, or test isolation. It names and scopes work that your test already performs.

Troubleshooting test.step

The step does not appear in the report

  • Confirm the code runs through Playwright Test and imports test from @playwright/test, not a similarly named test library.
  • Open the test-detail view of the HTML report or the trace for the same run; a console log or a different reporter may not display step hierarchy.
  • Make sure the callback is awaited. An unawaited promise can finish after the test has moved on or ended.

The report points inside a helper

Add box: true to the helper’s step call so an internal error points to the call site. Use this for reusable operations where the caller is more informative than the helper implementation.

An option is rejected or has no effect

Check the installed Playwright version against the option’s introduction version. box requires 1.39 or newer, location 1.48 or newer, timeout 1.50 or newer, and params and subtitle 1.63 or newer. Also verify that the configured reporter and trace viewer support the metadata you are trying to inspect.

A step appears to hang

Inspect the operations inside the callback first: navigation, locator waits, downloads, and assertions can each wait. Add a step-level timeout when you need a hard limit, then debug the specific action that consumes it. A timeout of 0 leaves the step without its own limit.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Conditional behavior is hidden rather than explained

Use step.skip(condition, description) for an expected product difference and provide a reason. Do not catch an assertion error merely to make the report look green; that removes the failure signal instead of documenting a valid skip.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Capturing a page image for a test artifact

Playwright can capture screenshots as part of a test. If you need a standalone screenshot service for a page outside the test browser, ScreenshotNeo is an alternative to setting up a separate browser process. It accepts one GET request and can return PNG, JPEG, WebP, or PDF; its MCP server also exposes screenshot, page-info, and PDF tools to AI agents.

Or skip the browser setup

For a direct page capture, call ScreenshotNeo’s API. Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. The MCP server works with Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options. The following calls use https://stripe.com as the target URL.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes 1,000 screenshots per month on its free plan with no card required. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Create a free ScreenshotNeo account to get started.

Performance and reliability considerations

  • Runtime: A step adds a named reporting boundary; it does not replace or accelerate the browser operations inside it. Excessively granular steps can make reports noisy and add reporter overhead.
  • Failure diagnosis: Grouping related actions gives the HTML report and trace a useful hierarchy without changing Playwright’s locator and assertion behavior.
  • Version compatibility: Keep the Playwright package and documentation version aligned, especially when using options introduced in 1.48, 1.50, or 1.63.
  • External captures: If a screenshot is produced by an external service, retain the response headers and status information so a failed load is not mistaken for a valid page image.

Official references

Frequently Asked Questions

Can a step return data to the test?

Yes. The value returned by the asynchronous callback is the value returned by the awaited test.step call.

Which option changes where an error is reported?

Use box: true to point errors to the step call site instead of an internal line in the step implementation.

Where should a screenshot or downloaded file be attached?

Use step.attach for an artifact belonging to one step; use testInfo.attach when it belongs to the entire test.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.