@lambdatest/smartui-cli 4.0.19 → 4.0.21
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 +659 -453
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -4,24 +4,27 @@
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var which = require('which');
|
|
6
6
|
var listr2 = require('listr2');
|
|
7
|
-
var
|
|
7
|
+
var chalk = require('chalk');
|
|
8
8
|
var path2 = require('path');
|
|
9
9
|
var fastify = require('fastify');
|
|
10
10
|
var fs5 = require('fs');
|
|
11
11
|
var Ajv = require('ajv');
|
|
12
12
|
var addErrors = require('ajv-errors');
|
|
13
|
+
var test = require('@playwright/test');
|
|
14
|
+
var util = require('util');
|
|
13
15
|
var winston = require('winston');
|
|
14
16
|
var FormData = require('form-data');
|
|
15
17
|
var axios = require('axios');
|
|
18
|
+
var https = require('https');
|
|
16
19
|
var child_process = require('child_process');
|
|
17
20
|
var spawn = require('cross-spawn');
|
|
18
|
-
var
|
|
21
|
+
var uuid = require('uuid');
|
|
19
22
|
var sharp = require('sharp');
|
|
20
23
|
|
|
21
24
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
25
|
|
|
23
26
|
var which__default = /*#__PURE__*/_interopDefault(which);
|
|
24
|
-
var
|
|
27
|
+
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
25
28
|
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
26
29
|
var fastify__default = /*#__PURE__*/_interopDefault(fastify);
|
|
27
30
|
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
@@ -29,6 +32,7 @@ var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
|
|
|
29
32
|
var addErrors__default = /*#__PURE__*/_interopDefault(addErrors);
|
|
30
33
|
var FormData__default = /*#__PURE__*/_interopDefault(FormData);
|
|
31
34
|
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
35
|
+
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
32
36
|
var spawn__default = /*#__PURE__*/_interopDefault(spawn);
|
|
33
37
|
var sharp__default = /*#__PURE__*/_interopDefault(sharp);
|
|
34
38
|
|
|
@@ -415,7 +419,6 @@ var constants_default = {
|
|
|
415
419
|
"Xperia 10 IV": { os: "android", viewport: { width: 412, height: 832 } },
|
|
416
420
|
"Honeywell CT40": { os: "android", viewport: { width: 360, height: 512 } }
|
|
417
421
|
},
|
|
418
|
-
FIGMA_API: "https://api.figma.com/v1/",
|
|
419
422
|
DEFAULT_FIGMA_CONFIG: {
|
|
420
423
|
"depth": 2,
|
|
421
424
|
"figma_config": [
|
|
@@ -960,6 +963,282 @@ var validateWebStaticConfig = ajv.compile(WebStaticConfigSchema);
|
|
|
960
963
|
var validateSnapshot = ajv.compile(SnapshotSchema);
|
|
961
964
|
var validateFigmaDesignConfig = ajv.compile(FigmaDesignConfigSchema);
|
|
962
965
|
var validateWebFigmaConfig = ajv.compile(FigmaWebConfigSchema);
|
|
966
|
+
util.promisify(setTimeout);
|
|
967
|
+
var isPollingActive = false;
|
|
968
|
+
function delDir(dir) {
|
|
969
|
+
if (fs5__default.default.existsSync(dir)) {
|
|
970
|
+
fs5__default.default.rmSync(dir, { recursive: true });
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function scrollToBottomAndBackToTop({
|
|
974
|
+
frequency = 100,
|
|
975
|
+
timing = 8,
|
|
976
|
+
remoteWindow = window
|
|
977
|
+
} = {}) {
|
|
978
|
+
return new Promise((resolve) => {
|
|
979
|
+
let scrolls = 1;
|
|
980
|
+
let scrollLength = remoteWindow.document.body.scrollHeight / frequency;
|
|
981
|
+
(function scroll() {
|
|
982
|
+
let scrollBy = scrollLength * scrolls;
|
|
983
|
+
remoteWindow.setTimeout(() => {
|
|
984
|
+
remoteWindow.scrollTo(0, scrollBy);
|
|
985
|
+
if (scrolls < frequency) {
|
|
986
|
+
scrolls += 1;
|
|
987
|
+
scroll();
|
|
988
|
+
}
|
|
989
|
+
if (scrolls === frequency) {
|
|
990
|
+
remoteWindow.setTimeout(() => {
|
|
991
|
+
remoteWindow.scrollTo(0, 0);
|
|
992
|
+
resolve();
|
|
993
|
+
}, timing);
|
|
994
|
+
}
|
|
995
|
+
}, timing);
|
|
996
|
+
})();
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
function launchBrowsers(ctx) {
|
|
1000
|
+
return __async(this, null, function* () {
|
|
1001
|
+
var _a;
|
|
1002
|
+
let browsers = {};
|
|
1003
|
+
const isHeadless = ((_a = process.env.HEADLESS) == null ? void 0 : _a.toLowerCase()) === "false" ? false : true;
|
|
1004
|
+
let launchOptions = { headless: isHeadless };
|
|
1005
|
+
if (ctx.config.web) {
|
|
1006
|
+
for (const browser of ctx.config.web.browsers) {
|
|
1007
|
+
switch (browser) {
|
|
1008
|
+
case constants_default.CHROME:
|
|
1009
|
+
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
1010
|
+
break;
|
|
1011
|
+
case constants_default.SAFARI:
|
|
1012
|
+
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
1013
|
+
break;
|
|
1014
|
+
case constants_default.FIREFOX:
|
|
1015
|
+
browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
|
|
1016
|
+
break;
|
|
1017
|
+
case constants_default.EDGE:
|
|
1018
|
+
launchOptions.args = ["--headless=new"];
|
|
1019
|
+
browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (ctx.config.mobile) {
|
|
1025
|
+
for (const device of ctx.config.mobile.devices) {
|
|
1026
|
+
if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
|
|
1027
|
+
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
1028
|
+
else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
|
|
1029
|
+
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return browsers;
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
function closeBrowsers(browsers) {
|
|
1036
|
+
return __async(this, null, function* () {
|
|
1037
|
+
var _a;
|
|
1038
|
+
for (const browserName of Object.keys(browsers))
|
|
1039
|
+
yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
function getWebRenderViewports(ctx) {
|
|
1043
|
+
let webRenderViewports = [];
|
|
1044
|
+
if (ctx.config.web) {
|
|
1045
|
+
for (const viewport of ctx.config.web.viewports) {
|
|
1046
|
+
webRenderViewports.push({
|
|
1047
|
+
viewport,
|
|
1048
|
+
viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
|
|
1049
|
+
fullPage: viewport.height ? false : true,
|
|
1050
|
+
device: false
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return webRenderViewports;
|
|
1055
|
+
}
|
|
1056
|
+
function getWebRenderViewportsForOptions(options) {
|
|
1057
|
+
let webRenderViewports = [];
|
|
1058
|
+
if (options.web && Array.isArray(options.web.viewports)) {
|
|
1059
|
+
for (const viewport of options.web.viewports) {
|
|
1060
|
+
if (Array.isArray(viewport) && viewport.length > 0) {
|
|
1061
|
+
let viewportObj = {
|
|
1062
|
+
width: viewport[0]
|
|
1063
|
+
};
|
|
1064
|
+
if (viewport.length > 1) {
|
|
1065
|
+
viewportObj.height = viewport[1];
|
|
1066
|
+
}
|
|
1067
|
+
webRenderViewports.push({
|
|
1068
|
+
viewport: viewportObj,
|
|
1069
|
+
viewportString: `${viewport[0]}${viewport[1] ? "x" + viewport[1] : ""}`,
|
|
1070
|
+
fullPage: viewport.length === 1,
|
|
1071
|
+
device: false
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return webRenderViewports;
|
|
1077
|
+
}
|
|
1078
|
+
function getMobileRenderViewports(ctx) {
|
|
1079
|
+
var _a;
|
|
1080
|
+
let mobileRenderViewports = {};
|
|
1081
|
+
mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
|
|
1082
|
+
mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
|
|
1083
|
+
if (ctx.config.mobile) {
|
|
1084
|
+
for (const device of ctx.config.mobile.devices) {
|
|
1085
|
+
let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
|
|
1086
|
+
let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
|
|
1087
|
+
let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
|
|
1088
|
+
(_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
|
|
1089
|
+
viewport: { width: portrait ? width : height, height: portrait ? height : width },
|
|
1090
|
+
viewportString: `${device} (${ctx.config.mobile.orientation})`,
|
|
1091
|
+
fullPage: ctx.config.mobile.fullPage,
|
|
1092
|
+
device: true,
|
|
1093
|
+
os
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return mobileRenderViewports;
|
|
1098
|
+
}
|
|
1099
|
+
function getMobileRenderViewportsForOptions(options) {
|
|
1100
|
+
var _a;
|
|
1101
|
+
let mobileRenderViewports = {};
|
|
1102
|
+
mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
|
|
1103
|
+
mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
|
|
1104
|
+
if (options.mobile) {
|
|
1105
|
+
for (const device of options.mobile.devices) {
|
|
1106
|
+
let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
|
|
1107
|
+
let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
|
|
1108
|
+
let orientation = options.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
|
|
1109
|
+
let portrait = orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT;
|
|
1110
|
+
let fullPage;
|
|
1111
|
+
if (options.mobile.fullPage === void 0 || options.mobile.fullPage) {
|
|
1112
|
+
fullPage = true;
|
|
1113
|
+
} else {
|
|
1114
|
+
fullPage = false;
|
|
1115
|
+
}
|
|
1116
|
+
(_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
|
|
1117
|
+
viewport: { width: portrait ? width : height, height: portrait ? height : width },
|
|
1118
|
+
viewportString: `${device} (${orientation})`,
|
|
1119
|
+
fullPage,
|
|
1120
|
+
device: true,
|
|
1121
|
+
os
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
return mobileRenderViewports;
|
|
1126
|
+
}
|
|
1127
|
+
function getRenderViewports(ctx) {
|
|
1128
|
+
let mobileRenderViewports = getMobileRenderViewports(ctx);
|
|
1129
|
+
let webRenderViewports = getWebRenderViewports(ctx);
|
|
1130
|
+
return [
|
|
1131
|
+
...webRenderViewports,
|
|
1132
|
+
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
1133
|
+
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1134
|
+
];
|
|
1135
|
+
}
|
|
1136
|
+
function getRenderViewportsForOptions(options) {
|
|
1137
|
+
let mobileRenderViewports = getMobileRenderViewportsForOptions(options);
|
|
1138
|
+
let webRenderViewports = getWebRenderViewportsForOptions(options);
|
|
1139
|
+
return [
|
|
1140
|
+
...webRenderViewports,
|
|
1141
|
+
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
1142
|
+
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1143
|
+
];
|
|
1144
|
+
}
|
|
1145
|
+
process.on("SIGINT", () => __async(void 0, null, function* () {
|
|
1146
|
+
if (isPollingActive) {
|
|
1147
|
+
console.log("Fetching results interrupted. Exiting...");
|
|
1148
|
+
isPollingActive = false;
|
|
1149
|
+
} else {
|
|
1150
|
+
console.log("\nExiting gracefully...");
|
|
1151
|
+
}
|
|
1152
|
+
process.exit(0);
|
|
1153
|
+
}));
|
|
1154
|
+
function startPolling(ctx) {
|
|
1155
|
+
return __async(this, null, function* () {
|
|
1156
|
+
ctx.log.info("Fetching results in progress....");
|
|
1157
|
+
isPollingActive = true;
|
|
1158
|
+
const intervalId = setInterval(() => __async(this, null, function* () {
|
|
1159
|
+
if (!isPollingActive) {
|
|
1160
|
+
clearInterval(intervalId);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
try {
|
|
1164
|
+
const resp = yield ctx.client.getScreenshotData(ctx.build.id, ctx.build.baseline, ctx.log);
|
|
1165
|
+
if (!resp.build) {
|
|
1166
|
+
ctx.log.info("Error: Build data is null.");
|
|
1167
|
+
clearInterval(intervalId);
|
|
1168
|
+
isPollingActive = false;
|
|
1169
|
+
}
|
|
1170
|
+
fs5__default.default.writeFileSync(ctx.options.fetchResultsFileName, JSON.stringify(resp, null, 2));
|
|
1171
|
+
ctx.log.debug(`Updated results in ${ctx.options.fetchResultsFileName}`);
|
|
1172
|
+
if (resp.build.build_status_ind === constants_default.BUILD_COMPLETE || resp.build.build_status_ind === constants_default.BUILD_ERROR) {
|
|
1173
|
+
clearInterval(intervalId);
|
|
1174
|
+
ctx.log.info(`Fetching results completed. Final results written to ${ctx.options.fetchResultsFileName}`);
|
|
1175
|
+
isPollingActive = false;
|
|
1176
|
+
let totalScreenshotsWithMismatches = 0;
|
|
1177
|
+
let totalVariantsWithMismatches = 0;
|
|
1178
|
+
const totalScreenshots = Object.keys(resp.screenshots || {}).length;
|
|
1179
|
+
let totalVariants = 0;
|
|
1180
|
+
for (const [screenshot, variants] of Object.entries(resp.screenshots || {})) {
|
|
1181
|
+
let screenshotHasMismatch = false;
|
|
1182
|
+
let variantMismatchCount = 0;
|
|
1183
|
+
totalVariants += variants.length;
|
|
1184
|
+
for (const variant of variants) {
|
|
1185
|
+
if (variant.mismatch_percentage > 0) {
|
|
1186
|
+
screenshotHasMismatch = true;
|
|
1187
|
+
variantMismatchCount++;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (screenshotHasMismatch) {
|
|
1191
|
+
totalScreenshotsWithMismatches++;
|
|
1192
|
+
totalVariantsWithMismatches += variantMismatchCount;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
ctx.log.info(
|
|
1196
|
+
chalk__default.default.green.bold(
|
|
1197
|
+
`
|
|
1198
|
+
Summary of Mismatches:
|
|
1199
|
+
${chalk__default.default.yellow("Total Variants with Mismatches:")} ${chalk__default.default.white(totalVariantsWithMismatches)} out of ${chalk__default.default.white(totalVariants)}
|
|
1200
|
+
${chalk__default.default.yellow("Total Screenshots with Mismatches:")} ${chalk__default.default.white(totalScreenshotsWithMismatches)} out of ${chalk__default.default.white(totalScreenshots)}
|
|
1201
|
+
${chalk__default.default.yellow("Branch Name:")} ${chalk__default.default.white(resp.build.branch)}
|
|
1202
|
+
${chalk__default.default.yellow("Project Name:")} ${chalk__default.default.white(resp.project.name)}
|
|
1203
|
+
${chalk__default.default.yellow("Build ID:")} ${chalk__default.default.white(resp.build.build_id)}
|
|
1204
|
+
`
|
|
1205
|
+
)
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
} catch (error) {
|
|
1209
|
+
if (error.message.includes("ENOTFOUND")) {
|
|
1210
|
+
ctx.log.error("Error: Network error occurred while fetching build results. Please check your connection and try again.");
|
|
1211
|
+
clearInterval(intervalId);
|
|
1212
|
+
} else {
|
|
1213
|
+
ctx.log.error(`Error fetching screenshot data: ${error.message}`);
|
|
1214
|
+
}
|
|
1215
|
+
clearInterval(intervalId);
|
|
1216
|
+
isPollingActive = false;
|
|
1217
|
+
}
|
|
1218
|
+
}), 5e3);
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
var pingIntervalId = null;
|
|
1222
|
+
function startPingPolling(ctx) {
|
|
1223
|
+
return __async(this, null, function* () {
|
|
1224
|
+
try {
|
|
1225
|
+
ctx.log.debug("Sending initial ping to server...");
|
|
1226
|
+
yield ctx.client.ping(ctx.build.id, ctx.log);
|
|
1227
|
+
ctx.log.debug("Initial ping sent successfully.");
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
ctx.log.error(`Error during initial ping: ${error.message}`);
|
|
1230
|
+
}
|
|
1231
|
+
pingIntervalId = setInterval(() => __async(this, null, function* () {
|
|
1232
|
+
try {
|
|
1233
|
+
ctx.log.debug("Sending ping to server...");
|
|
1234
|
+
yield ctx.client.ping(ctx.build.id, ctx.log);
|
|
1235
|
+
ctx.log.debug("Ping sent successfully.");
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
ctx.log.error(`Error during ping polling: ${error.message}`);
|
|
1238
|
+
}
|
|
1239
|
+
}), 10 * 60 * 1e3);
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
963
1242
|
|
|
964
1243
|
// src/lib/server.ts
|
|
965
1244
|
var server_default = (ctx) => __async(void 0, null, function* () {
|
|
@@ -999,6 +1278,48 @@ var server_default = (ctx) => __async(void 0, null, function* () {
|
|
|
999
1278
|
}
|
|
1000
1279
|
return reply.code(replyCode).send(replyBody);
|
|
1001
1280
|
}));
|
|
1281
|
+
server.post("/stop", opts, (_, reply) => __async(void 0, null, function* () {
|
|
1282
|
+
var _a, _b;
|
|
1283
|
+
let replyCode;
|
|
1284
|
+
let replyBody;
|
|
1285
|
+
try {
|
|
1286
|
+
if (ctx.config.delayedUpload) {
|
|
1287
|
+
ctx.log.debug("started after processing because of delayedUpload");
|
|
1288
|
+
(_a = ctx.snapshotQueue) == null ? void 0 : _a.startProcessingfunc();
|
|
1289
|
+
}
|
|
1290
|
+
yield new Promise((resolve) => {
|
|
1291
|
+
const intervalId = setInterval(() => {
|
|
1292
|
+
var _a2, _b2;
|
|
1293
|
+
if (((_a2 = ctx.snapshotQueue) == null ? void 0 : _a2.isEmpty()) && !((_b2 = ctx.snapshotQueue) == null ? void 0 : _b2.isProcessing())) {
|
|
1294
|
+
clearInterval(intervalId);
|
|
1295
|
+
resolve();
|
|
1296
|
+
}
|
|
1297
|
+
}, 1e3);
|
|
1298
|
+
});
|
|
1299
|
+
yield ctx.client.finalizeBuild(ctx.build.id, ctx.totalSnapshots, ctx.log);
|
|
1300
|
+
yield (_b = ctx.browser) == null ? void 0 : _b.close();
|
|
1301
|
+
if (ctx.server) {
|
|
1302
|
+
ctx.server.close();
|
|
1303
|
+
}
|
|
1304
|
+
let resp = yield ctx.client.getS3PreSignedURL(ctx);
|
|
1305
|
+
yield ctx.client.uploadLogs(ctx, resp.data.url);
|
|
1306
|
+
if (pingIntervalId !== null) {
|
|
1307
|
+
clearInterval(pingIntervalId);
|
|
1308
|
+
ctx.log.debug("Ping polling stopped immediately.");
|
|
1309
|
+
}
|
|
1310
|
+
replyCode = 200;
|
|
1311
|
+
replyBody = { data: { message: "success", type: "DELETE" } };
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
ctx.log.debug(error);
|
|
1314
|
+
ctx.log.debug(`stop endpoint failed; ${error}`);
|
|
1315
|
+
replyCode = 500;
|
|
1316
|
+
replyBody = { error: { message: error.message } };
|
|
1317
|
+
}
|
|
1318
|
+
return reply.code(replyCode).send(replyBody);
|
|
1319
|
+
}));
|
|
1320
|
+
server.get("/ping", opts, (_, reply) => {
|
|
1321
|
+
reply.code(200).send({ status: "Server is running", version: ctx.cliVersion });
|
|
1322
|
+
});
|
|
1002
1323
|
yield server.listen({ port: ctx.options.port });
|
|
1003
1324
|
let { port } = server.addresses()[0];
|
|
1004
1325
|
process.env.SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
|
|
@@ -1024,7 +1345,9 @@ var env_default = () => {
|
|
|
1024
1345
|
LT_SDK_DEBUG,
|
|
1025
1346
|
BASELINE_BRANCH,
|
|
1026
1347
|
CURRENT_BRANCH,
|
|
1027
|
-
PROJECT_NAME
|
|
1348
|
+
PROJECT_NAME,
|
|
1349
|
+
SMARTUI_API_PROXY,
|
|
1350
|
+
SMARTUI_API_SKIP_CERTIFICATES
|
|
1028
1351
|
} = process.env;
|
|
1029
1352
|
return {
|
|
1030
1353
|
PROJECT_TOKEN,
|
|
@@ -1042,7 +1365,9 @@ var env_default = () => {
|
|
|
1042
1365
|
CURRENT_BRANCH,
|
|
1043
1366
|
LT_SDK_DEBUG: LT_SDK_DEBUG === "true",
|
|
1044
1367
|
SMARTUI_DO_NOT_USE_CAPTURED_COOKIES: SMARTUI_DO_NOT_USE_CAPTURED_COOKIES === "true",
|
|
1045
|
-
PROJECT_NAME
|
|
1368
|
+
PROJECT_NAME,
|
|
1369
|
+
SMARTUI_API_PROXY,
|
|
1370
|
+
SMARTUI_API_SKIP_CERTIFICATES: SMARTUI_API_SKIP_CERTIFICATES === "true"
|
|
1046
1371
|
};
|
|
1047
1372
|
};
|
|
1048
1373
|
var logContext = {};
|
|
@@ -1061,7 +1386,7 @@ var logger = winston.createLogger({
|
|
|
1061
1386
|
let message = typeof info.message === "object" ? JSON.stringify(info.message).trim() : info.message.trim();
|
|
1062
1387
|
switch (info.level) {
|
|
1063
1388
|
case "warn":
|
|
1064
|
-
message =
|
|
1389
|
+
message = chalk__default.default.yellow(message);
|
|
1065
1390
|
break;
|
|
1066
1391
|
}
|
|
1067
1392
|
return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
|
|
@@ -1088,11 +1413,11 @@ var startServer_default = (ctx) => {
|
|
|
1088
1413
|
updateLogContext({ task: "startServer" });
|
|
1089
1414
|
try {
|
|
1090
1415
|
ctx2.server = yield server_default(ctx2);
|
|
1091
|
-
task.output =
|
|
1416
|
+
task.output = chalk__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
|
|
1092
1417
|
task.title = "SmartUI started";
|
|
1093
1418
|
} catch (error) {
|
|
1094
1419
|
ctx2.log.debug(error);
|
|
1095
|
-
task.output =
|
|
1420
|
+
task.output = chalk__default.default.gray(error.message);
|
|
1096
1421
|
throw new Error("SmartUI server setup failed");
|
|
1097
1422
|
}
|
|
1098
1423
|
}),
|
|
@@ -1107,16 +1432,16 @@ var auth_default = (ctx) => {
|
|
|
1107
1432
|
try {
|
|
1108
1433
|
const authResult = yield ctx2.client.auth(ctx2.log, ctx2.env);
|
|
1109
1434
|
if (authResult === 2) {
|
|
1110
|
-
task.output =
|
|
1435
|
+
task.output = chalk__default.default.gray(`New project '${ctx2.env.PROJECT_NAME}' created successfully`);
|
|
1111
1436
|
} else if (authResult === 0) {
|
|
1112
|
-
task.output =
|
|
1437
|
+
task.output = chalk__default.default.gray(`Using existing project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
|
|
1113
1438
|
} else if (authResult === 1) {
|
|
1114
|
-
task.output =
|
|
1439
|
+
task.output = chalk__default.default.gray(`Using existing project '${ctx2.env.PROJECT_NAME}'`);
|
|
1115
1440
|
}
|
|
1116
1441
|
task.title = "Authenticated with SmartUI";
|
|
1117
1442
|
} catch (error) {
|
|
1118
1443
|
ctx2.log.debug(error);
|
|
1119
|
-
task.output =
|
|
1444
|
+
task.output = chalk__default.default.gray(error.message);
|
|
1120
1445
|
throw new Error("Authentication failed");
|
|
1121
1446
|
}
|
|
1122
1447
|
}),
|
|
@@ -1125,65 +1450,36 @@ var auth_default = (ctx) => {
|
|
|
1125
1450
|
};
|
|
1126
1451
|
|
|
1127
1452
|
// package.json
|
|
1128
|
-
var version = "4.0.
|
|
1453
|
+
var version = "4.0.21";
|
|
1129
1454
|
var package_default = {
|
|
1130
1455
|
name: "@lambdatest/smartui-cli",
|
|
1131
|
-
version
|
|
1132
|
-
description: "A command line interface (CLI) to run SmartUI tests on LambdaTest",
|
|
1133
|
-
files: [
|
|
1134
|
-
"dist/**/*"
|
|
1135
|
-
],
|
|
1136
|
-
scripts: {
|
|
1137
|
-
build: "tsup",
|
|
1138
|
-
release: "pnpm run build && pnpm publish --access public --no-git-checks",
|
|
1139
|
-
"local-build": "pnpm run build && pnpm pack"
|
|
1140
|
-
},
|
|
1141
|
-
bin: {
|
|
1142
|
-
smartui: "./dist/index.cjs"
|
|
1143
|
-
},
|
|
1144
|
-
type: "module",
|
|
1145
|
-
keywords: [
|
|
1146
|
-
"lambdatest",
|
|
1147
|
-
"smartui",
|
|
1148
|
-
"cli"
|
|
1149
|
-
],
|
|
1150
|
-
author: "LambdaTest <keys@lambdatest.com>",
|
|
1151
|
-
license: "MIT",
|
|
1152
|
-
dependencies: {
|
|
1153
|
-
"@playwright/browser-chromium": "^1.47.2",
|
|
1154
|
-
"@playwright/browser-firefox": "^1.47.2",
|
|
1155
|
-
"@playwright/browser-webkit": "^1.47.2",
|
|
1156
|
-
"@playwright/test": "^1.47.2",
|
|
1157
|
-
"@types/cross-spawn": "^6.0.4",
|
|
1158
|
-
"@types/node": "^20.8.9",
|
|
1159
|
-
"@types/which": "^3.0.2",
|
|
1160
|
-
ajv: "^8.12.0",
|
|
1161
|
-
"ajv-errors": "^3.0.0",
|
|
1162
|
-
axios: "^1.6.0",
|
|
1163
|
-
chalk: "^4.1.2",
|
|
1164
|
-
commander: "^11.1.0",
|
|
1165
|
-
"cross-spawn": "^7.0.3",
|
|
1166
|
-
fastify: "^4.24.3",
|
|
1167
|
-
"form-data": "^4.0.0",
|
|
1168
|
-
listr2: "^7.0.1",
|
|
1169
|
-
sharp: "^0.33.4",
|
|
1170
|
-
tsup: "^7.2.0",
|
|
1171
|
-
which: "^4.0.0",
|
|
1172
|
-
winston: "^3.10.0"
|
|
1173
|
-
},
|
|
1174
|
-
devDependencies: {
|
|
1175
|
-
typescript: "^5.3.2"
|
|
1176
|
-
}
|
|
1177
|
-
};
|
|
1456
|
+
version};
|
|
1178
1457
|
var httpClient = class {
|
|
1179
|
-
constructor({ SMARTUI_CLIENT_API_URL, PROJECT_TOKEN, PROJECT_NAME, LT_USERNAME, LT_ACCESS_KEY }) {
|
|
1458
|
+
constructor({ SMARTUI_CLIENT_API_URL, PROJECT_TOKEN, PROJECT_NAME, LT_USERNAME, LT_ACCESS_KEY, SMARTUI_API_PROXY, SMARTUI_API_SKIP_CERTIFICATES }) {
|
|
1180
1459
|
this.projectToken = PROJECT_TOKEN || "";
|
|
1181
1460
|
this.projectName = PROJECT_NAME || "";
|
|
1182
1461
|
this.username = LT_USERNAME || "";
|
|
1183
1462
|
this.accessKey = LT_ACCESS_KEY || "";
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1463
|
+
let proxyUrl = null;
|
|
1464
|
+
try {
|
|
1465
|
+
const urlStr = (SMARTUI_API_PROXY == null ? void 0 : SMARTUI_API_PROXY.startsWith("http")) ? SMARTUI_API_PROXY : `http://${SMARTUI_API_PROXY}`;
|
|
1466
|
+
proxyUrl = SMARTUI_API_PROXY ? new URL(urlStr) : null;
|
|
1467
|
+
} catch (error) {
|
|
1468
|
+
console.error("Invalid proxy URL:", error);
|
|
1469
|
+
}
|
|
1470
|
+
const axiosConfig = {
|
|
1471
|
+
baseURL: SMARTUI_CLIENT_API_URL,
|
|
1472
|
+
proxy: proxyUrl ? {
|
|
1473
|
+
host: proxyUrl.hostname,
|
|
1474
|
+
port: proxyUrl.port ? Number(proxyUrl.port) : 80
|
|
1475
|
+
} : false
|
|
1476
|
+
};
|
|
1477
|
+
if (SMARTUI_API_SKIP_CERTIFICATES) {
|
|
1478
|
+
axiosConfig.httpsAgent = new https__default.default.Agent({
|
|
1479
|
+
rejectUnauthorized: false
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
this.axiosInstance = axios__default.default.create(axiosConfig);
|
|
1187
1483
|
this.axiosInstance.interceptors.request.use((config) => {
|
|
1188
1484
|
config.headers["projectToken"] = this.projectToken;
|
|
1189
1485
|
config.headers["projectName"] = this.projectName;
|
|
@@ -1246,14 +1542,15 @@ var httpClient = class {
|
|
|
1246
1542
|
}
|
|
1247
1543
|
});
|
|
1248
1544
|
}
|
|
1249
|
-
createBuild(git, config, log2, buildName) {
|
|
1545
|
+
createBuild(git, config, log2, buildName, isStartExec) {
|
|
1250
1546
|
return this.request({
|
|
1251
1547
|
url: "/build",
|
|
1252
1548
|
method: "POST",
|
|
1253
1549
|
data: {
|
|
1254
1550
|
git,
|
|
1255
1551
|
config,
|
|
1256
|
-
buildName
|
|
1552
|
+
buildName,
|
|
1553
|
+
isStartExec
|
|
1257
1554
|
}
|
|
1258
1555
|
}, log2);
|
|
1259
1556
|
}
|
|
@@ -1264,6 +1561,15 @@ var httpClient = class {
|
|
|
1264
1561
|
params: { buildId, baseline }
|
|
1265
1562
|
}, log2);
|
|
1266
1563
|
}
|
|
1564
|
+
ping(buildId, log2) {
|
|
1565
|
+
return this.request({
|
|
1566
|
+
url: "/build/ping",
|
|
1567
|
+
method: "POST",
|
|
1568
|
+
data: {
|
|
1569
|
+
buildId
|
|
1570
|
+
}
|
|
1571
|
+
}, log2);
|
|
1572
|
+
}
|
|
1267
1573
|
finalizeBuild(buildId, totalSnapshots, log2) {
|
|
1268
1574
|
let params = { buildId };
|
|
1269
1575
|
if (totalSnapshots > -1)
|
|
@@ -1288,6 +1594,23 @@ var httpClient = class {
|
|
|
1288
1594
|
}
|
|
1289
1595
|
}, ctx.log);
|
|
1290
1596
|
}
|
|
1597
|
+
processSnapshot(ctx, snapshot, snapshotUuid) {
|
|
1598
|
+
return this.request({
|
|
1599
|
+
url: `/build/${ctx.build.id}/snapshot`,
|
|
1600
|
+
method: "POST",
|
|
1601
|
+
headers: { "Content-Type": "application/json" },
|
|
1602
|
+
data: {
|
|
1603
|
+
name: snapshot.name,
|
|
1604
|
+
url: snapshot.url,
|
|
1605
|
+
snapshotUuid,
|
|
1606
|
+
test: {
|
|
1607
|
+
type: ctx.testType,
|
|
1608
|
+
source: "cli"
|
|
1609
|
+
},
|
|
1610
|
+
async: false
|
|
1611
|
+
}
|
|
1612
|
+
}, ctx.log);
|
|
1613
|
+
}
|
|
1291
1614
|
uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log2) {
|
|
1292
1615
|
browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
|
|
1293
1616
|
const file = fs5__default.default.readFileSync(ssPath);
|
|
@@ -1356,6 +1679,18 @@ var httpClient = class {
|
|
|
1356
1679
|
}
|
|
1357
1680
|
}, ctx.log);
|
|
1358
1681
|
}
|
|
1682
|
+
getS3PresignedURLForSnapshotUpload(ctx, snapshotName, snapshotUuid) {
|
|
1683
|
+
return this.request({
|
|
1684
|
+
url: `/snapshotuploadurl`,
|
|
1685
|
+
method: "POST",
|
|
1686
|
+
headers: { "Content-Type": "application/json" },
|
|
1687
|
+
data: {
|
|
1688
|
+
buildId: ctx.build.id,
|
|
1689
|
+
snapshotName,
|
|
1690
|
+
snapshotUuid
|
|
1691
|
+
}
|
|
1692
|
+
}, ctx.log);
|
|
1693
|
+
}
|
|
1359
1694
|
uploadLogs(ctx, uploadURL) {
|
|
1360
1695
|
const fileStream = fs5__default.default.createReadStream(constants_default.LOG_FILE_PATH);
|
|
1361
1696
|
const { size } = fs5__default.default.statSync(constants_default.LOG_FILE_PATH);
|
|
@@ -1373,6 +1708,20 @@ var httpClient = class {
|
|
|
1373
1708
|
// prevent axios from limiting the content size
|
|
1374
1709
|
}, ctx.log);
|
|
1375
1710
|
}
|
|
1711
|
+
uploadSnapshotToS3(ctx, uploadURL, snapshot) {
|
|
1712
|
+
return this.request({
|
|
1713
|
+
url: uploadURL,
|
|
1714
|
+
method: "PUT",
|
|
1715
|
+
headers: {
|
|
1716
|
+
"Content-Type": "application/json"
|
|
1717
|
+
},
|
|
1718
|
+
data: snapshot,
|
|
1719
|
+
maxBodyLength: Infinity,
|
|
1720
|
+
// prevent axios from limiting the body size
|
|
1721
|
+
maxContentLength: Infinity
|
|
1722
|
+
// prevent axios from limiting the content size
|
|
1723
|
+
}, ctx.log);
|
|
1724
|
+
}
|
|
1376
1725
|
processWebFigma(requestBody, log2) {
|
|
1377
1726
|
return this.request({
|
|
1378
1727
|
url: "figma-web/upload",
|
|
@@ -1507,362 +1856,108 @@ var ctx_default = (options) => {
|
|
|
1507
1856
|
fetchResults: fetchResultObj,
|
|
1508
1857
|
fetchResultsFileName: fetchResultsFileObj
|
|
1509
1858
|
},
|
|
1510
|
-
cliVersion: version,
|
|
1511
|
-
totalSnapshots: -1
|
|
1512
|
-
|
|
1513
|
-
};
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
}
|
|
1526
|
-
function isGitRepo() {
|
|
1527
|
-
try {
|
|
1528
|
-
executeCommand("git status");
|
|
1529
|
-
return true;
|
|
1530
|
-
} catch (error) {
|
|
1531
|
-
return false;
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
var git_default = (ctx) => {
|
|
1535
|
-
if (ctx.env.SMARTUI_GIT_INFO_FILEPATH) {
|
|
1536
|
-
let gitInfo = JSON.parse(fs5__default.default.readFileSync(ctx.env.SMARTUI_GIT_INFO_FILEPATH, "utf-8"));
|
|
1537
|
-
return {
|
|
1538
|
-
branch: ctx.env.CURRENT_BRANCH || gitInfo.branch || "",
|
|
1539
|
-
commitId: gitInfo.commit_id.slice(0, 6) || "",
|
|
1540
|
-
commitMessage: gitInfo.commit_body || "",
|
|
1541
|
-
commitAuthor: gitInfo.commit_author || "",
|
|
1542
|
-
githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${gitInfo.commit_id}` : "",
|
|
1543
|
-
baselineBranch: ctx.env.BASELINE_BRANCH || ""
|
|
1544
|
-
};
|
|
1545
|
-
} else {
|
|
1546
|
-
const splitCharacter = "<##>";
|
|
1547
|
-
const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
|
|
1548
|
-
const command4 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
|
|
1549
|
-
let res = executeCommand(command4).split(splitCharacter);
|
|
1550
|
-
var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
|
|
1551
|
-
var branch = ctx.env.CURRENT_BRANCH || branchAndTags[0];
|
|
1552
|
-
branchAndTags.slice(1);
|
|
1553
|
-
return {
|
|
1554
|
-
branch: branch || "",
|
|
1555
|
-
commitId: res[0] || "",
|
|
1556
|
-
commitMessage: res[2] || "",
|
|
1557
|
-
commitAuthor: res[7] || "",
|
|
1558
|
-
githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${res[1]}` : "",
|
|
1559
|
-
baselineBranch: ctx.env.BASELINE_BRANCH || ""
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
};
|
|
1563
|
-
var getGitInfo_default = (ctx) => {
|
|
1564
|
-
return {
|
|
1565
|
-
title: `Fetching git repo details`,
|
|
1566
|
-
skip: (ctx2) => {
|
|
1567
|
-
return !isGitRepo() && !ctx2.env.SMARTUI_GIT_INFO_FILEPATH ? "[SKIPPED] Fetching git repo details; not a git repo" : "";
|
|
1568
|
-
},
|
|
1569
|
-
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1570
|
-
if (ctx2.env.CURRENT_BRANCH && ctx2.env.CURRENT_BRANCH.trim() === "") {
|
|
1571
|
-
throw new Error("Error: The environment variable CURRENT_BRANCH cannot be empty.");
|
|
1572
|
-
}
|
|
1573
|
-
try {
|
|
1574
|
-
ctx2.git = git_default(ctx2);
|
|
1575
|
-
task.output = chalk6__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
|
|
1576
|
-
task.title = "Fetched git information";
|
|
1577
|
-
} catch (error) {
|
|
1578
|
-
ctx2.log.debug(error);
|
|
1579
|
-
task.output = chalk6__default.default.gray(`${error.message}`);
|
|
1580
|
-
throw new Error("Error fetching git repo details");
|
|
1581
|
-
}
|
|
1582
|
-
}),
|
|
1583
|
-
rendererOptions: { persistentOutput: true }
|
|
1584
|
-
};
|
|
1585
|
-
};
|
|
1586
|
-
var createBuild_default = (ctx) => {
|
|
1587
|
-
return {
|
|
1588
|
-
title: `Creating SmartUI build`,
|
|
1589
|
-
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1590
|
-
updateLogContext({ task: "createBuild" });
|
|
1591
|
-
try {
|
|
1592
|
-
let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log, ctx2.build.name);
|
|
1593
|
-
ctx2.build = {
|
|
1594
|
-
id: resp.data.buildId,
|
|
1595
|
-
name: resp.data.buildName,
|
|
1596
|
-
url: resp.data.buildURL,
|
|
1597
|
-
baseline: resp.data.baseline
|
|
1598
|
-
};
|
|
1599
|
-
task.output = chalk6__default.default.gray(`build id: ${resp.data.buildId}`);
|
|
1600
|
-
task.title = "SmartUI build created";
|
|
1601
|
-
} catch (error) {
|
|
1602
|
-
ctx2.log.debug(error);
|
|
1603
|
-
task.output = chalk6__default.default.gray(error.message);
|
|
1604
|
-
throw new Error("SmartUI build creation failed");
|
|
1605
|
-
}
|
|
1606
|
-
}),
|
|
1607
|
-
rendererOptions: { persistentOutput: true }
|
|
1608
|
-
};
|
|
1609
|
-
};
|
|
1610
|
-
var isPollingActive = false;
|
|
1611
|
-
function delDir(dir) {
|
|
1612
|
-
if (fs5__default.default.existsSync(dir)) {
|
|
1613
|
-
fs5__default.default.rmSync(dir, { recursive: true });
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
function scrollToBottomAndBackToTop({
|
|
1617
|
-
frequency = 100,
|
|
1618
|
-
timing = 8,
|
|
1619
|
-
remoteWindow = window
|
|
1620
|
-
} = {}) {
|
|
1621
|
-
return new Promise((resolve) => {
|
|
1622
|
-
let scrolls = 1;
|
|
1623
|
-
let scrollLength = remoteWindow.document.body.scrollHeight / frequency;
|
|
1624
|
-
(function scroll() {
|
|
1625
|
-
let scrollBy = scrollLength * scrolls;
|
|
1626
|
-
remoteWindow.setTimeout(() => {
|
|
1627
|
-
remoteWindow.scrollTo(0, scrollBy);
|
|
1628
|
-
if (scrolls < frequency) {
|
|
1629
|
-
scrolls += 1;
|
|
1630
|
-
scroll();
|
|
1631
|
-
}
|
|
1632
|
-
if (scrolls === frequency) {
|
|
1633
|
-
remoteWindow.setTimeout(() => {
|
|
1634
|
-
remoteWindow.scrollTo(0, 0);
|
|
1635
|
-
resolve();
|
|
1636
|
-
}, timing);
|
|
1637
|
-
}
|
|
1638
|
-
}, timing);
|
|
1639
|
-
})();
|
|
1640
|
-
});
|
|
1641
|
-
}
|
|
1642
|
-
function launchBrowsers(ctx) {
|
|
1643
|
-
return __async(this, null, function* () {
|
|
1644
|
-
var _a;
|
|
1645
|
-
let browsers = {};
|
|
1646
|
-
const isHeadless = ((_a = process.env.HEADLESS) == null ? void 0 : _a.toLowerCase()) === "false" ? false : true;
|
|
1647
|
-
let launchOptions = { headless: isHeadless };
|
|
1648
|
-
if (ctx.config.web) {
|
|
1649
|
-
for (const browser of ctx.config.web.browsers) {
|
|
1650
|
-
switch (browser) {
|
|
1651
|
-
case constants_default.CHROME:
|
|
1652
|
-
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
1653
|
-
break;
|
|
1654
|
-
case constants_default.SAFARI:
|
|
1655
|
-
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
1656
|
-
break;
|
|
1657
|
-
case constants_default.FIREFOX:
|
|
1658
|
-
browsers[constants_default.FIREFOX] = yield test.firefox.launch(launchOptions);
|
|
1659
|
-
break;
|
|
1660
|
-
case constants_default.EDGE:
|
|
1661
|
-
launchOptions.args = ["--headless=new"];
|
|
1662
|
-
browsers[constants_default.EDGE] = yield test.chromium.launch(__spreadValues({ channel: constants_default.EDGE_CHANNEL }, launchOptions));
|
|
1663
|
-
break;
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
if (ctx.config.mobile) {
|
|
1668
|
-
for (const device of ctx.config.mobile.devices) {
|
|
1669
|
-
if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "android" && !browsers[constants_default.CHROME])
|
|
1670
|
-
browsers[constants_default.CHROME] = yield test.chromium.launch(launchOptions);
|
|
1671
|
-
else if (constants_default.SUPPORTED_MOBILE_DEVICES[device].os === "ios" && !browsers[constants_default.SAFARI])
|
|
1672
|
-
browsers[constants_default.SAFARI] = yield test.webkit.launch(launchOptions);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
return browsers;
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
function closeBrowsers(browsers) {
|
|
1679
|
-
return __async(this, null, function* () {
|
|
1680
|
-
var _a;
|
|
1681
|
-
for (const browserName of Object.keys(browsers))
|
|
1682
|
-
yield (_a = browsers[browserName]) == null ? void 0 : _a.close();
|
|
1683
|
-
});
|
|
1684
|
-
}
|
|
1685
|
-
function getWebRenderViewports(ctx) {
|
|
1686
|
-
let webRenderViewports = [];
|
|
1687
|
-
if (ctx.config.web) {
|
|
1688
|
-
for (const viewport of ctx.config.web.viewports) {
|
|
1689
|
-
webRenderViewports.push({
|
|
1690
|
-
viewport,
|
|
1691
|
-
viewportString: `${viewport.width}${viewport.height ? "x" + viewport.height : ""}`,
|
|
1692
|
-
fullPage: viewport.height ? false : true,
|
|
1693
|
-
device: false
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
return webRenderViewports;
|
|
1698
|
-
}
|
|
1699
|
-
function getWebRenderViewportsForOptions(options) {
|
|
1700
|
-
let webRenderViewports = [];
|
|
1701
|
-
if (options.web && Array.isArray(options.web.viewports)) {
|
|
1702
|
-
for (const viewport of options.web.viewports) {
|
|
1703
|
-
if (Array.isArray(viewport) && viewport.length > 0) {
|
|
1704
|
-
let viewportObj = {
|
|
1705
|
-
width: viewport[0]
|
|
1706
|
-
};
|
|
1707
|
-
if (viewport.length > 1) {
|
|
1708
|
-
viewportObj.height = viewport[1];
|
|
1709
|
-
}
|
|
1710
|
-
webRenderViewports.push({
|
|
1711
|
-
viewport: viewportObj,
|
|
1712
|
-
viewportString: `${viewport[0]}${viewport[1] ? "x" + viewport[1] : ""}`,
|
|
1713
|
-
fullPage: viewport.length === 1,
|
|
1714
|
-
device: false
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
return webRenderViewports;
|
|
1720
|
-
}
|
|
1721
|
-
function getMobileRenderViewports(ctx) {
|
|
1722
|
-
var _a;
|
|
1723
|
-
let mobileRenderViewports = {};
|
|
1724
|
-
mobileRenderViewports[constants_default.MOBILE_OS_IOS] = [];
|
|
1725
|
-
mobileRenderViewports[constants_default.MOBILE_OS_ANDROID] = [];
|
|
1726
|
-
if (ctx.config.mobile) {
|
|
1727
|
-
for (const device of ctx.config.mobile.devices) {
|
|
1728
|
-
let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
|
|
1729
|
-
let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
|
|
1730
|
-
let portrait = ctx.config.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT ? true : false;
|
|
1731
|
-
(_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
|
|
1732
|
-
viewport: { width: portrait ? width : height, height: portrait ? height : width },
|
|
1733
|
-
viewportString: `${device} (${ctx.config.mobile.orientation})`,
|
|
1734
|
-
fullPage: ctx.config.mobile.fullPage,
|
|
1735
|
-
device: true,
|
|
1736
|
-
os
|
|
1737
|
-
});
|
|
1738
|
-
}
|
|
1859
|
+
cliVersion: version,
|
|
1860
|
+
totalSnapshots: -1,
|
|
1861
|
+
isStartExec: false
|
|
1862
|
+
};
|
|
1863
|
+
};
|
|
1864
|
+
function executeCommand(command7) {
|
|
1865
|
+
let dst = process.cwd();
|
|
1866
|
+
try {
|
|
1867
|
+
return child_process.execSync(command7, {
|
|
1868
|
+
cwd: dst,
|
|
1869
|
+
stdio: ["ignore"],
|
|
1870
|
+
encoding: "utf-8"
|
|
1871
|
+
});
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
throw new Error(error.message);
|
|
1739
1874
|
}
|
|
1740
|
-
return mobileRenderViewports;
|
|
1741
1875
|
}
|
|
1742
|
-
function
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
for (const device of options.mobile.devices) {
|
|
1749
|
-
let os = constants_default.SUPPORTED_MOBILE_DEVICES[device].os;
|
|
1750
|
-
let { width, height } = constants_default.SUPPORTED_MOBILE_DEVICES[device].viewport;
|
|
1751
|
-
let orientation = options.mobile.orientation || constants_default.MOBILE_ORIENTATION_PORTRAIT;
|
|
1752
|
-
let portrait = orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT;
|
|
1753
|
-
let fullPage;
|
|
1754
|
-
if (options.mobile.fullPage === void 0 || options.mobile.fullPage) {
|
|
1755
|
-
fullPage = true;
|
|
1756
|
-
} else {
|
|
1757
|
-
fullPage = false;
|
|
1758
|
-
}
|
|
1759
|
-
(_a = mobileRenderViewports[os]) == null ? void 0 : _a.push({
|
|
1760
|
-
viewport: { width: portrait ? width : height, height: portrait ? height : width },
|
|
1761
|
-
viewportString: `${device} (${orientation})`,
|
|
1762
|
-
fullPage,
|
|
1763
|
-
device: true,
|
|
1764
|
-
os
|
|
1765
|
-
});
|
|
1766
|
-
}
|
|
1876
|
+
function isGitRepo() {
|
|
1877
|
+
try {
|
|
1878
|
+
executeCommand("git status");
|
|
1879
|
+
return true;
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
return false;
|
|
1767
1882
|
}
|
|
1768
|
-
return mobileRenderViewports;
|
|
1769
|
-
}
|
|
1770
|
-
function getRenderViewports(ctx) {
|
|
1771
|
-
let mobileRenderViewports = getMobileRenderViewports(ctx);
|
|
1772
|
-
let webRenderViewports = getWebRenderViewports(ctx);
|
|
1773
|
-
return [
|
|
1774
|
-
...webRenderViewports,
|
|
1775
|
-
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
1776
|
-
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1777
|
-
];
|
|
1778
|
-
}
|
|
1779
|
-
function getRenderViewportsForOptions(options) {
|
|
1780
|
-
let mobileRenderViewports = getMobileRenderViewportsForOptions(options);
|
|
1781
|
-
let webRenderViewports = getWebRenderViewportsForOptions(options);
|
|
1782
|
-
return [
|
|
1783
|
-
...webRenderViewports,
|
|
1784
|
-
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
1785
|
-
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1786
|
-
];
|
|
1787
1883
|
}
|
|
1788
|
-
|
|
1789
|
-
if (
|
|
1790
|
-
|
|
1791
|
-
|
|
1884
|
+
var git_default = (ctx) => {
|
|
1885
|
+
if (ctx.env.SMARTUI_GIT_INFO_FILEPATH) {
|
|
1886
|
+
let gitInfo = JSON.parse(fs5__default.default.readFileSync(ctx.env.SMARTUI_GIT_INFO_FILEPATH, "utf-8"));
|
|
1887
|
+
return {
|
|
1888
|
+
branch: ctx.env.CURRENT_BRANCH || gitInfo.branch || "",
|
|
1889
|
+
commitId: gitInfo.commit_id.slice(0, 6) || "",
|
|
1890
|
+
commitMessage: gitInfo.commit_body || "",
|
|
1891
|
+
commitAuthor: gitInfo.commit_author || "",
|
|
1892
|
+
githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${gitInfo.commit_id}` : "",
|
|
1893
|
+
baselineBranch: ctx.env.BASELINE_BRANCH || ""
|
|
1894
|
+
};
|
|
1792
1895
|
} else {
|
|
1793
|
-
|
|
1896
|
+
const splitCharacter = "<##>";
|
|
1897
|
+
const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
|
|
1898
|
+
const command7 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
|
|
1899
|
+
let res = executeCommand(command7).split(splitCharacter);
|
|
1900
|
+
var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
|
|
1901
|
+
var branch = ctx.env.CURRENT_BRANCH || branchAndTags[0];
|
|
1902
|
+
branchAndTags.slice(1);
|
|
1903
|
+
return {
|
|
1904
|
+
branch: branch || "",
|
|
1905
|
+
commitId: res[0] || "",
|
|
1906
|
+
commitMessage: res[2] || "",
|
|
1907
|
+
commitAuthor: res[7] || "",
|
|
1908
|
+
githubURL: ctx.env.GITHUB_ACTIONS ? `${constants_default.GITHUB_API_HOST}/repos/${process.env.GITHUB_REPOSITORY}/statuses/${res[1]}` : "",
|
|
1909
|
+
baselineBranch: ctx.env.BASELINE_BRANCH || ""
|
|
1910
|
+
};
|
|
1794
1911
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1912
|
+
};
|
|
1913
|
+
var getGitInfo_default = (ctx) => {
|
|
1914
|
+
return {
|
|
1915
|
+
title: `Fetching git repo details`,
|
|
1916
|
+
skip: (ctx2) => {
|
|
1917
|
+
return !isGitRepo() && !ctx2.env.SMARTUI_GIT_INFO_FILEPATH ? "[SKIPPED] Fetching git repo details; not a git repo" : "";
|
|
1918
|
+
},
|
|
1919
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1920
|
+
if (ctx2.env.CURRENT_BRANCH && ctx2.env.CURRENT_BRANCH.trim() === "") {
|
|
1921
|
+
throw new Error("Error: The environment variable CURRENT_BRANCH cannot be empty.");
|
|
1805
1922
|
}
|
|
1806
1923
|
try {
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
clearInterval(intervalId);
|
|
1811
|
-
isPollingActive = false;
|
|
1812
|
-
}
|
|
1813
|
-
fs5__default.default.writeFileSync(ctx.options.fetchResultsFileName, JSON.stringify(resp, null, 2));
|
|
1814
|
-
ctx.log.debug(`Updated results in ${ctx.options.fetchResultsFileName}`);
|
|
1815
|
-
if (resp.build.build_status_ind === constants_default.BUILD_COMPLETE || resp.build.build_status_ind === constants_default.BUILD_ERROR) {
|
|
1816
|
-
clearInterval(intervalId);
|
|
1817
|
-
ctx.log.info(`Fetching results completed. Final results written to ${ctx.options.fetchResultsFileName}`);
|
|
1818
|
-
isPollingActive = false;
|
|
1819
|
-
let totalScreenshotsWithMismatches = 0;
|
|
1820
|
-
let totalVariantsWithMismatches = 0;
|
|
1821
|
-
const totalScreenshots = Object.keys(resp.screenshots || {}).length;
|
|
1822
|
-
let totalVariants = 0;
|
|
1823
|
-
for (const [screenshot, variants] of Object.entries(resp.screenshots || {})) {
|
|
1824
|
-
let screenshotHasMismatch = false;
|
|
1825
|
-
let variantMismatchCount = 0;
|
|
1826
|
-
totalVariants += variants.length;
|
|
1827
|
-
for (const variant of variants) {
|
|
1828
|
-
if (variant.mismatch_percentage > 0) {
|
|
1829
|
-
screenshotHasMismatch = true;
|
|
1830
|
-
variantMismatchCount++;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
if (screenshotHasMismatch) {
|
|
1834
|
-
totalScreenshotsWithMismatches++;
|
|
1835
|
-
totalVariantsWithMismatches += variantMismatchCount;
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
ctx.log.info(
|
|
1839
|
-
chalk6__default.default.green.bold(
|
|
1840
|
-
`
|
|
1841
|
-
Summary of Mismatches:
|
|
1842
|
-
${chalk6__default.default.yellow("Total Variants with Mismatches:")} ${chalk6__default.default.white(totalVariantsWithMismatches)} out of ${chalk6__default.default.white(totalVariants)}
|
|
1843
|
-
${chalk6__default.default.yellow("Total Screenshots with Mismatches:")} ${chalk6__default.default.white(totalScreenshotsWithMismatches)} out of ${chalk6__default.default.white(totalScreenshots)}
|
|
1844
|
-
${chalk6__default.default.yellow("Branch Name:")} ${chalk6__default.default.white(resp.build.branch)}
|
|
1845
|
-
${chalk6__default.default.yellow("Project Name:")} ${chalk6__default.default.white(resp.project.name)}
|
|
1846
|
-
${chalk6__default.default.yellow("Build ID:")} ${chalk6__default.default.white(resp.build.build_id)}
|
|
1847
|
-
`
|
|
1848
|
-
)
|
|
1849
|
-
);
|
|
1850
|
-
}
|
|
1924
|
+
ctx2.git = git_default(ctx2);
|
|
1925
|
+
task.output = chalk__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
|
|
1926
|
+
task.title = "Fetched git information";
|
|
1851
1927
|
} catch (error) {
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
} else {
|
|
1856
|
-
ctx.log.error(`Error fetching screenshot data: ${error.message}`);
|
|
1857
|
-
}
|
|
1858
|
-
clearInterval(intervalId);
|
|
1859
|
-
isPollingActive = false;
|
|
1928
|
+
ctx2.log.debug(error);
|
|
1929
|
+
task.output = chalk__default.default.gray(`${error.message}`);
|
|
1930
|
+
throw new Error("Error fetching git repo details");
|
|
1860
1931
|
}
|
|
1861
|
-
}),
|
|
1862
|
-
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1932
|
+
}),
|
|
1933
|
+
rendererOptions: { persistentOutput: true }
|
|
1934
|
+
};
|
|
1935
|
+
};
|
|
1936
|
+
var createBuild_default = (ctx) => {
|
|
1937
|
+
return {
|
|
1938
|
+
title: `Creating SmartUI build`,
|
|
1939
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1940
|
+
updateLogContext({ task: "createBuild" });
|
|
1941
|
+
try {
|
|
1942
|
+
let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log, ctx2.build.name, ctx2.isStartExec);
|
|
1943
|
+
ctx2.build = {
|
|
1944
|
+
id: resp.data.buildId,
|
|
1945
|
+
name: resp.data.buildName,
|
|
1946
|
+
url: resp.data.buildURL,
|
|
1947
|
+
baseline: resp.data.baseline,
|
|
1948
|
+
useKafkaFlow: resp.data.useKafkaFlow || false
|
|
1949
|
+
};
|
|
1950
|
+
task.output = chalk__default.default.gray(`build id: ${resp.data.buildId}`);
|
|
1951
|
+
task.title = "SmartUI build created";
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
ctx2.log.debug(error);
|
|
1954
|
+
task.output = chalk__default.default.gray(error.message);
|
|
1955
|
+
throw new Error("SmartUI build creation failed");
|
|
1956
|
+
}
|
|
1957
|
+
}),
|
|
1958
|
+
rendererOptions: { persistentOutput: true }
|
|
1959
|
+
};
|
|
1960
|
+
};
|
|
1866
1961
|
var exec_default = (ctx) => {
|
|
1867
1962
|
var _a;
|
|
1868
1963
|
return {
|
|
@@ -1878,13 +1973,13 @@ var exec_default = (ctx) => {
|
|
|
1878
1973
|
let totalOutput = "";
|
|
1879
1974
|
const output = listr2.createWritable((chunk) => {
|
|
1880
1975
|
totalOutput += chunk;
|
|
1881
|
-
task.output =
|
|
1976
|
+
task.output = chalk__default.default.gray(totalOutput);
|
|
1882
1977
|
});
|
|
1883
1978
|
(_b = childProcess.stdout) == null ? void 0 : _b.pipe(output);
|
|
1884
1979
|
(_c = childProcess.stderr) == null ? void 0 : _c.pipe(output);
|
|
1885
1980
|
childProcess.on("error", (error) => {
|
|
1886
1981
|
var _a3;
|
|
1887
|
-
task.output =
|
|
1982
|
+
task.output = chalk__default.default.gray(`error: ${error.message}`);
|
|
1888
1983
|
throw new Error(`Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' failed`);
|
|
1889
1984
|
});
|
|
1890
1985
|
childProcess.on("close", (code, signal) => __async(void 0, null, function* () {
|
|
@@ -1927,19 +2022,19 @@ var processSnapshot_default = (ctx) => {
|
|
|
1927
2022
|
let output = "";
|
|
1928
2023
|
for (let snapshot of (_b = ctx2.snapshotQueue) == null ? void 0 : _b.getProcessedSnapshots()) {
|
|
1929
2024
|
if (snapshot.error)
|
|
1930
|
-
output += `${
|
|
2025
|
+
output += `${chalk__default.default.red("\u2717")} ${chalk__default.default.gray(`${snapshot.name}
|
|
1931
2026
|
[error] ${snapshot.error}`)}
|
|
1932
2027
|
`;
|
|
1933
2028
|
else
|
|
1934
|
-
output += `${
|
|
1935
|
-
${snapshot.warnings.length ?
|
|
2029
|
+
output += `${chalk__default.default.green("\u2713")} ${chalk__default.default.gray(snapshot.name)}
|
|
2030
|
+
${snapshot.warnings.length ? chalk__default.default.gray(`[warning] ${snapshot.warnings.join("\n[warning] ")}
|
|
1936
2031
|
`) : ""}`;
|
|
1937
2032
|
}
|
|
1938
2033
|
task.output = output;
|
|
1939
2034
|
task.title = "Processed snapshots";
|
|
1940
2035
|
} catch (error) {
|
|
1941
2036
|
ctx2.log.debug(error);
|
|
1942
|
-
task.output =
|
|
2037
|
+
task.output = chalk__default.default.gray(error.message);
|
|
1943
2038
|
throw new Error("Processing of snapshots failed");
|
|
1944
2039
|
}
|
|
1945
2040
|
}),
|
|
@@ -1954,11 +2049,11 @@ var finalizeBuild_default = (ctx) => {
|
|
|
1954
2049
|
updateLogContext({ task: "finalizeBuild" });
|
|
1955
2050
|
try {
|
|
1956
2051
|
yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
|
|
1957
|
-
task.output =
|
|
2052
|
+
task.output = chalk__default.default.gray(`build url: ${ctx2.build.url}`);
|
|
1958
2053
|
task.title = "Finalized build";
|
|
1959
2054
|
} catch (error) {
|
|
1960
2055
|
ctx2.log.debug(error);
|
|
1961
|
-
task.output =
|
|
2056
|
+
task.output = chalk__default.default.gray(error.message);
|
|
1962
2057
|
throw new Error("Finalize build failed");
|
|
1963
2058
|
}
|
|
1964
2059
|
try {
|
|
@@ -2312,8 +2407,6 @@ function processSnapshot(snapshot, ctx) {
|
|
|
2312
2407
|
};
|
|
2313
2408
|
});
|
|
2314
2409
|
}
|
|
2315
|
-
|
|
2316
|
-
// src/lib/snapshotQueue.ts
|
|
2317
2410
|
var Queue = class {
|
|
2318
2411
|
constructor(ctx) {
|
|
2319
2412
|
this.snapshots = [];
|
|
@@ -2536,6 +2629,9 @@ var Queue = class {
|
|
|
2536
2629
|
try {
|
|
2537
2630
|
this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
|
|
2538
2631
|
let drop = false;
|
|
2632
|
+
if (this.ctx.isStartExec) {
|
|
2633
|
+
this.ctx.log.info(`Processing Snapshot: ${snapshot == null ? void 0 : snapshot.name}`);
|
|
2634
|
+
}
|
|
2539
2635
|
if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name)) {
|
|
2540
2636
|
drop = true;
|
|
2541
2637
|
this.ctx.log.info(`Skipping duplicate SmartUI snapshot '${snapshot.name}'. To capture duplicate screenshots, please set the 'delayedUpload' configuration as true in your config file.`);
|
|
@@ -2551,7 +2647,15 @@ var Queue = class {
|
|
|
2551
2647
|
}
|
|
2552
2648
|
if (!drop) {
|
|
2553
2649
|
let { processedSnapshot, warnings } = yield processSnapshot(snapshot, this.ctx);
|
|
2554
|
-
|
|
2650
|
+
if (this.ctx.build && this.ctx.build.useKafkaFlow) {
|
|
2651
|
+
const snapshotUuid = uuid.v4();
|
|
2652
|
+
const presignedResponse = yield this.ctx.client.getS3PresignedURLForSnapshotUpload(this.ctx, processedSnapshot.name, snapshotUuid);
|
|
2653
|
+
const uploadUrl = presignedResponse.data.url;
|
|
2654
|
+
yield this.ctx.client.uploadSnapshotToS3(this.ctx, uploadUrl, processedSnapshot);
|
|
2655
|
+
yield this.ctx.client.processSnapshot(this.ctx, processedSnapshot, snapshotUuid);
|
|
2656
|
+
} else {
|
|
2657
|
+
yield this.ctx.client.uploadSnapshot(this.ctx, processedSnapshot);
|
|
2658
|
+
}
|
|
2555
2659
|
this.ctx.totalSnapshots++;
|
|
2556
2660
|
this.processedSnapshots.push({ name: snapshot.name, warnings });
|
|
2557
2661
|
}
|
|
@@ -2591,14 +2695,14 @@ var Queue = class {
|
|
|
2591
2695
|
|
|
2592
2696
|
// src/commander/exec.ts
|
|
2593
2697
|
var command = new commander.Command();
|
|
2594
|
-
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").option("--buildName <string>", "Specify the build name").action(function(execCommand, _,
|
|
2698
|
+
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").option("--buildName <string>", "Specify the build name").action(function(execCommand, _, command7) {
|
|
2595
2699
|
return __async(this, null, function* () {
|
|
2596
|
-
const options =
|
|
2700
|
+
const options = command7.optsWithGlobals();
|
|
2597
2701
|
if (options.buildName === "") {
|
|
2598
2702
|
console.log(`Error: The '--buildName' option cannot be an empty string.`);
|
|
2599
2703
|
process.exit(1);
|
|
2600
2704
|
}
|
|
2601
|
-
let ctx = ctx_default(
|
|
2705
|
+
let ctx = ctx_default(command7.optsWithGlobals());
|
|
2602
2706
|
if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
|
|
2603
2707
|
ctx.log.error(`Error: Command not found "${execCommand[0]}"`);
|
|
2604
2708
|
return;
|
|
@@ -2853,13 +2957,13 @@ function captureScreenshots(ctx) {
|
|
|
2853
2957
|
else
|
|
2854
2958
|
yield captureScreenshotsSync(ctx, staticConfig, browsers);
|
|
2855
2959
|
delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
|
|
2856
|
-
output += `${
|
|
2960
|
+
output += `${chalk__default.default.gray(staticConfig.name)} ${chalk__default.default.green("\u2713")}
|
|
2857
2961
|
`;
|
|
2858
2962
|
ctx.task.output = output;
|
|
2859
2963
|
capturedScreenshots++;
|
|
2860
2964
|
} catch (error) {
|
|
2861
2965
|
ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
|
|
2862
|
-
output += `${
|
|
2966
|
+
output += `${chalk__default.default.gray(staticConfig.name)} ${chalk__default.default.red("\u2717")}
|
|
2863
2967
|
`;
|
|
2864
2968
|
ctx.task.output = output;
|
|
2865
2969
|
}
|
|
@@ -3036,14 +3140,14 @@ function processChunk(ctx, urlConfig) {
|
|
|
3036
3140
|
try {
|
|
3037
3141
|
yield captureScreenshotsAsync(ctx, staticConfig, browsers);
|
|
3038
3142
|
delDir(`screenshots/${staticConfig.name.toLowerCase().replace(/\s/g, "_")}`);
|
|
3039
|
-
let output = `${
|
|
3143
|
+
let output = `${chalk__default.default.gray(staticConfig.name)} ${chalk__default.default.green("\u2713")}
|
|
3040
3144
|
`;
|
|
3041
3145
|
ctx.task.output = ctx.task.output ? ctx.task.output + output : output;
|
|
3042
3146
|
finalOutput += output;
|
|
3043
3147
|
capturedScreenshots++;
|
|
3044
3148
|
} catch (error) {
|
|
3045
3149
|
ctx.log.debug(`screenshot capture failed for ${JSON.stringify(staticConfig)}; error: ${error}`);
|
|
3046
|
-
let output = `${
|
|
3150
|
+
let output = `${chalk__default.default.gray(staticConfig.name)} ${chalk__default.default.red("\u2717")}
|
|
3047
3151
|
`;
|
|
3048
3152
|
ctx.task.output += output;
|
|
3049
3153
|
finalOutput += output;
|
|
@@ -3077,7 +3181,7 @@ var captureScreenshots_default = (ctx) => {
|
|
|
3077
3181
|
task.title = "Screenshots captured successfully";
|
|
3078
3182
|
} catch (error) {
|
|
3079
3183
|
ctx2.log.debug(error);
|
|
3080
|
-
task.output =
|
|
3184
|
+
task.output = chalk__default.default.gray(`${error.message}`);
|
|
3081
3185
|
throw new Error("Capturing screenshots failed");
|
|
3082
3186
|
}
|
|
3083
3187
|
}),
|
|
@@ -3088,14 +3192,14 @@ var captureScreenshots_default = (ctx) => {
|
|
|
3088
3192
|
|
|
3089
3193
|
// src/commander/capture.ts
|
|
3090
3194
|
var command2 = new commander.Command();
|
|
3091
|
-
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").option("--buildName <string>", "Specify the build name").action(function(file, _,
|
|
3195
|
+
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").option("--buildName <string>", "Specify the build name").action(function(file, _, command7) {
|
|
3092
3196
|
return __async(this, null, function* () {
|
|
3093
|
-
const options =
|
|
3197
|
+
const options = command7.optsWithGlobals();
|
|
3094
3198
|
if (options.buildName === "") {
|
|
3095
3199
|
console.log(`Error: The '--buildName' option cannot be an empty string.`);
|
|
3096
3200
|
process.exit(1);
|
|
3097
3201
|
}
|
|
3098
|
-
let ctx = ctx_default(
|
|
3202
|
+
let ctx = ctx_default(command7.optsWithGlobals());
|
|
3099
3203
|
if (!fs5__default.default.existsSync(file)) {
|
|
3100
3204
|
ctx.log.error(`Web Static Config file ${file} not found.`);
|
|
3101
3205
|
return;
|
|
@@ -3154,7 +3258,7 @@ var uploadScreenshots_default = (ctx) => {
|
|
|
3154
3258
|
task.title = "Screenshots uploaded successfully";
|
|
3155
3259
|
} catch (error) {
|
|
3156
3260
|
ctx2.log.debug(error);
|
|
3157
|
-
task.output =
|
|
3261
|
+
task.output = chalk__default.default.gray(`${error.message}`);
|
|
3158
3262
|
throw new Error("Uploading screenshots failed");
|
|
3159
3263
|
}
|
|
3160
3264
|
}),
|
|
@@ -3169,14 +3273,14 @@ command3.name("upload").description("Upload screenshots from given directory").a
|
|
|
3169
3273
|
return val.split(",").map((ext) => ext.trim().toLowerCase());
|
|
3170
3274
|
}).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
|
|
3171
3275
|
return val.split(",").map((pattern) => pattern.trim());
|
|
3172
|
-
}).option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").option("--buildName <string>", "Specify the build name").action(function(directory, _,
|
|
3276
|
+
}).option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").option("--buildName <string>", "Specify the build name").action(function(directory, _, command7) {
|
|
3173
3277
|
return __async(this, null, function* () {
|
|
3174
|
-
const options =
|
|
3278
|
+
const options = command7.optsWithGlobals();
|
|
3175
3279
|
if (options.buildName === "") {
|
|
3176
3280
|
console.log(`Error: The '--buildName' option cannot be an empty string.`);
|
|
3177
3281
|
process.exit(1);
|
|
3178
3282
|
}
|
|
3179
|
-
let ctx = ctx_default(
|
|
3283
|
+
let ctx = ctx_default(command7.optsWithGlobals());
|
|
3180
3284
|
if (!fs5__default.default.existsSync(directory)) {
|
|
3181
3285
|
console.log(`Error: The provided directory ${directory} not found.`);
|
|
3182
3286
|
return;
|
|
@@ -3254,7 +3358,7 @@ var uploadFigmaDesigns_default2 = (ctx) => {
|
|
|
3254
3358
|
ctx2.log.debug(`Figma designs processed: ${results}`);
|
|
3255
3359
|
} catch (error) {
|
|
3256
3360
|
ctx2.log.debug(error);
|
|
3257
|
-
task.output =
|
|
3361
|
+
task.output = chalk__default.default.gray(`${error.message}`);
|
|
3258
3362
|
throw new Error("Uploading Figma designs failed");
|
|
3259
3363
|
}
|
|
3260
3364
|
}),
|
|
@@ -3351,17 +3455,17 @@ var uploadWebFigma_default2 = (ctx) => {
|
|
|
3351
3455
|
throw new Error("Uploading Web Figma Screenshot failed");
|
|
3352
3456
|
}
|
|
3353
3457
|
if (ctx2.build.id) {
|
|
3354
|
-
task.output =
|
|
3458
|
+
task.output = chalk__default.default.gray(`Build Id: ${ctx2.build.id}`);
|
|
3355
3459
|
let figmaOutput = yield fetchFigma_default(ctx2);
|
|
3356
3460
|
const jsonObject = JSON.parse(figmaOutput);
|
|
3357
3461
|
let output = JSON.stringify(jsonObject, null, 2);
|
|
3358
|
-
task.output = task.output + "\n" +
|
|
3462
|
+
task.output = task.output + "\n" + chalk__default.default.green(`${output}`);
|
|
3359
3463
|
}
|
|
3360
3464
|
task.title = "Web Figma images uploaded successfully to SmartUI";
|
|
3361
3465
|
ctx2.log.debug(`Web Figma processed: ${results}`);
|
|
3362
3466
|
} catch (error) {
|
|
3363
3467
|
ctx2.log.debug(error);
|
|
3364
|
-
task.output =
|
|
3468
|
+
task.output = chalk__default.default.gray(`${error.message}`);
|
|
3365
3469
|
throw new Error("Uploading Web Figma Screenshots failed");
|
|
3366
3470
|
}
|
|
3367
3471
|
}),
|
|
@@ -3371,10 +3475,10 @@ var uploadWebFigma_default2 = (ctx) => {
|
|
|
3371
3475
|
};
|
|
3372
3476
|
var uploadFigma = new commander.Command();
|
|
3373
3477
|
var uploadWebFigmaCommand = new commander.Command();
|
|
3374
|
-
uploadFigma.name("upload-figma").description("Capture screenshots of static sites").argument("<file>", "figma design config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").action(function(file, _,
|
|
3478
|
+
uploadFigma.name("upload-figma").description("Capture screenshots of static sites").argument("<file>", "figma design config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").action(function(file, _, command7) {
|
|
3375
3479
|
return __async(this, null, function* () {
|
|
3376
3480
|
var _a, _b;
|
|
3377
|
-
let ctx = ctx_default(
|
|
3481
|
+
let ctx = ctx_default(command7.optsWithGlobals());
|
|
3378
3482
|
if (!fs5__default.default.existsSync(file)) {
|
|
3379
3483
|
console.log(`Error: Figma Config file ${file} not found.`);
|
|
3380
3484
|
return;
|
|
@@ -3412,10 +3516,10 @@ uploadFigma.name("upload-figma").description("Capture screenshots of static site
|
|
|
3412
3516
|
}
|
|
3413
3517
|
});
|
|
3414
3518
|
});
|
|
3415
|
-
uploadWebFigmaCommand.name("upload-figma-web").description("Capture screenshots of static sites").argument("<file>", "figma config config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").action(function(file, _,
|
|
3519
|
+
uploadWebFigmaCommand.name("upload-figma-web").description("Capture screenshots of static sites").argument("<file>", "figma config config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").action(function(file, _, command7) {
|
|
3416
3520
|
return __async(this, null, function* () {
|
|
3417
3521
|
var _a;
|
|
3418
|
-
let ctx = ctx_default(
|
|
3522
|
+
let ctx = ctx_default(command7.optsWithGlobals());
|
|
3419
3523
|
if (!fs5__default.default.existsSync(file)) {
|
|
3420
3524
|
console.log(`Error: figma-web config file ${file} not found.`);
|
|
3421
3525
|
return;
|
|
@@ -3436,7 +3540,7 @@ uploadWebFigmaCommand.name("upload-figma-web").description("Capture screenshots
|
|
|
3436
3540
|
}
|
|
3437
3541
|
verifyFigmaWebConfig(ctx);
|
|
3438
3542
|
} catch (error) {
|
|
3439
|
-
ctx.log.error(
|
|
3543
|
+
ctx.log.error(chalk__default.default.red(`Invalid figma-web config; ${error.message}`));
|
|
3440
3544
|
return;
|
|
3441
3545
|
}
|
|
3442
3546
|
let tasks = new listr2.Listr(
|
|
@@ -3464,10 +3568,112 @@ uploadWebFigmaCommand.name("upload-figma-web").description("Capture screenshots
|
|
|
3464
3568
|
}
|
|
3465
3569
|
});
|
|
3466
3570
|
});
|
|
3571
|
+
var command4 = new commander.Command();
|
|
3572
|
+
command4.name("exec:start").description("Start SmartUI server").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").option("--buildName <string>", "Specify the build name").action(function() {
|
|
3573
|
+
return __async(this, null, function* () {
|
|
3574
|
+
const options = command4.optsWithGlobals();
|
|
3575
|
+
if (options.buildName === "") {
|
|
3576
|
+
console.log(`Error: The '--buildName' option cannot be an empty string.`);
|
|
3577
|
+
process.exit(1);
|
|
3578
|
+
}
|
|
3579
|
+
let ctx = ctx_default(command4.optsWithGlobals());
|
|
3580
|
+
ctx.snapshotQueue = new Queue(ctx);
|
|
3581
|
+
ctx.totalSnapshots = 0;
|
|
3582
|
+
ctx.isStartExec = true;
|
|
3583
|
+
let tasks = new listr2.Listr(
|
|
3584
|
+
[
|
|
3585
|
+
auth_default(),
|
|
3586
|
+
startServer_default(),
|
|
3587
|
+
getGitInfo_default(),
|
|
3588
|
+
createBuild_default()
|
|
3589
|
+
],
|
|
3590
|
+
{
|
|
3591
|
+
rendererOptions: {
|
|
3592
|
+
icon: {
|
|
3593
|
+
[listr2.ListrDefaultRendererLogLevels.OUTPUT]: `\u2192`
|
|
3594
|
+
},
|
|
3595
|
+
color: {
|
|
3596
|
+
[listr2.ListrDefaultRendererLogLevels.OUTPUT]: listr2.color.gray
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
);
|
|
3601
|
+
try {
|
|
3602
|
+
yield tasks.run(ctx);
|
|
3603
|
+
startPingPolling(ctx);
|
|
3604
|
+
if (ctx.options.fetchResults) {
|
|
3605
|
+
startPolling(ctx);
|
|
3606
|
+
}
|
|
3607
|
+
} catch (error) {
|
|
3608
|
+
console.error("Error during server execution:", error);
|
|
3609
|
+
}
|
|
3610
|
+
});
|
|
3611
|
+
});
|
|
3612
|
+
var server_default2 = command4;
|
|
3613
|
+
var command5 = new commander.Command();
|
|
3614
|
+
function getSmartUIServerAddress() {
|
|
3615
|
+
const serverAddress = process.env.SMARTUI_SERVER_ADDRESS || "http://localhost:49152";
|
|
3616
|
+
return serverAddress;
|
|
3617
|
+
}
|
|
3618
|
+
command5.name("exec:stop").description("Stop the SmartUI server").action(function() {
|
|
3619
|
+
return __async(this, null, function* () {
|
|
3620
|
+
try {
|
|
3621
|
+
const serverAddress = getSmartUIServerAddress();
|
|
3622
|
+
console.log(chalk__default.default.yellow(`Stopping server at ${serverAddress} from terminal...`));
|
|
3623
|
+
const response = yield axios__default.default.post(`${serverAddress}/stop`, { timeout: 15e3 }, {
|
|
3624
|
+
headers: {
|
|
3625
|
+
"Content-Type": "application/json"
|
|
3626
|
+
// Ensure the correct Content-Type header
|
|
3627
|
+
}
|
|
3628
|
+
});
|
|
3629
|
+
if (response.status === 200) {
|
|
3630
|
+
console.log(chalk__default.default.green("Server stopped successfully"));
|
|
3631
|
+
console.log(chalk__default.default.green(`Response: ${JSON.stringify(response.data)}`));
|
|
3632
|
+
} else {
|
|
3633
|
+
console.log(chalk__default.default.red("Failed to stop server"));
|
|
3634
|
+
}
|
|
3635
|
+
} catch (error) {
|
|
3636
|
+
if (error.code === "ECONNABORTED") {
|
|
3637
|
+
console.error(chalk__default.default.red("Error: SmartUI server did not respond in 15 seconds"));
|
|
3638
|
+
} else {
|
|
3639
|
+
console.error(chalk__default.default.red("Error while stopping server"));
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
});
|
|
3643
|
+
});
|
|
3644
|
+
var stopServer_default = command5;
|
|
3645
|
+
function getSmartUIServerAddress2() {
|
|
3646
|
+
const serverAddress = process.env.SMARTUI_SERVER_ADDRESS || "http://localhost:49152";
|
|
3647
|
+
return serverAddress;
|
|
3648
|
+
}
|
|
3649
|
+
var command6 = new commander.Command();
|
|
3650
|
+
command6.name("exec:ping").description("Ping the SmartUI server to check if it is running").action(function() {
|
|
3651
|
+
return __async(this, null, function* () {
|
|
3652
|
+
try {
|
|
3653
|
+
console.log(chalk__default.default.yellow("Pinging server..."));
|
|
3654
|
+
const serverAddress = getSmartUIServerAddress2();
|
|
3655
|
+
console.log(chalk__default.default.yellow(`Pinging server at ${serverAddress} from terminal...`));
|
|
3656
|
+
const response = yield axios__default.default.get(`${serverAddress}/ping`, { timeout: 15e3 });
|
|
3657
|
+
if (response.status === 200) {
|
|
3658
|
+
console.log(chalk__default.default.green("SmartUI Server is running"));
|
|
3659
|
+
console.log(chalk__default.default.green(`Response: ${JSON.stringify(response.data)}`));
|
|
3660
|
+
} else {
|
|
3661
|
+
console.log(chalk__default.default.red("Failed to reach the server"));
|
|
3662
|
+
}
|
|
3663
|
+
} catch (error) {
|
|
3664
|
+
if (error.code === "ECONNABORTED") {
|
|
3665
|
+
console.error(chalk__default.default.red("Error: SmartUI server did not respond in 15 seconds"));
|
|
3666
|
+
} else {
|
|
3667
|
+
console.error(chalk__default.default.red("SmartUI server is not running"));
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
});
|
|
3671
|
+
});
|
|
3672
|
+
var ping_default = command6;
|
|
3467
3673
|
|
|
3468
3674
|
// src/commander/commander.ts
|
|
3469
3675
|
var program = new commander.Command();
|
|
3470
|
-
program.name("smartui").description("CLI to help you run your SmartUI tests on LambdaTest platform").version(`v${version}`).option("-c --config <filepath>", "Config file path").addCommand(exec_default2).addCommand(capture_default).addCommand(configWeb).addCommand(configStatic).addCommand(upload_default).addCommand(configFigma).addCommand(uploadFigma).addCommand(configWebFigma).addCommand(uploadWebFigmaCommand);
|
|
3676
|
+
program.name("smartui").description("CLI to help you run your SmartUI tests on LambdaTest platform").version(`v${version}`).option("-c --config <filepath>", "Config file path").addCommand(exec_default2).addCommand(capture_default).addCommand(configWeb).addCommand(configStatic).addCommand(upload_default).addCommand(server_default2).addCommand(stopServer_default).addCommand(ping_default).addCommand(configFigma).addCommand(uploadFigma).addCommand(configWebFigma).addCommand(uploadWebFigmaCommand);
|
|
3471
3677
|
var commander_default = program;
|
|
3472
3678
|
(function() {
|
|
3473
3679
|
return __async(this, null, function* () {
|
|
@@ -3477,16 +3683,16 @@ var commander_default = program;
|
|
|
3477
3683
|
let { data: { latestVersion, deprecated, additionalDescription } } = yield client.checkUpdate(log2);
|
|
3478
3684
|
log2.info(`
|
|
3479
3685
|
LambdaTest SmartUI CLI v${package_default.version}`);
|
|
3480
|
-
log2.info(
|
|
3686
|
+
log2.info(chalk__default.default.yellow(`${additionalDescription}`));
|
|
3481
3687
|
if (deprecated) {
|
|
3482
3688
|
log2.warn(`This version is deprecated. A new version ${latestVersion} is available!`);
|
|
3483
3689
|
} else if (package_default.version !== latestVersion) {
|
|
3484
|
-
log2.info(
|
|
3690
|
+
log2.info(chalk__default.default.green(`A new version ${latestVersion} is available!`));
|
|
3485
3691
|
} else
|
|
3486
|
-
log2.info(
|
|
3692
|
+
log2.info(chalk__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
3487
3693
|
} catch (error) {
|
|
3488
3694
|
log2.debug(error);
|
|
3489
|
-
log2.info(
|
|
3695
|
+
log2.info(chalk__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
3490
3696
|
}
|
|
3491
3697
|
commander_default.parse();
|
|
3492
3698
|
});
|