This is the agent entry point for the WebdriverIO visual-testing monorepo. Humans should start with CONTRIBUTING.md. Agents should read this file first, then the skill that matches the task.
Do not copy policy into tool-specific files. Cursor, Claude Code, Copilot, and Codex should follow this document. Tool adapters (for example CLAUDE.md) stay thin and point here.
pnpm workspace. Source lives in packages/*/src. Other packages, the e2e tests
and the published packages use the compiled packages/*/dist.
packages/image-comparison-core screenshots, stitching and comparison engine (pixelmatch)
packages/visual-service @wdio/visual-service: save*/check* commands, matchers, Storybook runner
packages/ocr-service @wdio/ocr-service: OCR commands (tesseract.js or a system tesseract)
packages/visual-reporter @wdio/visual-reporter: Remix app + CLI that shows compare results
tests/specs e2e specs (WebdriverIO)
tests/configs wdio configs: local browsers, local Appium, LambdaTest, Sauce Labs
tests/fixtures local pages for the v10 specs
tests/lambdaTestBaseline committed baselines of the LambdaTest runs
tests/sauceLabsBaseline committed baselines of the Sauce Labs runs
apps/ mobile apps for the app tests
patches/ pnpm patches (see pnpm-workspace.yaml)
.changeset/ changesets (`main` releases v11 with the npm tag `latest`)
.github/workflows checks, e2e, scheduled-tests, release, ...
Use the Node version in .nvmrc (currently 24) and the pnpm version
pinned in package.json#packageManager (pnpm 11, through Corepack). Do not
switch the package manager.
corepack enable
pnpm install
pnpm build # compile all packages to packages/*/distCloud / Codespaces agents: run .agents/setup once, then
.agents/resume to confirm the toolchain before editing.
Edits to src/ are invisible to the e2e tests and to the other packages until
the build runs again: pnpm build, or pnpm watch while you work.
pnpm 11 refuses packages that are less than 1 day old (minimumReleaseAge).
Do not add an exception unless a release is blocked, and remove it when it is
not needed any more.
| Path | Owner |
|---|---|
packages/*/dist |
pnpm build |
packages/*/CHANGELOG.md |
the release process (changesets) |
.changeset/pre/, .changeset/pre.json |
the release process |
pnpm-lock.yaml |
pnpm install |
tests/lambdaTestBaseline/**, tests/sauceLabsBaseline/** |
CI runs, see visual-testing-baselines |
**/__snapshots__/** |
vitest -u, after you checked the new output |
Prefer the smallest proof that covers the change. pnpm test runs lint, types
and all unit tests; use it before you push, not as the first check.
The table is the regression check. It is not proof that a feature works the way a user runs it. Before you report a feature or bug fix as done, follow verify-visual-testing.
| Change | Minimum local proof |
|---|---|
| One file in a package | pnpm exec vitest run <path>.test.ts |
| Types or public options | the unit tests and pnpm run test:types |
| Style | pnpm run test:lint |
Web commands, screenshots, compare logic (image-comparison-core, visual-service) |
pnpm build, then the local Chrome suites: pnpm test.local.chrome.v10, pnpm test.local.chrome.v10.jasmine, pnpm test.local.chrome.v10.emulation, pnpm test.local.desktop.multi |
Desktop specs (tests/specs/basics, desktop*, matcher, folders) |
BASELINE_SETUP=true pnpm test.local.desktop, then pnpm test.local.desktop |
OCR (ocr-service) |
the unit tests and pnpm test.ocr.local.desktop (a local page in tests/fixtures/ocr with a committed font, also runs in CI) |
| Mobile (Appium, native app or mobile web) | a local emulator or simulator suite (test.local.emus.*, test.local.sims.*), or a cloud run |
| A failure only in CI on Linux | the Docker recipe in verify-visual-testing |
| Cloud configs or cloud baselines | a cloud run, see verify-visual-testing |
| Workflows, root tooling | pnpm test and the CI run of the PR |
Unit tests live next to the source (src/**/*.test.ts) in
image-comparison-core, and in tests/ in visual-service and ocr-service.
Mock the browser in unit tests; do not start a browser.
checks: every PR (also from forks) and every push tomainand to the maintenance branches. Lint, types and unit tests; the local headless Chrome suites (v10 Mocha, Jasmine, multi-remote, emulation) and the local desktop suite (setup run, then the real run).android emulator: the Android mobile web suite on an emulator in the runner (Appium, same-run baselines). It runs for PRs (also from forks) that change the core, the service, the mobile web spec or its configs. Locally: start an emulator and Appium, thenANDROID_AVD=<avd> ANDROID_PLATFORM_VERSION=<version> pnpm test.local.emus.web(see .github/scripts/android-emulator-e2e.sh).ios simulator: the same for iOS, on a new simulator in a macOS runner. Locally: start Appium with the XCUITest driver, thenIOS_DEVICE_NAME="<simulator>" IOS_PLATFORM_VERSION=<version> pnpm test.local.sims.web(see .github/scripts/ios-simulator-e2e.sh). Use a simulator that you made for tests, not one that you use for other work.e2e: the LambdaTest and Sauce Labs jobs. They need the cloud credentials, so they run only for branches in this repository, not for forks or Dependabot.scheduled-tests: the same cloud jobs, on a schedule or by hand. For a PR from a fork, set thebranchinput torefs/pull/<number>/head— only after a maintainer reviewed and trusts the exact commit: the run gives the cloud credentials to that code. Never start it for a fork without that review.release: manual, see visual-testing-release.
- The local suites make their baselines in the same run. Never commit
localBaseline/or.tmp/. - The cloud runs compare with committed baselines. LambdaTest turns off
autoSaveBaselinein CI, so a missing baseline fails. Sauce Labs leaves it on, so a new file name can pass by saving a baseline without a comparison. - Add or update cloud baselines only from CI runs, and look at each image before the commit. See visual-testing-baselines.
- Every change that users of a package can see needs a changeset
(
pnpm changeset). Changes to tests, CI or docs of this repository only do not need one. - Open PRs against
main. A backport tov10orv9is a separate PR with its own changeset. See CONTRIBUTING.md and visual-testing-release.
- Inspect
git status -sbbefore editing. Do not switch branches or rewrite history that another process is using. - Keep PRs to one topic. Conventional Commits (
fix: ...,feat: ...,test: ...,ci: ...,chore: ...). - Stage only intended files. Do not commit
node_modules,dist,.tmp,localBaselineor coverage output. - Tests must fail on the original defect: check that the new test fails without the fix. Do not hide flakes with retries, longer timeouts, or weaker assertions — fix the cause.
- Do not add TypeScript
ascasts. Use type guards,satisfies, annotations or typed helpers. In unit tests,mock<T>()fromvitest-mock-extendedis fine. - Verify user-facing work with verify-visual-testing before claiming it is done. Record the command, the output, and the observable result.
- American English. Match existing code style; do not reformat unrelated files.
- Contributor / GitHub process, branches, releases: CONTRIBUTING.md
- Service options and commands: packages/visual-service/README.md
- OCR service: packages/ocr-service/README.md
- Verify a change: .agents/skills/verify-visual-testing/SKILL.md
- Baselines: .agents/skills/visual-testing-baselines/SKILL.md
- Release and backports: .agents/skills/visual-testing-release/SKILL.md
- v11 migration guide: docs/v11-migration.md. When a change is noticeable for users of the packages, update it in the same PR.
The skills set metadata.internal: true because they are for this repository
only. Leave that flag on.