How Heal works
How Heal runs tests, uses its own config, and works with Playwright config
How Heal works
Heal runs your Playwright tests in a Heal-aware way: it discovers tests, respects your Playwright project layout and dependencies, injects runtime context into the Heal SDK, and can override Heal options from the CLI. This page explains how that works and how Heal config and Playwright config fit together.
Overview
When you run heal test, the Heal CLI does not simply call npx playwright test. It:
- Parses your repository — Finds test files and, if present, your
playwright.config.ts(or.js). - Builds an execution graph — Uses Playwright projects and dependencies to decide which tests belong to which project and in what order they can run.
- Runs each test in a worker — Executes test code in a controlled environment, rewriting
Heal.init()in fixtures to inject run info (execution id, run id, retry attempt) and optional CLI overrides (e.g.--headless,--wait-before-each). - Uses the Heal SDK at runtime — The SDK receives the merged options (your code + CLI + run info) and uses them for browser launch, cache/report paths, and behavior (headless, wait after each method, etc.).
So: Playwright config drives which tests run and in what order (projects/dependencies). Heal config (options in Heal.init() plus CLI overrides and injected runInfo) drives how the Heal SDK behaves during each run.
Heal’s own config
Heal’s behavior is configured in two places: inside your test code and on the CLI.
1. Options in your test code: Heal.init()
In your test files you call Heal.init() (usually inside a fixture) and pass options. These are Heal-specific and are not part of Playwright’s config.
Common options include:
headless— Run the browser headless or headed.waitAfterEachMethod(or similar) — Wait time in ms after each Heal SDK method (e.g.heal.click(),heal.type()).extensionPaths— Paths to browser extensions to load.storageStatePath— Path to a Playwright storage state file (e.g. for auth).locale— Browser locale.
Example:
const heal = await Heal.init({
headless: false,
waitAfterEachMethod: 2_000,
});The CLI parses these options from your code and can override some of them at run time (see below). Options that are not overridden (e.g. extensionPaths) are preserved as-is when the CLI rewrites the Heal.init() call.
2. Injected run context: runInfo
When you run heal test, the CLI does not run raw Playwright. It runs your test code in a worker and rewrites Heal.init() in fixture code to inject a runInfo object. That object typically includes:
id— Unique id for the whole execution (all tests in this run).runId— Unique id for this specific test run.attemptNumber— Retry attempt (e.g. 1, 2, 3).forceRealCache— Whether to skip temp cache and write to.heal-cache(from--force-real-cache).ccaEnabled— Whether to capture “check correct action” data for cached selectors.
The Heal SDK uses runInfo to tie this run to the right report and cache paths and to enable/disable features (e.g. real cache vs isolated cache). So Heal’s “config” for a run = your Heal.init() options + CLI overrides + this injected runInfo. There is no separate “Heal config file”; the config is in code and CLI flags.
3. CLI overrides
You can override some Heal options from the command line without changing your test files:
--headless true|false— Overrides theheadlessoption inHeal.init().--wait-before-each <ms>— Overrides the wait-after-each-method option.
CLI values take precedence over the options in your code. Other options (e.g. extensionPaths, storageStatePath) are left unchanged. See CLI reference for details.
4. Heal-specific directories
Heal uses directories that are not defined in Playwright config:
.heal-cache— Default cache directory for selector and step cache (used whenrunInfo.forceRealCacheis set or when the SDK runs with run info).heal-report— Default directory for Heal report outputs (e.g. HTML report path shown after a run).
These are part of Heal’s runtime behavior, not Playwright’s.
Playwright config: how Heal uses it
Heal optionally reads your existing Playwright config to drive test discovery and execution order. It does not start the Playwright test runner; it uses the config only to build an internal graph of what to run and in which order.
When a config file exists
Heal looks for playwright.config.ts or playwright.config.js in the workspace root. If found, it parses the config (using the same structure you use with defineConfig) and extracts:
testDir— Root directory for tests (if set).projects— Each project’s:nametestMatch(and/ortestDir) — Used to match test files to this project.dependencies— List of other project names that must run successfully before this project runs.
From this, Heal builds a dependency graph: nodes are projects (or a virtual “unmatched” node), and edges are dependencies. It then runs project nodes in an order that respects dependencies (e.g. “setup” project first, then “chromium”, etc.). Tests that don’t match any project are grouped into a virtual node and run without project ordering.
So:
- Playwright config → defines projects and dependencies.
- Heal → uses that only to decide which tests to run and in what order (which project/node first). Timeouts, reporters, workers, and other Playwright runner settings in the config are not used by Heal’s own runner; Heal has its own concurrency, timeout, and reporting (e.g.
--concurrency,--timeout, and report paths likeheal-report).
When no config file exists
If there is no playwright.config.ts (or .js), Heal does not use projects or dependencies. It discovers test files with a default pattern (e.g. **/*.{spec,test,setup}.{ts,js,tsx,jsx}) and runs them in a single “unmatched” group. So you can use Heal without any Playwright config; adding a config then enables project-based ordering and dependency between projects.
How it fits together
| Concern | Where it’s defined | What Heal does with it |
|---|---|---|
| Which tests run / execution order | Playwright config (testDir, projects, dependencies) | Parses config, builds dependency graph, runs tests in that order (or runs all tests in one group if no config). |
| Browser and Heal behavior | Heal.init() options in test code | Parses and preserves them; merges with CLI overrides and injects runInfo when rewriting Heal.init() for the run. |
| Overrides at run time | CLI flags (--headless, --wait-before-each, etc.) | Merged into the options passed to Heal.init(); CLI wins over code for those keys. |
| Run identity and cache/report | Injected by CLI (runInfo) | Injected into Heal.init() so the SDK can use the right execution id, run id, and cache/report paths. |
| Cache and report paths | Heal SDK defaults / run context | Uses .heal-cache and heal-report (or overrides) based on runInfo and options. |
So: Playwright config = test layout and project dependencies. Heal config = everything that controls the Heal SDK and runtime (options in code + CLI overrides + injected runInfo). The two work together: Playwright config tells Heal what to run and in what order; Heal config tells the SDK how to behave for each run.