@lambdatest/smartui-cli 4.1.27 → 4.1.29

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 +396 -53
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1716,6 +1716,130 @@ function calculateVariantCountFromSnapshot(snapshot, globalConfig) {
1716
1716
  }
1717
1717
  return variantCount;
1718
1718
  }
1719
+ function startPdfPolling(ctx) {
1720
+ console.log(chalk__default.default.yellow("\nFetching PDF test results..."));
1721
+ ctx.log.debug(`Starting fetching results for build: ${ctx.build.id || ctx.build.name}`);
1722
+ if (!ctx.build.id && !ctx.build.name) {
1723
+ ctx.log.error(chalk__default.default.red("Error: Build information not found for fetching results"));
1724
+ return;
1725
+ }
1726
+ if (!ctx.env.LT_USERNAME || !ctx.env.LT_ACCESS_KEY) {
1727
+ console.log(chalk__default.default.red("Error: LT_USERNAME and LT_ACCESS_KEY environment variables are required for fetching results"));
1728
+ return;
1729
+ }
1730
+ let attempts = 0;
1731
+ const maxAttempts = 60;
1732
+ console.log(chalk__default.default.yellow("Waiting for results..."));
1733
+ const interval = setInterval(() => __async(this, null, function* () {
1734
+ attempts++;
1735
+ try {
1736
+ const response = yield ctx.client.fetchPdfResults(ctx);
1737
+ if (response.screenshots) {
1738
+ clearInterval(interval);
1739
+ const pdfGroups = groupScreenshotsByPdf(response.screenshots);
1740
+ const pdfsWithMismatches = countPdfsWithMismatches(pdfGroups);
1741
+ const pagesWithMismatches = countPagesWithMismatches(response.screenshots);
1742
+ console.log(chalk__default.default.green("\n\u2713 PDF Test Results:"));
1743
+ console.log(chalk__default.default.green(`Build Name: ${response.build.name}`));
1744
+ console.log(chalk__default.default.green(`Project Name: ${response.project.name}`));
1745
+ console.log(chalk__default.default.green(`Total PDFs: ${Object.keys(pdfGroups).length}`));
1746
+ console.log(chalk__default.default.green(`Total Pages: ${response.screenshots.length}`));
1747
+ if (pdfsWithMismatches > 0 || pagesWithMismatches > 0) {
1748
+ console.log(chalk__default.default.yellow(`${pdfsWithMismatches} PDFs and ${pagesWithMismatches} Pages in build ${response.build.name} have changes present.`));
1749
+ } else {
1750
+ console.log(chalk__default.default.green("All PDFs match the baseline."));
1751
+ }
1752
+ Object.entries(pdfGroups).forEach(([pdfName, pages]) => {
1753
+ const hasMismatch = pages.some((page) => page.mismatch_percentage > 0);
1754
+ const statusColor = hasMismatch ? chalk__default.default.yellow : chalk__default.default.green;
1755
+ console.log(statusColor(`
1756
+ \u{1F4C4} ${pdfName} (${pages.length} pages)`));
1757
+ pages.forEach((page) => {
1758
+ const pageStatusColor = page.mismatch_percentage > 0 ? chalk__default.default.yellow : chalk__default.default.green;
1759
+ console.log(pageStatusColor(` - Page ${getPageNumber(page.screenshot_name)}: ${page.status} (Mismatch: ${page.mismatch_percentage}%)`));
1760
+ });
1761
+ });
1762
+ const formattedResults = {
1763
+ status: "success",
1764
+ data: {
1765
+ buildId: response.build.id,
1766
+ buildName: response.build.name,
1767
+ projectName: response.project.name,
1768
+ buildStatus: response.build.build_satus,
1769
+ pdfs: formatPdfsForOutput(pdfGroups)
1770
+ }
1771
+ };
1772
+ if (ctx.options.fetchResults && ctx.options.fetchResultsFileName) {
1773
+ const filename = ctx.options.fetchResultsFileName !== "" ? ctx.options.fetchResultsFileName : "pdf-results.json";
1774
+ fs5__default.default.writeFileSync(filename, JSON.stringify(formattedResults, null, 2));
1775
+ console.log(chalk__default.default.green(`
1776
+ Results saved to ${filename}`));
1777
+ }
1778
+ return;
1779
+ }
1780
+ if (attempts >= maxAttempts) {
1781
+ clearInterval(interval);
1782
+ console.log(chalk__default.default.red("\nTimeout: Could not fetch PDF results after 5 minutes"));
1783
+ return;
1784
+ }
1785
+ } catch (error) {
1786
+ ctx.log.debug(`Error during polling: ${error.message}`);
1787
+ if (attempts >= maxAttempts) {
1788
+ clearInterval(interval);
1789
+ console.log(chalk__default.default.red("\nTimeout: Could not fetch PDF results after 5 minutes"));
1790
+ if (error.response && error.response.data) {
1791
+ console.log(chalk__default.default.red(`Error details: ${JSON.stringify(error.response.data)}`));
1792
+ } else {
1793
+ console.log(chalk__default.default.red(`Error details: ${error.message}`));
1794
+ }
1795
+ return;
1796
+ }
1797
+ process.stdout.write(chalk__default.default.yellow("."));
1798
+ }
1799
+ }), 1e4);
1800
+ }
1801
+ function groupScreenshotsByPdf(screenshots) {
1802
+ const pdfGroups = {};
1803
+ screenshots.forEach((screenshot) => {
1804
+ const pdfName = screenshot.screenshot_name.split("#")[0];
1805
+ if (!pdfGroups[pdfName]) {
1806
+ pdfGroups[pdfName] = [];
1807
+ }
1808
+ pdfGroups[pdfName].push(screenshot);
1809
+ });
1810
+ return pdfGroups;
1811
+ }
1812
+ function countPdfsWithMismatches(pdfGroups) {
1813
+ let count = 0;
1814
+ Object.values(pdfGroups).forEach((pages) => {
1815
+ if (pages.some((page) => page.mismatch_percentage > 0)) {
1816
+ count++;
1817
+ }
1818
+ });
1819
+ return count;
1820
+ }
1821
+ function countPagesWithMismatches(screenshots) {
1822
+ return screenshots.filter((screenshot) => screenshot.mismatch_percentage > 0).length;
1823
+ }
1824
+ function formatPdfsForOutput(pdfGroups) {
1825
+ return Object.entries(pdfGroups).map(([pdfName, pages]) => {
1826
+ return {
1827
+ pdfName,
1828
+ pageCount: pages.length,
1829
+ pages: pages.map((page) => ({
1830
+ pageNumber: getPageNumber(page.screenshot_name),
1831
+ screenshotId: page.captured_image_id,
1832
+ mismatchPercentage: page.mismatch_percentage,
1833
+ status: page.status,
1834
+ screenshotUrl: page.shareable_link
1835
+ }))
1836
+ };
1837
+ });
1838
+ }
1839
+ function getPageNumber(screenshotName) {
1840
+ const parts = screenshotName.split("#");
1841
+ return parts.length > 1 ? parts[1] : "1";
1842
+ }
1719
1843
 
