Skip to content

Commit 21665da

Browse files
committed
resolve merge conflicts
2 parents 5ec08f2 + 1fc0265 commit 21665da

File tree

9 files changed

+105
-16
lines changed

9 files changed

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

src/lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export default {
126126
MOBILE_ORIENTATION_LANDSCAPE: 'landscape',
127127

128128
// build status
129+
BUILD_RUNNING: 'running',
129130
BUILD_COMPLETE: 'completed',
130131
BUILD_ERROR: 'error',
131132

src/lib/env.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export default (): Env => {
2424
USE_REMOTE_DISCOVERY,
2525
SMART_GIT,
2626
SHOW_RENDER_ERRORS,
27-
SMARTUI_SSE_URL='https://server-events.lambdatest.com'
27+
SMARTUI_SSE_URL='https://server-events.lambdatest.com',
28+
LT_SDK_SKIP_EXECUTION_LOGS
2829
} = process.env
2930

3031
return {
@@ -50,6 +51,7 @@ export default (): Env => {
5051
USE_REMOTE_DISCOVERY: USE_REMOTE_DISCOVERY === 'true',
5152
SMART_GIT: SMART_GIT === 'true',
5253
SHOW_RENDER_ERRORS: SHOW_RENDER_ERRORS === 'true',
53-
SMARTUI_SSE_URL
54+
SMARTUI_SSE_URL,
55+
LT_SDK_SKIP_EXECUTION_LOGS: LT_SDK_SKIP_EXECUTION_LOGS === 'true'
5456
}
5557
}

src/lib/processSnapshot.ts

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,22 @@ const globalCache = new NodeCache({ stdTTL: 3600, checkperiod: 600 });
1010
const MAX_RESOURCE_SIZE = 15 * (1024 ** 2); // 15MB
1111
var ALLOWED_RESOURCES = ['document', 'stylesheet', 'image', 'media', 'font', 'other'];
1212
const ALLOWED_STATUSES = [200, 201];
13-
const REQUEST_TIMEOUT = 1800000;
13+
const REQUEST_TIMEOUT = 180000;
1414
const MIN_VIEWPORT_HEIGHT = 1080;
15+
const MAX_WAIT_FOR_REQUEST_CALL = 30000;
16+
17+
const normalizeSameSite = (value) => {
18+
if (!value) return 'Lax';
19+
20+
const normalized = value.trim().toLowerCase();
21+
const mapping = {
22+
'lax': 'Lax',
23+
'strict': 'Strict',
24+
'none': 'None'
25+
};
26+
27+
return mapping[normalized] || value;
28+
};
1529

1630
export async function prepareSnapshot(snapshot: Snapshot, ctx: Context): Promise<Record<string, any>> {
1731
let processedOptions: Record<string, any> = {};
@@ -269,11 +283,11 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
269283
return false;
270284
}
271285

272-
if (cookie.sameSite && !['Strict', 'Lax', 'None'].includes(cookie.sameSite)) {
286+
const sameSiteValue = normalizeSameSite(cookie.sameSite);
287+
if (!['Strict', 'Lax', 'None'].includes(sameSiteValue)) {
273288
ctx.log.debug(`Skipping invalid custom cookie: invalid sameSite value '${cookie.sameSite}'`);
274289
return false;
275290
}
276-
277291
return true;
278292
}).map(cookie => ({
279293
name: cookie.name,
@@ -282,7 +296,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
282296
path: cookie.path || '/',
283297
httpOnly: cookie.httpOnly || false,
284298
secure: cookie.secure || false,
285-
sameSite: cookie.sameSite || 'Lax'
299+
sameSite: normalizeSameSite(cookie.sameSite)
286300
}));
287301

288302
if (validCustomCookies.length > 0) {
@@ -311,6 +325,8 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
311325
}
312326
}
313327

