Skip to content

Commit 183b544

Browse files
Merge pull request #464 from sushobhit-lt/TE-3906
Add debug info to snapshot timeout error
2 parents 857afa3 + 310298c commit 183b544

File tree

3 files changed

+61
-61
lines changed

3 files changed

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

src/lib/processSnapshot.ts

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { scrollToBottomAndBackToTop, smoothScrollToBottom, getRenderViewports, g
33
import { chromium, Locator } from "@playwright/test"
44
import constants from "./constants.js";
55
import { updateLogContext } from '../lib/logger.js'
6-
import NodeCache from 'node-cache';
6+
import NodeCache from 'node-cache';
77
import chalk from "chalk";
88

99
const globalCache = new NodeCache({ stdTTL: 3600, checkperiod: 600 });
@@ -16,14 +16,14 @@ const MAX_WAIT_FOR_REQUEST_CALL = 30000;
1616

1717
const normalizeSameSite = (value) => {
1818
if (!value) return 'Lax';
19-
19+
2020
const normalized = value.trim().toLowerCase();
2121
const mapping = {
2222
'lax': 'Lax',
2323
'strict': 'Strict',
2424
'none': 'None'
2525
};
26-
26+
2727
return mapping[normalized] || value;
2828
};
2929

@@ -149,7 +149,7 @@ export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise
149149
}
150150
}
151151
}
152-
if(options.ignoreType){
152+
if (options.ignoreType) {
153153
processedOptions.ignoreType = options.ignoreType;
154154
}
155155
}
@@ -206,7 +206,7 @@ export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise
206206
ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`);
207207

208208
let renderViewports;
209-
if((snapshot.options && snapshot.options.web) || (snapshot.options && snapshot.options.mobile)){
209+
if ((snapshot.options && snapshot.options.web) || (snapshot.options && snapshot.options.mobile)) {
210210
renderViewports = getRenderViewportsForOptions(snapshot.options)
211211
} else {
212212
renderViewports = getRenderViewports(ctx);
@@ -237,7 +237,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
237237
timestamp: "",
238238
snapshotUUID: "",
239239
browsers: {}
240-
};
240+
};
241241

242242
let processedOptions: Record<string, any> = {};
243243
if (ctx.config.requestHeaders && Array.isArray(ctx.config.requestHeaders)) {
@@ -297,13 +297,13 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
297297
// These custom cookies will be captured by the user in their automation browser and sent to CLI through the snapshot options using `customCookies` field.
298298
if (options?.customCookies && Array.isArray(options.customCookies) && options.customCookies.length > 0) {
299299
ctx.log.debug(`Setting ${options.customCookies.length} custom cookies`);
300-
300+
301301
const validCustomCookies = options.customCookies.filter(cookie => {
302302
if (!cookie.name || !cookie.value || !cookie.domain) {
303303
ctx.log.debug(`Skipping invalid custom cookie: missing required fields (name, value, or domain)`);
304304
return false;
305305
}
306-
306+
307307
const sameSiteValue = normalizeSameSite(cookie.sameSite);
308308
if (!['Strict', 'Lax', 'None'].includes(sameSiteValue)) {
309309
ctx.log.debug(`Skipping invalid custom cookie: invalid sameSite value '${cookie.sameSite}'`);
@@ -359,7 +359,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
359359
...constants.REQUEST_HEADERS
360360
}
361361
}
362-
362+
363363
try {
364364
// abort audio/video media requests
365365
if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
@@ -406,12 +406,12 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
406406
body = globalCache.get(requestUrl).body;
407407
} else {
408408
ctx.log.debug(`Resource not found in cache or global cache ${requestUrl} fetching from server`);
409-
if(ctx.build.checkPendingRequests){
409+
if (ctx.build.checkPendingRequests) {
410410
pendingRequests.add(requestUrl);
411411
}
412412
response = await page.request.fetch(request, requestOptions);
413413
body = await response.body();
414-
if(ctx.build.checkPendingRequests){
414+
if (ctx.build.checkPendingRequests) {
415415
pendingRequests.delete(requestUrl);
416416
}
417417
}
@@ -431,7 +431,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
431431
ctx.log.debug(`Handling request ${requestUrl}\n - skipping resource larger than 15MB`);
432432
} else if (!ALLOWED_RESOURCES.includes(request.resourceType())) {
433433
ctx.log.debug(`Handling request ${requestUrl}\n - skipping disallowed resource type [${request.resourceType()}]`);
434-
} else if (!ALLOWED_STATUSES.includes(response.status())) {
434+
} else if (!ALLOWED_STATUSES.includes(response.status())) {
435435
ctx.log.debug(`${globalViewport} Handling request ${requestUrl}\n - skipping disallowed status [${response.status()}]`);
436436

437437
if (response && response.headers()) {
@@ -441,12 +441,12 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
441441

442442
let responseOfRetry, bodyOfRetry
443443
ctx.log.debug(`Resource had a disallowed status ${requestUrl} fetching from server again`);
444-
if(ctx.build.checkPendingRequests){
444+
if (ctx.build.checkPendingRequests) {
445445
pendingRequests.add(requestUrl);
446446
}
447447
responseOfRetry = await page.request.fetch(request, requestOptions);
448448
bodyOfRetry = await responseOfRetry.body();
449-
if(ctx.build.checkPendingRequests){
449+
if (ctx.build.checkPendingRequests) {
450450
pendingRequests.delete(requestUrl);
451451
}
452452
if (responseOfRetry && responseOfRetry.status() && ALLOWED_STATUSES.includes(responseOfRetry.status())) {
@@ -477,18 +477,18 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
477477
statusCode: `${responseOfRetry.status()}`,
478478
url: requestUrl,
479479
resourceType: request.resourceType(),
480-
}
481-
480+
}
481+
482482
if ((response.status() >= 400 && response.status() < 600) && response.status() !== 0) {
483-
if (!discoveryErrors.browsers[globalBrowser]){
484-
discoveryErrors.browsers[globalBrowser] = {};
483+
if (!discoveryErrors.browsers[globalBrowser]) {
484+
discoveryErrors.browsers[globalBrowser] = {};
485485
}
486-
486+
487487
// Check if the discoveryErrors.browsers[globalBrowser] exists, and if not, initialize it
488488
if (discoveryErrors.browsers[globalBrowser] && !discoveryErrors.browsers[globalBrowser][globalViewport]) {
489489
discoveryErrors.browsers[globalBrowser][globalViewport] = [];
490490
}
491-
491+
492492
// Dynamically push the data into the correct browser and viewport
493493
if (discoveryErrors.browsers[globalBrowser]) {
494494
discoveryErrors.browsers[globalBrowser][globalViewport]?.push(data);
@@ -497,14 +497,14 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
497497
}
498498
} else {
499499
ctx.log.debug(`Handling request ${requestUrl}\n - content-type ${response.headers()['content-type']}`);
500-
500+
501501
if (ctx.config.useGlobalCache) {
502502
globalCache.set(requestUrl, {
503503
body: body.toString('base64'),
504504
type: response.headers()['content-type']
505505
});
506506
}
507-
507+
508508
cache[requestUrl] = {
509509
body: body.toString('base64'),
510510
type: response.headers()['content-type']
@@ -552,14 +552,14 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
552552

553553
if (options.web && Object.keys(options.web).length) {
554554
processedOptions.web = {};
555-
555+
556556
// Check and process viewports in web
557557
if (options.web.viewports && options.web.viewports.length > 0) {
558-
processedOptions.web.viewports = options.web.viewports.filter(viewport =>
558+
processedOptions.web.viewports = options.web.viewports.filter(viewport =>
559559
Array.isArray(viewport) && viewport.length > 0
560560
);
561561
}
562-
562+
563563
// Check and process browsers in web
564564
if (options.web.browsers && options.web.browsers.length > 0) {
565565
processedOptions.web.browsers = options.web.browsers;
@@ -568,19 +568,19 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
568568

569569
if (options.mobile && Object.keys(options.mobile).length) {
570570
processedOptions.mobile = {};
571-
571+
572572
// Check and process devices in mobile
573573
if (options.mobile.devices && options.mobile.devices.length > 0) {
574574
processedOptions.mobile.devices = options.mobile.devices;
575575
}
576-
576+
577577
// Check if 'fullPage' is provided and is a boolean, otherwise set default to true
578578
if (options.mobile.hasOwnProperty('fullPage') && typeof options.mobile.fullPage === 'boolean') {
579579
processedOptions.mobile.fullPage = options.mobile.fullPage;
580580
} else {
581581
processedOptions.mobile.fullPage = true; // Default value for fullPage
582582
}
583-
583+
584584
// Check if 'orientation' is provided and is valid, otherwise set default to 'portrait'
585585
if (options.mobile.hasOwnProperty('orientation') && (options.mobile.orientation === constants.MOBILE_ORIENTATION_PORTRAIT || options.mobile.orientation === constants.MOBILE_ORIENTATION_LANDSCAPE)) {
586586
processedOptions.mobile.orientation = options.mobile.orientation;
@@ -619,12 +619,12 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
619619
selectors.push(...value);
620620
break;
621621
case 'coordinates':
622-
selectors.push(...value.map(e =>`coordinates=${e}`));
622+
selectors.push(...value.map(e => `coordinates=${e}`));
623623
break;
624624
}
625625
}
626626
}
627-
if(options.ignoreType){
627+
if (options.ignoreType) {
628628
processedOptions.ignoreType = options.ignoreType;
629629
}
630630
}
@@ -663,7 +663,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
663663

664664
let renderViewports;
665665

666-
if((snapshot.options && snapshot.options.web) || (snapshot.options && snapshot.options.mobile)){
666+
if ((snapshot.options && snapshot.options.web) || (snapshot.options && snapshot.options.mobile)) {
667667
renderViewports = getRenderViewportsForOptions(snapshot.options)
668668
} else {
669669
renderViewports = getRenderViewports(ctx);
@@ -737,7 +737,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
737737
}
738738

739739
}
740-
if (ctx.config.cliEnableJavaScript && fullPage) {
740+
if (ctx.config.cliEnableJavaScript && fullPage) {
741741
if (ctx.config.lazyLoadConfiguration && ctx.config.lazyLoadConfiguration.enabled) {
742742
let stepValue = ctx.config.lazyLoadConfiguration.scrollStep || 250;
743743
let delayValue = ctx.config.lazyLoadConfiguration.scrollDelay || 300;
@@ -757,7 +757,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
757757
} catch (error) {
758758
ctx.log.debug(`Network idle failed due to ${error}`);
759759
}
760-
760+
761761

762762

763763
if (ctx.config.allowedAssets && ctx.config.allowedAssets.length) {
@@ -771,9 +771,9 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
771771
...constants.REQUEST_HEADERS
772772
}
773773
});
774-
774+
775775
const body = await response.body();
776-
776+
777777
if (body && body.length) {
778778
ctx.log.debug(`Caching asset ${assetUrl}`);
779779
cache[assetUrl] = {
@@ -830,27 +830,27 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
830830
const coordString = selector.replace('coordinates=', '');
831831
let pageHeight = height;
832832
if (viewport.height) {
833-
pageHeight = viewport.height;
833+
pageHeight = viewport.height;
834834
}
835835
const validation = validateCoordinates(
836-
coordString,
837-
pageHeight,
838-
viewport.width,
839-
snapshot.name
836+
coordString,
837+
pageHeight,
838+
viewport.width,
839+
snapshot.name
840840
);
841-
841+
842842
if (!validation.valid) {
843843
optionWarnings.add(validation.error!);
844844
continue;
845845
}
846846

847-
if(renderViewports.length > 1){
847+
if (renderViewports.length > 1) {
848848
optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, coordinates may not be accurate for multiple viewports`);
849849
}
850850

