@lambdatest/smartui-cli 4.0.5 → 4.0.7

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 +530 -86
  2. package/package.json +5 -5
package/dist/index.cjs CHANGED
@@ -33,8 +33,6 @@ var spawn__default = /*#__PURE__*/_interopDefault(spawn);
33
33
  var sharp__default = /*#__PURE__*/_interopDefault(sharp);
34
34
 
35
35
  var __defProp = Object.defineProperty;
36
- var __defProps = Object.defineProperties;
37
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
38
36
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
39
37
  var __hasOwnProp = Object.prototype.hasOwnProperty;
40
38
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -50,7 +48,6 @@ var __spreadValues = (a, b) => {
50
48
  }
51
49
  return a;
52
50
  };
53
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
54
51
  var __async = (__this, __arguments, generator) => {
55
52
  return new Promise((resolve, reject) => {
56
53
  var fulfilled = (value) => {
@@ -99,7 +96,8 @@ var constants_default = {
99
96
  },
100
97
  waitForTimeout: 1e3,
101
98
  enableJavaScript: false,
102
- allowedHostnames: []
99
+ allowedHostnames: [],
100
+ smartIgnore: false
103
101
  },
104
102
  DEFAULT_WEB_STATIC_CONFIG: [
105
103
  {
@@ -119,6 +117,64 @@ var constants_default = {
119
117
  EDGE: "edge",
120
118
  EDGE_CHANNEL: "msedge",
121
119
  WEBKIT: "webkit",
120
+ // discovery browser launch arguments
121
+ LAUNCH_ARGS: [
122
+ // disable the translate popup and optimization downloads
123
+ "--disable-features=Translate,OptimizationGuideModelDownloading",
124
+ // disable several subsystems which run network requests in the background
125
+ "--disable-background-networking",
126
+ // disable task throttling of timer tasks from background pages
127
+ "--disable-background-timer-throttling",
128
+ // disable backgrounding renderer processes
129
+ "--disable-renderer-backgrounding",
130
+ // disable backgrounding renderers for occluded windows (reduce nondeterminism)
131
+ "--disable-backgrounding-occluded-windows",
132
+ // disable crash reporting
133
+ "--disable-breakpad",
134
+ // disable client side phishing detection
135
+ "--disable-client-side-phishing-detection",
136
+ // disable default component extensions with background pages for performance
137
+ "--disable-component-extensions-with-background-pages",
138
+ // disable installation of default apps on first run
139
+ "--disable-default-apps",
140
+ // work-around for environments where a small /dev/shm partition causes crashes
141
+ "--disable-dev-shm-usage",
142
+ // disable extensions
143
+ "--disable-extensions",
144
+ // disable hang monitor dialogs in renderer processes
145
+ "--disable-hang-monitor",
146
+ // disable inter-process communication flooding protection for javascript
147
+ "--disable-ipc-flooding-protection",
148
+ // disable web notifications and the push API
149
+ "--disable-notifications",
150
+ // disable the prompt when a POST request causes page navigation
151
+ "--disable-prompt-on-repost",
152
+ // disable syncing browser data with google accounts
153
+ "--disable-sync",
154
+ // disable site-isolation to make network requests easier to intercept
155
+ "--disable-site-isolation-trials",
156
+ // disable the first run tasks, whether or not it's actually the first run
157
+ "--no-first-run",
158
+ // disable the sandbox for all process types that are normally sandboxed
159
+ "--no-sandbox",
160
+ // specify a consistent encryption backend across platforms
161
+ "--password-store=basic",
162
+ // use a mock keychain on Mac to prevent blocking permissions dialogs
163
+ "--use-mock-keychain",
164
+ // enable remote debugging on the first available port
165
+ "--remote-debugging-port=0",
166
+ // sets navigator.webdriver to false
167
+ "--disable-blink-features=AutomationControlled",
168
+ // disable UA-CH feature
169
+ `--disable-features=UserAgentClientHint`
170
+ ],
171
+ // discovery request headers
172
+ REQUEST_HEADERS: {
173
+ // `HeadlessChrome` is added to sec-ch-ua, `--disable-features=UserAgentClientHint` doesn't seem to work
174
+ "sec-ch-ua": '"Chromium";v="129", "Not=A?Brand";v="8"',
175
+ "sec-ch-ua-mobile": '"?0"',
176
+ "sec-ch-ua-platform": '"Windows"'
177
+ },
122
178
  // user agents
123
179
  CHROME_USER_AGENT: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.107 Safari/537.3",
124
180
  FIREFOX_USER_AGENT: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:112.0) Gecko/20100101 Firefox/112.0",
@@ -444,6 +500,10 @@ var ConfigSchema = {
444
500
  type: "boolean",
445
501
  errorMessage: "Invalid config; cliEnableJavaScript must be true/false"
446
502
  },
503
+ smartIgnore: {
504
+ type: "boolean",
505
+ errorMessage: "Invalid config; smartIgnore must be true/false"
506
+ },
447
507
  scrollTime: {
448
508
  type: "number",
449
509
  minimum: 1,
@@ -476,6 +536,10 @@ var ConfigSchema = {
476
536
  errorMessage: "Invalid config; password is mandatory"
477
537
  }
478
538
  }
539
+ },
540
+ delayedUpload: {
541
+ type: "boolean",
542
+ errorMessage: "Invalid config; delayedUpload must be true/false"
479
543
  }
480
544
  },
481
545
  anyOf: [
@@ -612,6 +676,64 @@ var SnapshotSchema = {
612
676
  errorMessage: "Invalid snapshot options; selectDOM xpath array must have unique and non-empty items"
613
677
  }
614
678
  }
679
+ },
680
+ web: {
681
+ type: "object",
682
+ properties: {
683
+ browsers: {
684
+ type: "array",
685
+ items: {
686
+ type: "string",
687
+ enum: [constants_default.CHROME, constants_default.FIREFOX, constants_default.SAFARI, constants_default.EDGE],
688
+ minLength: 1
689
+ },
690
+ uniqueItems: true,
691
+ errorMessage: `Invalid snapshot options; allowed browsers - ${constants_default.CHROME}, ${constants_default.FIREFOX}, ${constants_default.SAFARI}, ${constants_default.EDGE}`
692
+ },
693
+ viewports: {
694
+ type: "array",
695
+ items: {
696
+ type: "array",
697
+ items: {
698
+ type: "number",
699
+ minimum: 1
700
+ },
701
+ minItems: 1,
702
+ maxItems: 2,
703
+ errorMessage: "Invalid snapshot options; each viewport array must contain either a single width or a width and height tuple with positive values."
704
+ },
705
+ uniqueItems: true,
706
+ errorMessage: "Invalid snapshot options; viewports must be an array of unique arrays."
707
+ }
708
+ },
709
+ required: ["viewports"],
710
+ errorMessage: "Invalid snapshot options; web must include viewports property."
711
+ },
712
+ mobile: {
713
+ type: "object",
714
+ properties: {
715
+ devices: {
716
+ type: "array",
717
+ items: {
718
+ type: "string",
719
+ enum: Object.keys(constants_default.SUPPORTED_MOBILE_DEVICES),
720
+ minLength: 1
721
+ },
722
+ uniqueItems: true,
723
+ errorMessage: "Invalid snapshot options; devices must be an array of unique supported mobile devices."
724
+ },
725
+ fullPage: {
726
+ type: "boolean",
727
+ errorMessage: "Invalid snapshot options; fullPage must be a boolean."
728
+ },
729
+ orientation: {
730
+ type: "string",
731
+ enum: [constants_default.MOBILE_ORIENTATION_PORTRAIT, constants_default.MOBILE_ORIENTATION_LANDSCAPE],
732
+ errorMessage: "Invalid snapshot options; orientation must be either 'portrait' or 'landscape'."
733
+ }
734
+ },
735
+ required: ["devices"],
736
+ errorMessage: "Invalid snapshot options; mobile must include devices property."
615
737
  }
616
738
  },
617
739
  additionalProperties: false
@@ -722,21 +844,21 @@ var env_default = () => {
722
844
  const {
723
845
  PROJECT_TOKEN = "",
724
846
  SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
725
- LT_SDK_DEBUG,
726
847
  SMARTUI_GIT_INFO_FILEPATH,
848
+ SMARTUI_DO_NOT_USE_CAPTURED_COOKIES,
727
849
  HTTP_PROXY,
728
850
  HTTPS_PROXY,
729
851
  GITHUB_ACTIONS,
730
852
  FIGMA_TOKEN,
731
853
  LT_USERNAME,
732
854
  LT_ACCESS_KEY,
855
+ LT_SDK_DEBUG,
733
856
  BASELINE_BRANCH,
734
857
  CURRENT_BRANCH
735
858
  } = process.env;
736
859
  return {
737
860
  PROJECT_TOKEN,
738
861
  SMARTUI_CLIENT_API_URL,
739
- LT_SDK_DEBUG,
740
862
  SMARTUI_GIT_INFO_FILEPATH,
741
863
  HTTP_PROXY,
742
864
  HTTPS_PROXY,
@@ -745,7 +867,9 @@ var env_default = () => {
745
867
  LT_USERNAME,
746
868
  LT_ACCESS_KEY,
747
869
  BASELINE_BRANCH,
748
- CURRENT_BRANCH
870
+ CURRENT_BRANCH,
871
+ LT_SDK_DEBUG: LT_SDK_DEBUG === "true",
872
+ SMARTUI_DO_NOT_USE_CAPTURED_COOKIES: SMARTUI_DO_NOT_USE_CAPTURED_COOKIES === "true"
749
873
  };
750
874
  };
751
875
  var logContext = {};
@@ -754,7 +878,7 @@ function updateLogContext(newContext) {
754
878
  }
755
879
  var logLevel = () => {
756
880
  let env = env_default();
757
- return env.LT_SDK_DEBUG === "true" ? "debug" : "info";
881
+ return env.LT_SDK_DEBUG ? "debug" : "info";
758
882
  };
759
883
  var logger = winston.createLogger({
760
884
  format: winston.format.combine(
@@ -822,7 +946,7 @@ var auth_default = (ctx) => {
822
946
  };
823
947
 
824
948
  // package.json
825
- var version = "4.0.5";
949
+ var version = "4.0.7";
826
950
  var package_default = {
827
951
  name: "@lambdatest/smartui-cli",
828
952
  version,
@@ -846,10 +970,10 @@ var package_default = {
846
970
  author: "LambdaTest <keys@lambdatest.com>",
847
971
  license: "MIT",
848
972
  dependencies: {
849
- "@playwright/browser-chromium": "^1.45.3",
850
- "@playwright/browser-firefox": "^1.45.3",
851
- "@playwright/browser-webkit": "^1.45.3",
852
- "@playwright/test": "^1.45.3",
973
+ "@playwright/browser-chromium": "^1.47.2",
974
+ "@playwright/browser-firefox": "^1.47.2",
975
+ "@playwright/browser-webkit": "^1.47.2",
976
+ "@playwright/test": "^1.47.2",
853
977
  "@types/cross-spawn": "^6.0.4",
854
978
  "@types/node": "^20.8.9",
855
979
  "@types/which": "^3.0.2",
@@ -1034,7 +1158,7 @@ var httpClient = class {
1034
1158
  }
1035
1159
  };
1036
1160
  var ctx_default = (options) => {
1037
- var _a, _b, _c;
1161
+ var _a, _b, _c, _d, _e, _f, _g;
1038
1162
  let env = env_default();
1039
1163
  let webConfig;
1040
1164
  let mobileConfig;
@@ -1092,11 +1216,13 @@ var ctx_default = (options) => {
1092
1216
  mobile: mobileConfig,
1093
1217
  waitForPageRender: config.waitForPageRender || 0,
1094
1218
  waitForTimeout: config.waitForTimeout || 0,
1095
- enableJavaScript: config.enableJavaScript || false,
1096
- cliEnableJavaScript: config.cliEnableJavaScript || true,
1219
+ enableJavaScript: (_d = config.enableJavaScript) != null ? _d : false,
1220
+ cliEnableJavaScript: (_e = config.cliEnableJavaScript) != null ? _e : true,
1097
1221
  scrollTime: config.scrollTime || constants_default.DEFAULT_SCROLL_TIME,
1098
1222
  allowedHostnames: config.allowedHostnames || [],
1099
- basicAuthorization: basicAuthObj
1223
+ basicAuthorization: basicAuthObj,
1224
+ smartIgnore: (_f = config.smartIgnore) != null ? _f : false,
1225
+ delayedUpload: (_g = config.delayedUpload) != null ? _g : false
1100
1226
  },
1101
1227
  uploadFilePath: "",
1102
1228
  webStaticConfig: [],
@@ -1264,13 +1390,17 @@ var processSnapshot_default = (ctx) => {
1264
1390
  return {
1265
1391
  title: `Processing snapshots`,
1266
1392
  task: (ctx2, task) => __async(void 0, null, function* () {
1267
- var _a;
1393
+ var _a, _b;
1268
1394
  try {
1395
+ if (ctx2.config.delayedUpload) {
1396
+ ctx2.log.debug("started after processing because of delayedUpload");
1397
+ (_a = ctx2.snapshotQueue) == null ? void 0 : _a.startProcessingfunc();
1398
+ }
1269
1399
  yield new Promise((resolve) => {
1270
1400
  let output2 = "";
1271
1401
  const intervalId = setInterval(() => {
1272
- var _a2, _b, _c;
1273
- if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b = ctx2.snapshotQueue) == null ? void 0 : _b.isProcessing())) {
1402
+ var _a2, _b2, _c;
1403
+ if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b2 = ctx2.snapshotQueue) == null ? void 0 : _b2.isProcessing())) {
1274
1404
  clearInterval(intervalId);
1275
1405
  resolve();
1276
1406
  } else {
@@ -1279,7 +1409,7 @@ var processSnapshot_default = (ctx) => {
1279
1409
  }, 500);
1280
1410
  });
1281
1411
  let output = "";
1282
- for (let snapshot of (_a = ctx2.snapshotQueue) == null ? void 0 : _a.getProcessedSnapshots()) {
1412
+ for (let snapshot of (_b = ctx2.snapshotQueue) == null ? void 0 : _b.getProcessedSnapshots()) {
1283
1413
  if (snapshot.error)
1284
1414
  output += `${chalk7__default.default.red("\u2717")} ${chalk7__default.default.gray(`${snapshot.name}
1285
1415
  [error] ${snapshot.error}`)}
@@ -1415,6 +1545,28 @@ function getWebRenderViewports(ctx) {
1415
1545
  }
1416
1546
  return webRenderViewports;
1417
1547
  }
1548
+ function getWebRenderViewportsForOptions(options) {
1549
+ let webRenderViewports = [];
1550
+ if (options.web && Array.isArray(options.web.viewports)) {
1551
+ for (const viewport of options.web.viewports) {
1552
+ if (Array.isArray(viewport) && viewport.length > 0) {
1553
+ let viewportObj = {
1554
+ width: viewport[0]
1555
+ };
1556
+ if (viewport.length > 1) {
1557
+ viewportObj.height = viewport[1];
1558
+ }
1559
+ webRenderViewports.push({
1560
+ viewport: viewportObj,
1561
+ viewportString: `${viewport[0]}${viewport[1] ? "x" + viewport[1] : ""}`,
1562
+ fullPage: viewport.length === 1,
1563
+ device: false
1564
+ });
1565
+ }
1566
+ }
1567
+ }
1568
+ return webRenderViewports;
1569
+ }
1418
1570
  function getMobileRenderViewports(ctx) {
1419
1571
  var _a;
1420
1572
  let mobileRenderViewports = {};
@@ -1436,6 +1588,34 @@ function getMobileRenderViewports(ctx) {
1436
1588
  }
1437
1589
  return mobileRenderViewports;
1438
1590
  }
1591
+ function getMobileRenderViewportsForOptions(options) {
1592
+ var _a;
1593
+ let mobileRenderViewports = {};
1594
+ mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
1595
+ mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
1596
+ if (options.mobile) {
1597
+ for (const device of options.mobile.devices) {
1598
+ let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
1599
+ let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
1600
+ let orientation = options.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
1601
+ let portrait = orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT;
1602
+ let fullPage;
1603
+ if (options.mobile.fullPage === void 0 || options.mobile.fullPage) {
1604
+ fullPage = true;
1605
+ } else {
1606
+ fullPage = false;
1607
+ }
1608
+ (_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
1609
+ viewport: { width: portrait ? width : height, height: portrait ? height : width },
1610
+ viewportString: `${device} (${orientation})`,
1611
+ fullPage,
1612
+ device: true,
1613
+ os
1614
+ });
1615
+ }
1616
+ }
1617
+ return mobileRenderViewports;
1618
+ }
1439
1619
  function getRenderViewports(ctx) {
1440
1620
  let mobileRenderViewports = getMobileRenderViewports(ctx);
1441
1621
  let webRenderViewports = getWebRenderViewports(ctx);
@@ -1445,75 +1625,29 @@ function getRenderViewports(ctx) {
1445
1625
  ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
1446
1626
  ];
1447
1627
  }
1628
+ function getRenderViewportsForOptions(options) {
1629
+ let mobileRenderViewports = getMobileRenderViewportsForOptions(options);
1630
+ let webRenderViewports = getWebRenderViewportsForOptions(options);
1631
+ return [
1632
+ ...webRenderViewports,
1633
+ ...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
1634
+ ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
1635
+ ];
1636
+ }
1448
1637
  var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
1449
1638
  var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
1450
1639
  var ALLOWED_STATUSES = [200, 201];
1451
1640
  var REQUEST_TIMEOUT = 1e4;
1452
1641
  var MIN_VIEWPORT_HEIGHT = 1080;
1453
- var Queue = class {
1454
- constructor(ctx) {
1455
- this.snapshots = [];
1456
- this.processedSnapshots = [];
1457
- this.processing = false;
1458
- this.processingSnapshot = "";
1459
- this.ctx = ctx;
1460
- }
1461
- enqueue(item) {
1462
- this.snapshots.push(item);
1463
- if (!this.processing) {
1464
- this.processing = true;
1465
- this.processNext();
1466
- }
1467
- }
1468
- processNext() {
1469
- return __async(this, null, function* () {
1470
- if (!this.isEmpty()) {
1471
- const snapshot = this.snapshots.shift();
1472
- try {
1473
- this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
1474
- let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
1475
- yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
1476
- this.ctx.totalSnapshots++;
1477
- this.processedSnapshots.push({ name: snapshot.name, warnings });
1478
- } catch (error) {
1479
- this.ctx.log.debug(`snapshot failed; ${error}`);
1480
- this.processedSnapshots.push({ name: snapshot.name, error: error.message });
1481
- }
1482
- if (this.ctx.browser) {
1483
- for (let context of this.ctx.browser.contexts()) {
1484
- for (let page of context.pages()) {
1485
- yield page.close();
1486
- this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
1487
- }
1488
- yield context.close();
1489
- this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
1490
- }
1491
- }
1492
- this.processNext();
1493
- } else {
1494
- this.processing = false;
1495
- }
1496
- });
1497
- }
1498
- isProcessing() {
1499
- return this.processing;
1500
- }
1501
- getProcessingSnapshot() {
1502
- return this.processingSnapshot;
1503
- }
1504
- getProcessedSnapshots() {
1505
- return this.processedSnapshots;
1506
- }
1507
- isEmpty() {
1508
- return this.snapshots.length ? false : true;
1509
- }
1510
- };
1511
1642
  function processSnapshot(snapshot, ctx) {
1512
1643
  return __async(this, null, function* () {
1513
1644
  var _a;
1514
1645
  updateLogContext({ task: "discovery" });
1515
1646
  ctx.log.debug(`Processing snapshot ${snapshot.name} ${snapshot.url}`);
1516
- let launchOptions = { headless: true };
1647
+ let launchOptions = {
1648
+ headless: true,
1649
+ args: constants_default.LAUNCH_ARGS
1650
+ };
1517
1651
  let contextOptions = {
1518
1652
  javaScriptEnabled: ctx.config.cliEnableJavaScript,
1519
1653
  userAgent: constants_default.CHROME_USER_AGENT
@@ -1526,7 +1660,7 @@ function processSnapshot(snapshot, ctx) {
1526
1660
  }
1527
1661
  const context = yield ctx.browser.newContext(contextOptions);
1528
1662
  ctx.log.debug(`Browser context created with options ${JSON.stringify(contextOptions)}`);
1529
- if (snapshot.dom.cookies) {
1663
+ if (!ctx.env.SMARTUI_DO_NOT_USE_CAPTURED_COOKIES && snapshot.dom.cookies) {
1530
1664
  const domainName = new URL(snapshot.url).hostname;
1531
1665
  ctx.log.debug(`Setting cookies for domain: ${domainName}`);
1532
1666
  const cookieArray = snapshot.dom.cookies.split("; ").map((cookie) => {
@@ -1562,7 +1696,10 @@ function processSnapshot(snapshot, ctx) {
1562
1696
  yield page.route("**/*", (route, request) => __async(this, null, function* () {
1563
1697
  const requestUrl = request.url();
1564
1698
  const requestHostname = new URL(requestUrl).hostname;
1565
- let requestOptions = { timeout: REQUEST_TIMEOUT };
1699
+ let requestOptions = {
1700
+ timeout: REQUEST_TIMEOUT,
1701
+ headers: __spreadValues(__spreadValues({}, yield request.allHeaders()), constants_default.REQUEST_HEADERS)
1702
+ };
1566
1703
  try {
1567
1704
  if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
1568
1705
  throw new Error("resource type mp3/mp4/wav/ogg/webm");
@@ -1573,9 +1710,7 @@ function processSnapshot(snapshot, ctx) {
1573
1710
  if (ctx.config.basicAuthorization) {
1574
1711
  ctx.log.debug(`Adding basic authorization to the headers for root url`);
1575
1712
  let token = Buffer.from(`${ctx.config.basicAuthorization.username}:${ctx.config.basicAuthorization.password}`).toString("base64");
1576
- requestOptions.headers = __spreadProps(__spreadValues({}, request.headers()), {
1577
- Authorization: `Basic ${token}`
1578
- });
1713
+ requestOptions.headers.Authorization = `Basic ${token}`;
1579
1714
  }
1580
1715
  let response, body;
1581
1716
  if (requestUrl === snapshot.url) {
@@ -1652,6 +1787,33 @@ function processSnapshot(snapshot, ctx) {
1652
1787
  return true;
1653
1788
  return false;
1654
1789
  };
1790
+ if (options.web && Object.keys(options.web).length) {
1791
+ processedOptions.web = {};
1792
+ if (options.web.viewports && options.web.viewports.length > 0) {
1793
+ processedOptions.web.viewports = options.web.viewports.filter(
1794
+ (viewport) => Array.isArray(viewport) && viewport.length > 0
1795
+ );
1796
+ }
1797
+ if (options.web.browsers && options.web.browsers.length > 0) {
1798
+ processedOptions.web.browsers = options.web.browsers;
1799
+ }
1800
+ }
1801
+ if (options.mobile && Object.keys(options.mobile).length) {
1802
+ processedOptions.mobile = {};
1803
+ if (options.mobile.devices && options.mobile.devices.length > 0) {
1804
+ processedOptions.mobile.devices = options.mobile.devices;
1805
+ }
1806
+ if (options.mobile.hasOwnProperty("fullPage") && typeof options.mobile.fullPage === "boolean") {
1807
+ processedOptions.mobile.fullPage = options.mobile.fullPage;
1808
+ } else {
1809
+ processedOptions.mobile.fullPage = true;
1810
+ }
1811
+ if (options.mobile.hasOwnProperty("orientation") && (options.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT || options.mobile.orientation === constants_default.MOBILE_ORIENTATION_LANDSCAPE)) {
1812
+ processedOptions.mobile.orientation = options.mobile.orientation;
1813
+ } else {
1814
+ processedOptions.mobile.orientation = constants_default.MOBILE_ORIENTATION_PORTRAIT;
1815
+ }
1816
+ }
1655
1817
  if (options.element && Object.keys(options.element).length) {
1656
1818
  if (options.element.id)
1657
1819
  processedOptions.element = "#" + options.element.id;
@@ -1691,7 +1853,12 @@ function processSnapshot(snapshot, ctx) {
1691
1853
  }
1692
1854
  let navigated = false;
1693
1855
  let previousDeviceType = null;
1694
- let renderViewports = getRenderViewports(ctx);
1856
+ let renderViewports;
1857
+ if (snapshot.options && snapshot.options.web || snapshot.options && snapshot.options.mobile) {
1858
+ renderViewports = getRenderViewportsForOptions(snapshot.options);
1859
+ } else {
1860
+ renderViewports = getRenderViewports(ctx);
1861
+ }
1695
1862
  for (const { viewport, viewportString, fullPage, device } of renderViewports) {
1696
1863
  if (previousDeviceType !== null && previousDeviceType !== device) {
1697
1864
  navigated = false;
@@ -1750,6 +1917,7 @@ function processSnapshot(snapshot, ctx) {
1750
1917
  });
1751
1918
  }
1752
1919
  }
1920
+ ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`);
1753
1921
  }
1754
1922
  return {
1755
1923
  processedSnapshot: {
@@ -1764,6 +1932,282 @@ function processSnapshot(snapshot, ctx) {
1764
1932
  });
1765
1933
  }
1766
1934
 
1935
+ // src/lib/snapshotQueue.ts
1936
+ var Queue = class {
1937
+ constructor(ctx) {
1938
+ this.snapshots = [];
1939
+ this.processedSnapshots = [];
1940
+ this.processing = false;
1941
+ this.processingSnapshot = "";
1942
+ this.snapshotNames = [];
1943
+ this.variants = [];
1944
+ this.ctx = ctx;
1945
+ }
1946
+ enqueue(item) {
1947
+ this.snapshots.push(item);
1948
+ if (!this.ctx.config.delayedUpload) {
1949
+ if (!this.processing) {
1950
+ this.processing = true;
1951
+ this.processNext();
1952
+ }
1953
+ }
1954
+ }
1955
+ startProcessingfunc() {
1956
+ if (!this.processing) {
1957
+ this.processing = true;
1958
+ this.processNext();
1959
+ }
1960
+ }
1961
+ processGenerateVariants(snapshot) {
1962
+ if (snapshot.options) {
1963
+ if (snapshot.options.web) {
1964
+ this.generateWebVariants(snapshot, snapshot.options.web);
1965
+ }
1966
+ if (snapshot.options.mobile) {
1967
+ this.generateMobileVariants(snapshot, snapshot.options.mobile);
1968
+ }
1969
+ }
1970
+ if (!snapshot.options || snapshot.options && !snapshot.options.web && !snapshot.options.mobile) {
1971
+ this.generateVariants(snapshot, this.ctx.config);
1972
+ }
1973
+ }
1974
+ generateVariants(snapshot, config) {
1975
+ if (config.web) {
1976
+ const browsers = config.web.browsers || [];
1977
+ const viewports = config.web.viewports || [];
1978
+ for (const browser of browsers) {
1979
+ for (const viewport of viewports) {
1980
+ const width = viewport.width;
1981
+ const height = viewport.height || 0;
1982
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
1983
+ this.variants.push(variant);
1984
+ }
1985
+ }
1986
+ }
1987
+ if (config.mobile) {
1988
+ const devices = config.mobile.devices || [];
1989
+ const orientation = config.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
1990
+ for (const device of devices) {
1991
+ const variant = `${snapshot.name}_${device}_${orientation}`;
1992
+ this.variants.push(variant);
1993
+ }
1994
+ }
1995
+ }
1996
+ generateWebVariants(snapshot, webConfig) {
1997
+ var _a, _b, _c;
1998
+ 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];
1999
+ const viewports = webConfig.viewports || [];
2000
+ for (const browser of browsers) {
2001
+ for (const viewport of viewports) {
2002
+ const width = viewport[0];
2003
+ const height = viewport[1] || 0;
2004
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2005
+ this.variants.push(variant);
2006
+ }
2007
+ }
2008
+ }
2009
+ generateMobileVariants(snapshot, mobileConfig) {
2010
+ var _a, _b, _c;
2011
+ const devices = mobileConfig.devices || [];
2012
+ 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;
2013
+ for (const device of devices) {
2014
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2015
+ this.variants.push(variant);
2016
+ }
2017
+ }
2018
+ filterExistingVariants(snapshot, config) {
2019
+ let drop = true;
2020
+ if (snapshot.options && snapshot.options.web) {
2021
+ const webDrop = this.filterWebVariants(snapshot, snapshot.options.web);
2022
+ if (!webDrop)
2023
+ drop = false;
2024
+ }
2025
+ if (snapshot.options && snapshot.options.mobile) {
2026
+ const mobileDrop = this.filterMobileVariants(snapshot, snapshot.options.mobile);
2027
+ if (!mobileDrop)
2028
+ drop = false;
2029
+ }
2030
+ if (!snapshot.options || snapshot.options && !snapshot.options.web && !snapshot.options.mobile) {
2031
+ const configDrop = this.filterVariants(snapshot, config);
2032
+ if (!configDrop)
2033
+ drop = false;
2034
+ }
2035
+ return drop;
2036
+ }
2037
+ filterVariants(snapshot, config) {
2038
+ var _a;
2039
+ let allVariantsDropped = true;
2040
+ if (config.web) {
2041
+ const browsers = config.web.browsers || [];
2042
+ const viewports = config.web.viewports || [];
2043
+ for (const browser of browsers) {
2044
+ for (const viewport of viewports) {
2045
+ const width = viewport.width;
2046
+ const height = viewport.height || 0;
2047
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2048
+ if (!this.variants.includes(variant)) {
2049
+ allVariantsDropped = false;
2050
+ if (!snapshot.options)
2051
+ snapshot.options = {};
2052
+ if (!snapshot.options.web)
2053
+ snapshot.options.web = { browsers: [], viewports: [] };
2054
+ if (!snapshot.options.web.browsers.includes(browser)) {
2055
+ snapshot.options.web.browsers.push(browser);
2056
+ }
2057
+ const viewportExists = snapshot.options.web.viewports.some(
2058
+ (existingViewport) => existingViewport[0] === width && (existingViewport.length < 2 || existingViewport[1] === height)
2059
+ );
2060
+ if (!viewportExists) {
2061
+ if (height > 0) {
2062
+ snapshot.options.web.viewports.push([width, height]);
2063
+ } else {
2064
+ snapshot.options.web.viewports.push([width]);
2065
+ }
2066
+ }
2067
+ }
2068
+ }
2069
+ }
2070
+ }
2071
+ if (config.mobile) {
2072
+ const devices = config.mobile.devices || [];
2073
+ const orientation = config.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
2074
+ const fullPage = (_a = config.mobile.fullPage) != null ? _a : true;
2075
+ for (const device of devices) {
2076
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2077
+ if (!this.variants.includes(variant)) {
2078
+ allVariantsDropped = false;
2079
+ if (!snapshot.options)
2080
+ snapshot.options = {};
2081
+ if (!snapshot.options.mobile)
2082
+ snapshot.options.mobile = { devices: [], orientation: constants_default.MOBILE_ORIENTATION_PORTRAIT, fullPage };
2083
+ if (!snapshot.options.mobile.devices.includes(device)) {
2084
+ snapshot.options.mobile.devices.push(device);
2085
+ }
2086
+ snapshot.options.mobile.orientation = orientation;
2087
+ }
2088
+ }
2089
+ }
2090
+ return allVariantsDropped;
2091
+ }
2092
+ filterWebVariants(snapshot, webConfig) {
2093
+ var _a, _b, _c;
2094
+ 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];
2095
+ const viewports = webConfig.viewports || [];
2096
+ let allVariantsDropped = true;
2097
+ if (!snapshot.options) {
2098
+ snapshot.options = {};
2099
+ }
2100
+ snapshot.options.web = { browsers: [], viewports: [] };
2101
+ for (const browser of browsers) {
2102
+ for (const viewport of viewports) {
2103
+ const width = viewport[0];
2104
+ const height = viewport[1] || 0;
2105
+ const variant = `${snapshot.name}_${browser}_viewport[${width}]_viewport[${height}]`;
2106
+ if (!this.variants.includes(variant)) {
2107
+ allVariantsDropped = false;
2108
+ if (!snapshot.options.web.browsers.includes(browser)) {
2109
+ snapshot.options.web.browsers.push(browser);
2110
+ }
2111
+ const viewportExists = snapshot.options.web.viewports.some(
2112
+ (existingViewport) => existingViewport[0] === width && (existingViewport.length < 2 || existingViewport[1] === height)
2113
+ );
2114
+ if (!viewportExists) {
2115
+ if (height > 0) {
2116
+ snapshot.options.web.viewports.push([width, height]);
2117
+ } else {
2118
+ snapshot.options.web.viewports.push([width]);
2119
+ }
2120
+ }
2121
+ }
2122
+ }
2123
+ }
2124
+ return allVariantsDropped;
2125
+ }
2126
+ filterMobileVariants(snapshot, mobileConfig) {
2127
+ var _a, _b, _c, _d, _e, _f;
2128
+ if (!snapshot.options) {
2129
+ snapshot.options = {};
2130
+ }
2131
+ const devices = mobileConfig.devices || [];
2132
+ 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;
2133
+ const fullPage = (_f = (_e = mobileConfig.fullPage) != null ? _e : (_d = this.ctx.config.mobile) == null ? void 0 : _d.fullPage) != null ? _f : true;
2134
+ let allVariantsDropped = true;
2135
+ snapshot.options.mobile = { devices: [], orientation: constants_default.MOBILE_ORIENTATION_PORTRAIT, fullPage };
2136
+ for (const device of devices) {
2137
+ const variant = `${snapshot.name}_${device}_${orientation}`;
2138
+ if (!this.variants.includes(variant)) {
2139
+ allVariantsDropped = false;
2140
+ snapshot.options.mobile.devices.push(device);
2141
+ snapshot.options.mobile.orientation = orientation;
2142
+ }
2143
+ }
2144
+ return allVariantsDropped;
2145
+ }
2146
+ processNext() {
2147
+ return __async(this, null, function* () {
2148
+ if (!this.isEmpty()) {
2149
+ let snapshot;
2150
+ if (this.ctx.config.delayedUpload) {
2151
+ snapshot = this.snapshots.pop();
2152
+ } else {
2153
+ snapshot = this.snapshots.shift();
2154
+ }
2155
+ try {
2156
+ this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
2157
+ let drop = false;
2158
+ if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
2159
+ drop = true;
2160
+ 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.`);
2161
+ }
2162
+ if (this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
2163
+ drop = this.filterExistingVariants(snapshot, this.ctx.config);
2164
+ }
2165
+ if (snapshot && snapshot.name && !this.snapshotNames.includes(snapshot.name) && !drop) {
2166
+ this.snapshotNames.push(snapshot.name);
2167
+ }
2168
+ if (this.ctx.config.delayedUpload && snapshot && !drop) {
2169
+ this.processGenerateVariants(snapshot);
2170
+ }
2171
+ if (!drop) {
2172
+ let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
2173
+ yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
2174
+ this.ctx.totalSnapshots++;
2175
+ this.processedSnapshots.push({ name: snapshot.name, warnings });
2176
+ }
2177
+ } catch (error) {
2178
+ this.ctx.log.debug(`snapshot failed; ${error}`);
2179
+ this.processedSnapshots.push({ name: snapshot.name, error: error.message });
2180
+ }
2181
+ if (this.ctx.browser) {
2182
+ for (let context of this.ctx.browser.contexts()) {
2183
+ for (let page of context.pages()) {
2184
+ yield page.close();
2185
+ this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
2186
+ }
2187
+ yield context.close();
2188
+ this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
2189
+ }
2190
+ }
2191
+ this.processNext();
2192
+ } else {
2193
+ this.processing = false;
2194
+ }
2195
+ });
2196
+ }
2197
+ isProcessing() {
2198
+ return this.processing;
2199
+ }
2200
+ getProcessingSnapshot() {
2201
+ return this.processingSnapshot;
2202
+ }
2203
+ getProcessedSnapshots() {
2204
+ return this.processedSnapshots;
2205
+ }
2206
+ isEmpty() {
2207
+ return this.snapshots && this.snapshots.length ? false : true;
2208
+ }
2209
+ };
2210
+
1767
2211
  // src/commander/exec.ts
1768
2212
  var command = new commander.Command();
1769
2213
  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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdatest/smartui-cli",
3
- "version": "4.0.5",
3
+ "version": "4.0.7",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"
@@ -17,10 +17,10 @@
17
17
  "author": "LambdaTest <keys@lambdatest.com>",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@playwright/browser-chromium": "^1.45.3",
21
- "@playwright/browser-firefox": "^1.45.3",
22
- "@playwright/browser-webkit": "^1.45.3",
23
- "@playwright/test": "^1.45.3",
20
+ "@playwright/browser-chromium": "^1.47.2",
21
+ "@playwright/browser-firefox": "^1.47.2",
22
+ "@playwright/browser-webkit": "^1.47.2",
23
+ "@playwright/test": "^1.47.2",
24
24
  "@types/cross-spawn": "^6.0.4",
25
25
  "@types/node": "^20.8.9",
26
26
  "@types/which": "^3.0.2",