@lambdatest/smartui-cli 2.0.4 → 2.0.6
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 +395 -134
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8,14 +8,14 @@ var chalk = require('chalk');
|
|
|
8
8
|
var path2 = require('path');
|
|
9
9
|
var fastify = require('fastify');
|
|
10
10
|
var fs = require('fs');
|
|
11
|
-
var
|
|
11
|
+
var test = require('@playwright/test');
|
|
12
12
|
var Ajv = require('ajv');
|
|
13
13
|
var addErrors = require('ajv-errors');
|
|
14
|
+
var winston = require('winston');
|
|
14
15
|
var FormData = require('form-data');
|
|
15
16
|
var axios = require('axios');
|
|
16
17
|
var child_process = require('child_process');
|
|
17
18
|
var spawn = require('cross-spawn');
|
|
18
|
-
var test = require('@playwright/test');
|
|
19
19
|
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
21
|
|
|
@@ -66,6 +66,270 @@ var __async = (__this, __arguments, generator) => {
|
|
|
66
66
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
67
67
|
});
|
|
68
68
|
};
|
|
69
|
+
var MIN_VIEWPORT_HEIGHT = 1080;
|
|
70
|
+
var processSnapshot_default = (snapshot, ctx) => __async(void 0, null, function* () {
|
|
71
|
+
let options = snapshot.options;
|
|
72
|
+
let optionWarnings = /* @__PURE__ */ new Set();
|
|
73
|
+
let processedOptions = {};
|
|
74
|
+
if (options && Object.keys(options).length !== 0) {
|
|
75
|
+
ctx.log.debug(`Processing options: ${JSON.stringify(options)}`);
|
|
76
|
+
if (options.ignoreDOM && Object.keys(options.ignoreDOM).length !== 0 || options.selectDOM && Object.keys(options.selectDOM).length !== 0) {
|
|
77
|
+
if (!ctx.browser)
|
|
78
|
+
ctx.browser = yield test.chromium.launch({ headless: true });
|
|
79
|
+
let ignoreOrSelectDOM;
|
|
80
|
+
let ignoreOrSelectBoxes;
|
|
81
|
+
if (options.ignoreDOM && Object.keys(options.ignoreDOM).length !== 0) {
|
|
82
|
+
processedOptions.ignoreBoxes = {};
|
|
83
|
+
ignoreOrSelectDOM = "ignoreDOM";
|
|
84
|
+
ignoreOrSelectBoxes = "ignoreBoxes";
|
|
85
|
+
} else {
|
|
86
|
+
processedOptions.selectBoxes = {};
|
|
87
|
+
ignoreOrSelectDOM = "selectDOM";
|
|
88
|
+
ignoreOrSelectBoxes = "selectBoxes";
|
|
89
|
+
}
|
|
90
|
+
let selectors = [];
|
|
91
|
+
for (const [key, value] of Object.entries(options[ignoreOrSelectDOM])) {
|
|
92
|
+
switch (key) {
|
|
93
|
+
case "id":
|
|
94
|
+
selectors.push(...value.map((e) => "#" + e));
|
|
95
|
+
break;
|
|
96
|
+
case "class":
|
|
97
|
+
selectors.push(...value.map((e) => "." + e));
|
|
98
|
+
break;
|
|
99
|
+
case "xpath":
|
|
100
|
+
selectors.push(...value.map((e) => "xpath=" + e));
|
|
101
|
+
break;
|
|
102
|
+
case "cssSelector":
|
|
103
|
+
selectors.push(...value);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const vp of ctx.webConfig.viewports) {
|
|
108
|
+
const page = yield ctx.browser.newPage({ viewport: { width: vp.width, height: vp.height || MIN_VIEWPORT_HEIGHT } });
|
|
109
|
+
yield page.setContent(snapshot.dom.html);
|
|
110
|
+
let viewport = `${vp.width}${vp.height ? "x" + vp.height : ""}`;
|
|
111
|
+
if (!Array.isArray(processedOptions[ignoreOrSelectBoxes][viewport]))
|
|
112
|
+
processedOptions[ignoreOrSelectBoxes][viewport] = [];
|
|
113
|
+
let locators = [];
|
|
114
|
+
let boxes = [];
|
|
115
|
+
for (const selector of selectors) {
|
|
116
|
+
let l = yield page.locator(selector).all();
|
|
117
|
+
if (l.length === 0) {
|
|
118
|
+
optionWarnings.add(`For snapshot ${snapshot.name}, no element found for selector ${selector}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
locators.push(...l);
|
|
122
|
+
}
|
|
123
|
+
for (const locator of locators) {
|
|
124
|
+
let bb = yield locator.boundingBox();
|
|
125
|
+
if (bb)
|
|
126
|
+
boxes.push({
|
|
127
|
+
left: bb.x,
|
|
128
|
+
top: bb.y,
|
|
129
|
+
right: bb.x + bb.width,
|
|
130
|
+
bottom: bb.y + bb.height
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
processedOptions[ignoreOrSelectBoxes][viewport].push(...boxes);
|
|
134
|
+
yield page.close();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
processedSnapshot: {
|
|
140
|
+
name: snapshot.name,
|
|
141
|
+
url: snapshot.url,
|
|
142
|
+
dom: Buffer.from(snapshot.dom.html).toString("base64"),
|
|
143
|
+
options: processedOptions
|
|
144
|
+
},
|
|
145
|
+
warnings: [...optionWarnings, ...snapshot.dom.warnings]
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
var ajv = new Ajv__default.default({ allErrors: true });
|
|
149
|
+
ajv.addFormat("web-url", {
|
|
150
|
+
type: "string",
|
|
151
|
+
validate: (url) => {
|
|
152
|
+
const urlPattern = new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3}))(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*(\\?[;&a-z\\d%_.~+=-]*)?(\\#[-a-z\\d_]*)?$", "i");
|
|
153
|
+
return urlPattern.test(url.trim());
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
addErrors__default.default(ajv);
|
|
157
|
+
var ConfigSchema = {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties: {
|
|
160
|
+
web: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
browsers: {
|
|
164
|
+
type: "array",
|
|
165
|
+
items: { type: "string", enum: ["chrome", "firefox", "edge", "safari"] },
|
|
166
|
+
uniqueItems: true,
|
|
167
|
+
maxItems: 4,
|
|
168
|
+
errorMessage: "Invalid config; allowed browsers - chrome, firefox, edge, safari"
|
|
169
|
+
},
|
|
170
|
+
viewports: {
|
|
171
|
+
type: "array",
|
|
172
|
+
items: {
|
|
173
|
+
type: "array",
|
|
174
|
+
oneOf: [
|
|
175
|
+
{
|
|
176
|
+
items: [{ type: "number", minimum: 320, maximum: 7680 }],
|
|
177
|
+
minItems: 1,
|
|
178
|
+
maxItems: 1
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
items: [
|
|
182
|
+
{ type: "number", minimum: 320, maximum: 7680 },
|
|
183
|
+
{ type: "number", minimum: 320, maximum: 7680 }
|
|
184
|
+
],
|
|
185
|
+
minItems: 2,
|
|
186
|
+
maxItems: 2
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
errorMessage: "Invalid config; width/height must be >= 320 and <= 7680"
|
|
190
|
+
},
|
|
191
|
+
uniqueItems: true,
|
|
192
|
+
maxItems: 5,
|
|
193
|
+
errorMessage: "Invalid config; max unique viewports allowed - 5"
|
|
194
|
+
},
|
|
195
|
+
waitForPageRender: {
|
|
196
|
+
type: "number",
|
|
197
|
+
minimum: 0,
|
|
198
|
+
maximum: 3e5,
|
|
199
|
+
errorMessage: "Invalid config; waitForPageRender must be > 0 and <= 300000"
|
|
200
|
+
},
|
|
201
|
+
waitForTimeout: {
|
|
202
|
+
type: "number",
|
|
203
|
+
minimum: 0,
|
|
204
|
+
maximum: 3e4,
|
|
205
|
+
errorMessage: "Invalid config; waitForTimeout must be > 0 and <= 30000"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
required: ["browsers", "viewports"],
|
|
209
|
+
additionalProperties: false
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
required: ["web"],
|
|
213
|
+
additionalProperties: false
|
|
214
|
+
};
|
|
215
|
+
var WebStaticConfigSchema = {
|
|
216
|
+
type: "array",
|
|
217
|
+
items: {
|
|
218
|
+
type: "object",
|
|
219
|
+
properties: {
|
|
220
|
+
name: {
|
|
221
|
+
type: "string",
|
|
222
|
+
minLength: 1,
|
|
223
|
+
errorMessage: "name is mandatory and cannot be empty"
|
|
224
|
+
},
|
|
225
|
+
url: {
|
|
226
|
+
type: "string",
|
|
227
|
+
format: "web-url",
|
|
228
|
+
errorMessage: "url is mandatory and must be a valid web URL"
|
|
229
|
+
},
|
|
230
|
+
waitForTimeout: {
|
|
231
|
+
type: "number",
|
|
232
|
+
nullable: true,
|
|
233
|
+
minimum: 0,
|
|
234
|
+
maximum: 3e4,
|
|
235
|
+
errorMessage: "waitForTimeout must be > 0 and <= 30000"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
required: ["name", "url"],
|
|
239
|
+
additionalProperties: false
|
|
240
|
+
},
|
|
241
|
+
uniqueItems: true
|
|
242
|
+
};
|
|
243
|
+
var SnapshotSchema = {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
name: {
|
|
247
|
+
type: "string",
|
|
248
|
+
minLength: 1,
|
|
249
|
+
errorMessage: "Invalid snapshot; name is mandatory and cannot be empty"
|
|
250
|
+
},
|
|
251
|
+
url: {
|
|
252
|
+
type: "string",
|
|
253
|
+
format: "web-url",
|
|
254
|
+
errorMessage: "Invalid snapshot; url is mandatory and must be a valid web URL"
|
|
255
|
+
},
|
|
256
|
+
dom: {
|
|
257
|
+
type: "object"
|
|
258
|
+
},
|
|
259
|
+
options: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
ignoreDOM: {
|
|
263
|
+
type: "object",
|
|
264
|
+
properties: {
|
|
265
|
+
id: {
|
|
266
|
+
type: "array",
|
|
267
|
+
items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; id cannot be empty or have semicolon" },
|
|
268
|
+
uniqueItems: true,
|
|
269
|
+
errorMessage: "Invalid snapshot options; id array must have unique items"
|
|
270
|
+
},
|
|
271
|
+
class: {
|
|
272
|
+
type: "array",
|
|
273
|
+
items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; class cannot be empty or have semicolon" },
|
|
274
|
+
uniqueItems: true,
|
|
275
|
+
errorMessage: "Invalid snapshot options; class array must have unique items"
|
|
276
|
+
},
|
|
277
|
+
cssSelector: {
|
|
278
|
+
type: "array",
|
|
279
|
+
items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; cssSelector cannot be empty or have semicolon" },
|
|
280
|
+
uniqueItems: true,
|
|
281
|
+
errorMessage: "Invalid snapshot options; cssSelector array must have unique items"
|
|
282
|
+
},
|
|
283
|
+
xpath: {
|
|
284
|
+
type: "array",
|
|
285
|
+
items: { type: "string", minLength: 1 },
|
|
286
|
+
uniqueItems: true,
|
|
287
|
+
errorMessage: "Invalid snapshot options; xpath array must have unique and non-empty items"
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
selectDOM: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
id: {
|
|
295
|
+
type: "array",
|
|
296
|
+
items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; id cannot be empty or have semicolon" },
|
|
297
|
+
uniqueItems: true,
|
|
298
|
+
errorMessage: "Invalid snapshot options; id array must have unique items"
|
|
299
|
+
},
|
|
300
|
+
class: {
|
|
301
|
+
type: "array",
|
|
302
|
+
items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; class cannot be empty or have semicolon" },
|
|
303
|
+
uniqueItems: true,
|
|
304
|
+
errorMessage: "Invalid snapshot options; class array must have unique items"
|
|
305
|
+
},
|
|
306
|
+
cssSelector: {
|
|
307
|
+
type: "array",
|
|
308
|
+
items: { type: "string", minLength: 1, pattern: "^[^;]*$", errorMessage: "Invalid snapshot options; cssSelector cannot be empty or have semicolon" },
|
|
309
|
+
uniqueItems: true,
|
|
310
|
+
errorMessage: "Invalid snapshot options; cssSelector array must have unique items"
|
|
311
|
+
},
|
|
312
|
+
xpath: {
|
|
313
|
+
type: "array",
|
|
314
|
+
items: { type: "string", minLength: 1 },
|
|
315
|
+
uniqueItems: true,
|
|
316
|
+
errorMessage: "Invalid snapshot options; xpath array must have unique and non-empty items"
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
additionalProperties: false
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
required: ["name", "url", "dom"],
|
|
325
|
+
additionalProperties: false,
|
|
326
|
+
errorMessage: "Invalid snapshot"
|
|
327
|
+
};
|
|
328
|
+
var validateConfig = ajv.compile(ConfigSchema);
|
|
329
|
+
var validateWebStaticConfig = ajv.compile(WebStaticConfigSchema);
|
|
330
|
+
var validateSnapshot = ajv.compile(SnapshotSchema);
|
|
331
|
+
|
|
332
|
+
// src/lib/server.ts
|
|
69
333
|
var server_default = (ctx) => __async(void 0, null, function* () {
|
|
70
334
|
const server = fastify__default.default({ logger: false, bodyLimit: 1e7 });
|
|
71
335
|
const opts = {};
|
|
@@ -77,17 +341,19 @@ var server_default = (ctx) => __async(void 0, null, function* () {
|
|
|
77
341
|
reply.code(200).send({ data: { dom: SMARTUI_DOM } });
|
|
78
342
|
});
|
|
79
343
|
server.post("/snapshot", opts, (request, reply) => __async(void 0, null, function* () {
|
|
80
|
-
let { snapshot, testType } = request.body;
|
|
81
|
-
snapshot.dom = Buffer.from(snapshot.dom).toString("base64");
|
|
82
344
|
try {
|
|
83
|
-
|
|
345
|
+
let { snapshot, testType } = request.body;
|
|
346
|
+
if (!validateSnapshot(snapshot))
|
|
347
|
+
throw new Error(validateSnapshot.errors[0].message);
|
|
348
|
+
let { processedSnapshot, warnings } = yield processSnapshot_default(snapshot, ctx);
|
|
349
|
+
yield ctx.client.uploadSnapshot(ctx.build.id, processedSnapshot, testType, ctx.log);
|
|
350
|
+
ctx.totalSnapshots++;
|
|
351
|
+
reply.code(200).send({ data: { message: "success", warnings } });
|
|
84
352
|
} catch (error) {
|
|
85
|
-
reply.code(500).send({ error: { message: error.message } });
|
|
353
|
+
return reply.code(500).send({ error: { message: error.message } });
|
|
86
354
|
}
|
|
87
|
-
ctx.totalSnapshots++;
|
|
88
|
-
reply.code(200).send({ data: { message: "success" } });
|
|
89
355
|
}));
|
|
90
|
-
yield server.listen(
|
|
356
|
+
yield server.listen();
|
|
91
357
|
let { port } = server.addresses()[0];
|
|
92
358
|
process.env.SMARTUI_SERVER_ADDRESS = `http://localhost:${port}`;
|
|
93
359
|
return server;
|
|
@@ -98,42 +364,47 @@ var env_default = () => {
|
|
|
98
364
|
const {
|
|
99
365
|
PROJECT_TOKEN = "",
|
|
100
366
|
SMARTUI_CLIENT_API_URL = "https://api.lambdatest.com/visualui/1.0",
|
|
101
|
-
|
|
102
|
-
|
|
367
|
+
LT_SDK_LOG_LEVEL,
|
|
368
|
+
LT_SDK_DEBUG
|
|
103
369
|
} = process.env;
|
|
104
370
|
return {
|
|
105
371
|
PROJECT_TOKEN,
|
|
106
372
|
SMARTUI_CLIENT_API_URL,
|
|
107
|
-
|
|
108
|
-
|
|
373
|
+
LT_SDK_LOG_LEVEL,
|
|
374
|
+
LT_SDK_DEBUG
|
|
109
375
|
};
|
|
110
376
|
};
|
|
111
|
-
|
|
112
|
-
// src/lib/logger.ts
|
|
113
|
-
var logContext = {};
|
|
377
|
+
var logContext = { task: "smartui-cli" };
|
|
114
378
|
function updateLogContext(newContext) {
|
|
115
379
|
logContext = __spreadValues(__spreadValues({}, logContext), newContext);
|
|
116
380
|
}
|
|
117
381
|
var logLevel = () => {
|
|
118
382
|
let env = env_default();
|
|
119
|
-
let debug = env.
|
|
120
|
-
return debug || env.
|
|
383
|
+
let debug = env.LT_SDK_DEBUG === "true" ? "debug" : void 0;
|
|
384
|
+
return debug || env.LT_SDK_LOG_LEVEL || "info";
|
|
121
385
|
};
|
|
122
386
|
var logger = winston.createLogger({
|
|
123
387
|
level: logLevel(),
|
|
124
388
|
format: winston.format.combine(
|
|
125
389
|
winston.format.timestamp(),
|
|
126
390
|
winston.format.printf((info) => {
|
|
127
|
-
let contextString;
|
|
128
|
-
|
|
129
|
-
|
|
391
|
+
let contextString = Object.values(logContext).join(" | ");
|
|
392
|
+
let message = typeof info.message === "object" ? JSON.stringify(info.message) : info.message;
|
|
393
|
+
switch (info.level) {
|
|
394
|
+
case "debug":
|
|
395
|
+
message = chalk__default.default.blue(message);
|
|
396
|
+
break;
|
|
397
|
+
case "warn":
|
|
398
|
+
message = chalk__default.default.yellow(message);
|
|
399
|
+
break;
|
|
400
|
+
case "error":
|
|
401
|
+
message = chalk__default.default.red(message);
|
|
402
|
+
break;
|
|
130
403
|
}
|
|
131
|
-
|
|
132
|
-
message += info.message === "object" ? JSON.stringify(info.message) : info.message;
|
|
133
|
-
return message;
|
|
404
|
+
return info.level === "info" ? message : `[${contextString}:${info.level}] ` + message;
|
|
134
405
|
})
|
|
135
406
|
),
|
|
136
|
-
transports: [new winston.transports.
|
|
407
|
+
transports: [new winston.transports.Console()]
|
|
137
408
|
});
|
|
138
409
|
var logger_default = logger;
|
|
139
410
|
|
|
@@ -149,9 +420,8 @@ var startServer_default = (ctx) => {
|
|
|
149
420
|
task.output = chalk__default.default.gray(`listening on port ${(_a = ctx2.server.addresses()[0]) == null ? void 0 : _a.port}`);
|
|
150
421
|
task.title = "SmartUI started";
|
|
151
422
|
} catch (error) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
423
|
+
ctx2.log.debug(error);
|
|
424
|
+
task.output = chalk__default.default.gray(error.message);
|
|
155
425
|
throw new Error("SmartUI server setup failed");
|
|
156
426
|
}
|
|
157
427
|
}),
|
|
@@ -168,7 +438,7 @@ var auth_default = (ctx) => {
|
|
|
168
438
|
task.output = chalk__default.default.gray(`using project token '******#${ctx2.env.PROJECT_TOKEN.split("#").pop()}'`);
|
|
169
439
|
task.title = "Authenticated with SmartUI";
|
|
170
440
|
} catch (error) {
|
|
171
|
-
ctx2.log.debug(error
|
|
441
|
+
ctx2.log.debug(error);
|
|
172
442
|
task.output = chalk__default.default.gray(error.message);
|
|
173
443
|
throw new Error("Authentication failed");
|
|
174
444
|
}
|
|
@@ -237,104 +507,54 @@ function createWebStaticConfig(filepath) {
|
|
|
237
507
|
}
|
|
238
508
|
|
|
239
509
|
// package.json
|
|
240
|
-
var version = "2.0.
|
|
241
|
-
var
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
type: "object",
|
|
252
|
-
properties: {
|
|
253
|
-
web: {
|
|
254
|
-
type: "object",
|
|
255
|
-
properties: {
|
|
256
|
-
browsers: {
|
|
257
|
-
type: "array",
|
|
258
|
-
items: { type: "string", enum: ["chrome", "firefox", "edge", "safari"] },
|
|
259
|
-
uniqueItems: true,
|
|
260
|
-
maxItems: 4,
|
|
261
|
-
errorMessage: "Invalid config; allowed browsers - chrome, firefox, edge, safari"
|
|
262
|
-
},
|
|
263
|
-
viewports: {
|
|
264
|
-
type: "array",
|
|
265
|
-
items: {
|
|
266
|
-
type: "array",
|
|
267
|
-
oneOf: [
|
|
268
|
-
{
|
|
269
|
-
items: [{ type: "number", minimum: 320, maximum: 7680 }],
|
|
270
|
-
minItems: 1,
|
|
271
|
-
maxItems: 1
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
items: [
|
|
275
|
-
{ type: "number", minimum: 320, maximum: 7680 },
|
|
276
|
-
{ type: "number", minimum: 320, maximum: 7680 }
|
|
277
|
-
],
|
|
278
|
-
minItems: 2,
|
|
279
|
-
maxItems: 2
|
|
280
|
-
}
|
|
281
|
-
],
|
|
282
|
-
errorMessage: "Invalid config; width/height must be >= 320 and <= 7680"
|
|
283
|
-
},
|
|
284
|
-
uniqueItems: true,
|
|
285
|
-
maxItems: 5,
|
|
286
|
-
errorMessage: "Invalid config; max unique viewports allowed - 5"
|
|
287
|
-
},
|
|
288
|
-
waitForPageRender: {
|
|
289
|
-
type: "number",
|
|
290
|
-
minimum: 0,
|
|
291
|
-
maximum: 3e5,
|
|
292
|
-
errorMessage: "Invalid config; waitForPageRender must be > 0 and <= 300000"
|
|
293
|
-
},
|
|
294
|
-
waitForTimeout: {
|
|
295
|
-
type: "number",
|
|
296
|
-
minimum: 0,
|
|
297
|
-
maximum: 3e4,
|
|
298
|
-
errorMessage: "Invalid config; waitForTimeout must be > 0 and <= 30000"
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
required: ["browsers", "viewports"],
|
|
302
|
-
additionalProperties: false
|
|
303
|
-
}
|
|
510
|
+
var version = "2.0.6";
|
|
511
|
+
var package_default = {
|
|
512
|
+
name: "@lambdatest/smartui-cli",
|
|
513
|
+
version,
|
|
514
|
+
description: "A command line interface (CLI) to run SmartUI tests on LambdaTest",
|
|
515
|
+
files: [
|
|
516
|
+
"dist/**/*"
|
|
517
|
+
],
|
|
518
|
+
scripts: {
|
|
519
|
+
build: "tsup",
|
|
520
|
+
release: "pnpm run build && pnpm publish --access public --no-git-checks"
|
|
304
521
|
},
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
};
|
|
308
|
-
var WebStaticConfigSchema = {
|
|
309
|
-
type: "array",
|
|
310
|
-
items: {
|
|
311
|
-
type: "object",
|
|
312
|
-
properties: {
|
|
313
|
-
name: {
|
|
314
|
-
type: "string",
|
|
315
|
-
minLength: 1,
|
|
316
|
-
errorMessage: "name is mandatory and cannot be empty"
|
|
317
|
-
},
|
|
318
|
-
url: {
|
|
319
|
-
type: "string",
|
|
320
|
-
format: "web-url",
|
|
321
|
-
errorMessage: "url is mandatory and must be a valid web URL"
|
|
322
|
-
},
|
|
323
|
-
waitForTimeout: {
|
|
324
|
-
type: "number",
|
|
325
|
-
nullable: true,
|
|
326
|
-
minimum: 0,
|
|
327
|
-
maximum: 3e4,
|
|
328
|
-
errorMessage: "waitForTimeout must be > 0 and <= 30000"
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
required: ["name", "url"],
|
|
332
|
-
additionalProperties: false
|
|
522
|
+
bin: {
|
|
523
|
+
smartui: "./dist/index.cjs"
|
|
333
524
|
},
|
|
334
|
-
|
|
525
|
+
type: "module",
|
|
526
|
+
keywords: [
|
|
527
|
+
"lambdatest",
|
|
528
|
+
"smartui",
|
|
529
|
+
"cli"
|
|
530
|
+
],
|
|
531
|
+
author: "LambdaTest <keys@lambdatest.com>",
|
|
532
|
+
license: "MIT",
|
|
533
|
+
dependencies: {
|
|
534
|
+
"@playwright/browser-chromium": "^1.40.1",
|
|
535
|
+
"@playwright/browser-firefox": "^1.40.1",
|
|
536
|
+
"@playwright/browser-webkit": "^1.40.1",
|
|
537
|
+
"@playwright/test": "^1.40.1",
|
|
538
|
+
"@types/cross-spawn": "^6.0.4",
|
|
539
|
+
"@types/node": "^20.8.9",
|
|
540
|
+
"@types/which": "^3.0.2",
|
|
541
|
+
ajv: "^8.12.0",
|
|
542
|
+
"ajv-errors": "^3.0.0",
|
|
543
|
+
axios: "^1.6.0",
|
|
544
|
+
chalk: "^4.1.2",
|
|
545
|
+
commander: "^11.1.0",
|
|
546
|
+
"cross-spawn": "^7.0.3",
|
|
547
|
+
fastify: "^4.24.3",
|
|
548
|
+
"form-data": "^4.0.0",
|
|
549
|
+
listr2: "^7.0.1",
|
|
550
|
+
tsup: "^7.2.0",
|
|
551
|
+
which: "^4.0.0",
|
|
552
|
+
winston: "^3.10.0"
|
|
553
|
+
},
|
|
554
|
+
devDependencies: {
|
|
555
|
+
typescript: "^5.3.2"
|
|
556
|
+
}
|
|
335
557
|
};
|
|
336
|
-
var validateConfig = ajv.compile(ConfigSchema);
|
|
337
|
-
var validateWebStaticConfig = ajv.compile(WebStaticConfigSchema);
|
|
338
558
|
var HTTP_SCHEME = "https:";
|
|
339
559
|
var HTTP_SCHEME_PREFIX = "https://";
|
|
340
560
|
var WWW = "www.";
|
|
@@ -405,11 +625,18 @@ var httpClient = class {
|
|
|
405
625
|
return resp.data;
|
|
406
626
|
}).catch((error) => {
|
|
407
627
|
if (error.response) {
|
|
628
|
+
log.debug(`http response: ${JSON.stringify({
|
|
629
|
+
status: error.response.status,
|
|
630
|
+
headers: error.response.headers,
|
|
631
|
+
body: error.response.data
|
|
632
|
+
})}`);
|
|
408
633
|
throw new Error(JSON.stringify(error.response.data));
|
|
409
634
|
}
|
|
410
635
|
if (error.request) {
|
|
636
|
+
log.debug(`http request failed: ${error.toJSON()}`);
|
|
411
637
|
throw new Error(error.toJSON().message);
|
|
412
638
|
}
|
|
639
|
+
log.debug(`http request failed: ${error.message}`);
|
|
413
640
|
throw new Error(error.message);
|
|
414
641
|
});
|
|
415
642
|
});
|
|
@@ -494,6 +721,17 @@ var httpClient = class {
|
|
|
494
721
|
throw new Error(error.message);
|
|
495
722
|
});
|
|
496
723
|
}
|
|
724
|
+
checkUpdate(log) {
|
|
725
|
+
return this.request({
|
|
726
|
+
url: `/packageinfo`,
|
|
727
|
+
method: "GET",
|
|
728
|
+
headers: { "Content-Type": "application/json" },
|
|
729
|
+
params: {
|
|
730
|
+
packageName: package_default.name,
|
|
731
|
+
packageVersion: package_default.version
|
|
732
|
+
}
|
|
733
|
+
}, log);
|
|
734
|
+
}
|
|
497
735
|
};
|
|
498
736
|
var ctx_default = (options) => {
|
|
499
737
|
let env = env_default();
|
|
@@ -592,6 +830,7 @@ var getGitInfo_default = (ctx) => {
|
|
|
592
830
|
task.output = chalk__default.default.gray(`branch: ${ctx2.git.branch}, commit: ${ctx2.git.commitId}, author: ${ctx2.git.commitAuthor}`);
|
|
593
831
|
task.title = "Fetched git information";
|
|
594
832
|
} catch (error) {
|
|
833
|
+
ctx2.log.debug(error);
|
|
595
834
|
task.output = chalk__default.default.gray(`${error.message}`);
|
|
596
835
|
throw new Error("Error fetching git repo details");
|
|
597
836
|
}
|
|
@@ -616,6 +855,7 @@ var createBuild_default = (ctx) => {
|
|
|
616
855
|
task.output = chalk__default.default.gray(`build id: ${resp.data.buildId}`);
|
|
617
856
|
task.title = "SmartUI build created";
|
|
618
857
|
} catch (error) {
|
|
858
|
+
ctx2.log.debug(error);
|
|
619
859
|
task.output = chalk__default.default.gray(JSON.parse(error.message).message);
|
|
620
860
|
throw new Error("SmartUI build creation failed");
|
|
621
861
|
}
|
|
@@ -669,9 +909,9 @@ var finalizeBuild_default = (ctx) => {
|
|
|
669
909
|
task.output = chalk__default.default.gray(`build url: ${ctx2.build.url}`);
|
|
670
910
|
task.title = "Finalized build";
|
|
671
911
|
} catch (error) {
|
|
672
|
-
ctx2.log.debug(error
|
|
912
|
+
ctx2.log.debug(error);
|
|
673
913
|
task.output = chalk__default.default.gray(error.message);
|
|
674
|
-
throw new Error("Finalize build
|
|
914
|
+
throw new Error("Finalize build failed");
|
|
675
915
|
}
|
|
676
916
|
}),
|
|
677
917
|
rendererOptions: { persistentOutput: true }
|
|
@@ -682,10 +922,10 @@ var finalizeBuild_default = (ctx) => {
|
|
|
682
922
|
var command = new commander.Command();
|
|
683
923
|
command.name("exec").description("Run test commands around SmartUI").argument("<command...>", "Command supplied for running tests").action(function(execCommand, _, command3) {
|
|
684
924
|
return __async(this, null, function* () {
|
|
685
|
-
var _a;
|
|
925
|
+
var _a, _b;
|
|
686
926
|
let ctx = ctx_default(command3.optsWithGlobals());
|
|
687
927
|
if (!which__default.default.sync(execCommand[0], { nothrow: true })) {
|
|
688
|
-
|
|
928
|
+
ctx.log.error(`Error: Command not found "${execCommand[0]}"`);
|
|
689
929
|
return;
|
|
690
930
|
}
|
|
691
931
|
ctx.args.execCommand = execCommand;
|
|
@@ -713,9 +953,10 @@ command.name("exec").description("Run test commands around SmartUI").argument("<
|
|
|
713
953
|
try {
|
|
714
954
|
yield tasks.run(ctx);
|
|
715
955
|
} catch (error) {
|
|
716
|
-
|
|
956
|
+
ctx.log.info("\nRefer docs: https://www.lambdatest.com/support/docs/smart-visual-regression-testing/");
|
|
717
957
|
} finally {
|
|
718
958
|
yield (_a = ctx.server) == null ? void 0 : _a.close();
|
|
959
|
+
yield (_b = ctx.browser) == null ? void 0 : _b.close();
|
|
719
960
|
}
|
|
720
961
|
});
|
|
721
962
|
});
|
|
@@ -738,7 +979,7 @@ var BROWSER_FIREFOX = "firefox";
|
|
|
738
979
|
var BROWSER_EDGE = "edge";
|
|
739
980
|
var EDGE_CHANNEL = "msedge";
|
|
740
981
|
var PW_WEBKIT = "webkit";
|
|
741
|
-
var
|
|
982
|
+
var MIN_VIEWPORT_HEIGHT2 = 1080;
|
|
742
983
|
function captureScreenshots(ctx, screenshots) {
|
|
743
984
|
return __async(this, null, function* () {
|
|
744
985
|
var _a;
|
|
@@ -782,7 +1023,7 @@ function captureScreenshots(ctx, screenshots) {
|
|
|
782
1023
|
let { width, height } = ctx.webConfig.viewports[k];
|
|
783
1024
|
let ssName = `${browserName}-${width}x${height}-${screenshotId}.png`;
|
|
784
1025
|
let ssPath = `screenshots/${screenshotId}/${ssName}.png`;
|
|
785
|
-
yield page.setViewportSize({ width, height: height ||
|
|
1026
|
+
yield page.setViewportSize({ width, height: height || MIN_VIEWPORT_HEIGHT2 });
|
|
786
1027
|
if (height === 0)
|
|
787
1028
|
yield page.evaluate(scrollToBottomAndBackToTop);
|
|
788
1029
|
yield page.waitForTimeout(screenshot.waitForTimeout || 0);
|
|
@@ -873,6 +1114,26 @@ var capture_default = command2;
|
|
|
873
1114
|
var program = new commander.Command();
|
|
874
1115
|
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);
|
|
875
1116
|
var commander_default = program;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1117
|
+
(function() {
|
|
1118
|
+
return __async(this, null, function* () {
|
|
1119
|
+
let client = new httpClient(env_default());
|
|
1120
|
+
let log = logger_default;
|
|
1121
|
+
try {
|
|
1122
|
+
log.info(`
|
|
1123
|
+
LambdaTest SmartUI CLI v${package_default.version}`);
|
|
1124
|
+
let { data: { latestVersion, deprecated } } = yield client.checkUpdate(log);
|
|
1125
|
+
if (deprecated)
|
|
1126
|
+
log.warn(`This version is deprecated. A new version ${latestVersion} is available!
|
|
1127
|
+
`);
|
|
1128
|
+
else if (package_default.version !== latestVersion)
|
|
1129
|
+
log.info(chalk__default.default.gray(`A new version ${latestVersion} is available!
|
|
1130
|
+
`));
|
|
1131
|
+
else
|
|
1132
|
+
log.info(chalk__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
log.debug(error);
|
|
1135
|
+
log.info(chalk__default.default.gray("https://www.npmjs.com/package/@lambdatest/smartui-cli\n"));
|
|
1136
|
+
}
|
|
1137
|
+
commander_default.parse();
|
|
1138
|
+
});
|
|
1139
|
+
})();
|