artes 1.4.7 → 1.4.9

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 (40) hide show
  1. package/README.md +668 -668
  2. package/cucumber.config.js +223 -223
  3. package/docs/emulationDevicesList.md +152 -152
  4. package/docs/functionDefinitions.md +2401 -2401
  5. package/docs/stepDefinitions.md +402 -402
  6. package/executer.js +479 -479
  7. package/index.js +50 -50
  8. package/package.json +52 -52
  9. package/src/helper/contextManager/browserManager.js +74 -74
  10. package/src/helper/contextManager/requestManager.js +23 -23
  11. package/src/helper/controller/elementController.js +203 -185
  12. package/src/helper/controller/pomCollector.js +82 -82
  13. package/src/helper/executers/cleaner.js +19 -19
  14. package/src/helper/executers/exporter.js +15 -15
  15. package/src/helper/executers/helper.js +110 -110
  16. package/src/helper/executers/projectCreator.js +206 -206
  17. package/src/helper/executers/reportGenerator.js +70 -70
  18. package/src/helper/executers/testRunner.js +28 -28
  19. package/src/helper/executers/versionChecker.js +31 -31
  20. package/src/helper/imports/commons.js +57 -57
  21. package/src/helper/stepFunctions/APIActions.js +495 -495
  22. package/src/helper/stepFunctions/assertions.js +989 -989
  23. package/src/helper/stepFunctions/browserActions.js +22 -22
  24. package/src/helper/stepFunctions/elementInteractions.js +60 -60
  25. package/src/helper/stepFunctions/exporter.js +19 -19
  26. package/src/helper/stepFunctions/frameActions.js +72 -72
  27. package/src/helper/stepFunctions/keyboardActions.js +66 -66
  28. package/src/helper/stepFunctions/mouseActions.js +83 -83
  29. package/src/helper/stepFunctions/pageActions.js +43 -43
  30. package/src/hooks/context.js +15 -15
  31. package/src/hooks/hooks.js +215 -215
  32. package/src/stepDefinitions/API.steps.js +310 -310
  33. package/src/stepDefinitions/assertions.steps.js +1092 -1092
  34. package/src/stepDefinitions/browser.steps.js +7 -7
  35. package/src/stepDefinitions/frameActions.steps.js +76 -76
  36. package/src/stepDefinitions/keyboardActions.steps.js +265 -265
  37. package/src/stepDefinitions/mouseActions.steps.js +378 -378
  38. package/src/stepDefinitions/page.steps.js +71 -71
  39. package/src/stepDefinitions/random.steps.js +188 -188
  40. package/status-formatter.js +138 -138
