@wdio/image-comparison-core 1.1.4 → 1.2.1

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 (60) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/base.interfaces.d.ts +7 -0
  3. package/dist/base.interfaces.d.ts.map +1 -1
  4. package/dist/clientSideScripts/injectWebviewOverlay.test.js +29 -0
  5. package/dist/clientSideScripts/scrollElementIntoView.d.ts.map +1 -1
  6. package/dist/clientSideScripts/scrollElementIntoView.js +4 -1
  7. package/dist/clientSideScripts/scrollElementIntoView.test.js +4 -2
  8. package/dist/commands/check.interfaces.d.ts +1 -1
  9. package/dist/commands/check.interfaces.d.ts.map +1 -1
  10. package/dist/commands/checkFullPageScreen.d.ts.map +1 -1
  11. package/dist/commands/checkFullPageScreen.js +7 -2
  12. package/dist/commands/checkWebElement.d.ts.map +1 -1
  13. package/dist/commands/checkWebElement.js +7 -2
  14. package/dist/commands/checkWebScreen.d.ts.map +1 -1
  15. package/dist/commands/checkWebScreen.js +10 -3
  16. package/dist/commands/checkWebScreen.test.js +43 -0
  17. package/dist/commands/fullPage.interfaces.d.ts +6 -0
  18. package/dist/commands/fullPage.interfaces.d.ts.map +1 -1
  19. package/dist/commands/save.interfaces.d.ts +3 -1
  20. package/dist/commands/save.interfaces.d.ts.map +1 -1
  21. package/dist/commands/saveElement.d.ts +1 -1
  22. package/dist/commands/saveElement.d.ts.map +1 -1
  23. package/dist/commands/saveElement.js +2 -2
  24. package/dist/commands/saveFullPageScreen.d.ts.map +1 -1
  25. package/dist/commands/saveFullPageScreen.js +23 -3
  26. package/dist/commands/saveWebElement.d.ts +1 -1
  27. package/dist/commands/saveWebElement.d.ts.map +1 -1
  28. package/dist/commands/saveWebElement.js +24 -2
  29. package/dist/commands/saveWebScreen.d.ts +1 -1
  30. package/dist/commands/saveWebScreen.d.ts.map +1 -1
  31. package/dist/commands/saveWebScreen.js +23 -3
  32. package/dist/helpers/afterScreenshot.interfaces.d.ts +2 -0
  33. package/dist/helpers/afterScreenshot.interfaces.d.ts.map +1 -1
  34. package/dist/helpers/options.d.ts.map +1 -1
  35. package/dist/helpers/options.interfaces.d.ts +10 -0
  36. package/dist/helpers/options.interfaces.d.ts.map +1 -1
  37. package/dist/helpers/options.js +1 -0
  38. package/dist/helpers/utils.d.ts +6 -0
  39. package/dist/helpers/utils.d.ts.map +1 -1
  40. package/dist/helpers/utils.interfaces.d.ts +3 -0
  41. package/dist/helpers/utils.interfaces.d.ts.map +1 -1
  42. package/dist/helpers/utils.js +95 -29
  43. package/dist/helpers/utils.test.js +121 -1
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/methods/images.d.ts.map +1 -1
  47. package/dist/methods/images.interfaces.d.ts +4 -0
  48. package/dist/methods/images.interfaces.d.ts.map +1 -1
  49. package/dist/methods/images.js +4 -2
  50. package/dist/methods/rectangles.d.ts +33 -2
  51. package/dist/methods/rectangles.d.ts.map +1 -1
  52. package/dist/methods/rectangles.interfaces.d.ts +58 -0
  53. package/dist/methods/rectangles.interfaces.d.ts.map +1 -1
  54. package/dist/methods/rectangles.js +289 -15
  55. package/dist/methods/rectangles.test.js +558 -2
  56. package/dist/methods/screenshots.interfaces.d.ts +2 -1
  57. package/dist/methods/screenshots.interfaces.d.ts.map +1 -1
  58. package/dist/methods/takeElementScreenshots.js +30 -19
  59. package/dist/methods/takeElementScreenshots.test.js +49 -22
  60. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"rectangles.d.ts","sourceRoot":"","sources":["../../src/methods/rectangles.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,+BAA+B,EAC/B,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,wBAAwB,EACxB,gBAAgB,EAChB,uBAAuB,EACvB,YAAY,EACZ,8BAA8B,EAC9B,qCAAqC,EACxC,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAEvE;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,EAC7C,eAAe,EACf,WAAW,EACX,OAAO,EACP,OAAO,GACV,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4C/C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAoCjH;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CAAC,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAC;IAClF,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,OAAO,EAAE,qCAAqC,CAAC;CAClD,GAAG,8BAA8B,CAgDjC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,WASvC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,WAS9C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAC,OAAO,EAAE,OAAO,EAAC,MAAM,UAG9D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAC,OAAO,EAAE,GAAG,YAAY,CA4B1D;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,eAAe,EAAE,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAQ/I;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CACxC,eAAe,EAAE,WAAW,CAAC,OAAO,EACpC,OAAO,EAAE,aAAa,EAAE,GACzB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAY7B;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,YAAY,EAAE,EAAE,+BAA+B,+BAwBhI;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,8BAA8B,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAyGxH"}
