> ## Documentation Index
> Fetch the complete documentation index at: https://jam.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Turn a Jam into a test

> Get an end-to-end test from a Jam, in your own runner: a passing test for a flow, or a failing test for a bug.

Today a bug report becomes a test when someone replays it and writes the repro steps by hand, and a demo never becomes one. Give your agent the Jam instead.

The agent writes a test in your own runner: a failing regression test for a bug, or a passing test that protects a flow. The test's first line links back to the Jam it came from. When the test fails later, the failure can come back as a Jam.

> [!NOTE]
>
> * A coding agent with your code open, such as Claude Code, Cursor, Codex, GitHub Copilot CLI, or Gemini CLI.
> * The `jam-to-e2e` skill. Install the Jam plugin (see [Connect your client](https://jam.dev/docs/jam-mcp.md#connect-your-client)), or run `npx skills add jamdotdev/skills --skill jam-to-e2e`. The source is in [jamdotdev/skills](https://github.com/jamdotdev/skills).
> * [Jam MCP](https://jam.dev/docs/jam-mcp.md) or the [Jam CLI](https://jam.dev/docs/cli.md), so the agent can read the Jam.
> * For a Jam of each failure in CI, the Jam CLI in the CI job and a [personal access token](https://jam.dev/docs/personal-access-tokens.md).

## Steps

**1. Say which test you want**

```text wrap theme={"theme":"css-variables"}
Cover this flow with an e2e test: https://jam.dev/c/abc123
```

```text wrap theme={"theme":"css-variables"}
Write a regression test for this bug: https://jam.dev/c/abc123
```

A Jam is not always a bug report, so your request decides whether the test must pass or fail today.

| The Jam shows | Ask for | The test |
| - | - | - |
| A feature working: a demo, a walkthrough, a happy path | "Cover this flow" | Asserts what the Jam shows and passes today. It protects the flow against regressions. |
| A bug | "Write a regression test" | Asserts what should have happened. It fails until the bug is fixed, then passes. |

**2. Read the hand-off**

The agent returns the numbered steps in the order the test performs them, the test file and the command that runs it, the run output, and every difference between the Jam and your current code. Read the differences first. A renamed button is expected. A step that no longer exists means the flow changed since the recording.

Then review the test as you would a colleague's. Each test checks one claim. Locators find elements by role, label, or visible text. The assertion checks an outcome the person would see, such as a new URL or text that appears only after the action. There are no fixed sleeps.

**3. Check why it passed or failed**

Run the command from the hand-off twice. A flow test passes both times. A bug test fails on the assertion that encodes the bug, not on a locator timeout or a setup error. If the bug did not reproduce, the agent says so and what differed. Do not accept a test whose assertion was weakened until it passed.

**4. Set up a Jam for every failure (optional)**

```text wrap theme={"theme":"css-variables"}
Set up our e2e tests so every test that fails in CI uploads a Jam of the failure and posts its link on the pull request. Follow https://jam.dev/docs/cli-e2e-tests
```

Do this once per repo, and skip it when your suite already uploads a Jam of each failure. It changes the test config and the CI job, not the tests. From then on, every failure comes back the way the bug came in: a Jam with the video and the browser's console and network. Hand it to your agent like any other Jam. See [Hand a Jam to your agent](https://jam.dev/docs/hand-a-jam-to-your-agent.md).

## FAQ

> **What does the agent read from the Jam?**
>
> Title and description first, since narrated Jams often carry the numbered steps in the title. Then the user events for clicks, inputs, and navigation, the transcript for what the person said, and a frame overview for what was on screen. Console and network only when the test is about them. It writes the expected result down in one sentence, because that sentence is the assertion.
>
> Labels, routes, and button names come from the component that renders the screen today, never from the recording. That is where the differences in the hand-off come from.

> **Which runner does it use?**
>
> It uses whatever runner the repo has, such as Playwright, Cypress, [tester-army/e2e](https://github.com/tester-army/e2e), pytest with Playwright, or Capybara. It follows that runner's fixtures, sign-in setup, and naming. Only a project with no runner gets Playwright. One test per claim: a Jam that walks three screens is three tests.

> **How does it keep the test from flaking?**
>
> Locators by role, label, and visible text. Assertions that retry. Outcomes the server must have produced: a URL change, text that exists only after the action, state that survives a reload. No fixed sleeps, no `try/catch` that turns a failure into `false`.

> **Can it record the passing run?**
>
> Yes, with the [Jam CLI](https://jam.dev/docs/cli.md#quickstart) installed. Once the test passes, the agent offers an "after" Jam of the passing run. Say yes and it returns a link, so the ticket has the bug Jam, the test, and the proof. Say "Mark it as proof for the bug Jam and attach the pull request" and the bug Jam lists the passing run under **Related Jams**.

> **How does a failing run become a Jam?**
>
> With Playwright, the test keeps the video and the trace of each failure, and a CI step uploads them with `jam create jam`. The Jam plays the video with the console and network from the trace.
>
> With any other runner, `jam record run --cdp` wraps the test command and records the browser while it runs. It records every run, passing or failing, and keeps the test's exit code, so a failing test still fails the job.
>
> CI signs in with a personal access token in `JAM_TOKEN`. The commands are in [Record end-to-end tests](https://jam.dev/docs/cli-e2e-tests.md).

> **Can it cover a Jam with no narration?**
>
> Yes, with limits. The agent takes the steps from the clicks and inputs when the Jam has them, and from the frames when it does not. It cannot know the expected result from a silent recording, so it asks you or names the result it assumed. Say the expected result in the prompt instead: "The order total should update when the coupon is applied."

> **Where do the Jams come from?**
>
> Anyone can [record one](https://jam.dev/docs/creating-a-jam.md), a customer can send one through a Recording Link, or the agent can record its own proof runs. See [Request Jams from customers](https://jam.dev/docs/cli-request-jams.md) to create Recording Links from a script.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.