Heal
Concepts

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:

  1. Parses your repository — Finds test files and, if present, your playwright.config.ts (or .js).
  2. Builds an execution graph — Uses Playwright projects and dependencies to decide which tests belong to which project and in what order they can run.
  3. 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).
  4. 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 the headless option in Heal.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 when runInfo.forceRealCache is 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:
    • name
    • testMatch (and/or testDir) — 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 like heal-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

ConcernWhere it’s definedWhat Heal does with it
Which tests run / execution orderPlaywright 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 behaviorHeal.init() options in test codeParses and preserves them; merges with CLI overrides and injects runInfo when rewriting Heal.init() for the run.
Overrides at run timeCLI 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/reportInjected 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 pathsHeal SDK defaults / run contextUses .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.