1720
1844
  // src/lib/server.ts
1721
1845
  var uploadDomToS3ViaEnv = process.env.USE_LAMBDA_INTERNAL || false;
@@ -1764,7 +1888,6 @@ var server_default = (ctx) => __async(void 0, null, function* () {
1764
1888
  }
1765
1889
  } catch (error) {
1766
1890
  ctx.log.debug(`Failed to fetch capabilities for sessionId ${sessionId}: ${error.message}`);
1767
- console.log(`Failed to fetch capabilities for sessionId ${sessionId}: ${error.message}`);
1768
1891
  }
1769
1892
  }
1770
1893
  if (capsBuildId && capsBuildId !== "") {
@@ -1813,19 +1936,39 @@ var server_default = (ctx) => __async(void 0, null, function* () {
1813
1936
  }
1814
1937
  }, 1e3);
1815
1938
  });
1816
- yield ctx.client.finalizeBuild(ctx.build.id, ctx.totalSnapshots, ctx.log);
1817
- yield (_b = ctx.browser) == null ? void 0 : _b.close();
1939
+ for (const [sessionId, capabilities] of ctx.sessionCapabilitiesMap.entries()) {
1940
+ try {
1941
+ const buildId = (capabilities == null ? void 0 : capabilities.buildId) || "";
1942
+ const projectToken = (capabilities == null ? void 0 : capabilities.projectToken) || "";
1943
+ const totalSnapshots = (capabilities == null ? void 0 : capabilities.snapshotCount) || 0;
1944
+ const sessionBuildUrl = (capabilities == null ? void 0 : capabilities.buildURL) || "";
1945
+ const testId = (capabilities == null ? void 0 : capabilities.id) || "";
1946
+ if (buildId && projectToken) {
1947
+ yield ctx.client.finalizeBuildForCapsWithToken(buildId, totalSnapshots, projectToken, ctx.log);
1948
+ }
1949
+ if (testId && buildId) {
1950
+ buildUrls += `TestId ${testId}: ${sessionBuildUrl}
1951
+ `;
1952
+ }
1953
+ } catch (error) {
1954
+ ctx.log.debug(`Error finalizing build for session ${sessionId}: ${error.message}`);
1955
+ }
1956
+ }
1957
+ if (ctx.build && ctx.build.id) {
1958
+ yield ctx.client.finalizeBuild(ctx.build.id, ctx.totalSnapshots, ctx.log);
1959
+ let uploadCLILogsToS3 = ((_b = ctx == null ? void 0 : ctx.config) == null ? void 0 : _b.useLambdaInternal) || uploadDomToS3ViaEnv;
1960
+ if (!uploadCLILogsToS3) {
1961
+ ctx.log.debug(`Log file to be uploaded`);
1962
+ let resp = yield ctx.client.getS3PreSignedURL(ctx);
1963
+ yield ctx.client.uploadLogs(ctx, resp.data.url);
1964
+ } else {
1965
+ ctx.log.debug(`Skipping upload of CLI logs as useLambdaInternal is set`);
1966
+ }
1967
+ }
1968
+ yield (_c = ctx.browser) == null ? void 0 : _c.close();
1818
1969
  if (ctx.server) {
1819
1970
  ctx.server.close();
1820
1971
  }
