Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
83189c6
feat: update types to support igore arrays
wswebcreation Feb 25, 2026
b57d80f
feat: add ignore support for desktop and mobile Android
wswebcreation Feb 25, 2026
830e2b1
fix: add ignored regions to the right place for webscreenshots
wswebcreation Feb 25, 2026
7aabf34
chore: add new LT desktop images
wswebcreation Feb 25, 2026
e07d6c1
chore: new images for android
wswebcreation Feb 26, 2026
e8f617f
chore: adding new images
wswebcreation Feb 26, 2026
bc8d4c4
fix: add appium chrome options for Android
wswebcreation Feb 26, 2026
de463ca
fix: remove double-rounding in viewport offset calculation
wswebcreation Feb 26, 2026
28e8478
chore: remove not used elements
wswebcreation Feb 26, 2026
ea122aa
chore: remove images that need fixes
wswebcreation Feb 26, 2026
9862ef9
test: update tests
wswebcreation Feb 26, 2026
369e7c3
tesT: updated rules
wswebcreation Feb 26, 2026
2745baa
fix: previous fix for iOS reduced multiple ignore selectors to one
wswebcreation Feb 26, 2026
b934de8
fix: enhance fix for stale elements
wswebcreation Feb 26, 2026
e03776e
chore: add comments for the desktop spec
wswebcreation Feb 27, 2026
227f10a
fix: change bidi origin to document
wswebcreation Feb 27, 2026
e02642f
feat: add support for element ignore regions
wswebcreation Feb 27, 2026
f88ac4c
feat: add configurable ignore padding and implement for element scree…
wswebcreation Feb 27, 2026
1178087
tesT: fix bidi test
wswebcreation Feb 27, 2026
db29f22
feat: add element ignore regions for native Android web driver screen…
wswebcreation Feb 28, 2026
40ddd5f
chore: add new Android images for ignore element in element
wswebcreation Mar 1, 2026
8a3675b
fix: fix determining nativeWebScreenshot for Android
wswebcreation Mar 1, 2026
7003c8b
chore: add iOS element ignore image
wswebcreation Mar 1, 2026
b45de3a
test: add new mobile test for ignore element in element
wswebcreation Mar 1, 2026
5b5af95
feat: add ignore element support for fullpage
wswebcreation Mar 1, 2026
2a33d67
chore: add Lt images
wswebcreation Mar 1, 2026
0e9e82c
test: add tests
wswebcreation Mar 1, 2026
536bbf6
feat: support ignore elements for fullpage screenshots
wswebcreation Mar 1, 2026
3ddcc0d
fix: add croptop for fullpage an option
wswebcreation Mar 1, 2026
5e3787c
chore: add new mobile fullpage screenshots
wswebcreation Mar 1, 2026
4e084bf
chore: add android ignore tests
wswebcreation Mar 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports[`BaseClass > initializes default options correctly 1`] = `
"formatImageName": "{tag}-{browserName}-{width}x{height}-dpr-{dpr}",
"fullPageScrollTimeout": 1500,
"hideScrollBars": true,
"ignoreRegionPadding": 1,
"isHybridApp": false,
"savePerInstance": false,
"tabbableOptions": {
Expand Down
7 changes: 7 additions & 0 deletions packages/image-comparison-core/src/base.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ export interface BaseWebScreenshotOptions {
* @default true
*/
hideScrollBars?: boolean;
/**
* Padding in device pixels added to each side of ignore regions (makes each region 2× this value wider and higher).
* Helps avoid 1px boundary differences on high-DPR / BiDi. Set to 0 to disable.
* Applies to screen, element, and full-page web methods.
* @default 1
*/
ignoreRegionPadding?: number;
/**
* Elements to hide before taking screenshot
* @default []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,40 @@ describe('injectWebviewOverlay', () => {
})
})

it('should preserve float precision with non-integer DPR (Android)', () => {
Object.defineProperty(window, 'devicePixelRatio', {
value: 2.625,
configurable: true,
})
Object.defineProperty(window, 'innerWidth', {
value: 412,
configurable: true,
})
Object.defineProperty(document.documentElement, 'clientHeight', {
value: 363,
configurable: true,
})

injectWebviewOverlay(true)

const overlay = document.querySelector('[data-test="ics-overlay"]') as HTMLDivElement
const event = new window.MouseEvent('click', {
clientX: 206,
clientY: 181,
bubbles: true,
})
overlay.dispatchEvent(event)

const parsedData = JSON.parse(overlay.dataset.icsWebviewData!)

expect(parsedData).toEqual({
x: 206 * 2.625,
y: 181 * 2.625,
width: 412 * 2.625,
height: 363 * 2.625,
})
})

it('should use DPR = 1 for iOS (isAndroid = false)', () => {
injectWebviewOverlay(false)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ export function injectWebviewOverlay(isAndroid: boolean): void {
overlay.onclick = (event) => {
const { clientX: x, clientY: y } = event
const data = {
x: Math.round(x * dpr),
y: Math.round(y * dpr),
width: Math.round(window.innerWidth * dpr),
height: Math.round(document.documentElement.clientHeight * dpr),
x: x * dpr,
y: y * dpr,
width: window.innerWidth * dpr,
height: document.documentElement.clientHeight * dpr,
}

overlay.dataset.icsWebviewData = JSON.stringify(data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ describe('scrollElementIntoView', () => {
const result = scrollElementIntoView(mockElement, addressBarShadowPadding)

expect(result).toBe(50)
expect(mockHtmlNode.scrollTop).toBe(90)
// Document y = currentScroll(50) + BCR.top(100) - padding(10) = 140
expect(mockHtmlNode.scrollTop).toBe(140)
})

it('should return current scroll position when body node has scroll', () => {
Expand All @@ -80,7 +81,19 @@ describe('scrollElementIntoView', () => {
const result = scrollElementIntoView(mockElement, addressBarShadowPadding)

expect(result).toBe(50)
expect(mockBodyNode.scrollTop).toBe(90)
// Document y = currentScroll(50) + BCR.top(100) - padding(10) = 140
expect(mockBodyNode.scrollTop).toBe(140)
})

it('should not re-scroll when element is already at the viewport top', () => {
mockHtmlNode.scrollTop = 600
;(mockElement.getBoundingClientRect as ReturnType<typeof vi.fn>).mockReturnValue({ top: 0 })
const addressBarShadowPadding = 0
const result = scrollElementIntoView(mockElement, addressBarShadowPadding)

expect(result).toBe(600)
// Document y = currentScroll(600) + BCR.top(0) - padding(0) = 600 (stays put)
expect(mockHtmlNode.scrollTop).toBe(600)
})

it('should not scroll when neither html nor body is scrollable', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ export default function scrollElementIntoView(element: HTMLElement, addressBarSh
}

const { top } = element.getBoundingClientRect()
const yPosition = top - addressBarShadowPadding
// BCR.top is viewport-relative, so the element's document position is
// currentPosition + top. Scroll there (minus padding) to place the
// element at the viewport top.
const yPosition = currentPosition + top - addressBarShadowPadding

// Scroll to the position
if (htmlNode.scrollHeight > htmlNode.clientHeight) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ exports[`checkFullPageScreen > should execute checkFullPageScreen with basic opt
"hideAfterFirstScroll": [],
"hideElements": [],
"hideScrollBars": true,
"ignore": undefined,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": true,
},
Expand Down Expand Up @@ -328,6 +330,8 @@ exports[`checkFullPageScreen > should handle all full page specific options 1`]
"hideAfterFirstScroll": [],
"hideElements": [],
"hideScrollBars": false,
"ignore": undefined,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": false,
},
Expand Down Expand Up @@ -479,6 +483,8 @@ exports[`checkFullPageScreen > should handle hideAfterFirstScroll correctly 1`]
],
"hideElements": [],
"hideScrollBars": true,
"ignore": undefined,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": true,
},
Expand Down Expand Up @@ -630,6 +636,8 @@ exports[`checkFullPageScreen > should handle hideElements and removeElements cor
},
],
"hideScrollBars": true,
"ignore": undefined,
"ignoreRegionPadding": undefined,
"removeElements": [
{
"elementId": "remove-element",
Expand Down Expand Up @@ -867,6 +875,8 @@ exports[`checkFullPageScreen > should handle undefined method options with fallb
"hideAfterFirstScroll": [],
"hideElements": [],
"hideScrollBars": true,
"ignore": undefined,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ exports[`checkWebElement > should execute checkWebElement with basic options 2`]
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -97,6 +98,7 @@ exports[`checkWebElement > should execute checkWebElement with basic options 2`]
"enableLegacyScreenshotMethod": false,
"hideElements": [],
"hideScrollBars": true,
"ignoreRegionPadding": undefined,
"removeElements": [],
"resizeDimensions": undefined,
"waitForFontsLoaded": true,
Expand Down Expand Up @@ -425,6 +427,7 @@ exports[`checkWebElement > should handle custom element options 1`] = `
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -505,6 +508,7 @@ exports[`checkWebElement > should handle custom element options 1`] = `
},
],
"hideScrollBars": false,
"ignoreRegionPadding": undefined,
"removeElements": [
{
"elementId": "remove-element",
Expand Down Expand Up @@ -764,6 +768,7 @@ exports[`checkWebElement > should handle undefined method options with fallbacks
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -836,6 +841,7 @@ exports[`checkWebElement > should handle undefined method options with fallbacks
"enableLegacyScreenshotMethod": false,
"hideElements": [],
"hideScrollBars": true,
"ignoreRegionPadding": undefined,
"removeElements": [],
"resizeDimensions": undefined,
"waitForFontsLoaded": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ exports[`checkWebScreen > should execute checkWebScreen with basic options 2`] =
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -95,6 +96,7 @@ exports[`checkWebScreen > should execute checkWebScreen with basic options 2`] =
"enableLegacyScreenshotMethod": false,
"hideElements": [],
"hideScrollBars": true,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": true,
},
Expand Down Expand Up @@ -333,6 +335,7 @@ exports[`checkWebScreen > should handle all method options correctly 1`] = `
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -414,6 +417,7 @@ exports[`checkWebScreen > should handle all method options correctly 1`] = `
},
],
"hideScrollBars": false,
"ignoreRegionPadding": undefined,
"removeElements": [
{
"elementId": "remove-element",
Expand Down Expand Up @@ -577,6 +581,7 @@ exports[`checkWebScreen > should handle hideElements and removeElements correctl
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -658,6 +663,7 @@ exports[`checkWebScreen > should handle hideElements and removeElements correctl
},
],
"hideScrollBars": true,
"ignoreRegionPadding": undefined,
"removeElements": [
{
"elementId": "test-element",
Expand Down Expand Up @@ -821,6 +827,7 @@ exports[`checkWebScreen > should handle native context correctly 1`] = `
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -894,6 +901,7 @@ exports[`checkWebScreen > should handle native context correctly 1`] = `
"enableLegacyScreenshotMethod": false,
"hideElements": [],
"hideScrollBars": true,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": true,
},
Expand Down Expand Up @@ -1049,6 +1057,7 @@ exports[`checkWebScreen > should merge compare options correctly 1`] = `
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down Expand Up @@ -1122,6 +1131,7 @@ exports[`checkWebScreen > should merge compare options correctly 1`] = `
"enableLegacyScreenshotMethod": false,
"hideElements": [],
"hideScrollBars": true,
"ignoreRegionPadding": undefined,
"removeElements": [],
"waitForFontsLoaded": true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ exports[`saveElement > should execute saveWebElement when isNativeContext is fal
"baselineFolder": "/test/baseline",
"diffFolder": "/test/diff",
},
"ignore": undefined,
"instanceData": {
"appName": "TestApp",
"browserName": "Chrome",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface CheckMethodOptions extends BaseImageCompareOptions, BaseMobileB
/**
* Ignore elements and or areas
*/
ignore?: ElementIgnore[];
ignore?: (ElementIgnore | ElementIgnore[])[];
}

export interface InternalCheckMethodOptions extends InternalSaveMethodOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default async function checkFullPageScreen(
hideAfterFirstScroll = [],
hideScrollBars,
hideElements = [],
ignoreRegionPadding,
removeElements = [],
waitForFontsLoaded,
} = checkFullPageOptions.method
Expand All @@ -52,11 +53,13 @@ export default async function checkFullPageScreen(
hideAfterFirstScroll,
hideScrollBars,
hideElements,
ignore: checkFullPageOptions.method.ignore,
ignoreRegionPadding,
removeElements,
waitForFontsLoaded,
},
}
const { devicePixelRatio, fileName, base64Image } = await saveFullPageScreen({
const { devicePixelRatio, fileName, base64Image, ignoreRegions } = await saveFullPageScreen({
browserInstance,
folders,
instanceData,
Expand All @@ -73,6 +76,9 @@ export default async function checkFullPageScreen(
methodCompareOptions: compareOptions,
devicePixelRatio,
fileName,
additionalProperties: {
ignoreRegions: ignoreRegions || [],
},
})

// 5. Now execute the compare and return the data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default async function checkWebElement(
enableLegacyScreenshotMethod,
hideScrollBars,
resizeDimensions,
ignoreRegionPadding,
hideElements = [],
removeElements = [],
waitForFontsLoaded = false,
Expand All @@ -45,17 +46,19 @@ export default async function checkWebElement(
enableLegacyScreenshotMethod,
hideScrollBars,
resizeDimensions,
ignoreRegionPadding,
hideElements,
removeElements,
waitForFontsLoaded,
},
}
const { devicePixelRatio, fileName, base64Image } = await saveWebElement({
const { devicePixelRatio, fileName, base64Image, ignoreRegions } = await saveWebElement({
browserInstance,
instanceData,
folders,
element,
tag,
ignore: checkElementOptions.method.ignore,
saveElementOptions,
})

Expand All @@ -68,6 +71,9 @@ export default async function checkWebElement(
devicePixelRatio,
fileName,
isElementScreenshot: true,
additionalProperties: {
ignoreRegions: ignoreRegions || [],
},
})

// 4. Now execute the compare and return the data
Expand Down
Loading
Loading