artes 1.4.5 → 1.4.7
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/cucumber.config.js +2 -2
- package/executer.js +268 -89
- package/package.json +1 -2
- package/src/helper/executers/reportGenerator.js +1 -1
- package/src/helper/imports/commons.js +1 -1
- package/src/hooks/hooks.js +5 -27
- package/src/stepDefinitions/keyboardActions.steps.js +3 -0
- package/status-formatter.js +138 -0
package/cucumber.config.js
CHANGED
|
@@ -14,7 +14,7 @@ try {
|
|
|
14
14
|
console.log("Proceeding with default config.");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const defaultFormats = ["rerun:@rerun.txt",
|
|
17
|
+
const defaultFormats = ["rerun:@rerun.txt", "progress-bar", './status-formatter.js:null'];
|
|
18
18
|
|
|
19
19
|
const userFormatsFromEnv = process.env.REPORT_FORMAT
|
|
20
20
|
? JSON.parse(process.env.REPORT_FORMAT)
|
|
@@ -55,7 +55,7 @@ function loadVariables(cliVariables, artesConfigVariables) {
|
|
|
55
55
|
|
|
56
56
|
if (cliVariables) {
|
|
57
57
|
try {
|
|
58
|
-
cliVariables = JSON.parse(
|
|
58
|
+
cliVariables = JSON.parse(cliVariables);
|
|
59
59
|
} catch (err) {
|
|
60
60
|
console.error("Invalid JSON in process.env.VARS:", process.env.VARS);
|
|
61
61
|
envVars = {};
|
package/executer.js
CHANGED
|
@@ -22,6 +22,11 @@ if (fs.existsSync(artesConfigPath)) {
|
|
|
22
22
|
|
|
23
23
|
const args = process.argv.slice(2);
|
|
24
24
|
|
|
25
|
+
const getArgValue = (flag) => {
|
|
26
|
+
const index = args.indexOf(flag);
|
|
27
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
25
30
|
const flags = {
|
|
26
31
|
help: args.includes("-h") || args.includes("--help"),
|
|
27
32
|
version: args.includes("-v") || args.includes("--version"),
|
|
@@ -56,23 +61,25 @@ const flags = {
|
|
|
56
61
|
slowMo: args.includes("--slowMo"),
|
|
57
62
|
};
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const
|
|
64
|
+
|
|
65
|
+
const env = getArgValue("--env");
|
|
66
|
+
const vars = getArgValue("--saveVar");
|
|
67
|
+
const featureFiles = getArgValue("--features");
|
|
62
68
|
const features = flags.features && featureFiles;
|
|
63
|
-
const stepDef =
|
|
64
|
-
const tags =
|
|
65
|
-
const parallel =
|
|
66
|
-
const retry =
|
|
67
|
-
const rerun =
|
|
68
|
-
const percentage =
|
|
69
|
-
const browser =
|
|
70
|
-
const device =
|
|
71
|
-
const baseURL =
|
|
72
|
-
const width =
|
|
73
|
-
const height =
|
|
74
|
-
const timeout =
|
|
75
|
-
const slowMo =
|
|
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
|
+
|
|
76
83
|
|
|
77
84
|
flags.env ? (process.env.ENV = env) : "";
|
|
78
85
|
|
|
@@ -152,82 +159,243 @@ flags.timeout ? (process.env.TIMEOUT = timeout) : "";
|
|
|
152
159
|
flags.slowMo ? (process.env.SLOWMO = slowMo) : "";
|
|
153
160
|
|
|
154
161
|
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
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());
|
|
158
172
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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');
|
|
162
216
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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') {
|
|
204
269
|
notPassed++;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
+
};
|
|
228
395
|
}
|
|
229
396
|
}
|
|
230
397
|
|
|
398
|
+
|
|
231
399
|
function main() {
|
|
232
400
|
if (flags.help) return showHelp();
|
|
233
401
|
if (flags.version) return showVersion();
|
|
@@ -237,7 +405,9 @@ function main() {
|
|
|
237
405
|
|
|
238
406
|
logPomWarnings();
|
|
239
407
|
|
|
240
|
-
|
|
408
|
+
findDuplicateTestNames();
|
|
409
|
+
|
|
410
|
+
const testCoverage = testCoverageCalculation()
|
|
241
411
|
|
|
242
412
|
const testPercentage = (process.env.PERCENTAGE ? Number(process.env.PERCENTAGE) : artesConfig.testPercentage || 0)
|
|
243
413
|
|
|
@@ -266,13 +436,22 @@ if (fs.existsSync(path.join(process.cwd(), "node_modules", "artes" , "@rerun.txt
|
|
|
266
436
|
});
|
|
267
437
|
}
|
|
268
438
|
|
|
439
|
+
|
|
440
|
+
|
|
269
441
|
if (
|
|
270
442
|
flags.reportWithTrace ||
|
|
271
443
|
artesConfig.reportWithTrace ||
|
|
272
444
|
flags.report ||
|
|
273
445
|
artesConfig.report
|
|
274
|
-
)
|
|
275
|
-
|
|
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
|
+
}
|
|
276
455
|
|
|
277
456
|
|
|
278
457
|
if (!( process.env.TRACE === "true"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "artes",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.7",
|
|
4
4
|
"description": "The simplest way to automate UI and API tests using Cucumber-style steps.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"dayjs": "1.11.13",
|
|
31
31
|
"deasync": "^0.1.31",
|
|
32
32
|
"playwright": "^1.58.2",
|
|
33
|
-
"proper-lockfile": "^4.1.2",
|
|
34
33
|
"rimraf": "6.0.1"
|
|
35
34
|
},
|
|
36
35
|
"repository": {
|
|
@@ -30,7 +30,7 @@ function generateReport() {
|
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
if (fs.existsSync(moduleConfig.reportPath) && process.env.ZIP === "true") {
|
|
33
|
-
console.log(`🗜️ Zipping report folder
|
|
33
|
+
console.log(`🗜️ Zipping report folder...`);
|
|
34
34
|
|
|
35
35
|
const zipPath = path.join(
|
|
36
36
|
path.dirname(moduleConfig.reportPath),
|
|
@@ -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
|
|
38
|
+
"allure-result allure-results test-results @rerun.txt test-status null pomDuplicateWarnings.json",
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
module.exports = {
|
package/src/hooks/hooks.js
CHANGED
|
@@ -37,33 +37,10 @@ async function attachResponse(attachFn) {
|
|
|
37
37
|
? `${key}:\n${JSON.stringify(value, null, 2)}`
|
|
38
38
|
: `${key}:\n${value}`;
|
|
39
39
|
|
|
40
|
-
await attachFn(key, text, "
|
|
40
|
+
await attachFn(key, text, "application/json");
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function saveTestStatus(result, pickle) {
|
|
45
|
-
|
|
46
|
-
fs.mkdirSync(statusDir, { recursive: true });
|
|
47
|
-
|
|
48
|
-
const now = new Date();
|
|
49
|
-
const YYYY = now.getFullYear();
|
|
50
|
-
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
|
51
|
-
const DD = String(now.getDate()).padStart(2, '0');
|
|
52
|
-
const hh = String(now.getHours()).padStart(2, '0');
|
|
53
|
-
const mm = String(now.getMinutes()).padStart(2, '0');
|
|
54
|
-
const ss = String(now.getSeconds()).padStart(2, '0');
|
|
55
|
-
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
|
56
|
-
|
|
57
|
-
const timestamp = `${YYYY}${MM}${DD}-${hh}${mm}${ss}-${ms}`;
|
|
58
|
-
|
|
59
|
-
const fileName = `${result.status}-${pickle.name}-${pickle.id}-${timestamp}.txt`;
|
|
60
|
-
|
|
61
|
-
const filePath = path.join(statusDir, fileName);
|
|
62
|
-
|
|
63
|
-
fs.writeFileSync(filePath, "");
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
44
|
const projectHooksPath = path.resolve(
|
|
68
45
|
moduleConfig.projectPath,
|
|
69
46
|
"tests/steps/hooks.js",
|
|
@@ -169,6 +146,10 @@ After(async function ({result, pickle}) {
|
|
|
169
146
|
await projectHooks.After();
|
|
170
147
|
}
|
|
171
148
|
|
|
149
|
+
await attachResponse(allure.attachment);
|
|
150
|
+
context.response = await {};
|
|
151
|
+
allure.attachment('Variables', JSON.stringify(context.vars, null, 2), 'application/json')
|
|
152
|
+
|
|
172
153
|
const shouldReport =
|
|
173
154
|
cucumberConfig.default.successReport || result?.status !== Status.PASSED;
|
|
174
155
|
|
|
@@ -178,7 +159,6 @@ After(async function ({result, pickle}) {
|
|
|
178
159
|
await allure.attachment("Screenshot", screenshotBuffer, "image/png");
|
|
179
160
|
}
|
|
180
161
|
|
|
181
|
-
if(cucumberConfig.default.testPercentage>0) saveTestStatus(result, pickle);
|
|
182
162
|
|
|
183
163
|
if (cucumberConfig.default.reportWithTrace || cucumberConfig.default.trace) {
|
|
184
164
|
var tracePath = path.join(
|
|
@@ -202,8 +182,6 @@ After(async function ({result, pickle}) {
|
|
|
202
182
|
}
|
|
203
183
|
}
|
|
204
184
|
|
|
205
|
-
await attachResponse(allure.attachment);
|
|
206
|
-
context.response = await {};
|
|
207
185
|
|
|
208
186
|
await context.page?.close();
|
|
209
187
|
await context.browserContext?.close();
|
|
@@ -24,6 +24,7 @@ When(
|
|
|
24
24
|
"User types {string} by hand in {int} th of {string}",
|
|
25
25
|
async (text, order, elements) => {
|
|
26
26
|
const nthElement = await frame.nth(elements, order);
|
|
27
|
+
text = resolveVariable(text)
|
|
27
28
|
await nthElement.pressSequentially(text);
|
|
28
29
|
},
|
|
29
30
|
);
|
|
@@ -55,6 +56,7 @@ When(
|
|
|
55
56
|
"User types {string} in {int} th of {string}",
|
|
56
57
|
async (text, order, elements) => {
|
|
57
58
|
const nthElement = await frame.nth(elements, order);
|
|
59
|
+
text = resolveVariable(text)
|
|
58
60
|
await nthElement.fill(text);
|
|
59
61
|
},
|
|
60
62
|
);
|
|
@@ -65,6 +67,7 @@ When(
|
|
|
65
67
|
const elementCount = await frame.count(selectors);
|
|
66
68
|
value = await resolveVariable(value);
|
|
67
69
|
for (let i = 0; i < elementCount; i++) {
|
|
70
|
+
value = resolveVariable(value)
|
|
68
71
|
await frame.nth(selectors, i).fill(value);
|
|
69
72
|
}
|
|
70
73
|
},
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const { Formatter } = require('@cucumber/cucumber');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
class StatusFormatter extends Formatter {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(options);
|
|
8
|
+
|
|
9
|
+
const outputDir = './test-status';
|
|
10
|
+
if (!fs.existsSync(outputDir)) {
|
|
11
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const workerId = process.env.CUCUMBER_WORKER_ID || '0';
|
|
15
|
+
this.workerId = workerId;
|
|
16
|
+
this.outputFile = path.join(outputDir, `test-results-${workerId}.txt`);
|
|
17
|
+
this.outputDir = outputDir;
|
|
18
|
+
|
|
19
|
+
fs.writeFileSync(this.outputFile, '', 'utf8');
|
|
20
|
+
|
|
21
|
+
this.pickles = new Map();
|
|
22
|
+
this.testCases = new Map();
|
|
23
|
+
this.testCaseStartedIdToAttempt = new Map();
|
|
24
|
+
|
|
25
|
+
const { eventBroadcaster } = options;
|
|
26
|
+
|
|
27
|
+
eventBroadcaster.on('envelope', (envelope) => {
|
|
28
|
+
if (envelope.pickle) {
|
|
29
|
+
this.pickles.set(envelope.pickle.id, envelope.pickle);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (envelope.testCase) {
|
|
33
|
+
this.testCases.set(envelope.testCase.id, envelope.testCase);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (envelope.testCaseStarted) {
|
|
37
|
+
this.testCaseStartedIdToAttempt.set(
|
|
38
|
+
envelope.testCaseStarted.id,
|
|
39
|
+
envelope.testCaseStarted.testCaseId
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (envelope.testCaseFinished) {
|
|
44
|
+
this.handleTestCaseFinished(envelope.testCaseFinished);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (envelope.testRunFinished) {
|
|
48
|
+
this.handleTestRunFinished();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getFormattedTimestamp() {
|
|
54
|
+
const now = new Date();
|
|
55
|
+
const YYYY = now.getFullYear();
|
|
56
|
+
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
|
57
|
+
const DD = String(now.getDate()).padStart(2, '0');
|
|
58
|
+
const hh = String(now.getHours()).padStart(2, '0');
|
|
59
|
+
const mm = String(now.getMinutes()).padStart(2, '0');
|
|
60
|
+
const ss = String(now.getSeconds()).padStart(2, '0');
|
|
61
|
+
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
|
62
|
+
|
|
63
|
+
return `${YYYY}${MM}${DD}-${hh}${mm}${ss}-${ms}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
handleTestCaseFinished(testCaseFinished) {
|
|
67
|
+
try {
|
|
68
|
+
const timestamp = this.getFormattedTimestamp();
|
|
69
|
+
|
|
70
|
+
const testCaseId = this.testCaseStartedIdToAttempt.get(
|
|
71
|
+
testCaseFinished.testCaseStartedId
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!testCaseId) return;
|
|
75
|
+
|
|
76
|
+
const testCase = this.testCases.get(testCaseId);
|
|
77
|
+
if (!testCase) return;
|
|
78
|
+
|
|
79
|
+
const pickle = this.pickles.get(testCase.pickleId);
|
|
80
|
+
if (!pickle) return;
|
|
81
|
+
|
|
82
|
+
const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(
|
|
83
|
+
testCaseFinished.testCaseStartedId
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const status = testCaseAttempt?.worstTestStepResult?.status || 'UNKNOWN';
|
|
87
|
+
|
|
88
|
+
const info = {
|
|
89
|
+
name: pickle.name,
|
|
90
|
+
id: pickle.id,
|
|
91
|
+
uri: pickle.uri
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const line = `${timestamp} | ${status.toUpperCase()} | ${info.name} | ${info.id} | ${info.uri}\n`;
|
|
95
|
+
|
|
96
|
+
fs.appendFileSync(this.outputFile, line, 'utf8');
|
|
97
|
+
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Error in handleTestCaseFinished:', error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
handleTestRunFinished() {
|
|
104
|
+
if (this.workerId !== '0') {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
this.mergeResults();
|
|
110
|
+
}, 1000);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
mergeResults() {
|
|
114
|
+
try {
|
|
115
|
+
const testStatusFile = path.join(this.outputDir, 'test-status.txt');
|
|
116
|
+
|
|
117
|
+
const files = fs.readdirSync(this.outputDir)
|
|
118
|
+
.filter(f => f.startsWith('test-results-') && f.endsWith('.txt'))
|
|
119
|
+
.sort();
|
|
120
|
+
|
|
121
|
+
if (files.length === 0) {
|
|
122
|
+
console.log('No result files found to merge');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const combined = files
|
|
127
|
+
.map(f => fs.readFileSync(path.join(this.outputDir, f), 'utf8'))
|
|
128
|
+
.join('');
|
|
129
|
+
|
|
130
|
+
fs.writeFileSync(testStatusFile, combined, 'utf8');
|
|
131
|
+
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Error merging results:', error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = StatusFormatter;
|