851851

852-
const coordinateElement = {
853-
type: 'coordinates',
852+
const coordinateElement = {
853+
type: 'coordinates',
854854
...validation.coords
855855
};
856856
locators.push(coordinateElement as any);
@@ -952,30 +952,30 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
952952
let startTime = Date.now();
953953
ctx.log.debug(`${pendingRequests.size} Pending requests before wait for ${snapshot.name}: ${Array.from(pendingRequests)}`);
954954
while (pendingRequests.size > 0) {
955-
const elapsedTime = Date.now() - startTime;
956-
if (elapsedTime >= MAX_WAIT_FOR_REQUEST_CALL) {
957-
ctx.log.debug(`Timeout reached (${MAX_WAIT_FOR_REQUEST_CALL/1000}s). Stopping wait for pending requests.`);
958-
ctx.log.debug(`${pendingRequests.size} Pending requests after wait for ${snapshot.name}: ${Array.from(pendingRequests)}`);
959-
break;
960-
}
961-
await new Promise(resolve => setTimeout(resolve, 1000));
955+
const elapsedTime = Date.now() - startTime;
956+
if (elapsedTime >= MAX_WAIT_FOR_REQUEST_CALL) {
957+
ctx.log.debug(`Timeout reached (${MAX_WAIT_FOR_REQUEST_CALL / 1000}s). Stopping wait for pending requests.`);
958+
ctx.log.debug(`${pendingRequests.size} Pending requests after wait for ${snapshot.name}: ${Array.from(pendingRequests)}`);
959+
break;
960+
}
961+
await new Promise(resolve => setTimeout(resolve, 1000));
962962
}
963-
if(pendingRequests.size === 0) {
963+
if (pendingRequests.size === 0) {
964964
ctx.log.debug(`No pending requests for ${snapshot.name}.`);
965965
}
966-
};
966+
};
967967

