@lambdatest/smartui-cli 4.0.7 → 4.0.9
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/README.md +68 -0
- package/dist/index.cjs +367 -151
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SmartUI-CLI
|
|
2
|
+
|
|
3
|
+
<img height="400" src="https://user-images.githubusercontent.com/126776938/232535511-8d51cf1b-1a33-48fc-825c-b13e7a9ec388.png">
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.lambdatest.com/blog/?utm_source=github&utm_medium=repo&utm_campaign=playwright-sample" target="_bank">Blog</a>
|
|
7
|
+
⋅
|
|
8
|
+
<a href="https://www.lambdatest.com/support/docs/?utm_source=github&utm_medium=repo&utm_campaign=playwright-sample" target="_bank">Docs</a>
|
|
9
|
+
⋅
|
|
10
|
+
<a href="https://www.lambdatest.com/learning-hub/?utm_source=github&utm_medium=repo&utm_campaign=playwright-sample" target="_bank">Learning Hub</a>
|
|
11
|
+
⋅
|
|
12
|
+
<a href="https://www.lambdatest.com/newsletter/?utm_source=github&utm_medium=repo&utm_campaign=playwright-sample" target="_bank">Newsletter</a>
|
|
13
|
+
⋅
|
|
14
|
+
<a href="https://www.lambdatest.com/certifications/?utm_source=github&utm_medium=repo&utm_campaign=playwright-sample" target="_bank">Certifications</a>
|
|
15
|
+
⋅
|
|
16
|
+
<a href="https://www.youtube.com/c/LambdaTest" target="_bank">YouTube</a>
|
|
17
|
+
</p>
|
|
18
|
+
 
|
|
19
|
+
 
