From c6a154a7f5846493d8bc54194409dd90fb5d9671 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Tue, 9 Sep 2025 15:12:47 +0530 Subject: [PATCH 1/9] changes for passing coordinates in ignore select region --- src/lib/processSnapshot.ts | 52 ++++++++++++++++++++++++++- src/lib/utils.ts | 74 +++++++++++++++++++++++++++++++++++++- src/types.ts | 6 ++-- 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index 82d6ea4..88c08bf 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -1,5 +1,5 @@ import { Snapshot, Context, DiscoveryErrors } from "../types.js"; -import { scrollToBottomAndBackToTop, getRenderViewports, getRenderViewportsForOptions } from "./utils.js" +import { scrollToBottomAndBackToTop, getRenderViewports, getRenderViewportsForOptions, validateCoordinates } from "./utils.js" import { chromium, Locator } from "@playwright/test" import constants from "./constants.js"; import { updateLogContext } from '../lib/logger.js' @@ -126,6 +126,9 @@ export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise case 'cssSelector': selectors.push(...value); break; + case 'coordinates': + selectors.push(...value.map(e => `coordinates=${e}`)); + break; } } } @@ -500,6 +503,9 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): case 'cssSelector': selectors.push(...value); break; + case 'coordinates': + selectors.push(...value.map(e => `coordinates=${e}`)); + break; } } } @@ -663,6 +669,33 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString])) processedOptions[ignoreOrSelectBoxes][viewportString] = [] for (const selector of selectors) { + // Handle coordinates validation + if (selector.startsWith('coordinates=')) { + const coordString = selector.replace('coordinates=', ''); + const viewportSize = await page.viewportSize(); + + if (!viewportSize) { + optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, unable to get viewport size for coordinate validation`); + continue; + } + + const validation = validateCoordinates(coordString, viewportSize, snapshot.name, viewportString); + + + if (!validation.valid) { + optionWarnings.add(validation.error!); + continue; + } + + // Coordinates are valid - create a coordinate element + const coordinateElement = { + type: 'coordinates', + ...validation.coords + }; + locators.push(coordinateElement as any); + continue; + } + let l = await page.locator(selector).all() if (l.length === 0) { optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`); @@ -670,7 +703,22 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): } locators.push(...l); } + for (const locator of locators) { + if (locator && typeof locator === 'object' && locator.hasOwnProperty('type') && (locator as any).type === 'coordinates') { + const coordLocator = locator as any; + const { top, bottom, left, right } = coordLocator; + console.log(`locator: ${JSON.stringify(locator)}`); + // Coordinates already validated, push directly + processedOptions[ignoreOrSelectBoxes][viewportString].push({ + left: left, + top: top, + right: right, + bottom: bottom + }); + continue; + } + let bb = await locator.boundingBox(); if (bb) { // Calculate top and bottom from the bounding box properties @@ -738,3 +786,5 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): discoveryErrors: discoveryErrors } } + + diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 21384e1..5803804 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -509,4 +509,76 @@ export function calculateVariantCountFromSnapshot(snapshot: any, globalConfig?: } return variantCount; -} \ No newline at end of file +} + +export function validateCoordinates( + coordString: string, + viewportSize: { width: number, height: number }, + snapshotName: string, + viewportString: string +): { valid: boolean, error?: string, coords?: { top: number, bottom: number, left: number, right: number } } { + + // Parse coordinates + const coords = coordString.split(',').map(Number); + + // Check format + if (coords.length !== 4) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinates format: ${coordString}. Expected: top,bottom,left,right` + }; + } + + const [top, bottom, left, right] = coords; + + // Check if all values are numbers + if (coords.some(isNaN)) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate values: ${coordString}. All values must be numbers` + }; + } + + // Check coordinate bounds + if (top < 0 || left < 0 || bottom < 0 || right < 0) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate bounds: ${coordString}. top,left,bottom,right must be >= 0` + }; + } + + if (top >= bottom) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate bounds: ${coordString}. top must be < bottom` + }; + } + + if (left >= right) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate bounds: ${coordString}. left must be < right` + }; + } + + // Check viewport bounds + if (bottom > viewportSize.height) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, coordinates exceed viewport bounds: ${coordString}. bottom (${bottom}) exceeds viewport height (${viewportSize.height})` + }; + } + + if (right > viewportSize.width) { + return { + valid: false, + error: `for snapshot ${snapshotName} viewport ${viewportString}, coordinates exceed viewport bounds: ${coordString}. right (${right}) exceeds viewport width (${viewportSize.width})` + }; + } + + // All validations passed + return { + valid: true, + coords: { top, bottom, left, right } + }; +} \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 9637115..4378aad 100644 --- a/src/types.ts +++ b/src/types.ts @@ -122,13 +122,15 @@ export interface Snapshot { id?: Array, class?: Array, cssSelector?: Array, - xpath?: Array + xpath?: Array, + coordinates?: Array }, selectDOM?: { id?: Array, class?: Array, cssSelector?: Array, - xpath?: Array + xpath?: Array, + coordinates?: Array }, element?: { id?: string, From cbe0352b63287fc8863ceee2fd2f48e206cb7466 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Tue, 9 Sep 2025 15:50:56 +0530 Subject: [PATCH 2/9] removed debug log --- src/lib/processSnapshot.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index 88c08bf..ceb8b6f 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -708,7 +708,6 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): if (locator && typeof locator === 'object' && locator.hasOwnProperty('type') && (locator as any).type === 'coordinates') { const coordLocator = locator as any; const { top, bottom, left, right } = coordLocator; - console.log(`locator: ${JSON.stringify(locator)}`); // Coordinates already validated, push directly processedOptions[ignoreOrSelectBoxes][viewportString].push({ left: left, From c1ef20cffae80589486587145ed0d794743eb06b Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Wed, 10 Sep 2025 14:17:15 +0530 Subject: [PATCH 3/9] minor refactor --- src/lib/processSnapshot.ts | 3 --- src/lib/utils.ts | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index ceb8b6f..15a13fd 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -669,7 +669,6 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString])) processedOptions[ignoreOrSelectBoxes][viewportString] = [] for (const selector of selectors) { - // Handle coordinates validation if (selector.startsWith('coordinates=')) { const coordString = selector.replace('coordinates=', ''); const viewportSize = await page.viewportSize(); @@ -687,7 +686,6 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): continue; } - // Coordinates are valid - create a coordinate element const coordinateElement = { type: 'coordinates', ...validation.coords @@ -708,7 +706,6 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): if (locator && typeof locator === 'object' && locator.hasOwnProperty('type') && (locator as any).type === 'coordinates') { const coordLocator = locator as any; const { top, bottom, left, right } = coordLocator; - // Coordinates already validated, push directly processedOptions[ignoreOrSelectBoxes][viewportString].push({ left: left, top: top, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 5803804..572c91a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -518,10 +518,8 @@ export function validateCoordinates( viewportString: string ): { valid: boolean, error?: string, coords?: { top: number, bottom: number, left: number, right: number } } { - // Parse coordinates const coords = coordString.split(',').map(Number); - // Check format if (coords.length !== 4) { return { valid: false, @@ -531,7 +529,6 @@ export function validateCoordinates( const [top, bottom, left, right] = coords; - // Check if all values are numbers if (coords.some(isNaN)) { return { valid: false, @@ -539,7 +536,6 @@ export function validateCoordinates( }; } - // Check coordinate bounds if (top < 0 || left < 0 || bottom < 0 || right < 0) { return { valid: false, @@ -561,7 +557,6 @@ export function validateCoordinates( }; } - // Check viewport bounds if (bottom > viewportSize.height) { return { valid: false, @@ -576,7 +571,6 @@ export function validateCoordinates( }; } - // All validations passed return { valid: true, coords: { top, bottom, left, right } From 4b5da28a88f4b8b1e97ac62fe70ed2174c5d89eb Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Wed, 10 Sep 2025 16:17:30 +0530 Subject: [PATCH 4/9] validation check --- src/lib/schemaValidation.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/schemaValidation.ts b/src/lib/schemaValidation.ts index c6b1fdf..cdfc42d 100644 --- a/src/lib/schemaValidation.ts +++ b/src/lib/schemaValidation.ts @@ -395,6 +395,12 @@ const SnapshotSchema: JSONSchemaType = { uniqueItems: true, errorMessage: "Invalid snapshot options; ignoreDOM xpath array must have unique and non-empty items" }, + coordinates: { + type: "array", + items: { type: "string", minLength: 1 }, + uniqueItems: true, + errorMessage: "Invalid snapshot options; ignoreDOM coordinates array must have unique and non-empty items" + } } }, selectDOM: { @@ -424,6 +430,12 @@ const SnapshotSchema: JSONSchemaType = { uniqueItems: true, errorMessage: "Invalid snapshot options; selectDOM xpath array must have unique and non-empty items" }, + coordinates: { + type: "array", + items: { type: "string", minLength: 1 }, + uniqueItems: true, + errorMessage: "Invalid snapshot options; selectDOM coordinates array must have unique and non-empty items" + } } }, ignoreType: { From 4403f095b6a983e45ab0ec669349e65a50663b6b Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Wed, 10 Sep 2025 19:02:40 +0530 Subject: [PATCH 5/9] changes for page height and throwing warning for more than one viewport size --- src/lib/processSnapshot.ts | 21 ++++++++++++++------- src/lib/utils.ts | 24 ++++++++++++------------ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index 15a13fd..de121c4 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -671,14 +671,21 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): for (const selector of selectors) { if (selector.startsWith('coordinates=')) { const coordString = selector.replace('coordinates=', ''); - const viewportSize = await page.viewportSize(); - - if (!viewportSize) { - optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, unable to get viewport size for coordinate validation`); - continue; + let pageHeight = height; + if (viewport.height) { + pageHeight = viewport.height; + } + const validation = validateCoordinates( + coordString, + pageHeight, + viewport.width, + snapshot.name + ); + + + if(renderViewports.length > 1){ + optionWarnings.add(`for snapshot ${snapshot.name}, coordinates may not be accurate for multiple viewports`); } - - const validation = validateCoordinates(coordString, viewportSize, snapshot.name, viewportString); if (!validation.valid) { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 9979bf6..43f24cb 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -669,9 +669,9 @@ function getPageNumber(screenshotName: string): string { export function validateCoordinates( coordString: string, - viewportSize: { width: number, height: number }, - snapshotName: string, - viewportString: string + pageHeight: number, + pageWidth: number, + snapshotName: string ): { valid: boolean, error?: string, coords?: { top: number, bottom: number, left: number, right: number } } { const coords = coordString.split(',').map(Number); @@ -679,7 +679,7 @@ export function validateCoordinates( if (coords.length !== 4) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinates format: ${coordString}. Expected: top,bottom,left,right` + error: `for snapshot ${snapshotName}, invalid coordinates format: ${coordString}. Expected: top,bottom,left,right` }; } @@ -688,42 +688,42 @@ export function validateCoordinates( if (coords.some(isNaN)) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate values: ${coordString}. All values must be numbers` + error: `for snapshot ${snapshotName}, invalid coordinate values: ${coordString}. All values must be numbers` }; } if (top < 0 || left < 0 || bottom < 0 || right < 0) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate bounds: ${coordString}. top,left,bottom,right must be >= 0` + error: `for snapshot ${snapshotName}, invalid coordinate bounds: ${coordString}. top,left,bottom,right must be >= 0` }; } if (top >= bottom) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate bounds: ${coordString}. top must be < bottom` + error: `for snapshot ${snapshotName}, invalid coordinate bounds: ${coordString}. top must be < bottom` }; } if (left >= right) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, invalid coordinate bounds: ${coordString}. left must be < right` + error: `for snapshot ${snapshotName}, invalid coordinate bounds: ${coordString}. left must be < right` }; } - if (bottom > viewportSize.height) { + if (bottom > pageHeight) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, coordinates exceed viewport bounds: ${coordString}. bottom (${bottom}) exceeds viewport height (${viewportSize.height})` + error: `for snapshot ${snapshotName}, coordinates exceed viewport bounds: ${coordString}. bottom (${bottom}) exceeds viewport height (${pageHeight})` }; } - if (right > viewportSize.width) { + if (right > pageWidth) { return { valid: false, - error: `for snapshot ${snapshotName} viewport ${viewportString}, coordinates exceed viewport bounds: ${coordString}. right (${right}) exceeds viewport width (${viewportSize.width})` + error: `for snapshot ${snapshotName}, coordinates exceed viewport bounds: ${coordString}. right (${right}) exceeds viewport width (${pageWidth})` }; } From e304df086553936621b576655c9d5fdba2919837 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Thu, 11 Sep 2025 14:02:26 +0530 Subject: [PATCH 6/9] changes in warning logs --- src/lib/processSnapshot.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index de121c4..9c7d8f1 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -681,17 +681,15 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): viewport.width, snapshot.name ); - - - if(renderViewports.length > 1){ - optionWarnings.add(`for snapshot ${snapshot.name}, coordinates may not be accurate for multiple viewports`); - } - if (!validation.valid) { optionWarnings.add(validation.error!); continue; } + + if(renderViewports.length > 1){ + optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, coordinates may not be accurate for multiple viewports`); + } const coordinateElement = { type: 'coordinates', From f19751fbc6976a98bfb52977b3ca6414289d187a Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Thu, 11 Sep 2025 14:05:02 +0530 Subject: [PATCH 7/9] minor change --- src/lib/processSnapshot.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index 9c7d8f1..e075a9a 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -684,12 +684,15 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): if (!validation.valid) { optionWarnings.add(validation.error!); - continue; } if(renderViewports.length > 1){ optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, coordinates may not be accurate for multiple viewports`); } + + if(!validation.valid){ + continue; + } const coordinateElement = { type: 'coordinates', From 2815faa9656580049c33f44df86c41bf87e02bf6 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Thu, 11 Sep 2025 14:08:12 +0530 Subject: [PATCH 8/9] revert last change --- src/lib/processSnapshot.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/processSnapshot.ts b/src/lib/processSnapshot.ts index e075a9a..7128fcc 100644 --- a/src/lib/processSnapshot.ts +++ b/src/lib/processSnapshot.ts @@ -684,15 +684,13 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context): if (!validation.valid) { optionWarnings.add(validation.error!); + continue; } if(renderViewports.length > 1){ optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, coordinates may not be accurate for multiple viewports`); } - if(!validation.valid){ - continue; - } const coordinateElement = { type: 'coordinates', From 038de2ccee8482aa0a90846b1836931d50880b69 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Thu, 11 Sep 2025 19:20:51 +0530 Subject: [PATCH 9/9] version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6dc19d..1c2d24a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lambdatest/smartui-cli", - "version": "4.1.29", + "version": "4.1.30", "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest", "files": [ "dist/**/*"