@lambdatest/smartui-cli 3.0.11 → 3.0.12
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.
- package/dist/index.cjs +461 -367
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var which = require('which');
|
|
6
6
|
var listr2 = require('listr2');
|
|
7
|
-
var
|
|
7
|
+
var chalk7 = require('chalk');
|
|
8
8
|
var path2 = require('path');
|
|
9
9
|
var fastify = require('fastify');
|
|
10
10
|
var fs5 = require('fs');
|
|
11
|
-
var test = require('@playwright/test');
|
|
12
11
|
var Ajv = require('ajv');
|
|
13
12
|
var addErrors = require('ajv-errors');
|
|
14
13
|
var winston = require('winston');
|
|
@@ -16,11 +15,12 @@ var FormData = require('form-data');
|
|
|
16
15
|
var axios = require('axios');
|
|
17
16
|
var child_process = require('child_process');
|
|
18
17
|
var spawn = require('cross-spawn');
|
|
18
|
+
var test = require('@playwright/test');
|
|
19
19
|
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
21
|
|
|
22
22
|
var which__default = /*#__PURE__*/_interopDefault(which);
|
|
23
|
-
var
|
|
23
|
+
var chalk7__default = /*#__PURE__*/_interopDefault(chalk7);
|
|
24
24
|
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
25
25
|
var fastify__default = /*#__PURE__*/_interopDefault(fastify);
|
|
26
26
|
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
@@ -315,327 +315,7 @@ var constants_default = {
|
|
|
315
315
|
}
|
|
316
316
|
};
|
|
317
317
|
|
|
318
|
-
// src/lib/
|
|
319
|
-
function delDir(dir) {
|
|
320
|
-
if (fs5__default.default.existsSync(dir)) {
|
|
321
|
-
fs5__default.default.rmSync(dir, { recursive: true });
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function scrollToBottomAndBackToTop({
|
|
325
|
-
frequency = 100,
|
|
326
|
-
timing = 8,
|
|
327
|
-
remoteWindow = window
|
|
328
|
-
} = {}) {
|
|
329
|
-
return new Promise((resolve) => {
|
|
330
|
-
let scrolls = 1;
|
|
331
|
-
let scrollLength = remoteWindow.document.body.scrollHeight / frequency;
|
|
332
|
-
(function scroll() {
|
|
333
|
-
let scrollBy = scrollLength * scrolls;
|
|
334
|
-
remoteWindow.setTimeout(() => {
|
|
335
|
-
remoteWindow.scrollTo(0, scrollBy);
|
|
336
|
-
if (scrolls < frequency) {
|
|
337
|
-
scrolls += 1;
|
|
338
|
-
scroll();
|
|
339
|
-
}
|
|
340
|
-
if (scrolls === frequency) {
|
|
341
|
-
remoteWindow.setTimeout(() => {
|
|
342
|
-
remoteWindow.scrollTo(0, 0);
|
|
343
|
-
resolve();
|
|
344
|
-
}, timing);
|
|
345
|
-
}
|
|
346
|
-
}, timing);
|
|
347
|
-
})();
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
function launchBrowsers(ctx) {
|
|
351
|
-
return __async(this, null, function* () {
|
|
352
|
-
let browsers = {};
|
|
353
|
-
let launchOptions = { headless: true };
|
|
354
|
-
if (ctx.config.web) {
|
|
355
|
-
for (const browser of ctx.config.web.browsers) {
|
|
356
|
-
switch (browser) {
|
|
357
|
-
case constants_default.CHROME:
|
|
358
|
-
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
359
|
-
break;
|
|
360
|
-
case constants_default.SAFARI:
|
|
361
|
-
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
362
|
-
break;
|
|
363
|
-
case constants_default.FIREFOX:
|
|
364
|
-
browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
|
|
365
|
-
break;
|
|
366
|
-
case constants_default.EDGE:
|
|
367
|
-
browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (ctx.config.mobile) {
|
|
373
|
-
for (const device of ctx.config.mobile.devices) {
|
|
374
|
-
if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
|
|
375
|
-
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
376
|
-
else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
|
|
377
|
-
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return browsers;
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
function closeBrowsers(browsers) {
|
|
384
|
-
return __async(this, null, function* () {
|
|
385
|
-
var _a;
|
|
386
|
-
for (const browserName of Object.keys(browsers))
|
|
387
|
-
yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
function getWebRenderViewports(ctx) {
|
|
391
|
-
let webRenderViewports = [];
|
|
392
|
-
if (ctx.config.web) {
|
|
393
|
-
for (const viewport of ctx.config.web.viewports) {
|
|
394
|
-
webRenderViewports.push({
|
|
395
|
-
viewport,
|
|
396
|
-
viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
|
|
397
|
-
fullPage: viewport.height ? false : true,
|
|
398
|
-
device: false
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return webRenderViewports;
|
|
403
|
-
}
|
|
404
|
-
function getMobileRenderViewports(ctx) {
|
|
405
|
-
var _a;
|
|
406
|
-
let mobileRenderViewports = {};
|
|
407
|
-
mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
|
|
408
|
-
mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
|
|
409
|
-
if (ctx.config.mobile) {
|
|
410
|
-
for (const device of ctx.config.mobile.devices) {
|
|
411
|
-
let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
|
|
412
|
-
let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
|
|
413
|
-
let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
|
|
414
|
-
(_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
|
|
415
|
-
viewport: { width: portrait ? width : height, height: portrait ? height : width },
|
|
416
|
-
viewportString: `${device} (${ctx.config.mobile.orientation})`,
|
|
417
|
-
fullPage: ctx.config.mobile.fullPage,
|
|
418
|
-
device: true,
|
|
419
|
-
os
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
return mobileRenderViewports;
|
|
424
|
-
}
|
|
425
|
-
function getRenderViewports(ctx) {
|
|
426
|
-
let mobileRenderViewports = getMobileRenderViewports(ctx);
|
|
427
|
-
return [
|
|
428
|
-
...getWebRenderViewports(ctx),
|
|
429
|
-
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
430
|
-
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
431
|
-
];
|
|
432
|
-
}
|
|
433
|
-
var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
|
|
434
|
-
var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
|
|
435
|
-
var ALLOWED_STATUSES = [200, 201];
|
|
436
|
-
var REQUEST_TIMEOUT = 1e4;
|
|
437
|
-
var MIN_VIEWPORT_HEIGHT = 1080;
|
|
438
|
-
var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function* () {
|
|
439
|
-
var _a;
|
|
440
|
-
ctx.log.debug(`Processing snapshot ${snapshot.name}`);
|
|
441
|
-
let launchOptions = { headless: true };
|
|
442
|
-
let contextOptions = {
|
|
443
|
-
javaScriptEnabled: ctx.config.enableJavaScript,
|
|
444
|
-
userAgent: constants_default.CHROME_USER_AGENT
|
|
445
|
-
};
|
|
446
|
-
if (!((_a = ctx.browser) == null ? void 0 : _a.isConnected())) {
|
|
447
|
-
if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
|
|
448
|
-
launchOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
|
|
449
|
-
ctx.browser = yield test.chromium.launch(launchOptions);
|
|
450
|
-
ctx.log.debug(`Chromium launched with options ${JSON.stringify(launchOptions)}`);
|
|
451
|
-
}
|
|
452
|
-
const context = yield ctx.browser.newContext(contextOptions);
|
|
453
|
-
ctx.log.debug(`Browser context created with options ${JSON.stringify(contextOptions)}`);
|
|
454
|
-
const page = yield context.newPage();
|
|
455
|
-
let cache = {};
|
|
456
|
-
yield page.route("**/*", (route, request) => __async(void 0, null, function* () {
|
|
457
|
-
const requestUrl = request.url();
|
|
458
|
-
const requestHostname = new URL(requestUrl).hostname;
|
|
459
|
-
try {
|
|
460
|
-
if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
|
|
461
|
-
throw new Error("resource type mp3/mp4/wav/ogg/webm");
|
|
462
|
-
}
|
|
463
|
-
ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
|
|
464
|
-
if (ctx.config.enableJavaScript)
|
|
465
|
-
ALLOWED_RESOURCES.push("script");
|
|
466
|
-
const response = yield page.request.fetch(request, { timeout: REQUEST_TIMEOUT });
|
|
467
|
-
const body = yield response.body();
|
|
468
|
-
if (!body) {
|
|
469
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
470
|
-
- skipping no response`);
|
|
471
|
-
} else if (!body.length) {
|
|
472
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
473
|
-
- skipping empty response`);
|
|
474
|
-
} else if (requestUrl === snapshot.url) {
|
|
475
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
476
|
-
- skipping root resource`);
|
|
477
|
-
} else if (!ctx.config.allowedHostnames.includes(requestHostname)) {
|
|
478
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
479
|
-
- skipping remote resource`);
|
|
480
|
-
} else if (cache[requestUrl]) {
|
|
481
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
482
|
-
- skipping already cached resource`);
|
|
483
|
-
} else if (body.length > MAX_RESOURCE_SIZE) {
|
|
484
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
485
|
-
- skipping resource larger than 15MB`);
|
|
486
|
-
} else if (!ALLOWED_STATUSES.includes(response.status())) {
|
|
487
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
488
|
-
- skipping disallowed status [${response.status()}]`);
|
|
489
|
-
} else if (!ALLOWED_RESOURCES.includes(request.resourceType())) {
|
|
490
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
491
|
-
- skipping disallowed resource type [${request.resourceType()}]`);
|
|
492
|
-
} else {
|
|
493
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
494
|
-
- content-type ${response.headers()["content-type"]}`);
|
|
495
|
-
cache[requestUrl] = {
|
|
496
|
-
body: body.toString("base64"),
|
|
497
|
-
type: response.headers()["content-type"]
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
route.fulfill({
|
|
501
|
-
status: response.status(),
|
|
502
|
-
headers: response.headers(),
|
|
503
|
-
body
|
|
504
|
-
});
|
|
505
|
-
} catch (error) {
|
|
506
|
-
ctx.log.debug(`Handling request ${requestUrl}
|
|
507
|
-
- aborted due to ${error.message}`);
|
|
508
|
-
route.abort();
|
|
509
|
-
}
|
|
510
|
-
}));
|
|
511
|
-
let options = snapshot.options;
|
|
512
|
-
let optionWarnings = /* @__PURE__ */ new Set();
|
|
513
|
-
let processedOptions = {};
|
|
514
|
-
let selectors = [];
|
|
515
|
-
let ignoreOrSelectDOM;
|
|
516
|
-
let ignoreOrSelectBoxes;
|
|
517
|
-
if (options && Object.keys(options).length) {
|
|
518
|
-
ctx.log.debug(`Snapshot options: ${JSON.stringify(options)}`);
|
|
519
|
-
const isNotAllEmpty = (obj) => {
|
|
520
|
-
var _a2;
|
|
521
|
-
for (let key in obj)
|
|
522
|
-
if ((_a2 = obj[key]) == null ? void 0 : _a2.length)
|
|
523
|
-
return true;
|
|
524
|
-
return false;
|
|
525
|
-
};
|
|
526
|
-
if (options.element && Object.keys(options.element).length) {
|
|
527
|
-
if (options.element.id)
|
|
528
|
-
processedOptions.element = "#" + options.element.id;
|
|
529
|
-
else if (options.element.class)
|
|
530
|
-
processedOptions.element = "." + options.element.class;
|
|
531
|
-
else if (options.element.cssSelector)
|
|
532
|
-
processedOptions.element = options.element.cssSelector;
|
|
533
|
-
else if (options.element.xpath)
|
|
534
|
-
processedOptions.element = "xpath=" + options.element.xpath;
|
|
535
|
-
} else if (options.ignoreDOM && Object.keys(options.ignoreDOM).length && isNotAllEmpty(options.ignoreDOM)) {
|
|
536
|
-
processedOptions.ignoreBoxes = {};
|
|
537
|
-
ignoreOrSelectDOM = "ignoreDOM";
|
|
538
|
-
ignoreOrSelectBoxes = "ignoreBoxes";
|
|
539
|
-
} else if (options.selectDOM && Object.keys(options.selectDOM).length && isNotAllEmpty(options.selectDOM)) {
|
|
540
|
-
processedOptions.selectBoxes = {};
|
|
541
|
-
ignoreOrSelectDOM = "selectDOM";
|
|
542
|
-
ignoreOrSelectBoxes = "selectBoxes";
|
|
543
|
-
}
|
|
544
|
-
if (ignoreOrSelectDOM) {
|
|
545
|
-
for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
|
|
546
|
-
switch (key) {
|
|
547
|
-
case "id":
|
|
548
|
-
selectors.push(...value.map((e) => "#" + e));
|
|
549
|
-
break;
|
|
550
|
-
case "class":
|
|
551
|
-
selectors.push(...value.map((e) => "." + e));
|
|
552
|
-
break;
|
|
553
|
-
case "xpath":
|
|
554
|
-
selectors.push(...value.map((e) => "xpath=" + e));
|
|
555
|
-
break;
|
|
556
|
-
case "cssSelector":
|
|
557
|
-
selectors.push(...value);
|
|
558
|
-
break;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
let navigated = false;
|
|
564
|
-
let renderViewports = getRenderViewports(ctx);
|
|
565
|
-
for (const { viewport, viewportString, fullPage } of renderViewports) {
|
|
566
|
-
yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
|
|
567
|
-
ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
|
|
568
|
-
if (!navigated) {
|
|
569
|
-
try {
|
|
570
|
-
yield page.goto(snapshot.url, { waitUntil: "domcontentloaded" });
|
|
571
|
-
yield new Promise((r) => setTimeout(r, 1250));
|
|
572
|
-
if (ctx.config.waitForTimeout)
|
|
573
|
-
yield page.waitForTimeout(ctx.config.waitForTimeout);
|
|
574
|
-
navigated = true;
|
|
575
|
-
ctx.log.debug(`Navigated to ${snapshot.url}`);
|
|
576
|
-
} catch (error) {
|
|
577
|
-
ctx.log.debug(`Navigation to discovery page failed; ${error}`);
|
|
578
|
-
throw new Error(error.message);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
if (ctx.config.enableJavaScript && fullPage)
|
|
582
|
-
yield page.evaluate(scrollToBottomAndBackToTop);
|
|
583
|
-
try {
|
|
584
|
-
yield page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
585
|
-
ctx.log.debug("Network idle 500ms");
|
|
586
|
-
} catch (error) {
|
|
587
|
-
ctx.log.debug(`Network idle failed due to ${error}`);
|
|
588
|
-
}
|
|
589
|
-
if (processedOptions.element) {
|
|
590
|
-
let l = yield page.locator(processedOptions.element).all();
|
|
591
|
-
if (l.length === 0) {
|
|
592
|
-
throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${processedOptions.element}`);
|
|
593
|
-
} else if (l.length > 1) {
|
|
594
|
-
throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, multiple elements found for selector ${processedOptions.element}`);
|
|
595
|
-
}
|
|
596
|
-
} else if (selectors.length) {
|
|
597
|
-
let locators = [];
|
|
598
|
-
if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString]))
|
|
599
|
-
processedOptions[ignoreOrSelectBoxes][viewportString] = [];
|
|
600
|
-
for (const selector of selectors) {
|
|
601
|
-
let l = yield page.locator(selector).all();
|
|
602
|
-
if (l.length === 0) {
|
|
603
|
-
optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
locators.push(...l);
|
|
607
|
-
}
|
|
608
|
-
for (const locator of locators) {
|
|
609
|
-
let bb = yield locator.boundingBox();
|
|
610
|
-
if (bb)
|
|
611
|
-
processedOptions[ignoreOrSelectBoxes][viewportString].push({
|
|
612
|
-
left: bb.x,
|
|
613
|
-
top: bb.y,
|
|
614
|
-
right: bb.x + bb.width,
|
|
615
|
-
bottom: bb.y + bb.height
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
if (snapshot.dom.resources.length) {
|
|
621
|
-
for (let resource of snapshot.dom.resources) {
|
|
622
|
-
cache[resource.url] = {
|
|
623
|
-
body: resource.content,
|
|
624
|
-
type: resource.mimetype
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return {
|
|
629
|
-
processedSnapshot: {
|
|
630
|
-
name: snapshot.name,
|
|
631
|
-
url: snapshot.url,
|
|
632
|
-
dom: Buffer.from(snapshot.dom.html).toString("base64"),
|
|
633
|
-
resources: cache,
|
|
634
|
-
options: processedOptions
|
|
635
|
-
},
|
|
636
|
-
warnings: [...optionWarnings, ...snapshot.dom.warnings]
|
|
637
|
-
};
|
|
638
|
-
});
|
|
318
|
+
// src/lib/schemaValidation.ts
|
|
639
319
|
var ajv = new Ajv__default.default({ allErrors: true });
|
|
640
320
|
ajv.addFormat("web-url", {
|
|
641
321
|
type: "string",
|
|
@@ -962,32 +642,22 @@ var server_default = (ctx) => __async(void 0, null, function* () {
|
|
|
962
642
|
reply.code(200).send({ data: { dom: SMARTUI_DOM } });
|
|
963
643
|
});
|
|
964
644
|
server.post("/snapshot", opts, (request, reply) => __async(void 0, null, function* () {
|
|
645
|
+
var _a;
|
|
965
646
|
let replyCode;
|
|
966
647
|
let replyBody;
|
|
967
648
|
try {
|
|
968
649
|
let { snapshot, testType } = request.body;
|
|
969
650
|
if (!validateSnapshot(snapshot))
|
|
970
651
|
throw new Error(validateSnapshot.errors[0].message);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
ctx.totalSnapshots++;
|
|
652
|
+
ctx.testType = testType;
|
|
653
|
+
(_a = ctx.snapshotQueue) == null ? void 0 : _a.enqueue(snapshot);
|
|
974
654
|
replyCode = 200;
|
|
975
|
-
replyBody = { data: { message: "success", warnings } };
|
|
655
|
+
replyBody = { data: { message: "success", warnings: [] } };
|
|
976
656
|
} catch (error) {
|
|
977
657
|
ctx.log.debug(`snapshot failed; ${error}`);
|
|
978
658
|
replyCode = 500;
|
|
979
659
|
replyBody = { error: { message: error.message } };
|
|
980
660
|
}
|
|
981
|
-
if (ctx.browser) {
|
|
982
|
-
for (let context of ctx.browser.contexts()) {
|
|
983
|
-
for (let page of context.pages()) {
|
|
984
|
-
yield page.close();
|
|
985
|
-
ctx.log.debug(`Closed browser page`);
|
|
986
|
-
}
|
|
987
|
-
yield context.close();
|
|
988
|
-
ctx.log.debug(`Closed browser context`);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
661
|
return reply.code(replyCode).send(replyBody);
|
|
992
662
|
}));
|
|
993
663
|
yield server.listen({ port: ctx.options.port });
|
|
@@ -1048,13 +718,13 @@ var logger = winston.createLogger({
|
|
|
1048
718
|
let message = typeof info.message === "object" ? JSON.stringify(info.message) : info.message;
|
|
1049
719
|
switch (info.level) {
|
|
1050
720
|
case "debug":
|
|
1051
|
-
message =
|
|
721
|
+
message = chalk7__default.default.blue(message);
|
|
1052
722
|
break;
|
|
1053
723
|
case "warn":
|
|
1054
|
-
message =
|
|
724
|
+
message = chalk7__default.default.yellow(message);
|
|
1055
725
|
break;
|
|
1056
726
|
case "error":
|
|
1057
|
-
message =
|
|
727
|
+
message = chalk7__default.default.red(message);
|
|
1058
728
|
break;
|
|
1059
729
|
}
|
|
1060
730
|
return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
|
|
@@ -1073,11 +743,11 @@ var startServer_default = (ctx) => {
|
|
|
1073
743
|
updateLogContext({ task: "startServer" });
|
|
1074
744
|
try {
|
|
1075
745
|
ctx2.server = yield server_default(ctx2);
|
|
1076
|
-
task.output =
|
|
746
|
+
task.output = chalk7__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
|
|
1077
747
|
task.title = "SmartUI started";
|
|
1078
748
|
} catch (error) {
|
|
1079
749
|
ctx2.log.debug(error);
|
|
1080
|
-
task.output =
|
|
750
|
+
task.output = chalk7__default.default.gray(error.message);
|
|
1081
751
|
throw new Error("SmartUI server setup failed");
|
|
1082
752
|
}
|
|
1083
753
|
}),
|
|
@@ -1091,11 +761,11 @@ var auth_default = (ctx) => {
|
|
|
1091
761
|
updateLogContext({ task: "auth" });
|
|
1092
762
|
try {
|
|
1093
763
|
yield ctx2.client.auth(ctx2.log);
|
|
1094
|
-
task.output =
|
|
764
|
+
task.output = chalk7__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
|
|
1095
765
|
task.title = "Authenticated with SmartUI";
|
|
1096
766
|
} catch (error) {
|
|
1097
767
|
ctx2.log.debug(error);
|
|
1098
|
-
task.output =
|
|
768
|
+
task.output = chalk7__default.default.gray(error.message);
|
|
1099
769
|
throw new Error("Authentication failed");
|
|
1100
770
|
}
|
|
1101
771
|
}),
|
|
@@ -1104,7 +774,7 @@ var auth_default = (ctx) => {
|
|
|
1104
774
|
};
|
|
1105
775
|
|
|
1106
776
|
// package.json
|
|
1107
|
-
var version = "3.0.
|
|
777
|
+
var version = "3.0.12";
|
|
1108
778
|
var package_default = {
|
|
1109
779
|
name: "@lambdatest/smartui-cli",
|
|
1110
780
|
version,
|
|
@@ -1214,19 +884,19 @@ var httpClient = class {
|
|
|
1214
884
|
params
|
|
1215
885
|
}, log);
|
|
1216
886
|
}
|
|
1217
|
-
uploadSnapshot(
|
|
887
|
+
uploadSnapshot(ctx, snapshot) {
|
|
1218
888
|
return this.request({
|
|
1219
|
-
url: `/builds/${
|
|
889
|
+
url: `/builds/${ctx.build.id}/snapshot`,
|
|
1220
890
|
method: "POST",
|
|
1221
891
|
headers: { "Content-Type": "application/json" },
|
|
1222
892
|
data: {
|
|
1223
893
|
snapshot,
|
|
1224
894
|
test: {
|
|
1225
|
-
type: testType,
|
|
895
|
+
type: ctx.testType,
|
|
1226
896
|
source: "cli"
|
|
1227
897
|
}
|
|
1228
898
|
}
|
|
1229
|
-
}, log);
|
|
899
|
+
}, ctx.log);
|
|
1230
900
|
}
|
|
1231
901
|
uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log) {
|
|
1232
902
|
browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
|
|
@@ -1423,11 +1093,11 @@ var getGitInfo_default = (ctx) => {
|
|
|
1423
1093
|
}
|
|
1424
1094
|
try {
|
|
1425
1095
|
ctx2.git = git_default(ctx2);
|
|
1426
|
-
task.output =
|
|
1096
|
+
task.output = chalk7__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
|
|
1427
1097
|
task.title = "Fetched git information";
|
|
1428
1098
|
} catch (error) {
|
|
1429
1099
|
ctx2.log.debug(error);
|
|
1430
|
-
task.output =
|
|
1100
|
+
task.output = chalk7__default.default.gray(`${error.message}`);
|
|
1431
1101
|
throw new Error("Error fetching git repo details");
|
|
1432
1102
|
}
|
|
1433
1103
|
}),
|
|
@@ -1447,11 +1117,11 @@ var createBuild_default = (ctx) => {
|
|
|
1447
1117
|
url: resp.data.buildURL,
|
|
1448
1118
|
baseline: resp.data.baseline
|
|
1449
1119
|
};
|
|
1450
|
-
task.output =
|
|
1120
|
+
task.output = chalk7__default.default.gray(`build id: ${resp.data.buildId}`);
|
|
1451
1121
|
task.title = "SmartUI build created";
|
|
1452
1122
|
} catch (error) {
|
|
1453
1123
|
ctx2.log.debug(error);
|
|
1454
|
-
task.output =
|
|
1124
|
+
task.output = chalk7__default.default.gray(error.message);
|
|
1455
1125
|
throw new Error("SmartUI build creation failed");
|
|
1456
1126
|
}
|
|
1457
1127
|
}),
|
|
@@ -1470,13 +1140,13 @@ var exec_default = (ctx) => {
|
|
|
1470
1140
|
let totalOutput = "";
|
|
1471
1141
|
const output = listr2.createWritable((chunk) => {
|
|
1472
1142
|
totalOutput += chunk;
|
|
1473
|
-
task.output =
|
|
1143
|
+
task.output = chalk7__default.default.gray(totalOutput);
|
|
1474
1144
|
});
|
|
1475
1145
|
(_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
|
|
1476
1146
|
(_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
|
|
1477
1147
|
childProcess.on("error", (error) => {
|
|
1478
1148
|
var _a3;
|
|
1479
|
-
task.output =
|
|
1149
|
+
task.output = chalk7__default.default.gray(`error: ${error.message}`);
|
|
1480
1150
|
throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
|
|
1481
1151
|
});
|
|
1482
1152
|
childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
|
|
@@ -1494,6 +1164,46 @@ var exec_default = (ctx) => {
|
|
|
1494
1164
|
exitOnError: false
|
|
1495
1165
|
};
|
|
1496
1166
|
};
|
|
1167
|
+
var processSnapshot_default = (ctx) => {
|
|
1168
|
+
return {
|
|
1169
|
+
title: `Processing snapshots`,
|
|
1170
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1171
|
+
var _a;
|
|
1172
|
+
try {
|
|
1173
|
+
yield new Promise((resolve) => {
|
|
1174
|
+
let output2 = "";
|
|
1175
|
+
const intervalId = setInterval(() => {
|
|
1176
|
+
var _a2, _b, _c;
|
|
1177
|
+
if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b = ctx2.snapshotQueue) == null ? void 0 : _b.isProcessing())) {
|
|
1178
|
+
clearInterval(intervalId);
|
|
1179
|
+
resolve();
|
|
1180
|
+
} else {
|
|
1181
|
+
task.title = `Processing snapshot ${(_c = ctx2.snapshotQueue) == null ? void 0 : _c.getProcessingSnapshot()}`;
|
|
1182
|
+
}
|
|
1183
|
+
}, 500);
|
|
1184
|
+
});
|
|
1185
|
+
let output = "";
|
|
1186
|
+
for (let snapshot of (_a = ctx2.snapshotQueue) == null ? void 0 : _a.getProcessedSnapshots()) {
|
|
1187
|
+
if (snapshot.error)
|
|
1188
|
+
output += `${chalk7__default.default.red("\u2717")} ${chalk7__default.default.gray(`${snapshot.name}
|
|
1189
|
+
[error] ${snapshot.error}`)}
|
|
1190
|
+
`;
|
|
1191
|
+
else
|
|
1192
|
+
output += `${chalk7__default.default.green("\u2713")} ${chalk7__default.default.gray(snapshot.name)}
|
|
1193
|
+
${snapshot.warnings.length ? chalk7__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
|
|
1194
|
+
`) : ""}`;
|
|
1195
|
+
}
|
|
1196
|
+
task.output = output;
|
|
1197
|
+
task.title = "Processed snapshots";
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
ctx2.log.debug(error);
|
|
1200
|
+
task.output = chalk7__default.default.gray(error.message);
|
|
1201
|
+
throw new Error("Processing of snapshots failed");
|
|
1202
|
+
}
|
|
1203
|
+
}),
|
|
1204
|
+
rendererOptions: { persistentOutput: true }
|
|
1205
|
+
};
|
|
1206
|
+
};
|
|
1497
1207
|
var finalizeBuild_default = (ctx) => {
|
|
1498
1208
|
return {
|
|
1499
1209
|
title: `Finalizing build`,
|
|
@@ -1502,17 +1212,397 @@ var finalizeBuild_default = (ctx) => {
|
|
|
1502
1212
|
try {
|
|
1503
1213
|
yield new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
1504
1214
|
yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
|
|
1505
|
-
task.output =
|
|
1215
|
+
task.output = chalk7__default.default.gray(`build url: ${ctx2.build.url}`);
|
|
1506
1216
|
task.title = "Finalized build";
|
|
1507
1217
|
} catch (error) {
|
|
1508
1218
|
ctx2.log.debug(error);
|
|
1509
|
-
task.output =
|
|
1219
|
+
task.output = chalk7__default.default.gray(error.message);
|
|
1510
1220
|
throw new Error("Finalize build failed");
|
|
1511
1221
|
}
|
|
1512
1222
|
}),
|
|
1513
1223
|
rendererOptions: { persistentOutput: true }
|
|
1514
1224
|
};
|
|
1515
1225
|
};
|
|
1226
|
+
function delDir(dir) {
|
|
1227
|
+
if (fs5__default.default.existsSync(dir)) {
|
|
1228
|
+
fs5__default.default.rmSync(dir, { recursive: true });
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
function scrollToBottomAndBackToTop({
|
|
1232
|
+
frequency = 100,
|
|
1233
|
+
timing = 8,
|
|
1234
|
+
remoteWindow = window
|
|
1235
|
+
} = {}) {
|
|
1236
|
+
return new Promise((resolve) => {
|
|
1237
|
+
let scrolls = 1;
|
|
1238
|
+
let scrollLength = remoteWindow.document.body.scrollHeight / frequency;
|
|
1239
|
+
(function scroll() {
|
|
1240
|
+
let scrollBy = scrollLength * scrolls;
|
|
1241
|
+
remoteWindow.setTimeout(() => {
|
|
1242
|
+
remoteWindow.scrollTo(0, scrollBy);
|
|
1243
|
+
if (scrolls < frequency) {
|
|
1244
|
+
scrolls += 1;
|
|
1245
|
+
scroll();
|
|
1246
|
+
}
|
|
1247
|
+
if (scrolls === frequency) {
|
|
1248
|
+
remoteWindow.setTimeout(() => {
|
|
1249
|
+
remoteWindow.scrollTo(0, 0);
|
|
1250
|
+
resolve();
|
|
1251
|
+
}, timing);
|
|
1252
|
+
}
|
|
1253
|
+
}, timing);
|
|
1254
|
+
})();
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
function launchBrowsers(ctx) {
|
|
1258
|
+
return __async(this, null, function* () {
|
|
1259
|
+
let browsers = {};
|
|
1260
|
+
let launchOptions = { headless: true };
|
|
1261
|
+
if (ctx.config.web) {
|
|
1262
|
+
for (const browser of ctx.config.web.browsers) {
|
|
1263
|
+
switch (browser) {
|
|
1264
|
+
case constants_default.CHROME:
|
|
1265
|
+
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
1266
|
+
break;
|
|
1267
|
+
case constants_default.SAFARI:
|
|
1268
|
+
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
1269
|
+
break;
|
|
1270
|
+
case constants_default.FIREFOX:
|
|
1271
|
+
browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
|
|
1272
|
+
break;
|
|
1273
|
+
case constants_default.EDGE:
|
|
1274
|
+
browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
|
|
1275
|
+
break;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
if (ctx.config.mobile) {
|
|
1280
|
+
for (const device of ctx.config.mobile.devices) {
|
|
1281
|
+
if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
|
|
1282
|
+
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
1283
|
+
else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
|
|
1284
|
+
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return browsers;
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
function closeBrowsers(browsers) {
|
|
1291
|
+
return __async(this, null, function* () {
|
|
1292
|
+
var _a;
|
|
1293
|
+
for (const browserName of Object.keys(browsers))
|
|
1294
|
+
yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
function getWebRenderViewports(ctx) {
|
|
1298
|
+
let webRenderViewports = [];
|
|
1299
|
+
if (ctx.config.web) {
|
|
1300
|
+
for (const viewport of ctx.config.web.viewports) {
|
|
1301
|
+
webRenderViewports.push({
|
|
1302
|
+
viewport,
|
|
1303
|
+
viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
|
|
1304
|
+
fullPage: viewport.height ? false : true,
|
|
1305
|
+
device: false
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return webRenderViewports;
|
|
1310
|
+
}
|
|
1311
|
+
function getMobileRenderViewports(ctx) {
|
|
1312
|
+
var _a;
|
|
1313
|
+
let mobileRenderViewports = {};
|
|
1314
|
+
mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
|
|
1315
|
+
mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
|
|
1316
|
+
if (ctx.config.mobile) {
|
|
1317
|
+
for (const device of ctx.config.mobile.devices) {
|
|
1318
|
+
let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
|
|
1319
|
+
let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
|
|
1320
|
+
let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
|
|
1321
|
+
(_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
|
|
1322
|
+
viewport: { width: portrait ? width : height, height: portrait ? height : width },
|
|
1323
|
+
viewportString: `${device} (${ctx.config.mobile.orientation})`,
|
|
1324
|
+
fullPage: ctx.config.mobile.fullPage,
|
|
1325
|
+
device: true,
|
|
1326
|
+
os
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return mobileRenderViewports;
|
|
1331
|
+
}
|
|
1332
|
+
function getRenderViewports(ctx) {
|
|
1333
|
+
let mobileRenderViewports = getMobileRenderViewports(ctx);
|
|
1334
|
+
return [
|
|
1335
|
+
...getWebRenderViewports(ctx),
|
|
1336
|
+
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
1337
|
+
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1338
|
+
];
|
|
1339
|
+
}
|
|
1340
|
+
var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
|
|
1341
|
+
var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
|
|
1342
|
+
var ALLOWED_STATUSES = [200, 201];
|
|
1343
|
+
var REQUEST_TIMEOUT = 1e4;
|
|
1344
|
+
var MIN_VIEWPORT_HEIGHT = 1080;
|
|
1345
|
+
var Queue = class {
|
|
1346
|
+
constructor(ctx) {
|
|
1347
|
+
this.snapshots = [];
|
|
1348
|
+
this.processedSnapshots = [];
|
|
1349
|
+
this.processing = false;
|
|
1350
|
+
this.processingSnapshot = "";
|
|
1351
|
+
this.ctx = ctx;
|
|
1352
|
+
}
|
|
1353
|
+
enqueue(item) {
|
|
1354
|
+
this.snapshots.push(item);
|
|
1355
|
+
if (!this.processing) {
|
|
1356
|
+
this.processing = true;
|
|
1357
|
+
this.processNext();
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
processNext() {
|
|
1361
|
+
return __async(this, null, function* () {
|
|
1362
|
+
if (!this.isEmpty()) {
|
|
1363
|
+
const snapshot = this.snapshots.shift();
|
|
1364
|
+
try {
|
|
1365
|
+
this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
|
|
1366
|
+
let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
|
|
1367
|
+
yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
|
|
1368
|
+
this.ctx.totalSnapshots++;
|
|
1369
|
+
this.processedSnapshots.push({ name: snapshot.name, warnings });
|
|
1370
|
+
} catch (error) {
|
|
1371
|
+
this.ctx.log.debug(`snapshot failed; ${error}`);
|
|
1372
|
+
this.processedSnapshots.push({ name: snapshot.name, error: error.message });
|
|
1373
|
+
}
|
|
1374
|
+
if (this.ctx.browser) {
|
|
1375
|
+
for (let context of this.ctx.browser.contexts()) {
|
|
1376
|
+
for (let page of context.pages()) {
|
|
1377
|
+
yield page.close();
|
|
1378
|
+
this.ctx.log.debug(`Closed browser page for snapshot ${snapshot.name}`);
|
|
1379
|
+
}
|
|
1380
|
+
yield context.close();
|
|
1381
|
+
this.ctx.log.debug(`Closed browser context for snapshot ${snapshot.name}`);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
this.processNext();
|
|
1385
|
+
} else {
|
|
1386
|
+
this.processing = false;
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
isProcessing() {
|
|
1391
|
+
return this.processing;
|
|
1392
|
+
}
|
|
1393
|
+
getProcessingSnapshot() {
|
|
1394
|
+
return this.processingSnapshot;
|
|
1395
|
+
}
|
|
1396
|
+
getProcessedSnapshots() {
|
|
1397
|
+
return this.processedSnapshots;
|
|
1398
|
+
}
|
|
1399
|
+
isEmpty() {
|
|
1400
|
+
return this.snapshots.length ? false : true;
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
function processSnapshot(snapshot, ctx) {
|
|
1404
|
+
return __async(this, null, function* () {
|
|
1405
|
+
var _a;
|
|
1406
|
+
ctx.log.debug(`Processing snapshot ${snapshot.name}`);
|
|
1407
|
+
let launchOptions = { headless: true };
|
|
1408
|
+
let contextOptions = {
|
|
1409
|
+
javaScriptEnabled: ctx.config.enableJavaScript,
|
|
1410
|
+
userAgent: constants_default.CHROME_USER_AGENT
|
|
1411
|
+
};
|
|
1412
|
+
if (!((_a = ctx.browser) == null ? void 0 : _a.isConnected())) {
|
|
1413
|
+
if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
|
|
1414
|
+
launchOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
|
|
1415
|
+
ctx.browser = yield test.chromium.launch(launchOptions);
|
|
1416
|
+
ctx.log.debug(`Chromium launched with options ${JSON.stringify(launchOptions)}`);
|
|
1417
|
+
}
|
|
1418
|
+
const context = yield ctx.browser.newContext(contextOptions);
|
|
1419
|
+
ctx.log.debug(`Browser context created with options ${JSON.stringify(contextOptions)}`);
|
|
1420
|
+
const page = yield context.newPage();
|
|
1421
|
+
let cache = {};
|
|
1422
|
+
yield page.route("**/*", (route, request) => __async(this, null, function* () {
|
|
1423
|
+
const requestUrl = request.url();
|
|
1424
|
+
const requestHostname = new URL(requestUrl).hostname;
|
|
1425
|
+
try {
|
|
1426
|
+
if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
|
|
1427
|
+
throw new Error("resource type mp3/mp4/wav/ogg/webm");
|
|
1428
|
+
}
|
|
1429
|
+
ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
|
|
1430
|
+
if (ctx.config.enableJavaScript)
|
|
1431
|
+
ALLOWED_RESOURCES.push("script");
|
|
1432
|
+
const response = yield page.request.fetch(request, { timeout: REQUEST_TIMEOUT });
|
|
1433
|
+
const body = yield response.body();
|
|
1434
|
+
if (!body) {
|
|
1435
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1436
|
+
- skipping no response`);
|
|
1437
|
+
} else if (!body.length) {
|
|
1438
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1439
|
+
- skipping empty response`);
|
|
1440
|
+
} else if (requestUrl === snapshot.url) {
|
|
1441
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1442
|
+
- skipping root resource`);
|
|
1443
|
+
} else if (!ctx.config.allowedHostnames.includes(requestHostname)) {
|
|
1444
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1445
|
+
- skipping remote resource`);
|
|
1446
|
+
} else if (cache[requestUrl]) {
|
|
1447
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1448
|
+
- skipping already cached resource`);
|
|
1449
|
+
} else if (body.length > MAX_RESOURCE_SIZE) {
|
|
1450
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1451
|
+
- skipping resource larger than 15MB`);
|
|
1452
|
+
} else if (!ALLOWED_STATUSES.includes(response.status())) {
|
|
1453
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1454
|
+
- skipping disallowed status [${response.status()}]`);
|
|
1455
|
+
} else if (!ALLOWED_RESOURCES.includes(request.resourceType())) {
|
|
1456
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1457
|
+
- skipping disallowed resource type [${request.resourceType()}]`);
|
|
1458
|
+
} else {
|
|
1459
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1460
|
+
- content-type ${response.headers()["content-type"]}`);
|
|
1461
|
+
cache[requestUrl] = {
|
|
1462
|
+
body: body.toString("base64"),
|
|
1463
|
+
type: response.headers()["content-type"]
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
route.fulfill({
|
|
1467
|
+
status: response.status(),
|
|
1468
|
+
headers: response.headers(),
|
|
1469
|
+
body
|
|
1470
|
+
});
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
ctx.log.debug(`Handling request ${requestUrl}
|
|
1473
|
+
- aborted due to ${error.message}`);
|
|
1474
|
+
route.abort();
|
|
1475
|
+
}
|
|
1476
|
+
}));
|
|
1477
|
+
let options = snapshot.options;
|
|
1478
|
+
let optionWarnings = /* @__PURE__ */ new Set();
|
|
1479
|
+
let processedOptions = {};
|
|
1480
|
+
let selectors = [];
|
|
1481
|
+
let ignoreOrSelectDOM;
|
|
1482
|
+
let ignoreOrSelectBoxes;
|
|
1483
|
+
if (options && Object.keys(options).length) {
|
|
1484
|
+
ctx.log.debug(`Snapshot options: ${JSON.stringify(options)}`);
|
|
1485
|
+
const isNotAllEmpty = (obj) => {
|
|
1486
|
+
var _a2;
|
|
1487
|
+
for (let key in obj)
|
|
1488
|
+
if ((_a2 = obj[key]) == null ? void 0 : _a2.length)
|
|
1489
|
+
return true;
|
|
1490
|
+
return false;
|
|
1491
|
+
};
|
|
1492
|
+
if (options.element && Object.keys(options.element).length) {
|
|
1493
|
+
if (options.element.id)
|
|
1494
|
+
processedOptions.element = "#" + options.element.id;
|
|
1495
|
+
else if (options.element.class)
|
|
1496
|
+
processedOptions.element = "." + options.element.class;
|
|
1497
|
+
else if (options.element.cssSelector)
|
|
1498
|
+
processedOptions.element = options.element.cssSelector;
|
|
1499
|
+
else if (options.element.xpath)
|
|
1500
|
+
processedOptions.element = "xpath=" + options.element.xpath;
|
|
1501
|
+
} else if (options.ignoreDOM && Object.keys(options.ignoreDOM).length && isNotAllEmpty(options.ignoreDOM)) {
|
|
1502
|
+
processedOptions.ignoreBoxes = {};
|
|
1503
|
+
ignoreOrSelectDOM = "ignoreDOM";
|
|
1504
|
+
ignoreOrSelectBoxes = "ignoreBoxes";
|
|
1505
|
+
} else if (options.selectDOM && Object.keys(options.selectDOM).length && isNotAllEmpty(options.selectDOM)) {
|
|
1506
|
+
processedOptions.selectBoxes = {};
|
|
1507
|
+
ignoreOrSelectDOM = "selectDOM";
|
|
1508
|
+
ignoreOrSelectBoxes = "selectBoxes";
|
|
1509
|
+
}
|
|
1510
|
+
if (ignoreOrSelectDOM) {
|
|
1511
|
+
for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
|
|
1512
|
+
switch (key) {
|
|
1513
|
+
case "id":
|
|
1514
|
+
selectors.push(...value.map((e) => "#" + e));
|
|
1515
|
+
break;
|
|
1516
|
+
case "class":
|
|
1517
|
+
selectors.push(...value.map((e) => "." + e));
|
|
1518
|
+
break;
|
|
1519
|
+
case "xpath":
|
|
1520
|
+
selectors.push(...value.map((e) => "xpath=" + e));
|
|
1521
|
+
break;
|
|
1522
|
+
case "cssSelector":
|
|
1523
|
+
selectors.push(...value);
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
let navigated = false;
|
|
1530
|
+
let renderViewports = getRenderViewports(ctx);
|
|
1531
|
+
for (const { viewport, viewportString, fullPage } of renderViewports) {
|
|
1532
|
+
yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
|
|
1533
|
+
ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
|
|
1534
|
+
if (!navigated) {
|
|
1535
|
+
try {
|
|
1536
|
+
yield page.goto(snapshot.url, { waitUntil: "domcontentloaded" });
|
|
1537
|
+
yield new Promise((r) => setTimeout(r, 1250));
|
|
1538
|
+
if (ctx.config.waitForTimeout)
|
|
1539
|
+
yield page.waitForTimeout(ctx.config.waitForTimeout);
|
|
1540
|
+
navigated = true;
|
|
1541
|
+
ctx.log.debug(`Navigated to ${snapshot.url}`);
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
ctx.log.debug(`Navigation to discovery page failed; ${error}`);
|
|
1544
|
+
throw new Error(error.message);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
if (ctx.config.enableJavaScript && fullPage)
|
|
1548
|
+
yield page.evaluate(scrollToBottomAndBackToTop);
|
|
1549
|
+
try {
|
|
1550
|
+
yield page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
1551
|
+
ctx.log.debug("Network idle 500ms");
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
ctx.log.debug(`Network idle failed due to ${error}`);
|
|
1554
|
+
}
|
|
1555
|
+
if (processedOptions.element) {
|
|
1556
|
+
let l = yield page.locator(processedOptions.element).all();
|
|
1557
|
+
if (l.length === 0) {
|
|
1558
|
+
throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${processedOptions.element}`);
|
|
1559
|
+
} else if (l.length > 1) {
|
|
1560
|
+
throw new Error(`for snapshot ${snapshot.name} viewport ${viewportString}, multiple elements found for selector ${processedOptions.element}`);
|
|
1561
|
+
}
|
|
1562
|
+
} else if (selectors.length) {
|
|
1563
|
+
let locators = [];
|
|
1564
|
+
if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewportString]))
|
|
1565
|
+
processedOptions[ignoreOrSelectBoxes][viewportString] = [];
|
|
1566
|
+
for (const selector of selectors) {
|
|
1567
|
+
let l = yield page.locator(selector).all();
|
|
1568
|
+
if (l.length === 0) {
|
|
1569
|
+
optionWarnings.add(`for snapshot ${snapshot.name} viewport ${viewportString}, no element found for selector ${selector}`);
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1572
|
+
locators.push(...l);
|
|
1573
|
+
}
|
|
1574
|
+
for (const locator of locators) {
|
|
1575
|
+
let bb = yield locator.boundingBox();
|
|
1576
|
+
if (bb)
|
|
1577
|
+
processedOptions[ignoreOrSelectBoxes][viewportString].push({
|
|
1578
|
+
left: bb.x,
|
|
1579
|
+
top: bb.y,
|
|
1580
|
+
right: bb.x + bb.width,
|
|
1581
|
+
bottom: bb.y + bb.height
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (snapshot.dom.resources.length) {
|
|
1587
|
+
for (let resource of snapshot.dom.resources) {
|
|
1588
|
+
cache[resource.url] = {
|
|
1589
|
+
body: resource.content,
|
|
1590
|
+
type: resource.mimetype
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return {
|
|
1595
|
+
processedSnapshot: {
|
|
1596
|
+
name: snapshot.name,
|
|
1597
|
+
url: snapshot.url,
|
|
1598
|
+
dom: Buffer.from(snapshot.dom.html).toString("base64"),
|
|
1599
|
+
resources: cache,
|
|
1600
|
+
options: processedOptions
|
|
1601
|
+
},
|
|
1602
|
+
warnings: [...optionWarnings, ...snapshot.dom.warnings]
|
|
1603
|
+
};
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1516
1606
|
|
|
1517
1607
|
// src/commander/exec.ts
|
|
1518
1608
|
var command = new commander.Command();
|
|
@@ -1525,6 +1615,7 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
|
|
|
1525
1615
|
return;
|
|
1526
1616
|
}
|
|
1527
1617
|
ctx.args.execCommand = execCommand;
|
|
1618
|
+
ctx.snapshotQueue = new Queue(ctx);
|
|
1528
1619
|
ctx.totalSnapshots = 0;
|
|
1529
1620
|
let tasks = new listr2.Listr(
|
|
1530
1621
|
[
|
|
@@ -1533,6 +1624,7 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
|
|
|
1533
1624
|
getGitInfo_default(),
|
|
1534
1625
|
createBuild_default(),
|
|
1535
1626
|
exec_default(ctx),
|
|
1627
|
+
processSnapshot_default(),
|
|
1536
1628
|
finalizeBuild_default()
|
|
1537
1629
|
],
|
|
1538
1630
|
{
|
|
@@ -1551,8 +1643,10 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
|
|
|
1551
1643
|
} catch (error) {
|
|
1552
1644
|
ctx.log.info("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
|
|
1553
1645
|
} finally {
|
|
1554
|
-
yield (_a = ctx.
|
|
1555
|
-
|
|
1646
|
+
yield (_a = ctx.browser) == null ? void 0 : _a.close();
|
|
1647
|
+
ctx.log.debug(`Closed browser`);
|
|
1648
|
+
yield (_b = ctx.server) == null ? void 0 : _b.close();
|
|
1649
|
+
ctx.log.debug(`Closed server`);
|
|
1556
1650
|
}
|
|
1557
1651
|
});
|
|
1558
1652
|
});
|
|
@@ -1722,13 +1816,13 @@ function captureScreenshots(ctx) {
|
|
|
1722
1816
|
else
|
|
1723
1817
|
yield captureScreenshotsSync(ctx, staticConfig, browsers);
|
|
1724
1818
|
delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
|
|
1725
|
-
output += `${
|
|
1819
|
+
output += `${chalk7__default.default.gray(staticConfig.name)} ${chalk7__default.default.green("\u2713")}
|
|
1726
1820
|
`;
|
|
1727
1821
|
ctx.task.output = output;
|
|
1728
1822
|
capturedScreenshots++;
|
|
1729
1823
|
} catch (error) {
|
|
1730
1824
|
ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
|
|
1731
|
-
output += `${
|
|
1825
|
+
output += `${chalk7__default.default.gray(staticConfig.name)} ${chalk7__default.default.red("\u2717")}
|
|
1732
1826
|
`;
|
|
1733
1827
|
ctx.task.output = output;
|
|
1734
1828
|
}
|
|
@@ -1752,7 +1846,7 @@ var captureScreenshots_default = (ctx) => {
|
|
|
1752
1846
|
task.title = "Screenshots captured successfully";
|
|
1753
1847
|
} catch (error) {
|
|
1754
1848
|
ctx2.log.debug(error);
|
|
1755
|
-
task.output =
|
|
1849
|
+
task.output = chalk7__default.default.gray(`${error.message}`);
|
|
1756
1850
|
throw new Error("Capturing screenshots failed");
|
|
1757
1851
|
}
|
|
1758
1852
|
}),
|
|
@@ -1846,7 +1940,7 @@ var uploadFigmaDesigns_default2 = (ctx) => {
|
|
|
1846
1940
|
ctx2.log.debug(`Figma designs processed: ${results}`);
|
|
1847
1941
|
} catch (error) {
|
|
1848
1942
|
ctx2.log.debug(error);
|
|
1849
|
-
task.output =
|
|
1943
|
+
task.output = chalk7__default.default.gray(`${error.message}`);
|
|
1850
1944
|
throw new Error("Uploading Figma designs failed");
|
|
1851
1945
|
}
|
|
1852
1946
|
}),
|
|
@@ -1916,13 +2010,13 @@ LambdaTest SmartUI CLI v${package_default.version}`);
|
|
|
1916
2010
|
log.warn(`This version is deprecated. A new version ${latestVersion} is available!
|
|
1917
2011
|
`);
|
|
1918
2012
|
else if (package_default.version !== latestVersion)
|
|
1919
|
-
log.info(
|
|
2013
|
+
log.info(chalk7__default.default.gray(`A new version ${latestVersion} is available!
|
|
1920
2014
|
`));
|
|
1921
2015
|
else
|
|
1922
|
-
log.info(
|
|
2016
|
+
log.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
1923
2017
|
} catch (error) {
|
|
1924
2018
|
log.debug(error);
|
|
1925
|
-
log.info(
|
|
2019
|
+
log.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
1926
2020
|
}
|
|
1927
2021
|
commander_default.parse();
|
|
1928
2022
|
});
|