@lambdatest/smartui-cli 2.0.9 → 3.0.1

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 +696 -264
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -4,10 +4,10 @@
4
4
  var commander = require('commander');
5
5
  var which = require('which');
6
6
  var listr2 = require('listr2');
7
- var chalk = require('chalk');
7
+ var chalk8 = require('chalk');
8
8
  var path2 = require('path');
9
9
  var fastify = require('fastify');
10
- var fs2 = require('fs');
10
+ var fs4 = require('fs');
11
11
  var test = require('@playwright/test');
12
12
  var Ajv = require('ajv');
13
13
  var addErrors = require('ajv-errors');
@@ -20,10 +20,10 @@ var spawn = require('cross-spawn');
20
20
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
 
22
22
  var which__default = /*#__PURE__*/_interopDefault(which);
23
- var chalk__default = /*#__PURE__*/_interopDefault(chalk);
23
+ var chalk8__default = /*#__PURE__*/_interopDefault(chalk8);
24
24
  var path2__default = /*#__PURE__*/_interopDefault(path2);
25
25
  var fastify__default = /*#__PURE__*/_interopDefault(fastify);
26
- var fs2__default = /*#__PURE__*/_interopDefault(fs2);
26
+ var fs4__default = /*#__PURE__*/_interopDefault(fs4);
27
27
  var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
28
28
  var addErrors__default = /*#__PURE__*/_interopDefault(addErrors);
29
29
  var FormData__default = /*#__PURE__*/_interopDefault(FormData);
@@ -66,9 +66,241 @@ var __async = (__this, __arguments, generator) => {
66
66
  step((generator = generator.apply(__this, __arguments)).next());
67
67
  });
68
68
  };
