@lambdatest/smartui-cli 4.1.33 → 4.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.cjs +261 -58
  2. package/package.json +2 -1
package/dist/index.cjs CHANGED
@@ -61,6 +61,7 @@ var sharp__default = /*#__PURE__*/_interopDefault(sharp);
61
61
  var http__namespace = /*#__PURE__*/_interopNamespace(http);
62
62
 
63
63
  var __defProp = Object.defineProperty;
64
+ var __getOwnPropNames = Object.getOwnPropertyNames;
64
65
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
65
66
  var __hasOwnProp = Object.prototype.hasOwnProperty;
66
67
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -83,6 +84,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
83
84
  return require.apply(this, arguments);
84
85
  throw Error('Dynamic require of "' + x + '" is not supported');
85
86
  });
87
+ var __commonJS = (cb, mod) => function __require2() {
88
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
89
+ };
86
90
  var __async = (__this, __arguments, generator) => {
87
91
  return new Promise((resolve, reject) => {
88
92
  var fulfilled = (value) => {
@@ -104,6 +108,73 @@ var __async = (__this, __arguments, generator) => {
104
108
  });
105
109
  };
106
110
 
111
+ // node_modules/.pnpm/find-free-port@2.0.0/node_modules/find-free-port/index.js
112
+ var require_find_free_port = __commonJS({
113
+ "node_modules/.pnpm/find-free-port@2.0.0/node_modules/find-free-port/index.js"(exports, module) {
114
+ var net = __require("net");
115
+ function findFreePort(beg, ...rest) {
116
+ const p = rest.slice(0, rest.length - 1), cb = rest[rest.length - 1];
117
+ let [end, ip, cnt] = Array.from(p);
118
+ if (!ip && end && !/^\d+$/.test(end)) {
119
+ ip = end;
120
+ end = 65534;
121
+ } else {
122
+ if (end == null) {
123
+ end = 65534;
124
+ }
125
+ }
126
+ if (cnt == null) {
127
+ cnt = 1;
128
+ }
129
+ const retcb = cb;
130
+ const res = [];
131
+ const probe = function(ip2, port, cb2) {
132
+ const s = net.createConnection({ port, host: ip2 });
133
+ s.on("connect", function() {
134
+ s.end();
135
+ cb2(null, port + 1);
136
+ });
137
+ s.on("error", (err) => {
138
+ cb2(port);
139
+ });
140
+ };
141
+ var onprobe = function(port, nextPort) {
142
+ if (port) {
143
+ res.push(port);
144
+ if (res.length >= cnt) {
145
+ retcb(null, ...res);
146
+ } else {
147
+ setImmediate(() => probe(ip, port + 1, onprobe));
148
+ }
149
+ } else {
150
+ if (nextPort >= end) {
151
+ retcb(new Error("No available ports"));
152
+ } else {
153
+ setImmediate(() => probe(ip, nextPort, onprobe));
154
+ }
155
+ }
156
+ };
157
+ return probe(ip, beg, onprobe);
158
+ }
159
+ function findFreePortPmfy(beg, ...rest) {
160
+ const last = rest[rest.length - 1];
161
+ if (typeof last === "function") {
162
+ findFreePort(beg, ...rest);
163
+ } else {
164
+ return new Promise((resolve, reject) => {
165
+ findFreePort(beg, ...rest, (err, ...ports) => {
166
+ if (err)
167
+ reject(err);
168
+ else
169
+ resolve(ports);
170
+ });
171
+ });
172
+ }
173
+ }
174
+ module.exports = findFreePortPmfy;
175
+ }
176
+ });
177
+
107
178
  // src/lib/constants.ts
108
179
  var constants_default = {
109
180
  // default configs
@@ -152,6 +223,8 @@ var constants_default = {
152
223
  EDGE: "edge",
153
224
  EDGE_CHANNEL: "msedge",
154
225
  WEBKIT: "webkit",
226
+ MIN_PORT_RANGE: 49100,
227
+ MAX_PORT_RANGE: 6e4,
155
228
  // discovery browser launch arguments
156
229
  LAUNCH_ARGS: [
157
230
  // disable the translate popup and optimization downloads
@@ -785,6 +858,10 @@ var ConfigSchema = {
785
858
  type: "boolean",
786
859
  errorMessage: "Invalid config; useLambdaInternal must be true/false"
787
860
  },
861
+ useRemoteDiscovery: {
862
+ type: "boolean",
863
+ errorMessage: "Invalid config; useRemoteDiscovery must be true/false"
864
+ },
788
865
  useExtendedViewport: {
789
866
  type: "boolean",
790
867
  errorMessage: "Invalid config; useExtendedViewport must be true/false"
@@ -1089,6 +1166,14 @@ var SnapshotSchema = {
1089
1166
  minimum: 0,
1090
1167
  maximum: 100,
1091
1168
  errorMessage: "Invalid snapshot options; rejectionThreshold must be a number between 0 and 100"
1169
+ },
1170
+ customCookies: {
1171
+ type: "array",
1172
+ items: {
1173
+ type: "object",
1174
+ minProperties: 1
1175
+ },
1176
+ errorMessage: "Invalid snapshot options; customCookies must be an array of objects with string properties"
1092
1177
  }
1093
1178
  },
1094
1179
  additionalProperties: false
@@ -1810,10 +1895,11 @@ function startPdfPolling(ctx) {
1810
1895
  const maxAttempts = 60;
1811
1896
  console.log(chalk__default.default.yellow("Waiting for results..."));
1812
1897
  const interval = setInterval(() => __async(this, null, function* () {
1898
+ var _a;
1813
1899
  attempts++;
1814
1900
  try {
1815
1901
  const response = yield ctx.client.fetchPdfResults(ctx);
1816
- if (response.screenshots) {
1902
+ if (response.screenshots && ((_a = response.build) == null ? void 0 : _a.build_status) === constants_default.BUILD_COMPLETE) {
1817
1903
  clearInterval(interval);
1818
1904
  const pdfGroups = groupScreenshotsByPdf(response.screenshots);
1819
1905
  const pdfsWithMismatches = countPdfsWithMismatches(pdfGroups);
@@ -1971,7 +2057,32 @@ function validateCoordinates(coordString, pageHeight, pageWidth, snapshotName) {
1971
2057
  }
1972
2058
 
1973
2059
  // src/lib/server.ts
2060
+ var fp = require_find_free_port();
1974
2061
  var uploadDomToS3ViaEnv = process.env.USE_LAMBDA_INTERNAL || false;
2062
+ function findAvailablePort(server, startPort, log2) {
2063
+ return __async(this, null, function* () {
2064
+ let currentPort = startPort;
2065
+ try {
2066
+ yield server.listen({ port: currentPort });
2067
+ return currentPort;
2068
+ } catch (error) {
2069
+ if (error.code === "EADDRINUSE") {
2070
+ log2.debug(`Port ${currentPort} is in use, finding available port in range 49100-60000`);
2071
+ const availablePorts = yield fp(constants_default.MIN_PORT_RANGE, constants_default.MAX_PORT_RANGE);
2072
+ if (availablePorts.length > 0) {
2073
+ const freePort = availablePorts[0];
2074
+ yield server.listen({ port: freePort });
2075
+ log2.debug(`Found and started server on port ${freePort}`);
2076
+ return freePort;
2077
+ } else {
2078
+ throw new Error("No available ports found in range 49100-60000");
2079
+ }
2080
+ } else {
2081
+ throw error;
2082
+ }
2083
+ }
2084
+ });
2085
+ }
1975
2086
  var server_default = (ctx) => __async(void 0, null, function* () {
1976
2087
  const server = fastify__default.default({
1977
2088
  logger: {
@@ -2221,10 +2332,18 @@ var server_default = (ctx) => __async(void 0, null, function* () {
2221
2332
  return reply.code(replyCode).send(replyBody);
2222
2333
  }
2223
2334
  }));
2224
- yield server.listen({ port: ctx.options.port });
2225
- let { port } = server.addresses()[0];
2226
- process.env.SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
2227
- process.env.CYPRESS_SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
2335
+ if (ctx.sourceCommand && ctx.sourceCommand === "exec-start") {
2336
+ yield server.listen({ port: ctx.options.port });
2337
+ let { port } = server.addresses()[0];
2338
+ process.env.SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
2339
+ process.env.CYPRESS_SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
2340
+ ctx.log.debug(`Server started successfully on port ${port}`);
2341
+ } else {
2342
+ const actualPort = yield findAvailablePort(server, ctx.options.port, ctx.log);
2343
+ process.env.SMARTUI_SERVER_ADDRESS = `http://localhost:${actualPort}`;
2344
+ process.env.CYPRESS_SMARTUI_SERVER_ADDRESS = `http://localhost:${actualPort}`;
2345
+ ctx.log.debug(`Server started successfully on port ${actualPort}`);
2346
+ }
2228
2347
  return server;
2229
2348
  });
2230
2349
 
@@ -2369,7 +2488,7 @@ var authExec_default = (ctx) => {
2369
2488
  };
2370
2489
 
2371
2490
  // package.json
2372
- var version = "4.1.33";
2491
+ var version = "4.1.35";
2373
2492
  var package_default = {
2374
2493
  name: "@lambdatest/smartui-cli",
2375
2494
  version,
@@ -2425,6 +2544,7 @@ var package_default = {
2425
2544
  "simple-swizzle": "0.2.2"
2426
2545
  },
2427
2546
  devDependencies: {
2547
+ "find-free-port": "^2.0.0",
2428
2548
  typescript: "^5.3.2"
2429
2549
  }
2430
2550
  };
@@ -3041,6 +3161,9 @@ var httpClient = class {
3041
3161
  if (ctx.build.name !== void 0 && ctx.build.name !== "") {
3042
3162
  form.append("buildName", buildName);
3043
3163
  }
3164
+ if (ctx.options.markBaseline) {
3165
+ form.append("markBaseline", ctx.options.markBaseline.toString());
3166
+ }
3044
3167
  try {
3045
3168
  const response = yield this.axiosInstance.request({
3046
3169
  url: ctx.env.SMARTUI_UPLOAD_URL + "/pdf/upload",
@@ -3110,6 +3233,7 @@ var ctx_default = (options) => {
3110
3233
  let buildNameObj;
3111
3234
  let allowDuplicateSnapshotNames = false;
3112
3235
  let useLambdaInternal = false;
3236
+ let useRemoteDiscovery = false;
3113
3237
  let useExtendedViewport = false;
3114
3238
  let loadDomContent = false;
3115
3239
  try {
@@ -3184,6 +3308,9 @@ var ctx_default = (options) => {
3184
3308
  if (config.useLambdaInternal) {
3185
3309
  useLambdaInternal = true;
3186
3310
  }
3311
+ if (config.useRemoteDiscovery) {
3312
+ useRemoteDiscovery = true;
3313
+ }
3187
3314
  if (config.useExtendedViewport) {
3188
3315
  useExtendedViewport = true;
3189
3316
  }
@@ -3219,6 +3346,7 @@ var ctx_default = (options) => {
3219
3346
  requestHeaders: config.requestHeaders || {},
3220
3347
  allowDuplicateSnapshotNames,
3221
3348
  useLambdaInternal,
3349
+ useRemoteDiscovery,
3222
3350
  useExtendedViewport,
3223
3351
  loadDomContent,
3224
3352
  approvalThreshold: config.approvalThreshold,
@@ -3268,6 +3396,7 @@ var ctx_default = (options) => {
3268
3396
  isSnapshotCaptured: false,
3269
3397
  sessionCapabilitiesMap: /* @__PURE__ */ new Map(),
3270
3398
  buildToSnapshotCountMap: /* @__PURE__ */ new Map(),
3399
+ sessionIdToSnapshotNameMap: /* @__PURE__ */ new Map(),
3271
3400
  fetchResultsForBuild: new Array(),
3272
3401
  orgId: 0,
3273
3402
  userId: 0,
@@ -3425,7 +3554,7 @@ var createBuildExec_default = (ctx) => {
3425
3554
  }
3426
3555
  task.output = chalk__default.default.gray(`build id: ${resp.data.buildId}`);
3427
3556
  task.title = "SmartUI build created";
3428
- if (ctx2.env.USE_REMOTE_DISCOVERY) {
3557
+ if (ctx2.env.USE_REMOTE_DISCOVERY || ctx2.config.useRemoteDiscovery) {
3429
3558
  task.output += chalk__default.default.gray(`
3430
3559
  Using remote discovery for this build`);
3431
3560
  }
@@ -3887,6 +4016,39 @@ function processSnapshot(snapshot, ctx) {
3887
4016
  ctx.log.debug("No valid cookies to add");
3888
4017
  }
3889
4018
  }
4019
+ let options = snapshot.options;
4020
+ if ((options == null ? void 0 : options.customCookies) && Array.isArray(options.customCookies) && options.customCookies.length > 0) {
4021
+ ctx.log.debug(`Setting ${options.customCookies.length} custom cookies`);
4022
+ const validCustomCookies = options.customCookies.filter((cookie) => {
4023
+ if (!cookie.name || !cookie.value || !cookie.domain) {
4024
+ ctx.log.debug(`Skipping invalid custom cookie: missing required fields (name, value, or domain)`);
4025
+ return false;
4026
+ }
4027
+ if (cookie.sameSite && !["Strict", "Lax", "None"].includes(cookie.sameSite)) {
4028
+ ctx.log.debug(`Skipping invalid custom cookie: invalid sameSite value '${cookie.sameSite}'`);
4029
+ return false;
4030
+ }
4031
+ return true;
4032
+ }).map((cookie) => ({
4033
+ name: cookie.name,
4034
+ value: cookie.value,
4035
+ domain: cookie.domain,
4036
+ path: cookie.path || "/",
4037
+ httpOnly: cookie.httpOnly || false,
4038
+ secure: cookie.secure || false,
4039
+ sameSite: cookie.sameSite || "Lax"
4040
+ }));
4041
+ if (validCustomCookies.length > 0) {
4042
+ try {
4043
+ yield context.addCookies(validCustomCookies);
4044
+ ctx.log.debug(`Successfully added ${validCustomCookies.length} custom cookies`);
4045
+ } catch (error) {
4046
+ ctx.log.debug(`Failed to add custom cookies: ${error}`);
4047
+ }
4048
+ } else {
4049
+ ctx.log.debug("No valid custom cookies to add");
4050
+ }
4051
+ }
3890
4052
  const page = yield context.newPage();
3891
4053
  let cache = {};
3892
4054
  if (snapshot.dom.resources.length) {
@@ -4042,7 +4204,6 @@ function processSnapshot(snapshot, ctx) {
4042
4204
  route.abort();
4043
4205
  }
4044
4206
  }));
4045
- let options = snapshot.options;
4046
4207
  let optionWarnings = /* @__PURE__ */ new Set();
4047
4208
  let selectors = [];
4048
4209
  let ignoreOrSelectDOM;
@@ -4118,13 +4279,13 @@ function processSnapshot(snapshot, ctx) {
4118
4279
  for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
4119
4280
  switch (key) {
4120
4281
  case "id":
4121
- selectors.push(...value.map((e) => "#" + e));
4282
+ selectors.push(...value.map((e) => e.startsWith("#") ? e : "#" + e));
4122
4283
  break;
4123
4284
  case "class":
4124
- selectors.push(...value.map((e) => "." + e));
4285
+ selectors.push(...value.map((e) => e.startsWith(".") ? e : "." + e));
4125
4286
  break;
4126
4287
  case "xpath":
4127
- selectors.push(...value.map((e) => "xpath=" + e));
4288
+ selectors.push(...value.map((e) => e.startsWith("xpath=") ? e : "xpath=" + e));
4128
4289
  break;
4129
4290
  case "cssSelector":
4130
4291
  selectors.push(...value);
@@ -4182,6 +4343,9 @@ function processSnapshot(snapshot, ctx) {
4182
4343
  yield new Promise((r) => setTimeout(r, 1250));
4183
4344
  if (ctx.config.waitForTimeout)
4184
4345
  yield page.waitForTimeout(ctx.config.waitForTimeout);
4346
+ yield page.waitForLoadState("networkidle", { timeout: 1e4 }).catch(() => {
4347
+ ctx.log.debug("networkidle event failed to fire within 10s");
4348
+ });
4185
4349
  navigated = true;
4186
4350
  ctx.log.debug(`Navigated to ${snapshot.url}`);
4187
4351
  } catch (error) {
@@ -4239,14 +4403,7 @@ function processSnapshot(snapshot, ctx) {
4239
4403
  }
4240
4404
  }
4241
4405
  }
4242
- if (processedOptions.element) {
4243
- let l = yield page.locator(processedOptions.element).all();
4244
- if (l.length === 0) {
4245
- throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${processedOptions.element}`);
4246
- } else if (l.length > 1) {
4247
- throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, multiple elements found for selector ${processedOptions.element}`);
4248
- }
4249
- } else if (selectors.length) {
4406
+ if (selectors.length) {
4250
4407
  let height = 0;
4251
4408
  height = yield page.evaluate(() => {
4252
4409
  const DEFAULT_HEIGHT = 16384;
@@ -4271,7 +4428,6 @@ function processSnapshot(snapshot, ctx) {
4271
4428
  return Math.max(...measurements);
4272
4429
  });
4273
4430
  ctx.log.debug(`Calculated content height: ${height}`);
4274
- let locators = [];
4275
4431
  if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString]))
4276
4432
  processedOptions[ignoreOrSelectBoxes][viewportString] = [];
4277
4433
  for (const selector of selectors) {
@@ -4294,44 +4450,75 @@ function processSnapshot(snapshot, ctx) {
4294
4450
  if (renderViewports.length > 1) {
4295
4451
  optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, coordinates may not be accurate for multiple viewports`);
4296
4452
  }
4297
- const coordinateElement = __spreadValues({
4453
+ __spreadValues({
4298
4454
  type: "coordinates"
4299
4455
  }, validation.coords);
4300
- locators.push(coordinateElement);
4301
- continue;
4302
- }
4303
- let l = yield page.locator(selector).all();
4304
- if (l.length === 0) {
4305
- optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
4306
4456
  continue;
4307
- }
4308
- locators.push(...l);
4309
- }
4310
- for (const locator of locators) {
4311
- if (locator && typeof locator === "object" && locator.hasOwnProperty("type") && locator.type === "coordinates") {
4312
- const coordLocator = locator;
4313
- const { top, bottom, left, right } = coordLocator;
4314
- processedOptions[ignoreOrSelectBoxes][viewportString].push({
4315
- left,
4316
- top,
4317
- right,
4318
- bottom
4319
- });
4320
- continue;
4321
- }
4322
- let bb = yield locator.boundingBox();
4323
- if (bb) {
4324
- const top = bb.y;
4325
- const bottom = bb.y + bb.height;
4326
- if (top <= height && bottom <= height) {
4327
- processedOptions[ignoreOrSelectBoxes][viewportString].push({
4328
- left: bb.x,
4329
- top,
4330
- right: bb.x + bb.width,
4331
- bottom
4332
- });
4457
+ } else {
4458
+ const isXPath = selector.startsWith("xpath=");
4459
+ const selectorValue = isXPath ? selector.substring(6) : selector;
4460
+ const boxes = yield page.evaluate(({ selectorValue: selectorValue2, isXPath: isXPath2 }) => {
4461
+ try {
4462
+ const DEFAULT_HEIGHT = 16384;
4463
+ const DEFAULT_WIDTH = 7680;
4464
+ const body = document.body;
4465
+ const html = document.documentElement;
4466
+ let pageHeight;
4467
+ let pageWidth;
4468
+ if (!body || !html) {
4469
+ pageHeight = DEFAULT_HEIGHT;
4470
+ pageWidth = DEFAULT_WIDTH;
4471
+ } else {
4472
+ const measurements = [
4473
+ (body == null ? void 0 : body.scrollHeight) || 0,
4474
+ (body == null ? void 0 : body.offsetHeight) || 0,
4475
+ (html == null ? void 0 : html.clientHeight) || 0,
4476
+ (html == null ? void 0 : html.scrollHeight) || 0,
4477
+ (html == null ? void 0 : html.offsetHeight) || 0
4478
+ ];
4479
+ const allMeasurementsInvalid = measurements.every((measurement) => !measurement);
4480
+ if (allMeasurementsInvalid) {
4481
+ pageHeight = DEFAULT_HEIGHT;
4482
+ } else {
4483
+ pageHeight = Math.max(...measurements);
4484
+ }
4485
+ const measurementsWidth = [
4486
+ (body == null ? void 0 : body.scrollWidth) || 0,
4487
+ (body == null ? void 0 : body.offsetWidth) || 0,
4488
+ (html == null ? void 0 : html.clientWidth) || 0,
4489
+ (html == null ? void 0 : html.scrollWidth) || 0,
4490
+ (html == null ? void 0 : html.offsetWidth) || 0
4491
+ ];
4492
+ const allMeasurementsInvalidWidth = measurementsWidth.every((measurement) => !measurement);
4493
+ if (allMeasurementsInvalidWidth) {
4494
+ pageWidth = DEFAULT_WIDTH;
4495
+ } else {
4496
+ pageWidth = Math.max(...measurementsWidth);
4497
+ }
4498
+ }
4499
+ let elements = [];
4500
+ if (isXPath2) {
4501
+ const xpathResult = document.evaluate(
4502
+ selectorValue2,
4503
+ document,
4504
+ null,
4505
+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
4506
+ null
4507
+ );
4508
+ for (let i = 0; i < xpathResult.snapshotLength; i++) {
4509
+ elements.push(xpathResult.snapshotItem(i));
4510
+ }
4511
+ } else {
4512
+ elements = Array.from(document.querySelectorAll(selectorValue2));
4513
+ }
4514
+ return elements;
4515
+ } catch (error) {
4516
+ }
4517
+ }, { selectorValue, isXPath });
4518
+ if (boxes && boxes.length >= 1) {
4519
+ processedOptions[ignoreOrSelectBoxes][viewportString].push(...boxes);
4333
4520
  } else {
4334
- ctx.log.debug(`Bounding box for selector skipped due to exceeding height: ${JSON.stringify({ top, bottom, height })}`);
4521
+ optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
4335
4522
  }
4336
4523
  }
4337
4524
  }
@@ -4616,8 +4803,24 @@ var Queue = class {
4616
4803
  this.ctx.log.info(`Processing Snapshot: ${snapshot == null ? void 0 : snapshot.name}`);
4617
4804
  }
4618
4805
  if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name) && !this.ctx.config.allowDuplicateSnapshotNames) {
4619
- drop = true;
4620
- this.ctx.log.info(`Skipping duplicate SmartUI snapshot '${snapshot.name}'. To capture duplicate screenshots, please set the 'allowDuplicateSnapshotNames' or 'delayedUpload' configuration as true in your config file.`);
4806
+ if (this.ctx.sessionIdToSnapshotNameMap && snapshot.options && snapshot.options.sessionId) {
4807
+ if (this.ctx.sessionIdToSnapshotNameMap.has(snapshot.options.sessionId)) {
4808
+ console.log(`snapshot.options.sessionId`, snapshot.options.sessionId, `this.ctx.sessionIdToSnapshotNameMap`, JSON.stringify([...this.ctx.sessionIdToSnapshotNameMap]));
4809
+ const existingNames = this.ctx.sessionIdToSnapshotNameMap.get(snapshot.options.sessionId) || [];
4810
+ if (existingNames.includes(snapshot.name)) {
4811
+ drop = true;
4812
+ this.ctx.log.info(`Skipping123 duplicate SmartUI snapshot '${snapshot.name}'. To capture duplicate screenshots, please set the 'allowDuplicateSnapshotNames' or 'delayedUpload' configuration as true in your config file.`);
4813
+ } else {
4814
+ existingNames.push(snapshot.name);
4815
+ this.ctx.sessionIdToSnapshotNameMap.set(snapshot.options.sessionId, existingNames);
4816
+ }
4817
+ } else {
4818
+ this.ctx.sessionIdToSnapshotNameMap.set(snapshot.options.sessionId, [snapshot.name]);
4819
+ }
4820
+ } else {
4821
+ drop = true;
4822
+ this.ctx.log.info(`Skipping duplicate SmartUI snapshot '${snapshot.name}'. To capture duplicate screenshots, please set the 'allowDuplicateSnapshotNames' or 'delayedUpload' configuration as true in your config file.`);
4823
+ }
4621
4824
  }
4622
4825
  if (this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
4623
4826
  drop = this.filterExistingVariants(snapshot, this.ctx.config);
@@ -4644,7 +4847,7 @@ var Queue = class {
4644
4847
  }
4645
4848
  }
4646
4849
  let processedSnapshot, warnings, discoveryErrors;
4647
- if (this.ctx.env.USE_REMOTE_DISCOVERY) {
4850
+ if (this.ctx.env.USE_REMOTE_DISCOVERY || this.ctx.config.useRemoteDiscovery) {
4648
4851
  this.ctx.log.debug(`Using remote discovery`);
4649
4852
  let result = yield prepareSnapshot(snapshot, this.ctx);
4650
4853
  processedSnapshot = result.processedSnapshot;
@@ -6441,7 +6644,7 @@ function uploadPdfs(ctx, pdfPath) {
6441
6644
 
6442
6645
  // src/commander/uploadPdf.ts
6443
6646
  var command10 = new commander.Command();
6444
- command10.name("upload-pdf").description("Upload PDFs for visual comparison").argument("<directory>", "Path of the directory containing PDFs").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").option("--buildName <string>", "Specify the build name").action(function(directory, _, command11) {
6647
+ command10.name("upload-pdf").description("Upload PDFs for visual comparison").argument("<directory>", "Path of the directory containing PDFs").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").option("--buildName <string>", "Specify the build name").option("--markBaseline", "Mark this build baseline").action(function(directory, _, command11) {
6445
6648
  return __async(this, null, function* () {
6446
6649
  const options = command11.optsWithGlobals();
6447
6650
  if (options.buildName === "") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdatest/smartui-cli",
3
- "version": "4.1.33",
3
+ "version": "4.1.35",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"
@@ -47,6 +47,7 @@
47
47
  "simple-swizzle": "0.2.2"
48
48
  },
49
49
  "devDependencies": {
50
+ "find-free-port": "^2.0.0",
50
51
  "typescript": "^5.3.2"
51
52
  },
52
53
  "scripts": {