@lambdatest/smartui-cli 4.1.10 → 4.1.11

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.
Files changed (2) hide show
  1. package/dist/index.cjs +474 -14
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -465,6 +465,33 @@ var constants_default = {
465
465
  }
466
466
  ]
467
467
  }
468
+ },
469
+ APP_FIGMA_CONFIG: {
470
+ mobile: [
471
+ {
472
+ "name": "Pixel 8",
473
+ "platform": ["android 14"]
474
+ },
475
+ {
476
+ "name": "iPhone 15",
477
+ "platform": ["ios 17"]
478
+ }
479
+ ],
480
+ figma: {
481
+ "depth": 2,
482
+ "configs": [
483
+ {
484
+ "figma_file_token": "<token>",
485
+ "figma_ids": ["id-1", "id-2"],
486
+ "screenshot_names": ["homepage", "about"]
487
+ },
488
+ {
489
+ "figma_file_token": "<token>",
490
+ "figma_ids": ["id-3", "id-4"],
491
+ "screenshot_names": ["xyz", "abc"]
492
+ }
493
+ ]
494
+ }
468
495
  }
469
496
  };
470
497
 
@@ -1084,11 +1111,126 @@ var FigmaWebConfigSchema = {
1084
1111
  "required": ["web", "figma"],
1085
1112
  additionalProperties: false
1086
1113
  };
1114
+ var FigmaAppConfigSchema = {
1115
+ type: "object",
1116
+ "properties": {
1117
+ "web": {
1118
+ "type": "object",
1119
+ "properties": {
1120
+ browsers: {
1121
+ type: "array",
1122
+ items: { type: "string", enum: [constants_default.CHROME, constants_default.FIREFOX, constants_default.SAFARI, constants_default.EDGE] },
1123
+ uniqueItems: true,
1124
+ maxItems: 4,
1125
+ errorMessage: `allowed browsers - ${constants_default.CHROME}, ${constants_default.FIREFOX}, ${constants_default.SAFARI}, ${constants_default.EDGE}`
1126
+ },
1127
+ "viewports": {
1128
+ "type": "array",
1129
+ "items": {
1130
+ "type": "array",
1131
+ "items": {
1132
+ "type": "integer",
1133
+ "minimum": 1
1134
+ },
1135
+ "minItems": 1
1136
+ }
1137
+ }
1138
+ },
1139
+ "required": ["browsers"]
1140
+ },
1141
+ "mobile": {
1142
+ "type": "array",
1143
+ "items": {
1144
+ "type": "object",
1145
+ "properties": {
1146
+ name: {
1147
+ type: "string",
1148
+ minLength: 1,
1149
+ enum: Object.keys(constants_default.SUPPORTED_MOBILE_DEVICES),
1150
+ errorMessage: "unsupported mobile device name"
1151
+ },
1152
+ "platform": {
1153
+ "type": "array",
1154
+ "items": {
1155
+ "type": "string"
1156
+ },
1157
+ uniqueItems: true
1158
+ },
1159
+ orientation: {
1160
+ type: "string",
1161
+ enum: [constants_default.MOBILE_ORIENTATION_PORTRAIT, constants_default.MOBILE_ORIENTATION_LANDSCAPE],
1162
+ errorMessage: `Invalid config; orientation must be ${constants_default.MOBILE_ORIENTATION_PORTRAIT}/${constants_default.MOBILE_ORIENTATION_LANDSCAPE}`
1163
+ }
1164
+ },
1165
+ "required": ["name"]
1166
+ },
1167
+ uniqueItems: true
1168
+ },
1169
+ "figma": {
1170
+ "type": "object",
1171
+ "properties": {
1172
+ depth: {
1173
+ type: "integer",
1174
+ minimum: 2,
1175
+ errorMessage: "Depth must be an integer and greater than 1"
1176
+ },
1177
+ "configs": {
1178
+ "type": "array",
1179
+ "items": {
1180
+ "type": "object",
1181
+ "properties": {
1182
+ "figma_file_token": {
1183
+ "type": "string",
1184
+ minLength: 1,
1185
+ errorMessage: "figma_file_token is mandatory and cannot be empty"
1186
+ },
1187
+ "figma_ids": {
1188
+ "type": "array",
1189
+ "items": {
1190
+ "type": "string",
1191
+ minLength: 1,
1192
+ errorMessage: "Each ID in figma_ids must be a non-empty string"
1193
+ },
1194
+ minItems: 1,
1195
+ uniqueItems: true,
1196
+ errorMessage: {
1197
+ type: "figma_ids must be an array of strings",
1198
+ minItems: "figma_ids cannot be empty",
1199
+ uniqueItems: "figma_ids must contain unique values"
1200
+ }
1201
+ },
1202
+ "screenshot_names": {
1203
+ "type": "array",
1204
+ "items": {
1205
+ "type": "string"
1206
+ },
1207
+ uniqueItems: false
1208
+ }
1209
+ },
1210
+ "required": ["figma_file_token", "figma_ids"]
1211
+ },
1212
+ uniqueItems: true,
1213
+ errorMessage: {
1214
+ uniqueItems: "Each entry in the figma configs must be unique"
1215
+ }
1216
+ }
1217
+ },
1218
+ "required": ["configs"]
1219
+ },
1220
+ smartIgnore: {
1221
+ type: "boolean",
1222
+ errorMessage: "Invalid config; smartIgnore must be true/false"
1223
+ }
1224
+ },
1225
+ "required": ["mobile", "figma"],
1226
+ additionalProperties: false
1227
+ };
1087
1228
  var validateConfig = ajv.compile(ConfigSchema);