69
+
70
+ // src/lib/constants.ts
71
+ var constants_default = {
72
+ // default configs
73
+ DEFAULT_CONFIG: {
74
+ web: {
75
+ browsers: [
76
+ "chrome",
77
+ "firefox",
78
+ "safari",
79
+ "edge"
80
+ ],
81
+ viewports: [
82
+ [1920],
83
+ [1366],
84
+ [1028]
85
+ ]
86
+ },
87
+ mobile: {
88
+ devices: [
89
+ "iPhone 14",
90
+ "Galaxy S24"
91
+ ],
92
+ fullPage: true,
93
+ orientation: "portrait"
94
+ },
95
+ waitForTimeout: 1e3,
96
+ enableJavaScript: false,
97
+ allowedHostnames: []
98
+ },
99
+ DEFAULT_WEB_STATIC_CONFIG: [
100
+ {
101
+ "name": "lambdatest-home-page",
102
+ "url": "https://www.lambdatest.com",
103
+ "waitForTimeout": 1e3
104
+ },
105
+ {
106
+ "name": "example-page",
107
+ "url": "https://example.com/"
108
+ }
109
+ ],
110
+ // browsers
111
+ CHROME: "chrome",
112
+ SAFARI: "safari",
113
+ FIREFOX: "firefox",
114
+ EDGE: "edge",
115
+ EDGE_CHANNEL: "msedge",
116
+ WEBKIT: "webkit",
117
+ // viewports
118
+ MIN_VIEWPORT_HEIGHT: 1080,
119
+ // mobile
120
+ MOBILE_OS_ANDROID: "android",
121
+ MOBILE_OS_IOS: "ios",
122
+ MOBILE_ORIENTATION_PORTRAIT: "portrait",
123
+ MOBILE_ORIENTATION_LANDSCAPE: "landscape",
124
+ // CI
125
+ GITHUB_API_HOST: "https://api.github.com",
126
+ SUPPORTED_MOBILE_DEVICES: {
127
+ "Blackberry KEY2 LE": { os: "android", viewport: { width: 412, height: 618 } },
128
+ "Galaxy A12": { os: "android", viewport: { width: 360, height: 800 } },
129
+ "Galaxy A21s": { os: "android", viewport: { width: 412, height: 915 } },
130
+ "Galaxy A22": { os: "android", viewport: { width: 358, height: 857 } },
131
+ "Galaxy A31": { os: "android", viewport: { width: 412, height: 915 } },
132
+ "Galaxy A32": { os: "android", viewport: { width: 412, height: 915 } },
133
+ "Galaxy A51": { os: "android", viewport: { width: 412, height: 915 } },
134
+ "Galaxy A7": { os: "android", viewport: { width: 412, height: 846 } },
135
+ "Galaxy A70": { os: "android", viewport: { width: 412, height: 915 } },
136
+ "Galaxy A8": { os: "android", viewport: { width: 360, height: 740 } },
137
+ "Galaxy A8 Plus": { os: "android", viewport: { width: 412, height: 846 } },
138
+ "Galaxy J7 Prime": { os: "android", viewport: { width: 360, height: 640 } },
139
+ "Galaxy M12": { os: "android", viewport: { width: 412, height: 915 } },
140
+ "Galaxy M31": { os: "android", viewport: { width: 412, height: 892 } },
141
+ "Galaxy Note10": { os: "android", viewport: { width: 412, height: 869 } },
142
+ "Galaxy Note10 Plus": { os: "android", viewport: { width: 412, height: 869 } },
143
+ "Galaxy Note20": { os: "android", viewport: { width: 412, height: 915 } },
144
+ "Galaxy Note20 Ultra": { os: "android", viewport: { width: 412, height: 869 } },
145
+ "Galaxy S10": { os: "android", viewport: { width: 360, height: 760 } },
146
+ "Galaxy S10 Plus": { os: "android", viewport: { width: 412, height: 869 } },
147
+ "Galaxy S10e": { os: "android", viewport: { width: 412, height: 740 } },
148
+ "Galaxy S20": { os: "android", viewport: { width: 360, height: 800 } },
149
+ "Galaxy S20 FE": { os: "android", viewport: { width: 412, height: 914 } },
150
+ "Galaxy S20 Ultra": { os: "android", viewport: { width: 412, height: 915 } },
151
+ "Galaxy S20 Plus": { os: "android", viewport: { width: 384, height: 854 } },
152
+ "Galaxy S21": { os: "android", viewport: { width: 360, height: 800 } },
153
+ "Galaxy S21 FE": { os: "android", viewport: { width: 360, height: 780 } },
154
+ "Galaxy S21 Ultra": { os: "android", viewport: { width: 384, height: 854 } },
155
+ "Galaxy S21 Plus": { os: "android", viewport: { width: 360, height: 800 } },
156
+ "Galaxy S22": { os: "android", viewport: { width: 360, height: 780 } },
157
+ "Galaxy S22 Ultra": { os: "android", viewport: { width: 384, height: 854 } },
158
+ "Galaxy S23": { os: "android", viewport: { width: 360, height: 645 } },
159
+ "Galaxy S23 Plus": { os: "android", viewport: { width: 360, height: 648 } },
160
+ "Galaxy S23 Ultra": { os: "android", viewport: { width: 384, height: 689 } },
161
+ "Galaxy S24": { os: "android", viewport: { width: 360, height: 780 } },
162
+ "Galaxy S24 Plus": { os: "android", viewport: { width: 384, height: 832 } },
163
+ "Galaxy S24 Ultra": { os: "android", viewport: { width: 384, height: 832 } },
164
+ "Galaxy S7": { os: "android", viewport: { width: 360, height: 640 } },
165
+ "Galaxy S7 Edge": { os: "android", viewport: { width: 360, height: 640 } },
166
+ "Galaxy S8": { os: "android", viewport: { width: 360, height: 740 } },
167
+ "Galaxy S8 Plus": { os: "android", viewport: { width: 360, height: 740 } },
168
+ "Galaxy S9": { os: "android", viewport: { width: 360, height: 740 } },
169
+ "Galaxy S9 Plus": { os: "android", viewport: { width: 360, height: 740 } },
170
+ "Galaxy Tab A7 Lite": { os: "android", viewport: { width: 534, height: 894 } },
171
+ "Galaxy Tab A8": { os: "android", viewport: { width: 800, height: 1280 } },
172
+ "Galaxy Tab S3": { os: "android", viewport: { width: 1024, height: 768 } },
173
+ "Galaxy Tab S4": { os: "android", viewport: { width: 712, height: 1138 } },
174
+ "Galaxy Tab S7": { os: "android", viewport: { width: 800, height: 1192 } },
175
+ "Galaxy Tab S8": { os: "android", viewport: { width: 753, height: 1205 } },
176
+ "Galaxy Tab S8 Plus": { os: "android", viewport: { width: 825, height: 1318 } },
177
+ "Huawei Mate 20 Pro": { os: "android", viewport: { width: 360, height: 780 } },
178
+ "Huawei P20 Pro": { os: "android", viewport: { width: 360, height: 747 } },
179
+ "Huawei P30": { os: "android", viewport: { width: 360, height: 780 } },
180
+ "Huawei P30 Pro": { os: "android", viewport: { width: 360, height: 780 } },
181
+ "Microsoft Surface Duo": { os: "android", viewport: { width: 1114, height: 705 } },
182
+ "Moto G7 Play": { os: "android", viewport: { width: 360, height: 760 } },
183
+ "Moto G9 Play": { os: "android", viewport: { width: 393, height: 786 } },
184
+ "Moto G Stylus (2022)": { os: "android", viewport: { width: 432, height: 984 } },
185
+ "Nexus 5": { os: "android", viewport: { width: 360, height: 640 } },
186
+ "Nexus 5X": { os: "android", viewport: { width: 412, height: 732 } },
187
+ "Nokia 5": { os: "android", viewport: { width: 360, height: 640 } },
188
+ "Nothing Phone (1)": { os: "android", viewport: { width: 412, height: 915 } },
189
+ "OnePlus 10 Pro": { os: "android", viewport: { width: 412, height: 919 } },
190
+ "OnePlus 11": { os: "android", viewport: { width: 360, height: 804 } },
191
+ "OnePlus 6": { os: "android", viewport: { width: 412, height: 869 } },
192
+ "OnePlus 6T": { os: "android", viewport: { width: 412, height: 892 } },
193
+ "OnePlus 7": { os: "android", viewport: { width: 412, height: 892 } },
194
+ "OnePlus 7T": { os: "android", viewport: { width: 412, height: 914 } },
195
+ "OnePlus 8": { os: "android", viewport: { width: 412, height: 915 } },
196
+ "OnePlus 9": { os: "android", viewport: { width: 411, height: 915 } },
197
+ "OnePlus 9 Pro": { os: "android", viewport: { width: 412, height: 919 } },
198
+ "OnePlus Nord": { os: "android", viewport: { width: 412, height: 914 } },
199
+ "OnePlus Nord 2": { os: "android", viewport: { width: 412, height: 915 } },
200
+ "OnePlus Nord CE": { os: "android", viewport: { width: 412, height: 915 } },
201
+ "Oppo A12": { os: "android", viewport: { width: 360, height: 760 } },
202
+ "Oppo A15": { os: "android", viewport: { width: 360, height: 800 } },
203
+ "Oppo A54": { os: "android", viewport: { width: 360, height: 800 } },
204
+ "Oppo A5s": { os: "android", viewport: { width: 360, height: 760 } },
205
+ "Oppo F17": { os: "android", viewport: { width: 360, height: 800 } },
206
+ "Oppo K10": { os: "android", viewport: { width: 360, height: 804 } },
207
+ "Pixel 3": { os: "android", viewport: { width: 412, height: 823 } },
208
+ "Pixel 3 XL": { os: "android", viewport: { width: 412, height: 846 } },
209
+ "Pixel 3a": { os: "android", viewport: { width: 412, height: 823 } },
210
+ "Pixel 4": { os: "android", viewport: { width: 392, height: 830 } },
211
+ "Pixel 4 XL": { os: "android", viewport: { width: 412, height: 823 } },
212
+ "Pixel 4a": { os: "android", viewport: { width: 393, height: 851 } },
213
+ "Pixel 5": { os: "android", viewport: { width: 393, height: 851 } },
214
+ "Pixel 6": { os: "android", viewport: { width: 393, height: 786 } },
215
+ "Pixel 6 Pro": { os: "android", viewport: { width: 412, height: 892 } },
216
+ "Pixel 7": { os: "android", viewport: { width: 412, height: 915 } },
217
+ "Pixel 7 Pro": { os: "android", viewport: { width: 412, height: 892 } },
218
+ "Pixel 8": { os: "android", viewport: { width: 412, height: 915 } },
219
+ "Pixel 8 Pro": { os: "android", viewport: { width: 448, height: 998 } },
220
+ "Poco M2 Pro": { os: "android", viewport: { width: 393, height: 873 } },
221
+ "POCO X3 Pro": { os: "android", viewport: { width: 393, height: 873 } },
222
+ "Realme 5i": { os: "android", viewport: { width: 360, height: 800 } },
223
+ "Realme 7i": { os: "android", viewport: { width: 360, height: 800 } },
224
+ "Realme 8i": { os: "android", viewport: { width: 360, height: 804 } },
225
+ "Realme C21Y": { os: "android", viewport: { width: 360, height: 800 } },
226
+ "Realme C21": { os: "android", viewport: { width: 360, height: 800 } },
227
+ "Realme GT2 Pro": { os: "android", viewport: { width: 360, height: 804 } },
228
+ "Redmi 8": { os: "android", viewport: { width: 360, height: 760 } },
229
+ "Redmi 9": { os: "android", viewport: { width: 360, height: 800 } },
230
+ "Redmi 9C": { os: "android", viewport: { width: 360, height: 800 } },
231
+ "Redmi Note 10 Pro": { os: "android", viewport: { width: 393, height: 873 } },
232
+ "Redmi Note 8": { os: "android", viewport: { width: 393, height: 851 } },
233
+ "Redmi Note 8 Pro": { os: "android", viewport: { width: 393, height: 851 } },
234
+ "Redmi Note 9": { os: "android", viewport: { width: 393, height: 851 } },
235
+ "Redmi Note 9 Pro Max": { os: "android", viewport: { width: 393, height: 873 } },
236
+ "Redmi Y2": { os: "android", viewport: { width: 360, height: 720 } },
237
+ "Tecno Spark 7": { os: "android", viewport: { width: 360, height: 800 } },
238
+ "Vivo Y22": { os: "android", viewport: { width: 385, height: 860 } },
239
+ "Vivo T1": { os: "android", viewport: { width: 393, height: 873 } },
240
+ "Vivo V7": { os: "android", viewport: { width: 360, height: 720 } },
241
+ "Vivo Y11": { os: "android", viewport: { width: 360, height: 722 } },
242
+ "Vivo Y12": { os: "android", viewport: { width: 360, height: 722 } },
243
+ "Vivo Y20g": { os: "android", viewport: { width: 385, height: 854 } },
244
+ "Vivo Y50": { os: "android", viewport: { width: 393, height: 786 } },
245
+ "Xiaomi 12 Pro": { os: "android", viewport: { width: 412, height: 915 } },
246
+ "Xperia Z5": { os: "android", viewport: { width: 360, height: 640 } },
247
+ "Xperia Z5 Dual": { os: "android", viewport: { width: 360, height: 640 } },
248
+ "Zenfone 6": { os: "android", viewport: { width: 412, height: 892 } },
249
+ "iPad 10.2 (2019)": { os: "ios", viewport: { width: 810, height: 1080 } },
250
+ "iPad 10.2 (2020)": { os: "ios", viewport: { width: 834, height: 1194 } },
251
+ "iPad 10.2 (2021)": { os: "ios", viewport: { width: 810, height: 1080 } },
252
+ "iPad 9.7 (2017)": { os: "ios", viewport: { width: 768, height: 1024 } },
253
+ "iPad Air (2019)": { os: "ios", viewport: { width: 834, height: 1112 } },
254
+ "iPad Air (2020)": { os: "ios", viewport: { width: 820, height: 1180 } },
255
+ "iPad Air (2022)": { os: "ios", viewport: { width: 820, height: 1180 } },
256
+ "iPad mini (2019)": { os: "ios", viewport: { width: 768, height: 1024 } },
257
+ "iPad mini (2021)": { os: "ios", viewport: { width: 744, height: 1133 } },
258
+ "iPad Pro 11 (2021)": { os: "ios", viewport: { width: 834, height: 1194 } },
259
+ "iPad Pro 11 (2022)": { os: "ios", viewport: { width: 834, height: 1194 } },
260
+ "iPad Pro 12.9 (2018)": { os: "ios", viewport: { width: 1024, height: 1366 } },
261
+ "iPad Pro 12.9 (2020)": { os: "ios", viewport: { width: 1024, height: 1366 } },
262
+ "iPad Pro 12.9 (2021)": { os: "ios", viewport: { width: 1024, height: 1366 } },
263
+ "iPad Pro 12.9 (2022)": { os: "ios", viewport: { width: 1024, height: 1366 } },
264
+ "iPhone 11": { os: "ios", viewport: { width: 375, height: 812 } },
265
+ "iPhone 11 Pro": { os: "ios", viewport: { width: 375, height: 812 } },
266
+ "iPhone 11 Pro Max": { os: "ios", viewport: { width: 414, height: 896 } },
267
+ "iPhone 12": { os: "ios", viewport: { width: 390, height: 844 } },
268
+ "iPhone 12 Mini": { os: "ios", viewport: { width: 375, height: 812 } },
269
+ "iPhone 12 Pro": { os: "ios", viewport: { width: 390, height: 844 } },
270
+ "iPhone 12 Pro Max": { os: "ios", viewport: { width: 428, height: 926 } },
271
+ "iPhone 13": { os: "ios", viewport: { width: 390, height: 844 } },
272
+ "iPhone 13 Mini": { os: "ios", viewport: { width: 390, height: 844 } },
273
+ "iPhone 13 Pro": { os: "ios", viewport: { width: 390, height: 844 } },
274
+ "iPhone 13 Pro Max": { os: "ios", viewport: { width: 428, height: 926 } },
275
+ "iPhone 14": { os: "ios", viewport: { width: 390, height: 844 } },
276
+ "iPhone 14 Plus": { os: "ios", viewport: { width: 428, height: 926 } },
277
+ "iPhone 14 Pro": { os: "ios", viewport: { width: 390, height: 844 } },
278
+ "iPhone 14 Pro Max": { os: "ios", viewport: { width: 428, height: 928 } },
279
+ "iPhone 15": { os: "ios", viewport: { width: 393, height: 852 } },
280
+ "iPhone 15 Plus": { os: "ios", viewport: { width: 430, height: 932 } },
281
+ "iPhone 15 Pro": { os: "ios", viewport: { width: 393, height: 852 } },
282
+ "iPhone 15 Pro Max": { os: "ios", viewport: { width: 430, height: 932 } },
283
+ "iPhone 6": { os: "ios", viewport: { width: 375, height: 667 } },
284
+ "iPhone 6s": { os: "ios", viewport: { width: 375, height: 667 } },
285
+ "iPhone 6s Plus": { os: "ios", viewport: { width: 414, height: 736 } },
286
+ "iPhone 7": { os: "ios", viewport: { width: 375, height: 667 } },
287
+ "iPhone 7 Plus": { os: "ios", viewport: { width: 414, height: 736 } },
288
+ "iPhone 8": { os: "ios", viewport: { width: 375, height: 667 } },
289
+ "iPhone 8 Plus": { os: "ios", viewport: { width: 414, height: 736 } },
290
+ "iPhone SE (2016)": { os: "ios", viewport: { width: 320, height: 568 } },
291
+ "iPhone SE (2020)": { os: "ios", viewport: { width: 375, height: 667 } },
292
+ "iPhone SE (2022)": { os: "ios", viewport: { width: 375, height: 667 } },
293
+ "iPhone X": { os: "ios", viewport: { width: 375, height: 812 } },
294
+ "iPhone XR": { os: "ios", viewport: { width: 414, height: 896 } },
295
+ "iPhone XS": { os: "ios", viewport: { width: 375, height: 812 } },
296
+ "iPhone XS Max": { os: "ios", viewport: { width: 414, height: 896 } }
297
+ }
298
+ };
299
+
300
+ // src/lib/utils.ts
69
301
  function delDir(dir) {
70
- if (fs2__default.default.existsSync(dir)) {
71
- fs2__default.default.rmSync(dir, { recursive: true });
302
+ if (fs4__default.default.existsSync(dir)) {
303
+ fs4__default.default.rmSync(dir, { recursive: true });
72
304
  }
73
305
  }
74
306
  function scrollToBottomAndBackToTop({
@@ -97,9 +329,93 @@ function scrollToBottomAndBackToTop({
97
329
  })();
98
330
  });
99
331
  }
