artes 1.2.23 β†’ 1.2.25

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/README.md CHANGED
@@ -122,7 +122,7 @@ npx artes [options]
122
122
  | πŸ•ΆοΈ `--headless` | Run browser in headless mode | `artes --headless` |
123
123
  | ⚑ `--parallel` | Run tests in parallel mode | `artes --parallel 2` |
124
124
  | πŸ” `--retry` | Retry failed tests | `artes --retry 3` |
125
- | πŸ” `--rerun` | Rerun only the failed tests from previous run | `artes --rerun @rerun.txt`|
125
+ | πŸ” `--rerun` | Rerun only the failed tests from previous run | `artes --rerun @rerun.txt` |
126
126
  | 🎭 `--dryRun` | Perform a dry run without executing tests | `artes --dryRun` |
127
127
  | πŸ“ˆ `--percentage` | Set minimum success percentage to pass test run (default is 0) | `artes --percentage 85` |
128
128
  | 🌍 `--browser` | Specify browser to use (`chromium`, `firefox`, or `webkit`) | `artes --browser chromium` |
@@ -90,11 +90,15 @@ module.exports = {
90
90
  ? true
91
91
  : artesConfig.reportSuccess || false, // Include successful tests in report
92
92
 
93
- trace: process.env.TRACE === "true" ? process.env.TRACE : artesConfig.trace || false, // Enable tracing
94
-
95
- reportWithTrace: process.env.REPORT_WITH_TRACE === "true"
96
- ? process.env.REPORT_WITH_TRACE
97
- : artesConfig.reportWithTrace || false, // Include trace in report
93
+ trace:
94
+ process.env.TRACE === "true"
95
+ ? process.env.TRACE
96
+ : artesConfig.trace || false, // Enable tracing
97
+
98
+ reportWithTrace:
99
+ process.env.REPORT_WITH_TRACE === "true"
100
+ ? process.env.REPORT_WITH_TRACE
101
+ : artesConfig.reportWithTrace || false, // Include trace in report
98
102
 
99
103
  format: finalFormats, // Formatter names/paths
100
104
  formatOptions: artesConfig.formatOptions || {
@@ -434,6 +434,7 @@ await getURL();
434
434
  ```
435
435
 
436
436
  - **Returns**:
437
+
437
438
  - _(string)_: The current page URL.
438
439
 
439
440
  ***
@@ -655,6 +656,7 @@ await shouldHaveId("#element-id", "unique-id");
655
656
  ```
656
657
 
657
658
  - **Parameters**:
659
+
658
660
  - `id` _(string)_: The expected ID.
659
661
 
660
662
  Got it! Here’s the refined format for the assertion methods:
@@ -1542,6 +1544,7 @@ await shouldObjectContaining(".element", { key: "value" });
1542
1544
  ```
1543
1545
 
1544
1546
  - **Parameters**:
1547
+
1545
1548
  - `selector` _(string)_: The CSS selector for the element.
1546
1549
  - `properties` _(object)_: The object properties to check for.
1547
1550
 
package/executer.js CHANGED
@@ -111,7 +111,7 @@ flags.parallel ? (process.env.PARALLEL = parallel) : "";
111
111
 
112
112
  flags.retry ? (process.env.RETRY = retry) : "";
113
113
 
114
- flags.rerun ? ( process.env.RERUN = rerun) : "";
114
+ flags.rerun ? (process.env.RERUN = rerun) : "";
115
115
 
116
116
  flags.dryRun ? (process.env.DRYRUN = flags.dryRun) : "";
117
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artes",
3
- "version": "1.2.23",
3
+ "version": "1.2.25",
4
4
  "description": "The simplest way to automate UI and API tests using Cucumber-style steps.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -23,7 +23,7 @@ function createProject(createYes, noDeps) {
23
23
  });
24
24
 
25
25
  if (!noDeps) {
26
- execSync("npm i artes", { cwd: projectDir, stdio: "inherit" });
26
+ execSync("npm i artes@latest", { cwd: projectDir, stdio: "inherit" });
27
27
  console.log("πŸ“¦ Setting up browsers...");
28
28
  execSync("npx playwright install", { cwd: projectDir, stdio: "inherit" });
29
29
  }
@@ -40,10 +40,6 @@ function createProject(createYes, noDeps) {
40
40
  testWithReport: "npx artes -r",
41
41
  };
42
42
 
43
- packageJson.dependencies = {
44
- artes: "1.2.21",
45
- };
46
-
47
43
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
48
44
 
49
45
  const config = `module.exports = {
@@ -29,7 +29,6 @@ function generateReport() {
29
29
  `πŸ“‹ Report generated successfully in ${moduleConfig.reportPath}!`,
30
30
  );
31
31
 
32
-
33
32
  if (fs.existsSync(moduleConfig.reportPath) && process.env.ZIP === "true") {
34
33
  console.log(`πŸ—œοΈ Zipping report folder`);
35
34
 
@@ -61,8 +60,7 @@ function generateReport() {
61
60
 
62
61
  console.log(`πŸ—œοΈ Zipped in ${moduleConfig.reportPath}/report.zip!`);
63
62
  if (error) throw error;
64
- }
65
-
63
+ }
66
64
  } catch (err) {
67
65
  console.error("❌ Report generation failed:", err);
68
66
  process.env.EXIT_CODE = 1;
@@ -8,17 +8,24 @@ function runTests() {
8
8
  process.env.FORCE_COLOR = "1";
9
9
  process.env.FORCE_STDIO_TTY = "1";
10
10
 
11
- spawnSync("cucumber-js", ["--config=cucumber.config.js", `${process.env.RERUN ? path.join("../../", process.env.RERUN) : ""}`], {
12
- cwd: moduleConfig.modulePath,
13
- stdio: "inherit",
14
- shell: true,
15
- env: {
16
- ...process.env,
17
- FORCE_TTY: "1",
18
- FORCE_COLOR: "1",
19
- CI: "false",
11
+ spawnSync(
12
+ "cucumber-js",
13
+ [
14
+ "--config=cucumber.config.js",
15
+ `${process.env.RERUN ? path.join("../../", process.env.RERUN) : ""}`,
16
+ ],
17
+ {
18
+ cwd: moduleConfig.modulePath,
19
+ stdio: "inherit",
20
+ shell: true,
21
+ env: {
22
+ ...process.env,
23
+ FORCE_TTY: "1",
24
+ FORCE_COLOR: "1",
25
+ CI: "false",
26
+ },
20
27
  },
21
- });
28
+ );
22
29
  console.log("βœ… Tests running completed successfully!");
23
30
  } catch (error) {
24
31
  console.error("❌ Test execution failed:", error);
@@ -35,7 +35,7 @@ const moduleConfig = {
35
35
  stepsPath: path.join(projectPath, "/tests/steps/*.js"),
36
36
  pomPath: path.join(projectPath, "/tests/POMs"),
37
37
  cleanUpPaths:
38
- "allure-result allure-results test-results testsStatus EXIT_CODE.txt",
38
+ "allure-result allure-results test-results @rerun.txt testsStatus EXIT_CODE.txt",
39
39
  };
40
40
 
41
41
  module.exports = {
@@ -28,6 +28,58 @@ function getMimeType(filePath) {
28
28
  return mimeTypes[ext] || "application/octet-stream";
29
29
  }
30
30
 
31
+ function curlExtractor(url, method, headers, body, requestDataType) {
32
+ let curlCommand = `curl -X ${method} '${url}'`;
33
+
34
+ if (headers && Object.keys(headers).length > 0) {
35
+ for (const [key, value] of Object.entries(headers)) {
36
+ curlCommand += ` \\\n -H '${key}: ${value}'`;
37
+ }
38
+ }
39
+
40
+ if (body && Object.keys(body).length > 0) {
41
+ switch (requestDataType) {
42
+ case "multipart":
43
+ for (const [key, value] of Object.entries(body)) {
44
+ if (typeof value === "object" && value.buffer) {
45
+ curlCommand += ` \\\n -F '${key}=@${value.name}'`;
46
+ } else {
47
+ curlCommand += ` \\\n -F '${key}=${value}'`;
48
+ }
49
+ }
50
+ break;
51
+
52
+ case "urlencoded":
53
+ case "application/x-www-form-urlencoded":
54
+ const urlEncodedData = new URLSearchParams(body).toString();
55
+ curlCommand += ` \\\n --data-urlencode '${urlEncodedData}'`;
56
+ break;
57
+
58
+ case "form":
59
+ for (const [key, value] of Object.entries(body)) {
60
+ curlCommand += ` \\\n -F '${key}=${value}'`;
61
+ }
62
+ break;
63
+
64
+ default:
65
+ const hasContentType =
66
+ headers &&
67
+ Object.keys(headers).some(
68
+ (key) => key.toLowerCase() === "content-type",
69
+ );
70
+
71
+ if (!hasContentType) {
72
+ curlCommand += ` \\\n -H 'Content-Type: application/json'`;
73
+ }
74
+
75
+ curlCommand += ` \\\n --data-raw '${JSON.stringify(body)}'`;
76
+ break;
77
+ }
78
+ }
79
+
80
+ return curlCommand;
81
+ }
82
+
31
83
  function processForm(requestBody) {
32
84
  let formData = {};
33
85
  for (const [key, value] of Object.entries(requestBody)) {
@@ -108,7 +160,7 @@ async function requestMaker(headers, data, requestDataType) {
108
160
  return request;
109
161
  }
110
162
 
111
- async function responseMaker(request, response, duration) {
163
+ async function responseMaker(request, response, duration, curlCommand) {
112
164
  const responseObject = {};
113
165
 
114
166
  response &&
@@ -139,6 +191,8 @@ async function responseMaker(request, response, duration) {
139
191
  }
140
192
  }
141
193
 
194
+ curlCommand && Object.assign(responseObject, { "cURL Command": curlCommand });
195
+
142
196
  return responseObject;
143
197
  }
144
198
 
@@ -157,7 +211,15 @@ const api = {
157
211
 
158
212
  const duration = performance.now() - requestStarts;
159
213
 
160
- const response = responseMaker(payloadJSON, res, duration);
214
+ const curlCommand = curlExtractor(
215
+ res.url(),
216
+ "GET",
217
+ payloadJSON?.headers || {},
218
+ null,
219
+ null,
220
+ );
221
+
222
+ const response = responseMaker(payloadJSON, res, duration, curlCommand);
161
223
 
162
224
  context.response = await response;
163
225
  },
@@ -170,7 +232,9 @@ const api = {
170
232
 
171
233
  const duration = performance.now() - requestStarts;
172
234
 
173
- const response = responseMaker(payloadJSON, res, duration);
235
+ const curlCommand = curlExtractor(res.url(), "HEAD", {}, null, null);
236
+
237
+ const response = responseMaker(null, res, duration, curlCommand);
174
238
 
175
239
  context.response = await response;
176
240
  },
@@ -181,6 +245,7 @@ const api = {
181
245
  const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
182
246
 
183
247
  let req;
248
+ let bodyForCurl = payloadJSON?.body || {};
184
249
 
185
250
  switch (requestDataType) {
186
251
  case "multipart":
@@ -191,6 +256,7 @@ const api = {
191
256
  formRequest || {},
192
257
  requestDataType,
193
258
  );
259
+ bodyForCurl = formRequest;
194
260
  break;
195
261
  case "urlencoded":
196
262
  case "application/x-www-form-urlencoded":
@@ -223,7 +289,20 @@ const api = {
223
289
 
224
290
  const duration = performance.now() - requestStarts;
225
291
 
226
- const response = await responseMaker(payloadJSON, res, duration);
292
+ const curlCommand = curlExtractor(
293
+ res.url(),
294
+ "POST",
295
+ req.headers,
296
+ bodyForCurl,
297
+ requestDataType,
298
+ );
299
+
300
+ const response = await responseMaker(
301
+ payloadJSON,
302
+ res,
303
+ duration,
304
+ curlCommand,
305
+ );
227
306
 
228
307
  context.response = response;
229
308
  },
@@ -234,6 +313,7 @@ const api = {
234
313
  const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
235
314
 
236
315
  let req;
316
+ let bodyForCurl = payloadJSON?.body || {};
237
317
 
238
318
  switch (requestDataType) {
239
319
  case "multipart":
@@ -244,6 +324,7 @@ const api = {
244
324
  formRequest || {},
245
325
  requestDataType,
246
326
  );
327
+ bodyForCurl = formRequest;
247
328
  break;
248
329
  case "urlencoded":
249
330
  case "application/x-www-form-urlencoded":
@@ -276,7 +357,20 @@ const api = {
276
357
 
277
358
  const duration = performance.now() - requestStarts;
278
359
 
279
- const response = await responseMaker(payloadJSON, res, duration);
360
+ const curlCommand = curlExtractor(
361
+ res.url(),
362
+ "PUT",
363
+ req.headers,
364
+ bodyForCurl,
365
+ requestDataType,
366
+ );
367
+
368
+ const response = await responseMaker(
369
+ payloadJSON,
370
+ res,
371
+ duration,
372
+ curlCommand,
373
+ );
280
374
 
281
375
  context.response = response;
282
376
  },
@@ -287,6 +381,7 @@ const api = {
287
381
  const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
288
382
 
289
383
  let req;
384
+ let bodyForCurl = payloadJSON?.body || {};
290
385
 
291
386
  switch (requestDataType) {
292
387
  case "multipart":
@@ -297,6 +392,7 @@ const api = {
297
392
  formRequest || {},
298
393
  requestDataType,
299
394
  );
395
+ bodyForCurl = formRequest;
300
396
  break;
301
397
  case "urlencoded":
302
398
  case "application/x-www-form-urlencoded":
@@ -329,7 +425,20 @@ const api = {
329
425
 
330
426
  const duration = performance.now() - requestStarts;
331
427
 
332
- const response = await responseMaker(payloadJSON, res, duration);
428
+ const curlCommand = curlExtractor(
429
+ res.url(),
430
+ "PATCH",
431
+ req.headers,
432
+ bodyForCurl,
433
+ requestDataType,
434
+ );
435
+
436
+ const response = await responseMaker(
437
+ payloadJSON,
438
+ res,
439
+ duration,
440
+ curlCommand,
441
+ );
333
442
 
334
443
  context.response = response;
335
444
  },
@@ -350,7 +459,15 @@ const api = {
350
459
 
351
460
  const duration = performance.now() - requestStarts;
352
461
 
353
- const response = responseMaker(payloadJSON, res, duration);
462
+ const curlCommand = curlExtractor(
463
+ res.url(),
464
+ "DELETE",
465
+ payloadJSON?.headers || {},
466
+ payloadJSON?.body || {},
467
+ null,
468
+ );
469
+
470
+ const response = responseMaker(payloadJSON, res, duration, curlCommand);
354
471
 
355
472
  context.response = await response;
356
473
  },
@@ -103,7 +103,7 @@ Before(async function () {
103
103
 
104
104
  await context.page.setDefaultTimeout(cucumberConfig.default.timeout);
105
105
 
106
- if ( cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) {
106
+ if (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) {
107
107
  await browserContext.tracing.start({
108
108
  sources: true,
109
109
  screenshots: true,
@@ -142,10 +142,9 @@ After(async function ({ pickle, result }) {
142
142
  }
143
143
 
144
144
  const shouldReport =
145
- (cucumberConfig.default.successReport ||
146
- result?.status !== Status.PASSED)
145
+ cucumberConfig.default.successReport || result?.status !== Status.PASSED;
147
146
 
148
- if (shouldReport) {
147
+ if (shouldReport & (context.page.url() !== "about:blank")) {
149
148
  const screenshotPath = path.join(
150
149
  "test-results",
151
150
  "visualReport",
@@ -164,16 +163,17 @@ After(async function ({ pickle, result }) {
164
163
  }
165
164
 
166
165
  saveTestStatus(result, pickle);
167
- if((cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace)){
168
- var tracePath = path.join(
169
- moduleConfig.projectPath,
170
- `./traces/${pickle.name.replaceAll(" ", "_")}.zip`,
171
- );
172
- }
173
-
166
+ if (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) {
167
+ var tracePath = path.join(
168
+ moduleConfig.projectPath,
169
+ `./traces/${pickle.name.replaceAll(" ", "_")}.zip`,
170
+ );
171
+ }
174
172
 
175
173
  if (
176
- (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) && shouldReport && context.page.url() !== "about:blank"
174
+ (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) &&
175
+ shouldReport &&
176
+ context.page.url() !== "about:blank"
177
177
  ) {
178
178
  await context.browserContext.tracing.stop({
179
179
  path: tracePath,
@@ -188,11 +188,19 @@ if((cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace)){
188
188
  });
189
189
 
190
190
  if (!cucumberConfig.default.trace) {
191
- spawnSync("npx", ["rimraf", "--no-glob", path.join(moduleConfig.projectPath, "./traces")], {
192
- cwd: moduleConfig.projectPath,
193
- stdio: "inherit",
194
- shell: false,
195
- });
191
+ spawnSync(
192
+ "npx",
193
+ [
194
+ "rimraf",
195
+ "--no-glob",
196
+ path.join(moduleConfig.projectPath, "./traces"),
197
+ ],
198
+ {
199
+ cwd: moduleConfig.projectPath,
200
+ stdio: "inherit",
201
+ shell: false,
202
+ },
203
+ );
196
204
  }
197
205
  }
198
206
  }
@@ -204,7 +212,11 @@ if((cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace)){
204
212
  await context.browser?.close();
205
213
  await context.request?.dispose();
206
214
 
207
- if (shouldReport && context.page.video) {
215
+ if (
216
+ shouldReport &&
217
+ context.page.video &&
218
+ context.page.url() !== "about:blank"
219
+ ) {
208
220
  const video = context.page.video();
209
221
  if (video) {
210
222
  const videoPath = await video.path();
@@ -236,22 +248,27 @@ AfterAll(async () => {
236
248
 
237
249
  const failed = files.filter((f) => f.split("-")[0] === "FAILED").length;
238
250
 
239
- if (failed > 0 ){
251
+ if (failed > 0) {
240
252
  spawnSync("mv", ["@rerun.txt", moduleConfig.projectPath], {
241
253
  cwd: path.join(moduleConfig.projectPath, "node_modules", "artes"),
242
254
  stdio: "ignore",
243
255
  shell: true,
244
256
  });
245
257
  }
246
-
247
258
 
248
259
  if (cucumberConfig.default.testPercentage !== undefined) {
249
- const meetsThreshold = successPercentage >= cucumberConfig.default.testPercentage;
260
+ const meetsThreshold =
261
+ successPercentage >= cucumberConfig.default.testPercentage;
250
262
 
251
263
  if (meetsThreshold) {
252
- console.log(`βœ… Tests passed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`);
264
+ console.log(
265
+ `βœ… Tests passed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`,
266
+ );
253
267
  fs.writeFileSync(path.join(process.cwd(), "EXIT_CODE.txt"), "0");
254
- } else {console.log(`❌ Tests failed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`,);
268
+ } else {
269
+ console.log(
270
+ `❌ Tests failed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`,
271
+ );
255
272
  fs.writeFileSync(path.join(process.cwd(), "EXIT_CODE.txt"), "1");
256
273
  }
257
274
  }
@@ -7,7 +7,6 @@ const {
7
7
  random,
8
8
  } = require("../helper/imports/commons");
9
9
  const { api } = require("../helper/stepFunctions/exporter");
10
- const Ajv = require("ajv");
11
10
 
12
11
  When("User sends GET request to {string}", async function (url) {
13
12
  await api.get(url);