328+
const pendingRequests = new Set<string>();
329+
314330
// Use route to intercept network requests and discover resources
315331
await page.route('**/*', async (route, request) => {
316332
const requestUrl = request.url()
@@ -369,8 +385,14 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
369385
body = globalCache.get(requestUrl).body;
370386
} else {
371387
ctx.log.debug(`Resource not found in cache or global cache ${requestUrl} fetching from server`);
388+
if(ctx.build.checkPendingRequests){
389+
pendingRequests.add(requestUrl);
390+
}
372391
response = await page.request.fetch(request, requestOptions);
373392
body = await response.body();
393+
if(ctx.build.checkPendingRequests){
394+
pendingRequests.delete(requestUrl);
395+
}
374396
}
375397

376398
// handle response
@@ -398,15 +420,26 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
398420

399421
let responseOfRetry, bodyOfRetry
400422
ctx.log.debug(`Resource had a disallowed status ${requestUrl} fetching from server again`);
423+
if(ctx.build.checkPendingRequests){
424+
pendingRequests.add(requestUrl);
425+
}
401426
responseOfRetry = await page.request.fetch(request, requestOptions);
402427
bodyOfRetry = await responseOfRetry.body();
403-
428+
if(ctx.build.checkPendingRequests){
429+
pendingRequests.delete(requestUrl);
430+
}
404431
if (responseOfRetry && responseOfRetry.status() && ALLOWED_STATUSES.includes(responseOfRetry.status())) {
405432
ctx.log.debug(`Handling request after retry ${requestUrl}\n - content-type ${responseOfRetry.headers()['content-type']}`);
406433
cache[requestUrl] = {
407434
body: bodyOfRetry.toString('base64'),
408435
type: responseOfRetry.headers()['content-type']
409436
}
437+
if (ctx.config.useGlobalCache) {
438+
globalCache.set(requestUrl, {
439+
body: bodyOfRetry.toString('base64'),
440+
type: responseOfRetry.headers()['content-type']
441+
});
442+
}
410443
route.fulfill({
411444
status: responseOfRetry.status(),
412445
headers: responseOfRetry.headers(),
@@ -457,6 +490,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
457490
}
458491
}
459492

493+
460494
// Continue the request with the fetched response
461495
route.fulfill({
462496
status: response.status(),
@@ -673,6 +707,7 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
673707
} catch (error) {
674708
ctx.log.debug(`Network idle failed due to ${error}`);
675709
}
710+
676711

677712

678713
if (ctx.config.allowedAssets && ctx.config.allowedAssets.length) {
@@ -862,6 +897,28 @@ export default async function processSnapshot(snapshot: Snapshot, ctx: Context):
862897
ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`);
863898
}
864899

900+
// Wait for pending requests to complete
901+
const checkPending = async () => {
902+
let startTime = Date.now();
903+
ctx.log.debug(`${pendingRequests.size} Pending requests before wait for ${snapshot.name}: ${Array.from(pendingRequests)}`);
904+
while (pendingRequests.size > 0) {
905+
const elapsedTime = Date.now() - startTime;
906+
if (elapsedTime >= MAX_WAIT_FOR_REQUEST_CALL) {
907+
ctx.log.debug(`Timeout reached (${MAX_WAIT_FOR_REQUEST_CALL/1000}s). Stopping wait for pending requests.`);
908+
ctx.log.debug(`${pendingRequests.size} Pending requests after wait for ${snapshot.name}: ${Array.from(pendingRequests)}`);
909+
break;
910+
}
911+
await new Promise(resolve => setTimeout(resolve, 1000));
912+
}
913+
if(pendingRequests.size === 0) {
914+
ctx.log.debug(`No pending requests for ${snapshot.name}.`);
915+
}
916+
};
917+
918+
if (ctx.build.checkPendingRequests) {
919+
await checkPending();
920+
}
921+
865922
// Validate and report CSS injection after selector processing
866923
if (processedOptions.customCSS) {
867924
try {

src/lib/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ export function startPdfPolling(ctx: Context) {
546546
try {
547547
const response = await ctx.client.fetchPdfResults(ctx);
548548

549-
if (response.screenshots && response.build?.build_status === constants.BUILD_COMPLETE) {
549+
if (response.screenshots && response.build?.build_status !== constants.BUILD_RUNNING) {
550550
clearInterval(interval);
551551

552552
const pdfGroups = groupScreenshotsByPdf(response.screenshots);
@@ -759,12 +759,14 @@ export async function listenToSmartUISSE(
759759
const abortController = new AbortController();
760760

761761
try {
762+
const cookieKey = baseURL === 'https://server-events.lambdatest.com' ? 'accessToken' : 'stageAccessToken';
763+
762764
const response = await fetch(url, {
763765
method: 'GET',
764766
headers: {
765767
'Accept': 'text/event-stream',
766768
'Cache-Control': 'no-cache',
767-
'Cookie': `stageAccessToken=Basic ${accessToken}`
769+
'Cookie': `${cookieKey}=Basic ${accessToken}`
768770
},
769771
signal: abortController.signal
770772
});

src/tasks/createBuild.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRen
1818
url: resp.data.buildURL,
1919
baseline: resp.data.baseline,
2020
useKafkaFlow: resp.data.useKafkaFlow || false,
21+
checkPendingRequests: resp.data.checkPendingRequests || false,
2122
}
2223
process.env.SMARTUI_BUILD_ID = resp.data.buildId;
2324
process.env.SMARTUI_BUILD_NAME = resp.data.buildName;

src/tasks/createBuildExec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRen
2020
url: resp.data.buildURL,
2121
baseline: resp.data.baseline,
2222
useKafkaFlow: resp.data.useKafkaFlow || false,
23+
checkPendingRequests: resp.data.checkPendingRequests || false,
2324
}
2425
process.env.SMARTUI_BUILD_ID = resp.data.buildId;
2526
process.env.SMARTUI_BUILD_NAME = resp.data.buildName;

src/tasks/exec.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import chalk from 'chalk'
44
import spawn from 'cross-spawn'
55
import { updateLogContext } from '../lib/logger.js'
66
import { startPolling, startSSEListener } from '../lib/utils.js'
7+
import fs from 'fs'
8+
import path from 'path'
79

810
export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRendererFactory> => {
911
return {
@@ -31,12 +33,33 @@ export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRen
3133

3234
// Handle standard output
3335
let totalOutput = '';
34-
const output = createWritable((chunk: string) => {
35-
totalOutput += chunk;
36-
task.output = chalk.gray(totalOutput);
37-
})
38-
childProcess.stdout?.pipe(output);
39-
childProcess.stderr?.pipe(output);
36+
if (!ctx.env.LT_SDK_SKIP_EXECUTION_LOGS) {
37+
const output = createWritable((chunk: string) => {
38+
totalOutput += chunk;
39+
task.output = chalk.gray(totalOutput);
40+
})
41+
childProcess.stdout?.pipe(output);
42+
childProcess.stderr?.pipe(output);
43+
} else {
44+
// Write logs to file when skipping terminal output
45+
const logFileName = `execution-logs.log`;
46+
const logFilePath = path.join(process.cwd(), logFileName);
47+
const logStream = fs.createWriteStream(logFilePath, { flags: 'a' });
48+
49+
task.output = chalk.gray(`Execution logs being written to: ${logFileName}`);
50+
51+
childProcess.stdout?.on('data', (data) => {
52+
logStream.write(data);
53+
});
54+
55+
childProcess.stderr?.on('data', (data) => {
56+
logStream.write(data);
57+
});
58+
59+
childProcess.on('close', () => {
60+
logStream.end();
61+
});
62+
}
4063

4164
childProcess.on('error', (error) => {
4265
task.output = chalk.gray(`error: ${error.message}`);

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export interface Env {
125125
SMART_GIT: boolean;
126126
SHOW_RENDER_ERRORS: boolean;
127127
SMARTUI_SSE_URL: string;
128+
LT_SDK_SKIP_EXECUTION_LOGS: boolean;
128129
}
129130

130131
export interface Snapshot {
@@ -202,6 +203,7 @@ export interface Build {
202203
useKafkaFlow: boolean;
203204
hasDiscoveryError: boolean;
204205
projectId?: string;
206+
checkPendingRequests: boolean;
205207
}
206208

207209
export interface WebConfig {

0 commit comments

Comments
 (0)