# Assuremind Studio Guide

Assuremind Studio is a browser-based UI for writing tests, running them, reviewing results, and managing self-healing suggestions — all without touching the command line.

---

## Starting the Studio

```bash
npx assuremind studio
```

The server starts and your default browser opens at `http://localhost:4400`.

To use a different port:

```bash
npx assuremind studio --port 5000
```

To start the server without opening the browser (e.g., on a remote machine):

```bash
npx assuremind studio --no-open
```

The Studio port can also be set permanently in `autotest.config.ts`:

```typescript
export default defineConfig({
  studioPort: 4400,
  // ...
});
```

---

## Navigation

The sidebar on the left has these pages:

| Icon | Page | Purpose |
|------|------|---------|
| 📊 | Dashboard | Run summary, recent results, quick-action links |
| ✨ | Smart Tests | Generate test suites from user stories or Jira links |
| ✏️ | Test Editor | Create and manage suites, cases, and steps |
| ▶️ | Run Config | Configure and launch a test run |
| 📋 | Reports | Browse historical run results |
| 🔧 | Variables | Manage test variables and secrets |
| 🩺 | Self-Healing | Review and apply AI-generated selector fixes |
| ⚙️ | Settings | Environment, browsers, healing, capture settings, MCP integration, CI/CD integration |
| 🌿 | Git Control Center | Branch management, AI commits, push/pull, conflict resolution |

---

## Dashboard

The Dashboard shows at a glance:

- **Pass rate** — percentage of tests passing in the most recent run
- **Total tests** — suite and case counts
- **Last run** — time, duration, and status of the most recent run
- **Healing queue** — number of pending self-healing suggestions

**Quick actions:**

- **Run All** — starts a full run immediately
- **Open Studio** — returns to this page
- **Apply Healing** — jumps to the Self-Healing page

While data is loading, animated skeleton cards are shown so the layout never jumps.

---

## Test Editor

> **Writing steps:** for a full catalog of plain-English step phrasings (UI **and** API) with examples, see [WRITING-STEPS.md](WRITING-STEPS.md).

### Suite Types

Assuremind supports three suite types:

| Type | Description |
|------|-------------|
| **UI** | Standard Playwright browser automation tests. Steps show a blue "UI" badge. |
| **API** | API-only tests using HTTP request/assertion steps. |
| **Audit** | Full Playwright automation with built-in Lighthouse non-functional checks. Steps show an amber "Audit" badge. Screenshot, video, and trace are disabled by default. Test files stored in `tests/audit/`. |

### Audit Suites

Audit suites run exactly like UI suites (full Playwright automation) but add non-functional quality checks via Google Lighthouse.

**Audit Flag on Steps**

In Audit suites, each step has a `⚡ Audit` button. Clicking it marks that step as a Lighthouse checkpoint:
- When a marked step runs, Lighthouse audits `page.url()` at that point in the test
- Click `⚡ Audit` to mark, then confirm with **✓** to save or **✗** to cancel
- If no steps are marked, Lighthouse is skipped entirely even in Audit suites

**Non-Functional Checks**

Each test case in an Audit suite has a **Non-Functional Checks** section with three checkboxes:
- **⚡ Performance** — Lighthouse Performance score
- **♿ Accessibility** — Lighthouse Accessibility score
- **🔍 SEO** — Lighthouse SEO score

If all three are unchecked, Lighthouse is skipped for that case. A warning is shown if none are selected.

**Lighthouse Score Thresholds**

| Range | Rating | Effect |
|-------|--------|--------|
| 0–49 | Poor (red) | Test case **FAILS** |
| 50–89 | Needs Work (amber) | Warning only |
| 90–100 | Good (green) | Pass |

### Creating a Suite

1. Click the **+ New Suite** button (top right)
2. Enter a **name** — this becomes the folder name under `tests/` (e.g., "Login Tests" → `tests/login-tests/`); Audit suites use `tests/audit/`
3. Choose a **suite type**: UI, API, or Audit
4. Optionally add a **description** and **tags**
5. Click **Create**

### Creating a Test Case

1. Select a suite from the left panel
2. Click **+ New Case**
3. Enter a **name**, **priority** (`critical`, `high`, `medium`, `low`), and optional **tags**
4. Click **Create**

### Adding Steps

1. Open a case by clicking it
2. Click **+ Add Step** at the bottom of the step list
3. Type a plain-English instruction, e.g.:
   - `Go to the login page`
   - `Enter "admin@example.com" in the email field`
   - `Click the Login button`
   - `Verify the page title contains "Dashboard"`
4. Press **Enter** or click the checkmark

**Tips for writing good instructions:**
- Be specific about what to interact with: `Click the blue Submit button` rather than just `Submit`
- Use quotes for literal values: `Enter "admin@example.com" in the email field`
- Reference variables with double-braces: `Enter "{{ADMIN_EMAIL}}" in the email field`
- Include assertions: `Verify that the success banner is visible`

