# Agentic loop mode Heal can run inside an agentic development loop to check your work automatically. In this mode, Heal does **not** generate Playwright code. It runs validation, then exits with feedback once complete. Performance [#performance] | Metric | Heal vs `playwright-cli` + SoTA model | | ----------- | ---------------------------------------------------- | | Speed | **2x faster** | | Token usage | **2x more token-efficient** | | Accuracy | Heal handles caption, svgs, and complex interactions | Use with Claude [#use-with-claude] 1) Install Heal [#1-install-heal] ```bash npm i -g @heal-dev/heal-cli ``` 2) Login [#2-login] ```bash heal login ``` 3) Install the skill [#3-install-the-skill] ```bash npx heal skills install ``` 4) Ask Claude to validate with Heal [#4-ask-claude-to-validate-with-heal] ```bash claude "Read specs.md to create the new search function. Use heal to validate it works as described." ``` How it works [#how-it-works] Under the hood, this uses [`heal autopilot --navigation-mode`](/docs/cli/commands#heal-autopilot) through MCP. Heal drives the browser directly and validates behavior without creating a Playwright test file. # CLI Architecture CLI Architecture [#cli-architecture] This page is a placeholder for CLI architecture documentation. # Commands reference Heal CLI commands [#heal-cli-commands] The Heal CLI is the command-line entry point to **run your Playwright tests with Heal’s self-healing layer**, and to **use the Autopilot agent to generate or fix tests**. It works alongside the Heal SDK and your existing Playwright setup. Overview [#overview] * **`heal test`**: Run your Playwright tests with Heal’s self-healing runner and caching. * **`heal autopilot`**: Use the Autopilot agent to generate, fix, or refactor tests. * **`heal init`**: Scaffold a Heal-aware repository structure in your project. * **`heal login`**: Authenticate your Heal CLI and store your API token. * **`heal view`**: Open a local test report viewer Usage [#usage] ```bash heal [options] heal test [globs...] # Run tests heal autopilot [request] # Run the Autopilot agent to create/fix tests heal init # Initialize a Heal repository heal login # Authenticate and store your API token heal view [options] # Open local test report viewer ``` Global options [#global-options] These options are available on all commands: | Option | Description | | ------------ | --------------- | | `--version` | Show version. | | `-h, --help` | Show this help. | heal test [#heal-test] ```bash heal test [globs...] [options] ``` Description [#description] `heal test` runs your Playwright tests with Heal’s self-healing layer enabled. For a deeper walkthrough of running and tuning tests, see [Running tests](/docs/cli/running-tests). Common examples [#common-examples] ```bash # Run all tests heal test # Run a specific test heal test "tests/login.spec.ts" # Run tests matching a glob heal test "tests/**/*.spec.ts" # Run 4 tests in parallel heal test --concurrency 4 # Run in headless mode heal test --headless true # Retry failing tests 3 times heal test --retries 3 ``` Options [#options] | Option | Type | Default | Description | | --------------------------- | ------- | ------- | -------------------------------------------- | | `--concurrency ` | number | `1` | Parallel test count. | | `--timeout ` | number | `30000` | Per-test timeout in milliseconds. | | `--headless ` | boolean | — | Headless browser mode. | | `--retries ` | number | `2` | Retry attempts on failure. | | `--wait-before-ai ` | number | `0` | Wait before AI calls in milliseconds. | | `--wait-before-cache ` | number | `300` | Wait before cache lookup in milliseconds. | | `--smart-wait-timeout ` | number | `7000` | Smart wait before AI in milliseconds. | | `--worker-heap ` | number | — | Worker max old-gen heap size in megabytes. | | `--worker-young ` | number | — | Worker max young-gen heap size in megabytes. | heal autopilot [#heal-autopilot] ```bash heal autopilot [request] [options] ``` Description [#description-1] `heal autopilot` runs the Autopilot agent. Optionally pass the first command (for example, `heal autopilot "fix login test"`). For a full guide to how Autopilot creates tests, see [Creating tests](/docs/cli/creating-tests). For validation-only runs (no test file output), see [Agentic loop mode](/docs/cli/agentic-loop-mode). For MCP integration details, see [MCP server](/docs/cli/mcp-server). Examples [#examples] ```bash # Start autopilot in interactive mode heal autopilot # Run autopilot with a request heal autopilot "fix login test" # Run autopilot from a test case file in an agentic loop heal autopilot "implement the test from the instructions at testCase.txt" --headless true --interactive false ``` Options [#options-1] | Option | Type | Default | Description | | ---------------------- | ------- | ------- | ---------------------------------------------------- | | `--headless ` | boolean | `false` | Headless browser mode. | | `--interactive ` | boolean | `true` | If true, the autopilot asks questions interactively. | | `--navigation-mode` | boolean | `false` | Test with heal without writing a test file. | | `--max-iterations ` | number | `100` | Maximum iterations. | heal init [#heal-init] ```bash heal init ``` Description [#description-2] `heal init` initializes a Heal start repo structure for your end-to-end tests. Example [#example] ```bash heal init ``` heal login [#heal-login] ```bash heal login [options] ``` Description [#description-3] `heal login` authenticates your Heal CLI. Examples [#examples-1] ```bash npx heal login heal login ``` heal view [#heal-view] ```bash heal view [options] ``` Description [#description-4] `heal view` starts a local web server that browses Heal reports. It always uses the current working directory and looks for a `heal-report` folder there (for example, after `heal test`). Examples [#examples-2] ```bash # Default viewer on port 3000 heal view # Custom port npx heal view --port 4000 ``` Options [#options-2] | Option | Type | Default | Description | | ------------ | ------ | ------- | --------------------------------- | | `--port ` | number | `3000` | Port for the local report viewer. | See also [#see-also] * [CLI getting started](/docs/cli/getting-started) * [SDK overview](/docs/sdk) * [Commands reference](/docs/cli) *** To learn how the CLI fits into the rest of the ecosystem (SDK, platform, and more), start from the main [CLI reference](/docs/cli) and the [SDK overview](/docs/sdk). # Creating tests Creating tests with the CLI [#creating-tests-with-the-cli] Heal includes an **autopilot** that creates and fixes tests. heal autopilot [#heal-autopilot] Run the autopilot from your project root: ```bash heal autopilot ``` By default, the autopilot runs in an **interactive** mode: * It asks you questions about what you want to test (e.g. feature, page, critical path). * It may ask how to handle ambiguities or multiple possible flows. * It shows you proposed changes and can ask for confirmation before writing files. This mode is ideal when you are exploring a new area or want tighter control over what gets generated. Running autopilot without questions (--interactive false) [#running-autopilot-without-questions---interactive-false] For fully automated runs (for example, in CI or when you have a clear goal), you can add the `--interactive false` flag: ```bash heal autopilot --interactive false ``` With `--interactive false` enabled: * The autopilot **does not ask interactive questions**. * It proceeds with reasonable defaults for prompts and decisions. * It writes or updates tests directly based on its understanding of your project. Use `--interactive false` when you want to let Heal make decisions without asking for input. This is often used in fully agentic loops or in CI. Next steps [#next-steps] * Learn how to **run and tune tests** in [Running tests](/docs/cli/running-tests). * Explore all CLI commands and flags in [Commands](/docs/cli/commands). # Getting started import { Tabs, TabsList, TabsTrigger, TabsContent, } from 'fumadocs-ui/components/tabs'; Heal is a CLI-first agent that writes end-to-end **Playwright** test code. The [SDK](../sdk) is optional, and you can layer it in when your workflow needs it. The Heal CLI contains an **autopilot** that writes and fixes tests either interactively, or unsupervised (we avoid "headless" because that's already used for testing browsers). It requires the [heal-tracer](https://github.com/heal-dev/heal-playwright-tracer), an optimized statement level tracer that allows agents to precisely analyze Plwayright code. Requirements [#requirements] Before starting, make sure you have: * Node.js `22.14+` * npm * Playwright `1.58.1` Get started [#get-started] In this getting started guide, pick the path that matches how you want to try Heal: Starter repo CLI only **1. Clone the starter test repo** HTTPS SSH ```bash git clone https://github.com/heal-dev/heal-test-template.git; cd heal-test-template ``` ```bash git clone git@github.com:heal-dev/heal-test-template.git cd heal-test-template ``` **2. Install dependencies** Install dependencies (including Playwright `1.58.1` from the starter template), then run: ```bash npm install ``` **3. Authenticate with Heal** ```bash npx heal login ``` **4. Run the autopilot** ```bash npx heal autopilot ``` Make sure your project uses Playwright `1.58.1`, then run: **1. Install the Heal CLI globally** ```bash npm install -g @heal-dev/heal-cli ``` **2. Authenticate with Heal** Run: ```bash heal login ``` **3. Initialize a repo** ```bash mkdir my-project cd my-project heal init ``` `heal init` installs and configures the Heal Playwright tracer for your project. **4. Get started** ```bash heal autopilot ``` Editor extensions [#editor-extensions] To write tests in a more interactive experience, and run test code line by line, you can use Heal's Editor extensions: Next steps [#next-steps] * Running in an agent workflow? See [Agentic loop mode](/docs/cli/agentic-loop-mode). * Learn about all available commands in [Commands reference](/docs/cli/commands). # MCP server Heal MCP server [#heal-mcp-server] The `heal` MCP server exposes Autopilot over MCP so agents can run browser validation loops and test generation without shelling out to the CLI directly. Supported modes [#supported-modes] * `build`: create or edit a Playwright `.test.ts` file. * `navigate`: drive the browser to validate behavior without writing a test file (equivalent to CLI `--navigation-mode`). Tools [#tools] * `heal_autopilot_start({ mode, request })`: start a new autopilot run. * `heal_autopilot_poll({ runId })`: wait for progress updates. * `heal_autopilot_answer({ runId, ... })`: answer pending questions or security gates. * `heal_autopilot_cancel({ runId })`: cancel a running run. Suggested run loop [#suggested-run-loop] 1. Call `heal_autopilot_start({ mode, request })`. 2. While status is `running`: * If `pendingQuestion` is set, collect an answer and call `heal_autopilot_answer`. * If `pendingSecurityGate` is set, ask the user to explicitly allow or deny, then call `heal_autopilot_answer`. * Otherwise call `heal_autopilot_poll({ runId })`. 3. Stop when status is `succeeded`, `failed`, or `cancelled`. Output behavior [#output-behavior] * In `build` mode, autopilot writes or edits test files. * In `navigate` mode, no test file is written; use the final message as the validation result. See also [#see-also] * [Commands reference](/docs/cli/commands) * [Agentic loop mode](/docs/cli/agentic-loop-mode) # Running tests Running tests with Heal [#running-tests-with-heal] Heal wraps your existing Playwright tests and adds a self-healing, AI-assisted test runner on top. You run tests with: ```bash heal test [globs...] ``` This command discovers Playwright tests in your project and executes them with Heal’s runtime. Basic usage [#basic-usage] * Run all tests: ```bash heal test ``` * Run a single test file: ```bash heal test "tests/login.spec.ts" ``` * Run a subset of tests using a glob: ```bash heal test "tests/**/*.spec.ts" ``` Parallelism and performance [#parallelism-and-performance] Use `--concurrency` to run tests in parallel: ```bash heal test "tests/**/*.spec.ts" --concurrency 4 ``` Guidelines: * Tests tend to be CPU bound, so use a concurrency number that's slightly lower than your number of (v)CPUs. * Combine with `--timeout` to keep individual tests from hanging. Headless vs headed [#headless-vs-headed] By default Heal will follow your test's default, but you can override headless mode: ```bash heal test --headless true # Good for CI heal test --headless false # Good for local debugging ``` Retries and flakiness [#retries-and-flakiness] Heal can retry failing tests to reduce flakes due to website instability and auto-fix tests. ```bash heal test --retries 2 ``` In end-to-end testing, the recommendation is to use 0 retries when running tests locally, and up to 2 retries when running tests against a distant web app. We recommend using 2 retries with Heal except when debugging, because retries are also used to dynamically fix broken locators in [Heal steps](../sdk/overview). Smart waiting and AI timing [#smart-waiting-and-ai-timing] Heal introduces a few timing options to balance speed and robustness: * `--wait-before-ai ` – wait before grounding elements using AI * `--wait-before-cache ` – wait before interacting with cached elements * `--smart-wait-timeout ` – intelligently wait for relevant network requests to settle and DOM to be loaded before using AI Example: ```bash heal test --smart-wait-timeout 7000 --wait-before-ai 0 ``` Entry point and environment [#entry-point-and-environment] Override the entry URL for tests: ```bash heal test --entry-point "https://staging.example.com" ``` # Heal runners When Heal executes your tests from our cloud, traffic to your application originates from a small, stable set of IP addresses. If your app, staging environment, WAF, or VPN restricts inbound traffic, add the IPs below to your allowlist so Heal can reach it. IP allowlist [#ip-allowlist] | IP | | --------------- | | `13.37.19.74` | | `13.37.191.62` | | `13.39.7.132` | | `15.188.12.251` | | `15.236.54.162` | | `52.47.53.196` | # Secrets Secrets let you store sensitive values (API keys, passwords, tokens) in the Heal platform so they never have to be committed to your repository. Each secret is injected as an environment variable when your tests run. Adding a secret [#adding-a-secret] 1. Open your project in the Heal dashboard and go to the **Secrets** tab. 2. Click **Add secret**. 3. Enter a name (e.g. `ADMIN_PASSWORD`) and its value, then save. Secret values are write-only after creation: you can update or delete them, but they cannot be read back through the UI. Using secrets in tests [#using-secrets-in-tests] Secrets are available via `process.env` in your test code, the same as any other environment variable. ```typescript test('admin can access the dashboard', async ({ heal }) => { const { page, agent } = await heal.getPage(); await page.goto('https://app.example.com/login'); await page.fill('[name="email"]', 'admin@example.com'); await page.fill('[name="password"]', process.env.ADMIN_PASSWORD!); await agent.healClick('The sign in button'); await agent.healExpect('The admin dashboard is visible'); }); ``` Secrets are only injected during runs triggered by Heal (scheduled or manual runs from the dashboard). When running tests locally with the CLI, set the variables in your shell or a `.env` file that is **not** checked into version control. # Security We take security seriously. Learn about our practices, data handling, and compliance measures. General [#general] Do you have a trust center? [#do-you-have-a-trust-center] Yes. Visit the [Heal trust center](https://trust.heal.dev/) or contact us. Who are your data subprocessors? [#who-are-your-data-subprocessors] We work with a limited set of trusted third-party service providers to help us provide, improve, and secure our services: | Provider | Purpose | | ------------- | ---------------------------------- | | AWS | Infrastructure and hosting | | Azure Open AI | Large language models | | Clerk | Identity management | | Betterstack | Log management | | Linear | Issue tracking | | Slack | Internal communications | | PagerDuty | On-call and incident notifications | Can you permanently delete my data? [#can-you-permanently-delete-my-data] Yes, we provide mechanisms for permanent data deletion, as governed by our Terms. How can security vulnerabilities be reported? [#how-can-security-vulnerabilities-be-reported] We appreciate the work of security researchers and take all reports seriously. Security vulnerabilities can be reported confidentially to [security@heal.dev](mailto:security@heal.dev). We follow responsible disclosure practices and will: * Acknowledge receipt of your report within 24 hours * Provide an initial assessment within 72 hours * Work diligently to verify and address valid vulnerabilities * Keep you informed of our progress Do you have a bug bounty program? [#do-you-have-a-bug-bounty-program] We do not have a compensated bug bounty program yet (stay tuned!). For now, we're very happy to celebrate researchers who submit security findings publicly on social media. Researchers may report vulnerabilities using the following channels: * **Email:** [security@heal.dev](mailto:security@heal.dev) * **PGP key:** available upon request for encrypted communication How do you keep one customer’s data isolated from another’s? [#how-do-you-keep-one-customers-data-isolated-from-anothers] For our multi-tenant offering, row-level security enforces hard multitenancy. For our single-tenant offering, each customer has their own cluster, database, and (upon request) single-tenant LLM deployment. What customer data do you collect, and how is it encrypted? [#what-customer-data-do-you-collect-and-how-is-it-encrypted] We store only the artifacts required to generate, execute, and debug tests — test steps, test variables, logs, screenshots, and (optionally) API keys or seeded credentials. **At rest:** Data lives in our cloud provider’s encrypted block storage (AES-256), with AWS/GCP managed keys. **In transit:** All traffic flows over TLS 1.2/1.3 with modern cipher suites. How do you do quality assurance? [#how-do-you-do-quality-assurance] Testing is automated with Heal! We also run exploratory QA on all new features to inform what Heal should automate! Audit and Certifications [#audit-and-certifications] Do you run regular external security audits? [#do-you-run-regular-external-security-audits] Yes, we conduct regular external security audits and assessments to ensure the security and integrity of our platform. Our security program includes: * Annual penetration testing by independent third-party security firms * Quarterly vulnerability assessments * Continuous automated security scanning * Regular code reviews with security focus Summary reports of our security audits are available to enterprise customers under NDA. Is Heal.dev SOC2? [#is-healdev-soc2] Yes. Heal is SOC2 type 2 certified. Additional details can be found on the [Heal trust center](https://trust.heal.dev/). Enterprise [#enterprise] Can you provide a single-tenant offering? [#can-you-provide-a-single-tenant-offering] Yes, for enterprise customers with specific compliance or security requirements, we offer single-tenant deployment option, including single-tenant LLM services. This provides dedicated AI resources that are isolated from other customers' environments. Please contact our sales team to discuss your specific requirements and available options for single-tenant LLM solutions. Do you provide SSO/SAML? [#do-you-provide-ssosaml] Contact [support@heal.dev](mailto:support@heal.dev) if you need Single Sign-On via Security Assertion Markup Language (SAML) or other enterprise-grade authentication controls for your organization. Tests [#tests] Does Heal.dev access source code? [#does-healdev-access-source-code] Heal.dev does not access customer source code. It uses a browser to interact with your app just as a user would. The only data Heal sees is what the browser sees. Access to customer source code may occur only in limited circumstances: * When explicitly requested by the customer for troubleshooting or support * When necessary to address critical security vulnerabilities * As required to fulfill specific contracted services Any access to customer source code is: * Limited to the minimum necessary for the specific purpose * Conducted only by authorized personnel * Subject to strict access controls and logging * Handled in accordance with our confidentiality obligations Customers maintain full ownership and control of their source code at all times. # Slack notifications Installing the Heal Slack app [#installing-the-heal-slack-app] * In the Heal dashboard, open **Settings → Notifications** and click **Connect Slack**. * Slack will ask you to authorize the app. It needs permissions to read channel info, send messages, and handle slash commands. * After you authorize, Heal’s bot joins your workspace. Add it to a channel with `/invite @Heal`. * When the bot joins a channel, that channel is subscribed and Heal starts sending notifications there. * You can connect more than one workspace, and more than one channel per workspace. Filtering by noise level [#filtering-by-noise-level] Each channel subscription has a noise level that controls what Heal reports. You can change it at any time with the `/heal level ` slash command directly in the channel. | Level | What you get | | --------------------- | ----------------------------------------------------------------- | | `all_status` | A summary for every test execution, wether tests passed or failed | | `only_error_statuses` | Only runs that found Bugs and UI Changes. | | `only_bugs` | Only runs that found Bugs. | Most teams use `only_error_statuses` to be alerted of [Bugs and UI Changes](verdicts) Filtering by team and feature [#filtering-by-team-and-feature] If you have multiple teams or product areas using Heal, you can scope each channel to only receive notifications relevant to it. Use the `/heal` slash commands directly in the channel you want to configure: * `/heal team `: restrict this channel to tests belonging to a specific team. Run `/heal team clear` to remove the filter. * `/heal feature `: restrict to a specific feature. Run `/heal feature clear` to remove it. You can combine both: `/heal team backend feature payments` will only send notifications for tests in the `payments` feature owned by the `backend` team. A channel with no team or feature filter receives everything. Run `/heal help` to see the current subscription settings for a channel, or go to **Settings → Notifications** in the dashboard to manage all subscriptions in one place. # Test results import { BugVerdict, UiChangedVerdict } from '@/components/verdict-badge'; import { Screenshot } from '@/components/screenshot'; Verdicts [#verdicts] When a test run fails, Heal's agent analyzes the failure and posts a **verdict**: a structured diagnosis that explains why the run failed and drives what happens next. This allows Heal to only notify you of information that's relevant to you based on your notification settings. Verdicts: an ontology of test failures [#verdicts-an-ontology-of-test-failures] There are five verdict types: | Type | What it means | | -------------------- | -------------------------------------------------------------------------------- | | | The test caught an actual bug | | | The UI changed intentionally. It's not clear if it's intended, or if it's a bug. | | Unstable Locator | A locator in the test is flaky or broken. | | Broken Test Logic | The test code itself is wrong and needs to be fixed. | | Heal System Issue | Heal had an internal problem running the test. | In general, users configure Heal to only be notified of and . *** What happens next [#what-happens-next] When a bug is flagged, Heal doesn't expect anything. You can focus on fixing it in your app. That said, if Heal reports a that you think is irrelevant, you can give feedback by leaving a thumbs down (or using the Report flake button in the slack app) **Action needed:** Fix the bug! [#-1] When Heal detects a change in the UI, it will pause the test and ask for your input. You can either * Tell Heal that the UI change is expected, which will automatically update the test * Tell Heal that this is a bug, which will update the verdict to . **Action needed:** Tell Heal if the change is expected. Unstable Locator and Broken Test Logic [#unstable-locator-and-broken-test-logic] When an unstable locator or a broken test is detected, you don't need to do anything. Heal will improve the test on its own, and learn from its mistakes. You can still see the verdict in the UI for traceability. **Action needed:** None. Feedback [#feedback] You can submit feedback on any verdict using a thumbs-up or thumbs-down. In the slack app, you can also use the **Report flake** button. Over time, Heal learns from your preferences and gets smarter at sorting out bugs from noise. *** # Triggering Tests Tests can be triggered in two ways: manually from the Heal dashboard, or automatically from your CI pipeline using the Heal GitHub Action. Manual triggers [#manual-triggers] To run a test suite manually: 1. Open your project in the Heal dashboard and go to the **Tests** tab. 2. Select the suite you want to run. 3. Click **Run now**. The run will appear in the **Results** tab as soon as it starts. GitHub Action [#github-action] The [`heal-dev/heal-trigger`](https://github.com/heal-dev/heal-trigger) GitHub Action lets you trigger a Heal test run as part of any workflow — for example, after a deployment to staging. Setup [#setup] 1. Add your Heal API key as a secret in your GitHub repository (e.g. `HEAL_API_KEY`). See [Secrets](/docs/platform/secrets) for how to create an API key. 2. Add the action to your workflow: ```yaml - name: Run Heal tests uses: heal-dev/heal-trigger@v1 with: api-key: ${{ secrets.HEAL_API_KEY }} ``` Waiting for results [#waiting-for-results] By default the action triggers the run and exits immediately. To block the workflow until the run completes and fail the job if any tests fail, set `wait` to `true`: ```yaml - name: Run Heal tests uses: heal-dev/heal-trigger@v1 with: api-key: ${{ secrets.HEAL_API_KEY }} wait: true ``` See the [action repository](https://github.com/heal-dev/heal-trigger) for the full list of inputs and outputs. # Basic test example Good news, you generally don't have to care about this. Just ask the Heal [autopilot](../cli/creating-tests#heal-autopilot)! In case you need it, this example shows how to **extend Playwright’s `test`** with a **`heal` fixture** that creates one `Heal` instance per test, tears it down afterward, and uses `heal.getPage()` to call the heal agent. ```typescript import { test as base } from '@playwright/test'; import { Heal } from '@heal-dev/heal-cli/sdk'; type HealFixture = { heal: Heal; }; const test = base.extend({ heal: async ({}, use) => { // Initialize the Heal instance const heal = await Heal.init({ headless: true, }); await use(heal); await heal.close(); }, }); test.beforeEach(async ({ heal }) => { // per test setup if needed }); test.afterEach(async ({ heal }) => { // per test cleanup if needed }); test('my first test', async ({ heal }) => { // Get the page and agent const { page, agent } = await heal.getPage(); await agent.healClick('The login button'); }); ``` For more API detail, see the [SDK reference](/docs/sdk/reference). # Email utilities import { Tabs, TabsList, TabsTrigger, TabsContent } from 'fumadocs-ui/components/tabs'; Overview [#overview] The Heal SDK provides helpers for testing email flows without requiring an external mail service. Common examples include: sign-up confirmations, magic-link logins, password resets and email confirmations. Each organisation is assigned an email namespace on the Heal email tester domain: `*.@tester-heal.dev`. | Method | What it does | | ---------------------------------------- | --------------------------------------------------------- | | `Heal.getEmail()` | Returns a unique inbox address for this test run | | `Heal.readLastEmail(address, options?)` | Reads the most recent email at that address | | `Heal.emailLogin(address, since)` | Extracts a magic link from a login email | | `Heal.emailSignup(type, address, since)` | Extracts a confirmation code or link from a sign-up email | How it works [#how-it-works] These methods run **server-side** through heal's proxy: when the SDK is called, the request is sent to the Heal server which handles the email retrieval. All four methods poll until the email arrives, with a default timeout of **5 minutes**. Email namespace [#email-namespace] Each organisation gets a dedicated inbox namespace: ``` *.@tester-heal.dev ``` Any address of the form `anything.@tester-heal.dev` is valid and routable. You can find your org slug in the **Organisation** tab of the Heal dashboard. For example, if your slug is `acme`, then `confirm.acme@tester-heal.dev`, `reset.acme@tester-heal.dev`, or any address `Heal.getEmail()` returns will all land in inboxes your tests can read. *** Heal.getEmail() [#healgetemail] Returns a **unique disposable address** scoped to the current test run, within your organisation's namespace. This is useful for running many tests in parallel without email collisions. ```typescript static getEmail(): Promise; ``` **Example** ```typescript const email = await Heal.getEmail(); // e.g. "abc123.acme@tester-heal.dev" await page.goto('https://app.example.com/signup'); await agent.healType('the email field', email); await agent.healType('the password field', 'Test1234!'); await agent.healClick('the Sign up button'); ``` *** Heal.readLastEmail(address, options?) [#healreadlastemailaddress-options] Polls for and returns the **most recent email** delivered to the given address. Pass a `since` date (captured just before the action that triggers the email) so stale messages are ignored. ```typescript static readLastEmail( address: string, options?: { since?: Date; timeout?: number } ): Promise; ``` | Option | Type | Default | Description | | --------- | -------- | ---------------- | --------------------------------------- | | `since` | `Date` | — | Ignore emails older than this timestamp | | `timeout` | `number` | `300000` (5 min) | How long to poll before timing out (ms) | **Example** ```typescript const since = new Date(); await agent.healClick('the Send confirmation email button'); const message = await Heal.readLastEmail(emailAddress, { since, timeout: 30_000 }); const link = message.body.match(/https:\/\/\S+confirm\S+/)?.[0]; if (!link) throw new Error('Confirmation link not found'); await page.goto(link); await agent.healAssert('account is confirmed'); ``` *** Heal.emailLogin(address, since) [#healemailloginaddress-since] Polls for a login email and extracts the **magic link** from it. Shorthand for `readLastEmail` + link extraction for login flows. ```typescript static emailLogin(address: string, since: Date): Promise; ``` **Example** ```typescript const since = new Date(); await agent.healClick('the Send magic link button'); const magicLink = await Heal.emailLogin(email, since); await page.goto(magicLink); await agent.healAssert('user is logged in'); ``` *** Heal.emailSignup(type, address, since) [#healemailsignuptype-address-since] Polls for a sign-up confirmation email and extracts either a **confirmation code** or a **confirmation link** depending on `type`. ```typescript static emailSignup( type: 'confirmationCode' | 'confirmationLink', address: string, since: Date ): Promise; ``` **Example — confirmation code** ```typescript const since = new Date(); await agent.healClick('the Sign up button'); const code = await Heal.emailSignup('confirmationCode', email, since); await agent.healType('the confirmation code field', code); await agent.healClick('the Confirm button'); ``` **Example — confirmation link** ```typescript const since = new Date(); await agent.healClick('the Sign up button'); const link = await Heal.emailSignup('confirmationLink', email, since); await page.goto(link); await agent.healAssert('email is confirmed'); ``` *** Full example: parallel tests without collisions [#full-example-parallel-tests-without-collisions] Because `Heal.getEmail()` returns a unique address per test run, two tests signing up at the same time each get their own inbox without collisions. signup.test.ts signup-2.test.ts ```typescript import { Heal } from '@heal-dev/sdk'; test('user can sign up and confirm their email', async () => { const heal = await Heal.init({ headless: true }); try { const { page, agent } = await heal.getPage(); const email = await Heal.getEmail(); // e.g. "abc123.acme@tester-heal.dev" const since = new Date(); await page.goto('https://app.example.com/signup'); await agent.healType('the email field', email); await agent.healType('the password field', 'Test1234!'); await agent.healClick('the Sign up button'); await agent.healAssert('a confirmation email has been sent'); const link = await Heal.emailSignup('confirmationLink', email, since); await page.goto(link); await agent.healAssert('the email address is confirmed'); } finally { await heal.close(); } }); ``` ```typescript import { Heal } from '@heal-dev/sdk'; test('user can sign up and confirm their email', async () => { const heal = await Heal.init({ headless: true }); try { const { page, agent } = await heal.getPage(); const email = await Heal.getEmail(); // e.g. "xyz789.acme@tester-heal.dev" — different address, same test const since = new Date(); await page.goto('https://app.example.com/signup'); await agent.healType('the email field', email); await agent.healType('the password field', 'Test1234!'); await agent.healClick('the Sign up button'); await agent.healAssert('a confirmation email has been sent'); const link = await Heal.emailSignup('confirmationLink', email, since); await page.goto(link); await agent.healAssert('the email address is confirmed'); } finally { await heal.close(); } }); ``` *** Related pages [#related-pages] * **SDK overview**: [SDK overview](/docs/sdk) * **SMS utilities**: [SMS utilities](/docs/sdk/sms) * **Full API reference**: [SDK reference](/docs/sdk/reference) # Known limitations Known limitations [#known-limitations] This page lists current limitations and edge cases in the Heal SDK and Playwright integration. For usage patterns and the full API surface, see the [SDK overview](/docs/sdk) and [SDK reference](/docs/sdk/reference). Execution model and hooks [#execution-model-and-hooks] * `beforeEach` / `beforeAll` / `afterAll` hooks are not extracted into separate blocks. * `test.only()`, `test.skip()`, and `test.slow()` are not detected or handled specially. * Tag-based filtering is not currently supported. * `test.step()` is treated as regular code, without special nesting or reporting behavior. Fixtures and scope [#fixtures-and-scope] * The fixture scope option (`worker`, `module`) is not parsed. * Only `import * as blocks` imports are recognized for reusable block functions (default imports and other patterns are not supported). Blocks and helpers [#blocks-and-helpers] * Block extraction relies on the `import * as blocks from './blocks'` pattern for helper modules. * Other import styles (such as `import { loginWithCredentials } from './blocks'`) are not yet supported for automatic block handling. See also [#see-also] * **How to use the SDK in tests**: [SDK overview](/docs/sdk) * **Full method and option list**: [SDK reference](/docs/sdk/reference) # SDK overview import { Banner } from 'fumadocs-ui/components/banner'; SDK is optional. Heal's default flow is CLI-first and generates regular Playwright test code. Use AI-only runs mainly for non-deterministic workflows where deterministic Playwright steps are not practical. What the Heal SDK is [#what-the-heal-sdk-is] Heal is an SDK that allows you to write **stable end-to-end tests** using natural language on top of Playwright. Because Heal runs your tests through Playwright, you retain the full power of the Playwright engine while gaining a higher-level, intent-based API for interactions and assertions. Drop‑in replacement for Playwright actions [#dropin-replacement-for-playwright-actions] Heal SDK calls are designed to be a **drop‑in replacement for many playwright actions**. Instead of using locators, you describe the element's intent. ```ts // Finding the row with "John Doe" and clicking the Edit button inside it const userRow = page.locator('tr', { hasText: 'John Doe' }); await userRow.getByRole('button', { name: 'Edit' }).click(); // Filling a specific field in a modal that just appeared await page.getByLabel('Job Title').fill('Senior Engineer'); await page.getByRole('button', { name: 'Save Changes' }).click(); ``` with Heal you can instead write: ```ts // Heal understands the visual and semantic context automatically await agent.healClick('the Edit button for John Doe'); // Heal can handle descriptive input targets await agent.healType( 'the Job Title field in the edit modal', 'Senior Engineer', ); await agent.healClick('the Save button'); ``` The rest of your test structure — fixtures, configuration, test runner setup — can remain the same. Caching [#caching] Under the hood, heal grounds elements and dynamically find stable locators to cache. LLMs are called only when locators break, which makes Heal more stable, faster and token-efficient Mix and match [#mix-and-match] You can mix and match playwright and heal calls. Heal gives you accept to the playwright `page` object, as well as an `agent` object to call heal methods. ```typescript const { page, agent } = await heal.getPage(); // A Heal call await healAgent.leftClick( 'The contact doctor button in the first card on the left', ); // A playwright expect await expect(page).toHaveURL(/doctor/); ``` This means you can: * **Adopt Heal gradually** in specific flows or files. * Keep using **existing Playwright helpers, fixtures, and plugins**. * Reach for **raw Playwright** whenever you need low-level control. For full API details and supported patterns, see the [`SDK reference`](/docs/sdk/reference). Next steps [#next-steps] * **Deep dive into the API surface** in the [SDK reference](/docs/sdk/reference). * **Review edge cases and gaps** in [Known limitations](/docs/sdk/known-limitations). # SDK reference import { Banner } from 'fumadocs-ui/components/banner'; SDK reference [#sdk-reference] SDK is optional. Heal's default flow is CLI-first and generates regular Playwright test code. Use AI-only runs mainly for non-deterministic workflows where deterministic Playwright steps are not practical. Heal.init(options?) [#healinitoptions] Creates a **`Heal`** instance: launch a browser session, then returns a handle you use for `getPage()`, etc. Call **once per test** (or once per fixture scope), then **`close()`** when done. ```typescript static init(options?: HealOptions): Promise; ``` **`HealOptions`** (all optional): Creates a new Heal instance with an auto-healing browser context. | Option | Type | Description | | ------------------ | ------------------------ | ------------------------------------------- | | `headless` | `boolean` | Run browser in headless mode | | `extensionPaths` | `string[]` | Browser extension paths to load | | `locale` | `string` | Browser locale | | `extraHttpHeaders` | `Record` | Extra HTTP headers for all requests | | `storageStatePath` | `string` | Path to storage state JSON for auth/cookies | Heal instance methods [#heal-instance-methods] * **`getPage()`**: Returns `{ page, agent }` with Heal's self-healing proxy. * **`newPage()`**: Opens a new page in the browser context. * **`getBrowserContext()`**: Returns the underlying Playwright `BrowserContext`. * **`waitForNewPage(action, timeoutMS?)`**: Waits for a new page to open as a result of an action. * **`close()`**: Closes the browser and cleans up resources. getPage() [#getpage] Returns `{ page, agent }` for Heal’s **first** browser tab. Call heal actions on `agent`, and optionally call `page` for regular playwright. Calling `getPage()` multiple times returns the **same** page and agent—use this at the start of a test when a single tab is enough. ```typescript getPage(): Promise; ``` **Example** ```typescript const { page, agent } = await heal.getPage(); await page.goto(entryUrl); await agent.healClick('the login button'); ``` *** newPage() [#newpage] Opens a **new tab** in the same Playwright `BrowserContext` and returns a fresh `{ page, agent }` pair. Use when you need an extra tab without closing the first (e.g. parallel UI or a clean navigation stack). ```typescript newPage(): Promise; ``` **Example** ```typescript const { page: tab1, agent: agent1 } = await heal.getPage(); const { page: tab2, agent: agent2 } = await heal.newPage(); await tab2.goto('https://example.com'); await agent2.healClick('submit'); ``` *** getBrowserContext() [#getbrowsercontext] Returns the underlying Playwright **`BrowserContext`**. Use when you need low-level APIs (cookies, permissions, multiple pages without Heal’s agent, etc.). Prefer `getPage()` / `newPage()` / `waitForNewPage()` for normal Heal flows. ```typescript getBrowserContext(): BrowserContext; ``` **Example** ```typescript const context = heal.getBrowserContext(); const pages = context.pages(); ``` *** waitForNewPage(action, timeoutMS?) [#waitfornewpageaction-timeoutms] Runs an **async action** (typically one that opens a link in a new tab) and waits until a **new** page is created, then returns `{ page, agent }` for that new tab. Use when the **application** opens the tab (e.g. “Download” or “Open in new window”), not when you want to open a tab yourself—in that case use `newPage()`. ```typescript waitForNewPage(action: Promise, timeoutMS?: number): Promise; ``` **Example** ```typescript const { page: pdfTab, agent: pdfAgent } = await heal.waitForNewPage( agent.healClick('Télécharger la fiche patient'), ); await pdfAgent.healAssert('PDF viewer is visible'); await pdfTab.close(); ``` *** close() [#close] Closes the browser context and releases resources. Call **once** at the end of each test (e.g. in a fixture `after` hook or after the test body) so sessions do not leak. ```typescript close(): Promise; ``` **Example** ```typescript const heal = await Heal.init({ headless: true, locale: 'fr-FR' }); try { const { page, agent } = await heal.getPage(); // … test steps … } finally { await heal.close(); } ``` Browser interactions [#browser-interactions] Browser interactions should be called on an `HealPageAgent`, as returned by `getPage` or `newPage`. * **`agent.healClick(description)`**: Click an element described in natural language. * **`agent.healType(description, value)`**: Type into an element described in natural language. * **`agent.healSelectOption(description, option)`**: Select an option from a dropdown described in natural language. * **`agent.healHover(description)`**: Hover over an element described in natural language. * **`agent.healLocator(description)`**: Returns a Playwright `Locator` for an element described in natural language. * **`agent.healAssert(assertion)`**: Assert a condition on the page described in natural language. * **`agent.healScreenshot()`**: Takes a screenshot of the current page and returns it as a base64 string. Supported Playwright features [#supported-playwright-features] Configuration (playwright.config.ts) [#configuration-playwrightconfigts] * **`testDir`**: root test directory * **`projects`**: array of project configurations with `name`, `testMatch`, `testDir`, and `dependencies` * Project dependency graph with cycle detection and topological ordering Test patterns [#test-patterns] * `test()` and `it()` declarations * `test.describe()` with nesting * `test.afterEach()` (scope-aware) * Custom fixtures via `base.extend()` — function-style, array-style with `{ auto: true }`, and `{ option: true }` * Fixture teardown via `use()` boundary detection * Fixture dependency resolution and topological sorting * `test.use()` overrides (scope-aware within `describe` blocks) * Built-in fixtures auto-provided when needed: `page`, `context`, `browser`, `request` Test file extensions [#test-file-extensions] `*.spec.ts`, `*.spec.js`, `*.test.ts`, `*.test.js`, `*.spec.tsx`, `*.spec.jsx`, `*.test.tsx`, `*.test.jsx` Code patterns [#code-patterns] * Control flow blocks (`if`/`else`, `for`, `while`, `try`/`catch`, `switch`) treated as atomic executable units * File-level and `describe`-block-level variable declarations * Import statements preserved for execution context Blocks (reusable helper functions) [#blocks-reusable-helper-functions] * `import * as blocks from './blocks'` pattern * Automatic inlining of `await blocks.functionName(...)` calls * Parameter binding, return value capture, and setup/teardown support Related pages [#related-pages] * **Overview and examples**: [SDK overview](/docs/sdk) * **Edge cases and gaps**: [Known limitations](/docs/sdk/known-limitations) # SMS utilities Overview [#overview] The Heal SDK lets tests read incoming SMS messages, typically OTP codes or verification messages sent during login and signup flows. Phone numbers are assigned to your organisation on request. Contact the Heal team to get a number provisioned. | Method | What it does | | ----------------------------------- | ----------------------------------------------------------------- | | `Heal.readSMS(phoneNumber, since?)` | Returns the plain-text body of the most recent SMS to that number | How it works [#how-it-works] `readSMS` runs **server-side** through Heal's proxy. It polls until a message arrives, with a default timeout of **2 minutes**. Heal deletes the message after reading so it doesn't bleed into subsequent test runs. *** Heal.readSMS(phoneNumber, since?) [#healreadsmsphonenumber-since] Polls for and returns the plain-text body of the most recent SMS delivered to the given number. Pass a `since` date captured just before the action that triggers the SMS so stale messages are ignored. ```typescript static readSMS(phoneNumber: string, since?: Date): Promise; ``` | Parameter | Type | Description | | ------------- | -------- | ----------------------------------------------- | | `phoneNumber` | `string` | The number to read from (e.g. `'+14155552671'`) | | `since` | `Date` | Ignore messages older than this timestamp | **Example** ```typescript const since = new Date(); await agent.healClick('the Send code button'); const code = await Heal.readSMS('+14155552671', since); await agent.healType('the verification code field', code); await agent.healClick('the Verify button'); ``` *** Full example: SMS verification flow [#full-example-sms-verification-flow] ```typescript import { Heal } from '@heal-dev/sdk'; test('user can verify their phone number', async () => { const heal = await Heal.init({ headless: true }); try { const { page, agent } = await heal.getPage(); await page.goto('https://app.example.com/verify-phone'); await agent.healType('the phone number field', '+14155552671'); const since = new Date(); await agent.healClick('the Send code button'); const code = await Heal.readSMS('+14155552671', since); await agent.healType('the verification code field', code); await agent.healClick('the Verify button'); await agent.healAssert('phone number is verified'); } finally { await heal.close(); } }); ``` *** Related pages [#related-pages] * **SDK overview**: [SDK overview](/docs/sdk) * **Email utilities**: [Email utilities](/docs/sdk/email) * **Full API reference**: [SDK reference](/docs/sdk/reference)