Skip to content

Commit 9363467

Browse files
feat: provide proper dimensions based on emulated screens (#947)
* feat: provide proper dimensions based on emulated screens * fix: fix setting the correct viewport for BiDi this is a tmp fix * chore: updated logic for bidi - updated images - updated test - added test for fullpage * chore: fix tests and update changeset * chore: fix tests and update changeset
1 parent d75f11a commit 9363467

12 files changed

+340
-46
lines changed

.changeset/dull-readers-hammer.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"webdriver-image-comparison": patch
3+
"@wdio/visual-service": patch
4+
---
5+
6+
## 🐛 Bug-fixes
7+
8+
- #946: Visual Regression Changes in WDIO v9
9+
- Fixed screen size detection in emulated mode for filenames. Previously used incorrect browser window size.
10+
- Fixed screenshot behavior when `enableLegacyScreenshotMethod: true`, now correctly captures emulated screen instead of complete screen.
11+
- Fixed emulated device handling for Chrome and Edge browsers, now properly sets device metrics based on `deviceMetrics` or `deviceName` capabilities.
12+
13+
## Committers: 1
14+
15+
- Wim Selles ([@wswebcreation](https://github.com/wswebcreation))
16+

packages/visual-service/src/service.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ export default class WdioImageComparisonService extends BaseClass {
8484
} else {
8585
await this.#extendMultiremoteBrowser(capabilities as Capabilities.RequestedMultiremoteCapabilities)
8686
}
87+
// There is an issue with the emulation mode for Chrome or Edge with WebdriverIO v9
88+
// It doesn't set the correct emulation mode for the browser based on the capabilities
89+
// So we need to set the emulation mode manually
90+
// this is a temporary fix until the issue is fixed in WebdriverIO v9 and enough users have upgraded to the latest version
91+
await this.#setEmulation(this.#browser, capabilities)
8792

8893
/**
8994
* add custom matcher for visual comparison when expect has been added.
@@ -567,4 +572,44 @@ export default class WdioImageComparisonService extends BaseClass {
567572
}
568573
return this._contextManager
569574
}
575+
576+
async #setEmulationForBrowser(browserInstance: WebdriverIO.Browser, capabilities: WebdriverIO.Capabilities) {
577+
if (!browserInstance.isBidi) {
578+
return
579+
}
580+
581+
const chromeMobileEmulation = capabilities['goog:chromeOptions']?.mobileEmulation
582+
const edgeMobileEmulation = capabilities['ms:edgeOptions']?.mobileEmulation
583+
const mobileEmulation = chromeMobileEmulation || edgeMobileEmulation
584+
585+
if (!mobileEmulation) {
586+
return
587+
}
588+
589+
const { deviceName, deviceMetrics } = mobileEmulation
590+
591+
if (deviceName) {
592+
await (browserInstance.emulate as any)('device', deviceName)
593+
return
594+
}
595+
596+
const { pixelRatio: devicePixelRatio = 1, width = 320, height = 658 } = deviceMetrics || {}
597+
await browserInstance.browsingContextSetViewport({
598+
context: await browserInstance.getWindowHandle(),
599+
devicePixelRatio,
600+
viewport: { width, height }
601+
})
602+
}
603+
604+
async #setEmulation(browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser, capabilities: WebdriverIO.Capabilities) {
605+
if (browser.isMultiremote) {
606+
const multiremoteBrowser = browser as WebdriverIO.MultiRemoteBrowser
607+
for (const browserInstance of Object.values(multiremoteBrowser)) {
608+
await this.#setEmulationForBrowser(browserInstance, browserInstance.capabilities)
609+
}
610+
return
611+
}
612+
613+
await this.#setEmulationForBrowser(browser as WebdriverIO.Browser, capabilities)
614+
}
570615
}

packages/webdriver-image-comparison/src/clientSideScripts/getScreenDimensions.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,119 @@ describe('getScreenDimensions', () => {
4242

4343
expect(getScreenDimensions()).toMatchSnapshot()
4444
})
45+
46+
it('should detect mobile emulation and return emulated dimensions', () => {
47+
const mockScreen = {
48+
width: 375,
49+
height: 667
50+
}
51+
const originalScreen = window.screen
52+
53+
Object.defineProperty(window, 'screen', {
54+
value: mockScreen,
55+
configurable: true,
56+
writable: true
57+
})
58+
Object.defineProperty(window, 'devicePixelRatio', { value: 3, configurable: true })
59+
Object.defineProperty(window, 'innerWidth', { value: 375, configurable: true })
60+
Object.defineProperty(window, 'innerHeight', { value: 667, configurable: true })
61+
Object.defineProperty(window, 'outerWidth', { value: 375, configurable: true })
62+
Object.defineProperty(window, 'outerHeight', { value: 667, configurable: true })
63+
Object.defineProperty(window, 'matchMedia', {
64+
value: vi.fn().mockImplementation(() => ({
65+
matches: false,
66+
})),
67+
...CONFIGURABLE,
68+
})
69+
70+
const dimensions = getScreenDimensions()
71+
72+
Object.defineProperty(window, 'screen', {
73+
value: originalScreen,
74+
configurable: true,
75+
writable: true
76+
})
77+
78+
expect(dimensions.dimensions.window.screenWidth).toBe(375)
79+
expect(dimensions.dimensions.window.screenHeight).toBe(667)
80+
expect(dimensions.dimensions.window.outerWidth).toBe(375)
81+
expect(dimensions.dimensions.window.outerHeight).toBe(667)
82+
expect(dimensions.dimensions.window.devicePixelRatio).toBe(3)
83+
})
84+
85+
it('should handle desktop (non-emulated) dimensions correctly', () => {
86+
const mockScreen = {
87+
width: 1920,
88+
height: 1080
89+
}
90+
const originalScreen = window.screen
91+
92+
Object.defineProperty(window, 'screen', {
93+
value: mockScreen,
94+
configurable: true,
95+
writable: true
96+
})
97+
Object.defineProperty(window, 'devicePixelRatio', { value: 1, configurable: true })
98+
Object.defineProperty(window, 'innerWidth', { value: 1440, configurable: true })
99+
Object.defineProperty(window, 'innerHeight', { value: 900, configurable: true })
100+
Object.defineProperty(window, 'outerWidth', { value: 1440, configurable: true })
101+
Object.defineProperty(window, 'outerHeight', { value: 900, configurable: true })
102+
Object.defineProperty(window, 'matchMedia', {
103+
value: vi.fn().mockImplementation(() => ({
104+
matches: true,
105+
})),
106+
...CONFIGURABLE,
107+
})
108+
109+
const dimensions = getScreenDimensions()
110+
111+
Object.defineProperty(window, 'screen', {
112+
value: originalScreen,
113+
configurable: true,
114+
writable: true
115+
})
116+
117+
expect(dimensions.dimensions.window.screenWidth).toBe(1920)
118+
expect(dimensions.dimensions.window.screenHeight).toBe(1080)
119+
expect(dimensions.dimensions.window.outerWidth).toBe(1440)
120+
expect(dimensions.dimensions.window.outerHeight).toBe(900)
121+
expect(dimensions.dimensions.window.devicePixelRatio).toBe(1)
122+
})
123+
124+
it('should handle high DPI desktop displays', () => {
125+
const mockScreen = {
126+
width: 2880,
127+
height: 1800
128+
}
129+
const originalScreen = window.screen
130+
131+
Object.defineProperty(window, 'screen', {
132+
value: mockScreen,
133+
configurable: true,
134+
writable: true
135+
})
136+
Object.defineProperty(window, 'devicePixelRatio', { value: 2, configurable: true })
137+
Object.defineProperty(window, 'innerWidth', { value: 1440, configurable: true })
138+
Object.defineProperty(window, 'innerHeight', { value: 900, configurable: true })
139+
Object.defineProperty(window, 'outerWidth', { value: 1440, configurable: true })
140+
Object.defineProperty(window, 'outerHeight', { value: 900, configurable: true })
141+
Object.defineProperty(window, 'matchMedia', {
142+
value: vi.fn().mockImplementation(() => ({
143+
matches: true,
144+
})),
145+
...CONFIGURABLE,
146+
})
147+
148+
const dimensions = getScreenDimensions()
149+
150+
Object.defineProperty(window, 'screen', {
151+
value: originalScreen,
152+
configurable: true,
153+
writable: true
154+
})
155+
156+
expect(dimensions.dimensions.window.devicePixelRatio).toBe(2)
157+
expect(dimensions.dimensions.window.screenWidth).toBe(2880)
158+
expect(dimensions.dimensions.window.screenHeight).toBe(1800)
159+
})
45160
})

packages/webdriver-image-comparison/src/clientSideScripts/getScreenDimensions.ts

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,93 @@ import type { ScreenDimensions } from './screenDimensions.interfaces.js'
44
* Get all the screen dimensions
55
*/
66
export default function getScreenDimensions(): ScreenDimensions {
7+
// We need to determine if the screen is emulated, because that would return different values
8+
const width = window.innerWidth
9+
const height = window.innerHeight
10+
const dpr = window.devicePixelRatio || 1
11+
const minEdge = Math.min(width, height)
12+
const maxEdge = Math.max(width, height)
13+
const isLikelyEmulated =
14+
dpr >= 2 && // High-DPI signal
15+
minEdge <= 800 && // Catch phones/tablets in portrait/landscape
16+
maxEdge <= 1280 && // Conservative max for emulated tablet sizes
17+
width > 0 && height > 0 // Sanity check
18+
19+
// Other checks
720
const body = document.body
821
const html = document.documentElement
9-
1022
const bodyDimensions = {
23+
// On mobile & desktop: Total scrollable height of the body element, including content not visible on screen
1124
scrollHeight: !body ? 0 : body.scrollHeight,
25+
// On mobile & desktop: Height of body element including padding but not margin
1226
offsetHeight: !body ? 0 : body.offsetHeight,
1327
}
14-
1528
const htmlDimensions = {
29+
/** On mobile & desktop: Viewport height excluding scrollbars */
1630
clientHeight: !html ? 0 : html.clientHeight,
31+
/** On mobile & desktop: Viewport width excluding scrollbars */
1732
clientWidth: !html ? 0 : html.clientWidth,
33+
/** On mobile & desktop: Total scrollable height including overflow */
1834
scrollHeight: !html ? 0 : html.scrollHeight,
35+
/** On mobile & desktop: Total scrollable width including overflow */
1936
scrollWidth: !html ? 0 : html.scrollWidth,
37+
/** On mobile & desktop: Height including padding and border */
2038
offsetHeight: !html ? 0 : html.offsetHeight,
2139
}
2240

2341
const windowDimensions = {
42+
/**
43+
* Mobile: Viewport width (changes with zoom)
44+
* Desktop: Viewport width including scrollbars
45+
*/
2446
innerWidth: window.innerWidth,
47+
/**
48+
* Mobile: Viewport height (changes with zoom)
49+
* Desktop: Viewport height including scrollbars
50+
*/
2551
innerHeight: window.innerHeight,
52+
/**
53+
* Mobile: True if device is in landscape orientation
54+
* Desktop: Based on viewport aspect ratio
55+
*/
2656
isLandscape: window.matchMedia('(orientation: landscape)').matches,
27-
outerHeight: window.outerHeight === 0 ? htmlDimensions.clientHeight : window.outerHeight,
28-
outerWidth: window.outerWidth === 0 ? htmlDimensions.clientWidth : window.outerWidth,
57+
/**
58+
* Mobile: Full browser height including UI elements
59+
* Desktop: Browser window height including toolbars/status bar
60+
* Emulated: It will be the same as window.innerHeight
61+
*/
62+
outerHeight: isLikelyEmulated && window.outerHeight > 0?
63+
window.innerHeight :
64+
window.outerHeight === 0 ?
65+
htmlDimensions.clientHeight :
66+
window.outerHeight,
67+
/**
68+
* Mobile: Full browser width
69+
* Desktop: Browser window width
70+
* Emulated: It will be the same as window.innerWidth
71+
*/
72+
outerWidth: isLikelyEmulated && window.outerWidth > 0 ?
73+
window.innerWidth :
74+
window.outerWidth === 0 ?
75+
htmlDimensions.clientWidth :
76+
window.outerWidth,
77+
/**
78+
* Mobile: Physical pixel ratio (typically >1 for high DPI)
79+
* Desktop: Usually 1, or 2 for high DPI displays
80+
*/
2981
devicePixelRatio: window.devicePixelRatio,
30-
screenWidth: window.screen.width,
31-
screenHeight: window.screen.height,
82+
/**
83+
* Mobile: Physical screen width in CSS pixels
84+
* Desktop: Monitor width in pixels
85+
* Emulated: It will be the same as window.innerWidth
86+
*/
87+
screenWidth: isLikelyEmulated ? window.innerWidth : window.screen.width,
88+
/**
89+
* Mobile: Physical screen height in CSS pixels
90+
* Desktop: Monitor height in pixels
91+
* Emulated: It will be the same as window.innerHeight
92+
*/
93+
screenHeight: isLikelyEmulated ? window.innerHeight : window.screen.height,
3294
}
3395

3496
return {

0 commit comments

Comments
 (0)