artes 1.1.20 → 1.1.23

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
@@ -50,6 +50,8 @@ npx artes [options]
50
50
  | ✅ `-y, --yes` | Skip the confirmation prompt when creating an example project | `artes -c -y` or `artes --create --yes` |
51
51
  | 📊 `-r, --report` | Run tests and generate Allure report | `artes -r` or `artes --report` |
52
52
  | `--reportSuccess` | Add screenshots and video records for also Success test cases | `artes --reportSuccess` |
53
+ | `--trace` | Enable tracing | `artes --trace ` |
54
+ | `-rwt, --reportWithTrace` | Add trace to the report | ` artes -rwt ` or `artes --reportWithTrace`|
53
55
  | 📁 `--features` | Specify one or more feature files' relative paths to run (comma-separated) | `artes --features "tests/features/Alma,tests/features/Banan.feature"` |
54
56
  | 📜 `--stepDef` | Specify one or more step definition files' relative paths to use (comma-separated) | `artes --stepDef "tests/steps/login.js,tests/steps/home.js"` |
55
57
  | 🔖 `--tags` | Run tests with specified Cucumber tags | `artes --tags "@smoke or @wip"` |
@@ -295,7 +297,10 @@ You can configure Artes by editing the `artes.config.js` file. Below are the def
295
297
  | `pomPath` | `moduleConfig.pomPath` | Path to Page Object Models. |
296
298
  | `import` | `[]` | Support code paths. |
297
299
  | `testPercentage` | `0` | Define test coverage percentage |
298
- | `reportSuccess` | `true` | Add screenshots and video records for also success test cases |
300
+ | `report` | `false` | Generate report |
301
+ | `reportSuccess` | `false` | Add screenshots and video records for also success test cases |
302
+ | `trace` | `false` | Enable trace |
303
+ | `reportWithTrace` | `false` | Add trace to the report |
299
304
  | `format` | `["rerun:@rerun.txt", "allure-cucumberjs/reporter"]` | Formatter names/paths. |
300
305
  | `formatOptions` | `{ "resultsDir": "allure-result" }` | Formatter options. |
301
306
  | `parallel` | `1` | Number of parallel workers. |
