Skip to content

Commit d0a53d6

Browse files
Merge pull request #457 from LambdaTest/stage
Release PR: new scroll mechanism
2 parents 4f9deea + 94e96c4 commit d0a53d6

File tree

6 files changed

+127
-4
lines changed

6 files changed

+127
-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.51",
3+
"version": "4.1.52",
44
"description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
55
"files": [
66
"dist/**/*"

src/lib/ctx.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Context, Env, WebConfig, MobileConfig, basicAuth, tunnelConfig } from '../types.js'
1+
import { Context, Env, WebConfig, MobileConfig, basicAuth, tunnelConfig, lazyLoadConfig } from '../types.js'
22
import constants from './constants.js'
33
import { version } from '../../package.json'
44
import { validateConfig, validateConfigForScheduled } from './schemaValidation.js'
@@ -13,6 +13,7 @@ export default (options: Record<string, string>): Context => {
1313
let webConfig: WebConfig;
1414
let mobileConfig: MobileConfig;
1515
let basicAuthObj: basicAuth
16+
let lazyLoadConfigObj: lazyLoadConfig
1617
let tunnelObj: tunnelConfig
1718
let config = constants.DEFAULT_CONFIG;
1819
let port: number;
@@ -123,6 +124,9 @@ export default (options: Record<string, string>): Context => {
123124
if (config.basicAuthorization) {
124125
basicAuthObj = config.basicAuthorization;
125126
}
127+
if (config.lazyLoadConfiguration) {
128+
lazyLoadConfigObj = config.lazyLoadConfiguration;
129+
}
126130
if (config.tunnel) {
127131
tunnelObj = config.tunnel;
128132
}
@@ -163,6 +167,7 @@ export default (options: Record<string, string>): Context => {
163167
allowedHostnames: config.allowedHostnames || [],
164168
allowedAssets: config.allowedAssets || [],
165169
basicAuthorization: basicAuthObj,
170+
lazyLoadConfiguration: lazyLoadConfigObj,
166171
smartIgnore: config.smartIgnore ?? false,
167172
delayedUpload: config.delayedUpload ?? false,
168173
useGlobalCache: config.useGlobalCache ?? false,

src/lib/processSnapshot.ts

Lines changed: 44 additions & 2 deletions
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, validateCoordinates, resolveCustomCSS, parseCSSFile, validateCSSSelectors, generateCSSInjectionReport } from "./utils.js"
2+
import { scrollToBottomAndBackToTop, smoothScrollToBottom, getRenderViewports, getRenderViewportsForOptions, validateCoordinates, resolveCustomCSS, parseCSSFile, validateCSSSelectors, generateCSSInjectionReport } from "./utils.js"
33
import { chromium, Locator } from "@playwright/test"
44
import constants from "./constants.js";
55
import { updateLogContext } from '../lib/logger.js'
@@ -169,6 +169,21 @@ export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise
169169
processedOptions.useExtendedViewport = true;
170170
}
171171

172+
if (ctx.config.lazyLoadConfiguration && ctx.config.lazyLoadConfiguration.enabled) {
173+
let stepValue = ctx.config.lazyLoadConfiguration.scrollStep || 250;
174+
let delayValue = ctx.config.lazyLoadConfiguration.scrollDelay || 100;
175+
let maxScrollsValue = ctx.config.lazyLoadConfiguration.maxScrolls || 50;
176+
let jumpBackToTopValue = ctx.config.lazyLoadConfiguration.jumpBackToTop !== false;
177+
//Add this in processed options inside lazyLoadConfiguration key
178+
processedOptions.lazyLoadConfiguration = {
179+
enabled: true,
180+
scrollStep: stepValue,
181+
scrollDelay: delayValue,
182+
maxScrolls: maxScrollsValue,
183+
jumpBackToTop: jumpBackToTopValue
184+
};
185+
}
186+
172187
try {
173188
if (options?.customCSS) {
174189
const resolvedCSS = resolveCustomCSS(options.customCSS, '', ctx.log);
@@ -654,6 +669,21 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
654669
renderViewports = getRenderViewports(ctx);
655670
}
656671

672+
if (ctx.config.lazyLoadConfiguration && ctx.config.lazyLoadConfiguration.enabled) {
673+
let stepValue = ctx.config.lazyLoadConfiguration.scrollStep || 250;
674+
let delayValue = ctx.config.lazyLoadConfiguration.scrollDelay || 100;
675+
let maxScrollsValue = ctx.config.lazyLoadConfiguration.maxScrolls || 50;
676+
let jumpBackToTopValue = ctx.config.lazyLoadConfiguration.jumpBackToTop || false;
677+
//Add this in processed options inside lazyLoadConfiguration key
678+
processedOptions.lazyLoadConfiguration = {
679+
enabled: true,
680+
scrollStep: stepValue,
681+
scrollDelay: delayValue,
682+
maxScrolls: maxScrollsValue,
683+
jumpBackToTop: jumpBackToTopValue
684+
};
685+
}
686+
657687
for (const { viewport, viewportString, fullPage, device } of renderViewports) {
658688

659689
// Check if this is the first iteration or if the device type has changed from the previous iteration
@@ -707,7 +737,19 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
707737
}
708738

709739
}
710-
if (ctx.config.cliEnableJavaScript && fullPage) await page.evaluate(scrollToBottomAndBackToTop, { frequency: 100, timing: ctx.config.scrollTime });
740+
if (ctx.config.cliEnableJavaScript && fullPage) {
741+
if (ctx.config.lazyLoadConfiguration && ctx.config.lazyLoadConfiguration.enabled) {
742+
let stepValue = ctx.config.lazyLoadConfiguration.scrollStep || 250;
743+
let delayValue = ctx.config.lazyLoadConfiguration.scrollDelay || 300;
744+
let maxScrollsValue = ctx.config.lazyLoadConfiguration.maxScrolls || 50;
745+
let jumpBackToTopValue = ctx.config.lazyLoadConfiguration.jumpBackToTop !== false;
746+
ctx.log.debug('Starting lazy load scrolling with configuration: ' + JSON.stringify({ step: stepValue, delay: delayValue, maxScrolls: maxScrollsValue, jumpBackToTop: jumpBackToTopValue }));
747+
await page.evaluate(smoothScrollToBottom, { step: stepValue, delay: delayValue, maxScrolls: maxScrollsValue, jumpBackToTop: jumpBackToTopValue });
748+
ctx.log.debug('Completed lazy load scrolling');
749+
} else {
750+
await page.evaluate(scrollToBottomAndBackToTop, { frequency: 100, timing: ctx.config.scrollTime });
751+
}
752+
}
711753

712754
try {
713755
await page.waitForLoadState('networkidle', { timeout: 15000 });

src/lib/schemaValidation.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,39 @@ const ConfigSchema = {
175175
},
176176
}
177177
},
178+
lazyLoadConfiguration: {
179+
type: "object",
180+
properties: {
181+
enabled: {
182+
type: "boolean",
183+
errorMessage: "Invalid config; lazyLoad enabled must be true/false"
184+
},
185+
scrollStep: {
186+
type: "number",
187+
minimum: 50,
188+
maximum: 2000,
189+
errorMessage: "Invalid config; lazyLoad scrollStep must be > 50 and <= 2000"
190+
},
191+
scrollDelay: {
192+
type: "number",
193+
minimum: 100,
194+
maximum: 5000,
195+
errorMessage: "Invalid config; lazyLoad scrollDelay must be > 100 and <= 5000"
196+
},
197+
maxScrolls: {
198+
type: "number",
199+
minimum: 1,
200+
maximum: 100,
201+
errorMessage: "Invalid config; lazyLoad maxScrolls must be > 1 and <= 100"
202+
},
203+
jumpBackToTop: {
204+
type: "boolean",
205+
errorMessage: "Invalid config; lazyLoad jumpBackToTop must be true/false"
206+
}
207+
},
208+
required: ["enabled"],
209+
additionalProperties: false
210+
},
178211
delayedUpload: {
179212
type: "boolean",
180213
errorMessage: "Invalid config; delayedUpload must be true/false"

src/lib/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,40 @@ export function scrollToBottomAndBackToTop({
5858
});
5959
}
6060