### Test Recorder

The Test Recorder lets you create tests by interacting with your application in a real browser — **no coding, no AI, no cost**.

#### Recording a test

> **Note:** The recorder is available for **UI** and **Audit** suites only. API suites do not show the Record button.

1. Open a test case in the editor
2. Click the red **Record** button (between "Add Step" and "Generate All")
3. A headed Chromium browser opens your app's base URL
4. Interact with the page naturally — click buttons, fill forms, navigate
5. **Iframes are handled automatically** — the recorder injects into all frames and generates correct `frameLocator()` code
6. Each action appears in the live preview panel in real time
7. Click **Stop Recording** when done

Every recorded action becomes a test step with **pre-generated Playwright code** — no AI generation needed.

#### Assertion shortcuts

While recording, use these keyboard shortcuts to add assertions:

| Shortcut | Assertion Type | Behavior | Example Output |
|----------|---------------|----------|----------------|
| **Shift+Click** | Hard assertion — element visible | Test **stops** on failure | `await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();` |
| **Ctrl+Shift+Click** | Soft assertion — element visible | Test **continues**, failures collected at end | `await expect.soft(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();` |
| **Ctrl+Shift+U** | Current URL matches | Hard assertion | `await expect(page).toHaveURL(/dashboard/);` |
| **Ctrl+Shift+T** | Page title matches | Hard assertion | `await expect(page).toHaveTitle(/Dashboard/);` |

Assertion steps show a green badge in the live preview. Soft assertions show a blue **Soft** badge in the step editor.

#### Hard vs Soft Assertions

| Type | Keyword | Behavior | When to use |
|------|---------|----------|-------------|
| **Hard** | `Verify ...` | Test stops immediately on failure | Critical checks — login worked, page loaded |
| **Soft** | `Soft verify ...` | Test continues, all failures reported at end | Multiple checks on one page — verify 5 fields are correct |

**Three ways to use soft assertions:**

1. **Recorder** — Ctrl+Shift+Click instead of Shift+Click
2. **Plain English** — Start instruction with "Soft verify..." or "Soft check..." or "Soft assert..."
3. **Step toggle** — Click the **Hard/Soft** badge on any assertion step in the editor to switch

#### How locators are resolved

The recorder does **not** use simple CSS selectors or XPath. Instead, it:

1. Captures raw element metadata from the DOM (tag, attributes, text, ARIA properties)
2. Sends the metadata to Node.js where Playwright's accessibility tree is queried
3. Tries **6 locator strategies** in priority order, verifying each with `count() === 1`:
   - `data-testid` attribute
   - `getByRole()` with exact name + heading level
   - `getByRole()` with exact name
   - `getByLabel()`
   - `getByPlaceholder()`
   - `getByText()`
   - CSS fallback (last resort)
4. The first strategy that uniquely identifies the element is used

For elements **inside a shadow root**, the recorder uses `composedPath()[0]` to pierce the shadow boundary and builds a `host >> inner` pierce path automatically — no special action needed.

This produces the most resilient locators possible — the same quality as hand-written Playwright tests.

#### What makes it stand out vs other recorders

| Feature | Selenium IDE | Playwright Codegen | Assuremind Recorder |
|---------|-------------|-------------------|---------------------|
| Locator quality | CSS/XPath | Good | Best — 6 strategies, verified against live page |
| Accessibility tree | No | Partial | Full — every locator checked via Playwright API |
| **Iframe support** | Partial | Manual | **Auto** — detects iframes, generates `frameLocator()` code, both click & fill |
| **Shadow DOM** | No | Partial | **Auto** — `composedPath()` pierces shadow roots, generates `>>` locators |
| **JS dialogs** | No | No | **Auto** — dialogs auto-accepted, `page.once('dialog', ...)` prepended to triggering step |
| **Keyboard capture** | Partial | Partial | **Full** — Tab, Shift+Tab, arrows, Enter, Escape, Ctrl+A, Space |
| Assertions | Manual | Manual | Shift+Click (hard), Ctrl+Shift+Click (soft), URL & title shortcuts |
| Plain-English steps | No | No | Yes — human-readable instructions auto-generated |
| Self-healing after | No | No | Yes — 5-level AI healing cascade |
| RAG memory | No | No | Yes — recorded steps feed the learning loop |
| Cost | Free | Free | Free |

#### Biggest pain points solved