@@ -58,10 +58,23 @@ module.exports = {
58
58
  : moduleConfig.pomPath,
59
59
  import: artesConfig.import || [], // Support code paths
60
60
 
61
+ report:
62
+ process.env.REPORT_WITH_TRACE ??
63
+ artesConfig.reportWithTrace ??
64
+ process.env.REPORT ??
65
+ artesConfig.report ??
66
+ false, // Generate report
61
67
  // Formatting and output
62
68
  successReport: process.env.REPORT_SUCCESS
63
69
  ? true
64
70
  : artesConfig.reportSuccess || false, // Include successful tests in report
71
+
72
+ trace: process.env.TRACE ? process.env.TRACE : artesConfig.trace || false, // Enable tracing
73
+
74
+ reportWithTrace: process.env.REPORT_WITH_TRACE
75
+ ? process.env.REPORT_WITH_TRACE
76
+ : artesConfig.reportWithTrace || false, // Include trace in report
77
+
65
78
  format: finalFormats, // Formatter names/paths
66
79
  formatOptions: artesConfig.formatOptions || {
67
80
  resultsDir: `allure-result`,
package/executer.js CHANGED
@@ -7,6 +7,7 @@ const {
7
7
  generateReport,
8
8
  cleanUp,
9
9
  } = require("./src/helper/executers/exporter");
10
+ const artesConfig = require("../../artes.config");
10
11
  const fs = require("fs");
11
12
  const path = require("path");
12
13
 
@@ -20,6 +21,7 @@ const flags = {
20
21
  report: args.includes("-r") || args.includes("--report"),
21
22
  reportSuccess: args.includes("--reportSuccess"),
22
23
  trace: args.includes("-t") || args.includes("--trace"),
24
+ reportWithTrace: args.includes("-rwt") || args.includes("--reportWithTrace"),
23
25
  features: args.includes("--features"),
24
26
  stepDef: args.includes("--stepDef"),
25
27
  tags: args.includes("--tags"),
@@ -56,7 +58,10 @@ const slowMo = args[args.indexOf("--slowMo") + 1];
56
58
  flags.env && console.log("Running env:", env);
57
59
  flags.env ? (process.env.ENV = JSON.stringify(env)) : "";
58
60
 
59
- flags.report
61
+ flags.reportWithTrace ||
62
+ artesConfig.reportWithTrace ||
63
+ flags.report ||
64
+ artesConfig.report
60
65
  ? (process.env.REPORT_FORMAT = JSON.stringify([
61
66
  "allure-cucumberjs/reporter:./allure-results",
62
67
  ]))
@@ -73,8 +78,12 @@ flags.features ? (process.env.FEATURES = features) : "";
73
78
  flags.stepDef && console.log("Running step definitions:", flags.stepDef);
74
79
  flags.stepDef ? (process.env.STEP_DEFINITIONS = stepDef) : "";
75
80
 
81
+ flags.report ? (process.env.REPORT = true) : "";
82
+
76
83
  flags.trace ? (process.env.TRACE = true) : "";
77
84
 
85
+ flags.reportWithTrace ? (process.env.REPORT_WITH_TRACE = true) : "";
86
+
78
87
  flags.headless &&
79
88
  console.log("Running mode:", flags.headless ? "headless" : "headed");
80
89
  flags.headless ? (process.env.MODE = JSON.stringify(true)) : false;
@@ -112,27 +121,21 @@ function main() {
112
121
  if (flags.create) return createProject(flags.createYes);
113
122
 
114
123
  runTests();
115
- if (flags.report) generateReport();
124
+ if (
125
+ flags.reportWithTrace ||
126
+ artesConfig.reportWithTrace ||
127
+ flags.report ||
128
+ artesConfig.report
129
+ )
130
+ generateReport();
116
131
 
117
132
  if (
118
133
  fs.existsSync(
119
- path.join(
120
- process.cwd(),
121
- "node_modules",
122
- "artes",
123
- "testsStatus",
124
- "EXIT_CODE.txt",
125
- ),
134
+ path.join(process.cwd(), "node_modules", "artes", "EXIT_CODE.txt"),
126
135
  )
127
136
  ) {
128
137
  const data = fs.readFileSync(
129
- path.join(
130
- process.cwd(),
131
- "node_modules",
132
- "artes",
133
- "testsStatus",
134
- "EXIT_CODE.txt",
135
- ),
138
+ path.join(process.cwd(), "node_modules", "artes", "EXIT_CODE.txt"),
136
139
  "utf8",
137
140
  );
138
141
  process.env.EXIT_CODE = parseInt(data, 10);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artes",
3
- "version": "1.1.20",
3
+ "version": "1.1.23",
4
4
  "description": "The simplest way to automate UI and API tests using Cucumber-style steps.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -27,6 +27,12 @@ function showHelp() {
27
27
 
28
28
  ✅ --reportSuccess Generate screenshot and video record with also successful tests
29
29
  Usage: artes --reportSuccess
30
+
31
+ --trace Enable tracing for all tests
32
+ Usage: artes --trace
33
+
34
+ -rwt, --reportWithTrace Include trace in the report
35
+ Usage: artes --reportWithTrace
30
36
 
31
37
  📁 --features Specify one or more feature files' relative paths to run (comma-separated)
32
38
  Usage: artes --features "tests/features/Alma, tests/features/Banan.feature"
@@ -48,7 +48,10 @@ function createProject(createYes) {
48
48
  // timeout : 0, // number - Test timeout in seconds
49
49
  // slowMo: 0, // number - Slow down test execution (Default: 0 seconds)
50
50
  // parallel: 0, // number - Number of parallel workers
51
+ //report: true / boolean - Generate report
51
52
  // reportSuccess: false, // boolean - Add screenshots and video records to report also for success test cases
53
+ // trace: false, // boolean - Enable tracing
54
+ // reportWithTrace: false, // boolean - Include trace in report
52
55
  // format: [], // string[] - Formatter names/paths
53
56
  // formatOptions: {}, // object - Formatter options
54
57
  // retry: 0, // number - Retry attempts for failing tests
@@ -8,7 +8,7 @@ function generateReport() {
8
8
  spawnSync("npm", ["run", "testWithReport", moduleConfig.reportPath], {
9
9
  cwd: moduleConfig.modulePath,
10
10
  stdio: "ignore",
11
- shell: true,
11
+ shell: true
12
12
  });
13
13
 
14
14
  console.log(
@@ -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 @rerun.txt testsStatus",
38
+ "allure-result allure-results test-results @rerun.txt testsStatus EXIT_CODE.txt"
39
39
  };
40
40
 
41
41
  module.exports = {
@@ -8,20 +8,47 @@ const {
8
8
  BeforeStep,
9
9
  AfterAll,
10
10
  } = require("@cucumber/cucumber");
11
+ const { spawnSync } = require("child_process");
11
12
  const { invokeBrowser } = require("../helper/contextManager/browserManager");
12
13
  const { invokeRequest } = require("../helper/contextManager/requestManager");
13
14
  const { pomCollector } = require("../helper/controller/pomCollector");
14
15
  const cucumberConfig = require("../../cucumber.config");
15
16
  const { context } = require("./context");
16
17
  const fs = require("fs");
17
- const { moduleConfig } = require("artes/src/helper/imports/commons");
18
18
  const path = require("path");
19
+ const { moduleConfig } = require("artes/src/helper/imports/commons");
19
20
 
20
21
  const statusDir = path.join(process.cwd(), "testsStatus");
22
+ const HTTP_METHODS = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"];
21
23
 
22
24
  setDefaultTimeout(cucumberConfig.default.timeout);
23
25
 
24
- BeforeAll(async function () {
26
+ /* ------------------- Helpers ------------------- */
27
+
28
+ async function attachResponse(attachFn) {
29
+ if (!context.response) return;
30
+
31
+ for (const [key, value] of Object.entries(context.response)) {
32
+ const text =
33
+ typeof value === "object"
34
+ ? `${key}:\n${JSON.stringify(value, null, 2)}`
35
+ : `${key}:\n${value}`;
36
+
37
+ await attachFn(text, "text/plain");
38
+ }
39
+ }
40
+
41
+ function saveTestStatus(result, pickle) {
42
+ fs.mkdirSync(statusDir, { recursive: true });
43
+ fs.writeFileSync(
44
+ path.join(statusDir, `${result.status}-${pickle.id}.txt`),
45
+ "",
46
+ );
47
+ }
48
+
49
+ /* ------------------- Hooks ------------------- */
50
+
51
+ BeforeAll(() => {
25
52
  pomCollector();
26
53
  });
27
54
 
@@ -38,130 +65,135 @@ Before(async function () {
38
65
 
39
66
  await context.page.setDefaultTimeout(cucumberConfig.default.timeout);
40
67
 
41
- process.env.TRACE &&
42
- (await context.browserContext.tracing.start({
68
+ if (
69
+ (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) &&
70
+ !context.response
71
+ ) {
72
+ await browserContext.tracing.start({
43
73
  sources: true,
44
74
  screenshots: true,
45
75
  snapshots: true,
46
- }));
76
+ });
77
+ }
47
78
  });
48
79
 
49
- BeforeStep(async function ({ pickleStep }) {
50
- const stepText = pickleStep.text;
51
-
52
- const methods = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"];
53
-
54
- if (methods.some((method) => stepText.includes(method))) {
80
+ BeforeStep(({ pickleStep }) => {
81
+ if (HTTP_METHODS.some((method) => pickleStep.text.includes(method))) {
55
82
  context.response = {};
56
83
  }
57
84
  });
58
85
 
59
86
  AfterStep(async function ({ pickleStep }) {
60
- const stepText = pickleStep.text;
61
-
62
- const methods = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"];
63
-
64
- if (methods.some((method) => stepText.includes(method))) {
65
- if (await context.response) {
66
- for (const [key, value] of Object.entries(context.response)) {
67
- let text = `${key}:\n`;
68
-
69
- if (typeof value === "object") {
70
- text += JSON.stringify(value, null, 2);
71
- } else {
72
- text += value;
73
- }
74
-
75
- await this.attach(text, "text/plain");
76
- }
77
- }
87
+ if (HTTP_METHODS.some((method) => pickleStep.text.includes(method))) {
88
+ await attachResponse(this.attach);
78
89
  }
79
90
  });
80
91
 
81
92
  After(async function ({ pickle, result }) {
82
- if (process.env.REPORT_SUCCESS || result?.status !== Status.PASSED) {
83
- let img = await context.page.screenshot({
84
- path: `./test-results/visualReport/${pickle.name}/${pickle.name}.png`,
93
+ const shouldReport =
94
+ (cucumberConfig.default.successReport ||
95
+ result?.status !== Status.PASSED) &&
96
+ !context.response;
97
+
98
+ if (shouldReport) {
99
+ const screenshotPath = path.join(
100
+ "test-results",
101
+ "visualReport",
102
+ pickle.name,
103
+ `${pickle.name}.png`,
104
+ );
105
+
106
+ const img = await context.page.screenshot({
107
+ path: screenshotPath,
85
108
  type: "png",
86
109
  });
87
- await this.attach(img, "image/png");
110
+ await this.attach(img, {
111
+ mediaType: "image/png",
112
+ fileName: `${pickle.name.replaceAll(" ", "_")}.png`,
113
+ });
88
114
  }
89
115
 
90
- fs.mkdirSync(statusDir, { recursive: true });
116
+ saveTestStatus(result, pickle);
91
117
 
92
- fs.writeFileSync(
93
- path.join(statusDir, `${result.status}-${pickle.id}.txt`),
94
- "",
118
+ const tracePath = path.join(
119
+ moduleConfig.projectPath,
120
+ `./${pickle.name.replaceAll(" ", "_")}.zip`,
95
121
  );
96
122
 
97
- process.env.TRACE &&
98
- (await context.browserContext.tracing.stop({
99
- path: path.join(moduleConfig.projectPath, "./trace.zip"),
100
- }));
123
+ if (
124
+ (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) &&
125
+ !context.response &&
126
+ shouldReport
127
+ ) {
128
+ await context.browserContext.tracing.stop({
129
+ path: tracePath,
130
+ });
131
+
132
+ if (cucumberConfig.default.reportWithTrace) {
133
+ const trace = fs.readFileSync(tracePath);
101
134
 
102
- if (context.response) {
103
- for (const [key, value] of Object.entries(context.response)) {
104
- let text = `${key}:\n`;
135
+ await this.attach(trace, {
136
+ mediaType: "application/zip",
137
+ fileName: `${pickle.name.replace(/\s+/g, "_")}_trace.zip`,
138
+ });
105
139
 
106
- if (typeof value === "object") {
107
- text += JSON.stringify(value, null, 2);
108
- } else {
109
- text += value;
140
+ if (!cucumberConfig.default.trace) {
141
+ spawnSync("npx", ["rimraf", tracePath], {
142
+ cwd: moduleConfig.projectPath,
143
+ stdio: "inherit",
144
+ shell: true,
145
+ });
110
146
  }
111
-
112
- await this.attach(text, "text/plain");
113
147
  }
114
148
  }
115
149
 
116
- await context.page?.close();
150
+ await attachResponse(this.attach);
117
151
 
152
+ await context.page?.close();
118
153
  await context.browserContext?.close();
119
-
120
154
  await context.browser?.close();
121
-
122
155
  await context.request?.dispose();
123
156
 
124
- if (
125
- (process.env.REPORT_SUCCESS || result?.status !== Status.PASSED) &&
126
- context.page.video()
127
- ) {
128
- const videoPath = await context.page.video().path();
129
- await new Promise((resolve) => setTimeout(resolve, 1000));
157
+ if (shouldReport && context.page.video) {
158
+ const video = context.page.video();
159
+ if (video) {
160
+ const videoPath = await video.path();
161
+
162
+ await new Promise((resolve) => setTimeout(resolve, 1000));
130
163
 
131
- if (fs.existsSync(videoPath)) {
132
- const webmBuffer = await fs.readFileSync(videoPath);
133
- await this.attach(webmBuffer, "video/webm");
164
+ if (fs.existsSync(videoPath)) {
165
+ const webmBuffer = fs.readFileSync(videoPath);
166
+ await this.attach(webmBuffer, {
167
+ mediaType: "video/webm",
168
+ fileName: `${pickle.name.replaceAll(" ", "_")}.webm`,
169
+ });
170
+ }
134
171
  }
135
172
  }
136
173
  });
137
174
 
138
- AfterAll(async function () {
139
- if (fs.existsSync(statusDir)) {
140
- const files = fs.readdirSync(statusDir);
141
- const passedCount = files.filter(
142
- (file) => file.split("-")[0] === "PASSED",
143
- ).length;
144
- const totalTests = files.length;
145
-
146
- const successPercentage = (passedCount / totalTests) * 100;
147
- const successRate =
148
- successPercentage.toFixed(2) >= cucumberConfig.default.testPercentage;
149
-
150
- if (
151
- cucumberConfig.default.testPercentage != undefined &&
152
- !isNaN(successPercentage)
153
- ) {
154
- if (successRate) {
155
- console.log(
156
- `Tests passed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}% !`,
157
- );
158
- fs.writeFileSync(path.join(statusDir, `EXIT_CODE.txt`), "0");
159
- } else {
160
- console.log(
161
- `Tests failed at required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`,
162
- );
163
- fs.writeFileSync(path.join(statusDir, `EXIT_CODE.txt`), "1");
164
- }
175
+ AfterAll(() => {
176
+ if (!fs.existsSync(statusDir)) return;
177
+
178
+ const files = fs.readdirSync(statusDir);
179
+ const passedCount = files.filter((f) => f.split("-")[0] === "PASSED").length;
180
+ const totalTests = files.length;
181
+ const successPercentage = (passedCount / totalTests) * 100;
182
+
183
+ if (cucumberConfig.default.testPercentage !== undefined) {
184
+ const meetsThreshold =
185
+ successPercentage >= cucumberConfig.default.testPercentage;
186
+
187
+ if (meetsThreshold) {
188
+ console.log(
189
+ `✅ Tests passed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`,
190
+ );
191
+ fs.writeFileSync(path.join(process.cwd(), "EXIT_CODE.txt"), "0");
192
+ } else {
193
+ console.log(
194
+ `❌ Tests failed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}%!`,
195
+ );
196
+ fs.writeFileSync(path.join(process.cwd(), "EXIT_CODE.txt"), "1");
165
197
  }
166
198
  }
167
199
  });