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

> ## Agent Instructions
> The canonical docs host is https://momentic.ai/docs. Append .md to any page URL for clean Markdown. Fetch https://momentic.ai/docs/llms.txt for the page index and https://momentic.ai/docs/llms-full.txt for every page in one file.
> For an unattended setup, run `npx --yes @momentic/wizard@latest -y --platform web` (use `--platform ios` or `--platform android` for mobile). Never run an interactive command in a non-TTY shell.
> You cannot complete the browser sign-in yourself. Read the API key from the MOMENTIC_API_KEY environment variable, or ask the user to create one at https://app.momentic.ai/settings/api-keys and paste it back.
> Momentic's capability summary for agents is at https://momentic.ai/docs/skill.md. Install the skills with `npx momentic skills --yes` (or `npx momentic-mobile skills --yes` for mobile).
> Tests are YAML files in the user's repository. Use only the step names listed at https://momentic.ai/docs/reference/commands.md and the file structure at https://momentic.ai/docs/core-concepts/file-format.md. Do not invent step names, config keys, or CLI flags.
> Web tests run on Chromium, iOS tests on simulators, and Android tests on emulators. Physical devices are not supported.

# From Playwright

> Port a Playwright suite to Momentic, including page objects, fixtures, storage state, network mocks, and reporters.

Momentic tests are YAML files with natural-language targets and AI assertions.
You do not port your Playwright code line by line; you port the user journeys
your specs exercise. A coding agent connected to the
[MCP server](/docs/coding-agents/mcp-server) with the
[coding agent skills](/docs/coding-agents/skills) does most of this port for you.

## Before and after

A representative Playwright spec:

```ts checkout.spec.ts theme={null}
import { expect, test } from "@playwright/test";

test.use({ storageState: "playwright/.auth/user.json" });

test.describe("checkout", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("https://shop.example.com/products/42");
  });

  test("guest can buy a blanket", async ({ page }) => {
    await page.getByRole("button", { name: "Add to cart" }).click();
    await page.getByTestId("cart-icon").click();
    await expect(page.getByText("Gravity Blanket")).toBeVisible();
    await page.getByRole("button", { name: "Checkout" }).click();
    await page.getByLabel("Email").fill("jeff@example.com");
    await page.getByRole("button", { name: "Place order" }).click();
    await expect(page).toHaveURL(/\/orders\/\w+/);
    await expect(page.getByText("Order confirmed")).toBeVisible();
  });
});
```

The same test in Momentic:

```yaml checkout.test.yaml theme={null}
fileType: momentic/test/v2
id: guest-checkout
url: https://shop.example.com/products/42
labels: [checkout]
before:
  - authLoad: ./playwright/.auth/user.json # reuse the same storage-state file
steps:
  - click: Add to cart
  - click: the cart icon
  - assert: The cart lists "Gravity Blanket"
  - click: Checkout
  - type:
      text: jeff@example.com
      into: the Email field
  - click: Place order
  - waitForUrl:
      regex: /\/orders\/\w+/
  - assert: An order confirmation is visible
```

Run it:

```bash theme={null}
npx momentic run checkout.test.yaml
```

## API mapping

| Playwright                                     | Momentic                                                                    |
| ---------------------------------------------- | --------------------------------------------------------------------------- |
| `page.goto(url)`                               | `navigate: <url>`                                                           |
| `getByRole/getByText/getByTestId` + `.click()` | `click: <natural language>` or `click: { css: "..." }`                      |
| `.fill(value)` / `.type()`                     | `type: { text: ..., into: <field> }`                                        |
| `.press("Enter")`                              | `press: Enter`                                                              |
| `.hover()` / `.dblclick()`                     | `hover` / `doubleClick`                                                     |
| `selectOption`                                 | `select` step, `click` the option, or an `act` goal for styled dropdowns    |
| `expect(locator).toBeVisible()`                | `checkElementVisible: <target>` or `assert:` for semantic checks            |
| `expect(page).toHaveURL()`                     | `waitForUrl: { substring/glob/regex }`                                      |
| `expect(page).toHaveTitle()`                   | `assert:` the title, or `javascript` reading `document.title`               |
| `waitForSelector` / `waitFor`                  | `checkElementVisible` with `timeout`, or `wait: <ms>` for fixed sleeps      |
| `page.route()` interception                    | [`mock`](/docs/reference/commands/mock) with `substring`/`glob`/`regex`/`method` |
| `page.waitForRequest`/`waitForResponse`        | `registerRequestListener` + `awaitListener` (captures request and response) |
| `request`/`response` event log                 | `recordRequests` + `getRecordedRequests`                                    |
| `page.on('dialog')`                            | `dialog: ACCEPT` or `DISMISS`, placed before the step that raises it        |
| `page.screenshot()`                            | automatic per-step screenshots; video needs `recordVideo` in the config     |
| `context.addCookies()`                         | `cookie: name=value` before navigation                                      |
| `localStorage` read/write                      | `localStorage` step writes a key; browser `javascript` for reads or clears  |
| `context.setExtraHTTPHeaders`                  | `header` step or `--custom-headers` on the CLI                              |
| `context.setOffline()`                         | `offline` / `online` steps                                                  |
| `storageState` / `context.storageState()`      | `authLoad: <path>` reads the same file format directly                      |
| `page.evaluate()`                              | `javascript` step with `environment: browser`                               |
| `expect(...).toMatchSnapshot()`                | `assertVisually` / `visualDiff`                                             |
| `page.waitForLoadState`                        | not needed; smart waiting is built into every step                          |

