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

# Download Playwright Spec

> Export any Decipher test as a runnable @playwright/test spec — no AI, no Decipher account needed at runtime

The "Download Test" action exports a Decipher test as a self-contained `@playwright/test` spec file. The generated `.spec.js` is fully deterministic — no AI calls at runtime — and runs against a real browser using selectors mined from the test's most recent passed runs (with the recorded selectors as fallback). Useful for CI pipelines that need a static script, debugging selector flake locally, or running a test outside Decipher entirely.

### What's included

Selectors come from the latest 3 *passed* runs in the last 2 days (strict per-step intersection), falling back to the recorded selectors stored on the test. Login steps are inlined as raw Playwright actions with credentials substituted from your identity row. The script ships with a 1920×1080 viewport.

### What's skipped

Steps that need the Decipher runtime or AI to behave correctly are emitted as `// SKIPPED:` comments: `assert`, `code`, `tab-switch`, and `upload`.

## Step 1: Download the spec

<Steps>
  <Step title="Open your test">
    Navigate to **`/tests/<id>`** in the Decipher app.
  </Step>

  <Step title="Open the More Actions menu">
    Click the `⋯` button next to **Run Test** in the header.
  </Step>

  <Step title="Click 'Download Test'">
    The menu stays open showing **"Generating..."** while the server walks your test's recent runs and renders the spec, then downloads `<test-name>-<id>.spec.js`.
  </Step>
</Steps>

### Bulk export from `/tests/all`

Need to grab specs for many tests at once? On the **`/tests/all`** page, select tests via the row checkboxes and click **Export Playwright** in the bulk-actions bar. Decipher generates each spec using the same deterministic generator described above and packs them into a single zip:

`decipher-tests-<count>-<YYYY-MM-DD>.zip`

A few details worth knowing:

* **Same output as the single-test download.** Every `.spec.js` in the zip is byte-for-byte what you'd get by exporting that test individually, so the rest of this page (running, artifacts, troubleshooting) applies unchanged.
* **Partial success is fine.** If a few tests can't be rendered (e.g. no steps, or no recent passing runs to mine selectors from), the zip still contains the ones that succeeded and a toast lists how many were skipped.
* **Filenames are deterministic.** Specs are sorted by name in the zip; if two tests resolve to the same filename, later ones get a `-dup2`, `-dup3`, ... suffix.
* **Org-scoped.** You can only export tests within your own org — cross-org IDs surface as `Test <id> not found` in the failure list, never silently included.

Up to 5 specs are generated in parallel, so a 50-test selection typically takes a handful of seconds.

## Step 2: Set up a Playwright environment

<Note>
  If you already have a directory with `@playwright/test` installed, drop the spec there and skip straight to Step 3 — the rest of this section is only for first-time setup.
</Note>

In any folder you want to keep your generated specs in (e.g. `~/playwright-runs/`):

```bash theme={null}
mkdir -p ~/playwright-runs
cd ~/playwright-runs
npm init -y
npm install -D @playwright/test
npx playwright install chromium
```

You don't need a `playwright.config.ts` — the generated spec sets its own viewport via `test.use({ viewport: { width: 1920, height: 1080 } })` and disables Playwright's per-test timeout via `test.setTimeout(0)`.

## Step 3: Run the spec

Move (or download) the `.spec.js` file into the directory you set up in Step 2, then:

<CodeGroup>
  ```bash Headless (default) theme={null}
  npx playwright test add-internal-user-2377.spec.js
  ```

  ```bash Headed (visible browser) theme={null}
  npx playwright test add-internal-user-2377.spec.js --headed
  ```

  ```bash Single-browser, debug-friendly theme={null}
  npx playwright test add-internal-user-2377.spec.js --headed --workers=1
  ```
</CodeGroup>

Add `--headed` to either invocation to run with a visible browser window — useful for watching the test execute or debugging timing issues. Without it, Playwright runs Chromium headlessly.

The script logs every step's start, picked selector, click strategy, and elapsed time to your terminal. Failed steps print the underlying error and abort the test.

## What you'll see in the terminal

```text theme={null}
[0.12s] Test started
[0.45s] ▶ Step 1 (default): Click email field
[0.78s]   ↳ resolveLocator picked: [data-testid="email"]
[0.92s]   ↳ click: ok (locator.click)
[1.05s]   ✓ Step 1 ok (0.60s)
[1.25s] ▶ Step 2 (default): Type 'dev{{unique}}' into the Playground Name field
[1.51s]   ↳ resolveLocator picked: input[placeholder="Give your playground a name"]
[1.68s]   ✓ Step 2 ok (0.43s)
...
[t]   ✗ Step 12 FAILED after 15.02s: [__resolveLocator] no candidate selector found after 15000ms — ...
```

## Where Playwright Test puts artifacts

The runner (not the generated script) creates these files in the directory you ran `npx playwright test` from:

| File / Folder        | What it is                                                                          |
| -------------------- | ----------------------------------------------------------------------------------- |
| `test-results/`      | Per-failure artifacts: error context, screenshots, traces. Created on failure only. |
| `.last-run.json`     | Metadata about the most recent run. Powers `npx playwright test --last-failed`.     |
| `playwright-report/` | HTML report (only if you opt in via `--reporter=html`).                             |

Add `test-results/`, `playwright-report/`, and `.last-run.json` to your `.gitignore` if you commit your specs.

## Common questions

<AccordionGroup>
  <Accordion title="Can I run this with `node script.spec.js` directly?">
    No — the spec uses `@playwright/test`'s `test()`/`expect()` framework, so it must be run via `npx playwright test`. If you want a plain Node script you'd need the `playwright` runtime package and a different generator (not currently exposed).
  </Accordion>

  <Accordion title="Does it use AI or call Decipher at runtime?">
    No. The script only requires `@playwright/test` and a Chromium binary. Once downloaded, it has no Decipher dependencies and works fully offline.
  </Accordion>

  <Accordion title="Does it work for tests with login steps?">
    Yes, but only when the login uses a **credential identity** (username + password stored in Decipher). The generator decrypts the password and inlines the recorded login Playwright steps with `{{username}}` / `{{password}}` substituted in place. Manual identities are skipped because they go through the Decipher CUA agent at runtime.
  </Accordion>

  <Accordion title="Why are some steps marked SKIPPED in the output?">
    The deterministic generator can only emit step types whose action is fully described by the stored selectors. Steps that need the Decipher runtime (assertions, code-block execution, tab-switching by AI judgement, file uploads, etc.) are emitted as comments so the script structure still mirrors your test, but no action is taken.
  </Accordion>

  <Accordion title="The test fails — what should I check first?">
    The terminal log pinpoints which step failed and why. Three common categories:

    * **`no candidate selector found`** — every cached selector returned zero matches. Usually means the previous step didn't reach the expected page state (modal didn't open, navigation didn't happen).
    * **`locator.click failed (intercept/Timeout)`** — the script auto-escalates through `page.mouse.click` at the element's center, then a synthetic event dispatch. If all three fail the click target is genuinely unreachable.
    * **`tiebreak (location): … → nth(N)`** — the cached selector matched multiple visible elements; the script picks the one whose center is closest to the originally-recorded action area. If the wrong element was picked, the test was likely recorded against a layout the page no longer matches.
  </Accordion>
</AccordionGroup>