| Pain Point | How the Recorder Solves It |
|-----------|---------------------------|
| Writing tests is slow | Record a full test in 30 seconds |
| Selectors break constantly | Locators verified against Playwright's accessibility tree in real time |
| AI costs money | Recording + code generation = $0, zero AI calls |
| Non-technical testers can't write tests | Anyone who can click a browser can create tests |
| Assertions are hard to write | Shift+Click (hard), Ctrl+Shift+Click (soft), Ctrl+Shift+U for URL, Ctrl+Shift+T for title |
| Recorded tests are fragile | 6-strategy locator resolution + post-run 5-level self-healing |
| Apps use iframes (SAP, Salesforce) | Auto-detects iframe context, generates `frameLocator()` chains |
| Apps use Web Components / Shadow DOM | Automatic pierce-path detection using `composedPath()` + `>>` combinator |
| App pops up a JS alert mid-recording | Dialog auto-accepted, `page.once('dialog', ...)` handler prepended to the triggering step |
| Need to record Tab/arrow key navigation | Tab, Shift+Tab, arrow keys, Ctrl+A and Space captured automatically |

#### Iframe support

The recorder and AI code generator both fully support **same-origin iframes** — common in enterprise apps like SAP, Salesforce, and embedded widgets:

- The capture script is injected into **all frames** (main page + every iframe), not just the top-level page
- When an element is inside an iframe, the recorder detects the frame context and computes a selector for the iframe element (using `id`, `name`, `data-testid`, or `src`)
- Locators are resolved against the correct frame using `page.frameLocator('...')` — producing code like:
  ```typescript
  await page.frameLocator('#content-frame').getByRole('button', { name: 'Submit' }).click();
  await page.frameLocator('iframe[src*="payment"]').getByPlaceholder('Card number').fill('4111...');
  ```
- Both **click** and **fill** (text input) actions work inside iframes
- Dynamically added iframes are detected via `frameattached` events and injected automatically
- The recording banner only appears in the main frame — iframes display cleanly
- **Limitation:** Cross-origin iframes (different domain) cannot be instrumented due to browser security. The main frame and same-origin iframes work fully.

#### Shadow DOM support

The recorder automatically handles Web Components and any element nested inside a shadow root:

- Uses `composedPath()[0]` on every event to get the real inner element, not the shadow host that the browser re-targets events to
- Detects shadow root context via `getRootNode() instanceof ShadowRoot`
- Builds a **pierce path** (`host-selector >> inner-selector`) and uses Playwright's `>>` combinator in generated code:
  ```typescript
  await page.locator('my-login-form >> button[type="submit"]').click();
  await page.locator('custom-input >> input').fill('user@example.com');
  ```
- Works transparently — just interact with the element normally, the recorder handles the rest

#### JavaScript alerts & dialogs

When your app shows a JS `alert()`, `confirm()`, or `prompt()` during recording:

- The dialog is **automatically accepted** so it doesn't block the session
- A `page.once('dialog', dialog => dialog.accept())` handler is **prepended** to the triggering step's generated code, ensuring correct execution order during replay:
  ```typescript
  page.once('dialog', dialog => dialog.accept());
  await page.getByRole('button', { name: 'Delete' }).click();
  ```
- The dialog type and message are stored on the step for reference

#### Keyboard capture

Beyond click and fill, the recorder captures keyboard interactions:

| Key | When captured | Generated code |
|-----|--------------|----------------|
| **Enter** | While focused on a form field | `await page.getByLabel('...').press('Enter');` |
| **Escape** | Any time | `await page.keyboard.press('Escape');` |
| **Tab** | While focused on a form field | `await page.getByLabel('...').press('Tab');` |
| **Shift+Tab** | While focused on a form field | `await page.getByLabel('...').press('Shift+Tab');` |
| **Arrow keys** | When focused on a non-input element | `await page.keyboard.press('ArrowDown');` |
| **Ctrl+A** | Any time | `await page.keyboard.press('Control+A');` |
| **Space** | On a button or role=button element | `await page.getByRole('button', { name: '...' }).press(' ');` |

#### Technical details

- The recorder uses `context.exposeBinding()` for browser↔Node.js communication that survives page navigations — works across all frames
- Input values are captured on blur (when the user leaves the field), not on keystrokes — no duplicate steps
- The capture script is re-injected on every page load via `page.on('load')` and into all child frames via `page.on('frameattached')` + `page.on('framenavigated')`
- Recorded steps use the `recorder` strategy tag, distinguishing them from AI-generated code
- All recorded steps are immediately usable — no "Generate Code" step required

### Advanced Step Patterns

The AI code generator handles several advanced browser scenarios automatically — just describe them in plain English.

#### JavaScript Alerts & Dialogs

Mention "alert", "confirm dialog", "popup", "click OK", or "dismiss" and the AI generates a `page.once('dialog', ...)` handler registered before the triggering action:

| Instruction | Generated pattern |
|-------------|------------------|
| `Accept the alert dialog` | `page.once('dialog', d => d.accept())` |
| `Dismiss the confirmation popup` | `page.once('dialog', d => d.dismiss())` |
| `Enter "yes" in the prompt dialog` | `page.once('dialog', d => d.accept('yes'))` |
| `Verify the alert says "Are you sure?" and accept it` | Handler that checks `dialog.message()` then accepts |

#### Shadow DOM

