@lambdatest/smartui-cli 3.0.11 → 4.0.0

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 +754 -423
  2. package/package.json +2 -1
package/dist/index.cjs CHANGED
@@ -4,11 +4,10 @@
4
4
  var commander = require('commander');
5
5
  var which = require('which');
6
6
  var listr2 = require('listr2');
7
- var chalk8 = require('chalk');
7
+ var chalk7 = require('chalk');
8
8
  var path2 = require('path');
9
9
  var fastify = require('fastify');
10
10
  var fs5 = require('fs');
11
- var test = require('@playwright/test');
12
11
  var Ajv = require('ajv');
13
12
  var addErrors = require('ajv-errors');
14
13
  var winston = require('winston');
@@ -16,11 +15,13 @@ var FormData = require('form-data');
16
15
  var axios = require('axios');
17
16
  var child_process = require('child_process');
18
17
  var spawn = require('cross-spawn');
18
+ var test = require('@playwright/test');
19
+ var sharp = require('sharp');
19
20
 
20
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
22
 
22
23
  var which__default = /*#__PURE__*/_interopDefault(which);
23
- var chalk8__default = /*#__PURE__*/_interopDefault(chalk8);
24
+ var chalk7__default = /*#__PURE__*/_interopDefault(chalk7);
24
25
  var path2__default = /*#__PURE__*/_interopDefault(path2);
25
26
  var fastify__default = /*#__PURE__*/_interopDefault(fastify);
26
27
  var fs5__default = /*#__PURE__*/_interopDefault(fs5);
@@ -29,6 +30,7 @@ var addErrors__default = /*#__PURE__*/_interopDefault(addErrors);
29
30
  var FormData__default = /*#__PURE__*/_interopDefault(FormData);
30
31
  var axios__default = /*#__PURE__*/_interopDefault(axios);
31
32
  var spawn__default = /*#__PURE__*/_interopDefault(spawn);
33
+ var sharp__default = /*#__PURE__*/_interopDefault(sharp);
32
34
 
33
35
  var __defProp = Object.defineProperty;
34
36
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -128,6 +130,18 @@ var constants_default = {
128
130
  MOBILE_ORIENTATION_LANDSCAPE: "landscape",
129
131
  // CI
130
132
  GITHUB_API_HOST: "https://api.github.com",
133
+ // log file path
134
+ LOG_FILE_PATH: ".smartui.log",
135
+ // Disallowed file extension
136
+ FILE_EXTENSION_ZIP: ".zip",
137
+ FILE_EXTENSION_GIFS: "gif",
138
+ // Magic Numbers
139
+ MAGIC_NUMBERS: [
140
+ { ext: "jpg", magic: Buffer.from([255, 216, 255]) },
141
+ { ext: "jpeg", magic: Buffer.from([255, 216, 255]) },
142
+ { ext: "png", magic: Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]) },
143
+ { ext: "gif", magic: Buffer.from([71, 73, 70, 56]) }
144
+ ],
131
145
  SUPPORTED_MOBILE_DEVICES: {
132
146
  "Blackberry KEY2 LE": { os: "android", viewport: { width: 412, height: 618 } },
133
147
  "Galaxy A12": { os: "android", viewport: { width: 360, height: 800 } },
@@ -315,327 +329,7 @@ var constants_default = {
315
329
  }
316
330
  };
317
331
 
