@lambdatest/smartui-cli 3.0.12 → 4.0.1
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 +333 -75
- package/package.json +6 -5
package/dist/index.cjs
CHANGED
|
@@ -16,6 +16,7 @@ var axios = require('axios');
|
|
|
16
16
|
var child_process = require('child_process');
|
|
17
17
|
var spawn = require('cross-spawn');
|
|
18
18
|
var test = require('@playwright/test');
|
|
19
|
+
var sharp = require('sharp');
|
|
19
20
|
|
|
20
21
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
22
|
|
|
@@ -29,6 +30,7 @@ var addErrors__default = /*#__PURE__*/_interopDefault(addErrors);
|
|
|
29
30
|
var FormData__default = /*#__PURE__*/_interopDefault(FormData);
|
|
30
31
|
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
31
32
|
var spawn__default = /*#__PURE__*/_interopDefault(spawn);
|
|
33
|
+
var sharp__default = /*#__PURE__*/_interopDefault(sharp);
|
|
32
34
|
|
|
33
35
|
var __defProp = Object.defineProperty;
|
|
34
36
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -128,6 +130,20 @@ var constants_default = {
|
|
|
128
130
|
MOBILE_ORIENTATION_LANDSCAPE: "landscape",
|
|
129
131
|
// CI
|
|
130
132
|
GITHUB_API_HOST: "https://api.github.com",
|
|
133
|
+
// log file path
|
|
134
|
+
LOG_FILE_PATH: ".smartui.log",
|
|
135
|
+
// Disallowed file extension
|
|
136
|
+
FILE_EXTENSION_ZIP: ".zip",
|
|
137
|
+
FILE_EXTENSION_GIFS: "gif",
|
|
138
|
+
// Default scrollTime
|
|
139
|
+
DEFAULT_SCROLL_TIME: 8,
|
|
140
|
+
// Magic Numbers
|
|
141
|
+
MAGIC_NUMBERS: [
|
|
142
|
+
{ ext: "jpg", magic: Buffer.from([255, 216, 255]) },
|
|
143
|
+
{ ext: "jpeg", magic: Buffer.from([255, 216, 255]) },
|
|
144
|
+
{ ext: "png", magic: Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]) },
|
|
145
|
+
{ ext: "gif", magic: Buffer.from([71, 73, 70, 56]) }
|
|
146
|
+
],
|
|
131
147
|
SUPPORTED_MOBILE_DEVICES: {
|
|
132
148
|
"Blackberry KEY2 LE": { os: "android", viewport: { width: 412, height: 618 } },
|
|
133
149
|
"Galaxy A12": { os: "android", viewport: { width: 360, height: 800 } },
|
|
@@ -421,6 +437,16 @@ var ConfigSchema = {
|
|
|
421
437
|
type: "boolean",
|
|
422
438
|
errorMessage: "Invalid config; enableJavaScript must be true/false"
|
|
423
439
|
},
|
|
440
|
+
cliEnableJavaScript: {
|
|
441
|
+
type: "boolean",
|
|
442
|
+
errorMessage: "Invalid config; cliEnableJavaScript must be true/false"
|
|
443
|
+
},
|
|
444
|
+
scrollTime: {
|
|
445
|
+
type: "number",
|
|
446
|
+
minimum: 1,
|
|
447
|
+
maximum: 1e3,
|
|
448
|
+
errorMessage: "Invalid config; scrollTime must be > 1 and <= 1000"
|
|
449
|
+
},
|
|
424
450
|
allowedHostnames: {
|
|
425
451
|
type: "array",
|
|
426
452
|
items: {
|
|
@@ -632,7 +658,15 @@ var validateFigmaDesignConfig = ajv.compile(FigmaDesignConfigSchema);
|
|
|
632
658
|
|
|
633
659
|
// src/lib/server.ts
|
|
634
660
|
var server_default = (ctx) => __async(void 0, null, function* () {
|
|
635
|
-
const server = fastify__default.default({
|
|
661
|
+
const server = fastify__default.default({
|
|
662
|
+
logger: {
|
|
663
|
+
level: "debug",
|
|
664
|
+
stream: { write: (message) => {
|
|
665
|
+
ctx.log.debug(message);
|
|
666
|
+
} }
|
|
667
|
+
},
|
|
668
|
+
bodyLimit: 3e7
|
|
669
|
+
});
|
|
636
670
|
const opts = {};
|
|
637
671
|
const SMARTUI_DOM = fs5.readFileSync(path2__default.default.resolve(__dirname, "dom-serializer.js"), "utf-8");
|
|
638
672
|
server.get("/healthcheck", opts, (_, reply) => {
|
|
@@ -672,7 +706,6 @@ var env_default = () => {
|
|
|
672
706
|
const {
|
|
673
707
|
PROJECT_TOKEN = "",
|
|
674
708
|
SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
|
|
675
|
-
LT_SDK_LOG_LEVEL,
|
|
676
709
|
LT_SDK_DEBUG,
|
|
677
710
|
SMARTUI_GIT_INFO_FILEPATH,
|
|
678
711
|
HTTP_PROXY,
|
|
@@ -687,7 +720,6 @@ var env_default = () => {
|
|
|
687
720
|
return {
|
|
688
721
|
PROJECT_TOKEN,
|
|
689
722
|
SMARTUI_CLIENT_API_URL,
|
|
690
|
-
LT_SDK_LOG_LEVEL,
|
|
691
723
|
LT_SDK_DEBUG,
|
|
692
724
|
SMARTUI_GIT_INFO_FILEPATH,
|
|
693
725
|
HTTP_PROXY,
|
|
@@ -700,37 +732,37 @@ var env_default = () => {
|
|
|
700
732
|
CURRENT_BRANCH
|
|
701
733
|
};
|
|
702
734
|
};
|
|
703
|
-
var logContext = {
|
|
735
|
+
var logContext = {};
|
|
704
736
|
function updateLogContext(newContext) {
|
|
705
737
|
logContext = __spreadValues(__spreadValues({}, logContext), newContext);
|
|
706
738
|
}
|
|
707
739
|
var logLevel = () => {
|
|
708
740
|
let env = env_default();
|
|
709
|
-
|
|
710
|
-
return debug || env.LT_SDK_LOG_LEVEL || "info";
|
|
741
|
+
return env.LT_SDK_DEBUG === "true" ? "debug" : "info";
|
|
711
742
|
};
|
|
712
743
|
var logger = winston.createLogger({
|
|
713
|
-
level: logLevel(),
|
|
714
744
|
format: winston.format.combine(
|
|
715
745
|
winston.format.timestamp(),
|
|
716
746
|
winston.format.printf((info) => {
|
|
717
747
|
let contextString = Object.values(logContext).join(" | ");
|
|
718
|
-
let message = typeof info.message === "object" ? JSON.stringify(info.message) : info.message;
|
|
748
|
+
let message = typeof info.message === "object" ? JSON.stringify(info.message).trim() : info.message.trim();
|
|
719
749
|
switch (info.level) {
|
|
720
|
-
case "debug":
|
|
721
|
-
message = chalk7__default.default.blue(message);
|
|
722
|
-
break;
|
|
723
750
|
case "warn":
|
|
724
751
|
message = chalk7__default.default.yellow(message);
|
|
725
752
|
break;
|
|
726
|
-
case "error":
|
|
727
|
-
message = chalk7__default.default.red(message);
|
|
728
|
-
break;
|
|
729
753
|
}
|
|
730
754
|
return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
|
|
731
755
|
})
|
|
732
756
|
),
|
|
733
|
-
transports: [
|
|
757
|
+
transports: [
|
|
758
|
+
new winston.transports.Console({
|
|
759
|
+
level: logLevel()
|
|
760
|
+
}),
|
|
761
|
+
new winston.transports.File({
|
|
762
|
+
level: "debug",
|
|
763
|
+
filename: constants_default.LOG_FILE_PATH
|
|
764
|
+
})
|
|
765
|
+
]
|
|
734
766
|
});
|
|
735
767
|
var logger_default = logger;
|
|
736
768
|
|
|
@@ -774,7 +806,7 @@ var auth_default = (ctx) => {
|
|
|
774
806
|
};
|
|
775
807
|
|
|
776
808
|
// package.json
|
|
777
|
-
var version = "
|
|
809
|
+
var version = "4.0.1";
|
|
778
810
|
var package_default = {
|
|
779
811
|
name: "@lambdatest/smartui-cli",
|
|
780
812
|
version,
|
|
@@ -798,10 +830,10 @@ var package_default = {
|
|
|
798
830
|
author: "LambdaTest <keys@lambdatest.com>",
|
|
799
831
|
license: "MIT",
|
|
800
832
|
dependencies: {
|
|
801
|
-
"@playwright/browser-chromium": "^1.
|
|
802
|
-
"@playwright/browser-firefox": "^1.
|
|
803
|
-
"@playwright/browser-webkit": "^1.
|
|
804
|
-
"@playwright/test": "^1.
|
|
833
|
+
"@playwright/browser-chromium": "^1.45.3",
|
|
834
|
+
"@playwright/browser-firefox": "^1.45.3",
|
|
835
|
+
"@playwright/browser-webkit": "^1.45.3",
|
|
836
|
+
"@playwright/test": "^1.45.3",
|
|
805
837
|
"@types/cross-spawn": "^6.0.4",
|
|
806
838
|
"@types/node": "^20.8.9",
|
|
807
839
|
"@types/which": "^3.0.2",
|
|
@@ -814,6 +846,7 @@ var package_default = {
|
|
|
814
846
|
fastify: "^4.24.3",
|
|
815
847
|
"form-data": "^4.0.0",
|
|
816
848
|
listr2: "^7.0.1",
|
|
849
|
+
sharp: "^0.33.4",
|
|
817
850
|
tsup: "^7.2.0",
|
|
818
851
|
which: "^4.0.0",
|
|
819
852
|
winston: "^3.10.0"
|
|
@@ -829,11 +862,11 @@ var httpClient = class {
|
|
|
829
862
|
headers: { "projectToken": PROJECT_TOKEN }
|
|
830
863
|
});
|
|
831
864
|
}
|
|
832
|
-
request(config,
|
|
865
|
+
request(config, log2) {
|
|
833
866
|
return __async(this, null, function* () {
|
|
834
|
-
|
|
867
|
+
log2.debug(`http request: ${config.method} ${config.url}`);
|
|
835
868
|
return this.axiosInstance.request(config).then((resp) => {
|
|
836
|
-
|
|
869
|
+
log2.debug(`http response: ${JSON.stringify({
|
|
837
870
|
status: resp.status,
|
|
838
871
|
headers: resp.headers,
|
|
839
872
|
body: resp.data
|
|
@@ -842,7 +875,7 @@ var httpClient = class {
|
|
|
842
875
|
}).catch((error) => {
|
|
843
876
|
var _a;
|
|
844
877
|
if (error.response) {
|
|
845
|
-
|
|
878
|
+
log2.debug(`http response: ${JSON.stringify({
|
|
846
879
|
status: error.response.status,
|
|
847
880
|
headers: error.response.headers,
|
|
848
881
|
body: error.response.data
|
|
@@ -850,21 +883,21 @@ var httpClient = class {
|
|
|
850
883
|
throw new Error(((_a = error.response.data.error) == null ? void 0 : _a.message) || error.response.data.message);
|
|
851
884
|
}
|
|
852
885
|
if (error.request) {
|
|
853
|
-
|
|
886
|
+
log2.debug(`http request failed: ${error.toJSON()}`);
|
|
854
887
|
throw new Error(error.toJSON().message);
|
|
855
888
|
}
|
|
856
|
-
|
|
889
|
+
log2.debug(`http request failed: ${error.message}`);
|
|
857
890
|
throw new Error(error.message);
|
|
858
891
|
});
|
|
859
892
|
});
|
|
860
893
|
}
|
|
861
|
-
auth(
|
|
894
|
+
auth(log2) {
|
|
862
895
|
return this.request({
|
|
863
896
|
url: "/token/verify",
|
|
864
897
|
method: "GET"
|
|
865
|
-
},
|
|
898
|
+
}, log2);
|
|
866
899
|
}
|
|
867
|
-
createBuild(git, config,
|
|
900
|
+
createBuild(git, config, log2) {
|
|
868
901
|
return this.request({
|
|
869
902
|
url: "/build",
|
|
870
903
|
method: "POST",
|
|
@@ -872,9 +905,9 @@ var httpClient = class {
|
|
|
872
905
|
git,
|
|
873
906
|
config
|
|
874
907
|
}
|
|
875
|
-
},
|
|
908
|
+
}, log2);
|
|
876
909
|
}
|
|
877
|
-
finalizeBuild(buildId, totalSnapshots,
|
|
910
|
+
finalizeBuild(buildId, totalSnapshots, log2) {
|
|
878
911
|
let params = { buildId };
|
|
879
912
|
if (totalSnapshots > -1)
|
|
880
913
|
params.totalSnapshots = totalSnapshots;
|
|
@@ -882,7 +915,7 @@ var httpClient = class {
|
|
|
882
915
|
url: "/build",
|
|
883
916
|
method: "DELETE",
|
|
884
917
|
params
|
|
885
|
-
},
|
|
918
|
+
}, log2);
|
|
886
919
|
}
|
|
887
920
|
uploadSnapshot(ctx, snapshot) {
|
|
888
921
|
return this.request({
|
|
@@ -898,7 +931,7 @@ var httpClient = class {
|
|
|
898
931
|
}
|
|
899
932
|
}, ctx.log);
|
|
900
933
|
}
|
|
901
|
-
uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport,
|
|
934
|
+
uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log2) {
|
|
902
935
|
browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
|
|
903
936
|
const file = fs5__default.default.readFileSync(ssPath);
|
|
904
937
|
const form = new FormData__default.default();
|
|
@@ -915,7 +948,7 @@ var httpClient = class {
|
|
|
915
948
|
headers: form.getHeaders(),
|
|
916
949
|
data: form
|
|
917
950
|
}).then(() => {
|
|
918
|
-
|
|
951
|
+
log2.debug(`${ssName} for ${browserName} ${viewport} uploaded successfully`);
|
|
919
952
|
}).catch((error) => {
|
|
920
953
|
if (error.response) {
|
|
921
954
|
throw new Error(error.response.data.error.message);
|
|
@@ -926,7 +959,7 @@ var httpClient = class {
|
|
|
926
959
|
throw new Error(error.message);
|
|
927
960
|
});
|
|
928
961
|
}
|
|
929
|
-
checkUpdate(
|
|
962
|
+
checkUpdate(log2) {
|
|
930
963
|
return this.request({
|
|
931
964
|
url: `/packageinfo`,
|
|
932
965
|
method: "GET",
|
|
@@ -935,9 +968,9 @@ var httpClient = class {
|
|
|
935
968
|
packageName: package_default.name,
|
|
936
969
|
packageVersion: package_default.version
|
|
937
970
|
}
|
|
938
|
-
},
|
|
971
|
+
}, log2);
|
|
939
972
|
}
|
|
940
|
-
getFigmaFilesAndImages(figmaFileToken, figmaToken, queryParams, authToken, depth, markBaseline, buildName,
|
|
973
|
+
getFigmaFilesAndImages(figmaFileToken, figmaToken, queryParams, authToken, depth, markBaseline, buildName, log2) {
|
|
941
974
|
const requestBody = {
|
|
942
975
|
figma_file_token: figmaFileToken,
|
|
943
976
|
figma_token: figmaToken,
|
|
@@ -954,7 +987,34 @@ var httpClient = class {
|
|
|
954
987
|
"Content-Type": "application/json"
|
|
955
988
|
},
|
|
956
989
|
data: JSON.stringify(requestBody)
|
|
957
|
-
},
|
|
990
|
+
}, log2);
|
|
991
|
+
}
|
|
992
|
+
getS3PreSignedURL(ctx) {
|
|
993
|
+
return this.request({
|
|
994
|
+
url: `/loguploadurl`,
|
|
995
|
+
method: "POST",
|
|
996
|
+
headers: { "Content-Type": "application/json" },
|
|
997
|
+
data: {
|
|
998
|
+
buildId: ctx.build.id
|
|
999
|
+
}
|
|
1000
|
+
}, ctx.log);
|
|
1001
|
+
}
|
|
1002
|
+
uploadLogs(ctx, uploadURL) {
|
|
1003
|
+
const fileStream = fs5__default.default.createReadStream(constants_default.LOG_FILE_PATH);
|
|
1004
|
+
const { size } = fs5__default.default.statSync(constants_default.LOG_FILE_PATH);
|
|
1005
|
+
return this.request({
|
|
1006
|
+
url: uploadURL,
|
|
1007
|
+
method: "PUT",
|
|
1008
|
+
headers: {
|
|
1009
|
+
"Content-Type": "text/plain",
|
|
1010
|
+
"Content-Length": size
|
|
1011
|
+
},
|
|
1012
|
+
data: fileStream,
|
|
1013
|
+
maxBodyLength: Infinity,
|
|
1014
|
+
// prevent axios from limiting the body size
|
|
1015
|
+
maxContentLength: Infinity
|
|
1016
|
+
// prevent axios from limiting the content size
|
|
1017
|
+
}, ctx.log);
|
|
958
1018
|
}
|
|
959
1019
|
};
|
|
960
1020
|
var ctx_default = (options) => {
|
|
@@ -964,6 +1024,10 @@ var ctx_default = (options) => {
|
|
|
964
1024
|
let mobileConfig;
|
|
965
1025
|
let config = constants_default.DEFAULT_CONFIG;
|
|
966
1026
|
let port;
|
|
1027
|
+
let resolutionOff;
|
|
1028
|
+
let extensionFiles;
|
|
1029
|
+
let ignoreStripExtension;
|
|
1030
|
+
let ignoreFilePattern;
|
|
967
1031
|
try {
|
|
968
1032
|
if (options.config) {
|
|
969
1033
|
config = JSON.parse(fs5__default.default.readFileSync(options.config, "utf-8"));
|
|
@@ -979,6 +1043,10 @@ var ctx_default = (options) => {
|
|
|
979
1043
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
980
1044
|
throw new Error("Invalid port number. Port number must be an integer between 1 and 65535.");
|
|
981
1045
|
}
|
|
1046
|
+
resolutionOff = options.ignoreResolutions || false;
|
|
1047
|
+
extensionFiles = options.files || ["png", "jpeg", "jpg"];
|
|
1048
|
+
ignoreStripExtension = options.removeExtensions || false;
|
|
1049
|
+
ignoreFilePattern = options.ignoreDir || [];
|
|
982
1050
|
} catch (error) {
|
|
983
1051
|
console.log(`[smartui] Error: ${error.message}`);
|
|
984
1052
|
process.exit();
|
|
@@ -1005,8 +1073,11 @@ var ctx_default = (options) => {
|
|
|
1005
1073
|
waitForPageRender: config.waitForPageRender || 0,
|
|
1006
1074
|
waitForTimeout: config.waitForTimeout || 0,
|
|
1007
1075
|
enableJavaScript: config.enableJavaScript || false,
|
|
1076
|
+
cliEnableJavaScript: config.cliEnableJavaScript || true,
|
|
1077
|
+
scrollTime: config.scrollTime || constants_default.DEFAULT_SCROLL_TIME,
|
|
1008
1078
|
allowedHostnames: config.allowedHostnames || []
|
|
1009
1079
|
},
|
|
1080
|
+
uploadFilePath: "",
|
|
1010
1081
|
webStaticConfig: [],
|
|
1011
1082
|
git: {
|
|
1012
1083
|
branch: "",
|
|
@@ -1026,16 +1097,20 @@ var ctx_default = (options) => {
|
|
|
1026
1097
|
parallel: options.parallel ? true : false,
|
|
1027
1098
|
markBaseline: options.markBaseline ? true : false,
|
|
1028
1099
|
buildName: options.buildName || "",
|
|
1029
|
-
port
|
|
1100
|
+
port,
|
|
1101
|
+
ignoreResolutions: resolutionOff,
|
|
1102
|
+
fileExtension: extensionFiles,
|
|
1103
|
+
stripExtension: ignoreStripExtension,
|
|
1104
|
+
ignorePattern: ignoreFilePattern
|
|
1030
1105
|
},
|
|
1031
1106
|
cliVersion: version,
|
|
1032
1107
|
totalSnapshots: -1
|
|
1033
1108
|
};
|
|
1034
1109
|
};
|
|
1035
|
-
function executeCommand(
|
|
1110
|
+
function executeCommand(command5) {
|
|
1036
1111
|
let dst = process.cwd();
|
|
1037
1112
|
try {
|
|
1038
|
-
return child_process.execSync(
|
|
1113
|
+
return child_process.execSync(command5, {
|
|
1039
1114
|
cwd: dst,
|
|
1040
1115
|
stdio: ["ignore"],
|
|
1041
1116
|
encoding: "utf-8"
|
|
@@ -1066,8 +1141,8 @@ var git_default = (ctx) => {
|
|
|
1066
1141
|
} else {
|
|
1067
1142
|
const splitCharacter = "<##>";
|
|
1068
1143
|
const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
|
|
1069
|
-
const
|
|
1070
|
-
let res = executeCommand(
|
|
1144
|
+
const command5 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
|
|
1145
|
+
let res = executeCommand(command5).split(splitCharacter);
|
|
1071
1146
|
var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
|
|
1072
1147
|
var branch = ctx.env.CURRENT_BRANCH || branchAndTags[0];
|
|
1073
1148
|
branchAndTags.slice(1);
|
|
@@ -1208,9 +1283,9 @@ var finalizeBuild_default = (ctx) => {
|
|
|
1208
1283
|
return {
|
|
1209
1284
|
title: `Finalizing build`,
|
|
1210
1285
|
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
1286
|
+
var _a, _b;
|
|
1211
1287
|
updateLogContext({ task: "finalizeBuild" });
|
|
1212
1288
|
try {
|
|
1213
|
-
yield new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
1214
1289
|
yield ctx2.client.finalizeBuild(ctx2.build.id, ctx2.totalSnapshots, ctx2.log);
|
|
1215
1290
|
task.output = chalk7__default.default.gray(`build url: ${ctx2.build.url}`);
|
|
1216
1291
|
task.title = "Finalized build";
|
|
@@ -1219,6 +1294,17 @@ var finalizeBuild_default = (ctx) => {
|
|
|
1219
1294
|
task.output = chalk7__default.default.gray(error.message);
|
|
1220
1295
|
throw new Error("Finalize build failed");
|
|
1221
1296
|
}
|
|
1297
|
+
try {
|
|
1298
|
+
yield (_a = ctx2.browser) == null ? void 0 : _a.close();
|
|
1299
|
+
ctx2.log.debug(`Closed browser`);
|
|
1300
|
+
yield (_b = ctx2.server) == null ? void 0 : _b.close();
|
|
1301
|
+
ctx2.log.debug(`Closed server`);
|
|
1302
|
+
let resp = yield ctx2.client.getS3PreSignedURL(ctx2);
|
|
1303
|
+
yield ctx2.client.uploadLogs(ctx2, resp.data.url);
|
|
1304
|
+
fs5.unlinkSync(constants_default.LOG_FILE_PATH);
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
ctx2.log.debug(error);
|
|
1307
|
+
}
|
|
1222
1308
|
}),
|
|
1223
1309
|
rendererOptions: { persistentOutput: true }
|
|
1224
1310
|
};
|
|
@@ -1331,8 +1417,9 @@ function getMobileRenderViewports(ctx) {
|
|
|
1331
1417
|
}
|
|
1332
1418
|
function getRenderViewports(ctx) {
|
|
1333
1419
|
let mobileRenderViewports = getMobileRenderViewports(ctx);
|
|
1420
|
+
let webRenderViewports = getWebRenderViewports(ctx);
|
|
1334
1421
|
return [
|
|
1335
|
-
...
|
|
1422
|
+
...webRenderViewports,
|
|
1336
1423
|
...mobileRenderViewports[constants_default.MOBILE_OS_IOS],
|
|
1337
1424
|
...mobileRenderViewports[constants_default.MOBILE_OS_ANDROID]
|
|
1338
1425
|
];
|
|
@@ -1403,10 +1490,11 @@ var Queue = class {
|
|
|
1403
1490
|
function processSnapshot(snapshot, ctx) {
|
|
1404
1491
|
return __async(this, null, function* () {
|
|
1405
1492
|
var _a;
|
|
1493
|
+
updateLogContext({ task: "discovery" });
|
|
1406
1494
|
ctx.log.debug(`Processing snapshot ${snapshot.name}`);
|
|
1407
1495
|
let launchOptions = { headless: true };
|
|
1408
1496
|
let contextOptions = {
|
|
1409
|
-
javaScriptEnabled: ctx.config.
|
|
1497
|
+
javaScriptEnabled: ctx.config.cliEnableJavaScript,
|
|
1410
1498
|
userAgent: constants_default.CHROME_USER_AGENT
|
|
1411
1499
|
};
|
|
1412
1500
|
if (!((_a = ctx.browser) == null ? void 0 : _a.isConnected())) {
|
|
@@ -1527,8 +1615,13 @@ function processSnapshot(snapshot, ctx) {
|
|
|
1527
1615
|
}
|
|
1528
1616
|
}
|
|
1529
1617
|
let navigated = false;
|
|
1618
|
+
let previousDeviceType = null;
|
|
1530
1619
|
let renderViewports = getRenderViewports(ctx);
|
|
1531
|
-
for (const { viewport, viewportString, fullPage } of renderViewports) {
|
|
1620
|
+
for (const { viewport, viewportString, fullPage, device } of renderViewports) {
|
|
1621
|
+
if (previousDeviceType !== null && previousDeviceType !== device) {
|
|
1622
|
+
navigated = false;
|
|
1623
|
+
}
|
|
1624
|
+
previousDeviceType = device;
|
|
1532
1625
|
yield page.setViewportSize({ width: viewport.width, height: viewport.height || MIN_VIEWPORT_HEIGHT });
|
|
1533
1626
|
ctx.log.debug(`Page resized to ${viewport.width}x${viewport.height || MIN_VIEWPORT_HEIGHT}`);
|
|
1534
1627
|
if (!navigated) {
|
|
@@ -1544,14 +1637,15 @@ function processSnapshot(snapshot, ctx) {
|
|
|
1544
1637
|
throw new Error(error.message);
|
|
1545
1638
|
}
|
|
1546
1639
|
}
|
|
1547
|
-
if (ctx.config.
|
|
1548
|
-
yield page.evaluate(scrollToBottomAndBackToTop);
|
|
1640
|
+
if (ctx.config.cliEnableJavaScript && fullPage)
|
|
1641
|
+
yield page.evaluate(scrollToBottomAndBackToTop, { frequency: 100, timing: ctx.config.scrollTime });
|
|
1549
1642
|
try {
|
|
1550
1643
|
yield page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
1551
1644
|
ctx.log.debug("Network idle 500ms");
|
|
1552
1645
|
} catch (error) {
|
|
1553
1646
|
ctx.log.debug(`Network idle failed due to ${error}`);
|
|
1554
1647
|
}
|
|
1648
|
+
yield new Promise((r) => setTimeout(r, 1e3));
|
|
1555
1649
|
if (processedOptions.element) {
|
|
1556
1650
|
let l = yield page.locator(processedOptions.element).all();
|
|
1557
1651
|
if (l.length === 0) {
|
|
@@ -1606,10 +1700,9 @@ function processSnapshot(snapshot, ctx) {
|
|
|
1606
1700
|
|
|
1607
1701
|
// src/commander/exec.ts
|
|
1608
1702
|
var command = new commander.Command();
|
|
1609
|
-
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, _,
|
|
1703
|
+
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) {
|
|
1610
1704
|
return __async(this, null, function* () {
|
|
1611
|
-
|
|
1612
|
-
let ctx = ctx_default(command4.optsWithGlobals());
|
|
1705
|
+
let ctx = ctx_default(command5.optsWithGlobals());
|
|
1613
1706
|
if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
|
|
1614
1707
|
ctx.log.error(`Error: Command not found "${execCommand[0]}"`);
|
|
1615
1708
|
return;
|
|
@@ -1642,11 +1735,6 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
|
|
|
1642
1735
|
yield tasks.run(ctx);
|
|
1643
1736
|
} catch (error) {
|
|
1644
1737
|
ctx.log.info("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
|
|
1645
|
-
} finally {
|
|
1646
|
-
yield (_a = ctx.browser) == null ? void 0 : _a.close();
|
|
1647
|
-
ctx.log.debug(`Closed browser`);
|
|
1648
|
-
yield (_b = ctx.server) == null ? void 0 : _b.close();
|
|
1649
|
-
ctx.log.debug(`Closed server`);
|
|
1650
1738
|
}
|
|
1651
1739
|
});
|
|
1652
1740
|
});
|
|
@@ -1832,6 +1920,108 @@ function captureScreenshots(ctx) {
|
|
|
1832
1920
|
return { capturedScreenshots, output };
|
|
1833
1921
|
});
|
|
1834
1922
|
}
|
|
1923
|
+
function getImageDimensions(filePath) {
|
|
1924
|
+
const buffer = fs5__default.default.readFileSync(filePath);
|
|
1925
|
+
let width, height;
|
|
1926
|
+
if (buffer.toString("hex", 0, 2) === "ffd8") {
|
|
1927
|
+
let offset = 2;
|
|
1928
|
+
while (offset < buffer.length) {
|
|
1929
|
+
const marker = buffer.toString("hex", offset, offset + 2);
|
|
1930
|
+
offset += 2;
|
|
1931
|
+
const length = buffer.readUInt16BE(offset);
|
|
1932
|
+
if (marker === "ffc0" || marker === "ffc2") {
|
|
1933
|
+
height = buffer.readUInt16BE(offset + 3);
|
|
1934
|
+
width = buffer.readUInt16BE(offset + 5);
|
|
1935
|
+
return { width, height };
|
|
1936
|
+
}
|
|
1937
|
+
offset += length;
|
|
1938
|
+
}
|
|
1939
|
+
} else if (buffer.toString("hex", 1, 4) === "504e47") {
|
|
1940
|
+
width = buffer.readUInt32BE(16);
|
|
1941
|
+
height = buffer.readUInt32BE(20);
|
|
1942
|
+
return { width, height };
|
|
1943
|
+
}
|
|
1944
|
+
return null;
|
|
1945
|
+
}
|
|
1946
|
+
function isAllowedImage(filePath) {
|
|
1947
|
+
return __async(this, null, function* () {
|
|
1948
|
+
try {
|
|
1949
|
+
const fileBuffer = fs5__default.default.readFileSync(filePath);
|
|
1950
|
+
const isMagicValid = constants_default.MAGIC_NUMBERS.some((magic) => fileBuffer.slice(0, magic.magic.length).equals(magic.magic));
|
|
1951
|
+
const metadata = yield sharp__default.default(filePath).metadata();
|
|
1952
|
+
if (metadata.format === constants_default.FILE_EXTENSION_GIFS) {
|
|
1953
|
+
return false;
|
|
1954
|
+
}
|
|
1955
|
+
if (metadata.width > 0 && metadata.height > 0) {
|
|
1956
|
+
return true;
|
|
1957
|
+
}
|
|
1958
|
+
if (isMagicValid && metadata.format !== constants_default.FILE_EXTENSION_GIFS) {
|
|
1959
|
+
return true;
|
|
1960
|
+
}
|
|
1961
|
+
return false;
|
|
1962
|
+
} catch (error) {
|
|
1963
|
+
return false;
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
function uploadScreenshots(ctx) {
|
|
1968
|
+
return __async(this, null, function* () {
|
|
1969
|
+
const allowedExtensions = ctx.options.fileExtension.map((ext) => `.${ext.trim().toLowerCase()}`);
|
|
1970
|
+
let noOfScreenshots = 0;
|
|
1971
|
+
function processDirectory(directory, relativePath = "") {
|
|
1972
|
+
return __async(this, null, function* () {
|
|
1973
|
+
const files = fs5__default.default.readdirSync(directory);
|
|
1974
|
+
for (let file of files) {
|
|
1975
|
+
const filePath = path2__default.default.join(directory, file);
|
|
1976
|
+
const stat = fs5__default.default.statSync(filePath);
|
|
1977
|
+
const relativeFilePath = path2__default.default.join(relativePath, file);
|
|
1978
|
+
if (stat.isDirectory() && ctx.options.ignorePattern.includes(relativeFilePath)) {
|
|
1979
|
+
ctx.log.info(`Ignoring Directory ${relativeFilePath}`);
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (stat.isDirectory()) {
|
|
1983
|
+
yield processDirectory(filePath, relativeFilePath);
|
|
1984
|
+
} else {
|
|
1985
|
+
let fileExtension = path2__default.default.extname(file).toLowerCase();
|
|
1986
|
+
if (allowedExtensions.includes(fileExtension)) {
|
|
1987
|
+
const isValid = yield isAllowedImage(filePath);
|
|
1988
|
+
if (!isValid) {
|
|
1989
|
+
ctx.log.info(`File ${filePath} is not a valid ${fileExtension} image or is corrupted. Skipping.`);
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
let ssId = relativeFilePath;
|
|
1993
|
+
if (ctx.options.stripExtension) {
|
|
1994
|
+
ssId = path2__default.default.join(relativePath, path2__default.default.basename(file, fileExtension));
|
|
1995
|
+
}
|
|
1996
|
+
let viewport = "default";
|
|
1997
|
+
if (!ctx.options.ignoreResolutions) {
|
|
1998
|
+
const dimensions = getImageDimensions(filePath);
|
|
1999
|
+
if (!dimensions) {
|
|
2000
|
+
ctx.log.info(`Unable to determine dimensions for image: ${filePath}`);
|
|
2001
|
+
} else {
|
|
2002
|
+
const width = dimensions.width;
|
|
2003
|
+
const height = dimensions.height;
|
|
2004
|
+
viewport = `${width}x${height}`;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
yield ctx.client.uploadScreenshot(ctx.build, filePath, ssId, "default", viewport, ctx.log);
|
|
2008
|
+
ctx.log.info(`${filePath} : uploaded successfully`);
|
|
2009
|
+
noOfScreenshots++;
|
|
2010
|
+
} else {
|
|
2011
|
+
ctx.log.info(`File ${filePath} has invalid file extension: ${fileExtension}. Skipping`);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
yield processDirectory(ctx.uploadFilePath);
|
|
2018
|
+
if (noOfScreenshots == 0) {
|
|
2019
|
+
ctx.log.info(`No screenshots uploaded.`);
|
|
2020
|
+
} else {
|
|
2021
|
+
ctx.log.info(`${noOfScreenshots} screenshots uploaded successfully.`);
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
1835
2025
|
var captureScreenshots_default = (ctx) => {
|
|
1836
2026
|
return {
|
|
1837
2027
|
title: "Capturing screenshots",
|
|
@@ -1857,9 +2047,9 @@ var captureScreenshots_default = (ctx) => {
|
|
|
1857
2047
|
|
|
1858
2048
|
// src/commander/capture.ts
|
|
1859
2049
|
var command2 = new commander.Command();
|
|
1860
|
-
command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").option("--parallel", "Capture parallely on all browsers").action(function(file, _,
|
|
2050
|
+
command2.name("capture").description("Capture screenshots of static sites").argument("<file>", "Web static config file").option("--parallel", "Capture parallely on all browsers").action(function(file, _, command5) {
|
|
1861
2051
|
return __async(this, null, function* () {
|
|
1862
|
-
let ctx = ctx_default(
|
|
2052
|
+
let ctx = ctx_default(command5.optsWithGlobals());
|
|
1863
2053
|
if (!fs5__default.default.existsSync(file)) {
|
|
1864
2054
|
console.log(`Error: Web Static Config file ${file} not found.`);
|
|
1865
2055
|
return;
|
|
@@ -1899,6 +2089,71 @@ command2.name("capture").description("Capture screenshots of static sites").argu
|
|
|
1899
2089
|
});
|
|
1900
2090
|
});
|
|
1901
2091
|
var capture_default = command2;
|
|
2092
|
+
var uploadScreenshots_default = (ctx) => {
|
|
2093
|
+
return {
|
|
2094
|
+
title: "Uploading screenshots",
|
|
2095
|
+
task: (ctx2, task) => __async(void 0, null, function* () {
|
|
2096
|
+
try {
|
|
2097
|
+
ctx2.task = task;
|
|
2098
|
+
updateLogContext({ task: "upload" });
|
|
2099
|
+
yield uploadScreenshots(ctx2);
|
|
2100
|
+
task.title = "Screenshots uploaded successfully";
|
|
2101
|
+
} catch (error) {
|
|
2102
|
+
ctx2.log.debug(error);
|
|
2103
|
+
task.output = chalk7__default.default.gray(`${error.message}`);
|
|
2104
|
+
throw new Error("Uploading screenshots failed");
|
|
2105
|
+
}
|
|
2106
|
+
}),
|
|
2107
|
+
rendererOptions: { persistentOutput: true },
|
|
2108
|
+
exitOnError: false
|
|
2109
|
+
};
|
|
2110
|
+
};
|
|
2111
|
+
|
|
2112
|
+
// src/commander/upload.ts
|
|
2113
|
+
var command3 = new commander.Command();
|
|
2114
|
+
command3.name("upload").description("Upload screenshots from given directory").argument("<directory>", "Path of the directory").option("-R, --ignoreResolutions", "Ignore resolution").option("-F, --files <extensions>", "Comma-separated list of allowed file extensions", (val) => {
|
|
2115
|
+
return val.split(",").map((ext) => ext.trim().toLowerCase());
|
|
2116
|
+
}).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
|
|
2117
|
+
return val.split(",").map((pattern) => pattern.trim());
|
|
2118
|
+
}).action(function(directory, _, command5) {
|
|
2119
|
+
return __async(this, null, function* () {
|
|
2120
|
+
let ctx = ctx_default(command5.optsWithGlobals());
|
|
2121
|
+
if (!fs5__default.default.existsSync(directory)) {
|
|
2122
|
+
console.log(`Error: The provided directory ${directory} not found.`);
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (path2__default.default.extname(directory).toLowerCase() === constants_default.FILE_EXTENSION_ZIP) {
|
|
2126
|
+
ctx.log.debug(`Error: The provided directory ${directory} is a zip file. Zips are not accepted.`);
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
ctx.uploadFilePath = directory;
|
|
2130
|
+
let tasks = new listr2.Listr(
|
|
2131
|
+
[
|
|
2132
|
+
auth_default(),
|
|
2133
|
+
getGitInfo_default(),
|
|
2134
|
+
createBuild_default(),
|
|
2135
|
+
uploadScreenshots_default(),
|
|
2136
|
+
finalizeBuild_default()
|
|
2137
|
+
],
|
|
2138
|
+
{
|
|
2139
|
+
rendererOptions: {
|
|
2140
|
+
icon: {
|
|
2141
|
+
[listr2.ListrDefaultRendererLogLevels.OUTPUT]: `\u2192`
|
|
2142
|
+
},
|
|
2143
|
+
color: {
|
|
2144
|
+
[listr2.ListrDefaultRendererLogLevels.OUTPUT]: listr2.color.gray
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
);
|
|
2149
|
+
try {
|
|
2150
|
+
yield tasks.run(ctx);
|
|
2151
|
+
} catch (error) {
|
|
2152
|
+
console.log("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
});
|
|
2156
|
+
var upload_default = command3;
|
|
1902
2157
|
|
|
1903
2158
|
// src/lib/uploadFigmaDesigns.ts
|
|
1904
2159
|
var uploadFigmaDesigns_default = (ctx) => __async(void 0, null, function* () {
|
|
@@ -1950,11 +2205,11 @@ var uploadFigmaDesigns_default2 = (ctx) => {
|
|
|
1950
2205
|
};
|
|
1951
2206
|
|
|
1952
2207
|
// src/commander/uploadFigma.ts
|
|
1953
|
-
var
|
|
1954
|
-
|
|
2208
|
+
var command4 = new commander.Command();
|
|
2209
|
+
command4.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, _, command5) {
|
|
1955
2210
|
return __async(this, null, function* () {
|
|
1956
2211
|
var _a, _b;
|
|
1957
|
-
let ctx = ctx_default(
|
|
2212
|
+
let ctx = ctx_default(command5.optsWithGlobals());
|
|
1958
2213
|
if (!fs5__default.default.existsSync(file)) {
|
|
1959
2214
|
console.log(`Error: Figma Config file ${file} not found.`);
|
|
1960
2215
|
return;
|
|
@@ -1992,31 +2247,34 @@ command3.name("upload-figma").description("Capture screenshots of static sites")
|
|
|
1992
2247
|
}
|
|
1993
2248
|
});
|
|
1994
2249
|
});
|
|
1995
|
-
var uploadFigma_default =
|
|
2250
|
+
var uploadFigma_default = command4;
|
|
1996
2251
|
|
|
1997
2252
|
// src/commander/commander.ts
|
|
1998
2253
|
var program = new commander.Command();
|
|
1999
|
-
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(configFigma).addCommand(uploadFigma_default);
|
|
2254
|
+
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_default);
|
|
2000
2255
|
var commander_default = program;
|
|
2001
2256
|
(function() {
|
|
2002
2257
|
return __async(this, null, function* () {
|
|
2003
2258
|
let client = new httpClient(env_default());
|
|
2004
|
-
let
|
|
2259
|
+
let log2 = logger_default;
|
|
2005
2260
|
try {
|
|
2006
|
-
|
|
2261
|
+
let { data: { latestVersion, deprecated, additionalDescription, additionalDescriptionLatestVersion } } = yield client.checkUpdate(log2);
|
|
2262
|
+
log2.info(`
|
|
2007
2263
|
LambdaTest SmartUI CLI v${package_default.version}`);
|
|
2008
|
-
|
|
2009
|
-
if (deprecated)
|
|
2010
|
-
|
|
2264
|
+
log2.info(chalk7__default.default.yellow(`${additionalDescription}`));
|
|
2265
|
+
if (deprecated) {
|
|
2266
|
+
log2.warn(`This version is deprecated. A new version ${latestVersion} is available!`);
|
|
2267
|
+
log2.warn(`${additionalDescriptionLatestVersion}
|
|
2011
2268
|
`);
|
|
2012
|
-
else if (package_default.version !== latestVersion)
|
|
2013
|
-
|
|
2269
|
+
} else if (package_default.version !== latestVersion) {
|
|
2270
|
+
log2.info(chalk7__default.default.green(`A new version ${latestVersion} is available!`));
|
|
2271
|
+
log2.info(chalk7__default.default.red(`${additionalDescriptionLatestVersion}
|
|
2014
2272
|
`));
|
|
2015
|
-
else
|
|
2016
|
-
|
|
2273
|
+
} else
|
|
2274
|
+
log2.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
2017
2275
|
} catch (error) {
|
|
2018
|
-
|
|
2019
|
-
|
|
2276
|
+
log2.debug(error);
|
|
2277
|
+
log2.info(chalk7__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
2020
2278
|
}
|
|
2021
2279
|
commander_default.parse();
|
|
2022
2280
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lambdatest/smartui-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*"
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"author": "LambdaTest <keys@lambdatest.com>",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@playwright/browser-chromium": "^1.
|
|
21
|
-
"@playwright/browser-firefox": "^1.
|
|
22
|
-
"@playwright/browser-webkit": "^1.
|
|
23
|
-
"@playwright/test": "^1.
|
|
20
|
+
"@playwright/browser-chromium": "^1.45.3",
|
|
21
|
+
"@playwright/browser-firefox": "^1.45.3",
|
|
22
|
+
"@playwright/browser-webkit": "^1.45.3",
|
|
23
|
+
"@playwright/test": "^1.45.3",
|
|
24
24
|
"@types/cross-spawn": "^6.0.4",
|
|
25
25
|
"@types/node": "^20.8.9",
|
|
26
26
|
"@types/which": "^3.0.2",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"fastify": "^4.24.3",
|
|
34
34
|
"form-data": "^4.0.0",
|
|
35
35
|
"listr2": "^7.0.1",
|
|
36
|
+
"sharp": "^0.33.4",
|
|
36
37
|
"tsup": "^7.2.0",
|
|
37
38
|
"which": "^4.0.0",
|
|
38
39
|
"winston": "^3.10.0"
|