@lambdatest/smartui-cli 4.0.6 → 4.0.8

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 +680 -190
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -4,7 +4,7 @@
4
4
  var commander = require('commander');
5
5
  var which = require('which');
6
6
  var listr2 = require('listr2');
7
- var chalk7 = require('chalk');
7
+ var chalk6 = require('chalk');
8
8
  var path2 = require('path');
9
9
  var fastify = require('fastify');
10
10
  var fs5 = require('fs');
@@ -21,7 +21,7 @@ var sharp = require('sharp');
21
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
22
 
23
23
  var which__default = /*#__PURE__*/_interopDefault(which);
24
- var chalk7__default = /*#__PURE__*/_interopDefault(chalk7);
24
+ var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
25
25
  var path2__default = /*#__PURE__*/_interopDefault(path2);
26
26
  var fastify__default = /*#__PURE__*/_interopDefault(fastify);
27
27
  var fs5__default = /*#__PURE__*/_interopDefault(fs5);
@@ -187,6 +187,9 @@ var constants_default = {
187
187
  MOBILE_OS_IOS: "ios",
188
188
  MOBILE_ORIENTATION_PORTRAIT: "portrait",
189
189
  MOBILE_ORIENTATION_LANDSCAPE: "landscape",
190
+ // build status
191
+ BUILD_COMPLETE: "completed",
192
+ BUILD_ERROR: "error",
190
193
  // CI
191
194
  GITHUB_API_HOST: "https://api.github.com",
192
195
  // log file path
@@ -536,6 +539,10 @@ var ConfigSchema = {
536
539
  errorMessage: "Invalid config; password is mandatory"
537
540
  }
538
541
  }
542
+ },
543
+ delayedUpload: {
544
+ type: "boolean",
545
+ errorMessage: "Invalid config; delayedUpload must be true/false"
539
546
  }
540
547
  },
541
548
  anyOf: [
@@ -672,6 +679,64 @@ var SnapshotSchema = {
672
679
  errorMessage: "Invalid snapshot options; selectDOM xpath array must have unique and non-empty items"
673
680
  }
674
681
  }
682
+ },
683
+ web: {
684
+ type: "object",
685
+ properties: {
686
+ browsers: {
687
+ type: "array",
688
+ items: {
689
+ type: "string",
690
+ enum: [constants_default.CHROME, constants_default.FIREFOX, constants_default.SAFARI, constants_default.EDGE],
691
+ minLength: 1
692
+ },
693
+ uniqueItems: true,
694
+ errorMessage: `Invalid snapshot options; allowed browsers - ${constants_default.CHROME}, ${constants_default.FIREFOX}, ${constants_default.SAFARI}, ${constants_default.EDGE}`
695
+ },
696
+ viewports: {
697
+ type: "array",
698
+ items: {
699
+ type: "array",
700
+ items: {
701
+ type: "number",
702
+ minimum: 1
703
+ },
704
+ minItems: 1,
705
+ maxItems: 2,
706
+ errorMessage: "Invalid snapshot options; each viewport array must contain either a single width or a width and height tuple with positive values."
707
+ },
708
+ uniqueItems: true,
709
+ errorMessage: "Invalid snapshot options; viewports must be an array of unique arrays."
710
+ }
711
+ },
712
+ required: ["viewports"],
713
+ errorMessage: "Invalid snapshot options; web must include viewports property."
714
+ },
715
+ mobile: {
716
+ type: "object",
717
+ properties: {
718
+ devices: {
719
+ type: "array",
720
+ items: {
721
+ type: "string",
722
+ enum: Object.keys(constants_default.SUPPORTED_MOBILE_DEVICES),
723
+ minLength: 1
724
+ },
725
+ uniqueItems: true,
726
+ errorMessage: "Invalid snapshot options; devices must be an array of unique supported mobile devices."
727
+ },
728
+ fullPage: {
729
+ type: "boolean",
730
+ errorMessage: "Invalid snapshot options; fullPage must be a boolean."
731
+ },
732
+ orientation: {
733
+ type: "string",
734
+ enum: [constants_default.MOBILE_ORIENTATION_PORTRAIT, constants_default.MOBILE_ORIENTATION_LANDSCAPE],
735
+ errorMessage: "Invalid snapshot options; orientation must be either 'portrait' or 'landscape'."
736
+ }
737
+ },
738
+ required: ["devices"],
739
+ errorMessage: "Invalid snapshot options; mobile must include devices property."
675
740
  }
676
741
  },
677
742
  additionalProperties: false
@@ -826,7 +891,7 @@ var logger = winston.createLogger({
826
891
  let message = typeof info.message === "object" ? JSON.stringify(info.message).trim() : info.message.trim();
827
892
  switch (info.level) {
828
893
  case "warn":
829
- message = chalk7__default.default.yellow(message);
894
+ message = chalk6__default.default.yellow(message);
830
895
  break;
831
896
  }
832
897
  return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
@@ -853,11 +918,11 @@ var startServer_default = (ctx) => {
853
918
  updateLogContext({ task: "startServer" });
854
919
  try {
855
920
  ctx2.server = yield server_default(ctx2);
856
- task.output = chalk7__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
921
+ task.output = chalk6__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
857
922
  task.title = "SmartUI started";
858
923
  } catch (error) {
859
924
  ctx2.log.debug(error);
860
- task.output = chalk7__default.default.gray(error.message);
925
+ task.output = chalk6__default.default.gray(error.message);
861
926
  throw new Error("SmartUI server setup failed");
862
927
  }
863
928
  }),
@@ -871,11 +936,11 @@ var auth_default = (ctx) => {
871
936
  updateLogContext({ task: "auth" });
872
937
  try {
873
938
  yield ctx2.client.auth(ctx2.log);
874
- task.output = chalk7__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
939
+ task.output = chalk6__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
875
940
  task.title = "Authenticated with SmartUI";
876
941
  } catch (error) {
877
942
  ctx2.log.debug(error);
878
- task.output = chalk7__default.default.gray(error.message);
943
+ task.output = chalk6__default.default.gray(error.message);
879
944
  throw new Error("Authentication failed");
880
945
  }
881
946
  }),
@@ -884,7 +949,7 @@ var auth_default = (ctx) => {
884
949
  };
885
950
 
886
951
  // package.json
887
- var version = "4.0.6";
952
+ var version = "4.0.8";
888
953
  var package_default = {
889
954
  name: "@lambdatest/smartui-cli",
890
955
  version,
@@ -985,6 +1050,13 @@ var httpClient = class {
985
1050
  }
986
1051
  }, log2);
987
1052
  }