968-
if (ctx.build.checkPendingRequests) {
968+
if (ctx.build.checkPendingRequests) {
969969
await checkPending();
970-
}
970+
}
971971

972972
// Validate and report CSS injection after selector processing
973973
if (processedOptions.customCSS) {
974974
try {
975975
const cssRules = parseCSSFile(processedOptions.customCSS);
976976
const validationResult = await validateCSSSelectors(page, cssRules, ctx.log);
977977
const report = generateCSSInjectionReport(validationResult, ctx.log);
978-
978+
979979
if (validationResult.failedSelectors.length > 0) {
980980
validationResult.failedSelectors.forEach(selector => {
981981
optionWarnings.add(`customCSS selector not found: ${selector}`);
@@ -987,15 +987,15 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
987987
}
988988
}
989989

990-
990+
991991
let hasBrowserErrors = false;
992992
for (let browser in discoveryErrors.browsers) {
993993
if (discoveryErrors.browsers[browser]) {
994994
for (let viewport in discoveryErrors.browsers[browser]) {
995995
if (discoveryErrors.browsers[browser][viewport].length > 0) {
996996
hasBrowserErrors = true;
997-
ctx.build.hasDiscoveryError=true
998-
break;
997+
ctx.build.hasDiscoveryError = true
998+
break;
999999
}
10001000
}
10011001
}

src/lib/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ export default async (ctx: Context): Promise<FastifyInstance<Server, IncomingMes
316316
if (Date.now() - startTime > timeoutDuration) {
317317
replyCode = 202;
318318
replyBody = lastExternalResponse.data;
319-
replyBody.error = 'Request timed out, Snapshot still processing';
319+
replyBody.error = `Request timed out, Snapshot still processing (timeoutDuration: ${timeoutDuration / 1000}s, buildId: ${buildId}, snapshotName: ${snapshotName}, contextId: ${contextId})`;
320320
return reply.code(replyCode).send(replyBody);
321321
}
322322

0 commit comments

Comments
 (0)