318
- // src/lib/utils.ts
319
- function delDir(dir) {
320
- if (fs5__default.default.existsSync(dir)) {
321
- fs5__default.default.rmSync(dir, { recursive: true });
322
- }
323
- }
324
- function scrollToBottomAndBackToTop({
325
- frequency = 100,
326
- timing = 8,
327
- remoteWindow = window
328
- } = {}) {
329
- return new Promise((resolve) => {
330
- let scrolls = 1;
331
- let scrollLength = remoteWindow.document.body.scrollHeight / frequency;
332
- (function scroll() {
333
- let scrollBy = scrollLength * scrolls;
334
- remoteWindow.setTimeout(() => {
335
- remoteWindow.scrollTo(0, scrollBy);
336
- if (scrolls < frequency) {
337
- scrolls += 1;
338
- scroll();
339
- }
340
- if (scrolls === frequency) {
341
- remoteWindow.setTimeout(() => {
342
- remoteWindow.scrollTo(0, 0);
343
- resolve();
344
- }, timing);
345
- }
346
- }, timing);
347
- })();
348
- });
349
- }
350
- function launchBrowsers(ctx) {
351
- return __async(this, null, function* () {
352
- let browsers = {};
353
- let launchOptions = { headless: true };
354
- if (ctx.config.web) {
355
- for (const browser of ctx.config.web.browsers) {
356
- switch (browser) {
357
- case constants_default.CHROME:
358
- browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
359
- break;
360
- case constants_default.SAFARI:
361
- browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
362
- break;
363
- case constants_default.FIREFOX:
364
- browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
365
- break;
366
- case constants_default.EDGE:
367
- browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
368
- break;
369
- }
370
- }
371
- }
372
- if (ctx.config.mobile) {
373
- for (const device of ctx.config.mobile.devices) {
374
- if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
375
- browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
376
- else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
377
- browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
378
- }
379
- }
380
- return browsers;
381
- });
382
- }
383
- function closeBrowsers(browsers) {
384
- return __async(this, null, function* () {
385
- var _a;
386
- for (const browserName of Object.keys(browsers))
387
- yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
388
- });
389
- }
390
- function getWebRenderViewports(ctx) {
391
- let webRenderViewports = [];
392
- if (ctx.config.web) {
393
- for (const viewport of ctx.config.web.viewports) {
394
- webRenderViewports.push({
395
- viewport,
396
- viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
397
- fullPage: viewport.height ? false : true,
398
- device: false
399
- });
400
- }
401
- }
402
- return webRenderViewports;
403
- }
404
- function getMobileRenderViewports(ctx) {
405
- var _a;
406
- let mobileRenderViewports = {};
407
- mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
408
- mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
409
- if (ctx.config.mobile) {
410
- for (const device of ctx.config.mobile.devices) {
411
- let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
412
- let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
413
- let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
414
- (_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
415
- viewport: { width: portrait ? width : height, height: portrait ? height : width },
416
- viewportString: `${device} (${ctx.config.mobile.orientation})`,
417
- fullPage: ctx.config.mobile.fullPage,
418
- device: true,
419
- os
420
- });
421
- }
422
- }
423
- return mobileRenderViewports;
424
- }
425
- function getRenderViewports(ctx) {
426
- let mobileRenderViewports = getMobileRenderViewports(ctx);
427
- return [
428
- ...getWebRenderViewports(ctx),
429
- ...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
430
- ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
431
- ];
432
- }
433
- var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
434
- var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
435
- var ALLOWED_STATUSES = [200, 201];
436
- var REQUEST_TIMEOUT = 1e4;
437
- var MIN_VIEWPORT_HEIGHT = 1080;
438
- var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function* () {
439
- var _a;
440
- ctx.log.debug(`Processing snapshot ${snapshot.name}`);
441
- let launchOptions = { headless: true };
442
- let contextOptions = {
443
- javaScriptEnabled: ctx.config.enableJavaScript,
444
- userAgent: constants_default.CHROME_USER_AGENT
445
- };
446
- if (!((_a = ctx.browser) == null ? void 0 : _a.isConnected())) {
447
- if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
448
- launchOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
449
- ctx.browser = yield test.chromium.launch(launchOptions);
450
- ctx.log.debug(`Chromium launched with options ${JSON.stringify(launchOptions)}`);
451
- }
452
- const context = yield ctx.browser.newContext(contextOptions);
453
- ctx.log.debug(`Browser context created with options ${JSON.stringify(contextOptions)}`);
454
- const page = yield context.newPage();
455
- let cache = {};
456
- yield page.route("**/*", (route, request) => __async(void 0, null, function* () {
457
- const requestUrl = request.url();
458
- const requestHostname = new URL(requestUrl).hostname;
459
- try {
460
- if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
461
- throw new Error("resource type mp3/mp4/wav/ogg/webm");
462
- }
463
- ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
464
- if (ctx.config.enableJavaScript)
465
- ALLOWED_RESOURCES.push("script");
466
- const response = yield page.request.fetch(request, { timeout: REQUEST_TIMEOUT });
467
- const body = yield response.body();
468
- if (!body) {
469
- ctx.log.debug(`Handling request ${requestUrl}
470
- - skipping no response`);
471
- } else if (!body.length) {
472
- ctx.log.debug(`Handling request ${requestUrl}
473
- - skipping empty response`);
474
- } else if (requestUrl === snapshot.url) {
475
- ctx.log.debug(`Handling request ${requestUrl}
476
- - skipping root resource`);
477
- } else if (!ctx.config.allowedHostnames.includes(requestHostname)) {
478
- ctx.log.debug(`Handling request ${requestUrl}
479
- - skipping remote resource`);
480
- } else if (cache[requestUrl]) {
481
- ctx.log.debug(`Handling request ${requestUrl}
482
- - skipping already cached resource`);
483
- } else if (body.length > MAX_RESOURCE_SIZE) {
484
- ctx.log.debug(`Handling request ${requestUrl}
485
- - skipping resource larger than 15MB`);
486
- } else if (!ALLOWED_STATUSES.includes(response.status())) {
487
- ctx.log.debug(`Handling request ${requestUrl}
488
- - skipping disallowed status [${response.status()}]`);
489
- } else if (!ALLOWED_RESOURCES.includes(request.resourceType())) {
490
- ctx.log.debug(`Handling request ${requestUrl}
491
- - skipping disallowed resource type [${request.resourceType()}]`);
492
- } else {
493
- ctx.log.debug(`Handling request ${requestUrl}
494
- - content-type ${response.headers()["content-type"]}`);
495
- cache[requestUrl] = {
496
- body: body.toString("base64"),
497
- type: response.headers()["content-type"]
498
- };
499
- }
500
- route.fulfill({
501
- status: response.status(),
502
- headers: response.headers(),
503
- body
504
- });
505
- } catch (error) {
506
- ctx.log.debug(`Handling request ${requestUrl}
507
- - aborted due to ${error.message}`);
508
- route.abort();
509
- }
510
- }));
511
- let options = snapshot.options;
512
- let optionWarnings = /* @__PURE__ */ new Set();
513
- let processedOptions = {};
514
- let selectors = [];
515
- let ignoreOrSelectDOM;
516
- let ignoreOrSelectBoxes;
517
- if (options && Object.keys(options).length) {
518
- ctx.log.debug(`Snapshot options: ${JSON.stringify(options)}`);
519
- const isNotAllEmpty = (obj) => {
520
- var _a2;
521
- for (let key in obj)
522
- if ((_a2 = obj[key]) == null ? void 0 : _a2.length)
523
- return true;
524
- return false;
525
- };
526
- if (options.element && Object.keys(options.element).length) {
527
- if (options.element.id)
528
- processedOptions.element = "#" + options.element.id;
529
- else if (options.element.class)
530
- processedOptions.element = "." + options.element.class;
531
- else if (options.element.cssSelector)
532
- processedOptions.element = options.element.cssSelector;
533
- else if (options.element.xpath)
534
- processedOptions.element = "xpath=" + options.element.xpath;
535
- } else if (options.ignoreDOM && Object.keys(options.ignoreDOM).length && isNotAllEmpty(options.ignoreDOM)) {
536
- processedOptions.ignoreBoxes = {};
537
- ignoreOrSelectDOM = "ignoreDOM";
538
- ignoreOrSelectBoxes = "ignoreBoxes";
539
- } else if (options.selectDOM && Object.keys(options.selectDOM).length && isNotAllEmpty(options.selectDOM)) {
540
- processedOptions.selectBoxes = {};
541
- ignoreOrSelectDOM = "selectDOM";
542
- ignoreOrSelectBoxes = "selectBoxes";
543
- }
544
- if (ignoreOrSelectDOM) {
545
- for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
546
- switch (key) {
547
- case "id":
548
- selectors.push(...value.map((e) => "#" + e));
549
- break;
550
- case "class":
551
- selectors.push(...value.map((e) => "." + e));
552
- break;
553
- case "xpath":
554
- selectors.push(...value.map((e) => "xpath=" + e));
555
- break;
556
- case "cssSelector":
557
- selectors.push(...value);
558
- break;
559
- }
560
- }
561
- }
562
- }
563
- let navigated = false;
564
- let renderViewports = getRenderViewports(ctx);
565
- for (const { viewport, viewportString, fullPage } of renderViewports) {
566
- yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
567
- ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
568
- if (!navigated) {
569
- try {
570
- yield page.goto(snapshot.url, { waitUntil: "domcontentloaded" });
571
- yield new Promise((r) => setTimeout(r, 1250));
572
- if (ctx.config.waitForTimeout)
573
- yield page.waitForTimeout(ctx.config.waitForTimeout);
574
- navigated = true;
575
- ctx.log.debug(`Navigated to ${snapshot.url}`);
576
- } catch (error) {
577
- ctx.log.debug(`Navigation to discovery page failed; ${error}`);
578
- throw new Error(error.message);
579
- }
580
- }
581
- if (ctx.config.enableJavaScript && fullPage)
582
- yield page.evaluate(scrollToBottomAndBackToTop);
583
- try {
584
- yield page.waitForLoadState("networkidle", { timeout: 5e3 });
585
- ctx.log.debug("Network idle 500ms");
586
- } catch (error) {
587
- ctx.log.debug(`Network idle failed due to ${error}`);
588
- }
589
- if (processedOptions.element) {
590
- let l = yield page.locator(processedOptions.element).all();
591
- if (l.length === 0) {
592
- throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${processedOptions.element}`);
593
- } else if (l.length > 1) {
594
- throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, multiple elements found for selector ${processedOptions.element}`);
595
- }
596
- } else if (selectors.length) {
597
- let locators = [];
598
- if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString]))
599
- processedOptions[ignoreOrSelectBoxes][viewportString] = [];
600
- for (const selector of selectors) {
601
- let l = yield page.locator(selector).all();
602
- if (l.length === 0) {
603
- optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
604
- continue;
605
- }
606
- locators.push(...l);
607
- }
608
- for (const locator of locators) {
609
- let bb = yield locator.boundingBox();
610
- if (bb)
611
- processedOptions[ignoreOrSelectBoxes][viewportString].push({
612
- left: bb.x,
613
- top: bb.y,
614
- right: bb.x + bb.width,
615
- bottom: bb.y + bb.height
616
- });
617
- }
618
- }
619
- }
620
- if (snapshot.dom.resources.length) {
621
- for (let resource of snapshot.dom.resources) {
622
- cache[resource.url] = {
623
- body: resource.content,
624
- type: resource.mimetype
625
- };
626
- }
627
- }
628
- return {
629
- processedSnapshot: {
630
- name: snapshot.name,
631
- url: snapshot.url,
632
- dom: Buffer.from(snapshot.dom.html).toString("base64"),
633
- resources: cache,
634
- options: processedOptions
635
- },
636
- warnings: [...optionWarnings, ...snapshot.dom.warnings]
637
- };
638
- });
332
+ // src/lib/schemaValidation.ts
639
333
  var ajv = new Ajv__default.default({ allErrors: true });