1053
+ getScreenshotData(buildId, baseline, log2) {
1054
+ return this.request({
1055
+ url: "/screenshot",
1056
+ method: "GET",
1057
+ params: { buildId, baseline }
1058
+ }, log2);
1059
+ }
988
1060
  finalizeBuild(buildId, totalSnapshots, log2) {
989
1061
  let params = { buildId };
990
1062
  if (totalSnapshots > -1)
@@ -1096,7 +1168,7 @@ var httpClient = class {
1096
1168
  }
1097
1169
  };
1098
1170
  var ctx_default = (options) => {
1099
- var _a, _b, _c, _d, _e, _f;
1171
+ var _a, _b, _c, _d, _e, _f, _g;
1100
1172
  let env = env_default();
1101
1173
  let webConfig;
1102
1174
  let mobileConfig;
@@ -1107,6 +1179,8 @@ var ctx_default = (options) => {
1107
1179
  let extensionFiles;
1108
1180
  let ignoreStripExtension;
1109
1181
  let ignoreFilePattern;
1182
+ let fetchResultObj;
1183
+ let fetchResultsFileObj;
1110
1184
  try {
1111
1185
  if (options.config) {
1112
1186
  config = JSON.parse(fs5__default.default.readFileSync(options.config, "utf-8"));
@@ -1126,6 +1200,17 @@ var ctx_default = (options) => {
1126
1200
  extensionFiles = options.files || ["png", "jpeg", "jpg"];
1127
1201
  ignoreStripExtension = options.removeExtensions || false;
1128
1202
  ignoreFilePattern = options.ignoreDir || [];
1203
+ if (options.fetchResults) {
1204
+ if (options.fetchResults !== true && !options.fetchResults.endsWith(".json")) {
1205
+ console.error("Error: The file extension for --fetch-results must be .json");
1206
+ process.exit(1);
1207
+ }
1208
+ fetchResultObj = true;
1209
+ fetchResultsFileObj = options.fetchResults === true ? "results.json" : options.fetchResults;
1210
+ } else {
1211
+ fetchResultObj = false;
1212
+ fetchResultsFileObj = "";
1213
+ }
1129
1214
  } catch (error) {
1130
1215
  console.log(`[smartui] Error: ${error.message}`);
1131
1216
  process.exit();
@@ -1159,7 +1244,8 @@ var ctx_default = (options) => {
1159
1244
  scrollTime: config.scrollTime || constants_default.DEFAULT_SCROLL_TIME,
1160
1245
  allowedHostnames: config.allowedHostnames || [],
1161
1246
  basicAuthorization: basicAuthObj,
1162
- smartIgnore: (_f = config.smartIgnore) != null ? _f : false
1247
+ smartIgnore: (_f = config.smartIgnore) != null ? _f : false,
1248
+ delayedUpload: (_g = config.delayedUpload) != null ? _g : false
1163
1249
  },
1164
1250
  uploadFilePath: "",
1165
1251
  webStaticConfig: [],
@@ -1185,7 +1271,9 @@ var ctx_default = (options) => {
1185
1271
  ignoreResolutions: resolutionOff,
1186
1272
  fileExtension: extensionFiles,
1187
1273
  stripExtension: ignoreStripExtension,
1188
- ignorePattern: ignoreFilePattern
1274
+ ignorePattern: ignoreFilePattern,
1275
+ fetchResults: fetchResultObj,
1276
+ fetchResultsFileName: fetchResultsFileObj
1189
1277
  },
1190
1278
  cliVersion: version,
1191
1279
  totalSnapshots: -1
@@ -1252,11 +1340,11 @@ var getGitInfo_default = (ctx) => {
1252
1340
  }
1253
1341
  try {
1254
1342
  ctx2.git = git_default(ctx2);
1255
- task.output = chalk7__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
1343
+ task.output = chalk6__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
1256
1344
  task.title = "Fetched git information";
1257
1345
  } catch (error) {
1258
1346
  ctx2.log.debug(error);
1259
- task.output = chalk7__default.default.gray(`${error.message}`);
1347
+ task.output = chalk6__default.default.gray(`${error.message}`);
1260
1348
  throw new Error("Error fetching git repo details");
1261
1349
  }
1262
1350
  }),
@@ -1276,123 +1364,18 @@ var createBuild_default = (ctx) => {
1276
1364
  url: resp.data.buildURL,
1277
1365
  baseline: resp.data.baseline
1278
1366
  };
1279
- task.output = chalk7__default.default.gray(`build id: ${resp.data.buildId}`);
1367
+ task.output = chalk6__default.default.gray(`build id: ${resp.data.buildId}`);
1280
1368
  task.title = "SmartUI build created";
1281
1369
  } catch (error) {
1282
1370
  ctx2.log.debug(error);
1283
- task.output = chalk7__default.default.gray(error.message);
1371
+ task.output = chalk6__default.default.gray(error.message);
1284
1372
  throw new Error("SmartUI build creation failed");
1285
1373
  }
1286
1374
  }),
1287
1375
  rendererOptions: { persistentOutput: true }
1288
1376
  };
1289
1377
  };