1
+ {"version":3,"file":"rectangles.d.ts","sourceRoot":"","sources":["../../src/methods/rectangles.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,+BAA+B,EAC/B,wCAAwC,EACxC,sCAAsC,EACtC,uCAAuC,EACvC,gBAAgB,EAChB,iBAAiB,EACjB,8BAA8B,EAC9B,wBAAwB,EACxB,gBAAgB,EAChB,uBAAuB,EACvB,YAAY,EACZ,8BAA8B,EAC9B,qCAAqC,EACxC,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AAEvE;;GAEG;AACH,wBAAsB,0BAA0B,CAAC,EAC7C,eAAe,EACf,WAAW,EACX,OAAO,EACP,OAAO,GACV,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4C/C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAoCjH;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CAAC,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAC;IAClF,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,OAAO,EAAE,qCAAqC,CAAC;CAClD,GAAG,8BAA8B,CAgDjC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,WASvC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,WAS9C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAC,OAAO,EAAE,OAAO,EAAC,MAAM,UAG9D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAC,OAAO,EAAE,GAAG,YAAY,CA4B1D;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,eAAe,EAAE,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAQ/I;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CACxC,eAAe,EAAE,WAAW,CAAC,OAAO,EACpC,OAAO,EAAE,CAAC,aAAa,GAAG,aAAa,EAAE,CAAC,EAAE,GAC7C,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAY7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,+BAA+B,CACjD,OAAO,EAAE,sCAAsC,EAC/C,OAAO,EAAE,CAAC,aAAa,GAAG,aAAa,EAAE,CAAC,EAAE,GAC7C,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA4E7B;AAED;;;;;;GAMG;AACH,wBAAsB,iCAAiC,CACnD,OAAO,EAAE,wCAAwC,EACjD,OAAO,EAAE,CAAC,aAAa,GAAG,aAAa,EAAE,CAAC,EAAE,GAC7C,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA4D7B;AAED;;;;;;;;;GASG;AACH,wBAAsB,gCAAgC,CAClD,OAAO,EAAE,uCAAuC,EAChD,OAAO,EAAE,CAAC,aAAa,GAAG,aAAa,EAAE,CAAC,EAAE,GAC7C,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiF7B;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,YAAY,EAAE,EAAE,+BAA+B,+BAwBhI;AAuDD;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,8BAA8B,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAkIxH"}
@@ -118,6 +118,10 @@ export interface PrepareIgnoreRectanglesOptions {
118
118
  blockOutStatusBar?: boolean;
119
119
  blockOutToolBar?: boolean;
120
120
  };
121
+ /** Whether this is a hybrid (native + webview) app; enables status bar fallback when overlay reports zero */
122
+ isHybridApp?: boolean;
123
+ /** Platform version from the device (e.g. "14.0"); used for Android API level lookup in fallback */
124
+ platformVersion?: string;
121
125
  actualFilePath?: string;
122
126
  }
