From 43c4f350b43a5e4d9b7a36cf9b1a808bc608e4a3 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Wed, 10 Sep 2025 14:15:39 +0530 Subject: [PATCH 01/17] changes for threshold --- src/lib/ctx.ts | 2 ++ src/lib/httpClient.ts | 37 +++++++++++++++++++++++-------------- src/lib/snapshotQueue.ts | 4 +++- src/types.ts | 10 +++++++--- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/lib/ctx.ts b/src/lib/ctx.ts index e6faa1c..2836ed5 100644 --- a/src/lib/ctx.ts +++ b/src/lib/ctx.ts @@ -132,6 +132,8 @@ export default (options: Record): Context => { requestHeaders: config.requestHeaders || {}, allowDuplicateSnapshotNames: allowDuplicateSnapshotNames, useLambdaInternal: useLambdaInternal, + approvalThreshold: config.approvalThreshold, + rejectionThreshold: config.rejectionThreshold, }, uploadFilePath: '', webStaticConfig: [], diff --git a/src/lib/httpClient.ts b/src/lib/httpClient.ts index 9a1ae93..b3c6a4a 100644 --- a/src/lib/httpClient.ts +++ b/src/lib/httpClient.ts @@ -343,24 +343,33 @@ export default class httpClient { }, ctx.log) } - processSnapshot(ctx: Context, snapshot: ProcessedSnapshot, snapshotUuid: string, discoveryErrors: DiscoveryErrors, variantCount: number, sync: boolean = false) { + processSnapshot(ctx: Context, snapshot: ProcessedSnapshot, snapshotUuid: string, discoveryErrors: DiscoveryErrors, variantCount: number, sync: boolean = false, approvalThreshold: number| undefined, rejectionThreshold: number| undefined) { + const requestData: any = { + name: snapshot.name, + url: snapshot.url, + snapshotUuid: snapshotUuid, + variantCount: variantCount, + test: { + type: ctx.testType, + source: 'cli' + }, + discoveryErrors: discoveryErrors, + doRemoteDiscovery: snapshot.options.doRemoteDiscovery, + sync: sync + }; + + if (approvalThreshold !== undefined) { + requestData.approvalThreshold = approvalThreshold; + } + if (rejectionThreshold !== undefined) { + requestData.rejectionThreshold = rejectionThreshold; + } + return this.request({ url: `/build/${ctx.build.id}/snapshot`, method: 'POST', headers: { 'Content-Type': 'application/json' }, - data: { - name: snapshot.name, - url: snapshot.url, - snapshotUuid: snapshotUuid, - variantCount: variantCount, - test: { - type: ctx.testType, - source: 'cli' - }, - doRemoteDiscovery: snapshot.options.doRemoteDiscovery, - discoveryErrors: discoveryErrors, - sync: sync - } + data: requestData }, ctx.log) } diff --git a/src/lib/snapshotQueue.ts b/src/lib/snapshotQueue.ts index 0ca45a7..50bfb49 100644 --- a/src/lib/snapshotQueue.ts +++ b/src/lib/snapshotQueue.ts @@ -427,7 +427,9 @@ export default class Queue { } this.processNext(); } else { - await this.ctx.client.processSnapshot(this.ctx, processedSnapshot, snapshotUuid, discoveryErrors,calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config),snapshot?.options?.sync); + let approvalThreshold = snapshot?.options?.approvalThreshold || this.ctx.config.approvalThreshold; + let rejectionThreshold = snapshot?.options?.rejectionThreshold || this.ctx.config.rejectionThreshold; + await this.ctx.client.processSnapshot(this.ctx, processedSnapshot, snapshotUuid, discoveryErrors,calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config),snapshot?.options?.sync, approvalThreshold, rejectionThreshold); if(snapshot?.options?.contextId && this.ctx.contextToSnapshotMap?.has(snapshot.options.contextId)){ this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, 1); } diff --git a/src/types.ts b/src/types.ts index 0e34e6c..f830393 100644 --- a/src/types.ts +++ b/src/types.ts @@ -38,6 +38,8 @@ export interface Context { requestHeaders?: Array>; allowDuplicateSnapshotNames?: boolean; useLambdaInternal?: boolean; + approvalThreshold?: number; + rejectionThreshold?: number; }; uploadFilePath: string; webStaticConfig: WebStaticConfig; @@ -148,9 +150,11 @@ export interface Snapshot { }, loadDomContent?: boolean; ignoreType?: string[], - sessionId?: string - sync?: boolean; - contextId?: string; + sessionId?: string, + sync?: boolean, + contextId?: string, + approvalThreshold?: number, + rejectionThreshold?: number } } From e7bc81de99ed76494ee9bc1f2ea4dd1685e74695 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Wed, 10 Sep 2025 16:15:34 +0530 Subject: [PATCH 02/17] validation check --- src/lib/schemaValidation.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/schemaValidation.ts b/src/lib/schemaValidation.ts index c6b1fdf..df958a2 100644 --- a/src/lib/schemaValidation.ts +++ b/src/lib/schemaValidation.ts @@ -513,6 +513,14 @@ const SnapshotSchema: JSONSchemaType = { timeout: { type: "number", errorMessage: "Invalid snapshot options; timeout must be a number" + }, + approvalThreshold: { + type: "number", + errorMessage: "Invalid snapshot options; approvalThreshold must be a number" + }, + rejectionThreshold: { + type: "number", + errorMessage: "Invalid snapshot options; rejectionThreshold must be a number" } }, additionalProperties: false From db3c867e5b84351ba5adbefeaca624322cdf7725 Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Thu, 11 Sep 2025 15:16:22 +0530 Subject: [PATCH 03/17] added schema validation for config --- src/lib/schemaValidation.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/schemaValidation.ts b/src/lib/schemaValidation.ts index df958a2..fd0948a 100644 --- a/src/lib/schemaValidation.ts +++ b/src/lib/schemaValidation.ts @@ -266,6 +266,14 @@ const ConfigSchema = { useLambdaInternal: { type: "boolean", errorMessage: "Invalid config; useLambdaInternal must be true/false" + }, + approvalThreshold: { + type: "number", + errorMessage: "Invalid config; approvalThreshold must be a number" + }, + rejectionThreshold: { + type: "number", + errorMessage: "Invalid config; rejectionThreshold must be a number" } }, anyOf: [ From 29dafad1ea41c2a012f877455184fc068ff9b05b Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Thu, 11 Sep 2025 19:58:55 +0530 Subject: [PATCH 04/17] validation for threshold values --- src/lib/ctx.ts | 6 ++++++ src/lib/server.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/lib/ctx.ts b/src/lib/ctx.ts index 2836ed5..297c378 100644 --- a/src/lib/ctx.ts +++ b/src/lib/ctx.ts @@ -37,6 +37,12 @@ export default (options: Record): Context => { delete config.web.resolutions; } + if(config.approvalThreshold && config.rejectionThreshold) { + if(config.rejectionThreshold <= config.approvalThreshold) { + throw new Error('Invalid config; rejectionThreshold must be greater than approvalThreshold'); + } + } + let validateConfigFn = options.scheduled ? validateConfigForScheduled : validateConfig; // validate config diff --git a/src/lib/server.ts b/src/lib/server.ts index 2914526..9ccfcb6 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -38,6 +38,12 @@ export default async (ctx: Context): Promise Date: Fri, 12 Sep 2025 12:59:15 +0530 Subject: [PATCH 05/17] handling threshold constraints --- src/lib/schemaValidation.ts | 8 ++++++-- src/lib/server.ts | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib/schemaValidation.ts b/src/lib/schemaValidation.ts index 1c69440..09ca2c4 100644 --- a/src/lib/schemaValidation.ts +++ b/src/lib/schemaValidation.ts @@ -536,11 +536,15 @@ const SnapshotSchema: JSONSchemaType = { }, approvalThreshold: { type: "number", - errorMessage: "Invalid snapshot options; approvalThreshold must be a number" + minimum: 0, + maximum: 100, + errorMessage: "Invalid snapshot options; approvalThreshold must be a number between 0 and 100" }, rejectionThreshold: { type: "number", - errorMessage: "Invalid snapshot options; rejectionThreshold must be a number" + minimum: 0, + maximum: 100, + errorMessage: "Invalid snapshot options; rejectionThreshold must be a number between 0 and 100" } }, additionalProperties: false diff --git a/src/lib/server.ts b/src/lib/server.ts index 9ccfcb6..a578163 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -39,9 +39,9 @@ export default async (ctx: Context): Promise Date: Fri, 12 Sep 2025 13:27:33 +0530 Subject: [PATCH 06/17] handling console message --- src/tasks/processSnapshot.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tasks/processSnapshot.ts b/src/tasks/processSnapshot.ts index b6f8b57..f66a6c3 100644 --- a/src/tasks/processSnapshot.ts +++ b/src/tasks/processSnapshot.ts @@ -36,6 +36,9 @@ export default (ctx: Context): ListrTask Date: Mon, 15 Sep 2025 10:33:13 +0530 Subject: [PATCH 07/17] overrides simple-swizzle version to 0.2.2 --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 1c2d24a..3d34af1 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,9 @@ "which": "^4.0.0", "winston": "^3.10.0" }, + "overrides": { + "simple-swizzle": "0.2.2" + }, "devDependencies": { "typescript": "^5.3.2" } From 36ee857a24a366f44929714f6a54b0d76660c76c Mon Sep 17 00:00:00 2001 From: Sushobhit Dua <38429226+sushobhit-lt@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:08:06 +0530 Subject: [PATCH 08/17] auto tunnel support added in exec:start --- src/commander/server.ts | 7 +++++-- src/commander/stopServer.ts | 4 +++- src/lib/schemaValidation.ts | 5 +++++ src/lib/server.ts | 9 +++++++-- src/lib/utils.ts | 17 +++++++++++------ src/tasks/createBuildExec.ts | 14 +++++++++++++- src/tasks/finalizeBuild.ts | 2 +- src/types.ts | 1 + 8 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/commander/server.ts b/src/commander/server.ts index 8178162..475dfcb 100644 --- a/src/commander/server.ts +++ b/src/commander/server.ts @@ -8,6 +8,7 @@ import getGitInfo from '../tasks/getGitInfo.js'; import createBuildExec from '../tasks/createBuildExec.js'; import snapshotQueue from '../lib/snapshotQueue.js'; import { startPolling, startPingPolling } from '../lib/utils.js'; +import startTunnel from '../tasks/startTunnel.js' const command = new Command(); @@ -27,12 +28,13 @@ command ctx.snapshotQueue = new snapshotQueue(ctx); ctx.totalSnapshots = 0 ctx.isStartExec = true - + let tasks = new Listr( [ authExec(ctx), startServer(ctx), getGitInfo(ctx), + ...(ctx.config.tunnel && ctx.config.tunnel?.type === 'auto' ? [startTunnel(ctx)] : []), createBuildExec(ctx), ], @@ -51,7 +53,7 @@ command try { await tasks.run(ctx); if (ctx.build && ctx.build.id) { - startPingPolling(ctx); + startPingPolling(ctx, 'exec-start'); } if (ctx.options.fetchResults && ctx.build && ctx.build.id) { startPolling(ctx, '', false, '') @@ -59,6 +61,7 @@ command } catch (error) { console.error('Error during server execution:', error); + process.exit(1); } }); diff --git a/src/commander/stopServer.ts b/src/commander/stopServer.ts index 3aae301..0ff3150 100644 --- a/src/commander/stopServer.ts +++ b/src/commander/stopServer.ts @@ -33,8 +33,10 @@ command } } catch (error: any) { // Handle any errors during the HTTP request - if (error.code === 'ECONNABORTED') { + if (error && error.code === 'ECONNABORTED') { console.error(chalk.red('Error: SmartUI server did not respond in 15 seconds')); + } if (error && error.code === 'ECONNREFUSED') { + console.error(chalk.red('Error: Looks like smartui cli server is already stopped')); } else { console.error(chalk.red('Error while stopping server')); } diff --git a/src/lib/schemaValidation.ts b/src/lib/schemaValidation.ts index cdfc42d..0f64783 100644 --- a/src/lib/schemaValidation.ts +++ b/src/lib/schemaValidation.ts @@ -239,6 +239,11 @@ const ConfigSchema = { type: "string", errorMessage: "Invalid config; logFile should be a string value" }, + environment: { + type: "string", + enum: ["stage", "prod"], + errorMessage: "Invalid config; environment should be a string value either stage or prod" + } }, required: ["type"], additionalProperties: false diff --git a/src/lib/server.ts b/src/lib/server.ts index 2914526..8a78246 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -5,7 +5,7 @@ import { readFileSync, truncate } from 'fs' import { Context } from '../types.js' import { validateSnapshot } from './schemaValidation.js' import { pingIntervalId } from './utils.js'; -import { startPolling } from './utils.js'; +import { stopTunnelHelper } from './utils.js'; const uploadDomToS3ViaEnv = process.env.USE_LAMBDA_INTERNAL || false; export default async (ctx: Context): Promise> => { @@ -151,6 +151,11 @@ export default async (ctx: Context): Promise { +export async function startPingPolling(ctx: Context, event: string): Promise { try { ctx.log.debug('Sending initial ping to server...'); await ctx.client.ping(ctx.build.id, ctx.log); @@ -336,9 +336,9 @@ export async function startPingPolling(ctx: Context): Promise { // Start the polling interval pingIntervalId = setInterval(async () => { try { - ctx.log.debug('Sending ping to server...'); + ctx.log.debug('Sending ping to server...'+ event); await ctx.client.ping(ctx.build.id, ctx.log); - ctx.log.debug('Ping sent successfully.'); + ctx.log.debug('Ping sent successfully.'+ event); } catch (error: any) { ctx.log.error(`Error during ping polling: ${error.message}`); } @@ -390,6 +390,11 @@ export async function startTunnelBinary(ctx: Context) { ctx.config.tunnel.tunnelName = randomTunnelName } + if (tunnelConfig?.environment) { + tunnelArguments.environment = tunnelConfig.environment + } + + ctx.log.debug(`tunnel config ${JSON.stringify(tunnelArguments)}`) if (ctx.config.tunnel?.type === 'auto') { @@ -434,7 +439,7 @@ export async function startPollingForTunnel(ctx: Context, build_id: string, base const status = await tunnelInstance.stop(); ctx.log.debug('Tunnel is Stopped ? ' + status); - + return; } } catch (error: any) { if (error.message.includes('ENOTFOUND')) { @@ -450,10 +455,10 @@ export async function startPollingForTunnel(ctx: Context, build_id: string, base export async function stopTunnelHelper(ctx: Context) { const tunnelRunningStatus = await tunnelInstance.isRunning(); - ctx.log.debug('Running status of tunnel before stopping ? ' + tunnelRunningStatus); + ctx.log.debug('stop-tunnel:: Running status of tunnel before stopping ? ' + tunnelRunningStatus); const status = await tunnelInstance.stop(); - ctx.log.debug('Tunnel is Stopped ? ' + status); + ctx.log.debug('stop-tunnel:: Tunnel is Stopped ? ' + status); } /** diff --git a/src/tasks/createBuildExec.ts b/src/tasks/createBuildExec.ts index 1e58fb0..e999123 100644 --- a/src/tasks/createBuildExec.ts +++ b/src/tasks/createBuildExec.ts @@ -36,6 +36,9 @@ export default (ctx: Context): ListrTask Date: Mon, 15 Sep 2025 18:31:27 +0530 Subject: [PATCH 09/17] validation at build level --- src/lib/schemaValidation.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/schemaValidation.ts b/src/lib/schemaValidation.ts index 09ca2c4..143a194 100644 --- a/src/lib/schemaValidation.ts +++ b/src/lib/schemaValidation.ts @@ -269,10 +269,14 @@ const ConfigSchema = { }, approvalThreshold: { type: "number", + minimum: 0, + maximum: 100, errorMessage: "Invalid config; approvalThreshold must be a number" }, rejectionThreshold: { type: "number", + minimum: 0, + maximum: 100, errorMessage: "Invalid config; rejectionThreshold must be a number" } }, From 24beb63a9551fc1bee40d34321c306e36df035b6 Mon Sep 17 00:00:00 2001 From: parthkirsan Date: Tue, 16 Sep 2025 13:19:15 +0530 Subject: [PATCH 10/17] add is start exec for session caps --- src/lib/httpClient.ts | 5 +++-- src/lib/server.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/httpClient.ts b/src/lib/httpClient.ts index 9a1ae93..3cd4fba 100644 --- a/src/lib/httpClient.ts +++ b/src/lib/httpClient.ts @@ -281,7 +281,7 @@ export default class httpClient { } - getSmartUICapabilities(sessionId: string, config: any, git: any, log: Logger) { + getSmartUICapabilities(sessionId: string, config: any, git: any, log: Logger, isStartExec: boolean) { return this.request({ url: '/sessions/capabilities', method: 'GET', @@ -290,7 +290,8 @@ export default class httpClient { }, data: { git, - config + config, + isStartExec }, headers: { projectToken: '', diff --git a/src/lib/server.ts b/src/lib/server.ts index 2914526..6fa71e7 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -53,7 +53,7 @@ export default async (ctx: Context): Promise Date: Wed, 17 Sep 2025 14:28:31 +0530 Subject: [PATCH 11/17] handle tunnel cases with exec:start --- src/commander/exec.ts | 1 + src/commander/server.ts | 18 ++++++++++++- src/lib/httpClient.ts | 9 ++++--- src/lib/server.ts | 25 ++++++++++++----- src/lib/utils.ts | 52 +++++++++++++++++++----------------- src/tasks/createBuildExec.ts | 13 +++------ src/tasks/finalizeBuild.ts | 18 ++++++++++--- src/tasks/startTunnel.ts | 1 + src/types.ts | 2 ++ 9 files changed, 91 insertions(+), 48 deletions(-) diff --git a/src/commander/exec.ts b/src/commander/exec.ts index adf91e8..ddef0fd 100644 --- a/src/commander/exec.ts +++ b/src/commander/exec.ts @@ -40,6 +40,7 @@ command ctx.args.execCommand = execCommand ctx.snapshotQueue = new snapshotQueue(ctx) ctx.totalSnapshots = 0 + ctx.sourceCommand = 'exec' let tasks = new Listr( [ diff --git a/src/commander/server.ts b/src/commander/server.ts index 475dfcb..82cfc61 100644 --- a/src/commander/server.ts +++ b/src/commander/server.ts @@ -9,6 +9,7 @@ import createBuildExec from '../tasks/createBuildExec.js'; import snapshotQueue from '../lib/snapshotQueue.js'; import { startPolling, startPingPolling } from '../lib/utils.js'; import startTunnel from '../tasks/startTunnel.js' +const util = require('util'); const command = new Command(); @@ -28,6 +29,7 @@ command ctx.snapshotQueue = new snapshotQueue(ctx); ctx.totalSnapshots = 0 ctx.isStartExec = true + ctx.sourceCommand = 'exec-start' let tasks = new Listr( [ @@ -53,13 +55,27 @@ command try { await tasks.run(ctx); if (ctx.build && ctx.build.id) { - startPingPolling(ctx, 'exec-start'); + startPingPolling(ctx); } if (ctx.options.fetchResults && ctx.build && ctx.build.id) { startPolling(ctx, '', false, '') } + + // await ctx.client.getScreenshotData("567890", false, ctx.log, "755#a5ac6a67-289a-427d-b004-7dfff6c3484b#fanniemae-stage", 'smartui-bbf5b47005'); + } catch (error) { + // Log the error in a human-readable format + // ctx.log.debug(util.inspect(error, { showHidden: false, depth: null })); + + // console.log(`Json Error: ${JSON.stringify(error, null, 2)}`); + // if (error?.message.includes('ENOTFOUND')) { + // ctx.log.error('Error: Network error occurred while fetching build status while polling. Please check your connection and try again.'); + // } else { + // // Log the error in a human-readable format + // ctx.log.debug(util.inspect(error, { showHidden: false, depth: null })); + // ctx.log.error(`Error fetching build status while polling: ${JSON.stringify(error)}`); + // } console.error('Error during server execution:', error); process.exit(1); } diff --git a/src/lib/httpClient.ts b/src/lib/httpClient.ts index 3cd4fba..013307c 100644 --- a/src/lib/httpClient.ts +++ b/src/lib/httpClient.ts @@ -3,7 +3,7 @@ import FormData from 'form-data'; import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; import { Env, Snapshot, ProcessedSnapshot, Git, Build, Context, DiscoveryErrors } from '../types.js'; import constants from './constants.js'; -import type { Logger } from 'winston' +import { type Logger } from 'winston' import pkgJSON from './../../package.json' import https from 'https'; @@ -77,7 +77,7 @@ export default class httpClient { (response) => response, async (error) => { const { config } = error; - if (config && config.url === '/screenshot' && config.method === 'post') { + if (config && config.url === '/screenshot' && config.method === 'post' && error?.response?.status !== 401) { // Set default retry count and delay if not already defined if (!config.retryCount) { config.retryCount = 0; @@ -242,11 +242,12 @@ export default class httpClient { }, log) } - getScreenshotData(buildId: string, baseline: boolean, log: Logger, projectToken: string) { + getScreenshotData(buildId: string, baseline: boolean, log: Logger, projectToken: string, buildName: string) { + log.debug(`Fetching screenshot data for buildId: ${buildId} having buildName: ${buildName} with baseline: ${baseline}`); return this.request({ url: '/screenshot', method: 'GET', - params: { buildId, baseline }, + params: { buildId, baseline, buildName }, headers: {projectToken: projectToken} }, log); } diff --git a/src/lib/server.ts b/src/lib/server.ts index af0c338..b33755b 100644 --- a/src/lib/server.ts +++ b/src/lib/server.ts @@ -4,8 +4,7 @@ import fastify, { FastifyInstance, RouteShorthandOptions } from 'fastify'; import { readFileSync, truncate } from 'fs' import { Context } from '../types.js' import { validateSnapshot } from './schemaValidation.js' -import { pingIntervalId } from './utils.js'; -import { stopTunnelHelper } from './utils.js'; +import { pingIntervalId, startPollingForTunnel, stopTunnelHelper, isTunnelPolling } from './utils.js'; const uploadDomToS3ViaEnv = process.env.USE_LAMBDA_INTERNAL || false; export default async (ctx: Context): Promise> => { @@ -106,6 +105,7 @@ export default async (ctx: Context): Promise; try { + ctx.log.info('Received stop command. Finalizing build ...'); if(ctx.config.delayedUpload){ ctx.log.debug("started after processing because of delayedUpload") ctx.snapshotQueue?.startProcessingfunc() @@ -118,6 +118,7 @@ export default async (ctx: Context): Promise { +export async function startPingPolling(ctx: Context): Promise { try { ctx.log.debug('Sending initial ping to server...'); await ctx.client.ping(ctx.build.id, ctx.log); @@ -333,12 +334,13 @@ export async function startPingPolling(ctx: Context, event: string): Promise { try { - ctx.log.debug('Sending ping to server...'+ event); + ctx.log.debug('Sending ping to server... '+ sourceCommand); await ctx.client.ping(ctx.build.id, ctx.log); - ctx.log.debug('Ping sent successfully.'+ event); + ctx.log.debug('Ping sent successfully. '+ sourceCommand); } catch (error: any) { ctx.log.error(`Error during ping polling: ${error.message}`); } @@ -406,54 +408,56 @@ export async function startTunnelBinary(ctx: Context) { } } -export async function startPollingForTunnel(ctx: Context, build_id: string, baseline: boolean, projectToken: string): Promise { +export let isTunnelPolling: NodeJS.Timeout | null = null; + +export async function startPollingForTunnel(ctx: Context, build_id: string, baseline: boolean, projectToken: string, buildName: string): Promise { + if (isTunnelPolling) { + ctx.log.debug('Tunnel polling is already active. Skipping for build_id: ' + build_id); + return; + } const intervalId = setInterval(async () => { try { let resp; if (build_id) { - resp = await ctx.client.getScreenshotData(build_id, baseline, ctx.log, projectToken); + resp = await ctx.client.getScreenshotData(build_id, baseline, ctx.log, projectToken, buildName); } else if (ctx.build && ctx.build.id) { - resp = await ctx.client.getScreenshotData(ctx.build.id, ctx.build.baseline, ctx.log, ''); + resp = await ctx.client.getScreenshotData(ctx.build.id, ctx.build.baseline, ctx.log, '', ''); } else { + ctx.log.debug('No build information available for polling tunnel status.'); + clearInterval(intervalId); + await stopTunnelHelper(ctx); return; } - + ctx.log.debug(' resp from polling for tunnel status: ' + JSON.stringify(resp)); if (!resp.build) { ctx.log.info("Error: Build data is null."); clearInterval(intervalId); - - const tunnelRunningStatus = await tunnelInstance.isRunning(); - ctx.log.debug('Running status of tunnel before stopping ? ' + tunnelRunningStatus); - - const status = await tunnelInstance.stop(); - ctx.log.debug('Tunnel is Stopped ? ' + status); - + await stopTunnelHelper(ctx); return; } if (resp.build.build_status_ind === constants.BUILD_COMPLETE || resp.build.build_status_ind === constants.BUILD_ERROR) { clearInterval(intervalId); - - const tunnelRunningStatus = await tunnelInstance.isRunning(); - ctx.log.debug('Running status of tunnel before stopping ? ' + tunnelRunningStatus); - - const status = await tunnelInstance.stop(); - ctx.log.debug('Tunnel is Stopped ? ' + status); + await stopTunnelHelper(ctx); return; } } catch (error: any) { - if (error.message.includes('ENOTFOUND')) { + if (error?.message.includes('ENOTFOUND')) { ctx.log.error('Error: Network error occurred while fetching build status while polling. Please check your connection and try again.'); clearInterval(intervalId); } else { - ctx.log.error(`Error fetching build status while polling: ${error.message}`); + // Log the error in a human-readable format + ctx.log.debug(util.inspect(error, { showHidden: false, depth: null })); + ctx.log.error(`Error fetching build status while polling: ${JSON.stringify(error)}`); } clearInterval(intervalId); } }, 5000); + isTunnelPolling = intervalId; } export async function stopTunnelHelper(ctx: Context) { + ctx.log.debug('stop-tunnel:: Stopping the tunnel now'); const tunnelRunningStatus = await tunnelInstance.isRunning(); ctx.log.debug('stop-tunnel:: Running status of tunnel before stopping ? ' + tunnelRunningStatus); diff --git a/src/tasks/createBuildExec.ts b/src/tasks/createBuildExec.ts index e999123..d0f30fc 100644 --- a/src/tasks/createBuildExec.ts +++ b/src/tasks/createBuildExec.ts @@ -2,7 +2,7 @@ import { ListrTask, ListrRendererFactory } from 'listr2'; import { Context } from '../types.js' import chalk from 'chalk'; import { updateLogContext } from '../lib/logger.js'; -import { startTunnelBinary, startPollingForTunnel, stopTunnelHelper, startPingPolling } from '../lib/utils.js'; +import { stopTunnelHelper, startPingPolling } from '../lib/utils.js'; export default (ctx: Context): ListrTask => { return { @@ -42,16 +42,11 @@ export default (ctx: Context): ListrTask; + sourceCommand?: string; + autoTunnelStarted?: boolean; } export interface Env { From 0a9485234e1a3e49b3e28d150989a5c5b7da398a Mon Sep 17 00:00:00 2001 From: Nick-1234531 Date: Wed, 17 Sep 2025 17:45:59 +0530 Subject: [PATCH 12/17] version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d34af1..1cd49e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lambdatest/smartui-cli", - "version": "4.1.30", + "version": "4.1.31", "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest", "files": [ "dist/**/*" From fbd0e2b3fcd82addf20a3a742436be0bc24cbf52 Mon Sep 17 00:00:00 2001 From: Sushobhit Dua <38429226+sushobhit-lt@users.noreply.github.com> Date: Wed, 17 Sep 2025 22:01:02 +0530 Subject: [PATCH 13/17] remove ping-pong incase of auto tunnel --- src/commander/server.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/commander/server.ts b/src/commander/server.ts index 82cfc61..8746812 100644 --- a/src/commander/server.ts +++ b/src/commander/server.ts @@ -54,28 +54,15 @@ command try { await tasks.run(ctx); - if (ctx.build && ctx.build.id) { + if (ctx.build && ctx.build.id && !ctx.autoTunnelStarted) { startPingPolling(ctx); } if (ctx.options.fetchResults && ctx.build && ctx.build.id) { startPolling(ctx, '', false, '') } - // await ctx.client.getScreenshotData("567890", false, ctx.log, "755#a5ac6a67-289a-427d-b004-7dfff6c3484b#fanniemae-stage", 'smartui-bbf5b47005'); - } catch (error) { - // Log the error in a human-readable format - // ctx.log.debug(util.inspect(error, { showHidden: false, depth: null })); - - // console.log(`Json Error: ${JSON.stringify(error, null, 2)}`); - // if (error?.message.includes('ENOTFOUND')) { - // ctx.log.error('Error: Network error occurred while fetching build status while polling. Please check your connection and try again.'); - // } else { - // // Log the error in a human-readable format - // ctx.log.debug(util.inspect(error, { showHidden: false, depth: null })); - // ctx.log.error(`Error fetching build status while polling: ${JSON.stringify(error)}`); - // } console.error('Error during server execution:', error); process.exit(1); } From b76cf61af13e304b202595cbb2f86904915ffc1e Mon Sep 17 00:00:00 2001 From: Sushobhit Dua <38429226+sushobhit-lt@users.noreply.github.com> Date: Wed, 17 Sep 2025 22:34:33 +0530 Subject: [PATCH 14/17] optimise code --- src/commander/server.ts | 2 -- src/lib/snapshotQueue.ts | 2 +- src/tasks/createBuildExec.ts | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/commander/server.ts b/src/commander/server.ts index 8746812..4c6a157 100644 --- a/src/commander/server.ts +++ b/src/commander/server.ts @@ -9,7 +9,6 @@ import createBuildExec from '../tasks/createBuildExec.js'; import snapshotQueue from '../lib/snapshotQueue.js'; import { startPolling, startPingPolling } from '../lib/utils.js'; import startTunnel from '../tasks/startTunnel.js' -const util = require('util'); const command = new Command(); @@ -61,7 +60,6 @@ command startPolling(ctx, '', false, '') } - } catch (error) { console.error('Error during server execution:', error); process.exit(1); diff --git a/src/lib/snapshotQueue.ts b/src/lib/snapshotQueue.ts index 0ca45a7..963e060 100644 --- a/src/lib/snapshotQueue.ts +++ b/src/lib/snapshotQueue.ts @@ -380,7 +380,7 @@ export default class Queue { useKafkaFlow: resp.data.useKafkaFlow || false, } } else { - if (this.ctx.config.tunnel && this.ctx.config.tunnel?.type === 'auto') { + if (this.ctx.autoTunnelStarted) { await stopTunnelHelper(this.ctx) } throw new Error('SmartUI capabilities are missing in env variables or in driver capabilities'); diff --git a/src/tasks/createBuildExec.ts b/src/tasks/createBuildExec.ts index d0f30fc..9a469af 100644 --- a/src/tasks/createBuildExec.ts +++ b/src/tasks/createBuildExec.ts @@ -44,7 +44,7 @@ export default (ctx: Context): ListrTask Date: Thu, 18 Sep 2025 13:40:14 +0530 Subject: [PATCH 15/17] fix baseline --- src/lib/uploadAppFigma.ts | 1 + src/lib/uploadWebFigma.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/lib/uploadAppFigma.ts b/src/lib/uploadAppFigma.ts index 07f9e38..0802a89 100644 --- a/src/lib/uploadAppFigma.ts +++ b/src/lib/uploadAppFigma.ts @@ -19,6 +19,7 @@ export default async (ctx: Context): Promise => { smartIgnore: ctx.config.smartIgnore, git: ctx.git, platformType: 'app', + markBaseline: ctx.options.markBaseline, }; const responseData = await ctx.client.processWebFigma(requestBody, ctx.log); diff --git a/src/lib/uploadWebFigma.ts b/src/lib/uploadWebFigma.ts index 061f064..7a58d12 100644 --- a/src/lib/uploadWebFigma.ts +++ b/src/lib/uploadWebFigma.ts @@ -18,6 +18,7 @@ export default async (ctx: Context): Promise => { figma: figmaConfig, smartIgnore: ctx.config.smartIgnore, git: ctx.git, + markBaseline: ctx.options.markBaseline, }; const responseData = await ctx.client.processWebFigma(requestBody, ctx.log); From 6d4c388a34807e5bd24f3262e7b7d96a5aa6e61d Mon Sep 17 00:00:00 2001 From: Sushobhit Dua <38429226+sushobhit-lt@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:02:44 +0530 Subject: [PATCH 16/17] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1cd49e2..c95f5b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lambdatest/smartui-cli", - "version": "4.1.31", + "version": "4.1.32", "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest", "files": [ "dist/**/*" From 84d3fcc1bb2591a19af2d3626203040219bd274c Mon Sep 17 00:00:00 2001 From: Sushobhit Dua <38429226+sushobhit-lt@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:36:10 +0530 Subject: [PATCH 17/17] fix import statement --- src/lib/httpClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/httpClient.ts b/src/lib/httpClient.ts index 3a4cca5..2df51b6 100644 --- a/src/lib/httpClient.ts +++ b/src/lib/httpClient.ts @@ -3,7 +3,7 @@ import FormData from 'form-data'; import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; import { Env, Snapshot, ProcessedSnapshot, Git, Build, Context, DiscoveryErrors } from '../types.js'; import constants from './constants.js'; -import { type Logger } from 'winston' +import type { Logger } from 'winston' import pkgJSON from './../../package.json' import https from 'https';