For elements inside a Web Component's shadow root, use language like "inside the `<component-name>` component" or "in the shadow root" — the AI uses Playwright's `>>` pierce combinator:

```
Click the submit button inside the my-form component
Fill the email input inside custom-input
```

Generated code:
```typescript
await page.locator('my-form >> button[type="submit"]').click();
await page.locator('custom-input >> input').fill('user@example.com');
```

#### iFrames — Click and Fill

Both clicking and typing inside iframes work. Describe the action naturally, referencing the iframe if known:

```
Click the Submit button inside the payment iframe
Enter "4111111111111111" in the card number field inside the checkout frame
```

Generated code:
```typescript
await page.frameLocator('iframe[src*="payment"]').getByRole('button', { name: 'Submit' }).click();
await page.frameLocator('iframe[src*="checkout"]').getByPlaceholder('Card number').fill('4111111111111111');
```

#### Keyboard Actions

Describe key presses directly — the AI uses `page.keyboard.press()` or `page.keyboard.type()`:

| Instruction | Generated code |
|-------------|----------------|
| `Press Enter` | `await page.keyboard.press('Enter')` |
| `Press Escape to close the modal` | `await page.keyboard.press('Escape')` |
| `Press Tab to move to the next field` | `await page.keyboard.press('Tab')` |
| `Press Ctrl+A to select all text` | `await page.keyboard.press('Control+A')` |
| `Press Ctrl+Z to undo` | `await page.keyboard.press('Control+Z')` |
| `Type "Hello World" using the keyboard` | `await page.keyboard.type('Hello World')` |
| `Press Enter on the search field` | `await page.getByRole('textbox').press('Enter')` |

---

### Generating Code

After adding steps, click **Generate Code** (or **Generate** on an individual step) to have the AI write the Playwright code.

- Steps with **green code** icons have generated code
- Steps with **grey code** icons are pending generation
- Click any step's code icon to see or edit the generated code

**The AI only runs once per unique (instruction, URL pattern) pair.** Subsequent runs use the cached code, making them free and instant.

### Reordering Steps

Drag the ≡ handle on the left of any step to reorder it. The order numbers update automatically.

### Deleting

- Delete a step: click the **×** on the right of the step row
- Delete a case: open the case, click **⋮** → **Delete Case**
- Delete a suite: select the suite, click **⋮** → **Delete Suite**

Deletions are permanent. There is no undo in the Studio — use Git to recover.

---

## Story Generator (Smart Tests)

Generate an entire test suite from a plain-English user story:

1. Navigate to **Story Generator**
2. Type your story in the text area:
   ```
   As a user I want to reset my password so I can regain access to my account.
   Steps:
   - I click "Forgot password" on the login page
   - I enter my email address
   - I receive a reset email
   - I click the reset link
   - I enter a new password and confirm it
   - I am redirected to the login page with a success message
   ```
3. Optionally provide a **Suite Name** (AI generates one if blank)
4. Click **Generate Suite**

The AI creates:
- A suite directory with `suite.json`
- One or more test case files with steps
- Playwright code for each step

The generated files appear in the Test Editor immediately.

### Jira Integration Warning

If Jira credentials are not configured and you try to load a Jira issue, a **Configuration Required** modal appears. This modal includes a **"Don't show this again"** checkbox at the bottom — checking it and clicking Close suppresses the modal permanently for that integration (stored in browser localStorage). You can re-enable it by clearing site data or browser storage.

---

## Run Config

Configure and start a test run without the command line:

1. Navigate to **Run Config**
2. Choose a **scope**:
   - All suites
   - Specific suite (dropdown)
   - By tag (text input)
   - Specific test (text input)
3. Select **browsers**: Chromium, Firefox, WebKit (multi-select)
4. Set **parallel workers** (slider, 1–8)
5. Toggle **Headed mode** to watch the browser during the run
6. Toggle **Self-Healing** on/off for this run
7. Optionally select a **Device** for emulation (see below)
8. Click **Start Run**

A live progress panel shows test results as they complete. The WebSocket connection streams updates in real time — no page refresh needed.

### Stopping a run

While a run is in progress, a **Stop** button (red square icon) appears in the execution header. Clicking it sends an immediate stop signal to the server — the current step finishes, then all remaining test cases are skipped and the run exits cleanly. The UI transitions out of the running state automatically once the stop is confirmed.

When the run finishes, you are offered a link to the full report.

### Device Emulation

The **Device Emulation** card appears below the Browsers card. It lets you emulate a real mobile or tablet device using Playwright's built-in device descriptors. No manual viewport configuration needed — selecting a device automatically applies the correct viewport, user-agent, device pixel ratio, and touch/mobile flags.

**Categories:**