1821
- let uploadCLILogsToS3 = ((_c = ctx == null ? void 0 : ctx.config) == null ? void 0 : _c.useLambdaInternal) || uploadDomToS3ViaEnv;
1822
- if (!uploadCLILogsToS3) {
1823
- ctx.log.debug(`Log file to be uploaded`);
1824
- let resp = yield ctx.client.getS3PreSignedURL(ctx);
1825
- yield ctx.client.uploadLogs(ctx, resp.data.url);
1826
- } else {
1827
- ctx.log.debug(`Skipping upload of CLI logs as useLambdaInternal is set`);
1828
- }
1829
1972
  if (pingIntervalId !== null) {
1830
1973
  clearInterval(pingIntervalId);
1831
1974
  ctx.log.debug("Ping polling stopped immediately.");
@@ -1942,6 +2085,7 @@ var env_default = () => {
1942
2085
  const {
1943
2086
  PROJECT_TOKEN = "",
1944
2087
  SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
2088
+ SMARTUI_UPLOAD_URL = "https://api.lambdatest.com",
1945
2089
  SMARTUI_GIT_INFO_FILEPATH,
1946
2090
  SMARTUI_DO_NOT_USE_CAPTURED_COOKIES,
1947
2091
  HTTP_PROXY,
@@ -1964,6 +2108,7 @@ var env_default = () => {
1964
2108
  return {
1965
2109
  PROJECT_TOKEN,
1966
2110
  SMARTUI_CLIENT_API_URL,
2111
+ SMARTUI_UPLOAD_URL,
1967
2112
  SMARTUI_GIT_INFO_FILEPATH,
1968
2113
  HTTP_PROXY,
1969
2114
  HTTPS_PROXY,
@@ -2076,7 +2221,7 @@ var authExec_default = (ctx) => {
2076
2221
  };
2077
2222
 
2078
2223
  // package.json
2079
- var version = "4.1.27";
2224
+ var version = "4.1.29";
2080
2225
  var package_default = {
2081
2226
  name: "@lambdatest/smartui-cli",
2082
2227
  version,
@@ -2133,6 +2278,18 @@ var package_default = {
2133
2278
  }
2134
2279
  };
2135
2280
  var httpClient = class {
2281
+ handleHttpError(error, log2) {
2282
+ var _a;
2283
+ if (error && error.response) {
2284
+ log2.debug(`http response error: ${JSON.stringify({
2285
+ status: error.response.status,
2286
+ body: error.response.data
2287
+ })}`);
2288
+ throw new Error(((_a = error.response.data) == null ? void 0 : _a.message) || error.response.data || `HTTP ${error.response.status} error`);
2289
+ }
2290
+ log2.debug(`http request failed: ${error.message}`);
2291
+ throw new Error(error.message);
2292
+ }
2136
2293
  constructor({ SMARTUI_CLIENT_API_URL, PROJECT_TOKEN, PROJECT_NAME, LT_USERNAME, LT_ACCESS_KEY, SMARTUI_API_PROXY, SMARTUI_API_SKIP_CERTIFICATES }) {
2137
2294
  this.projectToken = PROJECT_TOKEN || "";
2138
2295
  this.projectName = PROJECT_NAME || "";
@@ -2189,6 +2346,8 @@ var httpClient = class {
2189
2346
  return this.axiosInstance(config);
2190
2347
  }
2191
2348
  return Promise.reject(error);
2349
+ } else {
2350
+ return Promise.reject(error);
2192
2351
  }
2193
2352
  })
2194
2353
  );
@@ -2245,7 +2404,11 @@ var httpClient = class {
2245
2404
  }
2246
2405
  const response = yield this.request({
2247
2406
  url: "/token/verify",
2248
- method: "GET"
2407
+ method: "GET",
2408
+ headers: {
2409
+ userName: env.LT_USERNAME,
2410
+ accessKey: env.LT_ACCESS_KEY
2411
+ }
2249
2412
  }, log2);
2250
2413
  if (response && response.projectToken) {
2251
2414
  this.projectToken = response.projectToken;
@@ -2263,8 +2426,8 @@ var httpClient = class {
2263
2426
  return __async(this, null, function* () {
2264
2427
  var _a, _b;
2265
2428
  let authResult = 1;
2266
- let userName = "";
2267
- let passWord = "";
2429
+ let userName = ctx.env.LT_USERNAME;
2430
+ let passWord = ctx.env.LT_ACCESS_KEY;
2268
2431
  if (ctx.config.tunnel) {
2269
2432
  if (((_a = ctx.config.tunnel) == null ? void 0 : _a.user) && ((_b = ctx.config.tunnel) == null ? void 0 : _b.key)) {
2270
2433
  userName = ctx.config.tunnel.user;
@@ -2308,10 +2471,14 @@ var httpClient = class {
2308
2471
  }
2309
2472
  });
2310
2473
  }
2311
- createBuild(git, config, log2, buildName, isStartExec, smartGit, markBaseline, baselineBuild, scheduled) {
2474
+ createBuild(git, config, log2, buildName, isStartExec, smartGit, markBaseline, baselineBuild, scheduled, userName, accessKey) {
2312
2475
  return this.request({
2313
2476
  url: "/build",
2314
2477
  method: "POST",
2478
+ headers: {
2479
+ userName,
2480
+ accessKey
2481
+ },
2315
2482
  data: {
2316
2483
  git,
2317
2484
  config,
@@ -2478,7 +2645,7 @@ var httpClient = class {
2478
2645
  }
2479
2646
  }, ctx.log);
2480
2647
  }
2481
- uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, log2) {
2648
+ uploadScreenshot({ id: buildId, name: buildName, baseline }, ssPath, ssName, browserName, viewport, url = "", log2) {
2482
2649
  browserName = browserName === constants_default.SAFARI ? constants_default.WEBKIT : browserName;
2483
2650
  const file = fs5__default.default.readFileSync(ssPath);
2484
2651
  const form = new FormData__default.default();
@@ -2489,6 +2656,7 @@ var httpClient = class {
2489
2656
  form.append("buildName", buildName);
2490
2657
  form.append("screenshotName", ssName);
2491
2658
  form.append("baseline", baseline.toString());
2659
+ form.append("pageUrl", url);
2492
2660
  return this.axiosInstance.request({
2493
2661
  url: `/screenshot`,
2494
2662
  method: "POST",
@@ -2706,6 +2874,61 @@ var httpClient = class {
2706
2874
  }
2707
2875
  }, ctx.log);
2708
2876
  }
2877
+ uploadPdf(ctx, form, buildName) {
2878
+ return __async(this, null, function* () {
2879
+ form.append("projectToken", this.projectToken);
2880
+ if (ctx.build.name !== void 0 && ctx.build.name !== "") {
2881
+ form.append("buildName", buildName);
2882
+ }
2883
+ try {
2884
+ const response = yield this.axiosInstance.request({
2885
+ url: ctx.env.SMARTUI_UPLOAD_URL + "/pdf/upload",
2886
+ method: "POST",
2887
+ headers: form.getHeaders(),
2888
+ data: form
2889
+ });
2890
+ ctx.log.debug(`http response: ${JSON.stringify({
2891
+ status: response.status,
2892
+ headers: response.headers,
2893
+ body: response.data
2894
+ })}`);
2895
+ return response.data;
2896
+ } catch (error) {
2897
+ this.handleHttpError(error, ctx.log);
2898
+ }
2899
+ });
2900
+ }
2901
+ fetchPdfResults(ctx) {
2902
+ return __async(this, null, function* () {
2903
+ const params = {};
2904
+ if (ctx.build.projectId) {
2905
+ params.project_id = ctx.build.projectId;
2906
+ } else {
2907
+ throw new Error("Project ID not found to fetch PDF results");
2908
+ }
2909
+ params.build_id = ctx.build.id;
2910
+ const auth = Buffer.from(`${this.username}:${this.accessKey}`).toString("base64");
2911
+ try {
2912
+ const response = yield axios__default.default.request({
2913
+ url: ctx.env.SMARTUI_UPLOAD_URL + "/smartui/2.0/build/screenshots",
2914
+ method: "GET",
2915
+ params,
2916
+ headers: {
2917
+ "accept": "application/json",
2918
+ "Authorization": `Basic ${auth}`
2919
+ }
2920
+ });
2921
+ ctx.log.debug(`http response: ${JSON.stringify({
2922
+ status: response.status,
2923
+ headers: response.headers,
2924
+ body: response.data
2925
+ })}`);
2926
+ return response.data;
2927
+ } catch (error) {
2928
+ this.handleHttpError(error, ctx.log);
2929
+ }
2930
+ });
2931
+ }
2709
2932
  };
2710
2933
  var ctx_default = (options) => {
2711
2934
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
@@ -2880,10 +3103,10 @@ var ctx_default = (options) => {
2880
3103
  mergeByBuild: false
2881
3104
  };
2882
3105
  };
2883
- function executeCommand(command10) {
3106
+ function executeCommand(command11) {
2884
3107
  let dst = process.cwd();
2885
3108
  try {
2886
- return child_process.execSync(command10, {
3109
+ return child_process.execSync(command11, {
2887
3110
  cwd: dst,
2888
3111
  stdio: ["ignore"],
2889
3112
  encoding: "utf-8"
@@ -2892,11 +3115,12 @@ function executeCommand(command10) {
2892
3115
  throw new Error(error.message);
2893
3116
  }
2894
3117
  }
2895
- function isGitRepo() {
3118
+ function isGitRepo(ctx) {
2896
3119
  try {
2897
3120
  executeCommand("git status");
2898
3121
  return true;
2899
3122
  } catch (error) {
3123
+ setNonGitInfo(ctx);
2900
3124
  return false;
2901
3125
  }
2902
3126
  }
@@ -2928,8 +3152,8 @@ var git_default = (ctx) => {
2928
3152
  } else {
2929
3153
  const splitCharacter = "<##>";
2930
3154
  const prettyFormat = ["%h", "%H", "%s", "%f", "%b", "%at", "%ct", "%an", "%ae", "%cn", "%ce", "%N", ""];
2931
- const command10 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
2932
- let res = executeCommand(command10).split(splitCharacter);
3155
+ const command11 = 'git log -1 --pretty=format:"' + prettyFormat.join(splitCharacter) + '" && git rev-parse --abbrev-ref HEAD && git tag --contains HEAD';
3156
+ let res = executeCommand(command11).split(splitCharacter);
2933
3157
  var branchAndTags = res[res.length - 1].split("\n").filter((n) => n);
2934
3158
  var branch = ctx.env.CURRENT_BRANCH || branchAndTags[0];
2935
3159
  branchAndTags.slice(1);
@@ -2947,11 +3171,30 @@ var git_default = (ctx) => {
2947
3171
  };
2948
3172
  }
2949
3173
  };
3174
+ function setNonGitInfo(ctx) {
3175
+ let branch = ctx.env.CURRENT_BRANCH || "unknown-branch";
3176
+ if (ctx.options.markBaseline) {
3177
+ ctx.env.BASELINE_BRANCH = branch;
3178
+ ctx.env.SMART_GIT = false;
3179
+ }
3180
+ let githubURL;
3181
+ if (ctx.options.githubURL && ctx.options.githubURL.startsWith("https://")) {
3182
+ githubURL = ctx.options.githubURL;
3183
+ }
3184
+ ctx.git = {
3185
+ branch,
3186
+ commitId: "-",
3187
+ commitAuthor: "-",
3188
+ commitMessage: "-",
3189
+ githubURL: githubURL ? githubURL : "",
3190
+ baselineBranch: ctx.options.baselineBranch || ctx.env.BASELINE_BRANCH || ""
3191
+ };
3192
+ }
2950
3193
  var getGitInfo_default = (ctx) => {
2951
3194
  return {
2952
3195
  title: `Fetching git repo details`,
2953
3196
  skip: (ctx2) => {
2954
- return !isGitRepo() && !ctx2.env.SMARTUI_GIT_INFO_FILEPATH ? "[SKIPPED] Fetching git repo details; not a git repo" : "";
3197
+ return !isGitRepo(ctx2) && !ctx2.env.SMARTUI_GIT_INFO_FILEPATH ? "[SKIPPED] Fetching git repo details; not a git repo" : "";
2955
3198
  },
2956
3199
  task: (ctx2, task) => __async(void 0, null, function* () {
2957
3200
  if (ctx2.env.CURRENT_BRANCH && ctx2.env.CURRENT_BRANCH.trim() === "") {
@@ -2981,7 +3224,7 @@ var createBuildExec_default = (ctx) => {
2981
3224
  updateLogContext({ task: "createBuild" });
2982
3225
  try {
2983
3226
  if (ctx2.authenticatedInitially && !ctx2.config.skipBuildCreation) {
2984
- let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log, ctx2.build.name, ctx2.isStartExec, ctx2.env.SMART_GIT, ctx2.options.markBaseline, ctx2.options.baselineBuild, ctx2.options.scheduled);
3227
+ let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log, ctx2.build.name, ctx2.isStartExec, ctx2.env.SMART_GIT, ctx2.options.markBaseline, ctx2.options.baselineBuild, ctx2.options.scheduled, ctx2.env.LT_USERNAME, ctx2.env.LT_ACCESS_KEY);
2985
3228
  if (resp && resp.data && resp.data.buildId) {
2986
3229
  ctx2.build = {
2987
3230
  id: resp.data.buildId,
@@ -3081,7 +3324,6 @@ var exec_default = (ctx) => {
3081
3324
  if (code !== null) {
3082
3325
  task.title = `Execution of '${(_a3 = ctx2.args.execCommand) == null ? void 0 : _a3.join(" ")}' completed; exited with code ${code}`;
3083
3326
  if (code !== 0) {
3084
- reject();
3085
3327
  process.exitCode = code;
3086
3328
  }
3087
3329
  } else if (signal !== null) {
@@ -3160,7 +3402,7 @@ var finalizeBuild_default = (ctx) => {
3160
3402
  task.output = chalk__default.default.gray(error.message);
3161
3403
  throw new Error("Finalize build failed");
3162
3404
  }
3163
- let buildUrls = `build url: ${ctx2.build.url}
3405
+ let buildUrls2 = `build url: ${ctx2.build.url}
3164
3406
  `;
3165
3407
  if (pingIntervalId !== null) {
3166
3408
  clearInterval(pingIntervalId);
@@ -3181,7 +3423,6 @@ var finalizeBuild_default = (ctx) => {
3181
3423
  } else {
3182
3424
  is_baseline = false;
3183
3425
  }
3184
- console.log(`start polling was called at finalize build for buildId: ${buildId}`);
3185
3426
  startPolling(ctx2, buildId, is_baseline, capabilities.projectToken);
3186
3427
  yield new Promise((resolve) => setTimeout(resolve, 7e3));
3187
3428
  ctx2.fetchResultsForBuild.push(buildId);
@@ -3191,14 +3432,14 @@ var finalizeBuild_default = (ctx) => {
3191
3432
  yield ctx2.client.finalizeBuildForCapsWithToken(buildId, totalSnapshots, projectToken, ctx2.log);
3192
3433
  }
3193
3434
  if (testId && buildId) {
3194
- buildUrls += `TestId ${testId}: ${sessionBuildUrl}
3435
+ buildUrls2 += `TestId ${testId}: ${sessionBuildUrl}
3195
3436
  `;
3196
3437
  }
3197
3438
  } catch (error) {
3198
3439
  ctx2.log.debug(`Error finalizing build for session ${sessionId}: ${error.message}`);
3199
3440
  }
3200
3441
  }
3201
- task.output = chalk__default.default.gray(buildUrls);
3442
+ task.output = chalk__default.default.gray(buildUrls2);
3202
3443
  task.title = "Finalized build";
3203
3444
  try {
3204
3445
  yield (_a = ctx2.browser) == null ? void 0 : _a.close();
@@ -3216,6 +3457,12 @@ var finalizeBuild_default = (ctx) => {
3216
3457
  let resp = ctx2.client.sendCliLogsToLSRS(ctx2);
3217
3458
  }
3218
3459
  }
3460
+ if (process.exitCode && process.exitCode !== 0) {
3461
+ ctx2.log.error(`Exiting process with code ${process.exitCode}`);
3462
+ task.output = chalk__default.default.gray(`Exiting process with code ${process.exitCode}`);
3463
+ task.title = "Build finalized with errors";
3464
+ throw new Error(`Process exited with code ${process.exitCode}`);
3465
+ }
3219
3466
  } catch (error) {
3220
3467
  ctx2.log.debug(error);
3221
3468
  }
@@ -4111,7 +4358,7 @@ var Queue = class {
4111
4358
  try {
4112
4359
  this.processingSnapshot = snapshot == null ? void 0 : snapshot.name;
4113
4360
  let drop = false;
4114
- if (this.ctx.isStartExec && !this.ctx.config.tunnel) {
4361
+ if (this.ctx.isStartExec) {
4115
4362
  this.ctx.log.info(`Processing Snapshot: ${snapshot == null ? void 0 : snapshot.name}`);
4116
4363
  }
4117
4364
  if (!this.ctx.config.delayedUpload && snapshot && snapshot.name && this.snapshotNames.includes(snapshot.name) && !this.ctx.config.allowDuplicateSnapshotNames) {
@@ -4306,15 +4553,15 @@ var startTunnel_default = (ctx) => {
4306
4553
 
4307
4554
  // src/commander/exec.ts
4308
4555
  var command = new commander.Command();
4309
- 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("--scheduled <string>", "Specify the schedule ID").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(execCommand, _, command10) {
4556
+ 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("--scheduled <string>", "Specify the schedule ID").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(execCommand, _, command11) {
4310
4557
  return __async(this, null, function* () {
4311
4558
  var _a;
4312
- const options = command10.optsWithGlobals();
4559
+ const options = command11.optsWithGlobals();
4313
4560
  if (options.buildName === "") {
4314
4561
  console.log(`Error: The '--buildName' option cannot be an empty string.`);
4315
4562
  process.exit(1);
4316
4563
  }
4317
- let ctx = ctx_default(command10.optsWithGlobals());
4564
+ let ctx = ctx_default(command11.optsWithGlobals());
4318
4565
  if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
4319
4566
  ctx.log.error(`Error: Command not found "${execCommand[0]}"`);
4320
4567
  return;
@@ -4537,7 +4784,7 @@ var createBuild_default = (ctx) => {
4537
4784
  task: (ctx2, task) => __async(void 0, null, function* () {
4538
4785
  updateLogContext({ task: "createBuild" });
4539
4786
  try {
4540
- let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log, ctx2.build.name, ctx2.isStartExec, ctx2.env.SMART_GIT, ctx2.options.markBaseline, ctx2.options.baselineBuild, ctx2.options.scheduled);
4787
+ let resp = yield ctx2.client.createBuild(ctx2.git, ctx2.config, ctx2.log, ctx2.build.name, ctx2.isStartExec, ctx2.env.SMART_GIT, ctx2.options.markBaseline, ctx2.options.baselineBuild, ctx2.options.scheduled, ctx2.env.LT_USERNAME, ctx2.env.LT_ACCESS_KEY);
4541
4788
  if (resp && resp.data && resp.data.buildId) {
4542
4789
  ctx2.build = {
4543
4790
  id: resp.data.buildId,
@@ -4608,7 +4855,7 @@ function captureScreenshotsForConfig(ctx, browsers, urlConfig, browserName, rend
4608
4855
  yield page == null ? void 0 : page.waitForTimeout(waitForTimeout || 0);
4609
4856
  yield executeDocumentScripts(ctx, page, "beforeSnapshot", beforeSnapshotScript);
4610
4857
  yield page == null ? void 0 : page.screenshot({ path: ssPath, fullPage });
4611
- yield ctx.client.uploadScreenshot(ctx.build, ssPath, name, browserName, viewportString, ctx.log);
4858
+ yield ctx.client.uploadScreenshot(ctx.build, ssPath, name, browserName, viewportString, url, ctx.log);
4612
4859
  }
4613
4860
  } catch (error) {
4614
4861
  throw new Error(`captureScreenshotsForConfig failed for browser ${browserName}; error: ${error}`);
@@ -4778,7 +5025,7 @@ function uploadScreenshots(ctx) {
4778
5025
  viewport = `${width}x${height}`;
4779
5026
  }
4780
5027
  }
4781
- yield ctx.client.uploadScreenshot(ctx.build, filePath, ssId, "default", viewport, ctx.log);
5028
+ yield ctx.client.uploadScreenshot(ctx.build, filePath, ssId, "default", viewport, "", ctx.log);
4782
5029
  ctx.log.info(`${filePath} : uploaded successfully`);
4783
5030
  noOfScreenshots++;
4784
5031
  } else {
@@ -4930,15 +5177,15 @@ var captureScreenshots_default = (ctx) => {
4930
5177
 
4931
5178
  // src/commander/capture.ts
4932
5179
  var command2 = new commander.Command();
4933
- 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("--scheduled <string>", "Specify the schedule ID").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(file, _, command10) {
5180
+ 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("--scheduled <string>", "Specify the schedule ID").option("--userName <string>", "Specify the LT username").option("--accessKey <string>", "Specify the LT accesskey").action(function(file, _, command11) {
4934
5181
  return __async(this, null, function* () {
4935
5182
  var _a, _b;
4936
- const options = command10.optsWithGlobals();
5183
+ const options = command11.optsWithGlobals();
4937
5184
  if (options.buildName === "") {
4938
5185
  console.log(`Error: The '--buildName' option cannot be an empty string.`);
4939
5186
  process.exit(1);
4940
5187
  }
4941
- let ctx = ctx_default(command10.optsWithGlobals());
5188
+ let ctx = ctx_default(command11.optsWithGlobals());
4942
5189
  ctx.isSnapshotCaptured = true;
4943
5190
  if (!fs5__default.default.existsSync(file)) {
4944
5191
  ctx.log.error(`Web Static Config file ${file} not found.`);
@@ -5026,14 +5273,14 @@ command3.name("upload").description("Upload screenshots from given directory").a
5026
5273
  return val.split(",").map((ext) => ext.trim().toLowerCase());
5027
5274
  }).option("-E, --removeExtensions", "Strips file extensions from snapshot names").option("-i, --ignoreDir <patterns>", "Comma-separated list of directories to ignore", (val) => {
5028
5275
  return val.split(",").map((pattern) => pattern.trim());
5029
- }).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, _, command10) {
5276
+ }).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, _, command11) {
5030
5277
  return __async(this, null, function* () {
5031
- const options = command10.optsWithGlobals();
5278
+ const options = command11.optsWithGlobals();
5032
5279
  if (options.buildName === "") {
5033
5280
  console.log(`Error: The '--buildName' option cannot be an empty string.`);
5034
5281
  process.exit(1);
5035
5282
  }
5036
- let ctx = ctx_default(command10.optsWithGlobals());
5283
+ let ctx = ctx_default(command11.optsWithGlobals());
5037
5284
  ctx.isSnapshotCaptured = true;
5038
5285
  if (!fs5__default.default.existsSync(directory)) {
5039
5286
  console.log(`Error: The provided directory ${directory} not found.`);
@@ -5311,10 +5558,10 @@ var uploadAppFigma_default2 = (ctx) => {
5311
5558
  var uploadFigma = new commander.Command();
5312
5559
  var uploadWebFigmaCommand = new commander.Command();
5313
5560
  var uploadAppFigmaCommand = new commander.Command();
5314
- 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, _, command10) {
5561
+ 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, _, command11) {
5315
5562
  return __async(this, null, function* () {
5316
5563
  var _a, _b;
5317
- let ctx = ctx_default(command10.optsWithGlobals());
5564
+ let ctx = ctx_default(command11.optsWithGlobals());
5318
5565
  ctx.isSnapshotCaptured = true;
5319
5566
  if (!fs5__default.default.existsSync(file)) {
5320
5567
  console.log(`Error: Figma Config file ${file} not found.`);
@@ -5353,10 +5600,10 @@ uploadFigma.name("upload-figma").description("Capture screenshots of static site
5353
5600
  }
5354
5601
  });
5355
5602
  });
5356
- 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, _, command10) {
5603
+ 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, _, command11) {
5357
5604
  return __async(this, null, function* () {
5358
5605
  var _a;
5359
- let ctx = ctx_default(command10.optsWithGlobals());
5606
+ let ctx = ctx_default(command11.optsWithGlobals());
5360
5607
  if (!fs5__default.default.existsSync(file)) {
5361
5608
  console.log(`Error: figma-web config file ${file} not found.`);
5362
5609
  return;
@@ -5406,10 +5653,10 @@ uploadWebFigmaCommand.name("upload-figma-web").description("Capture figma screen
5406
5653
  }
5407
5654
  });
5408
5655
  });
5409
- 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, _, command10) {
5656
+ 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, _, command11) {
5410
5657
  return __async(this, null, function* () {
5411
5658
  var _a;
5412
- let ctx = ctx_default(command10.optsWithGlobals());
5659
+ let ctx = ctx_default(command11.optsWithGlobals());
5413
5660
  if (!fs5__default.default.existsSync(file)) {
5414
5661
  console.log(`Error: figma-app config file ${file} not found.`);
5415
5662
  return;
@@ -5473,10 +5720,10 @@ command4.name("exec:start").description("Start SmartUI server").option("-P, --po
5473
5720
  ctx.isStartExec = true;
5474
5721
  let tasks = new listr2.Listr(
5475
5722
  [
5476
- auth_default(),
5723
+ authExec_default(),
5477
5724
  startServer_default(),
5478
5725
  getGitInfo_default(),
5479
- createBuild_default()
5726
+ createBuildExec_default()
5480
5727
  ],
5481
5728
  {
5482
5729
  rendererOptions: {
@@ -5491,8 +5738,10 @@ command4.name("exec:start").description("Start SmartUI server").option("-P, --po
5491
5738
  );
5492
5739
  try {
5493
5740
  yield tasks.run(ctx);
5494
- startPingPolling(ctx);
5495
- if (ctx.options.fetchResults) {
5741
+ if (ctx.build && ctx.build.id) {
5742
+ startPingPolling(ctx);
5743
+ }
5744
+ if (ctx.options.fetchResults && ctx.build && ctx.build.id) {
5496
5745
  startPolling(ctx, "", false, "");
5497
5746
  }
5498
5747
  } catch (error) {
@@ -5853,10 +6102,104 @@ command9.name("exec:pingTest").description("Ping the SmartUI server to check if
5853
6102
  });
5854
6103
  });
5855
6104
  var pingTest_default = command9;
6105
+ var uploadPdfs_default = (ctx) => {
6106
+ return {
6107
+ title: "Uploading PDFs",
6108
+ task: (ctx2, task) => __async(void 0, null, function* () {
6109
+ try {
6110
+ ctx2.task = task;
6111
+ updateLogContext({ task: "upload-pdf" });
6112
+ yield uploadPdfs(ctx2, ctx2.uploadFilePath);
6113
+ task.title = "PDFs uploaded successfully";
6114
+ } catch (error) {
6115
+ ctx2.log.debug(error);
6116
+ task.output = chalk__default.default.red(`${error.message}`);
6117
+ throw new Error("PDF upload failed");
6118
+ }
6119
+ }),
6120
+ rendererOptions: { persistentOutput: true },
6121
+ exitOnError: false
6122
+ };
6123
+ };
6124
+ function uploadPdfs(ctx, pdfPath) {
6125
+ return __async(this, null, function* () {
6126
+ const formData = new FormData__default.default();
6127
+ if (pdfPath.endsWith(".pdf")) {
6128
+ formData.append("pathToFiles", fs5__default.default.createReadStream(pdfPath));
6129
+ } else {
6130
+ const files = fs5__default.default.readdirSync(pdfPath);
6131
+ const pdfFiles = files.filter((file) => file.endsWith(".pdf"));
6132
+ pdfFiles.forEach((pdf) => {
6133
+ const filePath = path2__default.default.join(pdfPath, pdf);
6134
+ formData.append("pathToFiles", fs5__default.default.createReadStream(filePath));
6135
+ });
6136
+ }
6137
+ const buildName = ctx.options.buildName;
6138
+ if (buildName) {
6139
+ ctx.build.name = buildName;
6140
+ }
6141
+ try {
6142
+ const response = yield ctx.client.uploadPdf(ctx, formData, buildName);
6143
+ if (response && response.buildId) {
6144
+ ctx.build.id = response.buildId;
6145
+ ctx.log.debug(`PDF upload successful. Build ID: ${ctx.build.id}`);
6146
+ }
6147
+ if (response && response.projectId) {
6148
+ ctx.build.projectId = response.projectId;
6149
+ }
6150
+ } catch (error) {
6151
+ throw new Error(error.message);
6152
+ }
6153
+ });
6154
+ }
6155
+
6156
+ // src/commander/uploadPdf.ts
6157
+ var command10 = new commander.Command();
6158
+ command10.name("upload-pdf").description("Upload PDFs for visual comparison").argument("<directory>", "Path of the directory containing PDFs").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, _, command11) {
6159
+ return __async(this, null, function* () {
6160
+ const options = command11.optsWithGlobals();
6161
+ if (options.buildName === "") {
6162
+ console.log(`Error: The '--buildName' option cannot be an empty string.`);
6163
+ process.exit(1);
6164
+ }
6165
+ let ctx = ctx_default(command11.optsWithGlobals());
6166
+ if (!fs5__default.default.existsSync(directory)) {
6167
+ console.log(`Error: The provided directory ${directory} not found.`);
6168
+ process.exit(1);
6169
+ }
6170
+ ctx.uploadFilePath = directory;
6171
+ let tasks = new listr2.Listr(
6172
+ [
6173
+ auth_default(),
6174
+ uploadPdfs_default()
6175
+ ],
6176
+ {
6177
+ rendererOptions: {
6178
+ icon: {
6179
+ [listr2.ListrDefaultRendererLogLevels.OUTPUT]: `\u2192`
6180
+ },
6181
+ color: {
6182
+ [listr2.ListrDefaultRendererLogLevels.OUTPUT]: listr2.color.gray
6183
+ }
6184
+ }
6185
+ }
6186
+ );
6187
+ try {
6188
+ yield tasks.run(ctx);
6189
+ if (ctx.options.fetchResults) {
6190
+ startPdfPolling(ctx);
6191
+ }
6192
+ } catch (error) {
6193
+ console.log("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
6194
+ process.exit(1);
6195
+ }
6196
+ });
6197
+ });
6198
+ var uploadPdf_default = command10;
5856
6199
 
5857
6200
  // src/commander/commander.ts
5858
6201
  var program2 = new commander.Command();
5859
- program2.name("smartui").description("CLI to help you run your SmartUI tests on LambdaTest platform").version(`v${version}`).option("-c --config <filepath>", "Config file path").option("--markBaseline", "Mark this build baseline").option("--baselineBranch <string>", "Mark this build baseline").option("--baselineBuild <string>", "Mark this build baseline").option("--githubURL <string>", "GitHub URL including commitId").addCommand(exec_default2).addCommand(capture_default).addCommand(configWeb).addCommand(configStatic).addCommand(upload_default).addCommand(server_default2).addCommand(stopServer_default).addCommand(merge_default).addCommand(ping_default).addCommand(configFigma).addCommand(uploadFigma).addCommand(configWebFigma).addCommand(configAppFigma).addCommand(uploadWebFigmaCommand).addCommand(uploadAppFigmaCommand).addCommand(pingTest_default);
6202
+ program2.name("smartui").description("CLI to help you run your SmartUI tests on LambdaTest platform").version(`v${version}`).option("-c --config <filepath>", "Config file path").option("--markBaseline", "Mark this build baseline").option("--baselineBranch <string>", "Mark this build baseline").option("--baselineBuild <string>", "Mark this build baseline").option("--githubURL <string>", "GitHub URL including commitId").addCommand(exec_default2).addCommand(capture_default).addCommand(configWeb).addCommand(configStatic).addCommand(upload_default).addCommand(server_default2).addCommand(stopServer_default).addCommand(merge_default).addCommand(ping_default).addCommand(configFigma).addCommand(uploadFigma).addCommand(configWebFigma).addCommand(configAppFigma).addCommand(uploadWebFigmaCommand).addCommand(uploadAppFigmaCommand).addCommand(pingTest_default).addCommand(uploadPdf_default);
5860
6203
  var commander_default = program2;
5861
6204
  (function() {
5862
6205
  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.27",
3
+ "version": "4.1.29",
4
4
  "description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
5
5
  "files": [
6
6
  "dist/**/*"