332
+ function launchBrowsers(ctx) {
333
+ return __async(this, null, function* () {
334
+ let browsers = {};
335
+ let launchOptions = { headless: true };
336
+ if (ctx.config.web) {
337
+ for (const browser of ctx.config.web.browsers) {
338
+ switch (browser) {
339
+ case constants_default.CHROME:
340
+ browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
341
+ break;
342
+ case constants_default.SAFARI:
343
+ browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
344
+ break;
345
+ case constants_default.FIREFOX:
346
+ browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
347
+ break;
348
+ case constants_default.EDGE:
349
+ browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
350
+ break;
351
+ }
352
+ }
353
+ }
354
+ if (ctx.config.mobile) {
355
+ for (const device of ctx.config.mobile.devices) {
356
+ if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
357
+ browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
358
+ else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
359
+ browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
360
+ }
361
+ }
362
+ return browsers;
363
+ });
364
+ }
365
+ function closeBrowsers(browsers) {
366
+ return __async(this, null, function* () {
367
+ var _a;
368
+ for (const browserName of Object.keys(browsers))
369
+ yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
370
+ });
371
+ }
372
+ function getWebRenderViewports(ctx) {
373
+ let webRenderViewports = [];
374
+ if (ctx.config.web) {
375
+ for (const viewport of ctx.config.web.viewports) {
376
+ webRenderViewports.push({
377
+ viewport,
378
+ viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
379
+ fullPage: viewport.height ? false : true,
380
+ device: false
381
+ });
382
+ }
383
+ }
384
+ return webRenderViewports;
385
+ }
386
+ function getMobileRenderViewports(ctx) {
387
+ var _a;
388
+ let mobileRenderViewports = {};
389
+ mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
390
+ mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
391
+ if (ctx.config.mobile) {
392
+ for (const device of ctx.config.mobile.devices) {
393
+ let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
394
+ let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
395
+ let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
396
+ (_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
397
+ viewport: { width: portrait ? width : height, height: portrait ? height : width },
398
+ viewportString: `${device} (${ctx.config.mobile.orientation})`,
399
+ fullPage: ctx.config.mobile.fullPage,
400
+ device: true,
401
+ os
402
+ });
403
+ }
404
+ }
405
+ return mobileRenderViewports;
406
+ }
407
+ function getRenderViewports(ctx) {
408
+ let mobileRenderViewports = getMobileRenderViewports(ctx);
409
+ return [
410
+ ...getWebRenderViewports(ctx),
411
+ ...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
412
+ ...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
413
+ ];
414
+ }
100
415
  var MAX_RESOURCE_SIZE = 5 * 1024 ** 2;
101
416
  var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
102
417
  var ALLOWED_STATUSES = [200, 201];
418
+ var REQUEST_TIMEOUT = 1e4;
103
419
  var MIN_VIEWPORT_HEIGHT = 1080;
104
420
  var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function* () {
105
421
  ctx.log.debug(`Processing snapshot ${snapshot.name}`);
@@ -110,13 +426,13 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
110
426
  let cache = {};
111
427
  yield page.route("**/*", (route, request) => __async(void 0, null, function* () {
112
428
  const requestUrl = request.url();
113
- const snapshotHostname = new URL(snapshot.url).hostname;
114
429
  const requestHostname = new URL(requestUrl).hostname;
115
430
  try {
116
- const response = yield page.request.fetch(request);
117
- const body = yield response.body();
118
- if (ctx.webConfig.enableJavaScript)
431
+ ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
432
+ if (ctx.config.enableJavaScript)
119
433
  ALLOWED_RESOURCES.push("script");
434
+ const response = yield page.request.fetch(request, { timeout: REQUEST_TIMEOUT });
435
+ const body = yield response.body();
120
436
  if (!body) {
121
437
  ctx.log.debug(`Handling request ${requestUrl}
122
438
  - skipping no response`);
@@ -126,7 +442,7 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
126
442
  } else if (requestUrl === snapshot.url) {
127
443
  ctx.log.debug(`Handling request ${requestUrl}
128
444
  - skipping root resource`);
129
- } else if (requestHostname !== snapshotHostname) {
445
+ } else if (!ctx.config.allowedHostnames.includes(requestHostname)) {
130
446
  ctx.log.debug(`Handling request ${requestUrl}
131
447
  - skipping remote resource`);
132
448
  } else if (cache[requestUrl]) {
@@ -138,7 +454,7 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
138
454
  } else if (!ALLOWED_STATUSES.includes(response.status())) {
139
455
  ctx.log.debug(`Handling request ${requestUrl}
140
456
  - skipping disallowed status [${response.status()}]`);
141
- } else if (!ctx.webConfig.enableJavaScript && !ALLOWED_RESOURCES.includes(request.resourceType())) {
457
+ } else if (!ALLOWED_RESOURCES.includes(request.resourceType())) {
142
458
  ctx.log.debug(`Handling request ${requestUrl}
143
459
  - skipping disallowed resource type [${request.resourceType()}]`);
144
460
  } else {
@@ -155,48 +471,66 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
155
471
  body
156
472
  });
157
473
  } catch (error) {
158
- ctx.log.debug(`Handling request ${requestUrl} - aborted`);
474
+ ctx.log.debug(`Handling request ${requestUrl}
475
+ - aborted due to ${error.message}`);
159
476
  route.abort();
160
477
  }
161
478
  }));
162
479
  let options = snapshot.options;
163
480
  let optionWarnings = /* @__PURE__ */ new Set();
164
481
  let processedOptions = {};
165
- let selectors2 = [];
482
+ let selectors = [];
166
483
  let ignoreOrSelectDOM;
167
484
  let ignoreOrSelectBoxes;
