@lambdatest/smartui-cli 3.0.3 → 3.0.5
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/dom-serializer.js +26 -23
- package/dist/index.cjs +79 -39
- package/package.json +1 -1
package/dist/dom-serializer.js
CHANGED
|
@@ -332,32 +332,35 @@
|
|
|
332
332
|
for (let video of dom.querySelectorAll('video')) {
|
|
333
333
|
let videoId = video.getAttribute('data-smartui-element-id');
|
|
334
334
|
let cloneEl = clone.querySelector(`[data-smartui-element-id="${videoId}"]`);
|
|
335
|
-
// if the video already has a poster image, no work for us to do
|
|
336
|
-
if (video.getAttribute('poster')) {
|
|
337
|
-
cloneEl.removeAttribute('src');
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
let canvas = document.createElement('canvas');
|
|
341
|
-
let width = canvas.width = video.videoWidth;
|
|
342
|
-
let height = canvas.height = video.videoHeight;
|
|
343
|
-
let dataUrl;
|
|
344
|
-
canvas.getContext('2d').drawImage(video, 0, 0, width, height);
|
|
345
|
-
try {
|
|
346
|
-
dataUrl = canvas.toDataURL();
|
|
347
|
-
} catch (e) {
|
|
348
|
-
warnings.add(`data-smartui-element-id="${videoId}" : ${e.toString()}`);
|
|
349
|
-
}
|
|
350
335
|
|
|
351
|
-
//
|
|
352
|
-
|
|
336
|
+
// remove video sources
|
|
337
|
+
cloneEl.removeAttribute('src');
|
|
338
|
+
const sourceEls = cloneEl.querySelectorAll('source');
|
|
339
|
+
if (sourceEls.length) sourceEls.forEach((sourceEl) => sourceEl.remove());
|
|
340
|
+
|
|
341
|
+
// if the video doesn't have a poster image
|
|
342
|
+
if (!video.getAttribute('poster')) {
|
|
343
|
+
let canvas = document.createElement('canvas');
|
|
344
|
+
let width = canvas.width = video.videoWidth;
|
|
345
|
+
let height = canvas.height = video.videoHeight;
|
|
346
|
+
let dataUrl;
|
|
347
|
+
canvas.getContext('2d').drawImage(video, 0, 0, width, height);
|
|
348
|
+
try {
|
|
349
|
+
dataUrl = canvas.toDataURL();
|
|
350
|
+
} catch (e) {
|
|
351
|
+
warnings.add(`data-smartui-element-id="${videoId}" : ${e.toString()}`);
|
|
352
|
+
}
|
|
353
353
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
// resources.add(resource);
|
|
354
|
+
// if the canvas produces a blank image, skip
|
|
355
|
+
if (!dataUrl || dataUrl === 'data:,') continue;
|
|
357
356
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
357
|
+
// create a resource from the serialized data url
|
|
358
|
+
let resource = resourceFromDataURL(videoId, dataUrl);
|
|
359
|
+
resources.add(resource);
|
|
360
|
+
|
|
361
|
+
// set poster attribute to resource url to avoid making a real request
|
|
362
|
+
cloneEl.setAttribute('poster', resource.url);
|
|
363
|
+
}
|
|
361
364
|
}
|
|
362
365
|
}
|
|
363
366
|
|
package/dist/index.cjs
CHANGED
|
@@ -7,7 +7,7 @@ var listr2 = require('listr2');
|
|
|
7
7
|
var chalk8 = require('chalk');
|
|
8
8
|
var path2 = require('path');
|
|
9
9
|
var fastify = require('fastify');
|
|
10
|
-
var
|
|
10
|
+
var fs5 = require('fs');
|
|
11
11
|
var test = require('@playwright/test');
|
|
12
12
|
var Ajv = require('ajv');
|
|
13
13
|
var addErrors = require('ajv-errors');
|
|
@@ -23,7 +23,7 @@ var which__default = /*#__PURE__*/_interopDefault(which);
|
|
|
23
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
|
|
26
|
+
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
27
27
|
var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
|
|
28
28
|
var addErrors__default = /*#__PURE__*/_interopDefault(addErrors);
|
|
29
29
|
var FormData__default = /*#__PURE__*/_interopDefault(FormData);
|
|
@@ -304,8 +304,8 @@ var constants_default = {
|
|
|
304
304
|
|
|
305
305
|
// src/lib/utils.ts
|
|
306
306
|
function delDir(dir) {
|
|
307
|
-
if (
|
|
308
|
-
|
|
307
|
+
if (fs5__default.default.existsSync(dir)) {
|
|
308
|
+
fs5__default.default.rmSync(dir, { recursive: true });
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
function scrollToBottomAndBackToTop({
|
|
@@ -424,8 +424,13 @@ var REQUEST_TIMEOUT = 1e4;
|
|
|
424
424
|
var MIN_VIEWPORT_HEIGHT = 1080;
|
|
425
425
|
var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function* () {
|
|
426
426
|
ctx.log.debug(`Processing snapshot ${snapshot.name}`);
|
|
427
|
-
if (!ctx.browser)
|
|
428
|
-
|
|
427
|
+
if (!ctx.browser) {
|
|
428
|
+
let launchOptions = { headless: true };
|
|
429
|
+
if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
|
|
430
|
+
launchOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
|
|
431
|
+
ctx.browser = yield test.chromium.launch(launchOptions);
|
|
432
|
+
ctx.log.debug(`Chromium launched with options ${JSON.stringify(launchOptions)}`);
|
|
433
|
+
}
|
|
429
434
|
const context = yield ctx.browser.newContext({ userAgent: constants_default.CHROME_USER_AGENT });
|
|
430
435
|
const page = yield context.newPage();
|
|
431
436
|
let cache = {};
|
|
@@ -433,6 +438,9 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
|
|
|
433
438
|
const requestUrl = request.url();
|
|
434
439
|
const requestHostname = new URL(requestUrl).hostname;
|
|
435
440
|
try {
|
|
441
|
+
if (/\.(mp3|mp4|wav|ogg|webm)$/i.test(request.url())) {
|
|
442
|
+
throw new Error("resource type mp3/mp4/wav/ogg/webm");
|
|
443
|
+
}
|
|
436
444
|
ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
|
|
437
445
|
if (ctx.config.enableJavaScript)
|
|
438
446
|
ALLOWED_RESOURCES.push("script");
|
|
@@ -539,14 +547,21 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
|
|
|
539
547
|
yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
|
|
540
548
|
ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
|
|
541
549
|
if (!navigated) {
|
|
542
|
-
yield page.goto(snapshot.url);
|
|
550
|
+
yield page.goto(snapshot.url, { waitUntil: "domcontentloaded" });
|
|
551
|
+
yield new Promise((r) => setTimeout(r, 1250));
|
|
552
|
+
if (ctx.config.waitForTimeout)
|
|
553
|
+
yield page.waitForTimeout(ctx.config.waitForTimeout);
|
|
543
554
|
navigated = true;
|
|
544
555
|
ctx.log.debug(`Navigated to ${snapshot.url}`);
|
|
545
556
|
}
|
|
546
557
|
if (fullPage)
|
|
547
558
|
yield page.evaluate(scrollToBottomAndBackToTop);
|
|
548
|
-
|
|
549
|
-
|
|
559
|
+
try {
|
|
560
|
+
yield page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
561
|
+
ctx.log.debug("Network idle 500ms");
|
|
562
|
+
} catch (error) {
|
|
563
|
+
ctx.log.debug(`Network idle failed due to ${error}`);
|
|
564
|
+
}
|
|
550
565
|
if (processedOptions.element) {
|
|
551
566
|
let l = yield page.locator(processedOptions.element).all();
|
|
552
567
|
if (l.length === 0) {
|
|
@@ -580,6 +595,14 @@ var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function*
|
|
|
580
595
|
}
|
|
581
596
|
yield page.close();
|
|
582
597
|
yield context.close();
|
|
598
|
+
if (snapshot.dom.resources.length) {
|
|
599
|
+
for (let resource of snapshot.dom.resources) {
|
|
600
|
+
cache[resource.url] = {
|
|
601
|
+
body: resource.content,
|
|
602
|
+
type: resource.mimetype
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
583
606
|
return {
|
|
584
607
|
processedSnapshot: {
|
|
585
608
|
name: snapshot.name,
|
|
@@ -862,7 +885,7 @@ var validateSnapshot = ajv.compile(SnapshotSchema);
|
|
|
862
885
|
var server_default = (ctx) => __async(void 0, null, function* () {
|
|
863
886
|
const server = fastify__default.default({ logger: false, bodyLimit: 1e7 });
|
|
864
887
|
const opts = {};
|
|
865
|
-
const SMARTUI_DOM =
|
|
888
|
+
const SMARTUI_DOM = fs5.readFileSync(path2__default.default.resolve(__dirname, "dom-serializer.js"), "utf-8");
|
|
866
889
|
server.get("/healthcheck", opts, (_, reply) => {
|
|
867
890
|
reply.code(200).send({ cliVersion: ctx.cliVersion });
|
|
868
891
|
});
|
|
@@ -882,7 +905,7 @@ var server_default = (ctx) => __async(void 0, null, function* () {
|
|
|
882
905
|
return reply.code(500).send({ error: { message: error.message } });
|
|
883
906
|
}
|
|
884
907
|
}));
|
|
885
|
-
yield server.listen();
|
|
908
|
+
yield server.listen({ port: 49152 });
|
|
886
909
|
let { port } = server.addresses()[0];
|
|
887
910
|
process.env.SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
|
|
888
911
|
process.env.CYPRESS_SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
|
|
@@ -896,6 +919,9 @@ var env_default = () => {
|
|
|
896
919
|
SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
|
|
897
920
|
LT_SDK_LOG_LEVEL,
|
|
898
921
|
LT_SDK_DEBUG,
|
|
922
|
+
SMARTUI_GIT_INFO_FILEPATH,
|
|
923
|
+
HTTP_PROXY,
|
|
924
|
+
HTTPS_PROXY,
|
|
899
925
|
GITHUB_ACTIONS
|
|
900
926
|
} = process.env;
|
|
901
927
|
return {
|
|
@@ -903,6 +929,9 @@ var env_default = () => {
|
|
|
903
929
|
SMARTUI_CLIENT_API_URL,
|
|
904
930
|
LT_SDK_LOG_LEVEL,
|
|
905
931
|
LT_SDK_DEBUG,
|
|
932
|
+
SMARTUI_GIT_INFO_FILEPATH,
|
|
933
|
+
HTTP_PROXY,
|
|
934
|
+
HTTPS_PROXY,
|
|
906
935
|
GITHUB_ACTIONS
|
|
907
936
|
};
|
|
908
937
|
};
|
|
@@ -936,7 +965,7 @@ var logger = winston.createLogger({
|
|
|
936
965
|
return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
|
|
937
966
|
})
|
|
938
967
|
),
|
|
939
|
-
transports: [new winston.transports.Console()]
|
|
968
|
+
transports: [new winston.transports.Console(), new winston.transports.File({ filename: ".smartui.log" })]
|
|
940
969
|
});
|
|
941
970
|
var logger_default = logger;
|
|
942
971
|
|
|
@@ -980,7 +1009,7 @@ var auth_default = (ctx) => {
|
|
|
980
1009
|
};
|
|
981
1010
|
|
|
982
1011
|
// package.json
|
|
983
|
-
var version = "3.0.
|
|
1012
|
+
var version = "3.0.5";
|
|
984
1013
|
var package_default = {
|
|
985
1014
|
name: "@lambdatest/smartui-cli",
|
|
986
1015
|
version,
|
|
@@ -1053,7 +1082,7 @@ var httpClient = class {
|
|
|
1053
1082
|
headers: error.response.headers,
|
|
1054
1083
|
body: error.response.data
|
|
1055
1084
|
})}`);
|
|
1056
|
-
throw new Error((_a = error.response.data.error) == null ? void 0 : _a.message);
|
|
1085
|
+
throw new Error(((_a = error.response.data.error) == null ? void 0 : _a.message) || error.response.data.message);
|
|
1057
1086
|
}
|
|
1058
1087
|
if (error.request) {
|
|
1059
1088
|
log.debug(`http request failed: ${error.toJSON()}`);
|
|
@@ -1106,7 +1135,7 @@ var httpClient = class {
|
|
|
1106
1135
|
}
|
|
1107
1136
|
uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log) {
|
|
1108
1137
|
browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
|
|
1109
|
-
const file =
|
|
1138
|
+
const file = fs5__default.default.readFileSync(ssPath);
|
|
1110
1139
|
const form = new FormData__default.default();
|
|
1111
1140
|
form.append("screenshot", file, { filename: `${ssName}.png`, contentType: "image/png" });
|
|
1112
1141
|
form.append("browser", browserName);
|
|
@@ -1152,7 +1181,7 @@ var ctx_default = (options) => {
|
|
|
1152
1181
|
let config = constants_default.DEFAULT_CONFIG;
|
|
1153
1182
|
try {
|
|
1154
1183
|
if (options.config) {
|
|
1155
|
-
config = JSON.parse(
|
|
1184
|
+
config = JSON.parse(fs5__default.default.readFileSync(options.config, "utf-8"));
|
|
1156
1185
|
if ((_a = config.web) == null ? void 0 : _a.resolutions) {
|
|
1157
1186
|
config.web.viewports = config.web.resolutions;
|
|
1158
1187
|
delete config.web.resolutions;
|
|
@@ -1232,26 +1261,37 @@ function isGitRepo() {
|
|
|
1232
1261
|
}
|
|
1233
1262
|
}
|
|
1234
1263
|
var git_default = (ctx) => {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1264
|
+
if (ctx.env.SMARTUI_GIT_INFO_FILEPATH) {
|
|
1265
|
+
let gitInfo = JSON.parse(fs5__default.default.readFileSync(ctx.env.SMARTUI_GIT_INFO_FILEPATH, "utf-8"));
|
|
1266
|
+
return {
|
|
1267
|
+
branch: gitInfo.branch || "",
|
|
1268
|
+
commitId: gitInfo.commit_id.slice(0, 6) || "",
|
|
1269
|
+
commitMessage: gitInfo.commit_body || "",
|
|
1270
|
+
commitAuthor: gitInfo.commit_author || "",
|
|
1271
|
+
githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${gitInfo.commit_id}` : ""
|
|
1272
|
+
};
|
|
1273
|
+
} else {
|
|
1274
|
+
const splitCharacter = "<##>";
|
|
1275
|
+
const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
|
|
1276
|
+
const command3 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
|
|
1277
|
+
let res = executeCommand(command3).split(splitCharacter);
|
|
1278
|
+
var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
|
|
1279
|
+
var branch = branchAndTags[0];
|
|
1280
|
+
branchAndTags.slice(1);
|
|
1281
|
+
return {
|
|
1282
|
+
branch: branch || "",
|
|
1283
|
+
commitId: res[0] || "",
|
|
1284
|
+
commitMessage: res[2] || "",
|
|
1285
|
+
commitAuthor: res[7] || "",
|
|
1286
|
+
githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${res[1]}` : ""
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1249
1289
|
};
|
|
1250
1290
|
var getGitInfo_default = (ctx) => {
|
|
1251
1291
|
return {
|
|
1252
1292
|
title: `Fetching git repo details`,
|
|
1253
1293
|
skip: (ctx2) => {
|
|
1254
|
-
return !isGitRepo() ? "[SKIPPED] Fetching git repo details; not a git repo" : "";
|
|
1294
|
+
return !isGitRepo() && !ctx2.env.SMARTUI_GIT_INFO_FILEPATH ? "[SKIPPED] Fetching git repo details; not a git repo" : "";
|
|
1255
1295
|
},
|
|
1256
1296
|
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1257
1297
|
try {
|
|
@@ -1397,13 +1437,13 @@ function createConfig(filepath) {
|
|
|
1397
1437
|
console.log("Error: Config file must have .json extension");
|
|
1398
1438
|
return;
|
|
1399
1439
|
}
|
|
1400
|
-
if (
|
|
1440
|
+
if (fs5__default.default.existsSync(filepath)) {
|
|
1401
1441
|
console.log(`Error: SmartUI Config already exists: ${filepath}`);
|
|
1402
1442
|
console.log(`To create a new file, please specify the file name like: 'smartui config:create .smartui-config.json'`);
|
|
1403
1443
|
return;
|
|
1404
1444
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1445
|
+
fs5__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
|
|
1446
|
+
fs5__default.default.writeFileSync(filepath, JSON.stringify(constants_default.DEFAULT_CONFIG, null, 2) + "\n");
|
|
1407
1447
|
console.log(`Created SmartUI Config: ${filepath}`);
|
|
1408
1448
|
}
|
|
1409
1449
|
function createWebStaticConfig(filepath) {
|
|
@@ -1413,13 +1453,13 @@ function createWebStaticConfig(filepath) {
|
|
|
1413
1453
|
console.log("Error: Config file must have .json extension");
|
|
1414
1454
|
return;
|
|
1415
1455
|
}
|
|
1416
|
-
if (
|
|
1456
|
+
if (fs5__default.default.existsSync(filepath)) {
|
|
1417
1457
|
console.log(`Error: web-static config already exists: ${filepath}`);
|
|
1418
1458
|
console.log(`To create a new file, please specify the file name like: 'smartui config:create-web-static links.json'`);
|
|
1419
1459
|
return;
|
|
1420
1460
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1461
|
+
fs5__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
|
|
1462
|
+
fs5__default.default.writeFileSync(filepath, JSON.stringify(constants_default.DEFAULT_WEB_STATIC_CONFIG, null, 2) + "\n");
|
|
1423
1463
|
console.log(`Created web-static config: ${filepath}`);
|
|
1424
1464
|
}
|
|
1425
1465
|
|
|
@@ -1577,12 +1617,12 @@ var command2 = new commander.Command();
|
|
|
1577
1617
|
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) {
|
|
1578
1618
|
return __async(this, null, function* () {
|
|
1579
1619
|
let ctx = ctx_default(command3.optsWithGlobals());
|
|
1580
|
-
if (!
|
|
1620
|
+
if (!fs5__default.default.existsSync(file)) {
|
|
1581
1621
|
console.log(`Error: Web Static Config file ${file} not found.`);
|
|
1582
1622
|
return;
|
|
1583
1623
|
}
|
|
1584
1624
|
try {
|
|
1585
|
-
ctx.webStaticConfig = JSON.parse(
|
|
1625
|
+
ctx.webStaticConfig = JSON.parse(fs5__default.default.readFileSync(file, "utf8"));
|
|
1586
1626
|
if (!validateWebStaticConfig(ctx.webStaticConfig))
|
|
1587
1627
|
throw new Error(validateWebStaticConfig.errors[0].message);
|
|
1588
1628
|
} catch (error) {
|