640
334
  ajv.addFormat("web-url", {
641
335
  type: "string",
@@ -952,7 +646,15 @@ var validateFigmaDesignConfig = ajv.compile(FigmaDesignConfigSchema);
952
646
 
953
647
  // src/lib/server.ts
954
648
  var server_default = (ctx) => __async(void 0, null, function* () {
955
- const server = fastify__default.default({ logger: ctx.env.LT_SDK_DEBUG ? true : false, bodyLimit: 3e7 });
649
+ const server = fastify__default.default({
650
+ logger: {
651
+ level: "debug",
652
+ stream: { write: (message) => {
653
+ ctx.log.debug(message);
654
+ } }
655
+ },
656
+ bodyLimit: 3e7
657
+ });
956
658
  const opts = {};
957
659
  const SMARTUI_DOM = fs5.readFileSync(path2__default.default.resolve(__dirname, "dom-serializer.js"), "utf-8");
958
660
  server.get("/healthcheck", opts, (_, reply) => {
@@ -962,32 +664,22 @@ var server_default = (ctx) => __async(void 0, null, function* () {
962
664
  reply.code(200).send({ data: { dom: SMARTUI_DOM } });
963
665
  });
964
666
  server.post("/snapshot", opts, (request, reply) => __async(void 0, null, function* () {
667
+ var _a;
965
668
  let replyCode;
966
669
  let replyBody;
967
670
  try {
968
671
  let { snapshot, testType } = request.body;
969
672
  if (!validateSnapshot(snapshot))
970
673
  throw new Error(validateSnapshot.errors[0].message);
971
- let { processedSnapshot, warnings } = yield processSnapshot_default(snapshot, ctx);
972
- yield ctx.client.uploadSnapshot(ctx.build.id, processedSnapshot, testType, ctx.log);
973
- ctx.totalSnapshots++;
674
+ ctx.testType = testType;
675
+ (_a = ctx.snapshotQueue) == null ? void 0 : _a.enqueue(snapshot);
974
676
  replyCode = 200;
975
- replyBody = { data: { message: "success", warnings } };
677
+ replyBody = { data: { message: "success", warnings: [] } };
976
678
  } catch (error) {
977
679
  ctx.log.debug(`snapshot failed; ${error}`);
978
680
  replyCode = 500;
979
681
  replyBody = { error: { message: error.message } };
980
682
  }
981
- if (ctx.browser) {
982
- for (let context of ctx.browser.contexts()) {
983
- for (let page of context.pages()) {
984
- yield page.close();
985
- ctx.log.debug(`Closed browser page`);
986
- }
987
- yield context.close();
988
- ctx.log.debug(`Closed browser context`);
989
- }
990
- }
991
683
  return reply.code(replyCode).send(replyBody);
992
684
  }));
993
685
  yield server.listen({ port: ctx.options.port });
@@ -1002,7 +694,6 @@ var env_default = () => {
1002
694
  const {
1003
695
  PROJECT_TOKEN = "",
1004
696
  SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
1005
- LT_SDK_LOG_LEVEL,
1006
697
  LT_SDK_DEBUG,
1007
698
  SMARTUI_GIT_INFO_FILEPATH,
1008
699
  HTTP_PROXY,
@@ -1017,7 +708,6 @@ var env_default = () => {
1017
708
  return {
1018
709
  PROJECT_TOKEN,
1019
710
  SMARTUI_CLIENT_API_URL,
1020
- LT_SDK_LOG_LEVEL,
1021
711
  LT_SDK_DEBUG,
1022
712
  SMARTUI_GIT_INFO_FILEPATH,
1023
713
  HTTP_PROXY,
@@ -1030,37 +720,37 @@ var env_default = () => {
1030
720
  CURRENT_BRANCH
1031
721
  };
1032
722
  };
1033
- var logContext = { task: "smartui-cli" };
723
+ var logContext = {};
1034
724
  function updateLogContext(newContext) {
1035
725
  logContext = __spreadValues(__spreadValues({}, logContext), newContext);
1036
726
  }
1037
727
  var logLevel = () => {
1038
728
  let env = env_default();
1039
- let debug = env.LT_SDK_DEBUG === "true" ? "debug" : void 0;
1040
- return debug || env.LT_SDK_LOG_LEVEL || "info";
729
+ return env.LT_SDK_DEBUG === "true" ? "debug" : "info";
1041
730
  };
1042
731
  var logger = winston.createLogger({
1043
- level: logLevel(),
1044
732
  format: winston.format.combine(
1045
733
  winston.format.timestamp(),
1046
734
  winston.format.printf((info) => {
1047
735
  let contextString = Object.values(logContext).join(" | ");
1048
- let message = typeof info.message === "object" ? JSON.stringify(info.message) : info.message;
736
+ let message = typeof info.message === "object" ? JSON.stringify(info.message).trim() : info.message.trim();
1049
737
  switch (info.level) {
1050
- case "debug":
1051
- message = chalk8__default.default.blue(message);
1052
- break;
1053
738
  case "warn":
1054
- message = chalk8__default.default.yellow(message);
1055
- break;
1056
- case "error":
1057
- message = chalk8__default.default.red(message);
739
+ message = chalk7__default.default.yellow(message);
1058
740
  break;
1059
741
  }
1060
742
  return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
1061
743
  })
1062
744
  ),
1063
- transports: [new winston.transports.Console(), new winston.transports.File({ filename: ".smartui.log" })]
745
+ transports: [
746
+ new winston.transports.Console({
747
+ level: logLevel()
748
+ }),
749
+ new winston.transports.File({
750
+ level: "debug",
751
+ filename: constants_default.LOG_FILE_PATH
752
+ })
753
+ ]
1064
754
  });
1065
755
  var logger_default = logger;
1066
756
 
@@ -1073,11 +763,11 @@ var startServer_default = (ctx) => {
1073
763
  updateLogContext({ task: "startServer" });
1074
764
  try {
1075
765
  ctx2.server = yield server_default(ctx2);
1076
- task.output = chalk8__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
766
+ task.output = chalk7__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
1077
767
  task.title = "SmartUI started";
1078
768
  } catch (error) {
1079
769
  ctx2.log.debug(error);
1080
- task.output = chalk8__default.default.gray(error.message);
770
+ task.output = chalk7__default.default.gray(error.message);
1081
771
  throw new Error("SmartUI server setup failed");
1082
772
  }
1083
773
  }),
@@ -1091,11 +781,11 @@ var auth_default = (ctx) => {
1091
781
  updateLogContext({ task: "auth" });
1092
782
  try {
1093
783
  yield ctx2.client.auth(ctx2.log);
1094
- task.output = chalk8__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
784
+ task.output = chalk7__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
1095
785
  task.title = "Authenticated with SmartUI";
1096
786
  } catch (error) {
1097
787
  ctx2.log.debug(error);
1098
- task.output = chalk8__default.default.gray(error.message);
788
+ task.output = chalk7__default.default.gray(error.message);
1099
789
  throw new Error("Authentication failed");
1100
790
  }
1101
791
  }),
@@ -1104,7 +794,7 @@ var auth_default = (ctx) => {
1104
794
  };
1105
795
 
1106
796
  // package.json
1107
- var version = "3.0.11";
797
+ var version = "4.0.0";
1108
798
  var package_default = {
1109
799
  name: "@lambdatest/smartui-cli",
1110
800
  version,
@@ -1144,6 +834,7 @@ var package_default = {
1144
834
  fastify: "^4.24.3",
1145
835
  "form-data": "^4.0.0",
1146
836
  listr2: "^7.0.1",
837
+ sharp: "^0.33.4",
1147
838
  tsup: "^7.2.0",
1148
839
  which: "^4.0.0",
1149
840
  winston: "^3.10.0"
@@ -1159,11 +850,11 @@ var httpClient = class {
1159
850
  headers: { "projectToken": PROJECT_TOKEN }
1160
851
  });
1161
852
  }
1162
- request(config, log) {
853
+ request(config, log2) {
1163
854
  return __async(this, null, function* () {
1164
- log.debug(`http request: ${config.method} ${config.url}`);
855
+ log2.debug(`http request: ${config.method} ${config.url}`);
1165
856
  return this.axiosInstance.request(config).then((resp) => {
1166
- log.debug(`http response: ${JSON.stringify({
857
+ log2.debug(`http response: ${JSON.stringify({
1167
858
  status: resp.status,
1168
859
  headers: resp.headers,
1169
860
  body: resp.data
@@ -1172,7 +863,7 @@ var httpClient = class {
1172
863
  }).catch((error) => {
1173
864
  var _a;
1174
865
  if (error.response) {
1175
- log.debug(`http response: ${JSON.stringify({
866
+ log2.debug(`http response: ${JSON.stringify({
1176
867
  status: error.response.status,
1177
868
  headers: error.response.headers,
1178
869
  body: error.response.data
@@ -1180,21 +871,21 @@ var httpClient = class {
1180
871
  throw new Error(((_a = error.response.data.error) == null ? void 0 : _a.message) || error.response.data.message);
1181
872
  }
1182
873
  if (error.request) {
1183
- log.debug(`http request failed: ${error.toJSON()}`);
874
+ log2.debug(`http request failed: ${error.toJSON()}`);
1184
875
  throw new Error(error.toJSON().message);
1185
876
  }
1186
- log.debug(`http request failed: ${error.message}`);
877
+ log2.debug(`http request failed: ${error.message}`);
1187
878
  throw new Error(error.message);
1188
879
  });
1189
880
  });
1190
881
  }
1191
- auth(log) {
882
+ auth(log2) {
1192
883
  return this.request({
1193
884
  url: "/token/verify",
1194
885
  method: "GET"
1195
- }, log);
886
+ }, log2);
1196
887
  }
1197
- createBuild(git, config, log) {
888
+ createBuild(git, config, log2) {
1198
889
  return this.request({
1199
890
  url: "/build",
1200
891
  method: "POST",
@@ -1202,9 +893,9 @@ var httpClient = class {
1202
893
  git,
1203
894
  config
1204
895
  }
1205
- }, log);
896
+ }, log2);
1206
897
  }
1207
- finalizeBuild(buildId, totalSnapshots, log) {
898
+ finalizeBuild(buildId, totalSnapshots, log2) {
1208
899
  let params = { buildId };
1209
900
  if (totalSnapshots > -1)
1210
901
  params.totalSnapshots = totalSnapshots;
@@ -1212,23 +903,23 @@ var httpClient = class {
1212
903
  url: "/build",
1213
904
  method: "DELETE",
1214
905
  params
1215
- }, log);
906
+ }, log2);
1216
907
  }
1217
- uploadSnapshot(buildId, snapshot, testType, log) {
908
+ uploadSnapshot(ctx, snapshot) {
1218
909
  return this.request({
1219
- url: `/builds/${buildId}/snapshot`,
910
+ url: `/builds/${ctx.build.id}/snapshot`,
1220
911
  method: "POST",
1221
912
  headers: { "Content-Type": "application/json" },
1222
913
  data: {
1223
914
  snapshot,
1224
915
  test: {
1225
- type: testType,
916
+ type: ctx.testType,
1226
917
  source: "cli"
1227
918
  }
1228
919
  }
1229
- }, log);
920
+ }, ctx.log);
1230
921
  }
1231
- uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log) {
922
+ uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log2) {
1232
923
  browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
1233
924
  const file = fs5__default.default.readFileSync(ssPath);
1234
925
  const form = new FormData__default.default();
@@ -1245,7 +936,7 @@ var httpClient = class {
1245
936
  headers: form.getHeaders(),
1246
937
  data: form
1247
938
  }).then(() => {
1248
- log.debug(`${ssName} for ${browserName} ${viewport} uploaded successfully`);
939
+ log2.debug(`${ssName} for ${browserName} ${viewport} uploaded successfully`);
1249
940
  }).catch((error) => {
1250
941
  if (error.response) {
1251
942
  throw new Error(error.response.data.error.message);
@@ -1256,7 +947,7 @@ var httpClient = class {
1256
947
  throw new Error(error.message);
1257
948
  });
1258
949
  }
1259
- checkUpdate(log) {
950
+ checkUpdate(log2) {
1260
951
  return this.request({
1261
952
  url: `/packageinfo`,
1262
953
  method: "GET",
@@ -1265,9 +956,9 @@ var httpClient = class {
1265
956
  packageName: package_default.name,
1266
957
  packageVersion: package_default.version
1267
958
  }
1268
- }, log);
959
+ }, log2);
1269
960
  }
1270
- getFigmaFilesAndImages(figmaFileToken, figmaToken, queryParams, authToken, depth, markBaseline, buildName, log) {
961
+ getFigmaFilesAndImages(figmaFileToken, figmaToken, queryParams, authToken, depth, markBaseline, buildName, log2) {
1271
962
  const requestBody = {
1272
963
  figma_file_token: figmaFileToken,
1273
964
  figma_token: figmaToken,
@@ -1284,7 +975,34 @@ var httpClient = class {
1284
975
  "Content-Type": "application/json"
1285
976
  },
1286
977
  data: JSON.stringify(requestBody)
1287
- }, log);
978
+ }, log2);
979
+ }
980
+ getS3PreSignedURL(ctx) {
981
+ return this.request({
982
+ url: `/loguploadurl`,
983
+ method: "POST",
984
+ headers: { "Content-Type": "application/json" },
985
+ data: {
986
+ buildId: ctx.build.id
987
+ }
988
+ }, ctx.log);
989
+ }
990
+ uploadLogs(ctx, uploadURL) {
991
+ const fileStream = fs5__default.default.createReadStream(constants_default.LOG_FILE_PATH);
992
+ const { size } = fs5__default.default.statSync(constants_default.LOG_FILE_PATH);
993
+ return this.request({
994
+ url: uploadURL,
995
+ method: "PUT",
996
+ headers: {
997
+ "Content-Type": "text/plain",
998
+ "Content-Length": size
999
+ },
1000
+ data: fileStream,
1001
+ maxBodyLength: Infinity,
1002
+ // prevent axios from limiting the body size
1003
+ maxContentLength: Infinity
1004
+ // prevent axios from limiting the content size
1005
+ }, ctx.log);
1288
1006
  }
1289
1007
  };
1290
1008
  var ctx_default = (options) => {
@@ -1294,6 +1012,10 @@ var ctx_default = (options) => {
1294
1012
  let mobileConfig;
1295
1013
  let config = constants_default.DEFAULT_CONFIG;
1296
1014
  let port;
1015
+ let resolutionOff;
1016
+ let extensionFiles;
1017
+ let ignoreStripExtension;
1018
+ let ignoreFilePattern;
1297
1019
  try {
1298
1020
  if (options.config) {
1299
1021
  config = JSON.parse(fs5__default.default.readFileSync(options.config, "utf-8"));
@@ -1309,6 +1031,10 @@ var ctx_default = (options) => {
1309
1031
  if (isNaN(port) || port < 1 || port > 65535) {
1310
1032
  throw new Error("Invalid port number. Port number must be an integer between 1 and 65535.");
1311
1033
  }
1034
+ resolutionOff = options.ignoreResolutions || false;
1035
+ extensionFiles = options.files || ["png", "jpeg", "jpg"];
1036
+ ignoreStripExtension = options.removeExtensions || false;
1037
+ ignoreFilePattern = options.ignoreDir || [];
1312
1038
  } catch (error) {
1313
1039
  console.log(`[smartui] Error: ${error.message}`);
1314
1040
  process.exit();
@@ -1337,6 +1063,7 @@ var ctx_default = (options) => {
1337
1063
  enableJavaScript: config.enableJavaScript || false,
1338
1064
  allowedHostnames: config.allowedHostnames || []
1339
1065
  },
1066
+ uploadFilePath: "",
1340
1067
  webStaticConfig: [],
1341
1068
  git: {
1342
1069
  branch: "",
@@ -1356,16 +1083,20 @@ var ctx_default = (options) => {
1356
1083
  parallel: options.parallel ? true : false,
1357
1084
  markBaseline: options.markBaseline ? true : false,
1358
1085
  buildName: options.buildName || "",
1359
- port
1086
+ port,
1087
+ ignoreResolutions: resolutionOff,
1088
+ fileExtension: extensionFiles,
1089
+ stripExtension: ignoreStripExtension,
1090
+ ignorePattern: ignoreFilePattern
1360
1091
  },
1361
1092
  cliVersion: version,
1362
1093
  totalSnapshots: -1
1363
1094
  };
1364
1095
  };
1365
- function executeCommand(command4) {
1096
+ function executeCommand(command5) {
1366
1097
  let dst = process.cwd();
1367
1098
  try {
1368
- return child_process.execSync(command4, {
1099
+ return child_process.execSync(command5, {
1369
1100
  cwd: dst,
1370
1101
  stdio: ["ignore"],
1371
1102
  encoding: "utf-8"
@@ -1396,8 +1127,8 @@ var git_default = (ctx) => {
1396
1127
  } else {
1397
1128
  const splitCharacter = "<##>";
1398
1129
  const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
1399
- const command4 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
1400
- let res = executeCommand(command4).split(splitCharacter);
1130
+ const command5 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
1131
+ let res = executeCommand(command5).split(splitCharacter);
1401
1132
  var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
1402
1133
  var branch = ctx.env.CURRENT_BRANCH || branchAndTags[0];
1403
1134
  branchAndTags.slice(1);
@@ -1423,11 +1154,11 @@ var getGitInfo_default = (ctx) => {
1423
1154
  }
1424
1155
  try {
1425
1156
  ctx2.git = git_default(ctx2);
1426
- task.output = chalk8__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
1157
+ task.output = chalk7__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
1427
1158
  task.title = "Fetched git information";
1428
1159
  } catch (error) {
1429
1160
  ctx2.log.debug(error);
1430
- task.output = chalk8__default.default.gray(`${error.message}`);
1161
+ task.output = chalk7__default.default.gray(`${error.message}`);
1431
1162
  throw new Error("Error fetching git repo details");
1432
1163
  }
1433
1164
  }),
@@ -1447,11 +1178,11 @@ var createBuild_default = (ctx) => {
1447
1178
  url: resp.data.buildURL,
1448
1179
  baseline: resp.data.baseline
1449
1180
  };
1450
- task.output = chalk8__default.default.gray(`build id: ${resp.data.buildId}`);
1181
+ task.output = chalk7__default.default.gray(`build id: ${resp.data.buildId}`);
1451
1182
  task.title = "SmartUI build created";
1452
1183
  } catch (error) {
1453
1184
  ctx2.log.debug(error);
1454
- task.output = chalk8__default.default.gray(error.message);
1185
+ task.output = chalk7__default.default.gray(error.message);
1455
1186
  throw new Error("SmartUI build creation failed");
1456
1187
  }
1457
1188
  }),
@@ -1470,13 +1201,13 @@ var exec_default = (ctx) => {
1470
1201
  let totalOutput = "";
1471
1202
  const output = listr2.createWritable((chunk) => {
1472
1203
  totalOutput += chunk;
1473
- task.output = chalk8__default.default.gray(totalOutput);
1204
+ task.output = chalk7__default.default.gray(totalOutput);
1474
1205
  });
1475
1206
  (_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
1476
1207
  (_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
1477
1208
  childProcess.on("error", (error) => {
1478
1209
  var _a3;
1479
- task.output = chalk8__default.default.gray(`error: ${error.message}`);
1210
+ task.output = chalk7__default.default.gray(`error: ${error.message}`);
1480
1211
  throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
1481
1212
  });
1482
1213
  childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
@@ -1494,37 +1225,469 @@ var exec_default = (ctx) => {
1494
1225
  exitOnError: false
1495
1226
  };
1496
1227
  };
1228
+ var processSnapshot_default = (ctx) => {
1229
+ return {
1230
+ title: `Processing snapshots`,
1231
+ task: (ctx2, task) => __async(void 0, null, function* () {
1232
+ var _a;
1233
+ try {
1234
+ yield new Promise((resolve) => {
1235
+ let output2 = "";
1236
+ const intervalId = setInterval(() => {
1237
+ var _a2, _b, _c;
1238
+ if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b = ctx2.snapshotQueue) == null ? void 0 : _b.isProcessing())) {
1239
+ clearInterval(intervalId);
1240
+ resolve();
1241
+ } else {
1242
+ task.title = `Processing snapshot ${(_c = ctx2.snapshotQueue) == null ? void 0 : _c.getProcessingSnapshot()}`;
1243
+ }
1244
+ }, 500);
1245
+ });
1246
+ let output = "";
1247
+ for (let snapshot of (_a = ctx2.snapshotQueue) == null ? void 0 : _a.getProcessedSnapshots()) {
1248
+ if (snapshot.error)
1249
+ output += `${chalk7__default.default.red("\u2717")} ${chalk7__default.default.gray(`${snapshot.name}
1250
+ [error] ${snapshot.error}`)}
1251
+ `;
1252
+ else
1253
+ output += `${chalk7__default.default.green("\u2713")} ${chalk7__default.default.gray(snapshot.name)}
1254
+ ${snapshot.warnings.length ? chalk7__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
1255
+ `) : ""}`;
1256
+ }
1257
+ task.output = output;
1258
+ task.title = "Processed snapshots";
1259
+ } catch (error) {
1260
+ ctx2.log.debug(error);
1261
+ task.output = chalk7__default.default.gray(error.message);
1262
+ throw new Error("Processing of snapshots failed");
1263
+ }
1264
+ }),
1265
+ rendererOptions: { persistentOutput: true }
1266
+ };
1267
+ };
1497
1268
  var finalizeBuild_default = (ctx) => {
1498
1269
  return {
1499
1270
  title: `Finalizing build`,
1500
1271
  task: (ctx2, task) => __async(void 0, null, function* () {
1272
+ var _a, _b;
1501
1273
  updateLogContext({ task: "finalizeBuild" });
1502
1274
  try {
1503
- yield new Promise((resolve) => setTimeout(resolve, 2e3));
1504
1275
  yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
1505
- task.output = chalk8__default.default.gray(`build url: ${ctx2.build.url}`);
1276
+ task.output = chalk7__default.default.gray(`build url: ${ctx2.build.url}`);
1506
1277
  task.title = "Finalized build";
1507
1278
  } catch (error) {
1508
1279
  ctx2.log.debug(error);
1509
- task.output = chalk8__default.default.gray(error.message);
1280
+ task.output = chalk7__default.default.gray(error.message);
1510
1281
  throw new Error("Finalize build failed");
1511
1282
  }
1283
+ try {
1284
+ yield (_a = ctx2.browser) == null ? void 0 : _a.close();
1285
+ ctx2.log.debug(`Closed browser`);
1286
+ yield (_b = ctx2.server) == null ? void 0 : _b.close();
1287
+ ctx2.log.debug(`Closed server`);
1288
+ let resp = yield ctx2.client.getS3PreSignedURL(ctx2);
1289
+ yield ctx2.client.uploadLogs(ctx2, resp.data.url);
1290
+ fs5.unlinkSync(constants_default.LOG_FILE_PATH);
1291
+ } catch (error) {
1292
+ ctx2.log.debug(error);
1293
+ }
1512
1294
  }),
1513
1295
  rendererOptions: { persistentOutput: true }
1514
1296
  };
1515
1297
  };
1298
+ function delDir(dir) {
1299
+ if (fs5__default.default.existsSync(dir)) {
1300
+ fs5__default.default.rmSync(dir, { recursive: true });
1301
+ }
1302
+ }
1303
+ function scrollToBottomAndBackToTop({
1304
+ frequency = 100,
1305
+ timing = 8,
1306
+ remoteWindow = window
1307
+ } = {}) {
1308
+ return new Promise((resolve) => {
1309
+ let scrolls = 1;
1310
+ let scrollLength = remoteWindow.document.body.scrollHeight / frequency;
1311
+ (function scroll() {
1312
+ let scrollBy = scrollLength * scrolls;
1313
+ remoteWindow.setTimeout(() => {
1314
+ remoteWindow.scrollTo(0, scrollBy);
1315
+ if (scrolls < frequency) {
1316
+ scrolls += 1;
1317
+ scroll();
1318
+ }
1319
+ if (scrolls === frequency) {
1320
+ remoteWindow.setTimeout(() => {
1321
+ remoteWindow.scrollTo(0, 0);
1322
+ resolve();
1323
+ }, timing);
1324
+ }
1325
+ }, timing);
1326
+ })();
1327
+ });
1328
+ }
1329
+ function launchBrowsers(ctx) {
1330
+ return __async(this, null, function* () {
1331
+ let browsers = {};
1332
+ let launchOptions = { headless: true };
1333
+ if (ctx.config.web) {
1334
+ for (const browser of ctx.config.web.browsers) {
1335
+ switch (browser) {
1336
+ case constants_default.CHROME:
1337
+ browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
1338
+ break;
1339
+ case constants_default.SAFARI:
1340
+ browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
1341
+ break;
1342
+ case constants_default.FIREFOX:
1343
+ browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
1344
+ break;
1345
+ case constants_default.EDGE:
1346
+ browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
1347
+ break;
1348
+ }
1349
+ }
1350
+ }
1351
+ if (ctx.config.mobile) {
1352
+ for (const device of ctx.config.mobile.devices) {
1353
+ if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
1354
+ browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
1355
+ else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
1356
+ browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
1357
+ }
1358
+ }
1359
+ return browsers;
1360
+ });
1361
+ }
1362
+ function closeBrowsers(browsers) {
1363
+ return __async(this, null, function* () {
1364
+ var _a;
1365
+ for (const browserName of Object.keys(browsers))
1366
+ yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
1367
+ });
1368
+ }
1369
+ function getWebRenderViewports(ctx) {
1370
+ let webRenderViewports = [];
1371
+ if (ctx.config.web) {
1372
+ for (const viewport of ctx.config.web.viewports) {
1373
+ webRenderViewports.push({
1374
+ viewport,
1375
+ viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
1376
+ fullPage: viewport.height ? false : true,
1377
+ device: false
1378
+ });
1379
+ }
1380
+ }
1381
+ return webRenderViewports;
1382
+ }
1383
+ function getMobileRenderViewports(ctx) {
1384
+ var _a;
1385
+ let mobileRenderViewports = {};
1386
+ mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
1387
+ mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
1388
+ if (ctx.config.mobile) {
1389
+ for (const device of ctx.config.mobile.devices) {
1390
+ let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
1391
+ let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
1392
+ let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
1393
+ (_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
1394
+ viewport: { width: portrait ? width : height, height: portrait ? height : width },
1395
+ viewportString: `${device} (${ctx.config.mobile.orientation})`,
1396
+ fullPage: ctx.config.mobile.fullPage,
1397
+ device: true,
1398
+ os
1399
+ });
1400
+ }
1401
+ }
1402
+ return mobileRenderViewports;
1403
+ }
1404
+ function getRenderViewports(ctx) {
1405
+ let mobileRenderViewports = getMobileRenderViewports(ctx);
1406
+ return [
1407
+ ...getWebRenderViewports(ctx),
1408
+ ...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
1409
+ ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
1410
+ ];
1411
+ }
1412
+ var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
1413
+ var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
1414
+ var ALLOWED_STATUSES = [200, 201];
1415
+ var REQUEST_TIMEOUT = 1e4;
1416
+ var MIN_VIEWPORT_HEIGHT = 1080;
1417
+ var Queue = class {
1418
+ constructor(ctx) {
1419
+ this.snapshots = [];
1420
+ this.processedSnapshots = [];
1421
+ this.processing = false;
1422
+ this.processingSnapshot = "";
1423
+ this.ctx = ctx;
1424
+ }
1425
+ enqueue(item) {
1426
+ this.snapshots.push(item);
1427
+ if (!this.processing) {
1428
+ this.processing = true;
1429
+ this.processNext();
1430
+ }
1431
+ }
1432
+ processNext() {
1433
+ return __async(this, null, function* () {
1434
+ if (!this.isEmpty()) {
1435
+ const snapshot = this.snapshots.shift();
1436
+ try {
1437
+ this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
1438
+ let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
1439
+ yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
1440
+ this.ctx.totalSnapshots++;
1441
+ this.processedSnapshots.push({ name: snapshot.name, warnings });
1442
+ } catch (error) {
1443
+ this.ctx.log.debug(`snapshot failed; ${error}`);
1444
+ this.processedSnapshots.push({ name: snapshot.name, error: error.message });
1445
+ }
1446
+ if (this.ctx.browser) {
1447
+ for (let context of this.ctx.browser.contexts()) {
1448
+ for (let page of context.pages()) {
1449
+ yield page.close();
1450
+ this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
1451
+ }
1452
+ yield context.close();
1453
+ this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
1454
+ }
1455
+ }
1456
+ this.processNext();
1457
+ } else {
1458
+ this.processing = false;
1459
+ }
1460
+ });
1461
+ }
1462
+ isProcessing() {
1463
+ return this.processing;
1464
+ }
1465
+ getProcessingSnapshot() {
1466
+ return this.processingSnapshot;
1467
+ }
1468
+ getProcessedSnapshots() {
1469
+ return this.processedSnapshots;
1470
+ }
1471
+ isEmpty() {
1472
+ return this.snapshots.length ? false : true;
1473
+ }
1474
+ };
1475
+ function processSnapshot(snapshot, ctx) {
1476
+ return __async(this, null, function* () {
1477
+ var _a;
1478
+ updateLogContext({ task: "discovery" });
1479
+ ctx.log.debug(`Processing snapshot ${snapshot.name}`);
1480
+ let launchOptions = { headless: true };
1481
+ let contextOptions = {
1482
+ javaScriptEnabled: ctx.config.enableJavaScript,
1483
+ userAgent: constants_default.CHROME_USER_AGENT
1484
+ };
1485
+ if (!((_a = ctx.browser) == null ? void 0 : _a.isConnected())) {
1486
+ if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
1487
+ launchOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
1488
+ ctx.browser = yield test.firefox.launch(launchOptions);
1489
+ ctx.log.debug(`Firefox launched with options ${JSON.stringify(launchOptions)}`);
1490
+ }
1491
+ const context = yield ctx.browser.newContext(contextOptions);
1492
+ ctx.log.debug(`Browser context created with options ${JSON.stringify(contextOptions)}`);
1493
+ const page = yield context.newPage();
1494
+ let cache = {};
1495
+ yield page.route("**/*", (route, request) => __async(this, null, function* () {
1496
+ const requestUrl = request.url();
1497
+ const requestHostname = new URL(requestUrl).hostname;
1498
+ try {
1499
+ if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
1500
+ throw new Error("resource type mp3/mp4/wav/ogg/webm");
1501
+ }
1502
+ ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
1503
+ if (ctx.config.enableJavaScript)
1504
+ ALLOWED_RESOURCES.push("script");
1505
+ const response = yield page.request.fetch(request, { timeout: REQUEST_TIMEOUT });
1506
+ const body = yield response.body();
1507
+ if (!body) {
1508
+ ctx.log.debug(`Handling request ${requestUrl}
1509
+ - skipping no response`);
1510
+ } else if (!body.length) {
1511
+ ctx.log.debug(`Handling request ${requestUrl}
1512
+ - skipping empty response`);
1513
+ } else if (requestUrl === snapshot.url) {
1514
+ ctx.log.debug(`Handling request ${requestUrl}
1515
+ - skipping root resource`);
1516
+ } else if (!ctx.config.allowedHostnames.includes(requestHostname)) {
1517
+ ctx.log.debug(`Handling request ${requestUrl}
1518
+ - skipping remote resource`);
1519
+ } else if (cache[requestUrl]) {
1520
+ ctx.log.debug(`Handling request ${requestUrl}
1521
+ - skipping already cached resource`);
1522
+ } else if (body.length > MAX_RESOURCE_SIZE) {
1523
+ ctx.log.debug(`Handling request ${requestUrl}
1524
+ - skipping resource larger than 15MB`);
1525
+ } else if (!ALLOWED_STATUSES.includes(response.status())) {
1526
+ ctx.log.debug(`Handling request ${requestUrl}
1527
+ - skipping disallowed status [${response.status()}]`);
1528
+ } else if (!ALLOWED_RESOURCES.includes(request.resourceType())) {
1529
+ ctx.log.debug(`Handling request ${requestUrl}
1530
+ - skipping disallowed resource type [${request.resourceType()}]`);
1531
+ } else {
1532
+ ctx.log.debug(`Handling request ${requestUrl}
1533
+ - content-type ${response.headers()["content-type"]}`);
1534
+ cache[requestUrl] = {
1535
+ body: body.toString("base64"),
1536
+ type: response.headers()["content-type"]
1537
+ };
1538
+ }
1539
+ route.fulfill({
1540
+ status: response.status(),
1541
+ headers: response.headers(),
1542
+ body
1543
+ });
1544
+ } catch (error) {
1545
+ ctx.log.debug(`Handling request ${requestUrl}
1546
+ - aborted due to ${error.message}`);
1547
+ route.abort();
1548
+ }
1549
+ }));
1550
+ let options = snapshot.options;
1551
+ let optionWarnings = /* @__PURE__ */ new Set();
1552
+ let processedOptions = {};
1553
+ let selectors = [];
1554
+ let ignoreOrSelectDOM;
1555
+ let ignoreOrSelectBoxes;
1556
+ if (options && Object.keys(options).length) {
1557
+ ctx.log.debug(`Snapshot options: ${JSON.stringify(options)}`);
1558
+ const isNotAllEmpty = (obj) => {
1559
+ var _a2;
1560
+ for (let key in obj)
1561
+ if ((_a2 = obj[key]) == null ? void 0 : _a2.length)
1562
+ return true;
1563
+ return false;
1564
+ };
1565
+ if (options.element && Object.keys(options.element).length) {
1566
+ if (options.element.id)
1567
+ processedOptions.element = "#" + options.element.id;
1568
+ else if (options.element.class)
1569
+ processedOptions.element = "." + options.element.class;
1570
+ else if (options.element.cssSelector)
1571
+ processedOptions.element = options.element.cssSelector;
1572
+ else if (options.element.xpath)
1573
+ processedOptions.element = "xpath=" + options.element.xpath;
1574
+ } else if (options.ignoreDOM && Object.keys(options.ignoreDOM).length && isNotAllEmpty(options.ignoreDOM)) {
1575
+ processedOptions.ignoreBoxes = {};
1576
+ ignoreOrSelectDOM = "ignoreDOM";
1577
+ ignoreOrSelectBoxes = "ignoreBoxes";
1578
+ } else if (options.selectDOM && Object.keys(options.selectDOM).length && isNotAllEmpty(options.selectDOM)) {
1579
+ processedOptions.selectBoxes = {};
1580
+ ignoreOrSelectDOM = "selectDOM";
1581
+ ignoreOrSelectBoxes = "selectBoxes";
1582
+ }
1583
+ if (ignoreOrSelectDOM) {
1584
+ for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
1585
+ switch (key) {
1586
+ case "id":
1587
+ selectors.push(...value.map((e) => "#" + e));
1588
+ break;
1589
+ case "class":
1590
+ selectors.push(...value.map((e) => "." + e));
1591
+ break;
1592
+ case "xpath":
1593
+ selectors.push(...value.map((e) => "xpath=" + e));
1594
+ break;
1595
+ case "cssSelector":
1596
+ selectors.push(...value);
1597
+ break;
1598
+ }
1599
+ }
1600
+ }
1601
+ }
1602
+ let navigated = false;
1603
+ let renderViewports = getRenderViewports(ctx);
1604
+ for (const { viewport, viewportString, fullPage } of renderViewports) {
1605
+ yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
1606
+ ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
1607
+ if (!navigated) {
1608
+ try {
1609
+ yield page.goto(snapshot.url, { waitUntil: "domcontentloaded" });
1610
+ yield new Promise((r) => setTimeout(r, 1250));
1611
+ if (ctx.config.waitForTimeout)
1612
+ yield page.waitForTimeout(ctx.config.waitForTimeout);
1613
+ navigated = true;
1614
+ ctx.log.debug(`Navigated to ${snapshot.url}`);
1615
+ } catch (error) {
1616
+ ctx.log.debug(`Navigation to discovery page failed; ${error}`);
1617
+ throw new Error(error.message);
1618
+ }
1619
+ }
1620
+ if (ctx.config.enableJavaScript && fullPage)
1621
+ yield page.evaluate(scrollToBottomAndBackToTop);
1622
+ try {
1623
+ yield page.waitForLoadState("networkidle", { timeout: 5e3 });
1624
+ ctx.log.debug("Network idle 500ms");
1625
+ } catch (error) {
1626
+ ctx.log.debug(`Network idle failed due to ${error}`);
1627
+ }
1628
+ if (processedOptions.element) {
1629
+ let l = yield page.locator(processedOptions.element).all();
1630
+ if (l.length === 0) {
1631
+ throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${processedOptions.element}`);
1632
+ } else if (l.length > 1) {
1633
+ throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, multiple elements found for selector ${processedOptions.element}`);
1634
+ }
1635
+ } else if (selectors.length) {
1636
+ let locators = [];
1637
+ if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString]))
1638
+ processedOptions[ignoreOrSelectBoxes][viewportString] = [];
1639
+ for (const selector of selectors) {
1640
+ let l = yield page.locator(selector).all();
1641
+ if (l.length === 0) {
1642
+ optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
1643
+ continue;
1644
+ }
1645
+ locators.push(...l);
1646
+ }
1647
+ for (const locator of locators) {
1648
+ let bb = yield locator.boundingBox();
1649
+ if (bb)
1650
+ processedOptions[ignoreOrSelectBoxes][viewportString].push({
1651
+ left: bb.x,
1652
+ top: bb.y,
1653
+ right: bb.x + bb.width,
1654
+ bottom: bb.y + bb.height
1655
+ });
1656
+ }
1657
+ }
1658
+ }
1659
+ if (snapshot.dom.resources.length) {
1660
+ for (let resource of snapshot.dom.resources) {
1661
+ cache[resource.url] = {
1662
+ body: resource.content,
1663
+ type: resource.mimetype
1664
+ };
1665
+ }
1666
+ }
1667
+ return {
1668
+ processedSnapshot: {
1669
+ name: snapshot.name,
1670
+ url: snapshot.url,
1671
+ dom: Buffer.from(snapshot.dom.html).toString("base64"),
1672
+ resources: cache,
1673
+ options: processedOptions
1674
+ },
1675
+ warnings: [...optionWarnings, ...snapshot.dom.warnings]
1676
+ };
1677
+ });
1678
+ }
1516
1679
 
1517
1680
  // src/commander/exec.ts
1518
1681
  var command = new commander.Command();
1519
- 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, _, command4) {
1682
+ 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) {
1520
1683
  return __async(this, null, function* () {
1521
- var _a, _b;
1522
- let ctx = ctx_default(command4.optsWithGlobals());
1684
+ let ctx = ctx_default(command5.optsWithGlobals());
1523
1685
  if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
1524
1686
  ctx.log.error(`Error: Command not found "${execCommand[0]}"`);
1525
1687
  return;
1526
1688
  }
1527
1689
  ctx.args.execCommand = execCommand;
1690
+ ctx.snapshotQueue = new Queue(ctx);
1528
1691
  ctx.totalSnapshots = 0;
1529
1692
  let tasks = new listr2.Listr(
1530
1693
  [
@@ -1533,6 +1696,7 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
1533
1696
  getGitInfo_default(),
1534
1697
  createBuild_default(),
1535
1698
  exec_default(ctx),
1699
+ processSnapshot_default(),
1536
1700
  finalizeBuild_default()
1537
1701
  ],
1538
1702
  {
@@ -1550,9 +1714,6 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
1550
1714
  yield tasks.run(ctx);
1551
1715
  } catch (error) {
1552
1716
  ctx.log.info("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
1553
- } finally {
1554
- yield (_a = ctx.server) == null ? void 0 : _a.close();
1555
- yield (_b = ctx.browser) == null ? void 0 : _b.close();
1556
1717
  }
1557
1718
  });
1558
1719
  });
@@ -1722,13 +1883,13 @@ function captureScreenshots(ctx) {
1722
1883
  else
1723
1884
  yield captureScreenshotsSync(ctx, staticConfig, browsers);
1724
1885
  delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
1725
- output += `${chalk8__default.default.gray(staticConfig.name)} ${chalk8__default.default.green("\u2713")}
1886
+ output += `${chalk7__default.default.gray(staticConfig.name)} ${chalk7__default.default.green("\u2713")}
1726
1887
  `;
1727
1888
  ctx.task.output = output;
1728
1889
  capturedScreenshots++;
1729
1890
  } catch (error) {
1730
1891
  ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
1731
- output += `${chalk8__default.default.gray(staticConfig.name)} ${chalk8__default.default.red("\u2717")}
1892
+ output += `${chalk7__default.default.gray(staticConfig.name)} ${chalk7__default.default.red("\u2717")}
1732
1893
  `;
1733
1894
  ctx.task.output = output;
1734
1895
  }
@@ -1738,6 +1899,108 @@ function captureScreenshots(ctx) {
1738
1899
  return { capturedScreenshots, output };
1739
1900
  });
1740
1901
  }
1902
+ function getImageDimensions(filePath) {
1903
+ const buffer = fs5__default.default.readFileSync(filePath);
1904
+ let width, height;
1905
+ if (buffer.toString("hex", 0, 2) === "ffd8") {
1906
+ let offset = 2;
1907
+ while (offset < buffer.length) {
1908
+ const marker = buffer.toString("hex", offset, offset + 2);
1909
+ offset += 2;
1910
+ const length = buffer.readUInt16BE(offset);
1911
+ if (marker === "ffc0" || marker === "ffc2") {
1912
+ height = buffer.readUInt16BE(offset + 3);
1913
+ width = buffer.readUInt16BE(offset + 5);
1914
+ return { width, height };
1915
+ }
1916
+ offset += length;
1917
+ }
1918
+ } else if (buffer.toString("hex", 1, 4) === "504e47") {
1919
+ width = buffer.readUInt32BE(16);
1920
+ height = buffer.readUInt32BE(20);
1921
+ return { width, height };
1922
+ }
1923
+ return null;
1924
+ }
1925
+ function isAllowedImage(filePath) {
1926
+ return __async(this, null, function* () {
1927
+ try {
1928
+ const fileBuffer = fs5__default.default.readFileSync(filePath);
1929
+ const isMagicValid = constants_default.MAGIC_NUMBERS.some((magic) => fileBuffer.slice(0, magic.magic.length).equals(magic.magic));
1930
+ const metadata = yield sharp__default.default(filePath).metadata();
1931
+ if (metadata.format === constants_default.FILE_EXTENSION_GIFS) {
1932
+ return false;
1933
+ }
1934
+ if (metadata.width > 0 && metadata.height > 0) {
1935
+ return true;
1936
+ }
1937
+ if (isMagicValid && metadata.format !== constants_default.FILE_EXTENSION_GIFS) {
1938
+ return true;
1939
+ }
1940
+ return false;
1941
+ } catch (error) {
1942
+ return false;
1943
+ }
1944
+ });
1945
+ }
1946
+ function uploadScreenshots(ctx) {
1947
+ return __async(this, null, function* () {
1948
+ const allowedExtensions = ctx.options.fileExtension.map((ext) => `.${ext.trim().toLowerCase()}`);
1949
+ let noOfScreenshots = 0;
1950
+ function processDirectory(directory, relativePath = "") {
1951
+ return __async(this, null, function* () {
1952
+ const files = fs5__default.default.readdirSync(directory);
1953
+ for (let file of files) {
1954
+ const filePath = path2__default.default.join(directory, file);
1955
+ const stat = fs5__default.default.statSync(filePath);
1956
+ const relativeFilePath = path2__default.default.join(relativePath, file);
1957
+ if (stat.isDirectory() && ctx.options.ignorePattern.includes(relativeFilePath)) {
1958
+ ctx.log.info(`Ignoring Directory ${relativeFilePath}`);
1959
+ continue;
1960
+ }
1961
+ if (stat.isDirectory()) {
1962
+ yield processDirectory(filePath, relativeFilePath);
1963
+ } else {
1964
+ let fileExtension = path2__default.default.extname(file).toLowerCase();
1965
+ if (allowedExtensions.includes(fileExtension)) {
1966
+ const isValid = yield isAllowedImage(filePath);
1967
+ if (!isValid) {
1968
+ ctx.log.info(`File ${filePath} is not a valid ${fileExtension} image or is corrupted. Skipping.`);
1969
+ continue;
1970
+ }
1971
+ let ssId = relativeFilePath;
1972
+ if (ctx.options.stripExtension) {
1973
+ ssId = path2__default.default.join(relativePath, path2__default.default.basename(file, fileExtension));
1974
+ }
1975
+ let viewport = "default";
1976
+ if (!ctx.options.ignoreResolutions) {
1977
+ const dimensions = getImageDimensions(filePath);
1978
+ if (!dimensions) {
1979
+ ctx.log.info(`Unable to determine dimensions for image: ${filePath}`);
1980
+ } else {
1981
+ const width = dimensions.width;
1982
+ const height = dimensions.height;
1983
+ viewport = `${width}x${height}`;
1984
+ }
1985
+ }
1986
+ yield ctx.client.uploadScreenshot(ctx.build, filePath, ssId, "default", viewport, ctx.log);
1987
+ ctx.log.info(`${filePath} : uploaded successfully`);
1988
+ noOfScreenshots++;
1989
+ } else {
1990
+ ctx.log.info(`File ${filePath} has invalid file extension: ${fileExtension}. Skipping`);
1991
+ }
1992
+ }
1993
+ }
1994
+ });
1995
+ }
1996
+ yield processDirectory(ctx.uploadFilePath);
1997
+ if (noOfScreenshots == 0) {
1998
+ ctx.log.info(`No screenshots uploaded.`);
1999
+ } else {
2000
+ ctx.log.info(`${noOfScreenshots} screenshots uploaded successfully.`);
2001
+ }
2002
+ });
2003
+ }
1741
2004
  var captureScreenshots_default = (ctx) => {
1742
2005
  return {
1743
2006
  title: "Capturing screenshots",
@@ -1752,7 +2015,7 @@ var captureScreenshots_default = (ctx) => {
1752
2015
  task.title = "Screenshots captured successfully";
1753
2016
  } catch (error) {
1754
2017
  ctx2.log.debug(error);
1755
- task.output = chalk8__default.default.gray(`${error.message}`);
2018
+ task.output = chalk7__default.default.gray(`${error.message}`);
1756
2019
  throw new Error("Capturing screenshots failed");
1757
2020
  }
1758
2021
  }),
@@ -1763,9 +2026,9 @@ var captureScreenshots_default = (ctx) => {
1763
2026
 
1764
2027
  // src/commander/capture.ts
1765
2028
  var command2 = new commander.Command();
1766
- 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, _, command4) {
2029
+ 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) {
1767
2030
  return __async(this, null, function* () {
1768
- let ctx = ctx_default(command4.optsWithGlobals());
2031
+ let ctx = ctx_default(command5.optsWithGlobals());
1769
2032
  if (!fs5__default.default.existsSync(file)) {
1770
2033
  console.log(`Error: Web Static Config file ${file} not found.`);
1771
2034
  return;
@@ -1805,6 +2068,71 @@ command2.name("capture").description("Capture screenshots of static sites").argu
1805
2068
  });
1806
2069
  });
1807
2070
  var capture_default = command2;
2071
+ var uploadScreenshots_default = (ctx) => {
2072
+ return {
2073
+ title: "Uploading screenshots",
2074
+ task: (ctx2, task) => __async(void 0, null, function* () {
2075
+ try {
2076
+ ctx2.task = task;
2077
+ updateLogContext({ task: "upload" });
2078
+ yield uploadScreenshots(ctx2);
2079
+ task.title = "Screenshots uploaded successfully";
2080
+ } catch (error) {
2081
+ ctx2.log.debug(error);
2082
+ task.output = chalk7__default.default.gray(`${error.message}`);
2083
+ throw new Error("Uploading screenshots failed");
2084
+ }
2085
+ }),
2086
+ rendererOptions: { persistentOutput: true },
2087
+ exitOnError: false
2088
+ };
2089
+ };
2090
+
2091
+ // src/commander/upload.ts
2092
+ var command3 = new commander.Command();
2093
+ command3.name("upload").description("Upload screenshots from given directory").argument("<directory>", "Path of the directory").option("-R, --ignoreResolutions", "Ignore resolution").option("-F, --files <extensions>", "Comma-separated list of allowed file extensions", (val) => {
2094
+ return val.split(",").map((ext) => ext.trim().toLowerCase());
2095
+ }).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
2096
+ return val.split(",").map((pattern) => pattern.trim());
2097
+ }).action(function(directory, _, command5) {
2098
+ return __async(this, null, function* () {
2099
+ let ctx = ctx_default(command5.optsWithGlobals());
2100
+ if (!fs5__default.default.existsSync(directory)) {
2101
+ console.log(`Error: The provided directory ${directory} not found.`);
2102
+ return;
2103
+ }
2104
+ if (path2__default.default.extname(directory).toLowerCase() === constants_default.FILE_EXTENSION_ZIP) {
2105
+ ctx.log.debug(`Error: The provided directory ${directory} is a zip file. Zips are not accepted.`);
2106
+ return;
2107
+ }
2108
+ ctx.uploadFilePath = directory;
2109
+ let tasks = new listr2.Listr(
2110
+ [
2111
+ auth_default(),
2112
+ getGitInfo_default(),
2113
+ createBuild_default(),
2114
+ uploadScreenshots_default(),
2115
+ finalizeBuild_default()
2116
+ ],
2117
+ {
2118
+ rendererOptions: {
2119
+ icon: {
2120
+ [listr2.ListrDefaultRendererLogLevels.OUTPUT]: `\u2192`
2121
+ },
2122
+ color: {
2123
+ [listr2.ListrDefaultRendererLogLevels.OUTPUT]: listr2.color.gray
2124
+ }
2125
+ }
2126
+ }
2127
+ );
2128
+ try {
2129
+ yield tasks.run(ctx);
2130
+ } catch (error) {
2131
+ console.log("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
2132
+ }
2133
+ });
2134
+ });
2135
+ var upload_default = command3;
1808
2136
 
1809
2137
  // src/lib/uploadFigmaDesigns.ts
1810
2138
  var uploadFigmaDesigns_default = (ctx) => __async(void 0, null, function* () {
@@ -1846,7 +2174,7 @@ var uploadFigmaDesigns_default2 = (ctx) => {
1846
2174
  ctx2.log.debug(`Figma designs processed: ${results}`);
1847
2175
  } catch (error) {
1848
2176
  ctx2.log.debug(error);
1849
- task.output = chalk8__default.default.gray(`${error.message}`);
2177
+ task.output = chalk7__default.default.gray(`${error.message}`);
1850
2178
  throw new Error("Uploading Figma designs failed");
1851
2179
  }
1852
2180
  }),
@@ -1856,11 +2184,11 @@ var uploadFigmaDesigns_default2 = (ctx) => {
1856
2184
  };
1857
2185
 
1858
2186
  // src/commander/uploadFigma.ts
1859
- var command3 = new commander.Command();
1860
- command3.name("upload-figma").description("Capture screenshots of static sites").argument("<file>", "figma design config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").action(function(file, _, command4) {
2187
+ var command4 = new commander.Command();
2188
+ command4.name("upload-figma").description("Capture screenshots of static sites").argument("<file>", "figma design config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").action(function(file, _, command5) {
1861
2189
  return __async(this, null, function* () {
1862
2190
  var _a, _b;
1863
- let ctx = ctx_default(command4.optsWithGlobals());
2191
+ let ctx = ctx_default(command5.optsWithGlobals());
1864
2192
  if (!fs5__default.default.existsSync(file)) {
1865
2193
  console.log(`Error: Figma Config file ${file} not found.`);
1866
2194
  return;
@@ -1898,31 +2226,34 @@ command3.name("upload-figma").description("Capture screenshots of static sites")
1898
2226
  }
1899
2227
  });
1900
2228
  });
1901
- var uploadFigma_default = command3;
2229
+ var uploadFigma_default = command4;
1902
2230
 
1903
2231
  // src/commander/commander.ts
1904
2232
  var program = new commander.Command();
1905
- program.name("smartui").description("CLI to help you run your SmartUI tests on LambdaTest platform").version(`v${version}`).option("-c --config <filepath>", "Config file path").addCommand(exec_default2).addCommand(capture_default).addCommand(configWeb).addCommand(configStatic).addCommand(configFigma).addCommand(uploadFigma_default);
2233
+ program.name("smartui").description("CLI to help you run your SmartUI tests on LambdaTest platform").version(`v${version}`).option("-c --config <filepath>", "Config file path").addCommand(exec_default2).addCommand(capture_default).addCommand(configWeb).addCommand(configStatic).addCommand(upload_default).addCommand(configFigma).addCommand(uploadFigma_default);
1906
2234
  var commander_default = program;
1907
2235
  (function() {
1908
2236
  return __async(this, null, function* () {
1909
2237
  let client = new httpClient(env_default());
1910
- let log = logger_default;
2238
+ let log2 = logger_default;
1911
2239
  try {
1912
- log.info(`
2240
+ let { data: { latestVersion, deprecated, additionalDescription, additionalDescriptionLatestVersion } } = yield client.checkUpdate(log2);
2241
+ log2.info(`
1913
2242
  LambdaTest SmartUI CLI v${package_default.version}`);
1914
- let { data: { latestVersion, deprecated } } = yield client.checkUpdate(log);
1915
- if (deprecated)
1916
- log.warn(`This version is deprecated. A new version ${latestVersion} is available!
2243
+ log2.info(chalk7__default.default.yellow(`${additionalDescription}`));
2244
+ if (deprecated) {
2245
+ log2.warn(`This version is deprecated. A new version ${latestVersion} is available!`);
2246
+ log2.warn(`${additionalDescriptionLatestVersion}
1917
2247
  `);
1918
- else if (package_default.version !== latestVersion)
1919
- log.info(chalk8__default.default.gray(`A new version ${latestVersion} is available!
2248
+ } else if (package_default.version !== latestVersion) {
2249
+ log2.info(chalk7__default.default.green(`A new version ${latestVersion} is available!`));
2250
+ log2.info(chalk7__default.default.red(`${additionalDescriptionLatestVersion}
1920
2251
  `));
1921
- else
1922
- log.info(chalk8__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
2252
+ } else
2253
+ log2.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
1923
2254
  } catch (error) {
1924
- log.debug(error);
1925
- log.info(chalk8__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
2255
+ log2.debug(error);
2256
+ log2.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
1926
2257
  }
1927
2258
  commander_default.parse();
1928
2259
  });