168
485
  if (options && Object.keys(options).length) {
169
486
  ctx.log.debug(`Snapshot options: ${JSON.stringify(options)}`);
170
- if (options.ignoreDOM && Object.keys(options.ignoreDOM).length || options.selectDOM && Object.keys(options.selectDOM).length) {
171
- if (options.ignoreDOM && Object.keys(options.ignoreDOM).length) {
172
- processedOptions.ignoreBoxes = {};
173
- ignoreOrSelectDOM = "ignoreDOM";
174
- ignoreOrSelectBoxes = "ignoreBoxes";
175
- } else {
176
- processedOptions.selectBoxes = {};
177
- ignoreOrSelectDOM = "selectDOM";
178
- ignoreOrSelectBoxes = "selectBoxes";
179
- }
487
+ const isNotAllEmpty = (obj) => {
488
+ var _a;
489
+ for (let key in obj)
490
+ if ((_a = obj[key]) == null ? void 0 : _a.length)
491
+ return true;
492
+ return false;
493
+ };
494
+ if (options.element && Object.keys(options.element).length) {
495
+ if (options.element.id)
496
+ processedOptions.element = "#" + options.element.id;
497
+ else if (options.element.class)
498
+ processedOptions.element = "." + options.element.class;
499
+ else if (options.element.cssSelector)
500
+ processedOptions.element = options.element.cssSelector;
501
+ else if (options.element.xpath)
502
+ processedOptions.element = "xpath=" + options.element.xpath;
503
+ } else if (options.ignoreDOM && Object.keys(options.ignoreDOM).length && isNotAllEmpty(options.ignoreDOM)) {
504
+ processedOptions.ignoreBoxes = {};
505
+ ignoreOrSelectDOM = "ignoreDOM";
506
+ ignoreOrSelectBoxes = "ignoreBoxes";
507
+ } else if (options.selectDOM && Object.keys(options.selectDOM).length && isNotAllEmpty(options.selectDOM)) {
508
+ processedOptions.selectBoxes = {};
509
+ ignoreOrSelectDOM = "selectDOM";
510
+ ignoreOrSelectBoxes = "selectBoxes";
511
+ }
512
+ if (ignoreOrSelectDOM) {
180
513
  for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
181
514
  switch (key) {
182
515
  case "id":
183
- selectors2.push(...value.map((e) => "#" + e));
516
+ selectors.push(...value.map((e) => "#" + e));
184
517
  break;
185
518
  case "class":
186
- selectors2.push(...value.map((e) => "." + e));
519
+ selectors.push(...value.map((e) => "." + e));
187
520
  break;
188
521
  case "xpath":
189
- selectors2.push(...value.map((e) => "xpath=" + e));
522
+ selectors.push(...value.map((e) => "xpath=" + e));
190
523
  break;
191
524
  case "cssSelector":
192
- selectors2.push(...value);
525
+ selectors.push(...value);
193
526
  break;
194
527
  }
195
528
  }
196
529
  }
197
530
  }
198
531
  let navigated = false;
199
- for (const viewport of ctx.webConfig.viewports) {
532
+ let renderViewports = getRenderViewports(ctx);
533
+ for (const { viewport, viewportString, fullPage } of renderViewports) {
200
534
  yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
201
535
  ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
202
536
  if (!navigated) {
@@ -204,20 +538,25 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
204
538
  navigated = true;
205
539
  ctx.log.debug(`Navigated to ${snapshot.url}`);
206
540
  }
207
- if (!viewport.height)
541
+ if (fullPage)
208
542
  yield page.evaluate(scrollToBottomAndBackToTop);
209
543
  yield page.waitForLoadState("networkidle");
210
544
  ctx.log.debug("Network idle 500ms");
211
- if (selectors2.length) {
212
- let viewportString = `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`;
545
+ if (processedOptions.element) {
546
+ let l = yield page.locator(processedOptions.element).all();
547
+ if (l.length === 0) {
548
+ throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${processedOptions.element}`);
549
+ } else if (l.length > 1) {
550
+ throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, multiple elements found for selector ${processedOptions.element}`);
551
+ }
552
+ } else if (selectors.length) {
553
+ let locators = [];
213
554
  if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString]))
214
555
  processedOptions[ignoreOrSelectBoxes][viewportString] = [];
215
- let locators = [];
216
- let boxes = [];
217
- for (const selector of selectors2) {
556
+ for (const selector of selectors) {
218
557
  let l = yield page.locator(selector).all();
219
558
  if (l.length === 0) {
220
- optionWarnings.add(`For snapshot ${snapshot.name}, no element found for selector ${selector}`);
559
+ optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
221
560
  continue;
222
561
  }
223
562
  locators.push(...l);
@@ -225,14 +564,13 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
225
564
  for (const locator of locators) {
226
565
  let bb = yield locator.boundingBox();
227
566
  if (bb)
228
- boxes.push({
567
+ processedOptions[ignoreOrSelectBoxes][viewportString].push({
229
568
  left: bb.x,
230
569
  top: bb.y,
231
570
  right: bb.x + bb.width,
232
571
  bottom: bb.y + bb.height
233
572
  });
234
573
  }
235
- processedOptions[ignoreOrSelectBoxes][viewportString].push(...boxes);
236
574
  }
237
575
  }
238
576
  yield page.close();
@@ -269,10 +607,10 @@ var ConfigSchema = {
269
607
  properties: {
270
608
  browsers: {
271
609
  type: "array",
272
- items: { type: "string", enum: ["chrome", "firefox", "edge", "safari"] },
610
+ items: { type: "string", enum: [constants_default.CHROME, constants_default.FIREFOX, constants_default.SAFARI, constants_default.EDGE] },
273
611
  uniqueItems: true,
274
612
  maxItems: 4,
275
- errorMessage: "Invalid config; allowed browsers - chrome, firefox, edge, safari"
613
+ errorMessage: `Invalid config; allowed browsers - ${constants_default.CHROME}, ${constants_default.FIREFOX}, ${constants_default.SAFARI}, ${constants_default.EDGE}`
276
614
  },
277
615
  viewports: {
278
616
  type: "array",
@@ -298,29 +636,80 @@ var ConfigSchema = {
298
636
  uniqueItems: true,
299
637
  maxItems: 5,
300
638
  errorMessage: "Invalid config; max unique viewports allowed - 5"
639
+ }
640
+ },
641
+ required: ["browsers", "viewports"],
642
+ additionalProperties: false
643
+ },
644
+ mobile: {
645
+ type: "object",
646
+ properties: {
647
+ devices: {
648
+ type: "array",
649
+ items: {
650
+ type: "string",
651
+ enum: Object.keys(constants_default.SUPPORTED_MOBILE_DEVICES),
652
+ minLength: 1,
653
+ errorMessage: {
654
+ enum: "Invalid config; unsupported mobile devices",
655
+ minLength: "Invalid config; mobile device cannot be empty"
656
+ }
657
+ },
658
+ uniqueItems: true,
659
+ maxItems: 20,
660
+ errorMessage: {
661
+ uniqueItems: "Invalid config; duplicate mobile devices",
662
+ maxItems: "Invalid config; max mobile devices allowed - 20"
663
+ }
301
664
  },
302
- waitForPageRender: {
303
- type: "number",
304
- minimum: 0,
305
- maximum: 3e5,
306
- errorMessage: "Invalid config; waitForPageRender must be > 0 and <= 300000"
307
- },
308
- waitForTimeout: {
309
- type: "number",
310
- minimum: 0,
311
- maximum: 3e4,
312
- errorMessage: "Invalid config; waitForTimeout must be > 0 and <= 30000"
313
- },
314
- enableJavaScript: {
665
+ fullPage: {
315
666
  type: "boolean",
316
- errorMessage: "Invalid config; enableJavaScript must be true/false"
667
+ errorMessage: "Invalid config; fullPage must be true/false"
668
+ },
669
+ orientation: {
670
+ type: "string",
671
+ enum: [constants_default.MOBILE_ORIENTATION_PORTRAIT, constants_default.MOBILE_ORIENTATION_LANDSCAPE],
672
+ errorMessage: `Invalid config; orientation must be ${constants_default.MOBILE_ORIENTATION_PORTRAIT}/${constants_default.MOBILE_ORIENTATION_LANDSCAPE}`
317
673
  }
318
674
  },
319
- required: ["browsers", "viewports"],
675
+ required: ["devices"],
320
676
  additionalProperties: false
677
+ },
678
+ waitForPageRender: {
679
+ type: "number",
680
+ minimum: 0,
681
+ maximum: 3e5,
682
+ errorMessage: "Invalid config; waitForPageRender must be > 0 and <= 300000"
683
+ },
684
+ waitForTimeout: {
685
+ type: "number",
686
+ minimum: 0,
687
+ maximum: 3e4,
688
+ errorMessage: "Invalid config; waitForTimeout must be > 0 and <= 30000"
689
+ },
690
+ enableJavaScript: {
691
+ type: "boolean",
692
+ errorMessage: "Invalid config; enableJavaScript must be true/false"
693
+ },
694
+ allowedHostnames: {
695
+ type: "array",
696
+ items: {
697
+ type: "string",
698
+ minLength: 1,
699
+ errorMessage: {
700
+ minLength: "Invalid config; allowed hostname cannot be empty"
701
+ }
702
+ },
703
+ uniqueItems: true,
704
+ errorMessage: {
705
+ uniqueItems: "Invalid config; duplicates in allowedHostnames"
706
+ }
321
707
  }
322
708
  },
323
- required: ["web"],
709
+ anyOf: [
710
+ { required: ["web"] },
711
+ { required: ["mobile"] }
712
+ ],
324
713
  additionalProperties: false
325
714
  };
326
715
  var WebStaticConfigSchema = {
@@ -370,32 +759,56 @@ var SnapshotSchema = {
370
759
  options: {
371
760
  type: "object",
372
761
  properties: {
762
+ element: {
763
+ type: "object",
764
+ properties: {
765
+ id: {
766
+ type: "string",
767
+ pattern: "^[^;]*$",
768
+ errorMessage: "Invalid snapshot options; element id cannot be empty or have semicolon"
769
+ },
770
+ class: {
771
+ type: "string",
772
+ pattern: "^[^;]*$",
773
+ errorMessage: "Invalid snapshot options; element class cannot be empty or have semicolon"
774
+ },
775
+ cssSelector: {
776
+ type: "string",
777
+ pattern: "^[^;]*$",
778
+ errorMessage: "Invalid snapshot options; element cssSelector cannot be empty or have semicolon"
779
+ },
780
+ xpath: {
781
+ type: "string",
782
+ errorMessage: "Invalid snapshot options; element xpath cannot be empty"
783
+ }
784
+ }
785
+ },
373
786
  ignoreDOM: {
374
787
  type: "object",
375
788
  properties: {
376
789
  id: {
377
790
  type: "array",
378
- items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; id cannot be empty or have semicolon" },
791
+ items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; ignoreDOM id cannot be empty or have semicolon" },
379
792
  uniqueItems: true,
380
- errorMessage: "Invalid snapshot options; id array must have unique items"
793
+ errorMessage: "Invalid snapshot options; ignoreDOM id array must have unique items"
381
794
  },
382
795
  class: {
383
796
  type: "array",
384
- items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; class cannot be empty or have semicolon" },
797
+ items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; ignoreDOM class cannot be empty or have semicolon" },
385
798
  uniqueItems: true,
386
- errorMessage: "Invalid snapshot options; class array must have unique items"
799
+ errorMessage: "Invalid snapshot options; ignoreDOM class array must have unique items"
387
800
  },
388
801
  cssSelector: {
389
802
  type: "array",
390
- items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; cssSelector cannot be empty or have semicolon" },
803
+ items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; ignoreDOM cssSelector cannot be empty or have semicolon" },
391
804
  uniqueItems: true,
392
- errorMessage: "Invalid snapshot options; cssSelector array must have unique items"
805
+ errorMessage: "Invalid snapshot options; ignoreDOM cssSelector array must have unique items"
393
806
  },
394
807
  xpath: {
395
808
  type: "array",
396
809
  items: { type: "string", minLength: 1 },
397
810
  uniqueItems: true,
398
- errorMessage: "Invalid snapshot options; xpath array must have unique and non-empty items"
811
+ errorMessage: "Invalid snapshot options; ignoreDOM xpath array must have unique and non-empty items"
399
812
  }
400
813
  }
401
814
  },
@@ -404,27 +817,27 @@ var SnapshotSchema = {
404
817
  properties: {
405
818
  id: {
406
819
  type: "array",
407
- items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; id cannot be empty or have semicolon" },
820
+ items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; selectDOM id cannot be empty or have semicolon" },
408
821
  uniqueItems: true,
409
- errorMessage: "Invalid snapshot options; id array must have unique items"
822
+ errorMessage: "Invalid snapshot options; selectDOM id array must have unique items"
410
823
  },
411
824
  class: {
412
825
  type: "array",
413
- items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; class cannot be empty or have semicolon" },
826
+ items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; selectDOM class cannot be empty or have semicolon" },
414
827
  uniqueItems: true,
415
- errorMessage: "Invalid snapshot options; class array must have unique items"
828
+ errorMessage: "Invalid snapshot options; selectDOM class array must have unique items"
416
829
  },
417
830
  cssSelector: {
418
831
  type: "array",
419
- items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; cssSelector cannot be empty or have semicolon" },
832
+ items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; selectDOM cssSelector cannot be empty or have semicolon" },
420
833
  uniqueItems: true,
421
- errorMessage: "Invalid snapshot options; cssSelector array must have unique items"
834
+ errorMessage: "Invalid snapshot options; selectDOM cssSelector array must have unique items"
422
835
  },
423
836
  xpath: {
424
837
  type: "array",
425
838
  items: { type: "string", minLength: 1 },
426
839
  uniqueItems: true,
427
- errorMessage: "Invalid snapshot options; xpath array must have unique and non-empty items"
840
+ errorMessage: "Invalid snapshot options; selectDOM xpath array must have unique and non-empty items"
428
841
  }
429
842
  }
430
843
  }
@@ -444,7 +857,7 @@ var validateSnapshot = ajv.compile(SnapshotSchema);
444
857
  var server_default = (ctx) => __async(void 0, null, function* () {
445
858
  const server = fastify__default.default({ logger: false, bodyLimit: 1e7 });
446
859
  const opts = {};
447
- const SMARTUI_DOM = fs2.readFileSync(path2__default.default.resolve(__dirname, "dom-serializer.js"), "utf-8");
860
+ const SMARTUI_DOM = fs4.readFileSync(path2__default.default.resolve(__dirname, "dom-serializer.js"), "utf-8");
448
861
  server.get("/healthcheck", opts, (_, reply) => {
449
862
  reply.code(200).send({ cliVersion: ctx.cliVersion });
450
863
  });
@@ -477,13 +890,15 @@ var env_default = () => {
477
890
  PROJECT_TOKEN = "",
478
891
  SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
479
892
  LT_SDK_LOG_LEVEL,
480
- LT_SDK_DEBUG
893
+ LT_SDK_DEBUG,
894
+ GITHUB_ACTIONS
481
895
  } = process.env;
482
896
  return {
483
897
  PROJECT_TOKEN,
484
898
  SMARTUI_CLIENT_API_URL,
485
899
  LT_SDK_LOG_LEVEL,
486
- LT_SDK_DEBUG
900
+ LT_SDK_DEBUG,
901
+ GITHUB_ACTIONS
487
902
  };
488
903
  };
489
904
  var logContext = { task: "smartui-cli" };
@@ -504,13 +919,13 @@ var logger = winston.createLogger({
504
919
  let message = typeof info.message === "object" ? JSON.stringify(info.message) : info.message;
505
920
  switch (info.level) {
506
921
  case "debug":
507
- message = chalk__default.default.blue(message);
922
+ message = chalk8__default.default.blue(message);
508
923
  break;
509
924
  case "warn":
510
- message = chalk__default.default.yellow(message);
925
+ message = chalk8__default.default.yellow(message);
511
926
  break;
512
927
  case "error":
513
- message = chalk__default.default.red(message);
928
+ message = chalk8__default.default.red(message);
514
929
  break;
515
930
  }
516
931
  return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
@@ -529,11 +944,11 @@ var startServer_default = (ctx) => {
529
944
  updateLogContext({ task: "startServer" });
530
945
  try {
531
946
  ctx2.server = yield server_default(ctx2);
532
- task.output = chalk__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
947
+ task.output = chalk8__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
533
948
  task.title = "SmartUI started";
534
949
  } catch (error) {
535
950
  ctx2.log.debug(error);
536
- task.output = chalk__default.default.gray(error.message);
951
+ task.output = chalk8__default.default.gray(error.message);
537
952
  throw new Error("SmartUI server setup failed");
538
953
  }
539
954
  }),
@@ -547,80 +962,20 @@ var auth_default = (ctx) => {
547
962
  updateLogContext({ task: "auth" });
548
963
  try {
549
964
  yield ctx2.client.auth(ctx2.log);
550
- task.output = chalk__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
965
+ task.output = chalk8__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
551
966
  task.title = "Authenticated with SmartUI";
552
967
  } catch (error) {
553
968
  ctx2.log.debug(error);
554
- task.output = chalk__default.default.gray(error.message);
969
+ task.output = chalk8__default.default.gray(error.message);
555
970
  throw new Error("Authentication failed");
556
971
  }
557
972
  }),
558
973
  rendererOptions: { persistentOutput: true }
559
974
  };
560
975
  };
561
- var DEFAULT_WEB_STATIC_CONFIG = [
562
- {
563
- "name": "lambdatest-home-page",
564
- "url": "https://www.lambdatest.com",
565
- "waitForTimeout": 1e3
566
- },
567
- {
568
- "name": "example-page",
569
- "url": "https://example.com/"
570
- }
571
- ];
572
- var DEFAULT_CONFIG = {
573
- web: {
574
- browsers: [
575
- "chrome",
576
- "firefox",
577
- "safari",
578
- "edge"
579
- ],
580
- viewports: [
581
- [1920],
582
- [1366],
583
- [360]
584
- ],
585
- waitForTimeout: 1e3,
586
- enableJavaScript: false
587
- }
588
- };
589
- function createConfig(filepath) {
590
- filepath = filepath || ".smartui.json";
591
- let filetype = path2__default.default.extname(filepath);
592
- if (filetype != ".json") {
593
- console.log("Error: Config file must have .json extension");
594
- return;
595
- }
596
- if (fs2__default.default.existsSync(filepath)) {
597
- console.log(`Error: SmartUI Config already exists: ${filepath}`);
598
- console.log(`To create a new file, please specify the file name like: 'smartui config:create .smartui-config.json'`);
599
- return;
600
- }
601
- fs2__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
602
- fs2__default.default.writeFileSync(filepath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
603
- console.log(`Created SmartUI Config: ${filepath}`);
604
- }
605
- function createWebStaticConfig(filepath) {
606
- filepath = filepath || "url.json";
607
- let filetype = path2__default.default.extname(filepath);
608
- if (filetype != ".json") {
609
- console.log("Error: Config file must have .json extension");
610
- return;
611
- }
612
- if (fs2__default.default.existsSync(filepath)) {
613
- console.log(`Error: web-static config already exists: ${filepath}`);
614
- console.log(`To create a new file, please specify the file name like: 'smartui config:create-web-static links.json'`);
615
- return;
616
- }
617
- fs2__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
618
- fs2__default.default.writeFileSync(filepath, JSON.stringify(DEFAULT_WEB_STATIC_CONFIG, null, 2) + "\n");
619
- console.log(`Created web-static config: ${filepath}`);
620
- }
621
976
 
622
977
  // package.json
623
- var version = "2.0.9";
978
+ var version = "3.0.1";
624
979
  var package_default = {
625
980
  name: "@lambdatest/smartui-cli",
626
981
  version,
@@ -686,13 +1041,14 @@ var httpClient = class {
686
1041
  })}`);
687
1042
  return resp.data;
688
1043
  }).catch((error) => {
1044
+ var _a;
689
1045
  if (error.response) {
690
1046
  log.debug(`http response: ${JSON.stringify({
691
1047
  status: error.response.status,
692
1048
  headers: error.response.headers,
693
1049
  body: error.response.data
694
1050
  })}`);
695
- throw new Error(JSON.stringify(error.response.data));
1051
+ throw new Error((_a = error.response.data.error) == null ? void 0 : _a.message);
696
1052
  }
697
1053
  if (error.request) {
698
1054
  log.debug(`http request failed: ${error.toJSON()}`);
@@ -709,24 +1065,13 @@ var httpClient = class {
709
1065
  method: "GET"
710
1066
  }, log);
711
1067
  }
712
- createBuild({ branch, commitId, commitAuthor, commitMessage, githubURL }, config, log) {
1068
+ createBuild(git, config, log) {
713
1069
  return this.request({
714
1070
  url: "/build",
715
1071
  method: "POST",
716
1072
  data: {
717
- git: {
718
- branch,
719
- commitId,
720
- commitAuthor,
721
- commitMessage,
722
- githubURL
723
- },
724
- config: {
725
- browsers: config.browsers,
726
- resolutions: config.viewports,
727
- waitForPageRender: config.waitForPageRender,
728
- waitForTimeout: config.waitForTimeout
729
- }
1073
+ git,
1074
+ config
730
1075
  }
731
1076
  }, log);
732
1077
  }
@@ -754,28 +1099,27 @@ var httpClient = class {
754
1099
  }
755
1100
  }, log);
756
1101
  }
757
- uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, completed) {
758
- const file = fs2__default.default.readFileSync(ssPath);
1102
+ uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log) {
1103
+ browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
1104
+ const file = fs4__default.default.readFileSync(ssPath);
759
1105
  const form = new FormData__default.default();
760
- form.append("screenshots", file, { filename: `${ssName}.png`, contentType: "image/png" });
1106
+ form.append("screenshot", file, { filename: `${ssName}.png`, contentType: "image/png" });
761
1107
  form.append("browser", browserName);
762
- form.append("resolution", viewport);
1108
+ form.append("viewport", viewport);
763
1109
  form.append("buildId", buildId);
764
1110
  form.append("buildName", buildName);
765
1111
  form.append("screenshotName", ssName);
766
1112
  form.append("baseline", baseline.toString());
767
- form.append("completed", completed.toString());
768
1113
  return this.axiosInstance.request({
769
1114
  url: `/screenshot`,
770
1115
  method: "POST",
771
1116
  headers: form.getHeaders(),
772
1117
  data: form
773
1118
  }).then(() => {
774
- if (completed)
775
- delDir("screenshots");
1119
+ log.debug(`${ssName} for ${browserName} ${viewport} uploaded successfully`);
776
1120
  }).catch((error) => {
777
1121
  if (error.response) {
778
- throw new Error(JSON.stringify(error.response.data));
1122
+ throw new Error(error.response.data.error.message);
779
1123
  }
780
1124
  if (error.request) {
781
1125
  throw new Error(error.toJSON().message);
@@ -796,35 +1140,49 @@ var httpClient = class {
796
1140
  }
797
1141
  };
798
1142
  var ctx_default = (options) => {
1143
+ var _a, _b;
799
1144
  let env = env_default();
800
- let viewports = [];
801
- let config = DEFAULT_CONFIG;
1145
+ let webConfig;
1146
+ let mobileConfig;
1147
+ let config = constants_default.DEFAULT_CONFIG;
802
1148
  try {
803
1149
  if (options.config) {
804
- config = JSON.parse(fs2__default.default.readFileSync(options.config, "utf-8"));
805
- if (config.web.resolutions) {
1150
+ config = JSON.parse(fs4__default.default.readFileSync(options.config, "utf-8"));
1151
+ if ((_a = config.web) == null ? void 0 : _a.resolutions) {
806
1152
  config.web.viewports = config.web.resolutions;
807
1153
  delete config.web.resolutions;
808
1154
  }
1155
+ if (!validateConfig(config)) {
1156
+ throw new Error(validateConfig.errors[0].message);
1157
+ }
809
1158
  }
810
- if (!validateConfig(config))
811
- throw new Error(validateConfig.errors[0].message);
812
1159
  } catch (error) {
813
1160
  console.log(`[smartui] Error: ${error.message}`);
814
1161
  process.exit();
815
1162
  }
816
- for (let viewport of config.web.viewports)
817
- viewports.push({ width: viewport[0], height: viewport[1] || 0 });
1163
+ if (config.web) {
1164
+ webConfig = { browsers: config.web.browsers, viewports: [] };
1165
+ for (let viewport of (_b = config.web) == null ? void 0 : _b.viewports)
1166
+ webConfig.viewports.push({ width: viewport[0], height: viewport[1] || 0 });
1167
+ }
1168
+ if (config.mobile) {
1169
+ mobileConfig = {
1170
+ devices: config.mobile.devices,
1171
+ fullPage: config.mobile.fullPage || true,
1172
+ orientation: config.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT
1173
+ };
1174
+ }
818
1175
  return {
819
1176
  env,
820
1177
  log: logger_default,
821
1178
  client: new httpClient(env),
822
- webConfig: {
823
- browsers: config.web.browsers,
824
- viewports,
825
- waitForPageRender: config.web.waitForPageRender || 0,
826
- waitForTimeout: config.web.waitForTimeout || 0,
827
- enableJavaScript: config.web.enableJavaScript || false
1179
+ config: {
1180
+ web: webConfig,
1181
+ mobile: mobileConfig,
1182
+ waitForPageRender: config.waitForPageRender || 0,
1183
+ waitForTimeout: config.waitForTimeout || 0,
1184
+ enableJavaScript: config.enableJavaScript || false,
1185
+ allowedHostnames: config.allowedHostnames || []
828
1186
  },
829
1187
  webStaticConfig: [],
830
1188
  git: {
@@ -838,10 +1196,12 @@ var ctx_default = (options) => {
838
1196
  id: "",
839
1197
  name: "",
840
1198
  baseline: false,
841
- url: "",
842
- projectId: ""
1199
+ url: ""
843
1200
  },
844
1201
  args: {},
1202
+ options: {
1203
+ parallel: options.parallel ? true : false
1204
+ },
845
1205
  cliVersion: version,
846
1206
  totalSnapshots: -1
847
1207
  };
@@ -866,7 +1226,7 @@ function isGitRepo() {
866
1226
  return false;
867
1227
  }
868
1228
  }
869
- var git_default = () => {
1229
+ var git_default = (ctx) => {
870
1230
  const splitCharacter = "<##>";
871
1231
  const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
872
1232
  const command3 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
@@ -878,7 +1238,8 @@ var git_default = () => {
878
1238
  branch,
879
1239
  commitId: res[0] || "",
880
1240
  commitMessage: res[2] || "",
881
- commitAuthor: res[7] || ""
1241
+ commitAuthor: res[7] || "",
1242
+ githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${res[1]}` : ""
882
1243
  };
883
1244
  };
884
1245
  var getGitInfo_default = (ctx) => {
@@ -889,12 +1250,12 @@ var getGitInfo_default = (ctx) => {
889
1250
  },
890
1251
  task: (ctx2, task) => __async(void 0, null, function* () {
891
1252
  try {
892
- ctx2.git = git_default();
893
- task.output = chalk__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
1253
+ ctx2.git = git_default(ctx2);
1254
+ task.output = chalk8__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
894
1255
  task.title = "Fetched git information";
895
1256
  } catch (error) {
896
1257
  ctx2.log.debug(error);
897
- task.output = chalk__default.default.gray(`${error.message}`);
1258
+ task.output = chalk8__default.default.gray(`${error.message}`);
898
1259
  throw new Error("Error fetching git repo details");
899
1260
  }
900
1261
  }),
@@ -907,19 +1268,18 @@ var createBuild_default = (ctx) => {
907
1268
  task: (ctx2, task) => __async(void 0, null, function* () {
908
1269
  updateLogContext({ task: "createBuild" });
909
1270
  try {
910
- let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.webConfig, ctx2.log);
1271
+ let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log);
911
1272
  ctx2.build = {
912
1273
  id: resp.data.buildId,
913
1274
  name: resp.data.buildName,
914
- url: resp.buildURL,
915
- baseline: resp.data.baseline || false,
916
- projectId: resp.data.projectId
1275
+ url: resp.data.buildURL,
1276
+ baseline: resp.data.baseline
917
1277
  };
918
- task.output = chalk__default.default.gray(`build id: ${resp.data.buildId}`);
1278
+ task.output = chalk8__default.default.gray(`build id: ${resp.data.buildId}`);
919
1279
  task.title = "SmartUI build created";
920
1280
  } catch (error) {
921
1281
  ctx2.log.debug(error);
922
- task.output = chalk__default.default.gray(JSON.parse(error.message).message);
1282
+ task.output = chalk8__default.default.gray(error.message);
923
1283
  throw new Error("SmartUI build creation failed");
924
1284
  }
925
1285
  }),
@@ -938,13 +1298,13 @@ var exec_default = (ctx) => {
938
1298
  let totalOutput = "";
939
1299
  const output = listr2.createWritable((chunk) => {
940
1300
  totalOutput += chunk;
941
- task.output = chalk__default.default.gray(totalOutput);
1301
+ task.output = chalk8__default.default.gray(totalOutput);
942
1302
  });
943
1303
  (_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
944
1304
  (_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
945
1305
  childProcess.on("error", (error) => {
946
1306
  var _a3;
947
- task.output = chalk__default.default.gray(`error: ${error.message}`);
1307
+ task.output = chalk8__default.default.gray(`error: ${error.message}`);
948
1308
  throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
949
1309
  });
950
1310
  childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
@@ -970,11 +1330,11 @@ var finalizeBuild_default = (ctx) => {
970
1330
  try {
971
1331
  yield new Promise((resolve) => setTimeout(resolve, 2e3));
972
1332
  yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
973
- task.output = chalk__default.default.gray(`build url: ${ctx2.build.url}`);
1333
+ task.output = chalk8__default.default.gray(`build url: ${ctx2.build.url}`);
974
1334
  task.title = "Finalized build";
975
1335
  } catch (error) {
976
1336
  ctx2.log.debug(error);
977
- task.output = chalk__default.default.gray(error.message);
1337
+ task.output = chalk8__default.default.gray(error.message);
978
1338
  throw new Error("Finalize build failed");
979
1339
  }
980
1340
  }),
@@ -1025,6 +1385,40 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
1025
1385
  });
1026
1386
  });
1027
1387
  var exec_default2 = command;
1388
+ function createConfig(filepath) {
1389
+ filepath = filepath || ".smartui.json";
1390
+ let filetype = path2__default.default.extname(filepath);
1391
+ if (filetype != ".json") {
1392
+ console.log("Error: Config file must have .json extension");
1393
+ return;
1394
+ }
1395
+ if (fs4__default.default.existsSync(filepath)) {
1396
+ console.log(`Error: SmartUI Config already exists: ${filepath}`);
1397
+ console.log(`To create a new file, please specify the file name like: 'smartui config:create .smartui-config.json'`);
1398
+ return;
1399
+ }
1400
+ fs4__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
1401
+ fs4__default.default.writeFileSync(filepath, JSON.stringify(constants_default.DEFAULT_CONFIG, null, 2) + "\n");
1402
+ console.log(`Created SmartUI Config: ${filepath}`);
1403
+ }
1404
+ function createWebStaticConfig(filepath) {
1405
+ filepath = filepath || "url.json";
1406
+ let filetype = path2__default.default.extname(filepath);
1407
+ if (filetype != ".json") {
1408
+ console.log("Error: Config file must have .json extension");
1409
+ return;
1410
+ }
1411
+ if (fs4__default.default.existsSync(filepath)) {
1412
+ console.log(`Error: web-static config already exists: ${filepath}`);
1413
+ console.log(`To create a new file, please specify the file name like: 'smartui config:create-web-static links.json'`);
1414
+ return;
1415
+ }
1416
+ fs4__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
1417
+ fs4__default.default.writeFileSync(filepath, JSON.stringify(constants_default.DEFAULT_WEB_STATIC_CONFIG, null, 2) + "\n");
1418
+ console.log(`Created web-static config: ${filepath}`);
1419
+ }
1420
+
1421
+ // src/commander/config.ts
1028
1422
  var configWeb = new commander.Command();
1029
1423
  var configStatic = new commander.Command();
1030
1424
  configWeb.name("config:create").description("Create SmartUI config file").argument("[filepath]", "Optional config filepath").action(function(filepath, options) {
@@ -1037,73 +1431,107 @@ configStatic.name("config:create-web-static").description("Create Web Static con
1037
1431
  createWebStaticConfig(filepath);
1038
1432
  });
1039
1433
  });
1040
- var BROWSER_CHROME = "chrome";
1041
- var BROWSER_SAFARI = "safari";
1042
- var BROWSER_FIREFOX = "firefox";
1043
- var BROWSER_EDGE = "edge";
1044
- var EDGE_CHANNEL = "msedge";
1045
- var PW_WEBKIT = "webkit";
1046
- var MIN_VIEWPORT_HEIGHT2 = 1080;
1047
- function captureScreenshots(ctx, screenshots) {
1434
+ function captureScreenshotsForConfig(_0, _1, _2, _3, _4) {
1435
+ return __async(this, arguments, function* (ctx, browsers, { name, url, waitForTimeout }, browserName, renderViewports) {
1436
+ let pageOptions = { waitUntil: process.env.SMARTUI_PAGE_WAIT_UNTIL_EVENT || "load" };
1437
+ let ssId = name.toLowerCase().replace(/\s/g, "_");
1438
+ let context;
1439
+ let page;
1440
+ try {
1441
+ const browser = browsers[browserName];
1442
+ context = yield browser == null ? void 0 : browser.newContext();
1443
+ page = yield context == null ? void 0 : context.newPage();
1444
+ yield page == null ? void 0 : page.goto(url.trim(), pageOptions);
1445
+ for (let { viewport, viewportString, fullPage } of renderViewports) {
1446
+ let ssPath = `screenshots/${ssId}/${`${browserName}-${viewport.width}x${viewport.height}`}-${ssId}.png`;
1447
+ yield page == null ? void 0 : page.setViewportSize({ width: viewport.width, height: viewport.height || constants_default.MIN_VIEWPORT_HEIGHT });
1448
+ if (fullPage)
1449
+ yield page == null ? void 0 : page.evaluate(scrollToBottomAndBackToTop);
1450
+ yield page == null ? void 0 : page.waitForTimeout(waitForTimeout || 0);
1451
+ yield page == null ? void 0 : page.screenshot({ path: ssPath, fullPage });
1452
+ yield ctx.client.uploadScreenshot(ctx.build, ssPath, name, browserName, viewportString, ctx.log);
1453
+ }
1454
+ } catch (error) {
1455
+ throw new Error(`captureScreenshotsForConfig failed for browser ${browserName}; error: ${error}`);
1456
+ } finally {
1457
+ yield page == null ? void 0 : page.close();
1458
+ yield context == null ? void 0 : context.close();
1459
+ }
1460
+ });
1461
+ }
1462
+ function captureScreenshotsAsync(ctx, staticConfig, browsers) {
1463
+ return __async(this, null, function* () {
1464
+ let capturePromises = [];
1465
+ if (ctx.config.web) {
1466
+ for (let browserName of ctx.config.web.browsers) {
1467
+ let webRenderViewports = getWebRenderViewports(ctx);
1468
+ capturePromises.push(captureScreenshotsForConfig(ctx, browsers, staticConfig, browserName, webRenderViewports));
1469
+ }
1470
+ }
1471
+ if (ctx.config.mobile) {
1472
+ let mobileRenderViewports = getMobileRenderViewports(ctx);
1473
+ if (mobileRenderViewports[constants_default.MOBILE_OS_IOS].length) {
1474
+ capturePromises.push(captureScreenshotsForConfig(ctx, browsers, staticConfig, constants_default.SAFARI, mobileRenderViewports[constants_default.MOBILE_OS_IOS]));
1475
+ }
1476
+ if (mobileRenderViewports[constants_default.MOBILE_OS_ANDROID].length) {
1477
+ capturePromises.push(captureScreenshotsForConfig(ctx, browsers, staticConfig, constants_default.CHROME, mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]));
1478
+ }
1479
+ }
1480
+ return Promise.all(capturePromises);
1481
+ });
1482
+ }
1483
+ function captureScreenshotsSync(ctx, staticConfig, browsers) {
1484
+ return __async(this, null, function* () {
1485
+ if (ctx.config.web) {
1486
+ for (let browserName of ctx.config.web.browsers) {
1487
+ let webRenderViewports = getWebRenderViewports(ctx);
1488
+ yield captureScreenshotsForConfig(ctx, browsers, staticConfig, browserName, webRenderViewports);
1489
+ }
1490
+ }
1491
+ if (ctx.config.mobile) {
1492
+ let mobileRenderViewports = getMobileRenderViewports(ctx);
1493
+ if (mobileRenderViewports[constants_default.MOBILE_OS_IOS].length) {
1494
+ yield captureScreenshotsForConfig(ctx, browsers, staticConfig, constants_default.SAFARI, mobileRenderViewports[constants_default.MOBILE_OS_IOS]);
1495
+ }
1496
+ if (mobileRenderViewports[constants_default.MOBILE_OS_ANDROID].length) {
1497
+ yield captureScreenshotsForConfig(ctx, browsers, staticConfig, constants_default.CHROME, mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]);
1498
+ }
1499
+ }
1500
+ });
1501
+ }
1502
+ function captureScreenshots(ctx) {
1048
1503
  return __async(this, null, function* () {
1049
- var _a;
1050
1504
  delDir("screenshots");
1051
- let totalBrowsers = ctx.webConfig.browsers.length;
1052
- let totalViewports = ctx.webConfig.viewports.length;
1053
- let totalScreenshots = screenshots.length;
1505
+ let browsers = {};
1054
1506
  let capturedScreenshots = 0;
1055
- for (let i = 0; i < totalBrowsers; i++) {
1056
- let browserName = (_a = ctx.webConfig.browsers[i]) == null ? void 0 : _a.toLowerCase();
1057
- let browser;
1058
- let launchOptions = { headless: true };
1059
- let pageOptions = { waitUntil: process.env.SMARTUI_PAGE_WAIT_UNTIL_EVENT || "load" };
1507
+ let output = "";
1508
+ try {
1509
+ browsers = yield launchBrowsers(ctx);
1510
+ } catch (error) {
1511
+ yield closeBrowsers(browsers);
1512
+ ctx.log.debug(error);
1513
+ throw new Error(`Failed launching browsers`);
1514
+ }
1515
+ for (let staticConfig of ctx.webStaticConfig) {
1060
1516
  try {
1061
- switch (browserName) {
1062
- case BROWSER_CHROME:
1063
- browser = yield test.chromium.launch(launchOptions);
1064
- break;
1065
- case BROWSER_SAFARI:
1066
- browser = yield test.webkit.launch(launchOptions);
1067
- break;
1068
- case BROWSER_FIREFOX:
1069
- browser = yield test.firefox.launch(launchOptions);
1070
- break;
1071
- case BROWSER_EDGE:
1072
- launchOptions.channel = EDGE_CHANNEL;
1073
- browser = yield test.chromium.launch(launchOptions);
1074
- break;
1075
- }
1076
- const context = yield browser.newContext();
1077
- for (let j = 0; j < totalScreenshots; j++) {
1078
- let screenshot = screenshots[j];
1079
- let screenshotId = screenshot.name.toLowerCase().replace(/\s/g, "-");
1080
- const page = yield context.newPage();
1081
- yield page.goto(screenshot.url.trim(), pageOptions);
1082
- for (let k = 0; k < totalViewports; k++) {
1083
- let { width, height } = ctx.webConfig.viewports[k];
1084
- let ssName = `${browserName}-${width}x${height}-${screenshotId}.png`;
1085
- let ssPath = `screenshots/${screenshotId}/${ssName}.png`;
1086
- yield page.setViewportSize({ width, height: height || MIN_VIEWPORT_HEIGHT2 });
1087
- if (height === 0)
1088
- yield page.evaluate(scrollToBottomAndBackToTop);
1089
- yield page.waitForTimeout(screenshot.waitForTimeout || 0);
1090
- yield page.screenshot({ path: ssPath, fullPage: height ? false : true });
1091
- let completed = i == totalBrowsers - 1 && j == totalScreenshots - 1 && k == totalViewports - 1 ? true : false;
1092
- browserName = browserName === BROWSER_SAFARI ? PW_WEBKIT : browserName;
1093
- ctx.client.uploadScreenshot(ctx.build, ssPath, screenshot.name, browserName, `${width}x${height}`, completed);
1094
- capturedScreenshots++;
1095
- ctx.task.output = chalk__default.default.gray(`screenshots captured: ${capturedScreenshots}/${totalBrowsers * totalViewports * totalScreenshots}`);
1096
- }
1097
- yield page.close();
1098
- }
1099
- yield browser.close();
1517
+ if (ctx.options.parallel)
1518
+ yield captureScreenshotsAsync(ctx, staticConfig, browsers);
1519
+ else
1520
+ yield captureScreenshotsSync(ctx, staticConfig, browsers);
1521
+ output += `${chalk8__default.default.gray(staticConfig.name)} ${chalk8__default.default.green("\u2713")}
1522
+ `;
1523
+ ctx.task.output = output;
1524
+ capturedScreenshots++;
1100
1525
  } catch (error) {
1101
- if (browser)
1102
- yield browser.close();
1103
- throw error;
1526
+ ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
1527
+ output += `${chalk8__default.default.gray(staticConfig.name)} ${chalk8__default.default.red("\u2717")}
1528
+ `;
1529
+ ctx.task.output = output;
1104
1530
  }
1105
1531
  }
1106
- return totalBrowsers * totalViewports * totalScreenshots;
1532
+ yield closeBrowsers(browsers);
1533
+ delDir("screenshots");
1534
+ return { capturedScreenshots, output };
1107
1535
  });
1108
1536
  }
1109
1537
  var captureScreenshots_default = (ctx) => {
@@ -1112,11 +1540,15 @@ var captureScreenshots_default = (ctx) => {
1112
1540
  task: (ctx2, task) => __async(void 0, null, function* () {
1113
1541
  try {
1114
1542
  ctx2.task = task;
1115
- let totalScreenshots = yield captureScreenshots(ctx2, ctx2.webStaticConfig);
1543
+ updateLogContext({ task: "capture" });
1544
+ let { capturedScreenshots, output } = yield captureScreenshots(ctx2);
1545
+ if (capturedScreenshots != ctx2.webStaticConfig.length) {
1546
+ throw new Error(output);
1547
+ }
1116
1548
  task.title = "Screenshots captured successfully";
1117
- task.output = chalk__default.default.gray(`total screenshots: ${totalScreenshots}`);
1118
1549
  } catch (error) {
1119
- task.output = chalk__default.default.gray(`${error.message}`);
1550
+ ctx2.log.debug(error);
1551
+ task.output = chalk8__default.default.gray(`${error.message}`);
1120
1552
  throw new Error("Capturing screenshots failed");
1121
1553
  }
1122
1554
  }),
@@ -1127,15 +1559,15 @@ var captureScreenshots_default = (ctx) => {
1127
1559
 
1128
1560
  // src/commander/capture.ts
1129
1561
  var command2 = new commander.Command();
1130
- command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").action(function(file, _, command3) {
1562
+ 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, _, command3) {
1131
1563
  return __async(this, null, function* () {
1132
1564
  let ctx = ctx_default(command3.optsWithGlobals());
1133
- if (!fs2__default.default.existsSync(file)) {
1565
+ if (!fs4__default.default.existsSync(file)) {
1134
1566
  console.log(`Error: Web Static Config file ${file} not found.`);
1135
1567
  return;
1136
1568
  }
1137
1569
  try {
1138
- ctx.webStaticConfig = JSON.parse(fs2__default.default.readFileSync(file, "utf8"));
1570
+ ctx.webStaticConfig = JSON.parse(fs4__default.default.readFileSync(file, "utf8"));
1139
1571
  if (!validateWebStaticConfig(ctx.webStaticConfig))
1140
1572
  throw new Error(validateWebStaticConfig.errors[0].message);
1141
1573
  } catch (error) {
@@ -1186,13 +1618,13 @@ LambdaTest SmartUI CLI v${package_default.version}`);
1186
1618
  log.warn(`This version is deprecated. A new version ${latestVersion} is available!
1187
1619
  `);
1188
1620
  else if (package_default.version !== latestVersion)
1189
- log.info(chalk__default.default.gray(`A new version ${latestVersion} is available!
1621
+ log.info(chalk8__default.default.gray(`A new version ${latestVersion} is available!
1190
1622
  `));
1191
1623
  else
1192
- log.info(chalk__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
1624
+ log.info(chalk8__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
1193
1625
  } catch (error) {
1194
1626
  log.debug(error);
1195
- log.info(chalk__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
1627
+ log.info(chalk8__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
1196
1628
  }
1197
1629
  commander_default.parse();
1198
1630
  });