|
|
20
|
+
 
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
[<img height="58" width="200" src="https://user-images.githubusercontent.com/70570645/171866795-52c11b49-0728-4229-b073-4b704209ddde.png">](https://accounts.lambdatest.com/register?utm_source=github&utm_medium=repo&utm_campaign=playwright-sample)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
The **SmartUI-CLI** allows you to capture visual snapshots of your web applications, upload images, and run visual regression tests using [LambdaTest's SmartUI](https://www.lambdatest.com/visual-regression-testing) platform directly from the command line.
|
|
28
|
+
|
|
29
|
+
- [Installation](#installation)
|
|
30
|
+
- [Commands](#commands)
|
|
31
|
+
- [Documentation](#documentation)
|
|
32
|
+
- [Issues](#issues)
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```sh-session
|
|
37
|
+
$ npm install smartui-cli
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Note:**
|
|
41
|
+
If you face any problems executing tests with SmartUI-CLI `versions >= v4.x.x`, upgrade your Node.js version to `v20.3` or above.
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
- `npx smartui exec` - Capture DOM assets for visual testing across multiple browsers and resolutions.
|
|
45
|
+
- `npx smartui capture` - Bulk capture static URLs for visual testing.
|
|
46
|
+
- `npx smartui upload` - Upload custom images or screenshots for visual comparison.
|
|
47
|
+
- `npx smartui upload-figma` - Upload Figma design images for visual comparison.
|
|
48
|
+
- `npx smartui config` - Creates configuration file according to the usecase.
|
|
49
|
+
|
|
50
|
+
### Documentation
|
|
51
|
+
|
|
52
|
+
In addition to its core functionalities, the SmartUI CLI leverages LambdaTest's cloud infrastructure for robust, scalable visual regression testing across various browsers and devices.
|
|
53
|
+
|
|
54
|
+
- [SmartUI Selenium SDK](https://www.lambdatest.com/support/docs/smartui-selenium-java-sdk) - A complete SDK to capture DOM assets for visual tests.
|
|
55
|
+
- [LambdaTest Documentation](https://www.lambdatest.com/support/docs/) - Official LambdaTest documentation for SmartUI and other integrations.
|
|
56
|
+
- [Bulk capturing static URLs with SmartUI](https://www.lambdatest.com/support/docs/smartui-cli/) - Documentation for capturing satatic urls in bulk with SmartUI
|
|
57
|
+
- [Bring your own screenshots](https://www.lambdatest.com/support/docs/smartui-cli-upload/) - Documentation for capturing satatic urls in bulk
|
|
58
|
+
- [Figma CLI](https://www.lambdatest.com/support/docs/smartui-cli-figma/) - Documentation for uploading figma components to SmartUI
|
|
59
|
+
|
|
60
|
+
### Issues
|
|
61
|
+
|
|
62
|
+
If you encounter problems with SmartUI-CLI, [add an issue on GitHub](https://github.com/LambdaTest/smartui-cli/issues/new).
|
|
63
|
+
|
|
64
|
+
For other support issues, reach out via [LambdaTest Support](https://www.lambdatest.com/support).
|
|
65
|
+
|
|
66
|
+
------
|
|
67
|
+
|
|
68
|
+
[Know more](https://www.lambdatest.com/visual-regression-testing) about SmartUI and it's AI enabled comparison engines.
|
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var which = require('which');
|
|
6
6
|
var listr2 = require('listr2');
|
|
7
|
-
var
|
|
7
|
+
var chalk6 = require('chalk');
|
|
8
8
|
var path2 = require('path');
|
|
9
9
|
var fastify = require('fastify');
|
|
10
10
|
var fs5 = require('fs');
|
|
@@ -21,7 +21,7 @@ var sharp = require('sharp');
|
|
|
21
21
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
22
|
|
|
23
23
|
var which__default = /*#__PURE__*/_interopDefault(which);
|
|
24
|
-
var
|
|
24
|
+
var chalk6__default = /*#__PURE__*/_interopDefault(chalk6);
|
|
25
25
|
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
26
26
|
var fastify__default = /*#__PURE__*/_interopDefault(fastify);
|
|
27
27
|
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
@@ -187,6 +187,9 @@ var constants_default = {
|
|
|
187
187
|
MOBILE_OS_IOS: "ios",
|
|
188
188
|
MOBILE_ORIENTATION_PORTRAIT: "portrait",
|
|
189
189
|
MOBILE_ORIENTATION_LANDSCAPE: "landscape",
|
|
190
|
+
// build status
|
|
191
|
+
BUILD_COMPLETE: "completed",
|
|
192
|
+
BUILD_ERROR: "error",
|
|
190
193
|
// CI
|
|
191
194
|
GITHUB_API_HOST: "https://api.github.com",
|
|
192
195
|
// log file path
|
|
@@ -196,6 +199,8 @@ var constants_default = {
|
|
|
196
199
|
FILE_EXTENSION_GIFS: "gif",
|
|
197
200
|
// Default scrollTime
|
|
198
201
|
DEFAULT_SCROLL_TIME: 8,
|
|
202
|
+
// Default page load time
|
|
203
|
+
DEFAULT_PAGE_LOAD_TIMEOUT: 18e4,
|
|
199
204
|
// Magic Numbers
|
|
200
205
|
MAGIC_NUMBERS: [
|
|
201
206
|
{ ext: "jpg", magic: Buffer.from([255, 216, 255]) },
|
|
@@ -888,7 +893,7 @@ var logger = winston.createLogger({
|
|
|
888
893
|
let message = typeof info.message === "object" ? JSON.stringify(info.message).trim() : info.message.trim();
|
|
889
894
|
switch (info.level) {
|
|
890
895
|
case "warn":
|
|
891
|
-
message =
|
|
896
|
+
message = chalk6__default.default.yellow(message);
|
|
892
897
|
break;
|
|
893
898
|
}
|
|
894
899
|
return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
|
|
@@ -915,11 +920,11 @@ var startServer_default = (ctx) => {
|
|
|
915
920
|
updateLogContext({ task: "startServer" });
|
|
916
921
|
try {
|
|
917
922
|
ctx2.server = yield server_default(ctx2);
|
|
918
|
-
task.output =
|
|
923
|
+
task.output = chalk6__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
|
|
919
924
|
task.title = "SmartUI started";
|
|
920
925
|
} catch (error) {
|
|
921
926
|
ctx2.log.debug(error);
|
|
922
|
-
task.output =
|
|
927
|
+
task.output = chalk6__default.default.gray(error.message);
|
|
923
928
|
throw new Error("SmartUI server setup failed");
|
|
924
929
|
}
|
|
925
930
|
}),
|
|
@@ -933,11 +938,11 @@ var auth_default = (ctx) => {
|
|
|
933
938
|
updateLogContext({ task: "auth" });
|
|
934
939
|
try {
|
|
935
940
|
yield ctx2.client.auth(ctx2.log);
|
|
936
|
-
task.output =
|
|
941
|
+
task.output = chalk6__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
|
|
937
942
|
task.title = "Authenticated with SmartUI";
|
|
938
943
|
} catch (error) {
|
|
939
944
|
ctx2.log.debug(error);
|
|
940
|
-
task.output =
|
|
945
|
+
task.output = chalk6__default.default.gray(error.message);
|
|
941
946
|
throw new Error("Authentication failed");
|
|
942
947
|
}
|
|
943
948
|
}),
|
|
@@ -946,7 +951,7 @@ var auth_default = (ctx) => {
|
|
|
946
951
|
};
|
|
947
952
|
|
|
948
953
|
// package.json
|
|
949
|
-
var version = "4.0.
|
|
954
|
+
var version = "4.0.9";
|
|
950
955
|
var package_default = {
|
|
951
956
|
name: "@lambdatest/smartui-cli",
|
|
952
957
|
version,
|
|
@@ -956,7 +961,8 @@ var package_default = {
|
|
|
956
961
|
],
|
|
957
962
|
scripts: {
|
|
958
963
|
build: "tsup",
|
|
959
|
-
release: "pnpm run build && pnpm publish --access public --no-git-checks"
|
|
964
|
+
release: "pnpm run build && pnpm publish --access public --no-git-checks",
|
|
965
|
+
"local-build": "pnpm run build && pnpm pack"
|
|
960
966
|
},
|
|
961
967
|
bin: {
|
|
962
968
|
smartui: "./dist/index.cjs"
|
|
@@ -1047,6 +1053,13 @@ var httpClient = class {
|
|
|
1047
1053
|
}
|
|
1048
1054
|
}, log2);
|
|
1049
1055
|
}
|
|
1056
|
+
getScreenshotData(buildId, baseline, log2) {
|
|
1057
|
+
return this.request({
|
|
1058
|
+
url: "/screenshot",
|
|
1059
|
+
method: "GET",
|
|
1060
|
+
params: { buildId, baseline }
|
|
1061
|
+
}, log2);
|
|
1062
|
+
}
|
|
1050
1063
|
finalizeBuild(buildId, totalSnapshots, log2) {
|
|
1051
1064
|
let params = { buildId };
|
|
1052
1065
|
if (totalSnapshots > -1)
|
|
@@ -1090,13 +1103,13 @@ var httpClient = class {
|
|
|
1090
1103
|
}).then(() => {
|
|
1091
1104
|
log2.debug(`${ssName} for ${browserName} ${viewport} uploaded successfully`);
|
|
1092
1105
|
}).catch((error) => {
|
|
1093
|
-
|
|
1106
|
+
log2.error(`Unable to upload screenshot ${JSON.stringify(error)}`);
|
|
1107
|
+
if (error && error.response && error.response.data && error.response.data.error) {
|
|
1094
1108
|
throw new Error(error.response.data.error.message);
|
|
1095
1109
|
}
|
|
1096
|
-
if (error
|
|
1097
|
-
throw new Error(
|
|
1110
|
+
if (error) {
|
|
1111
|
+
throw new Error(JSON.stringify(error));
|
|
1098
1112
|
}
|
|
1099
|
-
throw new Error(error.message);
|
|
1100
1113
|
});
|
|
1101
1114
|
}
|
|
1102
1115
|
checkUpdate(log2) {
|
|
@@ -1169,6 +1182,9 @@ var ctx_default = (options) => {
|
|
|
1169
1182
|
let extensionFiles;
|
|
1170
1183
|
let ignoreStripExtension;
|
|
1171
1184
|
let ignoreFilePattern;
|
|
1185
|
+
let parallelObj;
|
|
1186
|
+
let fetchResultObj;
|
|
1187
|
+
let fetchResultsFileObj;
|
|
1172
1188
|
try {
|
|
1173
1189
|
if (options.config) {
|
|
1174
1190
|
config = JSON.parse(fs5__default.default.readFileSync(options.config, "utf-8"));
|
|
@@ -1188,6 +1204,18 @@ var ctx_default = (options) => {
|
|
|
1188
1204
|
extensionFiles = options.files || ["png", "jpeg", "jpg"];
|
|
1189
1205
|
ignoreStripExtension = options.removeExtensions || false;
|
|
1190
1206
|
ignoreFilePattern = options.ignoreDir || [];
|
|
1207
|
+
parallelObj = options.parallel ? options.parallel === true ? 1 : options.parallel : 1;
|
|
1208
|
+
if (options.fetchResults) {
|
|
1209
|
+
if (options.fetchResults !== true && !options.fetchResults.endsWith(".json")) {
|
|
1210
|
+
console.error("Error: The file extension for --fetch-results must be .json");
|
|
1211
|
+
process.exit(1);
|
|
1212
|
+
}
|
|
1213
|
+
fetchResultObj = true;
|
|
1214
|
+
fetchResultsFileObj = options.fetchResults === true ? "results.json" : options.fetchResults;
|
|
1215
|
+
} else {
|
|
1216
|
+
fetchResultObj = false;
|
|
1217
|
+
fetchResultsFileObj = "";
|
|
1218
|
+
}
|
|
1191
1219
|
} catch (error) {
|
|
1192
1220
|
console.log(`[smartui] Error: ${error.message}`);
|
|
1193
1221
|
process.exit();
|
|
@@ -1241,14 +1269,17 @@ var ctx_default = (options) => {
|
|
|
1241
1269
|
},
|
|
1242
1270
|
args: {},
|
|
1243
1271
|
options: {
|
|
1244
|
-
parallel:
|
|
1272
|
+
parallel: parallelObj,
|
|
1273
|
+
force: options.force ? true : false,
|
|
1245
1274
|
markBaseline: options.markBaseline ? true : false,
|
|
1246
1275
|
buildName: options.buildName || "",
|
|
1247
1276
|
port,
|
|
1248
1277
|
ignoreResolutions: resolutionOff,
|
|
1249
1278
|
fileExtension: extensionFiles,
|
|
1250
1279
|
stripExtension: ignoreStripExtension,
|
|
1251
|
-
ignorePattern: ignoreFilePattern
|
|
1280
|
+
ignorePattern: ignoreFilePattern,
|
|
1281
|
+
fetchResults: fetchResultObj,
|
|
1282
|
+
fetchResultsFileName: fetchResultsFileObj
|
|
1252
1283
|
},
|
|
1253
1284
|
cliVersion: version,
|
|
1254
1285
|
totalSnapshots: -1
|
|
@@ -1315,11 +1346,11 @@ var getGitInfo_default = (ctx) => {
|
|
|
1315
1346
|
}
|
|
1316
1347
|
try {
|
|
1317
1348
|
ctx2.git = git_default(ctx2);
|
|
1318
|
-
task.output =
|
|
1349
|
+
task.output = chalk6__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
|
|
1319
1350
|
task.title = "Fetched git information";
|
|
1320
1351
|
} catch (error) {
|
|
1321
1352
|
ctx2.log.debug(error);
|
|
1322
|
-
task.output =
|
|
1353
|
+
task.output = chalk6__default.default.gray(`${error.message}`);
|
|
1323
1354
|
throw new Error("Error fetching git repo details");
|
|
1324
1355
|
}
|
|
1325
1356
|
}),
|
|
@@ -1339,127 +1370,18 @@ var createBuild_default = (ctx) => {
|
|
|
1339
1370
|
url: resp.data.buildURL,
|
|
1340
1371
|
baseline: resp.data.baseline
|
|
1341
1372
|
};
|
|
1342
|
-
task.output =
|
|
1373
|
+
task.output = chalk6__default.default.gray(`build id: ${resp.data.buildId}`);
|
|
1343
1374
|
task.title = "SmartUI build created";
|
|
1344
1375
|
} catch (error) {
|
|
1345
1376
|
ctx2.log.debug(error);
|
|
1346
|
-
task.output =
|
|
1377
|
+
task.output = chalk6__default.default.gray(error.message);
|
|
1347
1378
|
throw new Error("SmartUI build creation failed");
|
|
1348
1379
|
}
|
|
1349
1380
|
}),
|
|
1350
1381
|
rendererOptions: { persistentOutput: true }
|
|
1351
1382
|
};
|
|
1352
1383
|
};
|
|
1353
|
-
var
|
|
1354
|
-
var _a;
|
|
1355
|
-
return {
|
|
1356
|
-
title: `Executing '${(_a = ctx.args.execCommand) == null ? void 0 : _a.join(" ")}'`,
|
|
1357
|
-
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1358
|
-
updateLogContext({ task: "exec" });
|
|
1359
|
-
return new Promise((resolve, reject) => {
|
|
1360
|
-
var _a2, _b, _c;
|
|
1361
|
-
const childProcess = spawn__default.default(ctx2.args.execCommand[0], (_a2 = ctx2.args.execCommand) == null ? void 0 : _a2.slice(1));
|
|
1362
|
-
let totalOutput = "";
|
|
1363
|
-
const output = listr2.createWritable((chunk) => {
|
|
1364
|
-
totalOutput += chunk;
|
|
1365
|
-
task.output = chalk7__default.default.gray(totalOutput);
|
|
1366
|
-
});
|
|
1367
|
-
(_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
|
|
1368
|
-
(_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
|
|
1369
|
-
childProcess.on("error", (error) => {
|
|
1370
|
-
var _a3;
|
|
1371
|
-
task.output = chalk7__default.default.gray(`error: ${error.message}`);
|
|
1372
|
-
throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
|
|
1373
|
-
});
|
|
1374
|
-
childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
|
|
1375
|
-
var _a3;
|
|
1376
|
-
if (code !== null) {
|
|
1377
|
-
task.title = `Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' completed; exited with code ${code}`;
|
|
1378
|
-
} else if (signal !== null) {
|
|
1379
|
-
throw new Error(`Child process killed with signal ${signal}`);
|
|
1380
|
-
}
|
|
1381
|
-
resolve();
|
|
1382
|
-
}));
|
|
1383
|
-
});
|
|
1384
|
-
}),
|
|
1385
|
-
rendererOptions: { persistentOutput: true },
|
|
1386
|
-
exitOnError: false
|
|
1387
|
-
};
|
|
1388
|
-
};
|
|
1389
|
-
var processSnapshot_default = (ctx) => {
|
|
1390
|
-
return {
|
|
1391
|
-
title: `Processing snapshots`,
|
|
1392
|
-
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1393
|
-
var _a, _b;
|
|
1394
|
-
try {
|
|
1395
|
-
if (ctx2.config.delayedUpload) {
|
|
1396
|
-
ctx2.log.debug("started after processing because of delayedUpload");
|
|
1397
|
-
(_a = ctx2.snapshotQueue) == null ? void 0 : _a.startProcessingfunc();
|
|
1398
|
-
}
|
|
1399
|
-
yield new Promise((resolve) => {
|
|
1400
|
-
let output2 = "";
|
|
1401
|
-
const intervalId = setInterval(() => {
|
|
1402
|
-
var _a2, _b2, _c;
|
|
1403
|
-
if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b2 = ctx2.snapshotQueue) == null ? void 0 : _b2.isProcessing())) {
|
|
1404
|
-
clearInterval(intervalId);
|
|
1405
|
-
resolve();
|
|
1406
|
-
} else {
|
|
1407
|
-
task.title = `Processing snapshot ${(_c = ctx2.snapshotQueue) == null ? void 0 : _c.getProcessingSnapshot()}`;
|
|
1408
|
-
}
|
|
1409
|
-
}, 500);
|
|
1410
|
-
});
|
|
1411
|
-
let output = "";
|
|
1412
|
-
for (let snapshot of (_b = ctx2.snapshotQueue) == null ? void 0 : _b.getProcessedSnapshots()) {
|
|
1413
|
-
if (snapshot.error)
|
|
1414
|
-
output += `${chalk7__default.default.red("\u2717")} ${chalk7__default.default.gray(`${snapshot.name}
|
|
1415
|
-
[error] ${snapshot.error}`)}
|
|
1416
|
-
`;
|
|
1417
|
-
else
|
|
1418
|
-
output += `${chalk7__default.default.green("\u2713")} ${chalk7__default.default.gray(snapshot.name)}
|
|
1419
|
-
${snapshot.warnings.length ? chalk7__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
|
|
1420
|
-
`) : ""}`;
|
|
1421
|
-
}
|
|
1422
|
-
task.output = output;
|
|
1423
|
-
task.title = "Processed snapshots";
|
|
1424
|
-
} catch (error) {
|
|
1425
|
-
ctx2.log.debug(error);
|
|
1426
|
-
task.output = chalk7__default.default.gray(error.message);
|
|
1427
|
-
throw new Error("Processing of snapshots failed");
|
|
1428
|
-
}
|
|
1429
|
-
}),
|
|
1430
|
-
rendererOptions: { persistentOutput: true }
|
|
1431
|
-
};
|
|
1432
|
-
};
|
|
1433
|
-
var finalizeBuild_default = (ctx) => {
|
|
1434
|
-
return {
|
|
1435
|
-
title: `Finalizing build`,
|
|
1436
|
-
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1437
|
-
var _a, _b;
|
|
1438
|
-
updateLogContext({ task: "finalizeBuild" });
|
|
1439
|
-
try {
|
|
1440
|
-
yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
|
|
1441
|
-
task.output = chalk7__default.default.gray(`build url: ${ctx2.build.url}`);
|
|
1442
|
-
task.title = "Finalized build";
|
|
1443
|
-
} catch (error) {
|
|
1444
|
-
ctx2.log.debug(error);
|
|
1445
|
-
task.output = chalk7__default.default.gray(error.message);
|
|
1446
|
-
throw new Error("Finalize build failed");
|
|
1447
|
-
}
|
|
1448
|
-
try {
|
|
1449
|
-
yield (_a = ctx2.browser) == null ? void 0 : _a.close();
|
|
1450
|
-
ctx2.log.debug(`Closed browser`);
|
|
1451
|
-
yield (_b = ctx2.server) == null ? void 0 : _b.close();
|
|
1452
|
-
ctx2.log.debug(`Closed server`);
|
|
1453
|
-
let resp = yield ctx2.client.getS3PreSignedURL(ctx2);
|
|
1454
|
-
yield ctx2.client.uploadLogs(ctx2, resp.data.url);
|
|
1455
|
-
fs5.unlinkSync(constants_default.LOG_FILE_PATH);
|
|
1456
|
-
} catch (error) {
|
|
1457
|
-
ctx2.log.debug(error);
|
|
1458
|
-
}
|
|
1459
|
-
}),
|
|
1460
|
-
rendererOptions: { persistentOutput: true }
|
|
1461
|
-
};
|
|
1462
|
-
};
|
|
1384
|
+
var isPollingActive = false;
|
|
1463
1385
|
function delDir(dir) {
|
|
1464
1386
|
if (fs5__default.default.existsSync(dir)) {
|
|
1465
1387
|
fs5__default.default.rmSync(dir, { recursive: true });
|
|
@@ -1493,8 +1415,10 @@ function scrollToBottomAndBackToTop({
|
|
|
1493
1415
|
}
|
|
1494
1416
|
function launchBrowsers(ctx) {
|
|
1495
1417
|
return __async(this, null, function* () {
|
|
1418
|
+
var _a;
|
|
1496
1419
|
let browsers = {};
|
|
1497
|
-
|
|
1420
|
+
const isHeadless = ((_a = process.env.HEADLESS) == null ? void 0 : _a.toLowerCase()) === "false" ? false : true;
|
|
1421
|
+
let launchOptions = { headless: isHeadless };
|
|
1498
1422
|
if (ctx.config.web) {
|
|
1499
1423
|
for (const browser of ctx.config.web.browsers) {
|
|
1500
1424
|
switch (browser) {
|
|
@@ -1634,6 +1558,197 @@ function getRenderViewportsForOptions(options) {
|
|
|
1634
1558
|
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1635
1559
|
];
|
|
1636
1560
|
}
|
|
1561
|
+
process.on("SIGINT", () => {
|
|
1562
|
+
if (isPollingActive) {
|
|
1563
|
+
console.log("Fetching results interrupted. Exiting...");
|
|
1564
|
+
isPollingActive = false;
|
|
1565
|
+
} else {
|
|
1566
|
+
console.log("\nExiting gracefully...");
|
|
1567
|
+
}
|
|
1568
|
+
process.exit(0);
|
|
1569
|
+
});
|
|
1570
|
+
function startPolling(ctx, task) {
|
|
1571
|
+
return __async(this, null, function* () {
|
|
1572
|
+
ctx.log.info("Fetching results in progress....");
|
|
1573
|
+
isPollingActive = true;
|
|
1574
|
+
const intervalId = setInterval(() => __async(this, null, function* () {
|
|
1575
|
+
if (!isPollingActive) {
|
|
1576
|
+
clearInterval(intervalId);
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
const resp = yield ctx.client.getScreenshotData(ctx.build.id, ctx.build.baseline, ctx.log);
|
|
1581
|
+
if (!resp.build) {
|
|
1582
|
+
ctx.log.info("Error: Build data is null.");
|
|
1583
|
+
clearInterval(intervalId);
|
|
1584
|
+
isPollingActive = false;
|
|
1585
|
+
}
|
|
1586
|
+
fs5__default.default.writeFileSync(ctx.options.fetchResultsFileName, JSON.stringify(resp, null, 2));
|
|
1587
|
+
ctx.log.debug(`Updated results in ${ctx.options.fetchResultsFileName}`);
|
|
1588
|
+
if (resp.build.build_status_ind === constants_default.BUILD_COMPLETE || resp.build.build_status_ind === constants_default.BUILD_ERROR) {
|
|
1589
|
+
clearInterval(intervalId);
|
|
1590
|
+
ctx.log.info(`Fetching results completed. Final results written to ${ctx.options.fetchResultsFileName}`);
|
|
1591
|
+
isPollingActive = false;
|
|
1592
|
+
let totalScreenshotsWithMismatches = 0;
|
|
1593
|
+
let totalVariantsWithMismatches = 0;
|
|
1594
|
+
const totalScreenshots = Object.keys(resp.screenshots || {}).length;
|
|
1595
|
+
let totalVariants = 0;
|
|
1596
|
+
for (const [screenshot, variants] of Object.entries(resp.screenshots || {})) {
|
|
1597
|
+
let screenshotHasMismatch = false;
|
|
1598
|
+
let variantMismatchCount = 0;
|
|
1599
|
+
totalVariants += variants.length;
|
|
1600
|
+
for (const variant of variants) {
|
|
1601
|
+
if (variant.mismatch_percentage > 0) {
|
|
1602
|
+
screenshotHasMismatch = true;
|
|
1603
|
+
variantMismatchCount++;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
if (screenshotHasMismatch) {
|
|
1607
|
+
totalScreenshotsWithMismatches++;
|
|
1608
|
+
totalVariantsWithMismatches += variantMismatchCount;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
ctx.log.info(
|
|
1612
|
+
chalk6__default.default.green.bold(
|
|
1613
|
+
`
|
|
1614
|
+
Summary of Mismatches:
|
|
1615
|
+
${chalk6__default.default.yellow("Total Variants with Mismatches:")} ${chalk6__default.default.white(totalVariantsWithMismatches)} out of ${chalk6__default.default.white(totalVariants)}
|
|
1616
|
+
${chalk6__default.default.yellow("Total Screenshots with Mismatches:")} ${chalk6__default.default.white(totalScreenshotsWithMismatches)} out of ${chalk6__default.default.white(totalScreenshots)}
|
|
1617
|
+
${chalk6__default.default.yellow("Branch Name:")} ${chalk6__default.default.white(resp.build.branch)}
|
|
1618
|
+
${chalk6__default.default.yellow("Project Name:")} ${chalk6__default.default.white(resp.project.name)}
|
|
1619
|
+
${chalk6__default.default.yellow("Build ID:")} ${chalk6__default.default.white(resp.build.build_id)}
|
|
1620
|
+
`
|
|
1621
|
+
)
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
} catch (error) {
|
|
1625
|
+
if (error.message.includes("ENOTFOUND")) {
|
|
1626
|
+
ctx.log.error("Error: Network error occurred while fetching build results. Please check your connection and try again.");
|
|
1627
|
+
clearInterval(intervalId);
|
|
1628
|
+
} else {
|
|
1629
|
+
ctx.log.error(`Error fetching screenshot data: ${error.message}`);
|
|
1630
|
+
}
|
|
1631
|
+
clearInterval(intervalId);
|
|
1632
|
+
isPollingActive = false;
|
|
1633
|
+
}
|
|
1634
|
+
}), 5e3);
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// src/tasks/exec.ts
|
|
1639
|
+
var exec_default = (ctx) => {
|
|
1640
|
+
var _a;
|
|
1641
|
+
return {
|
|
1642
|
+
title: `Executing '${(_a = ctx.args.execCommand) == null ? void 0 : _a.join(" ")}'`,
|
|
1643
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1644
|
+
if (ctx2.options.fetchResults) {
|
|
1645
|
+
startPolling(ctx2);
|
|
1646
|
+
}
|
|
1647
|
+
updateLogContext({ task: "exec" });
|
|
1648
|
+
return new Promise((resolve, reject) => {
|
|
1649
|
+
var _a2, _b, _c;
|
|
1650
|
+
const childProcess = spawn__default.default(ctx2.args.execCommand[0], (_a2 = ctx2.args.execCommand) == null ? void 0 : _a2.slice(1));
|
|
1651
|
+
let totalOutput = "";
|
|
1652
|
+
const output = listr2.createWritable((chunk) => {
|
|
1653
|
+
totalOutput += chunk;
|
|
1654
|
+
task.output = chalk6__default.default.gray(totalOutput);
|
|
1655
|
+
});
|
|
1656
|
+
(_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
|
|
1657
|
+
(_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
|
|
1658
|
+
childProcess.on("error", (error) => {
|
|
1659
|
+
var _a3;
|
|
1660
|
+
task.output = chalk6__default.default.gray(`error: ${error.message}`);
|
|
1661
|
+
throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
|
|
1662
|
+
});
|
|
1663
|
+
childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
|
|
1664
|
+
var _a3;
|
|
1665
|
+
if (code !== null) {
|
|
1666
|
+
task.title = `Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' completed; exited with code ${code}`;
|
|
1667
|
+
} else if (signal !== null) {
|
|
1668
|
+
throw new Error(`Child process killed with signal ${signal}`);
|
|
1669
|
+
}
|
|
1670
|
+
resolve();
|
|
1671
|
+
}));
|
|
1672
|
+
});
|
|
1673
|
+
}),
|
|
1674
|
+
rendererOptions: { persistentOutput: true },
|
|
1675
|
+
exitOnError: false
|
|
1676
|
+
};
|
|
1677
|
+
};
|
|
1678
|
+
var processSnapshot_default = (ctx) => {
|
|
1679
|
+
return {
|
|
1680
|
+
title: `Processing snapshots`,
|
|
1681
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1682
|
+
var _a, _b;
|
|
1683
|
+
try {
|
|
1684
|
+
if (ctx2.config.delayedUpload) {
|
|
1685
|
+
ctx2.log.debug("started after processing because of delayedUpload");
|
|
1686
|
+
(_a = ctx2.snapshotQueue) == null ? void 0 : _a.startProcessingfunc();
|
|
1687
|
+
}
|
|
1688
|
+
yield new Promise((resolve) => {
|
|
1689
|
+
let output2 = "";
|
|
1690
|
+
const intervalId = setInterval(() => {
|
|
1691
|
+
var _a2, _b2, _c;
|
|
1692
|
+
if (((_a2 = ctx2.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b2 = ctx2.snapshotQueue) == null ? void 0 : _b2.isProcessing())) {
|
|
1693
|
+
clearInterval(intervalId);
|
|
1694
|
+
resolve();
|
|
1695
|
+
} else {
|
|
1696
|
+
task.title = `Processing snapshot ${(_c = ctx2.snapshotQueue) == null ? void 0 : _c.getProcessingSnapshot()}`;
|
|
1697
|
+
}
|
|
1698
|
+
}, 500);
|
|
1699
|
+
});
|
|
1700
|
+
let output = "";
|
|
1701
|
+
for (let snapshot of (_b = ctx2.snapshotQueue) == null ? void 0 : _b.getProcessedSnapshots()) {
|
|
1702
|
+
if (snapshot.error)
|
|
1703
|
+
output += `${chalk6__default.default.red("\u2717")} ${chalk6__default.default.gray(`${snapshot.name}
|
|
1704
|
+
[error] ${snapshot.error}`)}
|
|
1705
|
+
`;
|
|
1706
|
+
else
|
|
1707
|
+
output += `${chalk6__default.default.green("\u2713")} ${chalk6__default.default.gray(snapshot.name)}
|
|
1708
|
+
${snapshot.warnings.length ? chalk6__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
|
|
1709
|
+
`) : ""}`;
|
|
1710
|
+
}
|
|
1711
|
+
task.output = output;
|
|
1712
|
+
task.title = "Processed snapshots";
|
|
1713
|
+
} catch (error) {
|
|
1714
|
+
ctx2.log.debug(error);
|
|
1715
|
+
task.output = chalk6__default.default.gray(error.message);
|
|
1716
|
+
throw new Error("Processing of snapshots failed");
|
|
1717
|
+
}
|
|
1718
|
+
}),
|
|
1719
|
+
rendererOptions: { persistentOutput: true }
|
|
1720
|
+
};
|
|
1721
|
+
};
|
|
1722
|
+
var finalizeBuild_default = (ctx) => {
|
|
1723
|
+
return {
|
|
1724
|
+
title: `Finalizing build`,
|
|
1725
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1726
|
+
var _a, _b;
|
|
1727
|
+
updateLogContext({ task: "finalizeBuild" });
|
|
1728
|
+
try {
|
|
1729
|
+
yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
|
|
1730
|
+
task.output = chalk6__default.default.gray(`build url: ${ctx2.build.url}`);
|
|
1731
|
+
task.title = "Finalized build";
|
|
1732
|
+
} catch (error) {
|
|
1733
|
+
ctx2.log.debug(error);
|
|
1734
|
+
task.output = chalk6__default.default.gray(error.message);
|
|
1735
|
+
throw new Error("Finalize build failed");
|
|
1736
|
+
}
|
|
1737
|
+
try {
|
|
1738
|
+
yield (_a = ctx2.browser) == null ? void 0 : _a.close();
|
|
1739
|
+
ctx2.log.debug(`Closed browser`);
|
|
1740
|
+
yield (_b = ctx2.server) == null ? void 0 : _b.close();
|
|
1741
|
+
ctx2.log.debug(`Closed server`);
|
|
1742
|
+
let resp = yield ctx2.client.getS3PreSignedURL(ctx2);
|
|
1743
|
+
yield ctx2.client.uploadLogs(ctx2, resp.data.url);
|
|
1744
|
+
fs5.unlinkSync(constants_default.LOG_FILE_PATH);
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
ctx2.log.debug(error);
|
|
1747
|
+
}
|
|
1748
|
+
}),
|
|
1749
|
+
rendererOptions: { persistentOutput: true }
|
|
1750
|
+
};
|
|
1751
|
+
};
|
|
1637
1752
|
var MAX_RESOURCE_SIZE = 15 * 1024 ** 2;
|
|
1638
1753
|
var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "other"];
|
|
1639
1754
|
var ALLOWED_STATUSES = [200, 201];
|
|
@@ -1641,18 +1756,19 @@ var REQUEST_TIMEOUT = 1e4;
|
|
|
1641
1756
|
var MIN_VIEWPORT_HEIGHT = 1080;
|
|
1642
1757
|
function processSnapshot(snapshot, ctx) {
|
|
1643
1758
|
return __async(this, null, function* () {
|
|
1644
|
-
var _a;
|
|
1759
|
+
var _a, _b;
|
|
1645
1760
|
updateLogContext({ task: "discovery" });
|
|
1646
1761
|
ctx.log.debug(`Processing snapshot ${snapshot.name} ${snapshot.url}`);
|
|
1762
|
+
const isHeadless = ((_a = process.env.HEADLESS) == null ? void 0 : _a.toLowerCase()) === "false" ? false : true;
|
|
1647
1763
|
let launchOptions = {
|
|
1648
|
-
headless:
|
|
1764
|
+
headless: isHeadless,
|
|
1649
1765
|
args: constants_default.LAUNCH_ARGS
|
|
1650
1766
|
};
|
|
1651
1767
|
let contextOptions = {
|
|
1652
1768
|
javaScriptEnabled: ctx.config.cliEnableJavaScript,
|
|
1653
1769
|
userAgent: constants_default.CHROME_USER_AGENT
|
|
1654
1770
|
};
|
|
1655
|
-
if (!((
|
|
1771
|
+
if (!((_b = ctx.browser) == null ? void 0 : _b.isConnected())) {
|
|
1656
1772
|
if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
|
|
1657
1773
|
launchOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
|
|
1658
1774
|
ctx.browser = yield test.chromium.launch(launchOptions);
|
|
@@ -2210,7 +2326,7 @@ var Queue = class {
|
|
|
2210
2326
|
|
|
2211
2327
|
// src/commander/exec.ts
|
|
2212
2328
|
var command = new commander.Command();
|
|
2213
|
-
command.name("exec").description("Run test commands around SmartUI").argument("<command...>", "Command supplied for running tests").option("-P, --port <number>", "Port number for the server").action(function(execCommand, _, command5) {
|
|
2329
|
+
command.name("exec").description("Run test commands around SmartUI").argument("<command...>", "Command supplied for running tests").option("-P, --port <number>", "Port number for the server").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(execCommand, _, command5) {
|
|
2214
2330
|
return __async(this, null, function* () {
|
|
2215
2331
|
let ctx = ctx_default(command5.optsWithGlobals());
|
|
2216
2332
|
if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
|
|
@@ -2319,7 +2435,7 @@ configFigma.name("config:create-figma").description("Create figma designs config
|
|
|
2319
2435
|
});
|
|
2320
2436
|
function captureScreenshotsForConfig(_0, _1, _2, _3, _4) {
|
|
2321
2437
|
return __async(this, arguments, function* (ctx, browsers, { name, url, waitForTimeout }, browserName, renderViewports) {
|
|
2322
|
-
let pageOptions = { waitUntil: process.env.SMARTUI_PAGE_WAIT_UNTIL_EVENT || "load" };
|
|
2438
|
+
let pageOptions = { waitUntil: process.env.SMARTUI_PAGE_WAIT_UNTIL_EVENT || "load", timeout: ctx.config.waitForPageRender || constants_default.DEFAULT_PAGE_LOAD_TIMEOUT };
|
|
2323
2439
|
let ssId = name.toLowerCase().replace(/\s/g, "_");
|
|
2324
2440
|
let context;
|
|
2325
2441
|
let contextOptions = {};
|
|
@@ -2414,13 +2530,13 @@ function captureScreenshots(ctx) {
|
|
|
2414
2530
|
else
|
|
2415
2531
|
yield captureScreenshotsSync(ctx, staticConfig, browsers);
|
|
2416
2532
|
delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
|
|
2417
|
-
output += `${
|
|
2533
|
+
output += `${chalk6__default.default.gray(staticConfig.name)} ${chalk6__default.default.green("\u2713")}
|
|
2418
2534
|
`;
|
|
2419
2535
|
ctx.task.output = output;
|
|
2420
2536
|
capturedScreenshots++;
|
|
2421
2537
|
} catch (error) {
|
|
2422
2538
|
ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
|
|
2423
|
-
output += `${
|
|
2539
|
+
output += `${chalk6__default.default.gray(staticConfig.name)} ${chalk6__default.default.red("\u2717")}
|
|
2424
2540
|
`;
|
|
2425
2541
|
ctx.task.output = output;
|
|
2426
2542
|
}
|
|
@@ -2532,21 +2648,113 @@ function uploadScreenshots(ctx) {
|
|
|
2532
2648
|
}
|
|
2533
2649
|
});
|
|
2534
2650
|
}
|
|
2651
|
+
function captureScreenshotsConcurrent(ctx) {
|
|
2652
|
+
return __async(this, null, function* () {
|
|
2653
|
+
delDir("screenshots");
|
|
2654
|
+
let totalSnapshots = ctx.webStaticConfig && ctx.webStaticConfig.length;
|
|
2655
|
+
let browserInstances = ctx.options.parallel || 1;
|
|
2656
|
+
let optimizeBrowserInstances = 0;
|
|
2657
|
+
optimizeBrowserInstances = Math.floor(Math.log2(totalSnapshots));
|
|
2658
|
+
if (optimizeBrowserInstances < 1) {
|
|
2659
|
+
optimizeBrowserInstances = 1;
|
|
2660
|
+
}
|
|
2661
|
+
if (optimizeBrowserInstances > browserInstances) {
|
|
2662
|
+
optimizeBrowserInstances = browserInstances;
|
|
2663
|
+
}
|
|
2664
|
+
if (ctx.options.force && browserInstances > 1) {
|
|
2665
|
+
optimizeBrowserInstances = browserInstances;
|
|
2666
|
+
}
|
|
2667
|
+
let urlsPerInstance = 0;
|
|
2668
|
+
if (optimizeBrowserInstances == 1) {
|
|
2669
|
+
urlsPerInstance = totalSnapshots;
|
|
2670
|
+
} else {
|
|
2671
|
+
urlsPerInstance = Math.ceil(totalSnapshots / optimizeBrowserInstances);
|
|
2672
|
+
}
|
|
2673
|
+
ctx.log.debug(`*** browserInstances requested ${ctx.options.parallel} `);
|
|
2674
|
+
ctx.log.debug(`*** optimizeBrowserInstances ${optimizeBrowserInstances} `);
|
|
2675
|
+
ctx.log.debug(`*** urlsPerInstance ${urlsPerInstance}`);
|
|
2676
|
+
ctx.task.output = `URLs : ${totalSnapshots} || Parallel Browser Instances: ${optimizeBrowserInstances}
|
|
2677
|
+
`;
|
|
2678
|
+
let staticURLChunks = splitURLs(ctx.webStaticConfig, urlsPerInstance);
|
|
2679
|
+
let totalCapturedScreenshots = 0;
|
|
2680
|
+
let output = "";
|
|
2681
|
+
const responses = yield Promise.all(staticURLChunks.map((urlConfig) => __async(this, null, function* () {
|
|
2682
|
+
let { capturedScreenshots, finalOutput } = yield processChunk(ctx, urlConfig);
|
|
2683
|
+
return { capturedScreenshots, finalOutput };
|
|
2684
|
+
})));
|
|
2685
|
+
responses.forEach((response) => {
|
|
2686
|
+
totalCapturedScreenshots += response.capturedScreenshots;
|
|
2687
|
+
output += response.finalOutput;
|
|
2688
|
+
});
|
|
2689
|
+
delDir("screenshots");
|
|
2690
|
+
return { totalCapturedScreenshots, output };
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
function splitURLs(arr, chunkSize) {
|
|
2694
|
+
const result = [];
|
|
2695
|
+
for (let i = 0; i < arr.length; i += chunkSize) {
|
|
2696
|
+
result.push(arr.slice(i, i + chunkSize));
|
|
2697
|
+
}
|
|
2698
|
+
return result;
|
|
2699
|
+
}
|
|
2700
|
+
function processChunk(ctx, urlConfig) {
|
|
2701
|
+
return __async(this, null, function* () {
|
|
2702
|
+
let browsers = {};
|
|
2703
|
+
let capturedScreenshots = 0;
|
|
2704
|
+
let finalOutput = "";
|
|
2705
|
+
try {
|
|
2706
|
+
browsers = yield launchBrowsers(ctx);
|
|
2707
|
+
} catch (error) {
|
|
2708
|
+
yield closeBrowsers(browsers);
|
|
2709
|
+
ctx.log.debug(error);
|
|
2710
|
+
throw new Error(`Failed launching browsers ${error}`);
|
|
2711
|
+
}
|
|
2712
|
+
for (let staticConfig of urlConfig) {
|
|
2713
|
+
try {
|
|
2714
|
+
yield captureScreenshotsAsync(ctx, staticConfig, browsers);
|
|
2715
|
+
delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
|
|
2716
|
+
let output = `${chalk6__default.default.gray(staticConfig.name)} ${chalk6__default.default.green("\u2713")}
|
|
2717
|
+
`;
|
|
2718
|
+
ctx.task.output = ctx.task.output ? ctx.task.output + output : output;
|
|
2719
|
+
finalOutput += output;
|
|
2720
|
+
capturedScreenshots++;
|
|
2721
|
+
} catch (error) {
|
|
2722
|
+
ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
|
|
2723
|
+
let output = `${chalk6__default.default.gray(staticConfig.name)} ${chalk6__default.default.red("\u2717")}
|
|
2724
|
+
`;
|
|
2725
|
+
ctx.task.output += output;
|
|
2726
|
+
finalOutput += output;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
yield closeBrowsers(browsers);
|
|
2730
|
+
return { capturedScreenshots, finalOutput };
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2535
2733
|
var captureScreenshots_default = (ctx) => {
|
|
2536
2734
|
return {
|
|
2537
2735
|
title: "Capturing screenshots",
|
|
2538
2736
|
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
2539
2737
|
try {
|
|
2540
2738
|
ctx2.task = task;
|
|
2739
|
+
if (ctx2.options.fetchResults) {
|
|
2740
|
+
startPolling(ctx2, task);
|
|
2741
|
+
}
|
|
2541
2742
|
updateLogContext({ task: "capture" });
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2743
|
+
if (ctx2.options.parallel) {
|
|
2744
|
+
let { totalCapturedScreenshots, output } = yield captureScreenshotsConcurrent(ctx2);
|
|
2745
|
+
if (totalCapturedScreenshots != ctx2.webStaticConfig.length) {
|
|
2746
|
+
throw new Error(output);
|
|
2747
|
+
}
|
|
2748
|
+
} else {
|
|
2749
|
+
let { capturedScreenshots, output } = yield captureScreenshots(ctx2);
|
|
2750
|
+
if (capturedScreenshots != ctx2.webStaticConfig.length) {
|
|
2751
|
+
throw new Error(output);
|
|
2752
|
+
}
|
|
2545
2753
|
}
|
|
2546
2754
|
task.title = "Screenshots captured successfully";
|
|
2547
2755
|
} catch (error) {
|
|
2548
2756
|
ctx2.log.debug(error);
|
|
2549
|
-
task.output =
|
|
2757
|
+
task.output = chalk6__default.default.gray(`${error.message}`);
|
|
2550
2758
|
throw new Error("Capturing screenshots failed");
|
|
2551
2759
|
}
|
|
2552
2760
|
}),
|
|
@@ -2557,21 +2765,26 @@ var captureScreenshots_default = (ctx) => {
|
|
|
2557
2765
|
|
|
2558
2766
|
// src/commander/capture.ts
|
|
2559
2767
|
var command2 = new commander.Command();
|
|
2560
|
-
command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").option("--parallel", "
|
|
2768
|
+
command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").option("-C, --parallel [number]", "Specify the number of instances per browser", parseInt).option("-F, --force", "forcefully apply the specified parallel instances per browser").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(file, _, command5) {
|
|
2561
2769
|
return __async(this, null, function* () {
|
|
2562
2770
|
let ctx = ctx_default(command5.optsWithGlobals());
|
|
2563
2771
|
if (!fs5__default.default.existsSync(file)) {
|
|
2564
|
-
|
|
2772
|
+
ctx.log.error(`Web Static Config file ${file} not found.`);
|
|
2565
2773
|
return;
|
|
2566
2774
|
}
|
|
2567
2775
|
try {
|
|
2568
2776
|
ctx.webStaticConfig = JSON.parse(fs5__default.default.readFileSync(file, "utf8"));
|
|
2569
2777
|
if (!validateWebStaticConfig(ctx.webStaticConfig))
|
|
2570
2778
|
throw new Error(validateWebStaticConfig.errors[0].message);
|
|
2779
|
+
if (ctx.webStaticConfig && ctx.webStaticConfig.length === 0) {
|
|
2780
|
+
ctx.log.error(`No URLs found in the specified config file -> ${file}`);
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2571
2783
|
} catch (error) {
|
|
2572
|
-
|
|
2784
|
+
ctx.log.error(`Invalid Web Static Config; ${error.message}`);
|
|
2573
2785
|
return;
|
|
2574
2786
|
}
|
|
2787
|
+
ctx.log.debug(ctx.config);
|
|
2575
2788
|
let tasks = new listr2.Listr(
|
|
2576
2789
|
[
|
|
2577
2790
|
auth_default(),
|
|
@@ -2605,12 +2818,15 @@ var uploadScreenshots_default = (ctx) => {
|
|
|
2605
2818
|
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
2606
2819
|
try {
|
|
2607
2820
|
ctx2.task = task;
|
|
2821
|
+
if (ctx2.options.fetchResults) {
|
|
2822
|
+
startPolling(ctx2, task);
|
|
2823
|
+
}
|
|
2608
2824
|
updateLogContext({ task: "upload" });
|
|
2609
2825
|
yield uploadScreenshots(ctx2);
|
|
2610
2826
|
task.title = "Screenshots uploaded successfully";
|
|
2611
2827
|
} catch (error) {
|
|
2612
2828
|
ctx2.log.debug(error);
|
|
2613
|
-
task.output =
|
|
2829
|
+
task.output = chalk6__default.default.gray(`${error.message}`);
|
|
2614
2830
|
throw new Error("Uploading screenshots failed");
|
|
2615
2831
|
}
|
|
2616
2832
|
}),
|
|
@@ -2625,7 +2841,7 @@ command3.name("upload").description("Upload screenshots from given directory").a
|
|
|
2625
2841
|
return val.split(",").map((ext) => ext.trim().toLowerCase());
|
|
2626
2842
|
}).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
|
|
2627
2843
|
return val.split(",").map((pattern) => pattern.trim());
|
|
2628
|
-
}).action(function(directory, _, command5) {
|
|
2844
|
+
}).option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(directory, _, command5) {
|
|
2629
2845
|
return __async(this, null, function* () {
|
|
2630
2846
|
let ctx = ctx_default(command5.optsWithGlobals());
|
|
2631
2847
|
if (!fs5__default.default.existsSync(directory)) {
|
|
@@ -2705,7 +2921,7 @@ var uploadFigmaDesigns_default2 = (ctx) => {
|
|
|
2705
2921
|
ctx2.log.debug(`Figma designs processed: ${results}`);
|
|
2706
2922
|
} catch (error) {
|
|
2707
2923
|
ctx2.log.debug(error);
|
|
2708
|
-
task.output =
|
|
2924
|
+
task.output = chalk6__default.default.gray(`${error.message}`);
|
|
2709
2925
|
throw new Error("Uploading Figma designs failed");
|
|
2710
2926
|
}
|
|
2711
2927
|
}),
|
|
@@ -2771,16 +2987,16 @@ var commander_default = program;
|
|
|
2771
2987
|
let { data: { latestVersion, deprecated, additionalDescription } } = yield client.checkUpdate(log2);
|
|
2772
2988
|
log2.info(`
|
|
2773
2989
|
LambdaTest SmartUI CLI v${package_default.version}`);
|
|
2774
|
-
log2.info(
|
|
2990
|
+
log2.info(chalk6__default.default.yellow(`${additionalDescription}`));
|
|
2775
2991
|
if (deprecated) {
|
|
2776
2992
|
log2.warn(`This version is deprecated. A new version ${latestVersion} is available!`);
|
|
2777
2993
|
} else if (package_default.version !== latestVersion) {
|
|
2778
|
-
log2.info(
|
|
2994
|
+
log2.info(chalk6__default.default.green(`A new version ${latestVersion} is available!`));
|
|
2779
2995
|
} else
|
|
2780
|
-
log2.info(
|
|
2996
|
+
log2.info(chalk6__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
2781
2997
|
} catch (error) {
|
|
2782
2998
|
log2.debug(error);
|
|
2783
|
-
log2.info(
|
|
2999
|
+
log2.info(chalk6__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
2784
3000
|
}
|
|
2785
3001
|
commander_default.parse();
|
|
2786
3002
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lambdatest/smartui-cli",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.9",
|
|
4
4
|
"description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*"
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup",
|
|
46
|
-
"release": "pnpm run build && pnpm publish --access public --no-git-checks"
|
|
46
|
+
"release": "pnpm run build && pnpm publish --access public --no-git-checks",
|
|
47
|
+
"local-build": "pnpm run build && pnpm pack"
|
|
47
48
|
}
|
|
48
49
|
}
|