| Tab | What it shows |
|-----|---------------|
| **All** | All presets |
| **Desktop** | Viewport-only presets: 1920×1080, 1440×900, 1366×768, 1280×720, 1024×768 |
| **Mobile** | iPhone 15 Pro/15/14/SE, Pixel 7/5, Galaxy S9+/S8 |
| **Tablet** | iPad Pro 11″, iPad (gen 7), iPad Mini, Galaxy Tab S4 |

**How to use:**
1. Click a device card — it highlights in teal and shows a device pill in the card header
2. Click the **×** in the header or click **No Emulation** to clear
3. An amber warning appears if a mobile/tablet device is selected without Chromium — mobile emulation works best on Chromium

**In Reports:** each case row shows a teal device badge (📱 / 🖥️ / tablet icon) when the run used device emulation.

**Equivalent CLI flag:** `--device "iPhone 15 Pro"`

### CI Config Generator

Click **Generate CI Config** to produce a pipeline file from your current run settings (browsers, parallel workers, device). Pick a platform tab — **GitHub Actions**, **GitLab CI**, or **Jenkins**.

- The YAML opens in a **preview** (read-only). Click **✎ Edit** to customise it — change branches, Node version, add steps, env vars, etc. Your edits apply to **Copy**, **Download**, and **Write to repo**. **↺ Reset** discards edits and regenerates from the run config.
- **Copy** and **Download** are always available.
- **Write to repo** writes the file into your project at its standard path:
  - GitHub Actions → `.github/workflows/assuremind.yml`
  - GitLab CI → `.gitlab-ci.yml`
  - Jenkins → `Jenkinsfile`

  This button only appears when **Settings → CI/CD Integration → "Write CI files to repo"** is enabled (off by default). It **only writes the file** — it does **not** commit or push. Review the change and commit it from the **Git Control Center** when you're ready.

---

## Reports

Browse all historical run results:

- Each run shows: status badge (✅ / ❌), date/time, duration, pass/fail/skip counts
- Click a run to see the **full result tree**: Suite → Case → Step
- Each step shows: status, duration, generated code, and screenshot (on failure)
- Failed steps show the error message and a screenshot thumbnail
- A **Downloads** panel appears for runs that captured files (see [File Upload & Download](#file-upload--download)) — click a file to save it locally

### Lighthouse Results (Audit Suites)

For Audit suite cases that have Lighthouse data, three tab buttons appear on the case row:
- **⚡ Speed** — Performance score
- **♿ A11y** — Accessibility score
- **🔍 SEO** — SEO score

Score colors follow the threshold rules: red for Poor (< 50), amber for Needs Work (50–89), green for Good (90+). A case row is highlighted red when any score is Poor (< 50).

**Allure Integration**: Audit cases produce three separate HTML report attachments per category — Performance Report, Accessibility Report, and SEO Report. The Allure test status is set to FAILED if any Lighthouse score is below 50.

**Filter and sort:**
- Use the search box to filter by run ID or date
- Use the **Status** filter to show only failed runs
- Use **Limit** to control how many runs are displayed (default: 20)

---

## Variables

Manage variables that can be referenced in step instructions:

### Global variables

Available in all test runs regardless of environment.

1. Click **+ Add Variable**
2. Enter a **key** (e.g., `ADMIN_EMAIL`) and **value**
3. Toggle **Secret** to mask the value in logs and UI
4. Click **Save**

### Environment-specific variables

Variables can be overridden per environment (`dev`, `staging`, `prod`).

1. Select the environment tab
2. Add variables that override globals for that environment
3. Use `--env <name>` when running to load the overrides

### Using variables in steps

Reference any variable in a step instruction using double-braces:

```
Enter "{{ADMIN_EMAIL}}" in the email field
Navigate to "{{BASE_URL}}/login"
```

Variables are interpolated into the generated Playwright code at run time.

---

## Self-Healing

When a test step fails and the AI generates a healed selector, it appears here for review.

### Healing event list

Each event shows:
- **Suite / Case / Step** — where the failure occurred
- **Original code** — the code that failed
- **Healed code** — the AI's suggested fix
- **Healing level** — 1–5 (higher = more aggressive strategy)
- **Status** — `pending`, `accepted`, or `rejected`

### Reviewing suggestions

- Click **Accept** to apply the healed code to the `.test.json` file
- Click **Reject** to dismiss the suggestion (original code is kept)
- Click **Accept All** to accept every pending suggestion in one click

Accepted changes are written immediately to the test files. The next run will use the healed code.

### Healing statistics

The top of the page shows:
- Total heals applied this month
- Daily budget remaining (based on `healing.dailyBudget` in config)
- Most healed suite and case

---

## Settings

Edit `autotest.config.ts` from the browser:

| Setting | Description |
|---------|-------------|
| Base URL | The URL of the application under test |
| Browsers | Which browsers to use by default |
| Headless | Run headless by default |
| Timeout | Step timeout in milliseconds |
| Retries | Number of retries on failure |
| Parallel | Number of parallel workers |
| Screenshot | `on`, `off`, or `on-failure` |
| Video | `on`, `off`, or `on-failure` |
| Trace | `on`, `off`, or `on-failure` |
| Healing enabled | Master switch for self-healing |
| Healing max level | Maximum healing cascade level (1–5) |
| Daily budget | Maximum AI spend on healing per day (USD) |
| Studio port | Port for the Studio server |
| MCP enabled | Master switch for MCP-enhanced code generation (ON by default) |
| MCP headless | Run MCP browser without visible window |
| MCP act-then-script | Two-phase generation: execute via MCP first, convert to code |
| MCP proactive healing | Validate selectors against live page before test runs |
| RAG enabled | Master switch for semantic memory from past runs (ON by default) |
| RAG Code Corpus | Remember instruction-to-code mappings from successful runs |
| RAG Healing Corpus | Remember past healing fixes for smarter self-repair |
| RAG Error Catalog | Track recurring error patterns to avoid known-bad selectors |
| RAG Embedder | Similarity engine: TF-IDF (local, free, offline) |

Changes are saved immediately to both `autotest.config.json` and `autotest.config.ts`.

---

## MCP Integration (AI-Sighted Generation)

The **MCP Integration** card in Settings controls how the AI code generator interacts with a real Playwright browser during code generation via the official `@playwright/mcp` server.

**Key concept:** MCP is used **only during code generation** — it takes accessibility snapshots so the AI sees actual page elements instead of guessing. Test execution is never affected.

### Settings

| Toggle | Default | Description |
|--------|---------|-------------|
| Enable MCP | ON | Master switch. When off, AI generates code blindly (original behavior). |
| Headless mode | ON | Run the MCP browser without a visible window. |
| Act-then-script | OFF | Two-phase generation: AI plans actions → executes on real browser → converts to Playwright code. Higher accuracy but slower. |
| Proactive healing | OFF | Before a test run, take a fresh snapshot and validate all selectors in generated code. Missing elements are flagged with suggestions via WebSocket. |

### How Snapshot-Driven Generation Works

1. You click **Generate Code** on a step (or **Generate All**).
2. The SmartRouter checks template cache first (free, instant).
3. If no cache hit, MCP launches a headless browser and navigates to your app URL.
4. An accessibility snapshot captures all interactive elements (buttons, inputs, links with their roles and names).
5. The AI receives the snapshot + your step instruction and generates Playwright code using real selectors.
6. The MCP browser stays open for subsequent steps (idle timeout: 30s), so only the first step pays the startup cost.

### Fallback

If MCP fails at any point (browser crash, timeout, network issue), generation silently falls back to blind mode. No error is shown to the user — the AI simply generates code without the snapshot, exactly as before MCP was enabled.

---

## RAG Memory (Retrieval-Augmented Generation)

RAG gives the AI **semantic memory** from past test runs. Every successful step, every healing fix, and every recurring error is indexed so the AI can retrieve similar experiences and generate better code over time.

### Three Corpora

| Corpus | What it stores | When it's used |
|--------|---------------|----------------|
| **Code Corpus** | Instruction-to-code mappings from successful runs | During generation — similar past steps retrieved as examples (score 0.65-0.90) or used directly as fuzzy cache hits (score >= 0.90) |
| **Healing Corpus** | Past healing events (instruction + error + fix) | During self-healing — proven past fixes injected into Level 2 repair prompt |
| **Error Catalog** | Recurring error patterns per URL with fix history | During generation — AI warned about known-bad selectors/patterns to avoid |

### How It Works

1. **Step passes** → the instruction + code are ingested into the Code Corpus.
2. **Step fails** → the error pattern is recorded in the Error Catalog.
3. **Healing succeeds** → the fix is ingested into the Healing Corpus, and the Error Catalog is updated with the fix.
4. **Next generation** → the SmartRouter queries the Code Corpus. High-confidence matches (>= 90%) are used directly ($0, instant). Lower matches (65-90%) are passed as examples in the AI prompt.
5. **Next healing** → Level 2 queries the Healing Corpus for similar past fixes and appends them to the repair instruction.

### Storage

All RAG data is stored as JSON files in `results/.rag/`:
- `idf-vocab.json` — TF-IDF learned vocabulary
- `code-corpus.json` — instruction → code mappings
- `healing-corpus.json` — failure → fix mappings
- `error-catalog.json` — recurring error patterns

### Consumer Experience

**RAG is ON by default — zero setup required.** It works automatically from the very first run.

- **Run 1** — memory is empty, AI generates code normally
- **Run 2+** — RAG kicks in silently: similar instructions are retrieved instead of making API calls (free + faster), healing uses proven past fixes
- **Run 10+** — most common steps are served from RAG memory at zero cost

RAG data is stored in `results/.rag/` as plain JSON — fully Git-friendly. Commit it to share memory across your team, or cache it in CI to persist between runs.

### FAQ

| Question | Answer |
|----------|--------|
| Do I need to configure anything? | No. RAG is ON by default with zero setup. |
| Does it cost anything? | No. TF-IDF embedder runs locally. RAG hits replace paid AI calls. |
| Does it slow down tests? | No. RAG lookup is <1ms. It speeds up generation. |
| Works in CI/CD? | Yes. Cache `results/.rag/` between runs to persist memory. |
| Share memory across team? | Commit `results/.rag/` to Git or use a CI cache step. |
| How to reset? | Delete the `results/.rag/` folder. |

### When to Use the Settings Card

Most users never need to touch RAG settings. The **Settings → RAG Memory** card exists for power-user scenarios:

| Scenario | Action |
|----------|--------|
| Debugging a flaky test | Turn OFF Code Corpus — forces fresh AI generation instead of reusing a possibly stale mapping |
| Healing keeps suggesting a bad fix | Turn OFF Healing Corpus — clears the influence of a bad past fix |
| Major app redesign (UI overhaul) | Turn OFF RAG entirely — old memory from the previous UI is now misleading |
| Error warnings are outdated | Turn OFF Error Catalog — stops avoiding selectors that are fine now |
| Want deterministic CI runs | Disable RAG in CI config, keep ON for local development |

### Settings Reference

Go to **Settings → RAG Memory** to configure:
- **Enable RAG** — Master switch (ON by default)
- **Code Corpus** — Toggle instruction-to-code memory
- **Healing Corpus** — Toggle healing fix memory
- **Error Catalog** — Toggle error pattern tracking
- **Embedder** — TF-IDF (local, free, offline) — no API calls needed

---

## Faker Data

The Faker Data modal (accessible from the Test Editor) lets you browse 100+ dynamic data generators across 16 categories. Search or browse by category, then select one or more tokens — the modal supports **multi-select**, showing selected tokens as removable chips in a chip bar. Click **"Insert N tokens"** to insert all selected tokens comma-separated into your step (e.g. `{{FAKE_PERSON_FIRSTNAME}}, {{FAKE_PERSON_LASTNAME}}, {{FAKE_INTERNET_EMAIL}}`). This is especially useful for form-filling steps where you need first name, last name, email, and phone all at once. Tokens resolve to fresh random values on every test run.

---

## File Upload & Download

### Uploading files (for upload steps)

In the **Test Editor**, click the **Files** button (next to *Fake Data*) to open the file picker:

1. Click **Choose a file to upload** and pick a file from your machine.
2. It's saved into your project at **`fixtures/uploads/`** (committed with the repo, so it works in CI) and appears in the list.
3. Click the file to insert a **`{{FILE:name}}`** token into your step. Delete files with the trash icon.

The token resolves to the file's relative path at runtime, so it works with `setInputFiles`:

| Step (plain English) | Generated code |
|----------------------|----------------|
| `Upload {{FILE:resume.pdf}} to the Resume field` | `await page.getByLabel('Resume').setInputFiles('{{FILE:resume.pdf}}');` |
| `Upload {{FILE:resume.pdf}}` | `await page.locator('input[type="file"]').setInputFiles('{{FILE:resume.pdf}}');` |

For a button that opens a native file chooser, keep the listener and the click in **one step**:

```js
const fileChooserPromise = page.waitForEvent('filechooser');
await page.getByRole('button', { name: 'Choose File' }).click();
(await fileChooserPromise).setFiles('{{FILE:resume.pdf}}');
```

Files inside an iframe still need `frameLocator(...)`, e.g.
`await page.frameLocator('#frame').locator('input[type="file"]').setInputFiles('{{FILE:doc.pdf}}');`

**Waiting for the upload to complete** — `setInputFiles` only attaches the file; there is no universal "upload done" event. The step waits for a completion signal:

| Step (plain English) | Wait behavior |
|----------------------|---------------|
| `Upload {{FILE:resume.pdf}} to the Resume field` | waits for the network to settle (`waitForLoadState('networkidle')`) |
| `Upload {{FILE:resume.pdf}} to the Resume field and wait for 'Upload complete'` | waits for the success text — **most reliable** |
| `Upload {{FILE:photo.png}}` | uploads to the page's file input, then waits for network |

**Example upload steps:**

```text
Upload {{FILE:resume.pdf}} to the Resume field
Upload {{FILE:resume.pdf}} to the Resume field and wait for 'Upload complete'
Upload {{FILE:photo.png}}
Upload {{FILE:invoice.pdf}} into the Attachment input
```

### Downloads (auto-captured)

You do **not** need any special code to handle downloads — just trigger them with a click. The step
**waits until the file finishes downloading** (`waitForEvent('download')` → `await download.path()`),
and the runner **automatically saves every download** to `results/downloads/<runId>/`. Avoid
`download.saveAs()` / `fs` in steps — the sandbox blocks them and it isn't needed.

**Example download steps:**

```text
Download the "Export CSV" link
Click the "Export" button to download
Download the file by clicking "Report PDF"
Click "Download invoice" and wait for download
```

Each generates:
```js
const downloadPromise = page.waitForEvent('download');
await page.getByRole('link', { name: 'Export CSV' }).click();
const download = await downloadPromise;
await download.path();   // resolves once the download is complete
```

To view and retrieve captured files:

1. Go to **Reports → Run Reports** and click the run to expand it.
2. A **Downloads** panel at the top lists every file captured during that run (name + size).
3. Click any file to download it to your local machine.

The panel only appears when the run produced downloads.

---

## Visual Regression

Visual regression is configured in **Settings** → Visual Regression and reviewed in **Reports** → Visual Diffs / Baselines tabs.

**Configuration** (Settings → Visual Regression):
| Setting | Description | Default |
|---------|------------|---------|
| Enabled | Master toggle for visual regression | Off |
| Threshold | Pixel diff percentage to fail (0.1% - 10%) | 0.5% |
| Full Page | Capture entire scrollable page | Off |
| Mask Selectors | CSS selectors to mask (ignore dynamic content like timestamps) | Empty |

**Per-step toggle** — In the Test Editor, click the **Eye icon** on any step to enable visual comparison for that step only. Only steps with the Eye icon active are compared during runs.

**Reports → Visual Diffs tab** — after runs with visual checks:
- Select a run from the dropdown
- Summary: passed, failed, new baseline counts
- Each diff shows three images: Baseline (expected), Actual (current), Diff (red highlights)
- **Approve** — update baseline to the new screenshot (intentional change)
- **Reject** — keep current baseline (it's a real bug, dev needs to fix)
- **Approve All** — bulk approve all diffs in a run

**Reports → Baselines tab** — gallery of all current baseline screenshots:
- Organized by suite → case → step
- Browser and viewport info per baseline
- Delete individual or bulk-delete baselines

**File structure:**
```
baselines/                     ← Committed to git
├── login-suite/valid-login/
│   ├── step-2-chromium-1920x1080.png
│   └── step-2-firefox-768x1024.png
results/visual-diffs/{runId}/  ← Gitignored, per-run
├── step-2-baseline.png
├── step-2-actual.png
└── step-2-diff.png
```

---

## CI/CD Integration

The **CI/CD Integration** page (`/ci-integration`) configures pipeline behavior — quality gates, PR comments, and notifications.

**Quality Gates** — define pass/fail thresholds:
| Gate | Description | Default |
|------|-----------|---------|
| Min Pass Rate | Minimum percentage of tests that must pass | 95% |
| Max Duration | Maximum run duration in seconds (0 = unlimited) | 0 |
| Required Tags | Tags where ALL tests must pass (e.g., smoke, critical) | Empty |

Quality gate history shows last 5 results as green/red badges.

**PR Comments** — auto-post test results:
- Supports GitHub (uses `GITHUB_TOKEN`) and GitLab (uses `GITLAB_TOKEN`)
- Markdown table with pass rate, failed count, duration, quality gate status
- Optional: healing summary, visual diff summary
- Posted automatically when `--ci` flag is used

**Notifications** — push results to team channels:
| Channel | Configuration |
|---------|--------------|
| Slack | Webhook URL — sends block kit message with pass/fail, failures list, healing summary |
| Microsoft Teams | Webhook URL — sends adaptive card |
| Email | Recipient list (SMTP placeholder) |
| Custom Webhook | Any URL + headers — sends full JSON run result |

Each channel has trigger options: On every run, On failure only, On healing events.

---

## WebSocket Live Updates

The Studio uses a WebSocket connection (`ws://localhost:<port>/ws`) to receive live updates:

| Event | Description |
|-------|-------------|
| `run:start` | A run has started — UI spinner activates |
| `run:case` | A test case has started executing |
| `run:step` | A step within a case has started |
| `run:complete` | A run has finished — results available |
| `run:stopped` | A run was stopped early via the Stop button |
| `run:error` | A run failed to start |
| `recorder:action` | A new action was recorded (click, fill, navigate, assert) |
| `recorder:stopped` | Recording session ended |

The connection indicator in the top bar shows:
- **Green dot** — connected
- **Grey dot** — disconnected (Studio server not running)

---

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Ctrl+S` | Save current changes |
| `Ctrl+Enter` | Generate code for selected step |
| `Ctrl+R` | Run all tests |
| `Esc` | Close modal / cancel edit |

---

## Tips

- **Browser auto-refresh** — the Studio does not auto-refresh when test files are edited externally (e.g., by `apply-healing`). Click the refresh icon or press `F5`.
- **Multiple windows** — you can open the Studio in multiple browser tabs. All tabs receive WebSocket events simultaneously.
- **Remote access** — start with `--no-open` on the server and open `http://<server-ip>:4400` in your local browser. Make sure the port is reachable.
- **Stopping the server** — press `Ctrl+C` in the terminal where `studio` is running.
