Skip to content

Commit 2254617

Browse files
authored
Merge pull request #358 from LambdaTest/stage
Release PR version: `4.1.30`
2 parents eac2d02 + ff4b578 commit 2254617

File tree

5 files changed

+136
-4
lines changed

5 files changed

+136
-4
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lambdatest/smartui-cli",
3-
"version": "4.1.29",
3+
"version": "4.1.30",
44
"description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
55
"files": [
66
"dist/**/*"

src/lib/processSnapshot.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Snapshot, Context, DiscoveryErrors } from "../types.js";
2-
import { scrollToBottomAndBackToTop, getRenderViewports, getRenderViewportsForOptions } from "./utils.js"
2+
import { scrollToBottomAndBackToTop, getRenderViewports, getRenderViewportsForOptions, validateCoordinates } from "./utils.js"
33
import { chromium, Locator } from "@playwright/test"
44
import constants from "./constants.js";
55
import { updateLogContext } from '../lib/logger.js'
@@ -126,6 +126,9 @@ export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise
126126
case 'cssSelector':
127127
selectors.push(...value);
128128
break;
129+
case 'coordinates':
130+
selectors.push(...value.map(e => `coordinates=${e}`));
131+
break;
129132
}
130133
}
131134
}
@@ -500,6 +503,9 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
500503
case 'cssSelector':
501504
selectors.push(...value);
502505
break;
506+
case 'coordinates':
507+
selectors.push(...value.map(e => `coordinates=${e}`));
508+
break;
503509
}
504510
}
505511
}
@@ -663,14 +669,58 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
663669
if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString])) processedOptions[ignoreOrSelectBoxes][viewportString] = []
664670

665671
for (const selector of selectors) {
672+
if (selector.startsWith('coordinates=')) {
673+
const coordString = selector.replace('coordinates=', '');
674+
let pageHeight = height;
675+
if (viewport.height) {
676+
pageHeight = viewport.height;
677+
}
678+
const validation = validateCoordinates(
679+
coordString,
680+
pageHeight,
681+
viewport.width,
682+
snapshot.name
683+
);
684+
685+
if (!validation.valid) {
686+
optionWarnings.add(validation.error!);
687+
continue;
688+
}
689+
690+
if(renderViewports.length > 1){
691+
optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, coordinates may not be accurate for multiple viewports`);
692+
}
693+
694+
695+
const coordinateElement = {
696+
type: 'coordinates',
697+
...validation.coords
698+
};
699+
locators.push(coordinateElement as any);
700+
continue;
701+
}
702+
666703
let l = await page.locator(selector).all()
667704
if (l.length === 0) {
668705
optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
669706
continue;
670707
}
671708
locators.push(...l);
672709
}
710+
673711
for (const locator of locators) {
712+
if (locator && typeof locator === 'object' && locator.hasOwnProperty('type') && (locator as any).type === 'coordinates') {
713+
const coordLocator = locator as any;
714+
const { top, bottom, left, right } = coordLocator;
715+
processedOptions[ignoreOrSelectBoxes][viewportString].push({
716+
left: left,
717+
top: top,
718+
right: right,
719+
bottom: bottom
720+
});
721+
continue;
722+
}
723+
674724
let bb = await locator.boundingBox();
675725
if (bb) {
676726
// Calculate top and bottom from the bounding box properties
@@ -738,3 +788,5 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
738788
discoveryErrors: discoveryErrors
739789
}
740790
}
791+
792+

src/lib/schemaValidation.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,12 @@ const SnapshotSchema: JSONSchemaType<Snapshot> = {
395395
uniqueItems: true,
396396
errorMessage: "Invalid snapshot options; ignoreDOM xpath array must have unique and non-empty items"
397397
},
398+
coordinates: {
399+
type: "array",
400+
items: { type: "string", minLength: 1 },
401+
uniqueItems: true,
402+
errorMessage: "Invalid snapshot options; ignoreDOM coordinates array must have unique and non-empty items"
403+
}
398404
}
399405
},
400406
selectDOM: {
@@ -424,6 +430,12 @@ const SnapshotSchema: JSONSchemaType<Snapshot> = {
424430
uniqueItems: true,
425431
errorMessage: "Invalid snapshot options; selectDOM xpath array must have unique and non-empty items"
426432
},
433+
coordinates: {
434+
type: "array",
435+
items: { type: "string", minLength: 1 },
436+
uniqueItems: true,
437+
errorMessage: "Invalid snapshot options; selectDOM coordinates array must have unique and non-empty items"
438+
}
427439
}
428440
},
429441
ignoreType: {

src/lib/utils.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,4 +665,70 @@ function formatPdfsForOutput(pdfGroups: Record<string, any[]>): any[] {
665665
function getPageNumber(screenshotName: string): string {
666666
const parts = screenshotName.split('#');
667667
return parts.length > 1 ? parts[1] : '1';
668+
}
669+
670+
export function validateCoordinates(
671+
coordString: string,
672+
pageHeight: number,
673+
pageWidth: number,
674+
snapshotName: string
675+
): { valid: boolean, error?: string, coords?: { top: number, bottom: number, left: number, right: number } } {
676+
677+
const coords = coordString.split(',').map(Number);
678+
679+
if (coords.length !== 4) {
680+
return {
681+
valid: false,
682+
error: `for snapshot ${snapshotName}, invalid coordinates format: ${coordString}. Expected: top,bottom,left,right`
683+
};
684+
}
685+
686+
const [top, bottom, left, right] = coords;
687+
688+
if (coords.some(isNaN)) {
689+
return {
690+
valid: false,
691+
error: `for snapshot ${snapshotName}, invalid coordinate values: ${coordString}. All values must be numbers`
692+
};
693+
}
694+
695+
if (top < 0 || left < 0 || bottom < 0 || right < 0) {
696+
return {
697+
valid: false,
698+
error: `for snapshot ${snapshotName}, invalid coordinate bounds: ${coordString}. top,left,bottom,right must be >= 0`
699+
};
700+
}
701+
702+
if (top >= bottom) {
703+
return {
704+
valid: false,
705+
error: `for snapshot ${snapshotName}, invalid coordinate bounds: ${coordString}. top must be < bottom`
706+
};
707+
}
708+
709+
if (left >= right) {
710+
return {
711+
valid: false,
712+
error: `for snapshot ${snapshotName}, invalid coordinate bounds: ${coordString}. left must be < right`
713+
};
714+
}
715+
716+
if (bottom > pageHeight) {
717+
return {
718+
valid: false,
719+
error: `for snapshot ${snapshotName}, coordinates exceed viewport bounds: ${coordString}. bottom (${bottom}) exceeds viewport height (${pageHeight})`
720+
};
721+
}
722+
723+
if (right > pageWidth) {
724+
return {
725+
valid: false,
726+
error: `for snapshot ${snapshotName}, coordinates exceed viewport bounds: ${coordString}. right (${right}) exceeds viewport width (${pageWidth})`
727+
};
728+
}
729+
730+
return {
731+
valid: true,
732+
coords: { top, bottom, left, right }
733+
};
668734
}

src/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,15 @@ export interface Snapshot {
123123
id?: Array<string>,
124124
class?: Array<string>,
125125
cssSelector?: Array<string>,
126-
xpath?: Array<string>
126+
xpath?: Array<string>,
127+
coordinates?: Array<string>
127128
},
128129
selectDOM?: {
129130
id?: Array<string>,
130131
class?: Array<string>,
131132
cssSelector?: Array<string>,
132-
xpath?: Array<string>
133+
xpath?: Array<string>,
134+
coordinates?: Array<string>
133135
},
134136
element?: {
135137
id?: string,

0 commit comments

Comments
 (0)