visual-regression-tracker 5.0.0__tar.gz → 6.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. visual_regression_tracker-6.0.0/.coveragerc +3 -0
  2. visual_regression_tracker-6.0.0/.github/dependabot.yml +60 -0
  3. visual_regression_tracker-6.0.0/.github/workflows/release.yml +76 -0
  4. visual_regression_tracker-6.0.0/.github/workflows/workflow.yml +29 -0
  5. visual_regression_tracker-6.0.0/.gitignore +12 -0
  6. visual_regression_tracker-6.0.0/.releaserc.json +13 -0
  7. visual_regression_tracker-6.0.0/PKG-INFO +243 -0
  8. visual_regression_tracker-6.0.0/README.md +223 -0
  9. visual_regression_tracker-6.0.0/package.json +8 -0
  10. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/pyproject.toml +6 -8
  11. visual_regression_tracker-6.0.0/requirements.txt +5 -0
  12. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/tests/test_config.py +8 -0
  13. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/tests/test_playwright.py +16 -13
  14. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/tests/test_playwright_async.py +16 -13
  15. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/tests/test_visualRegressionTracker.py +198 -6
  16. visual_regression_tracker-6.0.0/tox.ini +20 -0
  17. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker/config.py +2 -0
  18. visual_regression_tracker-6.0.0/visual_regression_tracker/playwright.py +140 -0
  19. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker/types.py +6 -0
  20. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker/visualRegressionTracker.py +81 -16
  21. visual_regression_tracker-6.0.0/visual_regression_tracker.egg-info/PKG-INFO +243 -0
  22. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker.egg-info/SOURCES.txt +11 -0
  23. visual_regression_tracker-6.0.0/visual_regression_tracker.egg-info/scm_file_list.json +27 -0
  24. visual_regression_tracker-6.0.0/visual_regression_tracker.egg-info/scm_version.json +8 -0
  25. visual_regression_tracker-5.0.0/PKG-INFO +0 -312
  26. visual_regression_tracker-5.0.0/README.md +0 -288
  27. visual_regression_tracker-5.0.0/visual_regression_tracker/playwright.py +0 -154
  28. visual_regression_tracker-5.0.0/visual_regression_tracker.egg-info/PKG-INFO +0 -312
  29. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/LICENSE +0 -0
  30. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/setup.cfg +0 -0
  31. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/tests/test_types.py +0 -0
  32. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker/__init__.py +0 -0
  33. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker/exceptions.py +0 -0
  34. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker.egg-info/dependency_links.txt +0 -0
  35. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker.egg-info/requires.txt +0 -0
  36. {visual_regression_tracker-5.0.0 → visual_regression_tracker-6.0.0}/visual_regression_tracker.egg-info/top_level.txt +0 -0
