Skip to content

Commit 40921d0

Browse files
committed
resolved merge conflict
2 parents 4b5da28 + 1a66531 commit 40921d0

File tree

13 files changed

+396
-9
lines changed

13 files changed

+396
-9
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lambdatest/smartui-cli",
3-
"version": "4.1.28",
3+
"version": "4.1.29",
44
"description": "A command line interface (CLI) to run SmartUI tests on LambdaTest",
55
"files": [
66
"dist/**/*"

src/commander/commander.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import stopServer from './stopServer.js'
1010
import ping from './ping.js'
1111
import merge from './merge.js'
1212
import pingTest from './pingTest.js'
13+
import uploadPdf from "./uploadPdf.js";
1314

1415
const program = new Command();
1516

@@ -38,6 +39,7 @@ program
3839
.addCommand(uploadWebFigmaCommand)
3940
.addCommand(uploadAppFigmaCommand)
4041
.addCommand(pingTest)
42+
.addCommand(uploadPdf)
4143

4244

4345

src/commander/server.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ command
5050

5151
try {
5252
await tasks.run(ctx);
53-
startPingPolling(ctx);
54-
if (ctx.options.fetchResults) {
53+
if (ctx.build && ctx.build.id) {
54+
startPingPolling(ctx);
55+
}
56+
if (ctx.options.fetchResults && ctx.build && ctx.build.id) {
5557
startPolling(ctx, '', false, '')
5658
}
57-
5859

5960
} catch (error) {
6061
console.error('Error during server execution:', error);

src/commander/uploadPdf.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {Command} from "commander";
2+
import { Context } from '../types.js';
3+
import ctxInit from '../lib/ctx.js';
4+
import { color, Listr, ListrDefaultRendererLogLevels, LoggerFormat } from 'listr2';
5+
import fs from 'fs';
6+
import auth from '../tasks/auth.js';
7+
import uploadPdfs from '../tasks/uploadPdfs.js';
8+
import {startPdfPolling} from "../lib/utils.js";
9+
const command = new Command();
10+
11+
command
12+
.name('upload-pdf')
13+
.description('Upload PDFs for visual comparison')
14+
.argument('<directory>', 'Path of the directory containing PDFs')
15+
.option('--fetch-results [filename]', 'Fetch results and optionally specify an output file, e.g., <filename>.json')
16+
.option('--buildName <string>', 'Specify the build name')
17+
.action(async function(directory, _, command) {
18+
const options = command.optsWithGlobals();
19+
if (options.buildName === '') {
20+
console.log(`Error: The '--buildName' option cannot be an empty string.`);
21+
process.exit(1);
22+
}
23+
let ctx: Context = ctxInit(command.optsWithGlobals());
24+
25+
if (!fs.existsSync(directory)) {
26+
console.log(`Error: The provided directory ${directory} not found.`);
27+
process.exit(1);
28+
}
29+
30+
ctx.uploadFilePath = directory;
31+
32+
let tasks = new Listr<Context>(
33+
[
34+
auth(ctx),
35+
uploadPdfs(ctx)
36+
],
37+
{
38+
rendererOptions: {
39+
icon: {
40+
[ListrDefaultRendererLogLevels.OUTPUT]: `→`
41+
},
42+
color: {
43+
[ListrDefaultRendererLogLevels.OUTPUT]: color.gray as LoggerFormat
44+
}
45+
}
46+
}
47+
);
48+
49+
try {
50+
await tasks.run(ctx);
51+
52+
if (ctx.options.fetchResults) {
53+
startPdfPolling(ctx);
54+
}
55+
} catch (error) {
56+
console.log('\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/');
57+
process.exit(1);
58+
}
59+
});
60+
61+
export default command;

src/lib/env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default (): Env => {
44
const {
55
PROJECT_TOKEN = '',
66
SMARTUI_CLIENT_API_URL = 'https://api.lambdatest.com/visualui/1.0',
7+
SMARTUI_UPLOAD_URL = 'https://api.lambdatest.com',
78
SMARTUI_GIT_INFO_FILEPATH,
89
SMARTUI_DO_NOT_USE_CAPTURED_COOKIES,
910
HTTP_PROXY,
@@ -27,6 +28,7 @@ export default (): Env => {
2728
return {
2829
PROJECT_TOKEN,
2930
SMARTUI_CLIENT_API_URL,
31+
SMARTUI_UPLOAD_URL: SMARTUI_UPLOAD_URL,
3032
SMARTUI_GIT_INFO_FILEPATH,
3133
HTTP_PROXY,
3234
HTTPS_PROXY,

src/lib/git.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ function executeCommand(command: string): string {
1717
}
1818
}
1919

20-
export function isGitRepo(): boolean {
20+
export function isGitRepo(ctx: Context): boolean {
2121
try {
2222
executeCommand('git status')
2323
return true
2424
} catch (error) {
25+
setNonGitInfo(ctx)
2526
return false
2627
}
2728
}
@@ -82,3 +83,25 @@ export default (ctx: Context): Git => {
8283
};
8384
}
8485
}
86+
87+
88+
function setNonGitInfo(ctx: Context) {
89+
let branch = ctx.env.CURRENT_BRANCH || 'unknown-branch'
90+
if (ctx.options.markBaseline) {
91+
ctx.env.BASELINE_BRANCH = branch
92+
ctx.env.SMART_GIT = false
93+
}
94+
let githubURL;
95+
if (ctx.options.githubURL && ctx.options.githubURL.startsWith('https://')) {
96+
githubURL = ctx.options.githubURL;
97+
}
98+
99+
ctx.git = {
100+
branch: branch,
101+
commitId: '-',
102+
commitAuthor: '-',
103+
commitMessage: '-',
104+
githubURL: githubURL? githubURL : '',
105+
baselineBranch: ctx.options.baselineBranch || ctx.env.BASELINE_BRANCH || ''
106+
}
107+
}

src/lib/httpClient.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ export default class httpClient {
1414
username: string;
1515
accessKey: string;
1616

17+
private handleHttpError(error: any, log: Logger): never {
18+
if (error && error.response) {
19+
log.debug(`http response error: ${JSON.stringify({
20+
status: error.response.status,
21+
body: error.response.data
22+
})}`);
23+
throw new Error(error.response.data?.message || error.response.data || `HTTP ${error.response.status} error`);
24+
}
25+
log.debug(`http request failed: ${error.message}`);
26+
throw new Error(error.message);
27+
}
28+
1729
constructor({ SMARTUI_CLIENT_API_URL, PROJECT_TOKEN, PROJECT_NAME, LT_USERNAME, LT_ACCESS_KEY, SMARTUI_API_PROXY, SMARTUI_API_SKIP_CERTIFICATES }: Env) {
1830
this.projectToken = PROJECT_TOKEN || '';
1931
this.projectName = PROJECT_NAME || '';
@@ -83,6 +95,8 @@ export default class httpClient {
8395

8496
// If we've reached max retries, reject with the error
8597
return Promise.reject(error);
98+
} else {
99+
return Promise.reject(error);
86100
}
87101
}
88102
);
@@ -644,4 +658,66 @@ export default class httpClient {
644658
}
645659
}, ctx.log);
646660
}
661+
662+
async uploadPdf(ctx: Context, form: FormData, buildName?: string): Promise<any> {
663+
form.append('projectToken', this.projectToken);
664+
if (ctx.build.name !== undefined && ctx.build.name !== '') {
665+
form.append('buildName', buildName);
666+
}
667+
668+
try {
669+
const response = await this.axiosInstance.request({
670+
url: ctx.env.SMARTUI_UPLOAD_URL + '/pdf/upload',
671+
method: 'POST',
672+
headers: form.getHeaders(),
673+
data: form,
674+
});
675+
676+
ctx.log.debug(`http response: ${JSON.stringify({
677+
status: response.status,
678+
headers: response.headers,
679+
body: response.data
680+
})}`);
681+
682+
return response.data;
683+
} catch (error: any) {
684+
this.handleHttpError(error, ctx.log);
685+
}
686+
}
687+
688+
async fetchPdfResults(ctx: Context): Promise<any> {
689+
const params: Record<string, string> = {};
690+
691+
if (ctx.build.projectId) {
692+
params.project_id = ctx.build.projectId;
693+
} else {
694+
throw new Error('Project ID not found to fetch PDF results');
695+
}
696+
params.build_id = ctx.build.id;
697+
698+
const auth = Buffer.from(`${this.username}:${this.accessKey}`).toString('base64');
699+
700+
try {
701+
const response = await axios.request({
702+
url: ctx.env.SMARTUI_UPLOAD_URL + '/smartui/2.0/build/screenshots',
703+
method: 'GET',
704+
params: params,
705+
headers: {
706+
'accept': 'application/json',
707+
'Authorization': `Basic ${auth}`
708+
}
709+
});
710+
711+
ctx.log.debug(`http response: ${JSON.stringify({
712+
status: response.status,
713+
headers: response.headers,
714+
body: response.data
715+
})}`);
716+
717+
return response.data;
718+
} catch (error: any) {
719+
this.handleHttpError(error, ctx.log);
720+
}
721+
}
647722
}
723+

src/lib/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default async (ctx: Context): Promise<FastifyInstance<Server, IncomingMes
6363
}
6464
} catch (error: any) {
6565
ctx.log.debug(`Failed to fetch capabilities for sessionId ${sessionId}: ${error.message}`);
66-
console.log(`Failed to fetch capabilities for sessionId ${sessionId}: ${error.message}`);
66+
// console.log(`Failed to fetch capabilities for sessionId ${sessionId}: ${error.message}`);
6767
}
6868
}
6969

src/lib/snapshotQueue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ export default class Queue {
286286
let drop = false;
287287

288288

289-
if (this.ctx.isStartExec && !this.ctx.config.tunnel) {
289+
if (this.ctx.isStartExec) {
290290
this.ctx.log.info(`Processing Snapshot: ${snapshot?.name}`);
291291
}
292292

0 commit comments

Comments
 (0)