Skip to content

Add visual regression assertions - #174

Merged
smnandre merged 6 commits into
playwright-php:mainfrom
smnandre:feat/visual-regression
Nov 7, 2026
Merged

smnandre merged 6 commits into
playwright-php:mainfrom
smnandre:feat/visual-regression

Conversation

@smnandre

@smnandre smnandre commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Add toHaveScreenshot() for pages and locators, with PNG and lossless WebP baselines, update modes, masks, and configurable tolerances. Save expected, actual, and diff images when a comparison fails.

The assertion uses Playwright’s private _expectScreenshot() API for screenshot stabilization and comparison.

@codecov

codecov Bot commented Sep 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@smnandre
smnandre requested a balanced review from Copilot September 26, 2026 22:24
@smnandre smnandre added the enhancement New feature or request label Sep 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Empty baselines can pass incorrectly, stale artifacts can persist, and several path, locator, and portability cases remain broken.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity · 1 Low severity

Open (5)
What changed in this PR

Adds visual regression assertions for pages and locators, backed by Playwright’s screenshot comparator.

Changes:

  • Adds PNG/WebP screenshot comparison, update modes, masks, tolerances, and failure artifacts.
  • Integrates assertions with PHPUnit tracing, snapshot paths, and browser/platform variants.
  • Adds documentation and unit/functional coverage.
File Description
bin/​lib/​handlers.js Bridges screenshot comparisons to Playwright.
src/​Assertions/​Internal/​AbstractAssertions.php Supports non-polled assertions.
src/​Assertions/​Internal/​ScreenshotExpectation.php Manages baselines and artifacts.
src/​Assertions/​LocatorAssertions.php Adds locator screenshot assertions.
src/​Assertions/​LocatorAssertionsInterface.php Exposes the locator API.
src/​Assertions/​Options/​ToHaveScreenshotOptions.php Defines and validates options.
src/​Assertions/​PageAssertions.php Adds page screenshot assertions.
src/​Assertions/​PageAssertionsInterface.php Exposes the page API.
src/​Locator/​Locator.php Sends locator comparison commands.
src/​Page/​Page.php Sends page comparison commands.
src/​Screenshot/​ScreenshotComparison.php Models comparison results.
src/​Testing/​Expect.php Resolves snapshot paths and variants.
src/​Testing/​ExpectDecorator.php Counts screenshot assertions.
src/​Testing/​ExpectInterface.php Exposes the testing API.
src/​Testing/​PlaywrightTestCaseTrait.php Configures baseline and output directories.
tests/​Functional/​Screenshot/​ToHaveScreenshotTest.php Exercises browser-level behavior.
tests/​Unit/​Assertions/​Internal/​ScreenshotExpectationTest.php Tests baseline lifecycle.
tests/​Unit/​Assertions/​LocatorAssertionsTest.php Tests locator integration.
tests/​Unit/​Assertions/​Options/​ToHaveScreenshotOptionsTest.php Tests option validation.
tests/​Unit/​Assertions/​PageAssertionsTest.php Tests page integration.
tests/​Unit/​Locator/​LocatorTest.php Tests locator transport behavior.
tests/​Unit/​Page/​PageTest.php Tests page transport behavior.
tests/​Unit/​Screenshot/​ScreenshotComparisonTest.php Tests response decoding.
tests/​Unit/​Testing/​ExpectFactoryTest.php Tests paths and PHPUnit integration.
docs/​guide/​assertions-reference.md Lists the new assertions.
docs/​guide/​visual-regression.md Documents visual regression workflows.
CHANGELOG.md Announces the feature.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Testing/Expect.php Resolved
Comment thread src/Testing/Expect.php Resolved
Comment thread tests/Unit/Locator/LocatorTest.php Resolved
@kbond
kbond force-pushed the feat/visual-regression branch from ea7f5c0 to 0722baa Compare November 7, 2026 12:32

kbond commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Rebased onto main, keeping the existing page tests and the Unreleased changelog entry. The transport now respects long or unlimited screenshot assertion timeouts, and malformed comparison responses or timeouts can no longer pass as matches.

The focused Chromium screenshot tests passed on Playwright 1.62.1 and 1.64.1.

@smnandre

smnandre commented Nov 7, 2026

Copy link
Copy Markdown
Member Author

Thak you so much @kbond ! 🙇

smnandre and others added 6 commits November 7, 2026 16:40
The bridge gains an expectScreenshot command for pages and locators. It
calls page._expectScreenshot(), the private method behind
@playwright/test's toHaveScreenshot(): screenshots until two consecutive
ones match, masks, then pixelmatch or ssim-cie94. The bridge applies the
defaults the JS runner adds on its side (animations disabled, caret
hidden, CSS scale). Images travel base64 encoded, masks as selectors
rebuilt into locators, frames included.

Page::expectScreenshot() and Locator::expectScreenshot() expose it as
@internal methods returning a ScreenshotComparison. PageInterface and
LocatorInterface are left untouched: downstream test doubles implement
them.
toHaveScreenshot() on page and locator assertions, and on expect() in
test cases, compares a stable screenshot with a baseline image, as in
@playwright/test:

- Baselines live next to the test file, <Test>.php-snapshots/, named
  after the browser of the page under test and the platform:
  card-chromium-linux.png. PNG by default, lossless WebP with .webp.
- PLAYWRIGHT_UPDATE_SNAPSHOTS takes the --update-snapshots values: none,
  missing (default), changed, all. "all" records without comparing.
- A failure writes card-expected, card-actual and card-diff to
  test-failures/<TestClass>-<testMethod>-<browser>/, removed once the
  baseline matches or is recorded again.
- ToHaveScreenshotOptions carries the @playwright/test defaults and
  rejects invalid values on construction.
A guide on why and where to compare screenshots (a defensive test on the
design system and precious pages), how baselines are named, updated and
reviewed, failure images, PNG and WebP, options, headless mode and limits.
- An empty baseline reached Playwright as "no baseline" and matched any
  screenshot. It now fails with a clear message, and is recorded again in
  the changed and all modes. The bridge sends any string it receives.
- A failure removes the images of the previous one, so a diff from an
  earlier mismatch never sits next to a later timeout.
- Locator::normalize() keeps the page of the locator, so a normalized
  locator names its baselines after the browser too.
- UNC paths (\\server\share\card.png) are absolute snapshot paths.
- The unreadable baseline test skips without POSIX permissions.
- The useItemsLocator() docblock is back on its helper.
@smnandre
smnandre force-pushed the feat/visual-regression branch from 2ec0be9 to b81aab4 Compare November 7, 2026 15:44
@smnandre
smnandre merged commit a1b79b3 into playwright-php:main Nov 7, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants