DEV Community

Trailguide
Trailguide

Posted on

Running Product Tours as Playwright Tests with @trailguide/playwright

The Problem with Documenting Features

You ship a new feature. It's great. But does anyone know how to use it?

You could write docs. You could record a video. You could build an in-app tour. But here's the thing: tours break. A redesign happens. A button moves. Suddenly your carefully crafted tour is clicking empty space, and users are confused.

Meanwhile, you've got Playwright tests running in CI. Those tests break immediately when UI changes. They're reliable. They're automated. They're already part of your workflow.

So why not make your product tours the same thing as your tests?

How @trailguide/playwright Works

Trailguide solves this by letting you define a trail (a series of clicks, assertions, and waits) in JSON, then run it through Playwright as an actual end-to-end test. One definition. Two purposes: documentation and validation.

The API is dead simple. You import runTrail, pass it a Playwright page object and a trail JSON object, and it executes every step exactly like a user would.

Here's what a trail looks like:

const trail = {
  title: "Creating a new project",
  steps: [
    {
      action: "click",
      selector: "[data-testid='new-project-btn']"
    },
    {
      action: "fill",
      selector: "input[name='projectName']",
      value: "My Awesome App"
    },
    {
      action: "click",
      selector: "button:has-text('Create')"
    },
    {
      assertion: "visible",
      selector: "h1:has-text('My Awesome App')"
    }
  ]
};

import { runTrail } from '@trailguide/playwright';

await runTrail(page, trail);
Enter fullscreen mode Exit fullscreen mode

That's it. The test clicks a button, fills a form, submits, and verifies the result. When something breaks, your CI catches it.

What Actions and Assertions Look Like

Trails support the actions you'd expect:

  • click: tap something
  • fill: type into an input
  • hover: mouse over
  • select: pick from a dropdown
  • check: toggle a checkbox
  • drag: move things around

And assertions to validate state:

  • visible: element exists and is shown
  • text: element contains text
  • value: input has a value
  • url: page is at a URL

You can also add waits to handle async behavior. Network waits. Selector presence. These prevent the flaky timing issues that make Playwright tests annoying.

Why This Matters

Here's the real win: you can use the same trail definition for two completely different things.

In your CI? Run it as a test. Get instant feedback when the UX breaks.

In production? Show it as an interactive tour. Guide users through features the exact same way you verified they work.

Or keep it test-only. No tour UI. Just validation. Whatever you need.

This means your documentation never lies. Your tour never guides users down a broken path. And when you refactor, you update once and both the test and the tour update together.

The Practical Benefit

Most teams have a problem: their tests don't match their product experience. Tests are mechanical. Tours are curated but fragile. They diverge.

With @trailguide/playwright, they're the same thing. You write the trail once. Playwright validates it. Your users follow it. No inconsistency. No drift. No broken tours shipping to production.

It's a small idea but it solves a real friction point in the dev workflow.

If you're already using Playwright and want to stop manually writing tour clicks (or stop tours breaking every sprint), Trailguide is free to try at gettrailguide.com. The MIT-licensed runtime works standalone, or you can go pro for analytics and managed tours.

Top comments (0)