123
127
  export interface PreparedIgnoreRectangles {
@@ -126,6 +130,60 @@ export interface PreparedIgnoreRectangles {
126
130
  /** Whether any ignore rectangles were found */
127
131
  hasIgnoreRectangles: boolean;
128
132
  }
133
+ export interface DetermineWebScreenIgnoreRegionsOptions {
134
+ /** The browser instance */
135
+ browserInstance: WebdriverIO.Browser;
136
+ /** The device pixel ratio */
137
+ devicePixelRatio: number;
138
+ /** The device rectangles (contains viewport offset for mobile) */
139
+ deviceRectangles: DeviceRectangles;
140
+ /** Whether this is an Android device */
141
+ isAndroid: boolean;
142
+ /** Whether this is an Android native web screenshot */
143
+ isAndroidNativeWebScreenshot: boolean;
144
+ /** Whether this is an iOS device */
145
+ isIOS: boolean;
146
+ /** Padding in device pixels added to each side of computed ignore regions (caller defaults to 1). */
147
+ ignoreRegionPadding: number;
148
+ }
149
+ /**
150
+ * Options for full-page web ignore regions (desktop and mobile).
151
+ * Full-page image is in document coordinates; on mobile scroll-and-stitch the canvas
152
+ * crops off the top addressBarShadowPadding (CSS px), so we subtract that from y.
153
+ */
154
+ export interface DetermineWebFullPageIgnoreRegionsOptions {
155
+ /** The browser instance */
156
+ browserInstance: WebdriverIO.Browser;
157
+ /** The device pixel ratio */
158
+ devicePixelRatio: number;
159
+ /** Padding in device pixels added to each side of computed ignore regions (caller defaults to 1). */
160
+ ignoreRegionPadding: number;
161
+ /**
162
+ * Top crop offset in CSS pixels (e.g. addressBarShadowPadding on mobile full-page).
163
+ * When set, canvas y = (documentY - fullPageCropTopPaddingCSS) × DPR so ignore regions
164
+ * align with the stitched image which crops this much from the top of each tile.
165
+ * @default 0
166
+ */
167
+ fullPageCropTopPaddingCSS?: number;
168
+ }
169
+ export interface DetermineWebElementIgnoreRegionsOptions {
170
+ /** The browser instance */
171
+ browserInstance: WebdriverIO.Browser;
172
+ /** The device pixel ratio */
173
+ devicePixelRatio: number;
174
+ /** The root element being captured in the element screenshot */
175
+ rootElement: WebdriverIO.Element;
176
+ /** Padding in device pixels added to each side of computed ignore regions (caller defaults to 1). */
177
+ ignoreRegionPadding: number;
178
+ /**
179
+ * When both this and isWebDriverElementScreenshot are true, the element image is at CSS pixel size
180
+ * (native driver returns a downscaled image). We then output ignore regions in CSS pixel coordinates
181
+ * by dividing by DPR; otherwise regions are in device pixels.
182
+ */
183
+ isAndroidNativeWebScreenshot?: boolean;
184
+ /** When true, the element screenshot came from the native driver (no fallback crop). */
185
+ isWebDriverElementScreenshot?: boolean;
186
+ }
129
187
  export interface BoundingBox extends BaseBoundingBox {
130
188
  }
131
189
  export interface IgnoreBoxes extends BoundingBox {
@@ -1 +1 @@
1
- {"version":3,"file":"rectangles.interfaces.d.ts","sourceRoot":"","sources":["../../src/methods/rectangles.interfaces.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rectangles.interfaces.d.ts","sourceRoot":"","sources":["../../src/methods/rectangles.interfaces.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { Jimp } from 'jimp';
2
+ import { ANDROID_OFFSETS, IOS_OFFSETS } from '../helpers/constants.js';
2
3
  import { calculateDprData, getBase64ScreenshotSize, isObject } from '../helpers/utils.js';
3
4
  import { getElementPositionAndroid, getElementPositionDesktop, getElementWebviewPosition } from './elementPosition.js';
4
5
  /**
@@ -186,6 +187,218 @@ export async function determineIgnoreRegions(browserInstance, ignores) {
186
187
  height: Math.round(region.height),
187
188
  }));
188
189
  }
190
+ /**
191
+ * Translate ignores to regions for web screen (viewport) screenshots.
192
+ * Uses getBoundingClientRect (CSS pixels) and converts to device pixels,
193
+ * accounting for the viewport offset on native web screenshot devices.
194
+ *
195
+ * Coordinate systems per platform:
196
+ * - Desktop / Android ChromeDriver: screenshot is viewport-only, BCR × DPR
197
+ * - iOS: full-device screenshot, viewport offset is in CSS points → (BCR + offset) × DPR
198
+ * - Android native web: full-device screenshot, viewport offset is already in
199
+ * device pixels (injectWebviewOverlay pre-scales by DPR) → BCR × DPR + offset
200
+ */
201
+ export async function determineWebScreenIgnoreRegions(options, ignores) {
202
+ const awaitedIgnores = await Promise.all(ignores);
203
+ const { elements, regions } = splitIgnores(awaitedIgnores);
204
+ const { browserInstance, devicePixelRatio, deviceRectangles, isAndroid, isAndroidNativeWebScreenshot, isIOS, ignoreRegionPadding: padding } = options;
205
+ // Get raw (unrounded) BCR values so we can multiply by DPR before
206
+ // rounding. The shared getBoundingClientRect script pre-rounds to CSS
207
+ // integers which loses sub-pixel precision that matters at higher DPRs.
208
+ const rawBcr = (el) => {
209
+ const rect = el.getBoundingClientRect();
210
+ return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
211
+ };
212
+ // Browsers can invalidate element references when the DOM is mutated
213
+ // (e.g. by beforeScreenshot CSS/style injection). Re-query via $$ to
214
+ // get fresh refs. Use $$ per unique selector so multiple elements
215
+ // sharing the same selector (e.g. from a $$ call) each resolve to
216
+ // the correct match by index.
217
+ const regionsFromElements = [];
218
+ const selectorCache = new Map();
219
+ const selectorIndex = new Map();
220
+ for (const element of elements) {
221
+ const selector = element.selector;
222
+ if (!selectorCache.has(selector)) {
223
+ const fresh = await browserInstance.$$(selector);
224
+ selectorCache.set(selector, fresh);
225
+ selectorIndex.set(selector, 0);
226
+ }
227
+ const idx = selectorIndex.get(selector);
228
+ const cached = selectorCache.get(selector);
229
+ const el = idx < cached.length ? cached[idx] : element;
230
+ selectorIndex.set(selector, idx + 1);
231
+ const bcr = await browserInstance.execute(rawBcr, el);
232
+ regionsFromElements.push(bcr);
233
+ }
234
+ return [...regions, ...regionsFromElements]
235
+ .map((region) => {
236
+ // Use floor for top-left and ceil for bottom-right so the
237
+ // device-pixel rectangle fully covers the CSS-pixel element.
238
+ // Rounding position and size independently can miss edge pixels.
239
+ let cssX = region.x;
240
+ let cssY = region.y;
241
+ if (isIOS) {
242
+ cssX += deviceRectangles.viewport.x;
243
+ cssY += deviceRectangles.viewport.y;
244
+ }
245
+ const left = Math.floor(cssX * devicePixelRatio);
246
+ const top = Math.floor(cssY * devicePixelRatio);
247
+ const right = Math.ceil((cssX + region.width) * devicePixelRatio);
248
+ const bottom = Math.ceil((cssY + region.height) * devicePixelRatio);
249
+ let x = left;
250
+ let y = top;
251
+ if (isAndroid && isAndroidNativeWebScreenshot) {
252
+ // Android native web viewport offset is already in device pixels
253
+ x += deviceRectangles.viewport.x;
254
+ y += deviceRectangles.viewport.y;
255
+ }
256
+ let width = right - left;
257
+ let height = bottom - top;
258
+ if (padding > 0) {
259
+ x = Math.max(0, x - padding);
260
+ y = Math.max(0, y - padding);
261
+ width += 2 * padding;
262
+ height += 2 * padding;
263
+ }
264
+ return { x, y, width, height };
265
+ });
266
+ }
267
+ /**
268
+ * Translate ignores to regions for web full-page screenshots (desktop and mobile).
269
+ * Full-page image (BiDi or scroll-and-stitch) is in document coordinates: (0,0) = top-left
270
+ * of document, device pixels. Uses getBoundingClientRect + (scrollX, scrollY) for elements,
271
+ * then converts to device pixels. Same logic for all platforms; no viewport offset needed
272
+ * because the stitched canvas is built in document space.
273
+ */
274
+ export async function determineWebFullPageIgnoreRegions(options, ignores) {
275
+ const awaitedIgnores = await Promise.all(ignores);
276
+ const { elements, regions } = splitIgnores(awaitedIgnores);
277
+ const { browserInstance, devicePixelRatio, ignoreRegionPadding: padding, fullPageCropTopPaddingCSS: cropTop = 0 } = options;
278
+ const rawDocumentBcr = (el) => {
279
+ const rect = el.getBoundingClientRect();
280
+ return {
281
+ x: rect.x + window.scrollX,
282
+ y: rect.y + window.scrollY,
283
+ width: rect.width,
284
+ height: rect.height,
285
+ };
286
+ };
287
+ const regionsFromElements = [];
288
+ const selectorCache = new Map();
289
+ const selectorIndex = new Map();
290
+ for (const element of elements) {
291
+ const selector = element.selector;
292
+ if (!selectorCache.has(selector)) {
293
+ const fresh = await browserInstance.$$(selector);
294
+ selectorCache.set(selector, fresh);
295
+ selectorIndex.set(selector, 0);
296
+ }
297
+ const idx = selectorIndex.get(selector);
298
+ const cached = selectorCache.get(selector);
299
+ const el = idx < cached.length ? cached[idx] : element;
300
+ selectorIndex.set(selector, idx + 1);
301
+ const bcr = await browserInstance.execute(rawDocumentBcr, el);
302
+ regionsFromElements.push(bcr);
303
+ }
304
+ return [...regions, ...regionsFromElements]
305
+ .map((region) => {
306
+ const left = Math.floor(region.x * devicePixelRatio);
307
+ const right = Math.ceil((region.x + region.width) * devicePixelRatio);
308
+ // On mobile full-page scroll-and-stitch, the canvas crops cropTop (e.g. 6px) from the top
309
+ // of each tile, so canvas y = (documentY - cropTop) × DPR
310
+ const topDevice = Math.floor((region.y - cropTop) * devicePixelRatio);
311
+ const bottomDevice = Math.ceil((region.y + region.height - cropTop) * devicePixelRatio);
312
+ const top = Math.max(0, topDevice);
313
+ const bottom = Math.max(top, bottomDevice);
314
+ let x = left;
315
+ let y = top;
316
+ let width = right - left;
317
+ let height = bottom - top;
318
+ if (padding > 0) {
319
+ x = Math.max(0, x - padding);
320
+ y = Math.max(0, y - padding);
321
+ width += 2 * padding;
322
+ height += 2 * padding;
323
+ }
324
+ return { x, y, width, height };
325
+ });
326
+ }
327
+ /**
328
+ * Translate ignores to regions for web element screenshots.
329
+ * By default regions are in *element-local* device pixels so they match the cropped element image
330
+ * (BiDi clip or fallback full-screenshot crop, both at device pixel size).
331
+ * Exception: when the element screenshot is from the native driver on Android native web
332
+ * (isWebDriverElementScreenshot && isAndroidNativeWebScreenshot), the driver returns an image at
333
+ * CSS pixel size (downscaled). We then output regions in CSS pixel coordinates (divide by DPR)
334
+ * so they align with that image. Fallback (full screenshot + crop) is at device size, so we do
335
+ * not downscale when fallback was used.
336
+ */
337
+ export async function determineWebElementIgnoreRegions(options, ignores) {
338
+ const awaitedIgnores = await Promise.all(ignores);
339
+ const { elements, regions } = splitIgnores(awaitedIgnores);
340
+ const { browserInstance, devicePixelRatio, rootElement, ignoreRegionPadding: padding, isAndroidNativeWebScreenshot, isWebDriverElementScreenshot, } = options;
341
+ // Compute bounding boxes relative to the root element: (childBCR - rootBCR)
342
+ const rawRelativeBcr = (el, root) => {
343
+ const elRect = el.getBoundingClientRect();
344
+ const rootRect = root.getBoundingClientRect();
345
+ return {
346
+ x: elRect.x - rootRect.x,
347
+ y: elRect.y - rootRect.y,
348
+ width: elRect.width,
349
+ height: elRect.height,
350
+ };
351
+ };
352
+ const regionsFromElements = [];
353
+ const selectorCache = new Map();
354
+ const selectorIndex = new Map();
355
+ for (const element of elements) {
356
+ const selector = element.selector;
357
+ if (!selectorCache.has(selector)) {
358
+ const fresh = await browserInstance.$$(selector);
359
+ selectorCache.set(selector, fresh);
360
+ selectorIndex.set(selector, 0);
361
+ }
362
+ const idx = selectorIndex.get(selector);
363
+ const cached = selectorCache.get(selector);
364
+ const el = idx < cached.length ? cached[idx] : element;
365
+ selectorIndex.set(selector, idx + 1);
366
+ const bcr = await browserInstance.execute(rawRelativeBcr, el, rootElement);
367
+ regionsFromElements.push(bcr);
368
+ }
369
+ // Both literal regions and element-derived regions are currently expected
370
+ // to be in CSS pixels relative to the element. Scale everything by DPR and
371
+ // express as device-pixel rectangles using the same floor-based rounding
372
+ // strategy as the BiDi element clip (x/y/width/height all floored).
373
+ // Then expand each region by ignoreRegionPadding on each side (configurable, default 1)
374
+ // to reduce 1px boundary differences on high-DPR / BiDi.
375
+ let result = [...regions, ...regionsFromElements]
376
+ .map((region) => {
377
+ let x = Math.floor(region.x * devicePixelRatio);
378
+ let y = Math.floor(region.y * devicePixelRatio);
379
+ let width = Math.floor(region.width * devicePixelRatio);
380
+ let height = Math.floor(region.height * devicePixelRatio);
381
+ if (padding > 0) {
382
+ x = Math.max(0, x - padding);
383
+ y = Math.max(0, y - padding);
384
+ width += 2 * padding;
385
+ height += 2 * padding;
386
+ }
387
+ return { x, y, width, height };
388
+ });
389
+ // Only downscale when the element image is at CSS pixel size: native driver element screenshot
390
+ // on Android native web (fallback false). Fallback uses a device-pixel crop, so no downscale.
391
+ if (isAndroidNativeWebScreenshot === true && isWebDriverElementScreenshot === true && devicePixelRatio > 0) {
392
+ const dpr = devicePixelRatio;
393
+ result = result.map((r) => ({
394
+ x: Math.round(r.x / dpr),
395
+ y: Math.round(r.y / dpr),
396
+ width: Math.round(r.width / dpr),
397
+ height: Math.round(r.height / dpr),
398
+ }));
399
+ }
400
+ return result;
401
+ }
189
402
  /**
190
403
  * Determine the device block outs
191
404
  */
@@ -211,11 +424,55 @@ export async function determineDeviceBlockOuts({ isAndroid, screenCompareOptions
211
424
  // }
212
425
  return rectangles;
213
426
  }
427
+ /**
428
+ * Return a status bar rectangle for hybrid-app fallback when the overlay reports zero height.
429
+ * Uses IOS_OFFSETS / ANDROID_OFFSETS so the system status bar is blocked out in webview context.
430
+ * Android: uses device platformVersion (e.g. "14.0") as API level when present and in list; otherwise latest.
431
+ * iOS: keyed by screen size only (no OS version in data). When device not in list, uses latest entry.
432
+ */
433
+ function getHybridAppStatusBarFallback(deviceRectangles, isAndroid, platformVersion) {
434
+ const { width: screenWidth, height: screenHeight } = deviceRectangles.screenSize;
435
+ if (screenWidth === 0 || screenHeight === 0) {
436
+ return null;
437
+ }
438
+ if (isAndroid) {
439
+ const apiLevels = Object.keys(ANDROID_OFFSETS).map(Number);
440
+ const latestApiLevel = apiLevels.length > 0 ? Math.max(...apiLevels) : 14;
441
+ const deviceApiLevel = platformVersion !== undefined ? parseInt(platformVersion, 10) : NaN;
442
+ const useApiLevel = Number.isInteger(deviceApiLevel) && apiLevels.includes(deviceApiLevel)
443
+ ? deviceApiLevel
444
+ : latestApiLevel;
445
+ const statusBarHeight = ANDROID_OFFSETS[useApiLevel]?.STATUS_BAR ?? 24;
446
+ return {
447
+ x: 0,
448
+ y: 0,
449
+ width: screenWidth,
450
+ height: statusBarHeight,
451
+ };
452
+ }
453
+ const isIphone = screenWidth < 1024 && screenHeight < 1024;
454
+ const deviceType = isIphone ? 'IPHONE' : 'IPAD';
455
+ const portraitHeight = screenWidth > screenHeight ? screenWidth : screenHeight;
456
+ const keys = Object.keys(IOS_OFFSETS[deviceType]).map(Number);
457
+ const exactMatch = keys.includes(portraitHeight);
458
+ const offsetPortraitHeight = exactMatch
459
+ ? portraitHeight
460
+ : (keys.length > 0 ? Math.max(...keys) : (isIphone ? 667 : 1024));
461
+ const orientation = screenWidth > screenHeight ? 'LANDSCAPE' : 'PORTRAIT';
462
+ const currentOffsets = IOS_OFFSETS[deviceType][offsetPortraitHeight]?.[orientation];
463
+ const statusBarHeight = currentOffsets?.STATUS_BAR ?? (isIphone ? 44 : 20);
464
+ return {
465
+ x: 0,
466
+ y: 0,
467
+ width: screenWidth,
468
+ height: statusBarHeight,
469
+ };
470
+ }
214
471
  /**
215
472
  * Prepare all ignore rectangles for image comparison
216
473
  */
217
474
  export async function prepareIgnoreRectangles(options) {
218
- const { blockOut, ignoreRegions, deviceRectangles, devicePixelRatio, isMobile, isNativeContext, isAndroid, isAndroidNativeWebScreenshot, isViewPortScreenshot, imageCompareOptions, actualFilePath } = options;
475
+ const { blockOut, ignoreRegions, deviceRectangles, devicePixelRatio, isMobile, isNativeContext, isAndroid, isAndroidNativeWebScreenshot, isViewPortScreenshot, imageCompareOptions, isHybridApp, platformVersion, actualFilePath } = options;
219
476
  // Get blockOut rectangles
220
477
  let webStatusAddressToolBarOptions = [];
221
478
  // Handle mobile web status/address/toolbar rectangles
@@ -230,6 +487,16 @@ export async function prepareIgnoreRectangles(options) {
230
487
  isViewPortScreenshot,
231
488
  };
232
489
  webStatusAddressToolBarOptions.push(...(determineStatusAddressToolBarRectangles({ deviceRectangles, options: statusAddressToolBarOptions })) || []);
490
+ // Hybrid-app fallback: in webview the overlay often reports statusBarAndAddressBar height 0.
491
+ // Use native offsets (IOS_OFFSETS / ANDROID_OFFSETS) so the system status bar is still blocked out.
492
+ const needStatusBarFallback = imageCompareOptions.blockOutStatusBar !== false &&
493
+ (isHybridApp === true || deviceRectangles.statusBarAndAddressBar.height === 0);
494
+ if (needStatusBarFallback) {
495
+ const fallback = getHybridAppStatusBarFallback(deviceRectangles, isAndroid, platformVersion);
496
+ if (fallback && fallback.height > 0) {
497
+ webStatusAddressToolBarOptions.push(fallback);
498
+ }
499
+ }
233
500
  if (webStatusAddressToolBarOptions.length > 0) {
234
501
  // There's an issue with the resemble lib when all the rectangles are 0,0,0,0, it will see this as a full
235
502
  // blockout of the image and the comparison will succeed with 0 % difference.
@@ -268,29 +535,36 @@ export async function prepareIgnoreRectangles(options) {
268
535
  }
269
536
  }
270
537
  }
271
- // Combine all ignore regions
272
- const ignoredBoxes = [
273
- // These come from the method
538
+ // blockOut and device bar rectangles are in CSS pixels, scale by DPR
539
+ const dprScaledBoxes = [
274
540
  ...blockOut,
275
- // @TODO: I'm defaulting ignore regions for devices
276
- // Need to check if this is the right thing to do for web and mobile browser tests
277
- ...ignoreRegions,
278
- // Only get info about the status bars when we are in the web context
279
541
  ...webStatusAddressToolBarOptions
280
542
  ]
281
- .map(
282
- // Make sure all the rectangles are equal to the dpr for the screenshot
283
- (rectangles) => {
543
+ .map((rectangles) => {
284
544
  return calculateDprData({
285
- // Adjust for the ResembleJS API
286
545
  bottom: rectangles.y + rectangles.height,
287
546
  right: rectangles.x + rectangles.width,
288
547
  left: rectangles.x,
289
548
  top: rectangles.y,
290
- },
291
- // For Android we don't need to do it times the pixel ratio, for all others we need to
292
- isAndroid ? 1 : devicePixelRatio);
549
+ }, isAndroid ? 1 : devicePixelRatio);
550
+ });
551
+ // ignoreRegions: for web they are already in device pixels (pre-scaled by the caller).
552
+ // For native iOS app they are in logical pixels (getElementRect / statusBar/homeBar),
553
+ // so we scale by DPR here to match the device-pixel screenshot.
554
+ const isNativeIos = isNativeContext && isMobile && !isAndroid;
555
+ const preScaledIgnoreBoxes = ignoreRegions.map((rectangles) => {
556
+ const box = {
557
+ left: rectangles.x,
558
+ top: rectangles.y,
559
+ right: rectangles.x + rectangles.width,
560
+ bottom: rectangles.y + rectangles.height,
561
+ };
562
+ if (isNativeIos) {
563
+ return calculateDprData({ ...box }, devicePixelRatio);
564
+ }
565
+ return box;
293
566
  });
567
+ const ignoredBoxes = [...dprScaledBoxes, ...preScaledIgnoreBoxes];
294
568
  return {
295
569
  ignoredBoxes,
296
570
  hasIgnoreRectangles: ignoredBoxes.length > 0