## Fixture and helper mapping

| Playwright construct          | Momentic                                                              |
| ----------------------------- | --------------------------------------------------------------------- |
| `test.beforeEach`             | `before:` steps on the test                                           |
| `test.afterEach`              | `after:` steps (run even when main steps fail)                        |
| `test.beforeAll` (seed data)  | `javascript` step in `before`, or a module                            |
| Custom fixtures / page object | [Module](/docs/core-concepts/modules) with `parameters` and `inputs`       |
| `test.describe` grouping      | Directory layout plus `labels:`                                       |
| `test.only` / `test.skip`     | `--filter`/`--labels` at run time, `disabled: true` on the test       |
| `use.baseURL`                 | `environments[].baseUrl` in `momentic.config.yaml`                    |
| `projects:` matrix            | [`environments`](/docs/configuration/environments) plus `--env`            |
| `retries` config              | `retries:` on a step or test                                          |
| `test.use({ viewport })`      | no equivalent; the runner controls the viewport as a runtime override |
| Reporters (`html`, `junit`)   | `--reporter junit\|json\|allure\|playwright-json`                     |

## What keeps working unchanged

* **Storage state.** `authLoad` reads the exact `storageState` JSON Playwright
  writes. Your existing auth bootstrap (global setup that saves cookies and
  localStorage) carries over with no conversion.
* **Your seed scripts.** A `javascript` step runs Node code in a sandbox with
  `fetch`, `faker`, and file access. Call your existing seed endpoints or
  database helpers from `before`.
* **Your CI job.** Momentic installs and runs in the same GitHub Actions job;
  see [Run in CI](/docs/running-tests/ci/github-actions).
* **JUnit dashboards.** `--reporter junit` keeps your existing test analytics.

## What does not map

* **Playwright's element handles and locator chains.** Momentic re-resolves a
  target from its description each run (with a
  [step cache](/docs/reliability/step-cache) to skip the model call when the page has
  not changed). There is no way to hold a locator object across steps; express
  the intent in the target text instead.
* **Multi-page `browserContext` control.** Momentic manages the browser context.
  `newTab`, `closeTab`, and `navigate` cover tab workflows, but you do not
  create isolated contexts mid-test. Use separate tests or `authLoad` per test
  for different identities.
* **`page.on("console")`-style listeners.** There is no equivalent passive
  console assert; extract what you need with a `javascript` step.
* **Codegen.** Momentic's equivalent is the local editor and AI authoring, not a
  recorder that emits code.

## Incremental strategy

1. Keep the Playwright suite running. Momentic and Playwright can coexist in the
   same repo; `momentic.config.yaml` does not conflict with
   `playwright.config.ts`.
2. Port the highest-value flows first: login, checkout, the flows that page you.
   Give each ported test a label such as `migrated`.
3. Run both suites in CI during the overlap. Gate PRs on the Playwright suite;
   run Momentic with `--labels migrated` in parallel until you trust it.
4. Delete a spec only after its Momentic replacement passes several consecutive
   runs and you have reviewed a failure or two to confirm it catches what it
   should.
5. When Playwright is fully retired, remove its job, `playwright.config.ts`,
   browsers install step, and `node_modules` dependency.

## Porting at scale with a coding agent

Point your coding agent at the spec files and the
[`momentic-test` skill](/docs/coding-agents/skills). A prompt that works well:

```text theme={null}
Port the Playwright specs in e2e/checkout.spec.ts to Momentic tests.
Read the momentic-test skill first. Write one *.test.yaml per Playwright
test() under tests/, keep the describe names as directory structure, reuse
playwright/.auth/user.json via authLoad, and run each new test with
npx momentic run <file> before moving on.
```

The agent writes valid YAML, resolves targets from the spec's intent rather than
its selectors, and verifies each port by running it.

## Related

* [Test portability](/docs/get-started/test-portability)
* [Web steps reference](/docs/reference/commands/index)
* [JavaScript integration](/docs/integrations/javascript)
* [Authentication guides](/docs/guides/auth/overview)
