Skip to content

Commit 42956e4

Browse files
New Major Release (#830)
* Ws/fix-747-improve-mobile-webview-screenshot (#777) * chore: update deps * chore: revert major eslint * feat: initial setup for device layer calculations - this is work in progress * chore: fixed some stuff and updated some broken UT's * fix: right import and export * fix: fix mocks * chore: optimise code a bit and add docs * chore: fix linking and types * fix: fix return data from overlay * feat: add Android support for blockouts based on device rectangles * feat: support ios and android for checkScreen - simplify determineStatusAddressToolBarRectangles - reduce rectangle tests - fix data url loading in Safari * feat: add checkElement support * fix: optimise code and fix UTs for element * feat: support savefullpagescreenshot with new viewport data for Android * feat: support fullpage for iOS portrait * test: fix tests * fix: multiple fixes for fullpage - iOS had issues on landscape - removed not needed code and files * chore: remove logs * fix: fix Android Appium 1 issues - add fallback - remove logs - update images for android phones * chore: update iOS stuff - add LANDSCAPE for local android * chore: updated safari desktop screenshots * test: update snapshots * feat: move the new logic to the webdriver-image-comparison module * fix: fix unit tests * breaking: taking element screenshot for iOS - some other small fixes * chore: optimise deviceRectangles - remove devicePlatformRect - enrich deviceRectangles with statusBar and homeBar - change devicePlatformRect output to x/y instead of left/top to match a coordinate system - fix UTs * chore: refactor stuff - rename function - add screendata for deviceRectangles * chore: improve readability for vars in utils * chore: update baseline and workflow * chore: last image updates * chore: update latest deps * fix: add fallback for Android and iOS mobile screensize - add more UT's * test: add more UT's * fix: fix orientation and update ios images * Add viewport context manager for managing device rectangles (#833) * feat: add viewport context manager * feat: add new context manager - revert old one - add a wrapper for each command that checks for changes - fix screensizes when the orientation changes and provide better fallback There is a TODO regarding Multiremote on the global browser * chore: fix UT's * chore: add a last test * feat: enhance context manager - add mobile context - add wrapper per command - add and fix UT's * fix: multiple fixes - fix capabilities, should be requested - assumed that the `'mobile: deviceInfo` didn't return landscape info - added new images for android * fix: reset orientation * Fix: fix iOS orientation and make it generic * chore: new iOS images * fix: support proper appium cap present - add new images - add nativeWebScreenshot for proper webviews screenshots * test: add iOS for webview tests - add new images * fix: fix tests * Ws/add bidi screenshot support (#831) * feat: add bidi fullpage screenshot support * chore: code optimizations * chore: add changeset * feat: deprecate root compare options (#832) * feat: deprecate root compare options * chore: add changeset * Update packages/webdriver-image-comparison/src/helpers/options.ts Co-authored-by: alcpereira <[email protected]> * Update packages/webdriver-image-comparison/src/helpers/utils.ts Co-authored-by: alcpereira <[email protected]> * Update packages/webdriver-image-comparison/src/helpers/utils.ts Co-authored-by: alcpereira <[email protected]> * chore: updated the options docs * chore: updated tests after feedback --------- Co-authored-by: alcpereira <[email protected]> * chore: rename new bidi flag * chore: update deps * chore: provide changelogs --------- Co-authored-by: alcpereira <[email protected]>
1 parent 80f65d2 commit 42956e4

File tree

172 files changed

+5415
-3730
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

172 files changed

+5415
-3730
lines changed

.changeset/many-olives-deny.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
---
2+
"webdriver-image-comparison": major
3+
"@wdio/visual-service": major
4+
"@wdio/visual-reporter": patch
5+
"@wdio/ocr-service": patch
6+
---
7+
8+
## 💥 BREAKING CHANGES
9+
10+
### 🔍 Viewport Screenshot Logic Reworked for Mobile Web & Hybrid Apps
11+
12+
#### What was the problem?
13+
14+
Screenshots for mobile devices were inconsistent due to platform differences. iOS captures the entire device screen (including status and address bars), while Android (using ChromeDriver) only captures the webview, unless the capability `"appium:nativeWebScreenshot": true` is used.
15+
16+
#### What changed?
17+
18+
We’ve reimplemented the logic to correctly handle both platforms by default.
19+
This fix addresses [[#747](https://github.com/webdriverio/visual-testing/pull/747)](https://github.com/webdriverio/visual-testing/pull/747).
20+
21+
💡 Credit to [Benjamin Karran (@ebekebe)](https://github.com/ebekebe) for pointing us in the right direction to improve this logic!
22+
23+
#### What’s the advantage?
24+
25+
✅ More **accurate full-page and element screenshots** on both Android and iOS.
26+
⚠️ But this change may **break your current baselines**, especially on Android and iOS.
27+
28+
---
29+
30+
### 🍏 iOS Element Screenshot Strategy Changed
31+
32+
#### What was the problem?
33+
34+
iOS element screenshots were previously cut from full-device screenshots, which could lead to misalignment or off-by-a-few-pixels issues.
35+
36+
#### What changed?
37+
38+
We now use the element screenshot endpoint directly.
39+
40+
#### What’s the advantage?
41+
42+
✅ More accurate iOS element screenshots.
43+
⚠️ But again, this may affect your existing baselines.
44+
45+
---
46+
47+
### 🖥️ New Full-Page Screenshot Strategy for **Desktop Web**
48+
49+
#### What was the problem?
50+
51+
The "previous" scroll-and-stitch method simulated user interaction by scrolling the page, waiting, taking a screenshot, and repeating until the entire page was captured.
52+
This works well for **lazy-loaded content**, but it is **slow and unstable** on other pages.
53+
54+
#### What changed?
55+
56+
We now use WebDriver BiDi’s [`[browsingContext.captureScreenshot](https://webdriver.io/docs/api/webdriverBidi#browsingcontextcapturescreenshot)`] to capture **full-page screenshots in one go**. This is the new **default strategy for desktop web browsers**.
57+
58+
📌 **Mobile platforms (iOS/Android)** still use the scroll-and-stitch approach for now.
59+
60+
#### What’s the advantage?
61+
✅ Execution time reduced by **50%+**
62+
✅ Logic is greatly simplified
63+
✅ More consistent and stable results on static or non-lazy pages
64+
📸 ![Example](https://github.com/user-attachments/assets/394ad1d6-bbc7-42dd-b93b-ff7eb5a80429)
65+
66+
**Still want the old scroll-and-stitch behavior or need fullpage screenshots for pages who have lazy-loading?**
67+
68+
Use the `userBasedFullPageScreenshot` option to simulate user-like scrolling. This remains the **better choice for pages with lazy-loading**:
69+
70+
```ts
71+
// wdio.conf.ts
72+
services: [
73+
["visual", {
74+
userBasedFullPageScreenshot: true
75+
}]
76+
]
77+
```
78+
79+
Or per test:
80+
81+
```ts
82+
await expect(browser).toMatchFullPageSnapshot('homepage', {
83+
userBasedFullPageScreenshot: true,
84+
})
85+
```
86+
87+
---
88+
89+
## 💅 Polish
90+
91+
### ⚠️ Deprecated Root-Level Compare Options
92+
93+
#### What was the problem?
94+
95+
Compare options were allowed at the root level of the service config, making them harder to group or discover.
96+
97+
#### What changed?
98+
99+
You now get a warning if you still use root-level keys. Please move them under the `compareOptions` property instead.
100+
101+
**Example warning:**
102+
103+
```log
104+
WARN The following root-level compare options are deprecated and should be moved under 'compareOptions':
105+
- blockOutStatusBar
106+
- ignoreColors
107+
In the next major version, these options will be removed from the root level.
108+
```
109+
110+
📘 See: [[compareOptions docs](https://webdriver.io/docs/visual-testing/service-options#compare-options)](https://webdriver.io/docs/visual-testing/service-options#compare-options)
111+
112+
---
113+
114+
## 🐛 Bug Fixes
115+
116+
-[[#747](https://github.com/your-repo/issues/747)](https://github.com/your-repo/issues/747): Fixed incorrect mobile webview context data.
117+
118+
---
119+
120+
## 🔧 Other
121+
122+
- 🆙 Updated dependencies
123+
- 🧪 Improved test coverage
124+
- 📸 Refreshed image baselines
125+
126+
---
127+
128+
## Committers: 1
129+
130+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ jobs:
176176
LAMBDATEST_USERNAME: ${{ secrets.LAMBDATEST_USERNAME }}
177177
LAMBDATEST_ACCESS_KEY: ${{ secrets.LAMBDATEST_ACCESS_KEY }}
178178
BUILD_PREFIX: true
179-
run: pnpm test.lambdatest.desktop --maxConcurrency=3
179+
run: pnpm test.lambdatest.desktop --maxConcurrency=4
180180

181181
- name: 📤 Upload artifacts
182182
uses: actions/upload-artifact@v4
@@ -239,7 +239,7 @@ jobs:
239239
LAMBDATEST_USERNAME: ${{ secrets.LAMBDATEST_USERNAME }}
240240
LAMBDATEST_ACCESS_KEY: ${{ secrets.LAMBDATEST_ACCESS_KEY }}
241241
BUILD_PREFIX: true
242-
run: pnpm test.lambdatest.emu.web --maxConcurrency=4
242+
run: pnpm test.lambdatest.emu.web --maxConcurrency=6
243243

244244
- name: 📤 Upload artifacts
245245
uses: actions/upload-artifact@v4
@@ -302,7 +302,7 @@ jobs:
302302
LAMBDATEST_USERNAME: ${{ secrets.LAMBDATEST_USERNAME }}
303303
LAMBDATEST_ACCESS_KEY: ${{ secrets.LAMBDATEST_ACCESS_KEY }}
304304
BUILD_PREFIX: true
305-
run: pnpm test.lambdatest.sims.web --maxConcurrency=4
305+
run: pnpm test.lambdatest.sims.web --maxConcurrency=6
306306

307307
- name: 📤 Upload artifacts
308308
uses: actions/upload-artifact@v4

package.json

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"scripts": {
1313
"build": "pnpm run -r build",
1414
"build:reporter": "node ./packages/visual-reporter/dist/cli.js --jsonOutput=.tmp/actual/output.json --reportFolder=.tmp/ --logLevel=debug",
15-
"clean": "rimraf coverage dist .tmp '**/dist'",
15+
"clean": "rimraf coverage dist .tmp **/dist",
1616
"release": "run-s build && changeset publish",
1717
"test": "run-s test:*",
1818
"test:lint": "rimraf .eslintcache && eslint packages tests",
@@ -56,43 +56,43 @@
5656
"webdriver-image-comparison": "workspace:*"
5757
},
5858
"devDependencies": {
59-
"@changesets/cli": "^2.29.0",
59+
"@changesets/cli": "^2.29.2",
6060
"@tsconfig/node20": "^20.1.5",
6161
"@types/eslint": "^9.6.1",
6262
"@types/inquirer": "^9.0.7",
6363
"@types/jsdom": "~21.1.7",
6464
"@types/node": "^22",
6565
"@types/xml2js": "~0.4.14",
66-
"@typescript-eslint/eslint-plugin": "^8.29.1",
67-
"@typescript-eslint/parser": "^8.29.1",
68-
"@typescript-eslint/utils": "^8.29.1",
69-
"@vitest/coverage-v8": "^3.0.8",
70-
"@vitest/ui": "^3.0.8",
71-
"@wdio/appium-service": "^9.12.4",
72-
"@wdio/cli": "^9.12.5",
73-
"@wdio/globals": "^9.9.1",
74-
"@wdio/local-runner": "^9.12.5",
75-
"@wdio/mocha-framework": "^9.12.5",
76-
"@wdio/sauce-service": "^9.12.5",
77-
"@wdio/shared-store-service": "^9.12.5",
78-
"@wdio/spec-reporter": "^9.12.3",
79-
"@wdio/types": "^9.12.2",
66+
"@typescript-eslint/eslint-plugin": "^8.30.1",
67+
"@wdio/globals": "^9.12.6",
68+
"@wdio/mocha-framework": "^9.12.6",
69+
"@typescript-eslint/parser": "^8.30.1",
70+
"@typescript-eslint/utils": "^8.30.1",
71+
"@vitest/coverage-v8": "^3.1.1",
72+
"@vitest/ui": "^3.1.1",
73+
"@wdio/appium-service": "^9.12.6",
74+
"@wdio/cli": "^9.12.6",
75+
"@wdio/local-runner": "^9.12.6",
76+
"@wdio/sauce-service": "^9.12.6",
77+
"@wdio/shared-store-service": "^9.12.6",
78+
"@wdio/spec-reporter": "^9.12.6",
79+
"@wdio/types": "^9.12.6",
8080
"cross-env": "^7.0.3",
81-
"eslint": "^9.23.0",
81+
"eslint": "^9.24.0",
8282
"eslint-plugin-import": "^2.31.0",
8383
"eslint-plugin-unicorn": "^56.0.1",
8484
"eslint-plugin-wdio": "^9.9.1",
8585
"husky": "^9.1.7",
86-
"jsdom": "^25.0.1",
86+
"jsdom": "^26.1.0",
8787
"npm-run-all2": "^7.0.2",
88-
"release-it": "^17.11.0",
88+
"release-it": "^18.1.2",
8989
"rimraf": "^6.0.1",
90-
"saucelabs": "^8.0.0",
90+
"saucelabs": "^9.0.2",
9191
"ts-node": "^10.9.2",
92-
"typescript": "^5.7.3",
93-
"vitest": "^3.0.8",
94-
"wdio-lambdatest-service": "^4.0.0",
95-
"webdriverio": "^9.12.4"
92+
"typescript": "^5.8.3",
93+
"vitest": "^3.1.1",
94+
"webdriverio": "^9.12.6",
95+
"wdio-lambdatest-service": "^4.0.0"
9696
},
9797
"packageManager": "[email protected]+sha256.cf86a7ad764406395d4286a6d09d730711720acc6d93e9dce9ac7ac4dc4a28a7"
9898
}

packages/ocr-service/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
"watch": "pnpm run build:tsc -w"
2929
},
3030
"dependencies": {
31-
"@wdio/globals": "^9.9.1",
31+
"@wdio/globals": "^9.12.6",
3232
"@wdio/logger": "^9.4.4",
33-
"@wdio/types": "^9.12.2",
33+
"@wdio/types": "^9.12.6",
3434
"fuse.js": "^7.1.0",
35-
"@inquirer/prompts": "7.3.2",
35+
"@inquirer/prompts": "7.4.1",
3636
"jimp": "^1.6.0",
3737
"node-tesseract-ocr": "^2.2.1",
3838
"tesseract.js": "^5.1.1",
@@ -42,4 +42,4 @@
4242
"@types/inquirer": "~9.0.7",
4343
"@types/xml2js": "~0.4.14"
4444
}
45-
}
45+
}

packages/ocr-service/src/utils/getData.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logger from '@wdio/logger'
22
import { getNodeOcrData, getSystemOcrData } from './tesseract.js'
33
import type { GetOcrData, Line, GetData, GetDataOptions, RectReturn, Words } from '../types.js'
4-
import { adjustElementBbox, getScreenshotSize, isRectanglesObject } from './index.js'
4+
import { adjustElementBbox, getBase64ScreenshotSize, isRectanglesObject } from './index.js'
55
import { drawHighlightedWords, processImage } from './imageProcessing.js'
66

77
const log = logger('@wdio/ocr-service:getData')
@@ -22,7 +22,7 @@ export default async function getData(browser: WebdriverIO.Browser, options: Get
2222
if (!cliFile) {
2323
const screenSize = await browser.getWindowSize()
2424
screenshot = await browser.takeScreenshot()
25-
const { width } = getScreenshotSize(screenshot)
25+
const { width } = getBase64ScreenshotSize(screenshot)
2626
dpr = width / screenSize.width
2727
} else {
2828
screenshot = cliFile

packages/ocr-service/src/utils/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { mkdirSync } from 'node:fs'
33
import type { ChainablePromiseElement } from 'webdriverio'
44
import type { ClickPoint, DetermineClickPointOptions, Rectangles, RectReturn, ScreenshotSize } from '../types.js'
55

6-
export function getScreenshotSize(screenshot: string): ScreenshotSize {
6+
export function getBase64ScreenshotSize(screenshot: string): ScreenshotSize {
77
return {
88
height: Math.round(Buffer.from(screenshot, 'base64').readUInt32BE(20)),
99
width: Math.round(Buffer.from(screenshot, 'base64').readUInt32BE(16)),

packages/ocr-service/tests/utils/__snapshots__/ocrGetData.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ exports[`getData > processes OCR data correctly using a haystack 1`] = `
3737
"Using system installed version of Tesseract",
3838
],
3939
[
40-
"It took '0.000s' to process the image.",
40+
"It took 'X.XXXs' to process the image.",
4141
],
4242
[
4343
"The following text was found through OCR:

packages/ocr-service/tests/utils/index.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { mkdirSync } from 'node:fs'
22
import { describe, it, expect, vi } from 'vitest'
3-
import { adjustElementBbox, createOcrDir, determineClickPoint, getDprPositions, getScreenshotSize, isRectanglesObject } from '../../src/utils/index.js'
3+
import { adjustElementBbox, createOcrDir, determineClickPoint, getDprPositions, getBase64ScreenshotSize, isRectanglesObject } from '../../src/utils/index.js'
44
import type { RectReturn } from '../../src/types.js'
55

66
vi.mock('node:fs', () => ({
@@ -15,26 +15,26 @@ function createMockScreenshot(width: number, height: number): string {
1515
return buffer.toString('base64')
1616
}
1717

18-
describe('getScreenshotSize', () => {
18+
describe('getBase64ScreenshotSize', () => {
1919
it('should correctly extract dimensions from a valid screenshot', () => {
2020
const width = 800
2121
const height = 600
2222
const base64 = createMockScreenshot(width, height)
23-
const result = getScreenshotSize(base64)
23+
const result = getBase64ScreenshotSize(base64)
2424

2525
expect(result).toEqual({ width: width, height: height })
2626
})
2727

2828
it('should handle invalid base64 strings gracefully', () => {
2929
const invalidBase64 = 'not-a-real-base64-string'
30-
const action = () => getScreenshotSize(invalidBase64)
30+
const action = () => getBase64ScreenshotSize(invalidBase64)
3131

3232
expect(action).toThrowError()
3333
})
3434

3535
it('should handle unexpected data layout', () => {
3636
const malformedBase64 = Buffer.from([1, 2, 3, 4, 5]).toString('base64')
37-
const action = () => getScreenshotSize(malformedBase64)
37+
const action = () => getBase64ScreenshotSize(malformedBase64)
3838

3939
expect(action).toThrowError()
4040
})

0 commit comments

Comments
 (0)