1290
- var exec_default = (ctx) => {
1291
- var _a;
1292
- return {
1293
- title: `Executing '${(_a = ctx.args.execCommand) == null ? void 0 : _a.join(" ")}'`,
1294
- task: (ctx2, task) => __async(void 0, null, function* () {
1295
- updateLogContext({ task: "exec" });
1296
- return new Promise((resolve, reject) => {
1297
- var _a2, _b, _c;
1298
- const childProcess = spawn__default.default(ctx2.args.execCommand[0], (_a2 = ctx2.args.execCommand) == null ? void 0 : _a2.slice(1));
1299
- let totalOutput = "";
1300
- const output = listr2.createWritable((chunk) => {
1301
- totalOutput += chunk;
1302
- task.output = chalk7__default.default.gray(totalOutput);
1303
- });
1304
- (_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
1305
- (_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
1306
- childProcess.on("error", (error) => {
1307
- var _a3;
1308
- task.output = chalk7__default.default.gray(`error: ${error.message}`);
1309
- throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
1310
- });
1311
- childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
1312
- var _a3;
1313
- if (code !== null) {
1314
- task.title = `Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' completed; exited with code ${code}`;
1315
- } else if (signal !== null) {
1316
- throw new Error(`Child process killed with signal ${signal}`);
1317
- }
1318
- resolve();
1319
- }));
1320
- });
1321
- }),
1322
- rendererOptions: { persistentOutput: true },
1323
- exitOnError: false
1324
- };
1325
- };
1326
- var processSnapshot_default = (ctx) => {
1327
- return {
1328
- title: `Processing snapshots`,
1329
- task: (ctx2, task) => __async(void 0, null, function* () {
1330
- var _a;
1331
- try {
1332
- yield new Promise((resolve) => {
1333
- let output2 = "";
1334
- const intervalId = setInterval(() => {
1335
- var _a2, _b, _c;
1336
- if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b = ctx2.snapshotQueue) == null ? void 0 : _b.isProcessing())) {
1337
- clearInterval(intervalId);
1338
- resolve();
1339
- } else {
1340
- task.title = `Processing snapshot ${(_c = ctx2.snapshotQueue) == null ? void 0 : _c.getProcessingSnapshot()}`;
1341
- }
1342
- }, 500);
1343
- });
1344
- let output = "";
1345
- for (let snapshot of (_a = ctx2.snapshotQueue) == null ? void 0 : _a.getProcessedSnapshots()) {
1346
- if (snapshot.error)
1347
- output += `${chalk7__default.default.red("\u2717")} ${chalk7__default.default.gray(`${snapshot.name}
1348
- [error] ${snapshot.error}`)}
1349
- `;
1350
- else
1351
- output += `${chalk7__default.default.green("\u2713")} ${chalk7__default.default.gray(snapshot.name)}
1352
- ${snapshot.warnings.length ? chalk7__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
1353
- `) : ""}`;
1354
- }
1355
- task.output = output;
1356
- task.title = "Processed snapshots";
1357
- } catch (error) {
1358
- ctx2.log.debug(error);
1359
- task.output = chalk7__default.default.gray(error.message);
1360
- throw new Error("Processing of snapshots failed");
1361
- }
1362
- }),
1363
- rendererOptions: { persistentOutput: true }
1364
- };
1365
- };
1366
- var finalizeBuild_default = (ctx) => {
1367
- return {
1368
- title: `Finalizing build`,
1369
- task: (ctx2, task) => __async(void 0, null, function* () {
1370
- var _a, _b;
1371
- updateLogContext({ task: "finalizeBuild" });
1372
- try {
1373
- yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
1374
- task.output = chalk7__default.default.gray(`build url: ${ctx2.build.url}`);
1375
- task.title = "Finalized build";
1376
- } catch (error) {
1377
- ctx2.log.debug(error);
1378
- task.output = chalk7__default.default.gray(error.message);
1379
- throw new Error("Finalize build failed");
1380
- }
1381
- try {
1382
- yield (_a = ctx2.browser) == null ? void 0 : _a.close();
1383
- ctx2.log.debug(`Closed browser`);
1384
- yield (_b = ctx2.server) == null ? void 0 : _b.close();
1385
- ctx2.log.debug(`Closed server`);
1386
- let resp = yield ctx2.client.getS3PreSignedURL(ctx2);
1387
- yield ctx2.client.uploadLogs(ctx2, resp.data.url);
1388
- fs5.unlinkSync(constants_default.LOG_FILE_PATH);
1389
- } catch (error) {
1390
- ctx2.log.debug(error);
1391
- }
1392
- }),
1393
- rendererOptions: { persistentOutput: true }
1394
- };
1395
- };
1378
+ var isPollingActive = false;
1396
1379
  function delDir(dir) {
1397
1380
  if (fs5__default.default.existsSync(dir)) {
1398
1381
  fs5__default.default.rmSync(dir, { recursive: true });
@@ -1478,6 +1461,28 @@ function getWebRenderViewports(ctx) {
1478
1461
  }
1479
1462
  return webRenderViewports;
1480
1463
  }
1464
+ function getWebRenderViewportsForOptions(options) {
1465
+ let webRenderViewports = [];
1466
+ if (options.web && Array.isArray(options.web.viewports)) {
1467
+ for (const viewport of options.web.viewports) {
1468
+ if (Array.isArray(viewport) && viewport.length > 0) {
1469
+ let viewportObj = {
1470
+ width: viewport[0]
1471
+ };
1472
+ if (viewport.length > 1) {
1473
+ viewportObj.height = viewport[1];
1474
+ }
1475
+ webRenderViewports.push({
1476
+ viewport: viewportObj,
1477
+ viewportString: `${viewport[0]}${viewport[1] ? "x" + viewport[1] : ""}`,
1478
+ fullPage: viewport.length === 1,
1479
+ device: false
1480
+ });
1481
+ }
1482
+ }
1483
+ }
1484
+ return webRenderViewports;
1485
+ }
1481
1486
  function getMobileRenderViewports(ctx) {
1482
1487
  var _a;
1483
1488
  let mobileRenderViewports = {};
@@ -1499,6 +1504,34 @@ function getMobileRenderViewports(ctx) {
1499
1504
  }
1500
1505
  return mobileRenderViewports;
1501
1506
  }
1507
+ function getMobileRenderViewportsForOptions(options) {
1508
+ var _a;
1509
+ let mobileRenderViewports = {};
1510
+ mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
1511
+ mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
1512
+ if (options.mobile) {
1513
+ for (const device of options.mobile.devices) {
1514
+ let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
1515
+ let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
1516
+ let orientation = options.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
1517
+ let portrait = orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT;
1518
+ let fullPage;
1519
+ if (options.mobile.fullPage === void 0 || options.mobile.fullPage) {
1520
+ fullPage = true;
1521
+ } else {
1522
+ fullPage = false;
1523
+ }
1524
+ (_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
1525
+ viewport: { width: portrait ? width : height, height: portrait ? height : width },
1526
+ viewportString: `${device} (${orientation})`,
1527
+ fullPage,
1528
+ device: true,
1529
+ os
1530
+ });
1531
+ }
1532
+ }
1533
+ return mobileRenderViewports;
1534
+ }
1502
1535
  function getRenderViewports(ctx) {
1503
1536
  let mobileRenderViewports = getMobileRenderViewports(ctx);
1504
1537
  let webRenderViewports = getWebRenderViewports(ctx);
@@ -1508,69 +1541,211 @@ function getRenderViewports(ctx) {
1508
1541
  ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
1509
1542
  ];
1510
1543
  }
1511
- var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
1512
- var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
1513
- var ALLOWED_STATUSES = [200, 201];
1514
- var REQUEST_TIMEOUT = 1e4;
1515
- var MIN_VIEWPORT_HEIGHT = 1080;
1516
- var Queue = class {
1517
- constructor(ctx) {
1518
- this.snapshots = [];
1519
- this.processedSnapshots = [];
1520
- this.processing = false;
1521
- this.processingSnapshot = "";
1522
- this.ctx = ctx;
1523
- }
1524
- enqueue(item) {
1525
- this.snapshots.push(item);
1526
- if (!this.processing) {
1527
- this.processing = true;
1528
- this.processNext();
1529
- }
1544
+ function getRenderViewportsForOptions(options) {
1545
+ let mobileRenderViewports = getMobileRenderViewportsForOptions(options);
1546
+ let webRenderViewports = getWebRenderViewportsForOptions(options);
1547
+ return [
1548
+ ...webRenderViewports,
1549
+ ...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
1550
+ ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
1551
+ ];
1552
+ }
1553
+ process.on("SIGINT", () => {
1554
+ if (isPollingActive) {
1555
+ console.log("Fetching results interrupted. Exiting...");
1556
+ isPollingActive = false;
1557
+ } else {
1558
+ console.log("\nExiting gracefully...");
1530
1559
  }
1531
- processNext() {
1532
- return __async(this, null, function* () {
1533
- if (!this.isEmpty()) {
1534
- const snapshot = this.snapshots.shift();
1535
- try {
1536
- this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
1537
- let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
1538
- yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
1539
- this.ctx.totalSnapshots++;
1540
- this.processedSnapshots.push({ name: snapshot.name, warnings });
1541
- } catch (error) {
1542
- this.ctx.log.debug(`snapshot failed; ${error}`);
1543
- this.processedSnapshots.push({ name: snapshot.name, error: error.message });
1560
+ process.exit(0);
1561
+ });
1562
+ function startPolling(ctx, task) {
1563
+ return __async(this, null, function* () {
1564
+ ctx.log.info("Fetching results in progress....");
1565
+ isPollingActive = true;
1566
+ const intervalId = setInterval(() => __async(this, null, function* () {
1567
+ if (!isPollingActive) {
1568
+ clearInterval(intervalId);
1569
+ return;
1570
+ }
1571
+ try {
1572
+ const resp = yield ctx.client.getScreenshotData(ctx.build.id, ctx.build.baseline, ctx.log);
1573
+ if (!resp.build) {
1574
+ ctx.log.info("Error: Build data is null.");
1575
+ clearInterval(intervalId);
1576
+ isPollingActive = false;
1544
1577
  }
1545
- if (this.ctx.browser) {
1546
- for (let context of this.ctx.browser.contexts()) {
1547
- for (let page of context.pages()) {
1548
- yield page.close();
1549
- this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
1578
+ fs5__default.default.writeFileSync(ctx.options.fetchResultsFileName, JSON.stringify(resp, null, 2));
1579
+ ctx.log.debug(`Updated results in ${ctx.options.fetchResultsFileName}`);
1580
+ if (resp.build.build_status_ind === constants_default.BUILD_COMPLETE || resp.build.build_status_ind === constants_default.BUILD_ERROR) {
1581
+ clearInterval(intervalId);
1582
+ ctx.log.info(`Fetching results completed. Final results written to ${ctx.options.fetchResultsFileName}`);
1583
+ isPollingActive = false;
1584
+ let totalScreenshotsWithMismatches = 0;
1585
+ let totalVariantsWithMismatches = 0;
1586
+ const totalScreenshots = Object.keys(resp.screenshots || {}).length;
1587
+ let totalVariants = 0;
1588
+ for (const [screenshot, variants] of Object.entries(resp.screenshots || {})) {
1589
+ let screenshotHasMismatch = false;
1590
+ let variantMismatchCount = 0;
1591
+ totalVariants += variants.length;
1592
+ for (const variant of variants) {
1593
+ if (variant.mismatch_percentage > 0) {
1594
+ screenshotHasMismatch = true;
1595
+ variantMismatchCount++;
1596
+ }
1597
+ }
1598
+ if (screenshotHasMismatch) {
1599
+ totalScreenshotsWithMismatches++;
1600
+ totalVariantsWithMismatches += variantMismatchCount;
1550
1601
  }
1551
- yield context.close();
1552
- this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
1553
1602
  }
1603
+ ctx.log.info(
1604
+ chalk6__default.default.green.bold(
1605
+ `
1606
+ Summary of Mismatches:
1607
+ ${chalk6__default.default.yellow("Total Variants with Mismatches:")} ${chalk6__default.default.white(totalVariantsWithMismatches)} out of ${chalk6__default.default.white(totalVariants)}
1608
+ ${chalk6__default.default.yellow("Total Screenshots with Mismatches:")} ${chalk6__default.default.white(totalScreenshotsWithMismatches)} out of ${chalk6__default.default.white(totalScreenshots)}
1609
+ ${chalk6__default.default.yellow("Branch Name:")} ${chalk6__default.default.white(resp.build.branch)}
1610
+ ${chalk6__default.default.yellow("Project Name:")} ${chalk6__default.default.white(resp.project.name)}
1611
+ ${chalk6__default.default.yellow("Build ID:")} ${chalk6__default.default.white(resp.build.build_id)}
1612
+ `
1613
+ )
1614
+ );
1554
1615
  }
1555
- this.processNext();
1556
- } else {
1557
- this.processing = false;
1616
+ } catch (error) {
1617
+ if (error.message.includes("ENOTFOUND")) {
1618
+ ctx.log.error("Error: Network error occurred while fetching build results. Please check your connection and try again.");
1619
+ clearInterval(intervalId);
1620
+ } else {
1621
+ ctx.log.error(`Error fetching screenshot data: ${error.message}`);
1622
+ }
1623
+ clearInterval(intervalId);
1624
+ isPollingActive = false;
1558
1625
  }
1559
- });
1560
- }
1561
- isProcessing() {
1562
- return this.processing;
1563
- }
1564
- getProcessingSnapshot() {
1565
- return this.processingSnapshot;
1566
- }
1567
- getProcessedSnapshots() {
1568
- return this.processedSnapshots;
1569
- }
1570
- isEmpty() {
1571
- return this.snapshots && this.snapshots.length ? false : true;
1572
- }
1626
+ }), 5e3);
1627
+ });
1628
+ }
1629
+
1630
+ // src/tasks/exec.ts
1631
+ var exec_default = (ctx) => {
1632
+ var _a;
1633
+ return {
1634
+ title: `Executing '${(_a = ctx.args.execCommand) == null ? void 0 : _a.join(" ")}'`,
1635
+ task: (ctx2, task) => __async(void 0, null, function* () {
1636
+ if (ctx2.options.fetchResults) {
1637
+ startPolling(ctx2);
1638
+ }
1639
+ updateLogContext({ task: "exec" });
1640
+ return new Promise((resolve, reject) => {
1641
+ var _a2, _b, _c;
1642
+ const childProcess = spawn__default.default(ctx2.args.execCommand[0], (_a2 = ctx2.args.execCommand) == null ? void 0 : _a2.slice(1));
1643
+ let totalOutput = "";
1644
+ const output = listr2.createWritable((chunk) => {
1645
+ totalOutput += chunk;
1646
+ task.output = chalk6__default.default.gray(totalOutput);
1647
+ });
1648
+ (_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
1649
+ (_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
1650
+ childProcess.on("error", (error) => {
1651
+ var _a3;
1652
+ task.output = chalk6__default.default.gray(`error: ${error.message}`);
1653
+ throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
1654
+ });
1655
+ childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
1656
+ var _a3;
1657
+ if (code !== null) {
1658
+ task.title = `Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' completed; exited with code ${code}`;
1659
+ } else if (signal !== null) {
1660
+ throw new Error(`Child process killed with signal ${signal}`);
1661
+ }
1662
+ resolve();
1663
+ }));
1664
+ });
1665
+ }),
1666
+ rendererOptions: { persistentOutput: true },
1667
+ exitOnError: false
1668
+ };
1669
+ };
1670
+ var processSnapshot_default = (ctx) => {
1671
+ return {
1672
+ title: `Processing snapshots`,
1673
+ task: (ctx2, task) => __async(void 0, null, function* () {
1674
+ var _a, _b;
1675
+ try {
1676
+ if (ctx2.config.delayedUpload) {
1677
+ ctx2.log.debug("started after processing because of delayedUpload");
1678
+ (_a = ctx2.snapshotQueue) == null ? void 0 : _a.startProcessingfunc();
1679
+ }
1680
+ yield new Promise((resolve) => {
1681
+ let output2 = "";
1682
+ const intervalId = setInterval(() => {
1683
+ var _a2, _b2, _c;
1684
+ if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b2 = ctx2.snapshotQueue) == null ? void 0 : _b2.isProcessing())) {
1685
+ clearInterval(intervalId);
1686
+ resolve();
1687
+ } else {
1688
+ task.title = `Processing snapshot ${(_c = ctx2.snapshotQueue) == null ? void 0 : _c.getProcessingSnapshot()}`;
1689
+ }
1690
+ }, 500);
1691
+ });
1692
+ let output = "";
1693
+ for (let snapshot of (_b = ctx2.snapshotQueue) == null ? void 0 : _b.getProcessedSnapshots()) {
1694
+ if (snapshot.error)
1695
+ output += `${chalk6__default.default.red("\u2717")} ${chalk6__default.default.gray(`${snapshot.name}
1696
+ [error] ${snapshot.error}`)}
1697
+ `;
1698
+ else
1699
+ output += `${chalk6__default.default.green("\u2713")} ${chalk6__default.default.gray(snapshot.name)}
1700
+ ${snapshot.warnings.length ? chalk6__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
1701
+ `) : ""}`;
1702
+ }
1703
+ task.output = output;
1704
+ task.title = "Processed snapshots";
1705
+ } catch (error) {
1706
+ ctx2.log.debug(error);
1707
+ task.output = chalk6__default.default.gray(error.message);
1708
+ throw new Error("Processing of snapshots failed");
1709
+ }
1710
+ }),
1711
+ rendererOptions: { persistentOutput: true }
1712
+ };
1713
+ };
1714
+ var finalizeBuild_default = (ctx) => {
1715
+ return {
1716
+ title: `Finalizing build`,
1717
+ task: (ctx2, task) => __async(void 0, null, function* () {
1718
+ var _a, _b;
1719
+ updateLogContext({ task: "finalizeBuild" });
1720
+ try {
1721
+ yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
1722
+ task.output = chalk6__default.default.gray(`build url: ${ctx2.build.url}`);
1723
+ task.title = "Finalized build";
1724
+ } catch (error) {
1725
+ ctx2.log.debug(error);
1726
+ task.output = chalk6__default.default.gray(error.message);
1727
+ throw new Error("Finalize build failed");
1728
+ }
1729
+ try {
1730
+ yield (_a = ctx2.browser) == null ? void 0 : _a.close();
1731
+ ctx2.log.debug(`Closed browser`);
1732
+ yield (_b = ctx2.server) == null ? void 0 : _b.close();
1733
+ ctx2.log.debug(`Closed server`);
1734
+ let resp = yield ctx2.client.getS3PreSignedURL(ctx2);
1735
+ yield ctx2.client.uploadLogs(ctx2, resp.data.url);
1736
+ fs5.unlinkSync(constants_default.LOG_FILE_PATH);
1737
+ } catch (error) {
1738
+ ctx2.log.debug(error);
1739
+ }
1740
+ }),
1741
+ rendererOptions: { persistentOutput: true }
1742
+ };
1573
1743
  };
1744
+ var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
1745
+ var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
1746
+ var ALLOWED_STATUSES = [200, 201];
1747
+ var REQUEST_TIMEOUT = 1e4;
1748
+ var MIN_VIEWPORT_HEIGHT = 1080;
1574
1749
  function processSnapshot(snapshot, ctx) {
1575
1750
  return __async(this, null, function* () {
1576
1751
  var _a;
@@ -1719,6 +1894,33 @@ function processSnapshot(snapshot, ctx) {
1719
1894
  return true;
1720
1895
  return false;
1721
1896
  };
1897
+ if (options.web && Object.keys(options.web).length) {
1898
+ processedOptions.web = {};
1899
+ if (options.web.viewports && options.web.viewports.length > 0) {
1900
+ processedOptions.web.viewports = options.web.viewports.filter(
1901
+ (viewport) => Array.isArray(viewport) && viewport.length > 0
1902
+ );
1903
+ }
1904
+ if (options.web.browsers && options.web.browsers.length > 0) {
1905
+ processedOptions.web.browsers = options.web.browsers;
1906
+ }
1907
+ }
1908
+ if (options.mobile && Object.keys(options.mobile).length) {
1909
+ processedOptions.mobile = {};
1910
+ if (options.mobile.devices && options.mobile.devices.length > 0) {
1911
+ processedOptions.mobile.devices = options.mobile.devices;
1912
+ }
1913
+ if (options.mobile.hasOwnProperty("fullPage") && typeof options.mobile.fullPage === "boolean") {
1914
+ processedOptions.mobile.fullPage = options.mobile.fullPage;
1915
+ } else {
1916
+ processedOptions.mobile.fullPage = true;
1917
+ }
1918
+ if (options.mobile.hasOwnProperty("orientation") && (options.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT || options.mobile.orientation === constants_default.MOBILE_ORIENTATION_LANDSCAPE)) {
1919
+ processedOptions.mobile.orientation = options.mobile.orientation;
1920
+ } else {
1921
+ processedOptions.mobile.orientation = constants_default.MOBILE_ORIENTATION_PORTRAIT;
1922
+ }
1923
+ }
1722
1924
  if (options.element && Object.keys(options.element).length) {
1723
1925
  if (options.element.id)
1724
1926
  processedOptions.element = "#" + options.element.id;
@@ -1758,7 +1960,12 @@ function processSnapshot(snapshot, ctx) {
1758
1960
  }
1759
1961
  let navigated = false;
1760
1962
  let previousDeviceType = null;
1761
- let renderViewports = getRenderViewports(ctx);
1963
+ let renderViewports;
1964
+ if (snapshot.options && snapshot.options.web || snapshot.options && snapshot.options.mobile) {
1965
+ renderViewports = getRenderViewportsForOptions(snapshot.options);
1966
+ } else {
1967
+ renderViewports = getRenderViewports(ctx);
1968
+ }
1762
1969
  for (const { viewport, viewportString, fullPage, device } of renderViewports) {
1763
1970
  if (previousDeviceType !== null && previousDeviceType !== device) {
1764
1971
  navigated = false;
@@ -1817,6 +2024,7 @@ function processSnapshot(snapshot, ctx) {
1817
2024
  });
1818
2025
  }
1819
2026
  }
2027
+ ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`);
1820
2028
  }