1088
1229
  var validateWebStaticConfig = ajv.compile(WebStaticConfigSchema);
1089
1230
  var validateSnapshot = ajv.compile(SnapshotSchema);
1090
1231
  var validateFigmaDesignConfig = ajv.compile(FigmaDesignConfigSchema);
1091
1232
  var validateWebFigmaConfig = ajv.compile(FigmaWebConfigSchema);
1233
+ var validateAppFigmaConfig = ajv.compile(FigmaAppConfigSchema);
1092
1234
  var lambdaTunnel = __require("@lambdatest/node-tunnel");
1093
1235
  util.promisify(setTimeout);
1094
1236
  var tunnelInstance;
@@ -1595,7 +1737,8 @@ var env_default = () => {
1595
1737
  CURRENT_BRANCH,
1596
1738
  PROJECT_NAME,
1597
1739
  SMARTUI_API_PROXY,
1598
- SMARTUI_API_SKIP_CERTIFICATES
1740
+ SMARTUI_API_SKIP_CERTIFICATES,
1741
+ USE_REMOTE_DISCOVERY
1599
1742
  } = process.env;
1600
1743
  return {
1601
1744
  PROJECT_TOKEN,
@@ -1615,7 +1758,8 @@ var env_default = () => {
1615
1758
  SMARTUI_DO_NOT_USE_CAPTURED_COOKIES: SMARTUI_DO_NOT_USE_CAPTURED_COOKIES === "true",
1616
1759
  PROJECT_NAME,
1617
1760
  SMARTUI_API_PROXY,
1618
- SMARTUI_API_SKIP_CERTIFICATES: SMARTUI_API_SKIP_CERTIFICATES === "true"
1761
+ SMARTUI_API_SKIP_CERTIFICATES: SMARTUI_API_SKIP_CERTIFICATES === "true",
1762
+ USE_REMOTE_DISCOVERY: USE_REMOTE_DISCOVERY === "true"
1619
1763
  };
1620
1764
  };
1621
1765
  var logContext = {};
@@ -1710,7 +1854,7 @@ var authExec_default = (ctx) => {
1710
1854
  };
1711
1855
 
1712
1856
  // package.json
1713
- var version = "4.1.10";
1857
+ var version = "4.1.11";
1714
1858
  var package_default = {
1715
1859
  name: "@lambdatest/smartui-cli",
1716
1860
  version,
@@ -2058,7 +2202,7 @@ var httpClient = class {
2058
2202
  type: ctx.testType,
2059
2203
  source: "cli"
2060
2204
  },
2061
- async: false,
2205
+ doRemoteDiscovery: snapshot.options.doRemoteDiscovery,
2062
2206
  discoveryErrors
2063
2207
  }
2064
2208
  }, ctx.log);
@@ -2079,7 +2223,7 @@ var httpClient = class {
2079
2223
  type: ctx.testType,
2080
2224
  source: "cli"
2081
2225
  },