package/executer.js CHANGED
@@ -1,479 +1,479 @@
1
- #!/usr/bin/env node
2
- const {
3
- showHelp,
4
- showVersion,
5
- createProject,
6
- runTests,
7
- generateReport,
8
- cleanUp,
9
- } = require("./src/helper/executers/exporter");
10
- const { logPomWarnings } = require("./src/helper/controller/pomCollector");
11
- const fs = require("fs");
12
- const path = require("path");
13
- const { spawnSync } = require("child_process");
14
-
15
- const artesConfigPath = path.resolve(process.cwd(), "artes.config.js");
16
-
17
- let artesConfig = {};
18
-
19
- if (fs.existsSync(artesConfigPath)) {
20
- artesConfig = require(artesConfigPath);
21
- }
22
-
23
- const args = process.argv.slice(2);
24
-
25
- const getArgValue = (flag) => {
26
- const index = args.indexOf(flag);
27
- return index >= 0 ? args[index + 1] : undefined;
28
- };
29
-
30
- const flags = {
31
- help: args.includes("-h") || args.includes("--help"),
32
- version: args.includes("-v") || args.includes("--version"),
33
- create: args.includes("-c") || args.includes("--create"),
34
- createYes: args.includes("-y") || args.includes("--yes"),
35
- noDeps: args.includes("--noDeps"),
36
- report: args.includes("-r") || args.includes("--report"),
37
- reportSuccess: args.includes("--reportSuccess"),
38
- trace: args.includes("-t") || args.includes("--trace"),
39
- reportWithTrace: args.includes("-rwt") || args.includes("--reportWithTrace"),
40
- singleFileReport: args.includes("--singleFileReport"),
41
- zip: args.includes("--zip"),
42
- features: args.includes("--features"),
43
- stepDef: args.includes("--stepDef"),
44
- tags: args.includes("--tags"),
45
- env: args.includes("--env"),
46
- saveVar: args.includes("--saveVar"),
47
- headless: args.includes("--headless"),
48
- parallel: args.includes("--parallel"),
49
- retry: args.includes("--retry"),
50
- rerun: args.includes("--rerun"),
51
- dryRun: args.includes("--dryRun"),
52
- percentage: args.includes("--percentage"),
53
- browser: args.includes("--browser"),
54
- offline: args.includes("--offline"),
55
- device: args.includes("--device"),
56
- baseURL: args.includes("--baseURL"),
57
- maximizeScreen: args.includes("--maxScreen"),
58
- width: args.includes("--width"),
59
- height: args.includes("--height"),
60
- timeout: args.includes("--timeout"),
61
- slowMo: args.includes("--slowMo"),
62
- };
63
-
64
-
65
- const env = getArgValue("--env");
66
- const vars = getArgValue("--saveVar");
67
- const featureFiles = getArgValue("--features");
68
- const features = flags.features && featureFiles;
69
- const stepDef = getArgValue("--stepDef");
70
- const tags = getArgValue("--tags");
71
- const parallel = getArgValue("--parallel");
72
- const retry = getArgValue("--retry");
73
- const rerun = getArgValue("--rerun");
74
- const percentage = getArgValue("--percentage");
75
- const browser = getArgValue("--browser");
76
- const device = getArgValue("--device");
77
- const baseURL = getArgValue("--baseURL");
78
- const width = getArgValue("--width");
79
- const height = getArgValue("--height");
80
- const timeout = getArgValue("--timeout");
81
- const slowMo = getArgValue("--slowMo");
82
-
83
-
84
- flags.env ? (process.env.ENV = env) : "";
85
-
86
- vars ? (process.env.VARS = vars) : "";
87
-
88
- flags.reportWithTrace ||
89
- artesConfig.reportWithTrace ||
90
- flags.report ||
91
- artesConfig.report
92
- ? (process.env.REPORT_FORMAT = JSON.stringify([
93
- "allure-cucumberjs/reporter:./allure-results",
94
- ]))
95
- : "";
96
-
97
- flags.reportSuccess ? (process.env.REPORT_SUCCESS = true) : "";
98
-
99
- flags.tags && console.log("Running tags:", tags);
100
- flags.tags ? (process.env.RUN_TAGS = JSON.stringify(tags)) : "";
101
-
102
- flags.features && console.log("Running features:", features);
103
- flags.features ? (process.env.FEATURES = features) : "";
104
-
105
- flags.stepDef && console.log("Running step definitions:", flags.stepDef);
106
- flags.stepDef ? (process.env.STEP_DEFINITIONS = stepDef) : "";
107
-
108
- flags.report ? (process.env.REPORT = true) : "";
109
-
110
- flags.trace ? (process.env.TRACE = true) : "";
111
-
112
- flags.reportWithTrace
113
- ? (process.env.REPORT_WITH_TRACE = true)
114
- : (process.env.REPORT_WITH_TRACE = false);
115
-
116
- flags.singleFileReport
117
- ? (process.env.SINGLE_FILE_REPORT = true)
118
- : (process.env.SINGLE_FILE_REPORT = false);
119
-
120
- flags.zip ? (process.env.ZIP = true) : (process.env.ZIP = false);
121
-
122
- flags.headless &&
123
- console.log("Running mode:", flags.headless ? "headless" : "headed");
124
- flags.headless ? (process.env.MODE = JSON.stringify(true)) : false;
125
-
126
- flags.parallel ? (process.env.PARALLEL = parallel) : "";
127
-
128
- flags.retry ? (process.env.RETRY = retry) : "";
129
-
130
- flags.rerun ? (process.env.RERUN = rerun) : "";
131
-
132
- flags.dryRun ? (process.env.DRYRUN = flags.dryRun) : "";
133
-
134
- flags.percentage ? (process.env.PERCENTAGE = percentage) : "";
135
-
136
- flags.browser && console.log("Running browser:", browser);
137
- flags.browser ? (process.env.BROWSER = JSON.stringify(browser)) : "";
138
-
139
- flags.browser && console.log("Running mode:", flags.offline && "Offline");
140
- flags.offline ? (process.env.OFFLINE = true) : "";
141
-
142
- flags.device && console.log("Running device:", device);
143
- flags.device ? (process.env.DEVICE = JSON.stringify(device)) : "";
144
-
145
- flags.baseURL ? (process.env.BASE_URL = JSON.stringify(baseURL)) : "";
146
-
147
- flags.maximizeScreen
148
- ? (process.env.MAXIMIZE_SCREEN = flags.maximizeScreen)
149
- : "";
150
-
151
- flags.width && console.log("Running width:", width);
152
- flags.width ? (process.env.WIDTH = width) : "";
153
-
154
- flags.height && console.log("Running height:", height);
155
- flags.height ? (process.env.HEIGHT = height) : "";
156
-
157
- flags.timeout ? (process.env.TIMEOUT = timeout) : "";
158
-
159
- flags.slowMo ? (process.env.SLOWMO = slowMo) : "";
160
-
161
-
162
- function findDuplicateTestNames() {
163
- const testStatusFile = path.join(process.cwd(), "node_modules", "artes" , "test-status", 'test-status.txt');
164
-
165
- if (!fs.existsSync(testStatusFile)) {
166
- console.error('test-status.txt not found');
167
- return;
168
- }
169
-
170
- const content = fs.readFileSync(testStatusFile, 'utf8');
171
- const lines = content.split('\n').filter(line => line.trim());
172
-
173
- const testNameToFiles = {};
174
-
175
- lines.forEach(line => {
176
- const parts = line.split(' | ');
177
- if (parts.length < 5) return;
178
-
179
- const testName = parts[2].trim();
180
- const filePath = parts[4].trim();
181
-
182
- if (!testNameToFiles[testName]) {
183
- testNameToFiles[testName] = new Set();
184
- }
185
-
186
- testNameToFiles[testName].add(filePath);
187
- });
188
-
189
- const duplicates = {};
190
-
191
- Object.entries(testNameToFiles).forEach(([testName, files]) => {
192
- if (files.size > 1) {
193
- duplicates[testName] = Array.from(files);
194
- }
195
- });
196
-
197
- if (Object.keys(duplicates).length > 0) {
198
- console.warn('\n\x1b[33m[WARNING] Duplicate scenarios names found: This will effect your reporting');
199
- Object.entries(duplicates).forEach(([testName, files]) => {
200
- console.log(`\x1b[33m"${testName}" exists in:`);
201
- files.forEach(file => {
202
- console.log(` - ${file}`);
203
- });
204
- console.log('');
205
- });
206
- console.log("\x1b[0m");
207
- }
208
-
209
- return duplicates;
210
- }
211
-
212
-
213
- function testCoverageCalculation() {
214
-
215
- const testStatusFile = path.join(process.cwd(), "node_modules", "artes" , "test-status", 'test-status.txt');
216
-
217
- if (!fs.existsSync(testStatusFile)) {
218
- console.error('test-status.txt not found');
219
- return null;
220
- }
221
-
222
- const content = fs.readFileSync(testStatusFile, 'utf8');
223
- const lines = content.split('\n').filter(line => line.trim());
224
-
225
- const map = {};
226
- const retriedTests = [];
227
- const uuidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
228
-
229
- lines.forEach(line => {
230
- const parts = line.split(' | ');
231
- if (parts.length < 5) return;
232
-
233
- const timestamp = parts[0].trim();
234
- const status = parts[1].trim();
235
- const scenario = parts[2].trim();
236
- const id = parts[3].trim();
237
- const uri = parts[4].trim();
238
-
239
- if (!uuidRegex.test(id)) return;
240
-
241
- if (!map[id]) {
242
- map[id] = {
243
- count: 1,
244
- latest: { status, scenario, timestamp, uri }
245
- };
246
- } else {
247
- map[id].count++;
248
- if (timestamp > map[id].latest.timestamp) {
249
- map[id].latest = { status, scenario, timestamp, uri };
250
- }
251
- }
252
- });
253
-
254
- let total = 0;
255
- let notPassed = 0;
256
-
257
- Object.entries(map).forEach(([id, data]) => {
258
- total++;
259
-
260
- if (data.count > 1) {
261
- retriedTests.push({
262
- scenario: data.latest.scenario,
263
- id,
264
- count: data.count
265
- });
266
- }
267
-
268
- if (data.latest.status !== 'PASSED') {
269
- notPassed++;
270
- }
271
- });
272
-
273
- if (retriedTests.length > 0) {
274
- console.warn('\n\x1b[33mRetried test cases:');
275
- retriedTests.forEach(t => {
276
- console.warn(`- "${t.scenario}" ran ${t.count} times`);
277
- });
278
- console.log("\x1b[0m");
279
- }
280
-
281
- return {
282
- percentage: (total - notPassed) / total * 100,
283
- totalTests: total,
284
- notPassed,
285
- passed: total - notPassed,
286
- latestStatuses: Object.fromEntries(
287
- Object.entries(map).map(([id, data]) => [
288
- id,
289
- data.latest.status
290
- ])
291
- )
292
- };
293
- }
294
-
295
- function getExecutor() {
296
- // GitHub Actions
297
- if (process.env.GITHUB_RUN_ID) {
298
- return {
299
- name: "GitHub Actions",
300
- type: "github",
301
- buildName: `Workflow #${process.env.GITHUB_RUN_NUMBER}`,
302
- buildOrder: Number(process.env.GITHUB_RUN_NUMBER),
303
- buildUrl: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
304
- };
305
-
306
- // Jenkins
307
- } else if (process.env.JENKINS_HOME) {
308
- return {
309
- name: "Jenkins",
310
- type: "jenkins",
311
- buildName: process.env.JOB_NAME || "Manual Run",
312
- buildOrder: Number(process.env.BUILD_NUMBER) || 1,
313
- buildUrl: process.env.BUILD_URL || ""
314
- };
315
-
316
- // GitLab CI
317
- } else if (process.env.CI_PIPELINE_ID) {
318
- return {
319
- name: "GitLab CI",
320
- type: "gitlab",
321
- buildName: `Pipeline #${process.env.CI_PIPELINE_IID}`,
322
- buildOrder: Number(process.env.CI_PIPELINE_IID) || 1,
323
- buildUrl: process.env.CI_PIPELINE_URL || ""
324
- };
325
-
326
- // Bitbucket Pipelines
327
- } else if (process.env.BITBUCKET_BUILD_NUMBER) {
328
- return {
329
- name: "Bitbucket Pipelines",
330
- type: "bitbucket",
331
- buildName: `Build #${process.env.BITBUCKET_BUILD_NUMBER}`,
332
- buildOrder: Number(process.env.BITBUCKET_BUILD_NUMBER),
333
- buildUrl: process.env.BITBUCKET_BUILD_URL || ""
334
- };
335
-
336
- // CircleCI
337
- } else if (process.env.CIRCLE_WORKFLOW_ID) {
338
- return {
339
- name: "CircleCI",
340
- type: "circleci",
341
- buildName: `Workflow #${process.env.CIRCLE_WORKFLOW_ID}`,
342
- buildOrder: Number(process.env.CIRCLE_BUILD_NUM) || 1,
343
- buildUrl: process.env.CIRCLE_BUILD_URL || ""
344
- };
345
-
346
- // Azure Pipelines
347
- } else if (process.env.BUILD_BUILDID) {
348
- return {
349
- name: "Azure Pipelines",
350
- type: "azure",
351
- buildName: `Build #${process.env.BUILD_BUILDID}`,
352
- buildOrder: Number(process.env.BUILD_BUILDID) || 1,
353
- buildUrl: process.env.BUILD_BUILDURI || ""
354
- };
355
-
356
- // TeamCity
357
- } else if (process.env.BUILD_NUMBER && process.env.TEAMCITY_VERSION) {
358
- return {
359
- name: "TeamCity",
360
- type: "teamcity",
361
- buildName: `Build #${process.env.BUILD_NUMBER}`,
362
- buildOrder: Number(process.env.BUILD_NUMBER) || 1,
363
- buildUrl: process.env.BUILD_URL || ""
364
- };
365
-
366
- // Travis CI
367
- } else if (process.env.TRAVIS_BUILD_NUMBER) {
368
- return {
369
- name: "Travis CI",
370
- type: "travis",
371
- buildName: `Build #${process.env.TRAVIS_BUILD_NUMBER}`,
372
- buildOrder: Number(process.env.TRAVIS_BUILD_NUMBER) || 1,
373
- buildUrl: process.env.TRAVIS_BUILD_WEB_URL || ""
374
- };
375
-
376
- // Bamboo
377
- } else if (process.env.bamboo_buildNumber) {
378
- return {
379
- name: "Bamboo",
380
- type: "bamboo",
381
- buildName: `Build #${process.env.bamboo_buildNumber}`,
382
- buildOrder: Number(process.env.bamboo_buildNumber) || 1,
383
- buildUrl: process.env.bamboo_resultsUrl || ""
384
- };
385
-
386
- // Default fallback (local/manual)
387
- } else {
388
- return {
389
- name: "Local Run",
390
- type: "local",
391
- buildName: "Manual Execution",
392
- buildOrder: 1,
393
- buildUrl: ""
394
- };
395
- }
396
- }
397
-
398
-
399
- function main() {
400
- if (flags.help) return showHelp();
401
- if (flags.version) return showVersion();
402
- if (flags.create) return createProject(flags.createYes, flags.noDeps);
403
-
404
- runTests();
405
-
406
- logPomWarnings();
407
-
408
- findDuplicateTestNames();
409
-
410
- const testCoverage = testCoverageCalculation()
411
-
412
- const testPercentage = (process.env.PERCENTAGE ? Number(process.env.PERCENTAGE) : artesConfig.testPercentage || 0)
413
-
414
- if (testPercentage > 0) {
415
-
416
- const meetsThreshold = testCoverage.percentage >= testPercentage
417
-
418
- if (meetsThreshold) {
419
- console.log(
420
- `✅ Tests passed required ${testPercentage}% success rate with ${testCoverage.percentage.toFixed(2)}%!`,
421
- );
422
- process.env.EXIT_CODE = parseInt(0, 10);
423
- } else {
424
- console.log(
425
- `❌ Tests failed required ${testPercentage}% success rate with ${testCoverage.percentage.toFixed(2)}%!`,
426
- );
427
- process.env.EXIT_CODE = parseInt(1, 10);
428
- }
429
- }
430
-
431
- if (fs.existsSync(path.join(process.cwd(), "node_modules", "artes" , "@rerun.txt"))) {
432
- spawnSync("mv", ["@rerun.txt", process.cwd()], {
433
- cwd: path.join(process.cwd(), "node_modules", "artes"),
434
- stdio: "inherit",
435
- shell: true,
436
- });
437
- }
438
-
439
-
440
-
441
- if (
442
- flags.reportWithTrace ||
443
- artesConfig.reportWithTrace ||
444
- flags.report ||
445
- artesConfig.report
446
- ){
447
- const executor = getExecutor();
448
-
449
- fs.writeFileSync(
450
- path.join(process.cwd(), "node_modules", "artes",'allure-result',"executor.json"),
451
- JSON.stringify(executor, null, 2)
452
- );
453
- generateReport();
454
- }
455
-
456
-
457
- if (!( process.env.TRACE === "true"
458
- ? process.env.TRACE
459
- : artesConfig.trace || false)) {
460
- spawnSync(
461
- "npx",
462
- [
463
- "rimraf",
464
- "--no-glob",
465
- path.join(process.cwd(), "traces"),
466
- ],
467
- {
468
- cwd: process.cwd(),
469
- stdio: "inherit",
470
- shell: false,
471
- },
472
- );
473
- }
474
-
475
- cleanUp();
476
- process.exit(process.env.EXIT_CODE);
477
- }
478
-
479
- main();
1
+ #!/usr/bin/env node
2
+ const {
3
+ showHelp,
4
+ showVersion,
5
+ createProject,
6
+ runTests,
7
+ generateReport,
8
+ cleanUp,
9
+ } = require("./src/helper/executers/exporter");
10
+ const { logPomWarnings } = require("./src/helper/controller/pomCollector");
11
+ const fs = require("fs");
12
+ const path = require("path");
13
+ const { spawnSync } = require("child_process");
14
+
15
+ const artesConfigPath = path.resolve(process.cwd(), "artes.config.js");
16
+
17
+ let artesConfig = {};
18
+
19
+ if (fs.existsSync(artesConfigPath)) {
20
+ artesConfig = require(artesConfigPath);
21
+ }
22
+
23
+ const args = process.argv.slice(2);
24
+
25
+ const getArgValue = (flag) => {
26
+ const index = args.indexOf(flag);
27
+ return index >= 0 ? args[index + 1] : undefined;
28
+ };
29
+
30
+ const flags = {
31
+ help: args.includes("-h") || args.includes("--help"),
32
+ version: args.includes("-v") || args.includes("--version"),
33
+ create: args.includes("-c") || args.includes("--create"),
34
+ createYes: args.includes("-y") || args.includes("--yes"),
35
+ noDeps: args.includes("--noDeps"),
36
+ report: args.includes("-r") || args.includes("--report"),
37
+ reportSuccess: args.includes("--reportSuccess"),
38
+ trace: args.includes("-t") || args.includes("--trace"),
39
+ reportWithTrace: args.includes("-rwt") || args.includes("--reportWithTrace"),
40
+ singleFileReport: args.includes("--singleFileReport"),
41
+ zip: args.includes("--zip"),
42
+ features: args.includes("--features"),
43
+ stepDef: args.includes("--stepDef"),
44
+ tags: args.includes("--tags"),
45
+ env: args.includes("--env"),
46
+ saveVar: args.includes("--saveVar"),
47
+ headless: args.includes("--headless"),
48
+ parallel: args.includes("--parallel"),
49
+ retry: args.includes("--retry"),
50
+ rerun: args.includes("--rerun"),
51
+ dryRun: args.includes("--dryRun"),
52
+ percentage: args.includes("--percentage"),
53
+ browser: args.includes("--browser"),
54
+ offline: args.includes("--offline"),
55
+ device: args.includes("--device"),
56
+ baseURL: args.includes("--baseURL"),
57
+ maximizeScreen: args.includes("--maxScreen"),
58
+ width: args.includes("--width"),
59
+ height: args.includes("--height"),
60
+ timeout: args.includes("--timeout"),
61
+ slowMo: args.includes("--slowMo"),
62
+ };
63
+
64
+
65
+ const env = getArgValue("--env");
66
+ const vars = getArgValue("--saveVar");
67
+ const featureFiles = getArgValue("--features");
68
+ const features = flags.features && featureFiles;
69
+ const stepDef = getArgValue("--stepDef");
70
+ const tags = getArgValue("--tags");
71
+ const parallel = getArgValue("--parallel");
72
+ const retry = getArgValue("--retry");
73
+ const rerun = getArgValue("--rerun");
74
+ const percentage = getArgValue("--percentage");
75
+ const browser = getArgValue("--browser");
76
+ const device = getArgValue("--device");
77
+ const baseURL = getArgValue("--baseURL");
78
+ const width = getArgValue("--width");
79
+ const height = getArgValue("--height");
80
+ const timeout = getArgValue("--timeout");
81
+ const slowMo = getArgValue("--slowMo");
82
+
83
+
84
+ flags.env ? (process.env.ENV = env) : "";
85
+
86
+ vars ? (process.env.VARS = vars) : "";
87
+
88
+ flags.reportWithTrace ||
89
+ artesConfig.reportWithTrace ||
90
+ flags.report ||
91
+ artesConfig.report
92
+ ? (process.env.REPORT_FORMAT = JSON.stringify([
93
+ "allure-cucumberjs/reporter:./allure-results",
94
+ ]))
95
+ : "";
96
+
97
+ flags.reportSuccess ? (process.env.REPORT_SUCCESS = true) : "";
98
+
99
+ flags.tags && console.log("Running tags:", tags);
100
+ flags.tags ? (process.env.RUN_TAGS = JSON.stringify(tags)) : "";
101
+
102
+ flags.features && console.log("Running features:", features);
103
+ flags.features ? (process.env.FEATURES = features) : "";
104
+
105
+ flags.stepDef && console.log("Running step definitions:", flags.stepDef);
106
+ flags.stepDef ? (process.env.STEP_DEFINITIONS = stepDef) : "";
107
+
108
+ flags.report ? (process.env.REPORT = true) : "";
109
+
110
+ flags.trace ? (process.env.TRACE = true) : "";
111
+
112
+ flags.reportWithTrace
113
+ ? (process.env.REPORT_WITH_TRACE = true)
114
+ : (process.env.REPORT_WITH_TRACE = false);
115
+
116
+ flags.singleFileReport
117
+ ? (process.env.SINGLE_FILE_REPORT = true)
118
+ : (process.env.SINGLE_FILE_REPORT = false);
119
+
120
+ flags.zip ? (process.env.ZIP = true) : (process.env.ZIP = false);
121
+
122
+ flags.headless &&
123
+ console.log("Running mode:", flags.headless ? "headless" : "headed");
124
+ flags.headless ? (process.env.MODE = JSON.stringify(true)) : false;
125
+
126
+ flags.parallel ? (process.env.PARALLEL = parallel) : "";
127
+
128
+ flags.retry ? (process.env.RETRY = retry) : "";
129
+
130
+ flags.rerun ? (process.env.RERUN = rerun) : "";
131
+
132
+ flags.dryRun ? (process.env.DRYRUN = flags.dryRun) : "";
133
+
134
+ flags.percentage ? (process.env.PERCENTAGE = percentage) : "";
135
+
136
+ flags.browser && console.log("Running browser:", browser);
137
+ flags.browser ? (process.env.BROWSER = JSON.stringify(browser)) : "";
138
+
139
+ flags.browser && console.log("Running mode:", flags.offline && "Offline");
140
+ flags.offline ? (process.env.OFFLINE = true) : "";
141
+
142
+ flags.device && console.log("Running device:", device);
143
+ flags.device ? (process.env.DEVICE = JSON.stringify(device)) : "";
144
+
145
+ flags.baseURL ? (process.env.BASE_URL = JSON.stringify(baseURL)) : "";
146
+
147
+ flags.maximizeScreen
148
+ ? (process.env.MAXIMIZE_SCREEN = flags.maximizeScreen)
149
+ : "";
150
+
151
+ flags.width && console.log("Running width:", width);
152
+ flags.width ? (process.env.WIDTH = width) : "";
153
+
154
+ flags.height && console.log("Running height:", height);
155
+ flags.height ? (process.env.HEIGHT = height) : "";
156
+
157
+ flags.timeout ? (process.env.TIMEOUT = timeout) : "";
158
+
159
+ flags.slowMo ? (process.env.SLOWMO = slowMo) : "";
160
+
161
+
162
+ function findDuplicateTestNames() {
163
+ const testStatusFile = path.join(process.cwd(), "node_modules", "artes" , "test-status", 'test-status.txt');
164
+
165
+ if (!fs.existsSync(testStatusFile)) {
166
+ console.error('test-status.txt not found');
167
+ return;
168
+ }
169
+
170
+ const content = fs.readFileSync(testStatusFile, 'utf8');
171
+ const lines = content.split('\n').filter(line => line.trim());
172
+
173
+ const testNameToFiles = {};
174
+
175
+ lines.forEach(line => {
176
+ const parts = line.split(' | ');
177
+ if (parts.length < 5) return;
178
+
179
+ const testName = parts[2].trim();
180
+ const filePath = parts[4].trim();
181
+
182
+ if (!testNameToFiles[testName]) {
183
+ testNameToFiles[testName] = new Set();
184
+ }
185
+
186
+ testNameToFiles[testName].add(filePath);
187
+ });
188
+
189
+ const duplicates = {};
190
+
191
+ Object.entries(testNameToFiles).forEach(([testName, files]) => {
192
+ if (files.size > 1) {
193
+ duplicates[testName] = Array.from(files);
194
+ }
195
+ });
196
+
197
+ if (Object.keys(duplicates).length > 0) {
198
+ console.warn('\n\x1b[33m[WARNING] Duplicate scenarios names found: This will effect your reporting');
199
+ Object.entries(duplicates).forEach(([testName, files]) => {
200
+ console.log(`\x1b[33m"${testName}" exists in:`);
201
+ files.forEach(file => {
202
+ console.log(` - ${file}`);
203
+ });
204
+ console.log('');
205
+ });
206
+ console.log("\x1b[0m");
207
+ }
208
+
209
+ return duplicates;
210
+ }
211
+
212
+
213
+ function testCoverageCalculation() {
214
+
215
+ const testStatusFile = path.join(process.cwd(), "node_modules", "artes" , "test-status", 'test-status.txt');
216
+
217
+ if (!fs.existsSync(testStatusFile)) {
218
+ console.error('test-status.txt not found');
219
+ return null;
220
+ }
221
+
222
+ const content = fs.readFileSync(testStatusFile, 'utf8');
223
+ const lines = content.split('\n').filter(line => line.trim());
224
+
225
+ const map = {};
226
+ const retriedTests = [];
227
+ const uuidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
228
+
229
+ lines.forEach(line => {
230
+ const parts = line.split(' | ');
231
+ if (parts.length < 5) return;
232
+
233
+ const timestamp = parts[0].trim();
234
+ const status = parts[1].trim();
235
+ const scenario = parts[2].trim();
236
+ const id = parts[3].trim();
237
+ const uri = parts[4].trim();
238
+
239
+ if (!uuidRegex.test(id)) return;
240
+
241
+ if (!map[id]) {
242
+ map[id] = {
243
+ count: 1,
244
+ latest: { status, scenario, timestamp, uri }
245
+ };
246
+ } else {
247
+ map[id].count++;
248
+ if (timestamp > map[id].latest.timestamp) {
249
+ map[id].latest = { status, scenario, timestamp, uri };
250
+ }
251
+ }
252
+ });
253
+
254
+ let total = 0;
255
+ let notPassed = 0;
256
+
257
+ Object.entries(map).forEach(([id, data]) => {
258
+ total++;
259
+
260
+ if (data.count > 1) {
261
+ retriedTests.push({
262
+ scenario: data.latest.scenario,
263
+ id,
264
+ count: data.count
265
+ });
266
+ }
267
+
268
+ if (data.latest.status !== 'PASSED') {
269
+ notPassed++;
270
+ }
271
+ });
272
+
273
+ if (retriedTests.length > 0) {
274
+ console.warn('\n\x1b[33mRetried test cases:');
275
+ retriedTests.forEach(t => {
276
+ console.warn(`- "${t.scenario}" ran ${t.count} times`);
277
+ });
278
+ console.log("\x1b[0m");
279
+ }
280
+
281
+ return {
282
+ percentage: (total - notPassed) / total * 100,
283
+ totalTests: total,
284
+ notPassed,
285
+ passed: total - notPassed,
286
+ latestStatuses: Object.fromEntries(
287
+ Object.entries(map).map(([id, data]) => [
288
+ id,
289
+ data.latest.status
290
+ ])
291
+ )
292
+ };
293
+ }
294
+
295
+ function getExecutor() {
296
+ // GitHub Actions
297
+ if (process.env.GITHUB_RUN_ID) {
298
+ return {
299
+ name: "GitHub Actions",
300
+ type: "github",
301
+ buildName: `Workflow #${process.env.GITHUB_RUN_NUMBER}`,
302
+ buildOrder: Number(process.env.GITHUB_RUN_NUMBER),
303
+ buildUrl: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
304
+ };
305
+
306
+ // Jenkins
307
+ } else if (process.env.JENKINS_HOME) {
308
+ return {
309
+ name: "Jenkins",
310
+ type: "jenkins",
311
+ buildName: process.env.JOB_NAME || "Manual Run",
312
+ buildOrder: Number(process.env.BUILD_NUMBER) || 1,
313
+ buildUrl: process.env.BUILD_URL || ""
314
+ };
315
+
316
+ // GitLab CI
317
+ } else if (process.env.CI_PIPELINE_ID) {
318
+ return {
319
+ name: "GitLab CI",
320
+ type: "gitlab",
321
+ buildName: `Pipeline #${process.env.CI_PIPELINE_IID}`,
322
+ buildOrder: Number(process.env.CI_PIPELINE_IID) || 1,
323
+ buildUrl: process.env.CI_PIPELINE_URL || ""
324
+ };
325
+
326
+ // Bitbucket Pipelines
327
+ } else if (process.env.BITBUCKET_BUILD_NUMBER) {
328
+ return {
329
+ name: "Bitbucket Pipelines",
330
+ type: "bitbucket",
331
+ buildName: `Build #${process.env.BITBUCKET_BUILD_NUMBER}`,
332
+ buildOrder: Number(process.env.BITBUCKET_BUILD_NUMBER),
333
+ buildUrl: process.env.BITBUCKET_BUILD_URL || ""
334
+ };
335
+
336
+ // CircleCI
337
+ } else if (process.env.CIRCLE_WORKFLOW_ID) {
338
+ return {
339
+ name: "CircleCI",
340
+ type: "circleci",
341
+ buildName: `Workflow #${process.env.CIRCLE_WORKFLOW_ID}`,
342
+ buildOrder: Number(process.env.CIRCLE_BUILD_NUM) || 1,
343
+ buildUrl: process.env.CIRCLE_BUILD_URL || ""
344
+ };
345
+
346
+ // Azure Pipelines
347
+ } else if (process.env.BUILD_BUILDID) {
348
+ return {
349
+ name: "Azure Pipelines",
350
+ type: "azure",
351
+ buildName: `Build #${process.env.BUILD_BUILDID}`,
352
+ buildOrder: Number(process.env.BUILD_BUILDID) || 1,
353
+ buildUrl: process.env.BUILD_BUILDURI || ""
354
+ };
355
+
356
+ // TeamCity
357
+ } else if (process.env.BUILD_NUMBER && process.env.TEAMCITY_VERSION) {
358
+ return {
359
+ name: "TeamCity",
360
+ type: "teamcity",
361
+ buildName: `Build #${process.env.BUILD_NUMBER}`,
362
+ buildOrder: Number(process.env.BUILD_NUMBER) || 1,
363
+ buildUrl: process.env.BUILD_URL || ""
364
+ };
365
+
366
+ // Travis CI
367
+ } else if (process.env.TRAVIS_BUILD_NUMBER) {
368
+ return {
369
+ name: "Travis CI",
370
+ type: "travis",
371
+ buildName: `Build #${process.env.TRAVIS_BUILD_NUMBER}`,
372
+ buildOrder: Number(process.env.TRAVIS_BUILD_NUMBER) || 1,
373
+ buildUrl: process.env.TRAVIS_BUILD_WEB_URL || ""
374
+ };
375
+
376
+ // Bamboo
377
+ } else if (process.env.bamboo_buildNumber) {
378
+ return {
379
+ name: "Bamboo",
380
+ type: "bamboo",
381
+ buildName: `Build #${process.env.bamboo_buildNumber}`,
382
+ buildOrder: Number(process.env.bamboo_buildNumber) || 1,
383
+ buildUrl: process.env.bamboo_resultsUrl || ""
384
+ };
385
+
386
+ // Default fallback (local/manual)
387
+ } else {
388
+ return {
389
+ name: "Local Run",
390
+ type: "local",
391
+ buildName: "Manual Execution",
392
+ buildOrder: 1,
393
+ buildUrl: ""
394
+ };
395
+ }
396
+ }
397
+
398
+
399
+ function main() {
400
+ if (flags.help) return showHelp();
401
+ if (flags.version) return showVersion();
402
+ if (flags.create) return createProject(flags.createYes, flags.noDeps);
403
+
404
+ runTests();
405
+
406
+ logPomWarnings();
407
+
408
+ findDuplicateTestNames();
409
+
410
+ const testCoverage = testCoverageCalculation()
411
+
412
+ const testPercentage = (process.env.PERCENTAGE ? Number(process.env.PERCENTAGE) : artesConfig.testPercentage || 0)
413
+
414
+ if (testPercentage > 0) {
415
+
416
+ const meetsThreshold = testCoverage.percentage >= testPercentage
417
+
418
+ if (meetsThreshold) {
419
+ console.log(
420
+ `✅ Tests passed required ${testPercentage}% success rate with ${testCoverage.percentage.toFixed(2)}%!`,
421
+ );
422
+ process.env.EXIT_CODE = parseInt(0, 10);
423
+ } else {
424
+ console.log(
425
+ `❌ Tests failed required ${testPercentage}% success rate with ${testCoverage.percentage.toFixed(2)}%!`,
426
+ );
427
+ process.env.EXIT_CODE = parseInt(1, 10);
428
+ }
429
+ }
430
+
431
+ if (fs.existsSync(path.join(process.cwd(), "node_modules", "artes" , "@rerun.txt"))) {
432
+ spawnSync("mv", ["@rerun.txt", process.cwd()], {
433
+ cwd: path.join(process.cwd(), "node_modules", "artes"),
434
+ stdio: "inherit",
435
+ shell: true,
436
+ });
437
+ }
438
+
439
+
440
+
441
+ if (
442
+ flags.reportWithTrace ||
443
+ artesConfig.reportWithTrace ||
444
+ flags.report ||
445
+ artesConfig.report
446
+ ){
447
+ const executor = getExecutor();
448
+
449
+ fs.writeFileSync(
450
+ path.join(process.cwd(), "node_modules", "artes",'allure-result',"executor.json"),
451
+ JSON.stringify(executor, null, 2)
452
+ );
453
+ generateReport();
454
+ }
455
+
456
+
457
+ if (!( process.env.TRACE === "true"
458
+ ? process.env.TRACE
459
+ : artesConfig.trace || false)) {
460
+ spawnSync(
461
+ "npx",
462
+ [
463
+ "rimraf",
464
+ "--no-glob",
465
+ path.join(process.cwd(), "traces"),
466
+ ],
467
+ {
468
+ cwd: process.cwd(),
469
+ stdio: "inherit",
470
+ shell: false,
471
+ },
472
+ );
473
+ }
474
+
475
+ cleanUp();
476
+ process.exit(process.env.EXIT_CODE);
477
+ }
478
+
479
+ main();