61+
export function smoothScrollToBottom({
62+
step = 250,
63+
delay = 300,
64+
maxScrolls = 50,
65+
jumpBackToTop = true
66+
} = {}): Promise<void> {
67+
return new Promise((resolve) => {
68+
let totalHeight = document.body.scrollHeight;
69+
let currentScroll = window.scrollY;
70+
let scrollCount = 0;
71+
72+
function scroll() {
73+
if (currentScroll + window.innerHeight >= totalHeight || scrollCount >= maxScrolls) {
74+
if (jumpBackToTop) {
75+
window.scrollTo(0, 0);
76+
}
77+
resolve();
78+
return;
79+
}
80+
81+
window.scrollBy(0, step);
82+
scrollCount++;
83+
84+
setTimeout(() => {
85+
currentScroll = window.scrollY;
86+
totalHeight = document.body.scrollHeight;
87+
scroll();
88+
}, delay);
89+
}
90+
91+
scroll();
92+
});
93+
}
94+
6195
export async function launchBrowsers(ctx: Context): Promise<Record<string, Browser>> {
6296
let browsers: Record<string, Browser> = {};
6397
const isHeadless = process.env.HEADLESS?.toLowerCase() === 'false' ? false : true;

src/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface Context {
2727
allowedHostnames: Array<string>;
2828
allowedAssets: Array<string>;
2929
basicAuthorization: basicAuth | undefined;
30+
lazyLoadConfiguration: lazyLoadConfig | undefined;
3031
smartIgnore: boolean;
3132
delayedUpload: boolean;
3233
useGlobalCache: boolean;
@@ -255,6 +256,14 @@ export interface basicAuth {
255256
password: string;
256257
}
257258

259+
export interface lazyLoadConfig {
260+
enabled: boolean;
261+
scrollStep: number;
262+
scrollDelay: number;
263+
maxScrolls: number;
264+
jumpBackToTop: boolean;
265+
}
266+
258267
export interface tunnelConfig {
259268
type: string;
260269
tunnelName: string;

0 commit comments

Comments
 (0)