1821
2029
  return {
1822
2030
  processedSnapshot: {
@@ -1831,9 +2039,285 @@ function processSnapshot(snapshot, ctx) {
1831
2039
  });
1832
2040
  }
1833
2041
 
2042
+ // src/lib/snapshotQueue.ts
2043
+ var Queue = class {
2044
+ constructor(ctx) {
2045
+ this.snapshots = [];
2046
+ this.processedSnapshots = [];
2047
+ this.processing = false;
2048
+ this.processingSnapshot = "";
2049
+ this.snapshotNames = [];
2050
+ this.variants = [];
2051
+ this.ctx = ctx;
2052
+ }
2053
+ enqueue(item) {
2054
+ this.snapshots.push(item);
2055
+ if (!this.ctx.config.delayedUpload) {
2056
+ if (!this.processing) {
2057
+ this.processing = true;
2058
+ this.processNext();
2059
+ }
2060
+ }
2061
+ }
2062
+ startProcessingfunc() {
2063
+ if (!this.processing) {
2064
+ this.processing = true;
2065
+ this.processNext();
2066
+ }
2067
+ }
2068
+ processGenerateVariants(snapshot) {
2069
+ if (snapshot.options) {
2070
+ if (snapshot.options.web) {
2071
+ this.generateWebVariants(snapshot, snapshot.options.web);
2072
+ }
2073
+ if (snapshot.options.mobile) {
2074
+ this.generateMobileVariants(snapshot, snapshot.options.mobile);
2075
+ }
2076
+ }
2077
+ if (!snapshot.options || snapshot.options && !snapshot.options.web && !snapshot.options.mobile) {
2078
+ this.generateVariants(snapshot, this.ctx.config);
2079
+ }
2080
+ }
2081
+ generateVariants(snapshot, config) {
2082
+ if (config.web) {
2083
+ const browsers = config.web.browsers || [];
2084
+ const viewports = config.web.viewports || [];
2085
+ for (const browser of browsers) {
2086
+ for (const viewport of viewports) {
2087
+ const width = viewport.width;
2088
+ const height = viewport.height || 0;
2089
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2090
+ this.variants.push(variant);
2091
+ }
2092
+ }
2093
+ }
2094
+ if (config.mobile) {
2095
+ const devices = config.mobile.devices || [];
2096
+ const orientation = config.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
2097
+ for (const device of devices) {
2098
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2099
+ this.variants.push(variant);
2100
+ }
2101
+ }
2102
+ }
2103
+ generateWebVariants(snapshot, webConfig) {
2104
+ var _a, _b, _c;
2105
+ const browsers = (_c = (_b = webConfig.browsers) != null ? _b : (_a = this.ctx.config.web) == null ? void 0 : _a.browsers) != null ? _c : [constants_default.CHROME, constants_default.EDGE, constants_default.FIREFOX, constants_default.SAFARI];
2106
+ const viewports = webConfig.viewports || [];
2107
+ for (const browser of browsers) {
2108
+ for (const viewport of viewports) {
2109
+ const width = viewport[0];
2110
+ const height = viewport[1] || 0;
2111
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2112
+ this.variants.push(variant);
2113
+ }
2114
+ }
2115
+ }
2116
+ generateMobileVariants(snapshot, mobileConfig) {
2117
+ var _a, _b, _c;
2118
+ const devices = mobileConfig.devices || [];
2119
+ const orientation = (_c = (_b = mobileConfig.orientation) != null ? _b : (_a = this.ctx.config.mobile) == null ? void 0 : _a.orientation) != null ? _c : constants_default.MOBILE_ORIENTATION_PORTRAIT;
2120
+ for (const device of devices) {
2121
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2122
+ this.variants.push(variant);
2123
+ }
2124
+ }
2125
+ filterExistingVariants(snapshot, config) {
2126
+ let drop = true;
2127
+ if (snapshot.options && snapshot.options.web) {
2128
+ const webDrop = this.filterWebVariants(snapshot, snapshot.options.web);
2129
+ if (!webDrop)
2130
+ drop = false;
2131
+ }
2132
+ if (snapshot.options && snapshot.options.mobile) {
2133
+ const mobileDrop = this.filterMobileVariants(snapshot, snapshot.options.mobile);
2134
+ if (!mobileDrop)
2135
+ drop = false;
2136
+ }
2137
+ if (!snapshot.options || snapshot.options && !snapshot.options.web && !snapshot.options.mobile) {
2138
+ const configDrop = this.filterVariants(snapshot, config);
2139
+ if (!configDrop)
2140
+ drop = false;
2141
+ }
2142
+ return drop;
2143
+ }
2144
+ filterVariants(snapshot, config) {
2145
+ var _a;
2146
+ let allVariantsDropped = true;
2147
+ if (config.web) {
2148
+ const browsers = config.web.browsers || [];
2149
+ const viewports = config.web.viewports || [];
2150
+ for (const browser of browsers) {
2151
+ for (const viewport of viewports) {
2152
+ const width = viewport.width;
2153
+ const height = viewport.height || 0;
2154
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2155
+ if (!this.variants.includes(variant)) {
2156
+ allVariantsDropped = false;
2157
+ if (!snapshot.options)
2158
+ snapshot.options = {};
2159
+ if (!snapshot.options.web)
2160
+ snapshot.options.web = { browsers: [], viewports: [] };
2161
+ if (!snapshot.options.web.browsers.includes(browser)) {
2162
+ snapshot.options.web.browsers.push(browser);
2163
+ }
2164
+ const viewportExists = snapshot.options.web.viewports.some(
2165
+ (existingViewport) => existingViewport[0] === width && (existingViewport.length < 2 || existingViewport[1] === height)
2166
+ );
2167
+ if (!viewportExists) {
2168
+ if (height > 0) {
2169
+ snapshot.options.web.viewports.push([width, height]);
2170
+ } else {
2171
+ snapshot.options.web.viewports.push([width]);
2172
+ }
2173
+ }
2174
+ }
2175
+ }
2176
+ }
2177
+ }
2178
+ if (config.mobile) {
2179
+ const devices = config.mobile.devices || [];
2180
+ const orientation = config.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
2181
+ const fullPage = (_a = config.mobile.fullPage) != null ? _a : true;
2182
+ for (const device of devices) {
2183
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2184
+ if (!this.variants.includes(variant)) {
2185
+ allVariantsDropped = false;
2186
+ if (!snapshot.options)
2187
+ snapshot.options = {};
2188
+ if (!snapshot.options.mobile)
2189
+ snapshot.options.mobile = { devices: [], orientation: constants_default.MOBILE_ORIENTATION_PORTRAIT, fullPage };
2190
+ if (!snapshot.options.mobile.devices.includes(device)) {
2191
+ snapshot.options.mobile.devices.push(device);
2192
+ }
2193
+ snapshot.options.mobile.orientation = orientation;
2194
+ }
2195
+ }
2196
+ }
2197
+ return allVariantsDropped;
2198
+ }
2199
+ filterWebVariants(snapshot, webConfig) {
2200
+ var _a, _b, _c;
2201
+ const browsers = (_c = (_b = webConfig.browsers) != null ? _b : (_a = this.ctx.config.web) == null ? void 0 : _a.browsers) != null ? _c : [constants_default.CHROME, constants_default.EDGE, constants_default.FIREFOX, constants_default.SAFARI];
2202
+ const viewports = webConfig.viewports || [];
2203
+ let allVariantsDropped = true;
2204
+ if (!snapshot.options) {
2205
+ snapshot.options = {};
2206
+ }
2207
+ snapshot.options.web = { browsers: [], viewports: [] };
2208
+ for (const browser of browsers) {
2209
+ for (const viewport of viewports) {
2210
+ const width = viewport[0];
2211
+ const height = viewport[1] || 0;
2212
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2213
+ if (!this.variants.includes(variant)) {
2214
+ allVariantsDropped = false;
2215
+ if (!snapshot.options.web.browsers.includes(browser)) {
2216
+ snapshot.options.web.browsers.push(browser);
2217
+ }
2218
+ const viewportExists = snapshot.options.web.viewports.some(
2219
+ (existingViewport) => existingViewport[0] === width && (existingViewport.length < 2 || existingViewport[1] === height)
2220
+ );
2221
+ if (!viewportExists) {
2222
+ if (height > 0) {
2223
+ snapshot.options.web.viewports.push([width, height]);
2224
+ } else {
2225
+ snapshot.options.web.viewports.push([width]);
2226
+ }
2227
+ }
2228
+ }
2229
+ }
2230
+ }
2231
+ return allVariantsDropped;
2232
+ }
2233
+ filterMobileVariants(snapshot, mobileConfig) {
2234
+ var _a, _b, _c, _d, _e, _f;
2235
+ if (!snapshot.options) {
2236
+ snapshot.options = {};
2237
+ }
2238
+ const devices = mobileConfig.devices || [];
2239
+ const orientation = (_c = (_b = mobileConfig.orientation) != null ? _b : (_a = this.ctx.config.mobile) == null ? void 0 : _a.orientation) != null ? _c : constants_default.MOBILE_ORIENTATION_PORTRAIT;
2240
+ const fullPage = (_f = (_e = mobileConfig.fullPage) != null ? _e : (_d = this.ctx.config.mobile) == null ? void 0 : _d.fullPage) != null ? _f : true;
2241
+ let allVariantsDropped = true;
2242
+ snapshot.options.mobile = { devices: [], orientation: constants_default.MOBILE_ORIENTATION_PORTRAIT, fullPage };
2243
+ for (const device of devices) {
2244
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2245
+ if (!this.variants.includes(variant)) {
2246
+ allVariantsDropped = false;
2247
+ snapshot.options.mobile.devices.push(device);
2248
+ snapshot.options.mobile.orientation = orientation;
2249
+ }
2250
+ }
2251
+ return allVariantsDropped;
2252
+ }
2253
+ processNext() {
2254
+ return __async(this, null, function* () {
2255
+ if (!this.isEmpty()) {
2256
+ let snapshot;
2257
+ if (this.ctx.config.delayedUpload) {
2258
+ snapshot = this.snapshots.pop();
2259
+ } else {
2260
+ snapshot = this.snapshots.shift();
2261
+ }
2262
+ try {
2263
+ this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
2264
+ let drop = false;
2265
+ if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
2266
+ drop = true;
2267
+ this.ctx.log.info(`Skipping duplicate SmartUI snapshot '${snapshot.name}'. To capture duplicate screenshots, please set the 'delayedUploads' configuration as true in your config file.`);
2268
+ }
2269
+ if (this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
2270
+ drop = this.filterExistingVariants(snapshot, this.ctx.config);
2271
+ }
2272
+ if (snapshot && snapshot.name && !this.snapshotNames.includes(snapshot.name) && !drop) {
2273
+ this.snapshotNames.push(snapshot.name);
2274
+ }
2275
+ if (this.ctx.config.delayedUpload && snapshot && !drop) {
2276
+ this.processGenerateVariants(snapshot);
2277
+ }
2278
+ if (!drop) {
2279
+ let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
2280
+ yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
2281
+ this.ctx.totalSnapshots++;
2282
+ this.processedSnapshots.push({ name: snapshot.name, warnings });
2283
+ }
2284
+ } catch (error) {
2285
+ this.ctx.log.debug(`snapshot failed; ${error}`);
2286
+ this.processedSnapshots.push({ name: snapshot.name, error: error.message });
2287
+ }
2288
+ if (this.ctx.browser) {
2289
+ for (let context of this.ctx.browser.contexts()) {
2290
+ for (let page of context.pages()) {
2291
+ yield page.close();
2292
+ this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
2293
+ }
2294
+ yield context.close();
2295
+ this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
2296
+ }
2297
+ }
2298
+ this.processNext();
2299
+ } else {
2300
+ this.processing = false;
2301
+ }
2302
+ });
2303
+ }
2304
+ isProcessing() {
2305
+ return this.processing;
2306
+ }
2307
+ getProcessingSnapshot() {
2308
+ return this.processingSnapshot;
2309
+ }
2310
+ getProcessedSnapshots() {
2311
+ return this.processedSnapshots;
2312
+ }
2313
+ isEmpty() {
2314
+ return this.snapshots && this.snapshots.length ? false : true;
2315
+ }
2316
+ };
2317
+
1834
2318
  // src/commander/exec.ts
