# Pytest Plugin for Visual Testing with Playwright
As of 2025-03-22 of all of the existing packages to do simple visual regression testing using playwright are long dead. I had a bunch of updates I wanted to make to existing systems so I rewrote the plugin with a bunch of updates:
- snapshots are always written on CI to easily download them as artifacts
- ability to mask out certain elements which cause comparison brittleness
- failing on `--update-snapshots` to make users manually review images
- snapshot name is optional, `test_name[browser][os].png` is auto-generated by default
- multiple snapshots in a single test, file names are auto incremented
- updated folder structure: `snapshots/file_name/test_name/test_name[browser][os].png`
- ability to configure directories, etc via ini + pytest config.
You can see this implemented in a [working project here](https://github.com/iloveitaly/python-starter-template/).
## Installation
```bash
pip install pytest-playwright-visual-snapshot
```
## Usage
This plugin provides a `assert_snapshot` fixture which is used to create snapshots and compare it.
```python
def test_myapp(page, assert_snapshot):
page.goto("https://example.com")
assert_snapshot(page)
```
Then, run pytest:
```bash
pytest
```
The first time you run pytest, snapshots will be created, and you will get the error:
```console
New snapshot(s) created. Please review images
```
The next run, the snapshots comparison will take place. To update snapshots, run:
```bash
pytest --update-snapshots
```
After updating, tests will fail and you will need to review images.
In case of a mismatch, `snapshot_tests_failures` folder will be created with `actual_..`, `expected_..` and `diff_..` images generated.
## Configuration
View all configuration options by running `pytest --help`. Here's a quick example:
```python
# NOTE this runs on any pytest invocation, even if no tests are run
def pytest_configure(config: Config):
config.option.playwright_visual_snapshots_path = Path("...")
config.option.playwright_visual_snapshot_failures_path = Path("...")
```
### Masking Elements
You can mask certain elements during screenshot capture to prevent them from causing comparison failures. This is useful for dynamic content like timestamps, user avatars, etc.
Configure global masks in your pytest.ini:
```ini
[pytest]
playwright_visual_snapshot_masks =
[data-clerk-component="UserButton"]
.timestamp
#dynamic-content
```
Or directly via `pytest_configure`:
```python
def pytest_configure(config: Config):
config.option.playwright_visual_snapshot_masks = [
'[data-clerk-component="UserButton"]',
'.timestamp',
'#dynamic-content'
]
```
Or specify masks directly in your test:
```python
def test_with_custom_masks(page, assert_snapshot):
page.goto("https://example.com")
assert_snapshot(page, mask_elements=[".user-avatar", "#timestamp"])
```
### GitHub Actions Script
The CI Chrome will be slightly different than your dev chrome. You'll want to pull down screenshots from your CI run and use those for comparison. Here's a script to do that:
```shell
failed_run_id=$(gh run list --status=failure --workflow=workflow_name.yml --json databaseId --jq '.[0].databaseId')
PLAYWRIGHT_RESULT_DIRECTORY=
rm -rf ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id} && \
mkdir -p ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id} && \
gh run --dir ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id} download $failed_run_id && \
cp -R ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id}/test-results/${PLAYWRIGHT_VISUAL_SNAPSHOT_DIRECTORY}/ ${PLAYWRIGHT_VISUAL_SNAPSHOT_DIRECTORY}/
```
## API
- `threshold` - sets the threshold for the comparison of the screenshots:`0` to `1`. Default is `0.1`
<!-- - `name` - `.png` extensions only. Default is `test_name[browser][os].png` (recommended) -->
- `fail_fast` - If `True`, will fail after first different pixel. `False` by default
- `mask_elements` - List of CSS selectors to mask during screenshot capture. These will be combined with any globally configured masks.
## Alternatives
- https://github.com/kumaraditya303/pytest-playwright-snapshot - long dead
- https://github.com/symon-storozhenko/pytest-playwright-visual - fork of the above repo, long dead
- https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker - requires a separate server to run
Raw data
{
"_id": null,
"home_page": null,
"name": "pytest-playwright-visual-snapshot",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "playwright, pytest, regression, testing, visual",
"author": null,
"author_email": "Michael Bianco <mike@mikebian.co>",
"download_url": "https://files.pythonhosted.org/packages/58/17/30950fbf944dd7cfda511e29483addb8e9ae42acecd803c85b5952b258dc/pytest_playwright_visual_snapshot-0.2.4.tar.gz",
"platform": null,
"description": "# Pytest Plugin for Visual Testing with Playwright\n\nAs of 2025-03-22 of all of the existing packages to do simple visual regression testing using playwright are long dead. I had a bunch of updates I wanted to make to existing systems so I rewrote the plugin with a bunch of updates:\n\n- snapshots are always written on CI to easily download them as artifacts\n- ability to mask out certain elements which cause comparison brittleness\n- failing on `--update-snapshots` to make users manually review images\n- snapshot name is optional, `test_name[browser][os].png` is auto-generated by default\n- multiple snapshots in a single test, file names are auto incremented\n- updated folder structure: `snapshots/file_name/test_name/test_name[browser][os].png`\n- ability to configure directories, etc via ini + pytest config.\n\nYou can see this implemented in a [working project here](https://github.com/iloveitaly/python-starter-template/).\n\n## Installation\n\n```bash\npip install pytest-playwright-visual-snapshot\n```\n\n## Usage\n\nThis plugin provides a `assert_snapshot` fixture which is used to create snapshots and compare it.\n\n```python\ndef test_myapp(page, assert_snapshot):\n page.goto(\"https://example.com\")\n assert_snapshot(page)\n```\n\nThen, run pytest:\n\n```bash\npytest\n```\n\nThe first time you run pytest, snapshots will be created, and you will get the error:\n\n```console\nNew snapshot(s) created. Please review images\n```\n\nThe next run, the snapshots comparison will take place. To update snapshots, run:\n\n```bash\npytest --update-snapshots\n```\n\nAfter updating, tests will fail and you will need to review images.\n\nIn case of a mismatch, `snapshot_tests_failures` folder will be created with `actual_..`, `expected_..` and `diff_..` images generated.\n\n## Configuration\n\nView all configuration options by running `pytest --help`. Here's a quick example:\n\n```python\n# NOTE this runs on any pytest invocation, even if no tests are run\ndef pytest_configure(config: Config):\n config.option.playwright_visual_snapshots_path = Path(\"...\")\n config.option.playwright_visual_snapshot_failures_path = Path(\"...\")\n```\n\n### Masking Elements\n\nYou can mask certain elements during screenshot capture to prevent them from causing comparison failures. This is useful for dynamic content like timestamps, user avatars, etc.\n\nConfigure global masks in your pytest.ini:\n\n```ini\n[pytest]\nplaywright_visual_snapshot_masks =\n [data-clerk-component=\"UserButton\"]\n .timestamp\n #dynamic-content\n```\n\nOr directly via `pytest_configure`:\n\n```python\ndef pytest_configure(config: Config):\n config.option.playwright_visual_snapshot_masks = [\n '[data-clerk-component=\"UserButton\"]',\n '.timestamp',\n '#dynamic-content'\n ]\n```\n\nOr specify masks directly in your test:\n\n```python\ndef test_with_custom_masks(page, assert_snapshot):\n page.goto(\"https://example.com\")\n assert_snapshot(page, mask_elements=[\".user-avatar\", \"#timestamp\"])\n```\n\n### GitHub Actions Script\n\nThe CI Chrome will be slightly different than your dev chrome. You'll want to pull down screenshots from your CI run and use those for comparison. Here's a script to do that:\n\n```shell\nfailed_run_id=$(gh run list --status=failure --workflow=workflow_name.yml --json databaseId --jq '.[0].databaseId')\nPLAYWRIGHT_RESULT_DIRECTORY=\n\nrm -rf ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id} && \\\nmkdir -p ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id} && \\\ngh run --dir ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id} download $failed_run_id && \\\ncp -R ${PLAYWRIGHT_RESULT_DIRECTORY}/${failed_run_id}/test-results/${PLAYWRIGHT_VISUAL_SNAPSHOT_DIRECTORY}/ ${PLAYWRIGHT_VISUAL_SNAPSHOT_DIRECTORY}/\n```\n\n## API\n\n- `threshold` - sets the threshold for the comparison of the screenshots:`0` to `1`. Default is `0.1`\n<!-- - `name` - `.png` extensions only. Default is `test_name[browser][os].png` (recommended) -->\n- `fail_fast` - If `True`, will fail after first different pixel. `False` by default\n- `mask_elements` - List of CSS selectors to mask during screenshot capture. These will be combined with any globally configured masks.\n\n## Alternatives\n\n- https://github.com/kumaraditya303/pytest-playwright-snapshot - long dead\n- https://github.com/symon-storozhenko/pytest-playwright-visual - fork of the above repo, long dead\n- https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker - requires a separate server to run\n",
"bugtrack_url": null,
"license": null,
"summary": "Easy pytest visual regression testing using playwright",
"version": "0.2.4",
"project_urls": {
"Repository": "https://github.com/iloveitaly/pytest-playwright-visual-snapshot"
},
"split_keywords": [
"playwright",
" pytest",
" regression",
" testing",
" visual"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "cba35a25438d115397685b2cb440582372706c93f0123de42e87f85549e00165",
"md5": "484681bf0d958e4dec8d1588cbc6b79f",
"sha256": "9c27c933053fa832f89eda9d9207a74604f08fb14d37af2e21c08d21b45a4886"
},
"downloads": -1,
"filename": "pytest_playwright_visual_snapshot-0.2.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "484681bf0d958e4dec8d1588cbc6b79f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 6368,
"upload_time": "2025-11-04T18:51:57",
"upload_time_iso_8601": "2025-11-04T18:51:57.869711Z",
"url": "https://files.pythonhosted.org/packages/cb/a3/5a25438d115397685b2cb440582372706c93f0123de42e87f85549e00165/pytest_playwright_visual_snapshot-0.2.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "581730950fbf944dd7cfda511e29483addb8e9ae42acecd803c85b5952b258dc",
"md5": "f03b2790e65c9c58ba2b4d602391fb84",
"sha256": "efa0361795437683de57cd7bdb8c1615190b5c9cba059b4cee912bd7b57a5f85"
},
"downloads": -1,
"filename": "pytest_playwright_visual_snapshot-0.2.4.tar.gz",
"has_sig": false,
"md5_digest": "f03b2790e65c9c58ba2b4d602391fb84",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 77766,
"upload_time": "2025-11-04T18:51:59",
"upload_time_iso_8601": "2025-11-04T18:51:59.007200Z",
"url": "https://files.pythonhosted.org/packages/58/17/30950fbf944dd7cfda511e29483addb8e9ae42acecd803c85b5952b258dc/pytest_playwright_visual_snapshot-0.2.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-04 18:51:59",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "iloveitaly",
"github_project": "pytest-playwright-visual-snapshot",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "pytest-playwright-visual-snapshot"
}