@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.
@@ -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
- // if the canvas produces a blank image, skip
352
- if (!dataUrl || dataUrl === 'data:,') continue;
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
- // create a resource from the serialized data url
355
- // let resource = resourceFromDataURL(videoId, dataUrl);
356
- // resources.add(resource);
354
+ // if the canvas produces a blank image, skip
355
+ if (!dataUrl || dataUrl === 'data:,') continue;
357
356
 
358
- // use a data attribute to avoid making a real request
359
- cloneEl.removeAttribute('src');
360
- cloneEl.setAttribute('poster', dataUrl);
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 fs4 = require('fs');
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 fs4__default = /*#__PURE__*/_interopDefault(fs4);
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 (fs4__default.default.existsSync(dir)) {
308
- fs4__default.default.rmSync(dir, { recursive: true });
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
- ctx.browser = yield test.chromium.launch({ headless: true });
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
- yield page.waitForLoadState("networkidle");
549
- ctx.log.debug("Network idle 500ms");
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 = fs4.readFileSync(path2__default.default.resolve(__dirname, "dom-serializer.js"), "utf-8");
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.3";
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 = fs4__default.default.readFileSync(ssPath);
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(fs4__default.default.readFileSync(options.config, "utf-8"));
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
- const splitCharacter = "<##>";
1236
- const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
1237
- const command3 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
1238
- let res = executeCommand(command3).split(splitCharacter);
1239
- var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
1240
- var branch = branchAndTags[0];
1241
- branchAndTags.slice(1);
1242
- return {
1243
- branch,
1244
- commitId: res[0] || "",
1245
- commitMessage: res[2] || "",
1246
- commitAuthor: res[7] || "",
1247
- githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${res[1]}` : ""
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 (fs4__default.default.existsSync(filepath)) {
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
- fs4__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
1406
- fs4__default.default.writeFileSync(filepath, JSON.stringify(constants_default.DEFAULT_CONFIG, null, 2) + "\n");
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 (fs4__default.default.existsSync(filepath)) {
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
- fs4__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
1422
- fs4__default.default.writeFileSync(filepath, JSON.stringify(constants_default.DEFAULT_WEB_STATIC_CONFIG, null, 2) + "\n");
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 (!fs4__default.default.existsSync(file)) {
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(fs4__default.default.readFileSync(file, "utf8"));
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdatest/smartui-cli",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"