@testomatio/reporter 0.8.0-beta.2 → 0.8.0-beta.20
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 +35 -0
- package/lib/adapter/codecept.js +0 -5
- package/lib/adapter/cucumber/current.js +1 -7
- package/lib/adapter/jasmine.js +1 -8
- package/lib/adapter/jest.js +1 -7
- package/lib/adapter/mocha.js +5 -10
- package/lib/adapter/playwright.js +1 -7
- package/lib/adapter/webdriver.js +1 -4
- package/lib/client.js +15 -8
- package/lib/constants.js +9 -0
- package/lib/pipe/csv.js +111 -0
- package/lib/pipe/github.js +32 -14
- package/lib/pipe/gitlab.js +192 -0
- package/lib/pipe/index.js +11 -3
- package/lib/pipe/testomatio.js +12 -3
- package/lib/util.js +14 -1
- package/lib/xmlReader.js +4 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -208,6 +208,24 @@ Run the following command from you project folder:
|
|
|
208
208
|
TESTOMATIO={API_KEY} npx start-test-run -c 'npx wdio wdio.conf.js'
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
+
## Pipes
|
|
212
|
+
Pipes allow you to get report inside different systems (e.g. Pull request comment, database etc)
|
|
213
|
+
For now next pipes available:
|
|
214
|
+
|
|
215
|
+
### Github
|
|
216
|
+
This pipe adds comment with run report to GitHub Pull Request.
|
|
217
|
+
|
|
218
|
+
To use it:
|
|
219
|
+
1. run your tests using github actions in Pull Request
|
|
220
|
+
2. pass `GH_PAT` (GitHub Personal Access Token) as environment variable.
|
|
221
|
+
|
|
222
|
+
### GitLab
|
|
223
|
+
This pipe adds comment with run report to GitLab Merge Request.
|
|
224
|
+
|
|
225
|
+
To use it:
|
|
226
|
+
1. run your tests in Merge Request (pipeline trigger should be `merge_request`)
|
|
227
|
+
2. pass `GITLAB_PAT` (GitLab Personal Access Token) as environment variable.
|
|
228
|
+
|
|
211
229
|
## JUnit Reports
|
|
212
230
|
|
|
213
231
|
> **Notice** JUnit reports are supported since 0.6.0
|
|
@@ -408,6 +426,23 @@ Add environments to run by providing `TESTOMATIO_ENV` as comma seperated values:
|
|
|
408
426
|
TESTOMATIO={API_KEY} TESTOMATIO_ENV="Windows, Chrome" <actual run command>
|
|
409
427
|
```
|
|
410
428
|
|
|
429
|
+
### Save test results to .csv file
|
|
430
|
+
Add an env to run by specifying the `TESTOMATIO_CSV_FILENAME` variable.
|
|
431
|
+
|
|
432
|
+
1) using default report name:
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
TESTOMATIO={API_KEY} TESTOMATIO_CSV_FILENAME="report.csv" <actual run command>
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
2) using unique report name:
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
TESTOMATIO={API_KEY} TESTOMATIO_CSV_FILENAME="test.csv" <actual run command>
|
|
442
|
+
```
|
|
443
|
+
_It's create a new /export folder with csv files_
|
|
444
|
+
|
|
445
|
+
|
|
411
446
|
### Attaching Test Artifacts
|
|
412
447
|
|
|
413
448
|
To save a test artifacts (screenshots and videos) of a failed test use S3 storage.
|
package/lib/adapter/codecept.js
CHANGED
|
@@ -35,11 +35,6 @@ function CodeceptReporter(config) {
|
|
|
35
35
|
const testTimeMap = {};
|
|
36
36
|
const { apiKey } = config;
|
|
37
37
|
|
|
38
|
-
if (!apiKey) {
|
|
39
|
-
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
38
|
const getDuration = test => {
|
|
44
39
|
if (testTimeMap[test.id]) {
|
|
45
40
|
return Date.now() - testTimeMap[test.id];
|
|
@@ -18,13 +18,7 @@ class CucumberReporter extends Formatter {
|
|
|
18
18
|
this.failures = [];
|
|
19
19
|
this.cases = [];
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
if (!apiKey || apiKey === '') {
|
|
23
|
-
console.log(chalk.red('TESTOMATIO key is empty, ignoring reports'));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
this.client = new TestomatClient({ apiKey });
|
|
21
|
+
this.client = new TestomatClient();
|
|
28
22
|
this.status = TRConstants.PASSED;
|
|
29
23
|
|
|
30
24
|
}
|
package/lib/adapter/jasmine.js
CHANGED
|
@@ -5,14 +5,7 @@ const { PASSED, FAILED } = require('../constants');
|
|
|
5
5
|
class JasmineReporter {
|
|
6
6
|
constructor(options) {
|
|
7
7
|
this.testTimeMap = {};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (!apiKey) {
|
|
11
|
-
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
this.client = new TestomatClient({ apiKey });
|
|
8
|
+
this.client = new TestomatClient({ apiKey: options?.apiKey });
|
|
16
9
|
this.client.createRun();
|
|
17
10
|
}
|
|
18
11
|
|
package/lib/adapter/jest.js
CHANGED
|
@@ -6,14 +6,8 @@ class JestReporter {
|
|
|
6
6
|
constructor(globalConfig, options) {
|
|
7
7
|
this._globalConfig = globalConfig;
|
|
8
8
|
this._options = options;
|
|
9
|
-
const { apiKey } = options;
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
this.client = new TestomatClient({ apiKey });
|
|
10
|
+
this.client = new TestomatClient({ apiKey: options?.apiKey });
|
|
17
11
|
this.client.createRun();
|
|
18
12
|
}
|
|
19
13
|
|
package/lib/adapter/mocha.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
2
|
const Mocha = require('mocha');
|
|
3
|
+
const chalk = require('chalk');
|
|
3
4
|
const TestomatClient = require('../client');
|
|
4
5
|
const TRConstants = require('../constants');
|
|
5
6
|
const { parseTest } = require('../util');
|
|
@@ -11,13 +12,7 @@ function MochaReporter(runner, opts) {
|
|
|
11
12
|
let passes = 0;
|
|
12
13
|
let failures = 0;
|
|
13
14
|
|
|
14
|
-
const { apiKey
|
|
15
|
-
|
|
16
|
-
if (!apiKey) {
|
|
17
|
-
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const client = new TestomatClient({ apiKey });
|
|
15
|
+
const client = new TestomatClient({ apiKey: opts?.reporterOptions?.apiKey });
|
|
21
16
|
|
|
22
17
|
runner.on(EVENT_RUN_BEGIN, () => {
|
|
23
18
|
client.createRun();
|
|
@@ -25,7 +20,7 @@ function MochaReporter(runner, opts) {
|
|
|
25
20
|
|
|
26
21
|
runner.on(EVENT_TEST_PASS, test => {
|
|
27
22
|
passes += 1;
|
|
28
|
-
console.log('
|
|
23
|
+
console.log(chalk.bold.green('✔'), test.fullTitle());
|
|
29
24
|
const testId = parseTest(test.title);
|
|
30
25
|
client.addTestRun(testId, TRConstants.PASSED, {
|
|
31
26
|
title: test.title,
|
|
@@ -35,7 +30,7 @@ function MochaReporter(runner, opts) {
|
|
|
35
30
|
|
|
36
31
|
runner.on(EVENT_TEST_FAIL, (test, err) => {
|
|
37
32
|
failures += 1;
|
|
38
|
-
console.log('
|
|
33
|
+
console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
|
|
39
34
|
const testId = parseTest(test.title);
|
|
40
35
|
client.addTestRun(testId, TRConstants.FAILED, {
|
|
41
36
|
error: err,
|
|
@@ -45,8 +40,8 @@ function MochaReporter(runner, opts) {
|
|
|
45
40
|
});
|
|
46
41
|
|
|
47
42
|
runner.on(EVENT_RUN_END, () => {
|
|
48
|
-
console.log('end: %d/%d', passes, passes + failures);
|
|
49
43
|
const status = failures === 0 ? TRConstants.PASSED : TRConstants.FAILED;
|
|
44
|
+
console.log(chalk.bold(status), `${passes} passed, ${failures} failed`);
|
|
50
45
|
client.updateRunStatus(status);
|
|
51
46
|
});
|
|
52
47
|
}
|
|
@@ -10,13 +10,7 @@ const { parseTest } = require('../util');
|
|
|
10
10
|
|
|
11
11
|
class TestomatioReporter {
|
|
12
12
|
constructor(config = {}) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (!apiKey) {
|
|
16
|
-
console.log('API Key is not provided. Testomat.io report is disabled');
|
|
17
|
-
} else {
|
|
18
|
-
this.client = new TestomatioClient({ apiKey });
|
|
19
|
-
}
|
|
13
|
+
this.client = new TestomatioClient({ apiKey: config?.apiKey });
|
|
20
14
|
|
|
21
15
|
this.videos = [];
|
|
22
16
|
}
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -7,10 +7,7 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
7
7
|
constructor(options) {
|
|
8
8
|
super(options);
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
if (!apiKey) return;
|
|
12
|
-
|
|
13
|
-
this.client = new TestomatClient({ apiKey });
|
|
10
|
+
this.client = new TestomatClient({ apiKey: options?.apiKey });
|
|
14
11
|
options = Object.assign(options, { stdout: true });
|
|
15
12
|
|
|
16
13
|
this._addTestPromises = [];
|
package/lib/client.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const axios = require('axios');
|
|
2
1
|
const createCallsiteRecord = require('callsite-record');
|
|
3
2
|
const { sep, join } = require('path');
|
|
4
3
|
const fs = require('fs');
|
|
@@ -6,6 +5,7 @@ const chalk = require('chalk');
|
|
|
6
5
|
const upload = require('./fileUploader');
|
|
7
6
|
const { PASSED, FAILED, FINISHED, APP_PREFIX } = require('./constants');
|
|
8
7
|
const pipesFactory = require('./pipe');
|
|
8
|
+
|
|
9
9
|
const { TESTOMATIO_ENV } = process.env;
|
|
10
10
|
|
|
11
11
|
|
|
@@ -15,17 +15,16 @@ class TestomatClient {
|
|
|
15
15
|
*
|
|
16
16
|
* @param {*} params
|
|
17
17
|
*/
|
|
18
|
-
constructor(params) {
|
|
19
|
-
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
18
|
+
constructor(params = {}) {
|
|
20
19
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
21
20
|
this.env = TESTOMATIO_ENV;
|
|
22
21
|
this.parallel = params.parallel;
|
|
23
22
|
const store = {}
|
|
24
23
|
this.pipes = pipesFactory(params, store);
|
|
25
24
|
this.queue = Promise.resolve();
|
|
26
|
-
this.axios = axios.create();
|
|
27
25
|
this.totalUploaded = 0;
|
|
28
26
|
this.version = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()).version;
|
|
27
|
+
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
/**
|
|
@@ -34,7 +33,9 @@ class TestomatClient {
|
|
|
34
33
|
* @returns {Promise} - resolves to Run id which should be used to update / add test
|
|
35
34
|
*/
|
|
36
35
|
createRun() {
|
|
37
|
-
|
|
36
|
+
// all pipes disabled, skipping
|
|
37
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
38
|
+
|
|
38
39
|
const runParams = {
|
|
39
40
|
title: this.title,
|
|
40
41
|
parallel: this.parallel,
|
|
@@ -43,7 +44,7 @@ class TestomatClient {
|
|
|
43
44
|
|
|
44
45
|
global.testomatioArtifacts = [];
|
|
45
46
|
|
|
46
|
-
this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.createRun(runParams))))
|
|
47
|
+
this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.createRun(runParams)))).catch(err => console.log(APP_PREFIX, err));;
|
|
47
48
|
|
|
48
49
|
return this.queue;
|
|
49
50
|
}
|
|
@@ -54,6 +55,9 @@ class TestomatClient {
|
|
|
54
55
|
* @returns {Promise}
|
|
55
56
|
*/
|
|
56
57
|
async addTestRun(testId, status, testData = {}) {
|
|
58
|
+
// all pipes disabled, skipping
|
|
59
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
60
|
+
|
|
57
61
|
const {
|
|
58
62
|
error = '',
|
|
59
63
|
time = '',
|
|
@@ -117,7 +121,7 @@ class TestomatClient {
|
|
|
117
121
|
artifacts,
|
|
118
122
|
};
|
|
119
123
|
|
|
120
|
-
this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.addTest(data))))
|
|
124
|
+
this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.addTest(data)))).catch(err => console.log(APP_PREFIX, err));;
|
|
121
125
|
return this.queue;
|
|
122
126
|
}
|
|
123
127
|
|
|
@@ -127,6 +131,9 @@ class TestomatClient {
|
|
|
127
131
|
* @returns {Promise}
|
|
128
132
|
*/
|
|
129
133
|
updateRunStatus(status, isParallel) {
|
|
134
|
+
// all pipes disabled, skipping
|
|
135
|
+
if (!this.pipes.filter(p => p.isEnabled).length) return;
|
|
136
|
+
|
|
130
137
|
let statusEvent;
|
|
131
138
|
if (status === FINISHED) statusEvent = 'finish';
|
|
132
139
|
if (status === PASSED) statusEvent = 'pass';
|
|
@@ -134,7 +141,7 @@ class TestomatClient {
|
|
|
134
141
|
if (isParallel) statusEvent += '_parallel';
|
|
135
142
|
const runParams = { status, statusEvent }
|
|
136
143
|
|
|
137
|
-
this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
144
|
+
this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams)))).catch(err => console.log(APP_PREFIX, err));;
|
|
138
145
|
|
|
139
146
|
if (upload.isArtifactsEnabled && this.totalUploaded > 0) {
|
|
140
147
|
console.log(
|
package/lib/constants.js
CHANGED
|
@@ -2,10 +2,19 @@ const chalk = require('chalk');
|
|
|
2
2
|
|
|
3
3
|
const APP_PREFIX = chalk.gray('[TESTOMATIO]');
|
|
4
4
|
|
|
5
|
+
const CSV_HEADERS = [
|
|
6
|
+
{ id: 'suite_title', title: 'Suite_title' },
|
|
7
|
+
{ id: 'title', title: 'Title' },
|
|
8
|
+
{ id: 'status', title: 'Status' },
|
|
9
|
+
{ id: 'message', title: 'Message' },
|
|
10
|
+
{ id: 'stack', title: 'Stack' },
|
|
11
|
+
];
|
|
12
|
+
|
|
5
13
|
module.exports = Object.freeze({
|
|
6
14
|
PASSED: 'passed',
|
|
7
15
|
FAILED: 'failed',
|
|
8
16
|
SKIPPED: 'skipped',
|
|
9
17
|
FINISHED: 'finished',
|
|
10
18
|
APP_PREFIX,
|
|
19
|
+
CSV_HEADERS
|
|
11
20
|
});
|
package/lib/pipe/csv.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const csvWriter = require('csv-writer');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const merge = require('lodash.merge');
|
|
6
|
+
const { isSameTest, getCurrentDateTime } = require('../util');
|
|
7
|
+
const { CSV_HEADERS } = require('../constants');
|
|
8
|
+
|
|
9
|
+
class CsvPipe {
|
|
10
|
+
|
|
11
|
+
constructor(params, store = {}) {
|
|
12
|
+
this.store = store;
|
|
13
|
+
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
14
|
+
this.isEnabled = true;
|
|
15
|
+
this.results = [];
|
|
16
|
+
|
|
17
|
+
this.outputDir = "export";
|
|
18
|
+
this.csvFilename = process.env.TESTOMATIO_CSV_FILENAME;
|
|
19
|
+
this.isCsvSave = false;
|
|
20
|
+
|
|
21
|
+
if (this.csvFilename !== undefined && this.csvFilename.split(".").length > 0 ) {
|
|
22
|
+
this.isCsvSave = true;
|
|
23
|
+
|
|
24
|
+
if(this.csvFilename.split(".")[0] === "report") {
|
|
25
|
+
this.outputFile = path.resolve(process.cwd(), this.outputDir, "report.csv");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.outputFile = path.resolve(process.cwd(), this.outputDir, getCurrentDateTime() + "_" + this.csvFilename.split(".")[0] + ".csv");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async createRun() {}
|
|
34
|
+
|
|
35
|
+
updateRun() {}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a folder that will contain the exported files
|
|
39
|
+
*/
|
|
40
|
+
checkExportDir() {
|
|
41
|
+
if (!fs.existsSync(this.outputDir)) {
|
|
42
|
+
return fs.mkdirSync(this.outputDir);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Save data to the csv file.
|
|
47
|
+
* @param {Object} data - data that will be added to the CSV file. Example: [{suite_title: "Suite #1", test: "Test-case-1", message: "Test msg"}]
|
|
48
|
+
* @param {Object} headers - csv file headers. Example: [{ id: 'suite_title', title: 'Suite_title' }]
|
|
49
|
+
*/
|
|
50
|
+
async saveToCsv(data, headers) {
|
|
51
|
+
// First, we check whether the export directory exists: if yes - OK, no - create it.
|
|
52
|
+
this.checkExportDir();
|
|
53
|
+
|
|
54
|
+
console.log(chalk.yellow(`The test results will be added to the csv. It will take some time...`));
|
|
55
|
+
//Create csv writer object
|
|
56
|
+
const writer = csvWriter.createObjectCsvWriter({
|
|
57
|
+
path: this.outputFile,
|
|
58
|
+
header: headers
|
|
59
|
+
});
|
|
60
|
+
//Save csv file based on the current data
|
|
61
|
+
try {
|
|
62
|
+
await writer.writeRecords(data);
|
|
63
|
+
console.log(chalk.green(`Recording completed! You can check the result in file = ${this.outputFile}`));
|
|
64
|
+
} catch(e) {
|
|
65
|
+
console.log('Unknown error: ', e);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Add test data to the result array for saving. As a result of this function, we get a result object to save.
|
|
70
|
+
* @param {Object} test - object which includes each test entry.
|
|
71
|
+
*/
|
|
72
|
+
addTest(test) {
|
|
73
|
+
if (!this.isEnabled) return;
|
|
74
|
+
|
|
75
|
+
const index = this.results.findIndex(t => isSameTest(t, test));
|
|
76
|
+
// update if they were already added
|
|
77
|
+
if (index >= 0) {
|
|
78
|
+
this.results[index] = merge(this.results[index], test);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const {suite_title, title, status, message, stack} = test;
|
|
83
|
+
|
|
84
|
+
this.results.push({
|
|
85
|
+
suite_title,
|
|
86
|
+
title,
|
|
87
|
+
status,
|
|
88
|
+
message,
|
|
89
|
+
stack
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Save short tests data to the csv file.
|
|
94
|
+
* @param {Object} runParams - run params.
|
|
95
|
+
*/
|
|
96
|
+
async finishRun(runParams) {
|
|
97
|
+
if (!this.isEnabled) return;
|
|
98
|
+
|
|
99
|
+
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
100
|
+
//Save results based on the default headers
|
|
101
|
+
if(this.isCsvSave) {
|
|
102
|
+
this.saveToCsv(this.results, CSV_HEADERS);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
toString() {
|
|
107
|
+
return 'csv exporter';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = CsvPipe;
|
package/lib/pipe/github.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
const path = require('path');
|
|
1
2
|
const chalk = require('chalk');
|
|
3
|
+
const humanizeDuration = require("humanize-duration");
|
|
4
|
+
const merge = require('lodash.merge');
|
|
2
5
|
const { Octokit } = require("@octokit/rest");
|
|
3
6
|
const { APP_PREFIX } = require('../constants');
|
|
4
7
|
const { ansiRegExp, isSameTest } = require('../util');
|
|
5
|
-
const merge = require('lodash.merge');
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class GitHubPipe {
|
|
@@ -14,11 +16,22 @@ class GitHubPipe {
|
|
|
14
16
|
this.tests = []
|
|
15
17
|
this.token = params.GH_PAT || process.env.GH_PAT;
|
|
16
18
|
this.ref = process.env.GITHUB_REF
|
|
17
|
-
this.repo = process.env.
|
|
19
|
+
this.repo = process.env.GITHUB_REPOSITORY
|
|
20
|
+
|
|
21
|
+
if (process.env.DEBUG) {
|
|
22
|
+
console.log(APP_PREFIX, 'GitHub Pipe: ', this.token ? 'TOKEN' : '*no token*', this.ref, this.repo);
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
if (!this.token || !this.ref || !this.repo) return;
|
|
19
26
|
this.isEnabled = true;
|
|
20
|
-
|
|
27
|
+
const matchedIssue = this.ref.match(/refs\/pull\/(\d+)\/merge/);
|
|
28
|
+
if (!matchedIssue) return;
|
|
29
|
+
this.issue = matchedIssue[1]
|
|
21
30
|
this.start = new Date();
|
|
31
|
+
|
|
32
|
+
if (process.env.DEBUG) {
|
|
33
|
+
console.log(APP_PREFIX, 'GitHub Pipe: Enabled');
|
|
34
|
+
}
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
async createRun() {}
|
|
@@ -57,13 +70,12 @@ class GitHubPipe {
|
|
|
57
70
|
const skippedCount = this.tests.filter(t => t.status === 'skipped').length;
|
|
58
71
|
|
|
59
72
|
let summary = `
|
|
60
|
-
### Run Report
|
|
61
73
|
|
|
62
|
-
| | ${statusEmoji(runParams.status)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
74
|
+
| [](https://testomat.io) | ${statusEmoji(runParams.status)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
63
75
|
| --- | --- |
|
|
64
76
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
65
77
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji('passed')} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
66
|
-
| Duration | 🕐 **${parseFloat(this.tests.reduce((a, t) => a + (t.run_time || 0), 0))
|
|
78
|
+
| Duration | 🕐 **${humanizeDuration(parseFloat(this.tests.reduce((a, t) => a + (t.run_time || 0), 0)))}** |
|
|
67
79
|
`
|
|
68
80
|
if (this.store.runUrl) {
|
|
69
81
|
summary += `| Testomat.io Report | 📊 [Run #${this.store.runId}](${this.store.runUrl}) | `;
|
|
@@ -92,8 +104,9 @@ class GitHubPipe {
|
|
|
92
104
|
if (t.stack) text += "```diff\n" + t.stack.replace(ansiRegExp(), '').trim() + "\n```\n";
|
|
93
105
|
|
|
94
106
|
if (t.artifacts && t.artifacts.length && !process.env.TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
95
|
-
t.artifacts.filter(f => f.endsWith('.png')).forEach(f => {
|
|
107
|
+
t.artifacts.filter(f => !!f).filter(f => f.endsWith('.png')).forEach(f => {
|
|
96
108
|
if (f.endsWith('.png')) return text+= `\n`
|
|
109
|
+
return text+= `[📄 ${path.basename(f)}](${f})\n`
|
|
97
110
|
});
|
|
98
111
|
}
|
|
99
112
|
|
|
@@ -105,18 +118,19 @@ class GitHubPipe {
|
|
|
105
118
|
let body = summary;
|
|
106
119
|
|
|
107
120
|
if (failures.length) {
|
|
108
|
-
body +=
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
body += "\n
|
|
121
|
+
body += `\n<details>\n<summary><h3>🟥 Failures (${failures.length})</h4></summary>\n\n${failures.join('\n')}\n`;
|
|
122
|
+
if (failures.length > 20) {
|
|
123
|
+
body += "\n> Notice\n> Only first 20 failures shown*"
|
|
124
|
+
}
|
|
125
|
+
body += "\n\n</details>";
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
if (this.tests.length > 0) {
|
|
116
|
-
body += "\n
|
|
129
|
+
body += "\n<details>\n<summary><h3>🐢 Slowest Tests</h3></summary>\n\n"
|
|
117
130
|
body += this.tests.sort((a, b) => b?.run_time - a?.run_time).slice(0, 5).map(t => {
|
|
118
|
-
return `* ${fullName(t)} (${parseFloat(t.run_time)
|
|
131
|
+
return `* ${fullName(t)} (${humanizeDuration(parseFloat(t.run_time))})`
|
|
119
132
|
}).join('\n')
|
|
133
|
+
body += "\n</details>"
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
try {
|
|
@@ -143,6 +157,10 @@ class GitHubPipe {
|
|
|
143
157
|
);
|
|
144
158
|
}
|
|
145
159
|
}
|
|
160
|
+
|
|
161
|
+
toString() {
|
|
162
|
+
return 'GitHub Reporter'
|
|
163
|
+
}
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
function statusEmoji(status) {
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const { ansiRegExp, isSameTest } = require('../util');
|
|
2
|
+
const { APP_PREFIX } = require('../constants');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const got = require('got');
|
|
5
|
+
const humanizeDuration = require('humanize-duration');
|
|
6
|
+
const merge = require('lodash.merge');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
//! GITLAB_PAT environment variable is required for this functionality to work and your pipeline trigger should be merge_request
|
|
10
|
+
|
|
11
|
+
class GitLabPipe {
|
|
12
|
+
isEnabled = false;
|
|
13
|
+
|
|
14
|
+
constructor(params, store = {}) {
|
|
15
|
+
this.ENV = process.env;
|
|
16
|
+
this.store = store;
|
|
17
|
+
this.tests = [];
|
|
18
|
+
// GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
|
|
19
|
+
this.token = params.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
20
|
+
|
|
21
|
+
if (this.ENV.DEBUG) {
|
|
22
|
+
console.log(
|
|
23
|
+
APP_PREFIX,
|
|
24
|
+
chalk.yellow('GitLab Pipe:'),
|
|
25
|
+
this.token ? 'TOKEN passed' : '*no token*',
|
|
26
|
+
`Project id: ${this.ENV.CI_PROJECT_ID}, MR id: ${this.ENV.CI_MERGE_REQUEST_IID}`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!this.ENV.CI_PROJECT_ID || !this.ENV.CI_MERGE_REQUEST_IID) {
|
|
31
|
+
console.warn(
|
|
32
|
+
`CI pipeline should be run in Merge Request to have ability to add the report comment.`,
|
|
33
|
+
);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!this.token) {
|
|
38
|
+
console.warn(chalk.grey(`Hint: GitLab CI variables are unavailable for unprotected branches by default.`));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.isEnabled = true;
|
|
43
|
+
|
|
44
|
+
if (this.ENV.DEBUG) {
|
|
45
|
+
console.log(APP_PREFIX, chalk.yellow('GitLab Pipe: Enabled'));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async createRun() {}
|
|
50
|
+
|
|
51
|
+
addTest(test) {
|
|
52
|
+
if (!this.isEnabled) return;
|
|
53
|
+
|
|
54
|
+
const index = this.tests.findIndex(t => isSameTest(t, test));
|
|
55
|
+
// update if they were already added
|
|
56
|
+
if (index >= 0) {
|
|
57
|
+
this.tests[index] = merge(this.tests[index], test);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.tests.push(test);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async finishRun(runParams) {
|
|
65
|
+
if (!this.isEnabled) return;
|
|
66
|
+
|
|
67
|
+
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
68
|
+
|
|
69
|
+
// ... create a comment on GitLab
|
|
70
|
+
const passedCount = this.tests.filter(t => t.status === 'passed').length;
|
|
71
|
+
const failedCount = this.tests.filter(t => t.status === 'failed').length;
|
|
72
|
+
const skippedCount = this.tests.filter(t => t.status === 'skipped').length;
|
|
73
|
+
|
|
74
|
+
// constructing the table
|
|
75
|
+
let summary = `
|
|
76
|
+
|
|
77
|
+
| [](https://testomat.io) | ${statusEmoji(
|
|
78
|
+
runParams.status,
|
|
79
|
+
)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
80
|
+
| --- | --- |
|
|
81
|
+
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
82
|
+
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
83
|
+
'passed',
|
|
84
|
+
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
85
|
+
| Duration | 🕐 **${humanizeDuration(parseFloat(this.tests.reduce((a, t) => a + (t.run_time || 0), 0)))}** |
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
if (this.ENV.CI_JOB_NAME && this.ENV.CI_JOB_ID) {
|
|
89
|
+
summary += `| Job | 👷 [${this.ENV.CI_JOB_ID}](${this.ENV.CI_JOB_URL})<br>Name: **${this.ENV.CI_JOB_NAME}**<br>Stage: **${this.ENV.CI_JOB_STAGE}** | `;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const failures = this.tests
|
|
93
|
+
.filter(t => t.status === 'failed')
|
|
94
|
+
.slice(0, 20)
|
|
95
|
+
.map(t => {
|
|
96
|
+
let text = `#### ${statusEmoji('failed')} ${fullName(t)} `;
|
|
97
|
+
text += '\n\n';
|
|
98
|
+
if (t.message)
|
|
99
|
+
text +=
|
|
100
|
+
'> ' +
|
|
101
|
+
t.message
|
|
102
|
+
.replace(/[^\x20-\x7E]/g, '')
|
|
103
|
+
.replace(ansiRegExp(), '')
|
|
104
|
+
.trim() +
|
|
105
|
+
'\n';
|
|
106
|
+
if (t.stack) text += '```diff\n' + t.stack.replace(ansiRegExp(), '').trim() + '\n```\n';
|
|
107
|
+
|
|
108
|
+
if (t.artifacts && t.artifacts.length && !this.ENV.TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
109
|
+
t.artifacts
|
|
110
|
+
.filter(f => !!f)
|
|
111
|
+
.filter(f => f.endsWith('.png'))
|
|
112
|
+
.forEach(f => {
|
|
113
|
+
if (f.endsWith('.png')) return (text += `\n`);
|
|
114
|
+
return (text += `[📄 ${path.basename(f)}](${f})\n`);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
text += '\n---\n';
|
|
119
|
+
|
|
120
|
+
return text;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
let body = summary;
|
|
124
|
+
|
|
125
|
+
if (failures.length) {
|
|
126
|
+
body += `\n<details>\n<summary><h3>🟥 Failures (${failures.length})</h4></summary>\n\n${failures.join('\n')}\n`;
|
|
127
|
+
if (failures.length > 20) {
|
|
128
|
+
body += '\n> Notice\n> Only first 20 failures shown*';
|
|
129
|
+
}
|
|
130
|
+
body += '\n\n</details>';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (this.tests.length > 0) {
|
|
134
|
+
body += '\n<details>\n<summary><h3>🐢 Slowest Tests</h3></summary>\n\n';
|
|
135
|
+
body += this.tests
|
|
136
|
+
.sort((a, b) => b?.run_time - a?.run_time)
|
|
137
|
+
.slice(0, 5)
|
|
138
|
+
.map(t => {
|
|
139
|
+
return `* ${fullName(t)} (${humanizeDuration(parseFloat(t.run_time))})`;
|
|
140
|
+
})
|
|
141
|
+
.join('\n');
|
|
142
|
+
body += '\n</details>';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const addCommentRequestURL = `https://gitlab.com/api/v4/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes?access_token=${this.token}`;
|
|
147
|
+
console.info(chalk.grey(`Adding comment via url: ${addCommentRequestURL}`));
|
|
148
|
+
const options = {
|
|
149
|
+
json: {
|
|
150
|
+
body,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const addCommentResponse = await got.post(addCommentRequestURL, options);
|
|
155
|
+
const commentID = JSON.parse(addCommentResponse.body).id;
|
|
156
|
+
const commentURL = `${this.ENV.CI_PROJECT_URL}/-/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}#note_${commentID}`;
|
|
157
|
+
|
|
158
|
+
console.log(APP_PREFIX, chalk.yellow('GitLab'), `Report created: ${chalk.magenta(commentURL)}`);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.log(
|
|
161
|
+
APP_PREFIX,
|
|
162
|
+
chalk.yellow('GitLab'),
|
|
163
|
+
`Couldn't create GitLab report\n${err}.
|
|
164
|
+
Reqest url: ${addCommentRequestURL}
|
|
165
|
+
Request data: ${JSON.stringify(options)}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
toString() {
|
|
171
|
+
return 'GitLab Reporter';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
updateRun() {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function statusEmoji(status) {
|
|
178
|
+
if (status === 'passed') return '🟢';
|
|
179
|
+
if (status === 'failed') return '🔴';
|
|
180
|
+
if (status === 'skipped') return '🟡';
|
|
181
|
+
return '';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function fullName(t) {
|
|
185
|
+
let line = '';
|
|
186
|
+
if (t.suite_title) line = `${t.suite_title}: `;
|
|
187
|
+
line += `**${t.title}**`;
|
|
188
|
+
if (t.example) line += ` \`[${Object.values(t.example)}]\``;
|
|
189
|
+
return line;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = GitLabPipe;
|
package/lib/pipe/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
3
4
|
const { APP_PREFIX } = require('../constants');
|
|
4
5
|
const TestomatioPipe = require('./testomatio');
|
|
5
6
|
const GitHubPipe = require('./github');
|
|
7
|
+
const GitLabPipe = require('./gitlab');
|
|
8
|
+
const CsvPipe = require('./csv');
|
|
6
9
|
|
|
7
10
|
module.exports = function(params, opts) {
|
|
8
11
|
const extraPipes = [];
|
|
@@ -36,10 +39,15 @@ module.exports = function(params, opts) {
|
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
|
|
39
|
-
const
|
|
42
|
+
const pipes = [
|
|
40
43
|
new TestomatioPipe(params, opts),
|
|
41
|
-
new GitHubPipe(params, opts),
|
|
44
|
+
new GitHubPipe(params, opts),
|
|
45
|
+
new GitLabPipe(params, opts),
|
|
46
|
+
new CsvPipe(params, opts),
|
|
47
|
+
...extraPipes
|
|
42
48
|
];
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
console.log(APP_PREFIX, chalk.cyan('Pipes:'), chalk.cyan(pipes.filter(p => p.isEnabled).map(p => p.toString()).join(', ') || "No pipes enabled"));
|
|
51
|
+
|
|
52
|
+
return pipes;
|
|
45
53
|
}
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -15,9 +15,15 @@ class TestomatioPipe {
|
|
|
15
15
|
constructor(params, store = {}) {
|
|
16
16
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
17
17
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
18
|
+
if (process.env.DEBUG) {
|
|
19
|
+
console.log(APP_PREFIX, 'Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
20
|
+
}
|
|
18
21
|
if (!this.apiKey) {
|
|
19
22
|
return;
|
|
20
23
|
}
|
|
24
|
+
if (process.env.DEBUG) {
|
|
25
|
+
console.log(APP_PREFIX, 'Testomatio Pipe: Enabled');
|
|
26
|
+
}
|
|
21
27
|
this.store = store;
|
|
22
28
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
23
29
|
this.axios = axios.create();
|
|
@@ -26,7 +32,6 @@ class TestomatioPipe {
|
|
|
26
32
|
this.runId = params.runId || process.env.runId;
|
|
27
33
|
this.createNewTests = !!process.env.TESTOMATIO_CREATE;
|
|
28
34
|
|
|
29
|
-
|
|
30
35
|
if (!isValidUrl(this.url.trim())) {
|
|
31
36
|
this.isEnabled = false;
|
|
32
37
|
console.log(
|
|
@@ -56,7 +61,7 @@ class TestomatioPipe {
|
|
|
56
61
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
57
62
|
this.store.runUrl = this.runUrl;
|
|
58
63
|
this.store.runId = this.runId;
|
|
59
|
-
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId
|
|
64
|
+
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
|
|
60
65
|
process.env.runId = this.runId;
|
|
61
66
|
} catch (err) {
|
|
62
67
|
console.log(
|
|
@@ -122,7 +127,11 @@ class TestomatioPipe {
|
|
|
122
127
|
} catch (err) {
|
|
123
128
|
console.log(APP_PREFIX, 'Error updating status, skipping...', err);
|
|
124
129
|
}
|
|
125
|
-
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
toString() {
|
|
133
|
+
return 'Testomatio Reporter';
|
|
134
|
+
}
|
|
126
135
|
}
|
|
127
136
|
|
|
128
137
|
module.exports = TestomatioPipe;
|
package/lib/util.js
CHANGED
|
@@ -138,7 +138,19 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
const isSameTest = (test, t) => {
|
|
141
|
-
return
|
|
141
|
+
return (typeof t === 'object')
|
|
142
|
+
&& (typeof test === 'object')
|
|
143
|
+
&& t.title == test.title
|
|
144
|
+
&& t.suite_title == test.suite_title
|
|
145
|
+
&& Object.values(t.example || {}) == Object.values(test.example || {})
|
|
146
|
+
&& t.test_id == test.test_id
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const getCurrentDateTime = () => {
|
|
150
|
+
const today = new Date();
|
|
151
|
+
|
|
152
|
+
return today.getFullYear() + '_' + (today.getMonth() + 1) + '_' + today.getDate() + '_' +
|
|
153
|
+
today.getHours() + "_" + today.getMinutes() + "_" + today.getSeconds();
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
module.exports = {
|
|
@@ -150,4 +162,5 @@ module.exports = {
|
|
|
150
162
|
fetchSourceCode,
|
|
151
163
|
fetchSourceCodeFromStackTrace,
|
|
152
164
|
fetchFilesFromStackTrace,
|
|
165
|
+
getCurrentDateTime
|
|
153
166
|
};
|
package/lib/xmlReader.js
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require("fs");
|
|
|
4
4
|
|
|
5
5
|
// const util = require("util"); // you can see a result
|
|
6
6
|
const { XMLParser } = require("fast-xml-parser");
|
|
7
|
-
const { PASSED, FAILED, SKIPPED } = require('./constants');
|
|
7
|
+
const { APP_PREFIX, PASSED, FAILED, SKIPPED } = require('./constants');
|
|
8
8
|
const { fetchFilesFromStackTrace, fetchSourceCode, fetchSourceCodeFromStackTrace } = require('./util');
|
|
9
9
|
const upload = require('./fileUploader');
|
|
10
10
|
const pipesFactory = require('./pipe');
|
|
@@ -45,6 +45,9 @@ class XmlReader {
|
|
|
45
45
|
this.stats = {}
|
|
46
46
|
this.stats.language = opts.lang?.toLowerCase();
|
|
47
47
|
this.filesToUpload = {}
|
|
48
|
+
|
|
49
|
+
this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
|
|
50
|
+
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
connectAdapter() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "0.8.0-beta.
|
|
3
|
+
"version": "0.8.0-beta.20",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"repository": "git@github.com:testomatio/reporter.git",
|
|
@@ -16,10 +16,14 @@
|
|
|
16
16
|
"dotenv": "^16.0.1",
|
|
17
17
|
"fast-xml-parser": "^4.0.8",
|
|
18
18
|
"glob": "^8.0.3",
|
|
19
|
+
"got": "^11.8.3",
|
|
19
20
|
"has-flag": "^5.0.1",
|
|
21
|
+
"humanize-duration": "^3.27.3",
|
|
20
22
|
"is-valid-path": "^0.1.1",
|
|
21
23
|
"json-cycle": "^1.3.0",
|
|
22
|
-
"lodash.memoize": "^4.1.2"
|
|
24
|
+
"lodash.memoize": "^4.1.2",
|
|
25
|
+
"lodash.merge": "^4.6.2",
|
|
26
|
+
"csv-writer": "^1.6.0"
|
|
23
27
|
},
|
|
24
28
|
"files": [
|
|
25
29
|
"bin",
|
|
@@ -27,6 +31,7 @@
|
|
|
27
31
|
"testcafe"
|
|
28
32
|
],
|
|
29
33
|
"scripts": {
|
|
34
|
+
"clear-exportdir": "rm -rf export/",
|
|
30
35
|
"pretty": "prettier --write .",
|
|
31
36
|
"lint": "eslint lib",
|
|
32
37
|
"lint:fix": "eslint lib --fix",
|
|
@@ -34,13 +39,13 @@
|
|
|
34
39
|
"init": "cd ./tests/adapter/examples/cucumber && npm i",
|
|
35
40
|
"test:unit": "mocha tests",
|
|
36
41
|
"test:adapter": "mocha './tests/adapter/index.test.js'",
|
|
42
|
+
"test:pipes": "mocha './tests/pipes/*_test.js'",
|
|
37
43
|
"test:adapter:jest:example": "jest './tests/adapter/examples/jest/index.test.js' --config='./tests/adapter/examples/jest/jest.config.js'",
|
|
38
44
|
"test:adapter:mocha:example": "mocha './tests/adapter/examples/mocha/index.test.js' --config='./tests/adapter/examples/mocha/mocha.config.js'",
|
|
39
45
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
40
46
|
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
41
47
|
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js"
|
|
42
48
|
},
|
|
43
|
-
"peerDependencies": {},
|
|
44
49
|
"devDependencies": {
|
|
45
50
|
"@cucumber/cucumber": "^8.6.0",
|
|
46
51
|
"@wdio/reporter": "^7.16.13",
|