1835
2319
  var command = new commander.Command();
1836
- command.name("exec").description("Run test commands around SmartUI").argument("<command...>", "Command supplied for running tests").option("-P, --port <number>", "Port number for the server").action(function(execCommand, _, command5) {
2320
+ command.name("exec").description("Run test commands around SmartUI").argument("<command...>", "Command supplied for running tests").option("-P, --port <number>", "Port number for the server").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(execCommand, _, command5) {
1837
2321
  return __async(this, null, function* () {
1838
2322
  let ctx = ctx_default(command5.optsWithGlobals());
1839
2323
  if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
@@ -2037,13 +2521,13 @@ function captureScreenshots(ctx) {
2037
2521
  else
2038
2522
  yield captureScreenshotsSync(ctx, staticConfig, browsers);
2039
2523
  delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
2040
- output += `${chalk7__default.default.gray(staticConfig.name)} ${chalk7__default.default.green("\u2713")}
2524
+ output += `${chalk6__default.default.gray(staticConfig.name)} ${chalk6__default.default.green("\u2713")}
2041
2525
  `;
2042
2526
  ctx.task.output = output;
2043
2527
  capturedScreenshots++;
2044
2528
  } catch (error) {
2045
2529
  ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
2046
- output += `${chalk7__default.default.gray(staticConfig.name)} ${chalk7__default.default.red("\u2717")}
2530
+ output += `${chalk6__default.default.gray(staticConfig.name)} ${chalk6__default.default.red("\u2717")}
2047
2531
  `;
2048
2532
  ctx.task.output = output;
2049
2533
  }
@@ -2161,6 +2645,9 @@ var captureScreenshots_default = (ctx) => {
2161
2645
  task: (ctx2, task) => __async(void 0, null, function* () {
2162
2646
  try {
2163
2647
  ctx2.task = task;
2648
+ if (ctx2.options.fetchResults) {
2649
+ startPolling(ctx2, task);
2650
+ }
2164
2651
  updateLogContext({ task: "capture" });
2165
2652
  let { capturedScreenshots, output } = yield captureScreenshots(ctx2);
2166
2653
  if (capturedScreenshots != ctx2.webStaticConfig.length) {
@@ -2169,7 +2656,7 @@ var captureScreenshots_default = (ctx) => {
2169
2656
  task.title = "Screenshots captured successfully";
2170
2657
  } catch (error) {
2171
2658
  ctx2.log.debug(error);
2172
- task.output = chalk7__default.default.gray(`${error.message}`);
2659
+ task.output = chalk6__default.default.gray(`${error.message}`);
2173
2660
  throw new Error("Capturing screenshots failed");
2174
2661
  }
2175
2662
  }),
@@ -2180,7 +2667,7 @@ var captureScreenshots_default = (ctx) => {
2180
2667
 
2181
2668
  // src/commander/capture.ts
2182
2669
  var command2 = new commander.Command();
2183
- command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").option("--parallel", "Capture parallely on all browsers").action(function(file, _, command5) {
2670
+ command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").option("--parallel", "Capture parallely on all browsers").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(file, _, command5) {
2184
2671
  return __async(this, null, function* () {
2185
2672
  let ctx = ctx_default(command5.optsWithGlobals());
2186
2673
  if (!fs5__default.default.existsSync(file)) {
@@ -2228,12 +2715,15 @@ var uploadScreenshots_default = (ctx) => {
2228
2715
  task: (ctx2, task) => __async(void 0, null, function* () {
2229
2716
  try {
2230
2717
  ctx2.task = task;
2718
+ if (ctx2.options.fetchResults) {
2719
+ startPolling(ctx2, task);
2720
+ }
2231
2721
  updateLogContext({ task: "upload" });
2232
2722
  yield uploadScreenshots(ctx2);
2233
2723
  task.title = "Screenshots uploaded successfully";
2234
2724
  } catch (error) {
2235
2725
  ctx2.log.debug(error);
2236
- task.output = chalk7__default.default.gray(`${error.message}`);
2726
+ task.output = chalk6__default.default.gray(`${error.message}`);
2237
2727
  throw new Error("Uploading screenshots failed");
2238
2728
  }
2239
2729
  }),
@@ -2248,7 +2738,7 @@ command3.name("upload").description("Upload screenshots from given directory").a
2248
2738
  return val.split(",").map((ext) => ext.trim().toLowerCase());
2249
2739
  }).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
2250
2740
  return val.split(",").map((pattern) => pattern.trim());
2251
- }).action(function(directory, _, command5) {
2741
+ }).option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(directory, _, command5) {
2252
2742
  return __async(this, null, function* () {
2253
2743
  let ctx = ctx_default(command5.optsWithGlobals());
2254
2744
  if (!fs5__default.default.existsSync(directory)) {
@@ -2328,7 +2818,7 @@ var uploadFigmaDesigns_default2 = (ctx) => {
2328
2818
  ctx2.log.debug(`Figma designs processed: ${results}`);
2329
2819
  } catch (error) {
2330
2820
  ctx2.log.debug(error);
2331
- task.output = chalk7__default.default.gray(`${error.message}`);
2821
+ task.output = chalk6__default.default.gray(`${error.message}`);
2332
2822
  throw new Error("Uploading Figma designs failed");
2333
2823
  }
2334
2824
  }),
@@ -2394,16 +2884,16 @@ var commander_default = program;
2394
2884
  let { data: { latestVersion, deprecated, additionalDescription } } = yield client.checkUpdate(log2);
2395
2885
  log2.info(`
2396
2886
  LambdaTest SmartUI CLI v${package_default.version}`);
2397
- log2.info(chalk7__default.default.yellow(`${additionalDescription}`));
2887
+ log2.info(chalk6__default.default.yellow(`${additionalDescription}`));
2398
2888
  if (deprecated) {
2399
2889
  log2.warn(`This version is deprecated. A new version ${latestVersion} is available!`);
2400
2890
  } else if (package_default.version !== latestVersion) {
2401
- log2.info(chalk7__default.default.green(`A new version ${latestVersion} is available!`));
2891
+ log2.info(chalk6__default.default.green(`A new version ${latestVersion} is available!`));
2402
2892
  } else
2403
- log2.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
2893
+ log2.info(chalk6__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
2404
2894
  } catch (error) {
2405
2895
  log2.debug(error);
2406
- log2.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
2896
+ log2.info(chalk6__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
2407
2897
  }
2408
2898
  commander_default.parse();
2409
2899
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdatest/smartui-cli",
3
- "version": "4.0.6",
3
+ "version": "4.0.8",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"