@lambdatest/smartui-cli 4.1.39 → 4.1.40

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 +420 -168
  2. package/package.json +2 -1
package/dist/index.cjs CHANGED
@@ -12,6 +12,7 @@ var Ajv = require('ajv');
12
12
  var addErrors = require('ajv-errors');
13
13
  var test = require('@playwright/test');
14
14
  var util$1 = require('util');
15
+ var postcss = require('postcss');
15
16
  var winston = require('winston');
16
17
  var stringify = require('json-stringify-safe');
17
18
  var FormData = require('form-data');
@@ -51,6 +52,7 @@ var fastify__default = /*#__PURE__*/_interopDefault(fastify);
51
52
  var fs6__default = /*#__PURE__*/_interopDefault(fs6);
52
53
  var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
53
54
  var addErrors__default = /*#__PURE__*/_interopDefault(addErrors);
55
+ var postcss__default = /*#__PURE__*/_interopDefault(postcss);
54
56
  var stringify__default = /*#__PURE__*/_interopDefault(stringify);
55
57
  var FormData__default = /*#__PURE__*/_interopDefault(FormData);
56
58
  var axios__default = /*#__PURE__*/_interopDefault(axios);
@@ -888,6 +890,10 @@ var ConfigSchema = {
888
890
  type: "boolean",
889
891
  errorMessage: "Invalid config; loadDomContent must be true/false"
890
892
  },
893
+ customCSS: {
894
+ type: "string",
895
+ errorMessage: "Invalid config; customCSS must be a string"
896
+ },
891
897
  approvalThreshold: {
892
898
  type: "number",
893
899
  minimum: 0,
@@ -977,7 +983,9 @@ var SnapshotSchema = {
977
983
  name: {
978
984
  type: "string",
979
985
  minLength: 1,
980
- errorMessage: "Invalid snapshot; name is mandatory and cannot be empty"
986
+ maxLength: 255,
987
+ pattern: "^.*\\S.*$",
988
+ errorMessage: "Invalid snapshot: name is mandatory, cannot be empty, and must not exceed 255 characters."
981
989
  },
982
990
  url: {
983
991
  type: "string",
@@ -1196,6 +1204,10 @@ var SnapshotSchema = {
1196
1204
  minProperties: 1
1197
1205
  },
1198
1206
  errorMessage: "Invalid snapshot options; customCookies must be an array of objects with string properties"
1207
+ },
1208
+ customCSS: {
1209
+ type: "string",
1210
+ errorMessage: "Invalid snapshot options; customCSS must be a string"
1199
1211
  }
1200
1212
  },
1201
1213
  additionalProperties: false
@@ -2203,6 +2215,129 @@ function startSSEListener(ctx) {
2203
2215
  }
2204
2216
  });
2205
2217
  }
2218
+ function resolveCustomCSS(cssValue, configPath, logger2) {
2219
+ if (!cssValue || typeof cssValue !== "string") {
2220
+ throw new Error("customCSS must be a non-empty string");
2221
+ }
2222
+ const trimmed = cssValue.trim();
2223
+ if (trimmed.length === 0) {
2224
+ throw new Error("customCSS cannot be empty");
2225
+ }
2226
+ const path7 = __require("path");
2227
+ const isLikelyFilePath = trimmed.endsWith(".css") || trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/") || path7.isAbsolute(trimmed);
2228
+ if (isLikelyFilePath) {
2229
+ logger2.debug(`customCSS appears to be a file path: ${trimmed}`);
2230
+ const ext = path7.extname(trimmed).toLowerCase();
2231
+ if (ext && ext !== ".css") {
2232
+ throw new Error(`Invalid customCSS file type: ${ext}. Only .css files are supported.`);
2233
+ }
2234
+ const baseDir = path7.dirname(configPath);
2235
+ const resolvedPath = path7.isAbsolute(trimmed) ? trimmed : path7.resolve(baseDir, trimmed);
2236
+ logger2.debug(`Resolved customCSS file path: ${resolvedPath}`);
2237
+ if (!fs6__default.default.existsSync(resolvedPath)) {
2238
+ throw new Error(`customCSS file not found: ${resolvedPath}`);
2239
+ }
2240
+ const stats = fs6__default.default.statSync(resolvedPath);
2241
+ if (!stats.isFile()) {
2242
+ throw new Error(`customCSS path is not a file: ${resolvedPath}`);
2243
+ }
2244
+ try {
2245
+ const cssContent = fs6__default.default.readFileSync(resolvedPath, "utf-8");
2246
+ logger2.debug(`Read ${cssContent.length} characters from customCSS file`);
2247
+ return cssContent;
2248
+ } catch (error) {
2249
+ if (error.message.includes("Invalid CSS syntax")) {
2250
+ throw error;
2251
+ }
2252
+ throw new Error(`Failed to read customCSS file: ${error.message}`);
2253
+ }
2254
+ } else {
2255
+ logger2.debug("customCSS appears to be inline CSS");
2256
+ return trimmed;
2257
+ }
2258
+ }
2259
+ function parseCSSFile(cssContent) {
2260
+ const rules = [];
2261
+ try {
2262
+ const ast = postcss__default.default.parse(cssContent);
2263
+ ast.walkRules((rule) => {
2264
+ var _a, _b;
2265
+ const declarations = [];
2266
+ rule.walkDecls((decl) => {
2267
+ declarations.push({
2268
+ property: decl.prop,
2269
+ value: decl.value,
2270
+ important: decl.important
2271
+ });
2272
+ });
2273
+ rules.push({
2274
+ selector: rule.selector,
2275
+ declarations,
2276
+ source: {
2277
+ start: (_a = rule.source) == null ? void 0 : _a.start,
2278
+ end: (_b = rule.source) == null ? void 0 : _b.end
2279
+ }
2280
+ });
2281
+ });
2282
+ } catch (error) {
2283
+ throw new Error(`Failed to parse CSS: ${error.message}`);
2284
+ }
2285
+ return rules;
2286
+ }
2287
+ function validateCSSSelectors(page, cssRules, logger2) {
2288
+ return __async(this, null, function* () {
2289
+ const failedSelectors = [];
2290
+ let successCount = 0;
2291
+ for (const rule of cssRules) {
2292
+ const selector = rule.selector;
2293
+ if (selector.includes(":") || selector.includes("@") || selector.includes("::")) {
2294
+ successCount++;
2295
+ continue;
2296
+ }
2297
+ try {
2298
+ const elementExists = yield page.evaluate(({ selectorValue }) => {
2299
+ try {
2300
+ const elements = document.querySelectorAll(selectorValue);
2301
+ return elements.length > 0;
2302
+ } catch (error) {
2303
+ return false;
2304
+ }
2305
+ }, { selectorValue: selector });
2306
+ if (elementExists) {
2307
+ successCount++;
2308
+ logger2.debug(`CSS selector valid: ${selector}`);
2309
+ } else {
2310
+ failedSelectors.push(selector);
2311
+ logger2.debug(`CSS selector found no elements: ${selector}`);
2312
+ }
2313
+ } catch (error) {
2314
+ failedSelectors.push(selector);
2315
+ logger2.debug(`CSS selector validation error for "${selector}": ${error.message}`);
2316
+ }
2317
+ }
2318
+ return {
2319
+ successCount,
2320
+ failedSelectors,
2321
+ totalRules: cssRules.length
2322
+ };
2323
+ });
2324
+ }
2325
+ function generateCSSInjectionReport(validationResult, logger2) {
2326
+ const lines = [];
2327
+ lines.push(chalk__default.default.cyan("[SmartUI] CSS Injection Report:"));
2328
+ if (validationResult.successCount > 0) {
2329
+ lines.push(chalk__default.default.green(`[SmartUI] \u2705 Success: ${validationResult.successCount} rules applied.`));
2330
+ }
2331
+ if (validationResult.failedSelectors.length > 0) {
2332
+ lines.push(chalk__default.default.yellow(`[SmartUI] \u26A0\uFE0F Warning: ${validationResult.failedSelectors.length} selector(s) failed to find an element:`));
2333
+ validationResult.failedSelectors.forEach((selector) => {
2334
+ lines.push(chalk__default.default.yellow(`[SmartUI] - ${selector}`));
2335
+ });
2336
+ }
2337
+ const report = lines.join("\n");
2338
+ logger2.info(report);
2339
+ return report;
2340
+ }
2206
2341
 
2207
2342
  // src/lib/server.ts
2208
2343
  var fp = require_find_free_port();
@@ -2532,7 +2667,8 @@ var env_default = () => {
2532
2667
  SMART_GIT,
2533
2668
  SHOW_RENDER_ERRORS,
2534
2669
  SMARTUI_SSE_URL = "https://server-events.lambdatest.com",
2535
- LT_SDK_SKIP_EXECUTION_LOGS
2670
+ LT_SDK_SKIP_EXECUTION_LOGS,
2671
+ MAX_CONCURRENT_PROCESSING
2536
2672
  } = process.env;
2537
2673
  return {
2538
2674
  PROJECT_TOKEN,
@@ -2558,7 +2694,8 @@ var env_default = () => {
2558
2694
  SMART_GIT: SMART_GIT === "true",
2559
2695
  SHOW_RENDER_ERRORS: SHOW_RENDER_ERRORS === "true",
2560
2696
  SMARTUI_SSE_URL,
2561
- LT_SDK_SKIP_EXECUTION_LOGS: LT_SDK_SKIP_EXECUTION_LOGS === "true"
2697
+ LT_SDK_SKIP_EXECUTION_LOGS: LT_SDK_SKIP_EXECUTION_LOGS === "true",
2698
+ MAX_CONCURRENT_PROCESSING: MAX_CONCURRENT_PROCESSING ? parseInt(MAX_CONCURRENT_PROCESSING, 10) : 0
2562
2699
  };
2563
2700
  };
2564
2701
  var logContext = {};
@@ -2653,7 +2790,7 @@ var authExec_default = (ctx) => {
2653
2790
  };
2654
2791
 
2655
2792
  // package.json
2656
- var version = "4.1.39";
2793
+ var version = "4.1.40";
2657
2794
  var package_default = {
2658
2795
  name: "@lambdatest/smartui-cli",
2659
2796
  version,
@@ -2699,6 +2836,7 @@ var package_default = {
2699
2836
  "json-stringify-safe": "^5.0.1",
2700
2837
  listr2: "^7.0.1",
2701
2838
  "node-cache": "^5.1.2",
2839
+ postcss: "^8.5.6",
2702
2840
  sharp: "^0.33.4",
2703
2841
  tsup: "^7.2.0",
2704
2842
  uuid: "^11.0.3",
@@ -3441,6 +3579,18 @@ var ctx_default = (options) => {
3441
3579
  if (!validateConfigFn(config)) {
3442
3580
  throw new Error(validateConfigFn.errors[0].message);
3443
3581
  }
3582
+ if (config.customCSS) {
3583
+ try {
3584
+ config.customCSS = resolveCustomCSS(
3585
+ config.customCSS,
3586
+ options.config,
3587
+ logger_default
3588
+ );
3589
+ logger_default.debug("Successfully resolved and validated customCSS from config");
3590
+ } catch (error) {
3591
+ throw new Error(`customCSS error: ${error.message}`);
3592
+ }
3593
+ }
3444
3594
  } else {
3445
3595
  logger_default.info("## No config file provided. Using default config.");
3446
3596
  }
@@ -3468,6 +3618,8 @@ var ctx_default = (options) => {
3468
3618
  if (options.userName && options.accessKey) {
3469
3619
  env.LT_USERNAME = options.userName;
3470
3620
  env.LT_ACCESS_KEY = options.accessKey;
3621
+ process.env.LT_USERNAME = options.userName;
3622
+ process.env.LT_ACCESS_KEY = options.accessKey;
3471
3623
  }
3472
3624
  } catch (error) {
3473
3625
  console.log(`[smartui] Error: ${error.message}`);
@@ -3540,7 +3692,8 @@ var ctx_default = (options) => {
3540
3692
  loadDomContent,
3541
3693
  approvalThreshold: config.approvalThreshold,
3542
3694
  rejectionThreshold: config.rejectionThreshold,
3543
- showRenderErrors: (_k = config.showRenderErrors) != null ? _k : false
3695
+ showRenderErrors: (_k = config.showRenderErrors) != null ? _k : false,
3696
+ customCSS: config.customCSS
3544
3697
  },
3545
3698
  uploadFilePath: "",
3546
3699
  webStaticConfig: [],
@@ -3579,7 +3732,9 @@ var ctx_default = (options) => {
3579
3732
  baselineBranch: options.baselineBranch || "",
3580
3733
  baselineBuild: options.baselineBuild || "",
3581
3734
  githubURL: options.githubURL || "",
3582
- showRenderErrors: options.showRenderErrors ? true : false
3735
+ showRenderErrors: options.showRenderErrors ? true : false,
3736
+ userName: options.userName || "",
3737
+ accessKey: options.accessKey || ""
3583
3738
  },
3584
3739
  cliVersion: version,
3585
3740
  totalSnapshots: -1,
@@ -3601,6 +3756,21 @@ var ctx_default = (options) => {
3601
3756
  mergeByBuild: false
3602
3757
  };
3603
3758
  };
3759
+
3760
+ // src/lib/execCommandOptions.ts
3761
+ var execCommandOptions_default = (ctx) => {
3762
+ if (ctx.args.execCommand && !ctx.options.userName && !ctx.options.accessKey) {
3763
+ for (const arg of ctx.args.execCommand) {
3764
+ if (arg.includes("lambdaTestUserName")) {
3765
+ ctx.env.LT_USERNAME = arg.split("=")[1];
3766
+ }
3767
+ if (arg.includes("lambdaTestAccessKey")) {
3768
+ ctx.env.LT_ACCESS_KEY = arg.split("=")[1];
3769
+ }
3770
+ }
3771
+ }
3772
+ return ctx;
3773
+ };
3604
3774
  function executeCommand(command11) {
3605
3775
  let dst = process.cwd();
3606
3776
  try {
@@ -4157,6 +4327,19 @@ function prepareSnapshot(snapshot, ctx) {
4157
4327
  if (ctx.config.useExtendedViewport) {
4158
4328
  processedOptions.useExtendedViewport = true;
4159
4329
  }
4330
+ try {
4331
+ if (options == null ? void 0 : options.customCSS) {
4332
+ const resolvedCSS = resolveCustomCSS(options.customCSS, "", ctx.log);
4333
+ processedOptions.customCSS = resolvedCSS;
4334
+ ctx.log.debug("Using per-snapshot customCSS (overriding config)");
4335
+ } else if (ctx.config.customCSS) {
4336
+ processedOptions.customCSS = ctx.config.customCSS;
4337
+ ctx.log.debug("Using config customCSS");
4338
+ }
4339
+ } catch (error) {
4340
+ ctx.log.warn(`customCSS warning: ${error.message}`);
4341
+ chalk__default.default.yellow(`[SmartUI] warning: ${error.message}`);
4342
+ }
4160
4343
  processedOptions.allowedAssets = ctx.config.allowedAssets;
4161
4344
  processedOptions.selectors = selectors;
4162
4345
  processedOptions.ignoreDOM = options == null ? void 0 : options.ignoreDOM;
@@ -4560,6 +4743,17 @@ function processSnapshot(snapshot, ctx) {
4560
4743
  if (ctx.config.useExtendedViewport) {
4561
4744
  processedOptions.useExtendedViewport = true;
4562
4745
  }
4746
+ try {
4747
+ if (options == null ? void 0 : options.customCSS) {
4748
+ const resolvedCSS = resolveCustomCSS(options.customCSS, "", ctx.log);
4749
+ processedOptions.customCSS = resolvedCSS;
4750
+ } else if (ctx.config.customCSS) {
4751
+ processedOptions.customCSS = ctx.config.customCSS;
4752
+ }
4753
+ } catch (error) {
4754
+ optionWarnings.add(`${error.message}`);
4755
+ }
4756
+ ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`);
4563
4757
  let navigated = false;
4564
4758
  let previousDeviceType = null;
4565
4759
  let renderViewports;
@@ -4793,6 +4987,21 @@ function processSnapshot(snapshot, ctx) {
4793
4987
  if (ctx.build.checkPendingRequests) {
4794
4988
  yield checkPending();
4795
4989
  }
4990
+ if (processedOptions.customCSS) {
4991
+ try {
4992
+ const cssRules = parseCSSFile(processedOptions.customCSS);
4993
+ const validationResult = yield validateCSSSelectors(page, cssRules, ctx.log);
4994
+ const report = generateCSSInjectionReport(validationResult, ctx.log);
4995
+ if (validationResult.failedSelectors.length > 0) {
4996
+ validationResult.failedSelectors.forEach((selector) => {
4997
+ optionWarnings.add(`customCSS selector not found: ${selector}`);
4998
+ });
4999
+ }
5000
+ } catch (error) {
5001
+ ctx.log.warn(`CSS validation failed: ${error.message}`);
5002
+ optionWarnings.add(`CSS validation error: ${error.message}`);
5003
+ }
5004
+ }
4796
5005
  let hasBrowserErrors = false;
4797
5006
  for (let browser in discoveryErrors.browsers) {
4798
5007
  if (discoveryErrors.browsers[browser]) {
@@ -4841,6 +5050,8 @@ var Queue = class {
4841
5050
  this.processingSnapshot = "";
4842
5051
  this.snapshotNames = [];
4843
5052
  this.variants = [];
5053
+ this.activeProcessingCount = 0;
5054
+ this.MAX_CONCURRENT_PROCESSING = 5;
4844
5055
  this.ctx = ctx;
4845
5056
  }
4846
5057
  enqueue(item) {
@@ -5054,200 +5265,240 @@ var Queue = class {
5054
5265
  }
5055
5266
  processNext() {
5056
5267
  return __async(this, null, function* () {
5057
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
5058
5268
  if (!this.isEmpty()) {
5269
+ const useRemoteDiscovery = this.ctx.env.USE_REMOTE_DISCOVERY || this.ctx.config.useRemoteDiscovery;
5270
+ if (useRemoteDiscovery && !this.ctx.config.delayedUpload && !this.ctx.config.allowDuplicateSnapshotNames) {
5271
+ let maxConcurrentProcessing = this.ctx.env.MAX_CONCURRENT_PROCESSING === 0 ? this.MAX_CONCURRENT_PROCESSING : this.ctx.env.MAX_CONCURRENT_PROCESSING;
5272
+ if (maxConcurrentProcessing > 15 || maxConcurrentProcessing < 1) {
5273
+ this.ctx.log.info(`Larger than 15 concurrent processing. Setting to 5.`);
5274
+ maxConcurrentProcessing = 5;
5275
+ }
5276
+ this.ctx.log.info(`Max concurrent processing: ${maxConcurrentProcessing}`);
5277
+ const snapshotsToProcess = [];
5278
+ const maxSnapshots = Math.min(maxConcurrentProcessing - this.activeProcessingCount, this.snapshots.length);
5279
+ for (let i = 0; i < maxSnapshots; i++) {
5280
+ let snapshot2;
5281
+ if (this.ctx.config.delayedUpload) {
5282
+ snapshot2 = this.snapshots.pop();
5283
+ } else {
5284
+ snapshot2 = this.snapshots.shift();
5285
+ }
5286
+ if (snapshot2) {
5287
+ snapshotsToProcess.push(snapshot2);
5288
+ }
5289
+ }
5290
+ if (snapshotsToProcess.length > 0) {
5291
+ this.activeProcessingCount += snapshotsToProcess.length;
5292
+ const processingPromises = snapshotsToProcess.map((snapshot2) => this.processSnapshot(snapshot2));
5293
+ yield Promise.allSettled(processingPromises);
5294
+ this.activeProcessingCount -= snapshotsToProcess.length;
5295
+ if (!this.isEmpty()) {
5296
+ this.processNext();
5297
+ } else {
5298
+ this.processing = false;
5299
+ }
5300
+ return;
5301
+ }
5302
+ }
5059
5303
  let snapshot;
5060
5304
  if (this.ctx.config.delayedUpload) {
5061
5305
  snapshot = this.snapshots.pop();
5062
5306
  } else {
5063
5307
  snapshot = this.snapshots.shift();
5064
5308
  }
5065
- try {
5066
- this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
5067
- let drop = false;
5068
- if (this.ctx.isStartExec) {
5069
- this.ctx.log.info(`Processing Snapshot: ${snapshot == null ? void 0 : snapshot.name}`);
5070
- }
5071
- if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name) && !this.ctx.config.allowDuplicateSnapshotNames) {
5072
- if (this.ctx.sessionIdToSnapshotNameMap && snapshot.options && snapshot.options.sessionId) {
5073
- if (this.ctx.sessionIdToSnapshotNameMap.has(snapshot.options.sessionId)) {
5074
- console.log(`snapshot.options.sessionId`, snapshot.options.sessionId, `this.ctx.sessionIdToSnapshotNameMap`, JSON.stringify([...this.ctx.sessionIdToSnapshotNameMap]));
5075
- const existingNames = this.ctx.sessionIdToSnapshotNameMap.get(snapshot.options.sessionId) || [];
5076
- if (existingNames.includes(snapshot.name)) {
5077
- drop = true;
5078
- 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.`);
5079
- } else {
5080
- existingNames.push(snapshot.name);
5081
- this.ctx.sessionIdToSnapshotNameMap.set(snapshot.options.sessionId, existingNames);
5082
- }
5309
+ if (snapshot) {
5310
+ yield this.processSnapshot(snapshot);
5311
+ this.processNext();
5312
+ }
5313
+ } else {
5314
+ this.processing = false;
5315
+ }
5316
+ });
5317
+ }
5318
+ processSnapshot(snapshot) {
5319
+ return __async(this, null, function* () {
5320
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
5321
+ try {
5322
+ this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
5323
+ let drop = false;
5324
+ if (this.ctx.isStartExec) {
5325
+ this.ctx.log.info(`Processing Snapshot: ${snapshot == null ? void 0 : snapshot.name}`);
5326
+ }
5327
+ if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name) && !this.ctx.config.allowDuplicateSnapshotNames) {
5328
+ if (this.ctx.sessionIdToSnapshotNameMap && snapshot.options && snapshot.options.sessionId) {
5329
+ if (this.ctx.sessionIdToSnapshotNameMap.has(snapshot.options.sessionId)) {
5330
+ console.log(`snapshot.options.sessionId`, snapshot.options.sessionId, `this.ctx.sessionIdToSnapshotNameMap`, JSON.stringify([...this.ctx.sessionIdToSnapshotNameMap]));
5331
+ const existingNames = this.ctx.sessionIdToSnapshotNameMap.get(snapshot.options.sessionId) || [];
5332
+ if (existingNames.includes(snapshot.name)) {
5333
+ drop = true;
5334
+ 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.`);
5083
5335
  } else {
5084
- this.ctx.sessionIdToSnapshotNameMap.set(snapshot.options.sessionId, [snapshot.name]);
5336
+ existingNames.push(snapshot.name);
5337
+ this.ctx.sessionIdToSnapshotNameMap.set(snapshot.options.sessionId, existingNames);
5085
5338
  }
5086
5339
  } else {
5087
- drop = true;
5088
- 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.`);
5340
+ this.ctx.sessionIdToSnapshotNameMap.set(snapshot.options.sessionId, [snapshot.name]);
5089
5341
  }
5342
+ } else {
5343
+ drop = true;
5344
+ 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.`);
5090
5345
  }
5091
- if (this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
5092
- drop = this.filterExistingVariants(snapshot, this.ctx.config);
5093
- }
5094
- if (snapshot && snapshot.name && !this.snapshotNames.includes(snapshot.name) && !drop) {
5095
- this.snapshotNames.push(snapshot.name);
5346
+ }
5347
+ if (this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
5348
+ drop = this.filterExistingVariants(snapshot, this.ctx.config);
5349
+ }
5350
+ if (snapshot && snapshot.name && !this.snapshotNames.includes(snapshot.name) && !drop) {
5351
+ this.snapshotNames.push(snapshot.name);
5352
+ }
5353
+ if (this.ctx.config.delayedUpload && snapshot && !drop) {
5354
+ this.processGenerateVariants(snapshot);
5355
+ }
5356
+ if (!drop) {
5357
+ const sessionId = (_a = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _a.sessionId;
5358
+ let capsBuildId = "";
5359
+ let capsProjectToken = "";
5360
+ let useCapsBuildId = false;
5361
+ let useKafkaFlowCaps = false;
5362
+ if (sessionId && ((_b = this.ctx.sessionCapabilitiesMap) == null ? void 0 : _b.has(sessionId))) {
5363
+ const cachedCapabilities = this.ctx.sessionCapabilitiesMap.get(sessionId);
5364
+ capsProjectToken = (cachedCapabilities == null ? void 0 : cachedCapabilities.projectToken) || "";
5365
+ capsBuildId = (cachedCapabilities == null ? void 0 : cachedCapabilities.buildId) || "";
5366
+ useKafkaFlowCaps = (cachedCapabilities == null ? void 0 : cachedCapabilities.useKafkaFlow) || false;
5367
+ if (capsBuildId != "" && capsProjectToken != "") {
5368
+ useCapsBuildId = true;
5369
+ }
5096
5370
  }
5097
- if (this.ctx.config.delayedUpload && snapshot && !drop) {
5098
- this.processGenerateVariants(snapshot);
5371
+ let processedSnapshot, warnings, discoveryErrors;
5372
+ if (this.ctx.env.USE_REMOTE_DISCOVERY || this.ctx.config.useRemoteDiscovery) {
5373
+ this.ctx.log.debug(`Using remote discovery`);
5374
+ let result = yield prepareSnapshot(snapshot, this.ctx);
5375
+ processedSnapshot = result.processedSnapshot;
5376
+ warnings = result.warnings;
5377
+ } else {
5378
+ this.ctx.log.debug(`Using local discovery`);
5379
+ let result = yield processSnapshot(snapshot, this.ctx);
5380
+ processedSnapshot = result.processedSnapshot;
5381
+ warnings = result.warnings;
5382
+ discoveryErrors = result.discoveryErrors;
5099
5383
  }
5100
- if (!drop) {
5101
- const sessionId = (_a = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _a.sessionId;
5102
- let capsBuildId = "";
5103
- let capsProjectToken = "";
5104
- let useCapsBuildId = false;
5105
- let useKafkaFlowCaps = false;
5106
- if (sessionId && ((_b = this.ctx.sessionCapabilitiesMap) == null ? void 0 : _b.has(sessionId))) {
5107
- const cachedCapabilities = this.ctx.sessionCapabilitiesMap.get(sessionId);
5108
- capsProjectToken = (cachedCapabilities == null ? void 0 : cachedCapabilities.projectToken) || "";
5109
- capsBuildId = (cachedCapabilities == null ? void 0 : cachedCapabilities.buildId) || "";
5110
- useKafkaFlowCaps = (cachedCapabilities == null ? void 0 : cachedCapabilities.useKafkaFlow) || false;
5111
- if (capsBuildId != "" && capsProjectToken != "") {
5112
- useCapsBuildId = true;
5384
+ if (useCapsBuildId) {
5385
+ this.ctx.log.info(`Using cached buildId: ${capsBuildId}`);
5386
+ let approvalThreshold = ((_c = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _c.approvalThreshold) || this.ctx.config.approvalThreshold;
5387
+ let rejectionThreshold = ((_d = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _d.rejectionThreshold) || this.ctx.config.rejectionThreshold;
5388
+ if (useKafkaFlowCaps) {
5389
+ let snapshotUuid = uuid.v4();
5390
+ if (((_e = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _e.contextId) && ((_f = this.ctx.contextToSnapshotMap) == null ? void 0 : _f.has(snapshot.options.contextId))) {
5391
+ snapshotUuid = snapshot.options.contextId;
5113
5392
  }
5114
- }
5115
- let processedSnapshot, warnings, discoveryErrors;
5116
- if (this.ctx.env.USE_REMOTE_DISCOVERY || this.ctx.config.useRemoteDiscovery) {
5117
- this.ctx.log.debug(`Using remote discovery`);
5118
- let result = yield prepareSnapshot(snapshot, this.ctx);
5119
- processedSnapshot = result.processedSnapshot;
5120
- warnings = result.warnings;
5393
+ let uploadDomToS3 = this.ctx.config.useLambdaInternal || uploadDomToS3ViaEnv3;
5394
+ if (!uploadDomToS3) {
5395
+ this.ctx.log.debug(`Uploading dom to S3 for snapshot using presigned URL for CAPS`);
5396
+ const presignedResponse = yield this.ctx.client.getS3PresignedURLForSnapshotUploadCaps(this.ctx, processedSnapshot.name, snapshotUuid, capsBuildId, capsProjectToken);
5397
+ const uploadUrl = presignedResponse.data.url;
5398
+ yield this.ctx.client.uploadSnapshotToS3Caps(this.ctx, uploadUrl, processedSnapshot, capsProjectToken);
5399
+ } else {
5400
+ this.ctx.log.debug(`Uploading dom to S3 for snapshot using LSRS`);
5401
+ yield this.ctx.client.sendDomToLSRSForCaps(this.ctx, processedSnapshot, snapshotUuid, capsBuildId, capsProjectToken);
5402
+ }
5403
+ yield this.ctx.client.processSnapshotCaps(this.ctx, processedSnapshot, snapshotUuid, capsBuildId, capsProjectToken, discoveryErrors, calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config), (_g = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _g.sync, approvalThreshold, rejectionThreshold);
5121
5404
  } else {
5122
- this.ctx.log.debug(`Using local discovery`);
5123
- let result = yield processSnapshot(snapshot, this.ctx);
5124
- processedSnapshot = result.processedSnapshot;
5125
- warnings = result.warnings;
5126
- discoveryErrors = result.discoveryErrors;
5405
+ yield this.ctx.client.uploadSnapshotForCaps(this.ctx, processedSnapshot, capsBuildId, capsProjectToken, discoveryErrors, calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config), (_h = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _h.sync, approvalThreshold, rejectionThreshold);
5127
5406
  }
5128
- if (useCapsBuildId) {
5129
- this.ctx.log.info(`Using cached buildId: ${capsBuildId}`);
5130
- let approvalThreshold = ((_c = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _c.approvalThreshold) || this.ctx.config.approvalThreshold;
5131
- let rejectionThreshold = ((_d = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _d.rejectionThreshold) || this.ctx.config.rejectionThreshold;
5132
- if (useKafkaFlowCaps) {
5133
- let snapshotUuid = uuid.v4();
5134
- if (((_e = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _e.contextId) && ((_f = this.ctx.contextToSnapshotMap) == null ? void 0 : _f.has(snapshot.options.contextId))) {
5135
- snapshotUuid = snapshot.options.contextId;
5407
+ const cachedCapabilities = this.ctx.sessionCapabilitiesMap.get(sessionId);
5408
+ const currentCount = (cachedCapabilities == null ? void 0 : cachedCapabilities.snapshotCount) || 0;
5409
+ cachedCapabilities.snapshotCount = currentCount + 1;
5410
+ this.ctx.sessionCapabilitiesMap.set(sessionId, cachedCapabilities);
5411
+ if (((_i = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _i.contextId) && this.ctx.contextToSnapshotMap) {
5412
+ this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, capsBuildId);
5413
+ }
5414
+ } else {
5415
+ if (!((_j = this.ctx.build) == null ? void 0 : _j.id)) {
5416
+ if (this.ctx.authenticatedInitially) {
5417
+ let resp = yield this.ctx.client.createBuild(this.ctx.git, this.ctx.config, this.ctx.log, this.ctx.build.name, false, false, false, "");
5418
+ this.ctx.build = {
5419
+ id: resp.data.buildId,
5420
+ name: resp.data.buildName,
5421
+ url: resp.data.buildURL,
5422
+ baseline: resp.data.baseline,
5423
+ useKafkaFlow: resp.data.useKafkaFlow || false
5424
+ };
5425
+ } else {
5426
+ if (this.ctx.autoTunnelStarted) {
5427
+ yield stopTunnelHelper(this.ctx);
5136
5428
  }
5137
- let uploadDomToS3 = this.ctx.config.useLambdaInternal || uploadDomToS3ViaEnv3;
5138
- if (!uploadDomToS3) {
5139
- this.ctx.log.debug(`Uploading dom to S3 for snapshot using presigned URL for CAPS`);
5140
- const presignedResponse = yield this.ctx.client.getS3PresignedURLForSnapshotUploadCaps(this.ctx, processedSnapshot.name, snapshotUuid, capsBuildId, capsProjectToken);
5141
- const uploadUrl = presignedResponse.data.url;
5142
- yield this.ctx.client.uploadSnapshotToS3Caps(this.ctx, uploadUrl, processedSnapshot, capsProjectToken);
5143
- } else {
5144
- this.ctx.log.debug(`Uploading dom to S3 for snapshot using LSRS`);
5145
- yield this.ctx.client.sendDomToLSRSForCaps(this.ctx, processedSnapshot, snapshotUuid, capsBuildId, capsProjectToken);
5429
+ throw new Error("SmartUI capabilities are missing in env variables or in driver capabilities");
5430
+ }
5431
+ if (this.ctx.options.fetchResults) {
5432
+ if (this.ctx.build && this.ctx.build.id) {
5433
+ startPolling(this.ctx, "", false, "");
5146
5434
  }
5147
- yield this.ctx.client.processSnapshotCaps(this.ctx, processedSnapshot, snapshotUuid, capsBuildId, capsProjectToken, discoveryErrors, calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config), (_g = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _g.sync, approvalThreshold, rejectionThreshold);
5148
- } else {
5149
- yield this.ctx.client.uploadSnapshotForCaps(this.ctx, processedSnapshot, capsBuildId, capsProjectToken, discoveryErrors, calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config), (_h = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _h.sync, approvalThreshold, rejectionThreshold);
5150
5435
  }
5151
- const cachedCapabilities = this.ctx.sessionCapabilitiesMap.get(sessionId);
5152
- const currentCount = (cachedCapabilities == null ? void 0 : cachedCapabilities.snapshotCount) || 0;
5153
- cachedCapabilities.snapshotCount = currentCount + 1;
5154
- this.ctx.sessionCapabilitiesMap.set(sessionId, cachedCapabilities);
5155
- if (((_i = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _i.contextId) && this.ctx.contextToSnapshotMap) {
5156
- this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, capsBuildId);
5436
+ }
5437
+ if (this.ctx.build && this.ctx.build.useKafkaFlow) {
5438
+ let snapshotUuid = uuid.v4();
5439
+ let snapshotUploadResponse;
5440
+ if (((_k = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _k.contextId) && ((_l = this.ctx.contextToSnapshotMap) == null ? void 0 : _l.has(snapshot.options.contextId))) {
5441
+ snapshotUuid = snapshot.options.contextId;
5157
5442
  }
5158
- } else {
5159
- if (!((_j = this.ctx.build) == null ? void 0 : _j.id)) {
5160
- if (this.ctx.authenticatedInitially) {
5161
- let resp = yield this.ctx.client.createBuild(this.ctx.git, this.ctx.config, this.ctx.log, this.ctx.build.name, false, false, false, "");
5162
- this.ctx.build = {
5163
- id: resp.data.buildId,
5164
- name: resp.data.buildName,
5165
- url: resp.data.buildURL,
5166
- baseline: resp.data.baseline,
5167
- useKafkaFlow: resp.data.useKafkaFlow || false
5168
- };
5169
- } else {
5170
- if (this.ctx.autoTunnelStarted) {
5171
- yield stopTunnelHelper(this.ctx);
5172
- }
5173
- throw new Error("SmartUI capabilities are missing in env variables or in driver capabilities");
5174
- }
5175
- if (this.ctx.options.fetchResults) {
5176
- if (this.ctx.build && this.ctx.build.id) {
5177
- startPolling(this.ctx, "", false, "");
5178
- }
5179
- }
5443
+ let uploadDomToS3 = this.ctx.config.useLambdaInternal || uploadDomToS3ViaEnv3;
5444
+ if (!uploadDomToS3) {
5445
+ this.ctx.log.debug(`Uploading dom to S3 for snapshot using presigned URL`);
5446
+ const presignedResponse = yield this.ctx.client.getS3PresignedURLForSnapshotUpload(this.ctx, processedSnapshot.name, snapshotUuid);
5447
+ const uploadUrl = presignedResponse.data.url;
5448
+ snapshotUploadResponse = yield this.ctx.client.uploadSnapshotToS3(this.ctx, uploadUrl, processedSnapshot);
5449
+ } else {
5450
+ this.ctx.log.debug(`Uploading dom to S3 for snapshot using LSRS`);
5451
+ snapshotUploadResponse = yield this.ctx.client.sendDomToLSRS(this.ctx, processedSnapshot, snapshotUuid);
5180
5452
  }
5181
- if (this.ctx.build && this.ctx.build.useKafkaFlow) {
5182
- let snapshotUuid = uuid.v4();
5183
- let snapshotUploadResponse;
5184
- if (((_k = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _k.contextId) && ((_l = this.ctx.contextToSnapshotMap) == null ? void 0 : _l.has(snapshot.options.contextId))) {
5185
- snapshotUuid = snapshot.options.contextId;
5186
- }
5187
- let uploadDomToS3 = this.ctx.config.useLambdaInternal || uploadDomToS3ViaEnv3;
5188
- if (!uploadDomToS3) {
5189
- this.ctx.log.debug(`Uploading dom to S3 for snapshot using presigned URL`);
5190
- const presignedResponse = yield this.ctx.client.getS3PresignedURLForSnapshotUpload(this.ctx, processedSnapshot.name, snapshotUuid);
5191
- const uploadUrl = presignedResponse.data.url;
5192
- snapshotUploadResponse = yield this.ctx.client.uploadSnapshotToS3(this.ctx, uploadUrl, processedSnapshot);
5193
- } else {
5194
- this.ctx.log.debug(`Uploading dom to S3 for snapshot using LSRS`);
5195
- snapshotUploadResponse = yield this.ctx.client.sendDomToLSRS(this.ctx, processedSnapshot, snapshotUuid);
5196
- }
5197
- if (!snapshotUploadResponse || Object.keys(snapshotUploadResponse).length === 0) {
5198
- this.ctx.log.debug(`snapshot failed; Unable to upload dom to S3`);
5199
- this.processedSnapshots.push({ name: snapshot == null ? void 0 : snapshot.name, error: `snapshot failed; Unable to upload dom to S3` });
5200
- if (this.ctx.browser) {
5201
- for (let context of this.ctx.browser.contexts()) {
5202
- for (let page of context.pages()) {
5203
- yield page.close();
5204
- this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
5205
- }
5206
- yield context.close();
5207
- this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
5453
+ if (!snapshotUploadResponse || Object.keys(snapshotUploadResponse).length === 0) {
5454
+ this.ctx.log.debug(`snapshot failed; Unable to upload dom to S3`);
5455
+ this.processedSnapshots.push({ name: snapshot == null ? void 0 : snapshot.name, error: `snapshot failed; Unable to upload dom to S3` });
5456
+ if (this.ctx.browser) {
5457
+ for (let context of this.ctx.browser.contexts()) {
5458
+ for (let page of context.pages()) {
5459
+ yield page.close();
5460
+ this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
5208
5461
  }
5462
+ yield context.close();
5463
+ this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
5209
5464
  }
5210
- if ((_m = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _m.contextId) {
5211
- (_o = this.ctx.contextToSnapshotMap) == null ? void 0 : _o.set((_n = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _n.contextId, "2");
5212
- }
5213
- this.processNext();
5214
- } else {
5215
- let approvalThreshold = ((_p = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _p.approvalThreshold) || this.ctx.config.approvalThreshold;
5216
- let rejectionThreshold = ((_q = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _q.rejectionThreshold) || this.ctx.config.rejectionThreshold;
5217
- yield this.ctx.client.processSnapshot(this.ctx, processedSnapshot, snapshotUuid, discoveryErrors, calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config), (_r = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _r.sync, approvalThreshold, rejectionThreshold);
5218
- if (((_s = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _s.contextId) && ((_t = this.ctx.contextToSnapshotMap) == null ? void 0 : _t.has(snapshot.options.contextId))) {
5219
- this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, this.ctx.build.id);
5220
- }
5221
- this.ctx.log.debug(`ContextId: ${(_u = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _u.contextId} status set to uploaded`);
5465
+ }
5466
+ if ((_m = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _m.contextId) {
5467
+ (_o = this.ctx.contextToSnapshotMap) == null ? void 0 : _o.set((_n = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _n.contextId, "2");
5222
5468
  }
5223
5469
  } else {
5224
- this.ctx.log.info(`Uploading snapshot to S3`);
5225
- yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot, discoveryErrors);
5470
+ let approvalThreshold = ((_p = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _p.approvalThreshold) || this.ctx.config.approvalThreshold;
5471
+ let rejectionThreshold = ((_q = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _q.rejectionThreshold) || this.ctx.config.rejectionThreshold;
5472
+ yield this.ctx.client.processSnapshot(this.ctx, processedSnapshot, snapshotUuid, discoveryErrors, calculateVariantCountFromSnapshot(processedSnapshot, this.ctx.config), (_r = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _r.sync, approvalThreshold, rejectionThreshold);
5473
+ if (((_s = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _s.contextId) && ((_t = this.ctx.contextToSnapshotMap) == null ? void 0 : _t.has(snapshot.options.contextId))) {
5474
+ this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, this.ctx.build.id);
5475
+ }
5476
+ this.ctx.log.debug(`ContextId: ${(_u = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _u.contextId} status set to uploaded`);
5226
5477
  }
5227
- this.ctx.totalSnapshots++;
5478
+ } else {
5479
+ this.ctx.log.info(`Uploading snapshot to S3`);
5480
+ yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot, discoveryErrors);
5228
5481
  }
5229
- this.processedSnapshots.push({ name: snapshot == null ? void 0 : snapshot.name, warnings });
5230
- }
5231
- } catch (error) {
5232
- this.ctx.log.debug(`snapshot failed; ${error}`);
5233
- this.processedSnapshots.push({ name: snapshot == null ? void 0 : snapshot.name, error: error.message });
5234
- if (((_v = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _v.contextId) && this.ctx.contextToSnapshotMap) {
5235
- this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, "2");
5482
+ this.ctx.totalSnapshots++;
5236
5483
  }
5484
+ this.processedSnapshots.push({ name: snapshot == null ? void 0 : snapshot.name, warnings });
5237
5485
  }
5238
- if (this.ctx.browser) {
5239
- for (let context of this.ctx.browser.contexts()) {
5240
- for (let page of context.pages()) {
5241
- yield page.close();
5242
- this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
5243
- }
5244
- yield context.close();
5245
- this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
5486
+ } catch (error) {
5487
+ this.ctx.log.debug(`snapshot failed; ${error}`);
5488
+ this.processedSnapshots.push({ name: snapshot == null ? void 0 : snapshot.name, error: error.message });
5489
+ if (((_v = snapshot == null ? void 0 : snapshot.options) == null ? void 0 : _v.contextId) && this.ctx.contextToSnapshotMap) {
5490
+ this.ctx.contextToSnapshotMap.set(snapshot.options.contextId, "2");
5491
+ }
5492
+ }
5493
+ if (this.ctx.browser) {
5494
+ for (let context of this.ctx.browser.contexts()) {
5495
+ for (let page of context.pages()) {
5496
+ yield page.close();
5497
+ this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
5246
5498
  }
5499
+ yield context.close();
5500
+ this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
5247
5501
  }
5248
- this.processNext();
5249
- } else {
5250
- this.processing = false;
5251
5502
  }
5252
5503
  });
5253
5504
  }
@@ -5304,6 +5555,7 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
5304
5555
  ctx.snapshotQueue = new Queue(ctx);
5305
5556
  ctx.totalSnapshots = 0;
5306
5557
  ctx.sourceCommand = "exec";
5558
+ execCommandOptions_default(ctx);
5307
5559
  let tasks = new listr2.Listr(
5308
5560
  [
5309
5561
  authExec_default(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdatest/smartui-cli",
3
- "version": "4.1.39",
3
+ "version": "4.1.40",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"
@@ -37,6 +37,7 @@
37
37
  "json-stringify-safe": "^5.0.1",
38
38
  "listr2": "^7.0.1",
39
39
  "node-cache": "^5.1.2",
40
+ "postcss": "^8.5.6",
40
41
  "sharp": "^0.33.4",
41
42
  "tsup": "^7.2.0",
42
43
  "uuid": "^11.0.3",