@@ -0,0 +1,3 @@
1
+ [run]
2
+ concurrency = greenlet
3
+ branch = True
@@ -0,0 +1,60 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ # requirements.txt pins the dev tools; pyproject.toml keeps ranges, raised only when a fix needs it
5
+ directory: "/"
6
+ schedule:
7
+ interval: weekly
8
+ open-pull-requests-limit: 5
9
+ versioning-strategy: increase-if-necessary
10
+ groups:
11
+ # one PR for all minor/patch bumps
12
+ pip-minor-patch:
13
+ patterns:
14
+ - "*"
15
+ update-types:
16
+ - minor
17
+ - patch
18
+ # majors usually need code changes, so they come apart from minor/patch
19
+ pip-major:
20
+ patterns:
21
+ - "*"
22
+ update-types:
23
+ - major
24
+ # security fixes in a single PR too
25
+ pip-security:
26
+ applies-to: security-updates
27
+ patterns:
28
+ - "*"
29
+
30
+ - package-ecosystem: npm
31
+ # package.json holds only the release tooling that release.yml installs
32
+ directory: "/"
33
+ schedule:
34
+ interval: weekly
35
+ open-pull-requests-limit: 5
36
+ groups:
37
+ release-tooling:
38
+ patterns:
39
+ - "*"
40
+ update-types:
41
+ - minor
42
+ - patch
43
+ release-tooling-security:
44
+ applies-to: security-updates
45
+ patterns:
46
+ - "*"
47
+ ignore:
48
+ # preset 10 needs conventional-changelog-writer 9; semantic-release 25 bundles writer 8
49
+ - dependency-name: conventional-changelog-conventionalcommits
50
+ update-types:
51
+ - version-update:semver-major
52
+
53
+ - package-ecosystem: github-actions
54
+ directory: "/"
55
+ schedule:
56
+ interval: weekly
57
+ groups:
58
+ github-actions:
59
+ patterns:
60
+ - "*"
@@ -0,0 +1,76 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [master, next]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: release
12
+
13
+ jobs:
14
+ release:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ contents: write
18
+ issues: write
19
+ outputs:
20
+ version: ${{ steps.semantic-release.outputs.version }}
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+ with:
24
+ fetch-depth: 0
25
+ - uses: actions/setup-python@v7
26
+ with:
27
+ python-version: '3.15'
28
+ - name: Test
29
+ run: |
30
+ pip install build '.[playwright]' -r requirements.txt
31
+ python -m playwright install --with-deps chromium
32
+ python -m pytest tests
33
+ - uses: actions/setup-node@v7
34
+ with:
35
+ node-version: 24
36
+ - name: Install the release tooling
37
+ run: npm install --no-package-lock --no-audit --no-fund
38
+ - name: Build, tag and draft the GitHub release
39
+ id: semantic-release
40
+ env:
41
+ GITHUB_TOKEN: ${{ github.token }}
42
+ run: npx semantic-release
43
+ - uses: actions/upload-artifact@v7
44
+ if: steps.semantic-release.outputs.version
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+ if-no-files-found: error
49
+
50
+ publish:
51
+ needs: release
52
+ if: needs.release.outputs.version
53
+ runs-on: ubuntu-latest
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/project/visual-regression-tracker/${{ needs.release.outputs.version }}/
57
+ permissions:
58
+ id-token: write
59
+ steps:
60
+ - uses: actions/download-artifact@v8
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+ - uses: pypa/gh-action-pypi-publish@release/v1
65
+
66
+ github-release:
67
+ needs: [release, publish]
68
+ runs-on: ubuntu-latest
69
+ permissions:
70
+ contents: write
71
+ steps:
72
+ - name: Publish the GitHub release
73
+ env:
74
+ GH_TOKEN: ${{ github.token }}
75
+ VERSION: ${{ needs.release.outputs.version }}
76
+ run: gh release edit "$VERSION" --repo "$GITHUB_REPOSITORY" --draft=false
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master, next ]
8
+
9
+ jobs:
10
+ build:
11
+
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ['3.15']
17
+
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v7
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ - name: Install Tox
25
+ run: pip install tox
26
+ - name: Install Playwright system dependencies
27
+ run: pip install playwright && python -m playwright install-deps chromium
28
+ - name: Run Tox
29
+ run: tox -e py
@@ -0,0 +1,12 @@
1
+ .coverage
2
+ junit
3
+ visual_regression_tracker.egg-info
4
+ htmlcov
5
+ build
6
+ .idea
7
+ dist
8
+ __pycache__
9
+ .tox
10
+ .vscode
11
+ .python-version
12
+ node_modules/
@@ -0,0 +1,13 @@
1
+ {
2
+ "tagFormat": "${version}",
3
+ "branches": ["master", { "name": "next", "prerelease": "rc" }],
4
+ "plugins": [
5
+ ["@semantic-release/commit-analyzer", { "preset": "conventionalcommits" }],
6
+ ["@semantic-release/release-notes-generator", { "preset": "conventionalcommits" }],
7
+ ["@semantic-release/exec", {
8
+ "prepareCmd": "SETUPTOOLS_SCM_PRETEND_VERSION=${nextRelease.version} python -m build",
9
+ "successCmd": "echo \"version=${nextRelease.version}\" >> \"$GITHUB_OUTPUT\""
10
+ }],
11
+ ["@semantic-release/github", { "draftRelease": true, "successCommentCondition": false }]
12
+ ]
13
+ }
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: visual_regression_tracker
3
+ Version: 6.0.0
4
+ Summary: Open source, self hosted solution for visual testing and managing results of visual testing.
5
+ License-Expression: Apache-2.0
6
+ Project-URL: Homepage, https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker
7
+ Project-URL: Source, https://github.com/Visual-Regression-Tracker/sdk-python
8
+ Project-URL: Issues, https://github.com/Visual-Regression-Tracker/sdk-python/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.15
11
+ Classifier: Topic :: Software Development :: Testing
12
+ Requires-Python: >=3.15
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: requests>=2.32.4
16
+ Requires-Dist: dacite>=1.9
17
+ Provides-Extra: playwright
18
+ Requires-Dist: playwright>=1.50; extra == "playwright"
19
+ Dynamic: license-file
20
+
21
+ # Python SDK for [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker)
22
+
23
+ [![CI](https://github.com/Visual-Regression-Tracker/sdk-python/actions/workflows/workflow.yml/badge.svg)](https://github.com/Visual-Regression-Tracker/sdk-python/actions/workflows/workflow.yml)
24
+ [![PyPI](https://img.shields.io/pypi/v/visual-regression-tracker)](https://pypi.org/project/visual-regression-tracker/)
25
+
26
+ Requires Python 3.15 or later. Use 5.0 or later of this SDK with VRT 6.0; image URLs in results need VRT server 5.0.10 or later.
27
+
28
+ ## Install
29
+
30
+ ```sh
31
+ pip install visual-regression-tracker
32
+
33
+ # or, with the Playwright integration
34
+ pip install "visual-regression-tracker[playwright]"
35
+ python -m playwright install
36
+ ```
37
+
38
+ ## Configuration
39
+
40
+ | Option | Environment variable | Default | Meaning |
41
+ |---|---|---|---|
42
+ | `apiUrl` | `VRT_APIURL` | `http://localhost:4200` | URL of the VRT API |
43
+ | `apiKey` | `VRT_APIKEY` | required | API key of a VRT user |
44
+ | `project` | `VRT_PROJECT` | required | Project name or ID |
45
+ | `branchName` | `VRT_BRANCHNAME` | required | Current git branch |
46
+ | `ciBuildId` | `VRT_CIBUILDID` | none | CI build ID, such as the commit SHA; runs with the same ID share a build |
47
+ | `enableSoftAssert` | `VRT_ENABLESOFTASSERT` | `false` | Log a failed check instead of raising `TestRunError` |
48
+ | `httpTimeoutInSeconds` | `VRT_HTTPTIMEOUTINSECONDS` | `360` | How long to wait for the server's answer, `0` for no limit. Connecting is limited to 10 s. |
49
+ | `sourceBranch` | `VRT_SOURCEBRANCH` | the project's main branch | Branch whose baselines `branchName` is compared with, such as the target branch of a pull request |
50
+
51
+ The server compares images while it answers `track()`. With VLM checks or a busy comparison queue that can
52
+ take minutes, so keep `httpTimeoutInSeconds` above 300.
53
+
54
+ A `Config` passed to the constructor is used as it is:
55
+
56
+ ```python
57
+ from visual_regression_tracker import VisualRegressionTracker, Config
58
+
59
+ vrt = VisualRegressionTracker(Config(
60
+ apiUrl='http://localhost:4200',
61
+ apiKey='tXZVHX0EA4YQM1MGDD',
62
+ project='Default project',
63
+ branchName='develop',
64
+ ciBuildId='commit_sha',
65
+ ))
66
+ ```
67
+
68
+ Without one, the SDK reads `vrt.json` from the current working directory, then lets environment variables
69
+ override it. `Config.default(path)` reads another file.
70
+
71
+ ```json
72
+ {
73
+ "apiUrl": "http://localhost:4200",
74
+ "apiKey": "tXZVHX0EA4YQM1MGDD",
75
+ "project": "Default project",
76
+ "branchName": "develop",
77
+ "ciBuildId": "commit_sha",
78
+ "enableSoftAssert": false,
79
+ "httpTimeoutInSeconds": 360,
80
+ "sourceBranch": "main"
81
+ }
82
+ ```
83
+
84
+ ```sh
85
+ VRT_APIURL="http://localhost:4200" \
86
+ VRT_APIKEY="tXZVHX0EA4YQM1MGDD" \
87
+ VRT_PROJECT="Default project" \
88
+ VRT_BRANCHNAME="develop" \
89
+ VRT_CIBUILDID="commit_sha" \
90
+ VRT_ENABLESOFTASSERT=true \
91
+ VRT_HTTPTIMEOUTINSECONDS=360 \
92
+ VRT_SOURCEBRANCH="main" \
93
+ python -m pytest
94
+ ```
95
+
96
+ Empty variables are ignored. `VRT_ENABLESOFTASSERT` is true for `true` or `1`.
97
+
98
+ ## Usage
99
+
100
+ ```python
101
+ from visual_regression_tracker import VisualRegressionTracker, TestRun, IgnoreArea
102
+
103
+ with VisualRegressionTracker() as vrt: # start() on enter, stop() on exit
104
+ result = vrt.track(TestRun(
105
+ name='Home page',
106
+ imageBase64=image_base64,
107
+ ))
108
+ ```
109
+
110
+ Without the context manager, call `build = vrt.start()` (returns `Build(id, projectId)`) and `vrt.stop()`.
111
+
112
+ `TestRun` fields:
113
+
114
+ | Field | Meaning |
115
+ |---|---|
116
+ | `name` | Required. Name of the image in VRT |
117
+ | `imageBase64` | The PNG image, base64 encoded, sent as JSON |
118
+ | `imagePath` | Or: path of a PNG file, sent as multipart |
119
+ | `imageBuffer` | Or: the PNG image as `bytes`, sent as multipart |
120
+ | `os`, `browser`, `viewport`, `device`, `customTags` | Optional. Together with `name` they pick the baseline |
121
+ | `diffTollerancePercent` | Optional. Allowed difference in %, default 0. The spelling is the API's. |
122
+ | `ignoreAreas` | Optional. List of `IgnoreArea(x, y, width, height)` in pixels |
123
+ | `comment` | Optional. Shown with the test run in the VRT app |
124
+ | `merge` | Optional. `True`: approving this run on a feature branch updates the baseline of the branch it was compared with |
125
+
126
+ Set exactly one of the image fields. Multipart avoids the server's JSON size limit (`BODY_PARSER_JSON_LIMIT`)
127
+ and the base64 overhead, so prefer it for large screenshots.
128
+
129
+ ### Playwright
130
+
131
+ ```python
132
+ from playwright.sync_api import sync_playwright
133
+ from visual_regression_tracker.playwright import PlaywrightVisualRegressionTracker, PageTrackOptions
134
+
135
+ with sync_playwright() as playwright:
136
+ browser_type = playwright.chromium
137
+ page = browser_type.launch().new_page()
138
+ page.goto('https://www.python.org/')
139
+
140
+ with PlaywrightVisualRegressionTracker(browser_type) as vrt:
141
+ vrt.trackPage(page, 'Home page')
142
+ vrt.trackElementHandle(page.query_selector('#touchnav-wrapper'), 'Navigation')
143
+ ```
144
+
145
+ The screenshot is sent as multipart. `browser` is the Playwright browser name and `viewport` the page's viewport size. The async API has
146
+ `trackPageAsync` and `trackElementHandleAsync`.
147
+
148
+ The last argument is optional: `PageTrackOptions` for `trackPage`, `ElementHandleTrackOptions` for
149
+ `trackElementHandle`. Both take:
150
+
151
+ - `diffTollerancePercent`, `ignoreAreas`, `customTags`, `comment` and `merge`, as in `TestRun`;
152
+ - `agent: Agent(os, device, viewport)`: baseline fields Playwright doesn't know. `viewport` is used for
153
+ element handles and for pages without a viewport size;
154
+ - `screenshotOptions`: arguments for Playwright's `screenshot()`. `PageScreenshotOptions` takes
155
+ `full_page`, `clip`, `omit_background`, `timeout`, `type`, `quality` and `path`.
156
+ `ElementHandleScreenshotOptions` takes the same, without `full_page` and `clip`.
157
+
158
+ ## Result and statuses
159
+
160
+ `track` and the Playwright methods return a `TestRunResult`:
161
+
162
+ - `testRunResponse`: `id`, `status`, `url` (the test run in the VRT app), `diffPercent`,
163
+ `diffTollerancePercent`, `pixelMisMatchCount`, `merge`;
164
+ - `imageUrl`, `diffUrl` and `baselineUrl`: the images, under `{apiUrl}/images/`. `diffUrl` and
165
+ `baselineUrl` are `None` when there is no diff or baseline.
166
+
167
+ | `TestRunStatus` | Server status | `track` |
168
+ |---|---|---|
169
+ | `OK` | `ok` | passes |
170
+ | `APPROVED` | `approved` | passes |
171
+ | `AUTO_APPROVED` | `autoApproved` | passes |
172
+ | `FAILED` | `failed` | passes, as in the other SDKs |
173
+ | `NEW` | `new` (no baseline yet) | raises `TestRunError` |
174
+ | `UNRESOLVED` | `unresolved` (difference found) | raises `TestRunError` |
175
+ | `UNKNOWN` | any other | passes, logs a warning |
176
+
177
+ With `enableSoftAssert`, `track` logs `new` and `unresolved` as errors instead of raising.
178
+
179
+ ## Errors
180
+
181
+ All errors derive from `VisualRegressionTrackerError`.
182
+
183
+ | Error | When |
184
+ |---|---|
185
+ | `ServerError` | The server answered with an HTTP error. `status_code` and the server's `message`; 413 and 422 add a hint about the server's size limits. |
186
+ | `RequestError` | No answer: connection refused, connect timeout, or read timeout after `httpTimeoutInSeconds`. Has the `url`. A read timeout on `track` doesn't mean the server dropped the test run: it may still be stored. |
187
+ | `TestRunError` | `track` got `new` or `unresolved`. Has the `status`. |
188
+ | `MissingConfigurationError` | A required option is not set. Has the `field`. |
189
+ | `InvalidConfigurationError` | An option has an invalid value. Has the `field`. |
190
+ | `VisualRegressionTrackerError` | `track` or `stop` before `start`. |
191
+ | `ValueError` | A `TestRun` with more than one image field. |
192
+
193
+ When the server answers 503 with a `Retry-After` of at most 60 seconds (the comparison queue is full, or the
194
+ server is restarting), the SDK waits that long and sends the request again, up to 3 times, logging a warning
195
+ each time. After that, or without a usable `Retry-After`, it raises `ServerError`.
196
+
197
+ ## Development
198
+
199
+ ```sh
200
+ pip install -e '.[playwright]' -r requirements.txt
201
+ python -m playwright install chromium
202
+ python -m pytest
203
+ ```
204
+
205
+ `tox` runs the tests on every supported Python.
206
+
207
+ ## Releasing
208
+
209
+ Merging to `master` releases. [semantic-release](https://semantic-release.gitbook.io/) reads the
210
+ [Conventional Commits](https://www.conventionalcommits.org/) since the last tag: `fix:`, `perf:` and `revert:` make a
211
+ patch, `feat:` a minor, and `!` after the type (`feat!:`) or a `BREAKING CHANGE:` footer a major release.
212
+ Other types (`chore:`, `ci:`, `docs:`, ...) release nothing. A squash merge takes the PR title (or, for a
213
+ single-commit PR, that commit's message) as its subject, so keep both conventional.
214
+
215
+ The `Release` workflow runs the tests, tags the commit (`5.1.0`, no `v`), publishes to PyPI and then
216
+ publishes the GitHub release with the generated notes. The package version comes from the tag
217
+ (setuptools-scm); nothing is committed back.
218
+
219
+ If publishing fails, the tag and a draft release already exist: re-run the failed jobs of that run
220
+ (not the whole run) before anything else is merged.
221
+
222
+ ### Release candidates
223
+
224
+ Every push to `next` releases a pre-release of the next version, tagged `X.Y.Z-rc.N`
225
+ (`6.0.0-rc.1` once a breaking change is in).
226
+ It goes to PyPI as a pre-release, `X.Y.ZrcN` (setuptools-scm normalizes the tag to PEP 440), so
227
+ `pip install visual-regression-tracker` keeps installing the last stable release and
228
+ `pip install --pre visual-regression-tracker` installs the candidate.
229
+ Its GitHub release is marked as a pre-release.
230
+
231
+ Breaking changes go into `next` through pull requests that target it. Merge `master` into `next` when
232
+ `master` gets new commits.
233
+
234
+ To release the final version, merge `next` into `master` with a merge commit, not a squash, so that
235
+ semantic-release still sees the breaking commits. That push releases it. The repository deletes merged
236
+ branches, so create `next` again from `master` before the next round of candidates.
237
+
238
+ ## Links
239
+
240
+ - [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker): the server and how to run it
241
+ - [Python examples](https://github.com/Visual-Regression-Tracker/examples-python)
242
+ - [Other SDKs and agents](https://github.com/Visual-Regression-Tracker)
243
+ - [Licence: Apache-2.0](LICENSE)
@@ -0,0 +1,223 @@
1
+ # Python SDK for [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker)
2
+
3
+ [![CI](https://github.com/Visual-Regression-Tracker/sdk-python/actions/workflows/workflow.yml/badge.svg)](https://github.com/Visual-Regression-Tracker/sdk-python/actions/workflows/workflow.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/visual-regression-tracker)](https://pypi.org/project/visual-regression-tracker/)
5
+
6
+ Requires Python 3.15 or later. Use 5.0 or later of this SDK with VRT 6.0; image URLs in results need VRT server 5.0.10 or later.
7
+
8
+ ## Install
9
+
10
+ ```sh
11
+ pip install visual-regression-tracker
12
+
13
+ # or, with the Playwright integration
14
+ pip install "visual-regression-tracker[playwright]"
15
+ python -m playwright install
16
+ ```
17
+
18
+ ## Configuration
19
+
20
+ | Option | Environment variable | Default | Meaning |
21
+ |---|---|---|---|
22
+ | `apiUrl` | `VRT_APIURL` | `http://localhost:4200` | URL of the VRT API |
23
+ | `apiKey` | `VRT_APIKEY` | required | API key of a VRT user |
24
+ | `project` | `VRT_PROJECT` | required | Project name or ID |
25
+ | `branchName` | `VRT_BRANCHNAME` | required | Current git branch |
26
+ | `ciBuildId` | `VRT_CIBUILDID` | none | CI build ID, such as the commit SHA; runs with the same ID share a build |
27
+ | `enableSoftAssert` | `VRT_ENABLESOFTASSERT` | `false` | Log a failed check instead of raising `TestRunError` |
28
+ | `httpTimeoutInSeconds` | `VRT_HTTPTIMEOUTINSECONDS` | `360` | How long to wait for the server's answer, `0` for no limit. Connecting is limited to 10 s. |
29
+ | `sourceBranch` | `VRT_SOURCEBRANCH` | the project's main branch | Branch whose baselines `branchName` is compared with, such as the target branch of a pull request |
30
+
31
+ The server compares images while it answers `track()`. With VLM checks or a busy comparison queue that can
32
+ take minutes, so keep `httpTimeoutInSeconds` above 300.
33
+
34
+ A `Config` passed to the constructor is used as it is:
35
+
36
+ ```python
37
+ from visual_regression_tracker import VisualRegressionTracker, Config
38
+
39
+ vrt = VisualRegressionTracker(Config(
40
+ apiUrl='http://localhost:4200',
41
+ apiKey='tXZVHX0EA4YQM1MGDD',
42
+ project='Default project',
43
+ branchName='develop',
44
+ ciBuildId='commit_sha',
45
+ ))
46
+ ```
47
+
48
+ Without one, the SDK reads `vrt.json` from the current working directory, then lets environment variables
49
+ override it. `Config.default(path)` reads another file.
50
+
51
+ ```json
52
+ {
53
+ "apiUrl": "http://localhost:4200",
54
+ "apiKey": "tXZVHX0EA4YQM1MGDD",
55
+ "project": "Default project",
56
+ "branchName": "develop",
57
+ "ciBuildId": "commit_sha",
58
+ "enableSoftAssert": false,
59
+ "httpTimeoutInSeconds": 360,
60
+ "sourceBranch": "main"
61
+ }
62
+ ```
63
+
64
+ ```sh
65
+ VRT_APIURL="http://localhost:4200" \
66
+ VRT_APIKEY="tXZVHX0EA4YQM1MGDD" \
67
+ VRT_PROJECT="Default project" \
68
+ VRT_BRANCHNAME="develop" \
69
+ VRT_CIBUILDID="commit_sha" \
70
+ VRT_ENABLESOFTASSERT=true \
71
+ VRT_HTTPTIMEOUTINSECONDS=360 \
72
+ VRT_SOURCEBRANCH="main" \
73
+ python -m pytest
74
+ ```
75
+
76
+ Empty variables are ignored. `VRT_ENABLESOFTASSERT` is true for `true` or `1`.
77
+
78
+ ## Usage
79
+
80
+ ```python
81
+ from visual_regression_tracker import VisualRegressionTracker, TestRun, IgnoreArea
82
+
83
+ with VisualRegressionTracker() as vrt: # start() on enter, stop() on exit
84
+ result = vrt.track(TestRun(
85
+ name='Home page',
86
+ imageBase64=image_base64,
87
+ ))
88
+ ```
89
+
90
+ Without the context manager, call `build = vrt.start()` (returns `Build(id, projectId)`) and `vrt.stop()`.
91
+
92
+ `TestRun` fields:
93
+
94
+ | Field | Meaning |
95
+ |---|---|
96
+ | `name` | Required. Name of the image in VRT |
97
+ | `imageBase64` | The PNG image, base64 encoded, sent as JSON |
98
+ | `imagePath` | Or: path of a PNG file, sent as multipart |
99
+ | `imageBuffer` | Or: the PNG image as `bytes`, sent as multipart |
100
+ | `os`, `browser`, `viewport`, `device`, `customTags` | Optional. Together with `name` they pick the baseline |
101
+ | `diffTollerancePercent` | Optional. Allowed difference in %, default 0. The spelling is the API's. |
102
+ | `ignoreAreas` | Optional. List of `IgnoreArea(x, y, width, height)` in pixels |
103
+ | `comment` | Optional. Shown with the test run in the VRT app |
104
+ | `merge` | Optional. `True`: approving this run on a feature branch updates the baseline of the branch it was compared with |
105
+
106
+ Set exactly one of the image fields. Multipart avoids the server's JSON size limit (`BODY_PARSER_JSON_LIMIT`)
107
+ and the base64 overhead, so prefer it for large screenshots.
108
+
109
+ ### Playwright
110
+
111
+ ```python
112
+ from playwright.sync_api import sync_playwright
113
+ from visual_regression_tracker.playwright import PlaywrightVisualRegressionTracker, PageTrackOptions
114
+
115
+ with sync_playwright() as playwright:
116
+ browser_type = playwright.chromium
117
+ page = browser_type.launch().new_page()
118
+ page.goto('https://www.python.org/')
119
+
120
+ with PlaywrightVisualRegressionTracker(browser_type) as vrt:
121
+ vrt.trackPage(page, 'Home page')
122
+ vrt.trackElementHandle(page.query_selector('#touchnav-wrapper'), 'Navigation')
123
+ ```
124
+
125
+ The screenshot is sent as multipart. `browser` is the Playwright browser name and `viewport` the page's viewport size. The async API has
126
+ `trackPageAsync` and `trackElementHandleAsync`.
127
+
128
+ The last argument is optional: `PageTrackOptions` for `trackPage`, `ElementHandleTrackOptions` for
129
+ `trackElementHandle`. Both take:
130
+
131
+ - `diffTollerancePercent`, `ignoreAreas`, `customTags`, `comment` and `merge`, as in `TestRun`;
132
+ - `agent: Agent(os, device, viewport)`: baseline fields Playwright doesn't know. `viewport` is used for
133
+ element handles and for pages without a viewport size;
134
+ - `screenshotOptions`: arguments for Playwright's `screenshot()`. `PageScreenshotOptions` takes
135
+ `full_page`, `clip`, `omit_background`, `timeout`, `type`, `quality` and `path`.
136
+ `ElementHandleScreenshotOptions` takes the same, without `full_page` and `clip`.
137
+
138
+ ## Result and statuses
139
+
140
+ `track` and the Playwright methods return a `TestRunResult`:
141
+
142
+ - `testRunResponse`: `id`, `status`, `url` (the test run in the VRT app), `diffPercent`,
143
+ `diffTollerancePercent`, `pixelMisMatchCount`, `merge`;
144
+ - `imageUrl`, `diffUrl` and `baselineUrl`: the images, under `{apiUrl}/images/`. `diffUrl` and
145
+ `baselineUrl` are `None` when there is no diff or baseline.
146
+
147
+ | `TestRunStatus` | Server status | `track` |
148
+ |---|---|---|
149
+ | `OK` | `ok` | passes |
150
+ | `APPROVED` | `approved` | passes |
151
+ | `AUTO_APPROVED` | `autoApproved` | passes |
152
+ | `FAILED` | `failed` | passes, as in the other SDKs |
153
+ | `NEW` | `new` (no baseline yet) | raises `TestRunError` |
154
+ | `UNRESOLVED` | `unresolved` (difference found) | raises `TestRunError` |
155
+ | `UNKNOWN` | any other | passes, logs a warning |
156
+
157
+ With `enableSoftAssert`, `track` logs `new` and `unresolved` as errors instead of raising.
158
+
159
+ ## Errors
160
+
161
+ All errors derive from `VisualRegressionTrackerError`.
162
+
163
+ | Error | When |
164
+ |---|---|
165
+ | `ServerError` | The server answered with an HTTP error. `status_code` and the server's `message`; 413 and 422 add a hint about the server's size limits. |
166
+ | `RequestError` | No answer: connection refused, connect timeout, or read timeout after `httpTimeoutInSeconds`. Has the `url`. A read timeout on `track` doesn't mean the server dropped the test run: it may still be stored. |
167
+ | `TestRunError` | `track` got `new` or `unresolved`. Has the `status`. |
168
+ | `MissingConfigurationError` | A required option is not set. Has the `field`. |
169
+ | `InvalidConfigurationError` | An option has an invalid value. Has the `field`. |
170
+ | `VisualRegressionTrackerError` | `track` or `stop` before `start`. |
171
+ | `ValueError` | A `TestRun` with more than one image field. |
172
+
173
+ When the server answers 503 with a `Retry-After` of at most 60 seconds (the comparison queue is full, or the
174
+ server is restarting), the SDK waits that long and sends the request again, up to 3 times, logging a warning
175
+ each time. After that, or without a usable `Retry-After`, it raises `ServerError`.
176
+
177
+ ## Development
178
+
179
+ ```sh
180
+ pip install -e '.[playwright]' -r requirements.txt
181
+ python -m playwright install chromium
182
+ python -m pytest
183
+ ```
184
+
185
+ `tox` runs the tests on every supported Python.
186
+
187
+ ## Releasing
188
+
189
+ Merging to `master` releases. [semantic-release](https://semantic-release.gitbook.io/) reads the
190
+ [Conventional Commits](https://www.conventionalcommits.org/) since the last tag: `fix:`, `perf:` and `revert:` make a
191
+ patch, `feat:` a minor, and `!` after the type (`feat!:`) or a `BREAKING CHANGE:` footer a major release.
192
+ Other types (`chore:`, `ci:`, `docs:`, ...) release nothing. A squash merge takes the PR title (or, for a
193
+ single-commit PR, that commit's message) as its subject, so keep both conventional.
194
+
195
+ The `Release` workflow runs the tests, tags the commit (`5.1.0`, no `v`), publishes to PyPI and then
196
+ publishes the GitHub release with the generated notes. The package version comes from the tag
197
+ (setuptools-scm); nothing is committed back.
198
+
199
+ If publishing fails, the tag and a draft release already exist: re-run the failed jobs of that run
200
+ (not the whole run) before anything else is merged.
201
+
202
+ ### Release candidates
203
+
204
+ Every push to `next` releases a pre-release of the next version, tagged `X.Y.Z-rc.N`
205
+ (`6.0.0-rc.1` once a breaking change is in).
206
+ It goes to PyPI as a pre-release, `X.Y.ZrcN` (setuptools-scm normalizes the tag to PEP 440), so
207
+ `pip install visual-regression-tracker` keeps installing the last stable release and
208
+ `pip install --pre visual-regression-tracker` installs the candidate.
209
+ Its GitHub release is marked as a pre-release.
210
+
211
+ Breaking changes go into `next` through pull requests that target it. Merge `master` into `next` when
212
+ `master` gets new commits.
213
+
214
+ To release the final version, merge `next` into `master` with a merge commit, not a squash, so that
215
+ semantic-release still sees the breaking commits. That push releases it. The repository deletes merged
216
+ branches, so create `next` again from `master` before the next round of candidates.
217
+
218
+ ## Links
219
+
220
+ - [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker): the server and how to run it
221
+ - [Python examples](https://github.com/Visual-Regression-Tracker/examples-python)
222
+ - [Other SDKs and agents](https://github.com/Visual-Regression-Tracker)
223
+ - [Licence: Apache-2.0](LICENSE)
@@ -0,0 +1,8 @@
1
+ {
2
+ "private": true,
3
+ "devDependencies": {
4
+ "@semantic-release/exec": "7.1.0",
5
+ "conventional-changelog-conventionalcommits": "9.3.1",
6
+ "semantic-release": "25.0.9"
7
+ }
8
+ }