2082
- async: false,
2226
+ doRemoteDiscovery: snapshot.options.doRemoteDiscovery,
2083
2227
  discoveryErrors
2084
2228
  }
2085
2229
  }, ctx.log);
@@ -2317,6 +2461,10 @@ var ctx_default = (options) => {
2317
2461
  fetchResultsFileObj = "";
2318
2462
  }
2319
2463
  buildNameObj = options.buildName || "";
2464
+ if (options.userName && options.accessKey) {
2465
+ env.LT_USERNAME = options.userName;
2466
+ env.LT_ACCESS_KEY = options.accessKey;
2467
+ }
2320
2468
  } catch (error) {
2321
2469
  console.log(`[smartui] Error: ${error.message}`);
2322
2470
  process.exit();
@@ -2709,6 +2857,145 @@ var ALLOWED_RESOURCES = ["document", "stylesheet", "image", "media", "font", "ot
2709
2857
  var ALLOWED_STATUSES = [200, 201];
2710
2858
  var REQUEST_TIMEOUT = 18e5;
2711
2859
  var MIN_VIEWPORT_HEIGHT = 1080;
2860
+ function prepareSnapshot(snapshot, ctx) {
2861
+ return __async(this, null, function* () {
2862
+ let processedOptions = {};
2863
+ processedOptions.cliEnableJavascript = ctx.config.cliEnableJavaScript;
2864
+ processedOptions.ignoreHTTPSErrors = ctx.config.ignoreHTTPSErrors;
2865
+ if (ctx.config.basicAuthorization) {
2866
+ processedOptions.basicAuthorization = ctx.config.basicAuthorization;
2867
+ }
2868
+ ctx.config.allowedHostnames.push(new URL(snapshot.url).hostname);
2869
+ processedOptions.allowedHostnames = ctx.config.allowedHostnames;
2870
+ processedOptions.skipCapturedCookies = ctx.env.SMARTUI_DO_NOT_USE_CAPTURED_COOKIES;
2871
+ if (ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY)
2872
+ processedOptions.proxy = { server: ctx.env.HTTP_PROXY || ctx.env.HTTPS_PROXY };
2873
+ if (ctx.env.SMARTUI_HTTP_PROXY || ctx.env.SMARTUI_HTTPS_PROXY)
2874
+ processedOptions.proxy = { server: ctx.env.SMARTUI_HTTP_PROXY || ctx.env.SMARTUI_HTTPS_PROXY };
2875
+ let options = snapshot.options;
2876
+ let optionWarnings = /* @__PURE__ */ new Set();
2877
+ let selectors = [];
2878
+ let ignoreOrSelectDOM;
2879
+ if (options && Object.keys(options).length) {
2880
+ ctx.log.debug(`Snapshot options: ${JSON.stringify(options)}`);
2881
+ const isNotAllEmpty = (obj) => {
2882
+ var _a;
2883
+ for (let key in obj)
2884
+ if ((_a = obj[key]) == null ? void 0 : _a.length)
2885
+ return true;
2886
+ return false;
2887
+ };
2888
+ if (options.loadDomContent) {
2889
+ processedOptions.loadDomContent = true;
2890
+ }
2891
+ if (options.sessionId) {
2892
+ const sessionId = options.sessionId;
2893
+ processedOptions.sessionId = sessionId;
2894
+ if (ctx.sessionCapabilitiesMap && ctx.sessionCapabilitiesMap.has(sessionId)) {
2895
+ const sessionCapabilities = ctx.sessionCapabilitiesMap.get(sessionId);
2896
+ if (sessionCapabilities && sessionCapabilities.id) {
2897
+ processedOptions.testId = sessionCapabilities.id;
2898
+ }
2899
+ }
2900
+ }
2901
+ if (options.web && Object.keys(options.web).length) {
2902
+ processedOptions.web = {};
2903
+ if (options.web.viewports && options.web.viewports.length > 0) {
2904
+ processedOptions.web.viewports = options.web.viewports.filter(
2905
+ (viewport) => Array.isArray(viewport) && viewport.length > 0
2906
+ );
2907
+ }
2908
+ if (options.web.browsers && options.web.browsers.length > 0) {
2909
+ processedOptions.web.browsers = options.web.browsers;
2910
+ }
2911
+ }
2912
+ if (options.mobile && Object.keys(options.mobile).length) {
2913
+ processedOptions.mobile = {};
2914
+ if (options.mobile.devices && options.mobile.devices.length > 0) {
2915
+ processedOptions.mobile.devices = options.mobile.devices;
2916
+ }
2917
+ if (options.mobile.hasOwnProperty("fullPage") && typeof options.mobile.fullPage === "boolean") {
2918
+ processedOptions.mobile.fullPage = options.mobile.fullPage;
2919
+ } else {
2920
+ processedOptions.mobile.fullPage = true;
2921
+ }
2922
+ if (options.mobile.hasOwnProperty("orientation") && (options.mobile.orientation === constants_default.MOBILE_ORIENTATION_PORTRAIT || options.mobile.orientation === constants_default.MOBILE_ORIENTATION_LANDSCAPE)) {
2923
+ processedOptions.mobile.orientation = options.mobile.orientation;
2924
+ } else {
2925
+ processedOptions.mobile.orientation = constants_default.MOBILE_ORIENTATION_PORTRAIT;
2926
+ }
2927
+ }
2928
+ if (options.element && Object.keys(options.element).length) {
2929
+ if (options.element.id)
2930
+ processedOptions.element = "#" + options.element.id;
2931
+ else if (options.element.class)
2932
+ processedOptions.element = "." + options.element.class;
2933
+ else if (options.element.cssSelector)
2934
+ processedOptions.element = options.element.cssSelector;
2935
+ else if (options.element.xpath)
2936
+ processedOptions.element = "xpath=" + options.element.xpath;
2937
+ } else if (options.ignoreDOM && Object.keys(options.ignoreDOM).length && isNotAllEmpty(options.ignoreDOM)) {
2938
+ processedOptions.ignoreBoxes = {};
2939
+ ignoreOrSelectDOM = "ignoreDOM";
2940
+ } else if (options.selectDOM && Object.keys(options.selectDOM).length && isNotAllEmpty(options.selectDOM)) {
2941
+ processedOptions.selectBoxes = {};
2942
+ ignoreOrSelectDOM = "selectDOM";
2943
+ }
2944
+ if (ignoreOrSelectDOM) {
2945
+ for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
2946
+ switch (key) {
2947
+ case "id":
2948
+ selectors.push(...value.map((e) => "#" + e));
2949
+ break;
2950
+ case "class":
2951
+ selectors.push(...value.map((e) => "." + e));
2952
+ break;
2953
+ case "xpath":
2954
+ selectors.push(...value.map((e) => "xpath=" + e));
2955
+ break;
2956
+ case "cssSelector":
2957
+ selectors.push(...value);
2958
+ break;
2959
+ }
2960
+ }
2961
+ }
2962
+ if (options.ignoreType) {
2963
+ processedOptions.ignoreType = options.ignoreType;
2964
+ }
2965
+ }
2966
+ if (ctx.config.tunnel) {
2967
+ if (ctx.tunnelDetails && ctx.tunnelDetails.tunnelPort != -1 && ctx.tunnelDetails.tunnelHost != "") {
2968
+ const tunnelAddress = `http://${ctx.tunnelDetails.tunnelHost}:${ctx.tunnelDetails.tunnelPort}`;
2969
+ processedOptions.tunnelAddress = tunnelAddress;
2970
+ ctx.log.debug(`Tunnel address added to processedOptions: ${tunnelAddress}`);
2971
+ }
2972
+ }
2973
+ processedOptions.allowedAssets = ctx.config.allowedAssets;
2974
+ processedOptions.selectors = selectors;
2975
+ processedOptions.ignoreDOM = options == null ? void 0 : options.ignoreDOM;
2976
+ processedOptions.selectDOM = options == null ? void 0 : options.selectDOM;
2977
+ ctx.log.debug(`Processed options: ${JSON.stringify(processedOptions)}`);
2978
+ let renderViewports;
2979
+ if (snapshot.options && snapshot.options.web || snapshot.options && snapshot.options.mobile) {
2980
+ renderViewports = getRenderViewportsForOptions(snapshot.options);
2981
+ } else {
2982
+ renderViewports = getRenderViewports(ctx);
2983
+ }
2984
+ processedOptions.doRemoteDiscovery = true;
2985
+ return {
2986
+ processedSnapshot: {
2987
+ name: snapshot.name,
2988
+ url: snapshot.url,
2989
+ dom: Buffer.from(snapshot.dom.html).toString("base64"),
2990
+ resources: {},
2991
+ options: processedOptions,
2992
+ cookies: Buffer.from(snapshot.dom.cookies).toString("base64"),
2993
+ renderViewports
2994
+ },
2995
+ warnings: [...optionWarnings, ...snapshot.dom.warnings]
2996
+ };
2997
+ });
2998
+ }
2712
2999
  function processSnapshot(snapshot, ctx) {
2713
3000
  return __async(this, null, function* () {
2714
3001
  var _a, _b;
@@ -2722,6 +3009,7 @@ function processSnapshot(snapshot, ctx) {
2722
3009
  snapshotUUID: "",
2723
3010
  browsers: {}
2724
3011
  };
3012
+ let processedOptions = {};
2725
3013
  let globalViewport = "";
2726
3014
  let globalBrowser = constants_default.CHROME;
2727
3015
  let launchOptions = {
@@ -2913,7 +3201,6 @@ function processSnapshot(snapshot, ctx) {
2913
3201
  }));
2914
3202
  let options = snapshot.options;
2915
3203
  let optionWarnings = /* @__PURE__ */ new Set();
2916
- let processedOptions = {};
2917
3204
  let selectors = [];
2918
3205
  let ignoreOrSelectDOM;
2919
3206
  let ignoreOrSelectBoxes;
@@ -3458,7 +3745,19 @@ var Queue = class {
3458
3745
  useCapsBuildId = true;
3459
3746
  }
3460
3747
  }
3461
- let { processedSnapshot, warnings, discoveryErrors } = yield processSnapshot(snapshot, this.ctx);
3748
+ let processedSnapshot, warnings, discoveryErrors;
3749
+ if (this.ctx.env.USE_REMOTE_DISCOVERY) {
3750
+ this.ctx.log.debug(`Using remote discovery`);
3751
+ let result = yield prepareSnapshot(snapshot, this.ctx);
3752
+ processedSnapshot = result.processedSnapshot;
3753
+ warnings = result.warnings;
3754
+ } else {
3755
+ this.ctx.log.debug(`Using local discovery`);
3756
+ let result = yield processSnapshot(snapshot, this.ctx);
3757
+ processedSnapshot = result.processedSnapshot;
3758
+ warnings = result.warnings;
3759
+ discoveryErrors = result.discoveryErrors;
3760
+ }
3462
3761
  if (useCapsBuildId) {
3463
3762
  if (useKafkaFlowCaps) {
3464
3763
  const snapshotUuid = uuid.v4();
@@ -3580,7 +3879,7 @@ var startTunnel_default = (ctx) => {
3580
3879
 
3581
3880
  // src/commander/exec.ts
3582
3881
  var command = new commander.Command();
3583
- 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) {
3882
+ 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").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(execCommand, _, command7) {
3584
3883
  return __async(this, null, function* () {
3585
3884
  var _a;
3586
3885
  const options = command7.optsWithGlobals();
@@ -3692,7 +3991,7 @@ function createWebFigmaConfig(filepath) {
3692
3991
  console.log(`Created figma web config: ${filepath}`);
3693
3992
  }
3694
3993
  function verifyFigmaWebConfig(ctx) {
3695
- var _a;
3994
+ var _a, _b;
3696
3995
  if (ctx.env.FIGMA_TOKEN == "") {
3697
3996
  throw new Error("Missing FIGMA_TOKEN in Environment Variables");
3698
3997
  }
@@ -3717,17 +4016,44 @@ function verifyFigmaWebConfig(ctx) {
3717
4016
  if (new Set(screenshots).size !== screenshots.length) {
3718
4017
  throw new Error("Found duplicate screenshot names in figma config");
3719
4018
  }
3720
- return true;
4019
+ let mobileConfig = ((_b = ctx.config) == null ? void 0 : _b.mobile) || {};
4020
+ if (Array.isArray(mobileConfig)) {
4021
+ for (const config of mobileConfig) {
4022
+ const deviceName = config.name;
4023
+ if (constants_default.SUPPORTED_MOBILE_DEVICES[deviceName]) {
4024
+ const deviceData = constants_default.SUPPORTED_MOBILE_DEVICES[deviceName];
4025
+ config.width = deviceData.viewport.width;
4026
+ config.height = deviceData.viewport.height;
4027
+ }
4028
+ }
4029
+ }
3721
4030
  }
3722
4031
  function isValidArray(input) {
3723
4032
  return Array.isArray(input) && input.length > 0;
3724
4033
  }
4034
+ function createAppFigmaConfig(filepath) {
4035
+ filepath = filepath || ".smartui.json";
4036
+ let filetype = path2__default.default.extname(filepath);
4037
+ if (filetype != ".json") {
4038
+ console.log("Error: figma app config file must have .json extension");
4039
+ return;
4040
+ }
4041
+ if (fs5__default.default.existsSync(filepath)) {
4042
+ console.log(`Error: figma app config already exists: ${filepath}`);
4043
+ console.log(`To create a new figma app config, please specify the file name like: 'smartui config:create-figma-app <fileName>.json'`);
4044
+ return;
4045
+ }
4046
+ fs5__default.default.mkdirSync(path2__default.default.dirname(filepath), { recursive: true });
4047
+ fs5__default.default.writeFileSync(filepath, JSON.stringify(constants_default.APP_FIGMA_CONFIG, null, 2) + "\n");
4048
+ console.log(`Created figma app config: ${filepath}`);
4049
+ }
3725
4050
 
3726
4051
  // src/commander/config.ts
3727
4052
  var configWeb = new commander.Command();
3728
4053
  var configStatic = new commander.Command();
3729
4054
  var configFigma = new commander.Command();
3730
4055
  var configWebFigma = new commander.Command();
4056
+ var configAppFigma = new commander.Command();
3731
4057
  configWeb.name("config:create").description("Create SmartUI config file").argument("[filepath]", "Optional config filepath").action(function(filepath, options) {
3732
4058
  return __async(this, null, function* () {
3733
4059
  createConfig(filepath);
@@ -3748,6 +4074,11 @@ configWebFigma.name("config:create-figma-web").description("Create figma config
3748
4074
  createWebFigmaConfig(filepath);
3749
4075
  });
3750
4076
  });
4077
+ configAppFigma.name("config:create-figma-app").description("Create figma config file for mobile apps").argument("[filepath]", "Optional config filepath").action(function(filepath, options) {
4078
+ return __async(this, null, function* () {
4079
+ createAppFigmaConfig(filepath);
4080
+ });
4081
+ });
3751
4082
  var auth_default = (ctx) => {
3752
4083
  return {
3753
4084
  title: `Authenticating with SmartUI`,
@@ -4162,7 +4493,7 @@ var captureScreenshots_default = (ctx) => {
4162
4493
 
4163
4494
  // src/commander/capture.ts
4164
4495
  var command2 = new commander.Command();
4165
- 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) {
4496
+ 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").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(file, _, command7) {
4166
4497
  return __async(this, null, function* () {
4167
4498
  var _a, _b;
4168
4499
  const options = command7.optsWithGlobals();
@@ -4255,7 +4586,7 @@ command3.name("upload").description("Upload screenshots from given directory").a
4255
4586
  return val.split(",").map((ext) => ext.trim().toLowerCase());
4256
4587
  }).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
4257
4588
  return val.split(",").map((pattern) => pattern.trim());
4258
- }).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) {
4589
+ }).option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").option("--buildName <string>", "Specify the build name").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(directory, _, command7) {
4259
4590
  return __async(this, null, function* () {
4260
4591
  const options = command7.optsWithGlobals();
4261
4592
  if (options.buildName === "") {
@@ -4370,6 +4701,9 @@ var uploadWebFigma_default = (ctx) => __async(void 0, null, function* () {
4370
4701
  };
4371
4702
  const responseData = yield ctx.client.processWebFigma(requestBody, ctx.log);
4372
4703
  ctx.log.debug("responseData : " + JSON.stringify(responseData));
4704
+ if (responseData && responseData.error && responseData.error.message) {
4705
+ throw new Error(responseData.error.message);
4706
+ }
4373
4707
  if (responseData.data.message == "success") {
4374
4708
  results = responseData.data.message;
4375
4709
  ctx.build = {
@@ -4460,8 +4794,82 @@ var uploadWebFigma_default2 = (ctx) => {
4460
4794
  exitOnError: true
4461
4795
  };
4462
4796
  };
4797
+
4798
+ // src/lib/uploadAppFigma.ts
4799
+ var uploadAppFigma_default = (ctx) => __async(void 0, null, function* () {
4800
+ var _a, _b;
4801
+ const figmaConfig = ctx.config && ((_a = ctx.config) == null ? void 0 : _a.figma) || {};
4802
+ const mobileConfig = ctx.config && ((_b = ctx.config) == null ? void 0 : _b.mobile) || {};
4803
+ let results = "";
4804
+ const buildName = ctx.options.buildName;
4805
+ if (figmaConfig && figmaConfig.configs && figmaConfig.configs.length > 0) {
4806
+ const authToken = `Basic ${Buffer.from(`${ctx.env.LT_USERNAME}:${ctx.env.LT_ACCESS_KEY}`).toString("base64")}`;
4807
+ const requestBody = {
4808
+ figma_token: ctx.env.FIGMA_TOKEN,
4809
+ auth: authToken,
4810
+ build_name: buildName,
4811
+ mobile: mobileConfig,
4812
+ figma: figmaConfig,
4813
+ smartIgnore: ctx.config.smartIgnore,
4814
+ git: ctx.git,
4815
+ platformType: "app"
4816
+ };
4817
+ const responseData = yield ctx.client.processWebFigma(requestBody, ctx.log);
4818
+ ctx.log.debug("responseData : " + JSON.stringify(responseData));
4819
+ if (responseData && responseData.error && responseData.error.message) {
4820
+ throw new Error(responseData.error.message);
4821
+ }
4822
+ if (responseData.data.message == "success") {
4823
+ results = responseData.data.message;
4824
+ ctx.build = {
4825
+ id: responseData.data.buildId,
4826
+ url: responseData.data.buildURL || "https://smartui.lambdatestinternal.com",
4827
+ baseline: responseData.data.baseline ? responseData.data.baseline : false
4828
+ };
4829
+ }
4830
+ } else {
4831
+ throw new Error("No Figma configuration found in config file");
4832
+ }
4833
+ return results;
4834
+ });
4835
+
4836
+ // src/tasks/uploadAppFigma.ts
4837
+ var uploadAppFigma_default2 = (ctx) => {
4838
+ return {
4839
+ title: "Processing App Figma",
4840
+ task: (ctx2, task) => __async(void 0, null, function* () {
4841
+ try {
4842
+ ctx2.task = task;
4843
+ updateLogContext({ task: "upload-figma-app" });
4844
+ let results = yield uploadAppFigma_default(ctx2);
4845
+ if (results != "success") {
4846
+ throw new Error("Uploading App Figma Screenshot failed");
4847
+ }
4848
+ if (ctx2.build.id) {
4849
+ task.output = chalk__default.default.gray(`Build Id: ${ctx2.build.id}`);
4850
+ let figmaOutput = yield fetchFigma_default(ctx2);
4851
+ const jsonObject = JSON.parse(figmaOutput);
4852
+ let output = JSON.stringify(jsonObject, null, 2);
4853
+ task.output = task.output + "\n" + chalk__default.default.green(`${output}`);
4854
+ }
4855
+ if (ctx2.options.fetchResults) {
4856
+ startPolling(ctx2, "", false, "");
4857
+ }
4858
+ task.title = "App Figma images uploaded successfully to SmartUI";
4859
+ ctx2.log.debug(`App Figma processed: ${results}`);
4860
+ } catch (error) {
4861
+ ctx2.log.debug(error);
4862
+ task.output = chalk__default.default.gray(`${error.message}`);
4863
+ throw new Error("Uploading App Figma Screenshots failed");
4864
+ }
4865
+ }),
4866
+ rendererOptions: { persistentOutput: true },
4867
+ exitOnError: true
4868
+ };
4869
+ };
4463
4870
  var uploadFigma = new commander.Command();
4464
4871
  var uploadWebFigmaCommand = new commander.Command();
4872
+ var uploadAppFigmaCommand = new commander.Command();
4465
4873
  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) {
4466
4874
  return __async(this, null, function* () {
4467
4875
  var _a, _b;
@@ -4504,7 +4912,7 @@ uploadFigma.name("upload-figma").description("Capture screenshots of static site
4504
4912
  }
4505
4913
  });
4506
4914
  });
4507
- 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").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(file, _, command7) {
4915
+ uploadWebFigmaCommand.name("upload-figma-web").description("Capture figma screenshots into CLI build").argument("<file>", "figma config config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(file, _, command7) {
4508
4916
  return __async(this, null, function* () {
4509
4917
  var _a;
4510
4918
  let ctx = ctx_default(command7.optsWithGlobals());
@@ -4556,6 +4964,58 @@ uploadWebFigmaCommand.name("upload-figma-web").description("Capture screenshots
4556
4964
  }
4557
4965
  });
4558
4966
  });
4967
+ uploadAppFigmaCommand.name("upload-figma-app").description("Capture figma screenshots into App Build").argument("<file>", "figma config config file").option("--markBaseline", "Mark the uploaded images as baseline").option("--buildName <buildName>", "Name of the build").option("--fetch-results [filename]", "Fetch results and optionally specify an output file, e.g., <filename>.json").action(function(file, _, command7) {
4968
+ return __async(this, null, function* () {
4969
+ var _a;
4970
+ let ctx = ctx_default(command7.optsWithGlobals());
4971
+ if (!fs5__default.default.existsSync(file)) {
4972
+ console.log(`Error: figma-app config file ${file} not found.`);
4973
+ return;
4974
+ }
4975
+ try {
4976
+ ctx.config = JSON.parse(fs5__default.default.readFileSync(file, "utf8"));
4977
+ ctx.log.info(JSON.stringify(ctx.config));
4978
+ if (!validateAppFigmaConfig(ctx.config)) {
4979
+ ctx.log.debug(JSON.stringify(validateAppFigmaConfig.errors, null, 2));
4980
+ (_a = validateAppFigmaConfig.errors) == null ? void 0 : _a.forEach((error) => {
4981
+ if (error.keyword === "additionalProperties") {
4982
+ ctx.log.warn(`Additional property "${error.params.additionalProperty}" is not allowed.`);
4983
+ } else {
4984
+ const validationError = error.message;
4985
+ throw new Error(validationError || "Invalid figma-app config found in file : " + file);
4986
+ }
4987
+ });
4988
+ }
4989
+ verifyFigmaWebConfig(ctx);
4990
+ } catch (error) {
4991
+ ctx.log.error(chalk__default.default.red(`Invalid figma-app config; ${error.message}`));
4992
+ return;
4993
+ }
4994
+ let tasks = new listr2.Listr(
4995
+ [
4996
+ auth_default(),
4997
+ getGitInfo_default(),
4998
+ uploadAppFigma_default2(),
4999
+ finalizeBuild_default()
5000
+ ],
5001
+ {
5002
+ rendererOptions: {
5003
+ icon: {
5004
+ [listr2.ListrDefaultRendererLogLevels.OUTPUT]: `\u2192`
5005
+ },
5006
+ color: {
5007
+ [listr2.ListrDefaultRendererLogLevels.OUTPUT]: listr2.color.gray
5008
+ }
5009
+ }
5010
+ }
5011
+ );
5012
+ try {
5013
+ yield tasks.run(ctx);
5014
+ } catch (error) {
5015
+ console.log("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
5016
+ }
5017
+ });
5018
+ });
4559
5019
  var command4 = new commander.Command();
4560
5020
  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() {
4561
5021
  return __async(this, null, function* () {
@@ -4661,7 +5121,7 @@ var ping_default = command6;
4661
5121
 
4662
5122
  // src/commander/commander.ts
4663
5123
  var program = new commander.Command();
4664
- 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);
5124
+ 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(configAppFigma).addCommand(uploadWebFigmaCommand).addCommand(uploadAppFigmaCommand);
4665
5125
  var commander_default = program;
4666
5126
  (function() {
4667
5127
  return __async(this, null, function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lambdatest/smartui-cli",
3
- "version": "4.1.10",
3
+ "version": "4.1.11",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"