@testomatio/reporter 1.1.0-beta.cypress-trace-time → 1.1.0-beta.fix-junit-reporter
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/Changelog.md +367 -0
- package/lib/adapter/cucumber/current.js +2 -1
- package/lib/adapter/cucumber/legacy.js +2 -1
- package/lib/adapter/cypress-plugin/index.js +31 -25
- package/lib/adapter/mocha.js +2 -1
- package/lib/bin/startTest.js +2 -1
- package/lib/config.js +5 -0
- package/lib/pipe/testomatio.js +2 -1
- package/lib/utils/utils.js +6 -1
- package/lib/xmlReader.js +106 -104
- package/package.json +1 -1
package/Changelog.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
<!-- pending release updates -->
|
|
2
|
+
# 1.1.0
|
|
3
|
+
|
|
4
|
+
* Assign Run by label:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
TESTOMATIO={API_KEY} TESTOMATIO_LABEL="release,module:checkout" <actual run command>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
# 1.0.18
|
|
11
|
+
|
|
12
|
+
* Fixed stack traces for CodeceptJS
|
|
13
|
+
|
|
14
|
+
# 1.0.17
|
|
15
|
+
|
|
16
|
+
Renamed `TESTOMATIO_STACK_FILTER` to `TESTOMATIO_STACK_IGNORE`
|
|
17
|
+
|
|
18
|
+
# 1.0.16
|
|
19
|
+
|
|
20
|
+
* Addded [stack trace configuration](./docs/stacktrace.md) and documentation:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
TESTOMATIO_STACK_IGNORE="tests/support/**.js" <actual-run-command>
|
|
24
|
+
```
|
|
25
|
+
* Jest: fixed reporting tests without a suite title
|
|
26
|
+
|
|
27
|
+
# 1.0.15
|
|
28
|
+
|
|
29
|
+
* Attach Run to Jira Issue via `TESTOMATIO_JIRA_ID` env variable:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
TESTOMATIO_JIRA_ID=TST-12 <actual run command>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
* Mocha - removed requirement of TESTOMATIO API Key
|
|
36
|
+
|
|
37
|
+
# 1.0.14
|
|
38
|
+
|
|
39
|
+
* Execute tests by tag names. Use this filter to select tests associated with specific tags.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
TESTOMATIO={API_KEY} npx start-test-run -c 'actual run command' --filter 'testomatio:tag-name=smoke'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
* more instruction you can find in docs/pipes/testomatio.md
|
|
46
|
+
|
|
47
|
+
# 1.0.13
|
|
48
|
+
|
|
49
|
+
* JUnit improvements
|
|
50
|
+
* Match test from source code by adding Test ID as a comment:
|
|
51
|
+
|
|
52
|
+
```java
|
|
53
|
+
// @T8acca9eb
|
|
54
|
+
```
|
|
55
|
+
* Match test from output by adding Test ID as output:
|
|
56
|
+
|
|
57
|
+
```java
|
|
58
|
+
System.out.println("tid://@T8acca9eb");
|
|
59
|
+
```
|
|
60
|
+
* Support for suite before and after output
|
|
61
|
+
* Improved support for artifacts
|
|
62
|
+
|
|
63
|
+
# 1.0.12
|
|
64
|
+
|
|
65
|
+
& Logger refactoring by @olexandr13 in #208
|
|
66
|
+
* fix undefined logs by @olexandr13 in #210
|
|
67
|
+
|
|
68
|
+
# 1.0.11
|
|
69
|
+
|
|
70
|
+
* fix steps duplication for codecept report by @olexandr13 in #209
|
|
71
|
+
|
|
72
|
+
# 1.0.10
|
|
73
|
+
|
|
74
|
+
* Added `TESTOMATIO_PUBLISH=1` variable to automatically publish run report
|
|
75
|
+
|
|
76
|
+
# 1.0.9
|
|
77
|
+
|
|
78
|
+
* Support XUnit format
|
|
79
|
+
* Improved support for parametrized Java tests
|
|
80
|
+
|
|
81
|
+
# 1.0.8
|
|
82
|
+
|
|
83
|
+
* Fixed `Can't read push of undefined` when logging steps
|
|
84
|
+
|
|
85
|
+
# 1.0.6
|
|
86
|
+
|
|
87
|
+
* Testomat.io. Auto-detect current build url and report it to Testomat.io. Manually url can be set with `BUILD_URL` variable:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
BUILD_URL=https://.... TESTOMATIO=apiKey <actual test command>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
# 1.0.5
|
|
94
|
+
|
|
95
|
+
* Fix "create tests" params processing for testomatio pipe
|
|
96
|
+
|
|
97
|
+
# 1.0.4
|
|
98
|
+
|
|
99
|
+
* Fixed parallel run
|
|
100
|
+
|
|
101
|
+
# 1.0.3
|
|
102
|
+
|
|
103
|
+
* Fixed reporting parallel runs
|
|
104
|
+
|
|
105
|
+
# 1.0.0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
* Added [`TESTOMATIO_SHARED_RUN` option](https://github.com/testomatio/reporter/blob/master/docs/pipes.md#reporting-parallel-execution-to-to-same-run) to use a shared run for parallel executions
|
|
109
|
+
* Reworked [documentation](https://github.com/testomatio/reporter/tree/master#readme).
|
|
110
|
+
* Added an option to obtain [S3 configuration](https://github.com/testomatio/reporter/blob/master/docs/artifacts.md#configuration) from Testomat.io
|
|
111
|
+
* Introduced [pipes](https://github.com/testomatio/reporter/blob/master/docs/pipes.md):
|
|
112
|
+
* GitHub
|
|
113
|
+
* GitLab
|
|
114
|
+
* CSV Pipe
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# 0.7.6
|
|
118
|
+
|
|
119
|
+
* Updated to use AWS S3 3.0 SDK for uploading
|
|
120
|
+
|
|
121
|
+
# 0.7.5
|
|
122
|
+
|
|
123
|
+
* Fixed reporting skipped tests in mocha
|
|
124
|
+
|
|
125
|
+
# 0.7.4
|
|
126
|
+
|
|
127
|
+
* Fixed parsing source code in JUnit files
|
|
128
|
+
|
|
129
|
+
# 0.7.3
|
|
130
|
+
|
|
131
|
+
* CodeceptJS: Upload all traces and videos from artifacts
|
|
132
|
+
* Fixed reporting skipped test in XML
|
|
133
|
+
* added `--timelimit` option to `report-xml` command line
|
|
134
|
+
|
|
135
|
+
# 0.7.2
|
|
136
|
+
|
|
137
|
+
* Fixed uploading non-existing file
|
|
138
|
+
|
|
139
|
+
# 0.7.1
|
|
140
|
+
|
|
141
|
+
* Support for NUnit XML v3 format
|
|
142
|
+
|
|
143
|
+
# 0.7.0
|
|
144
|
+
|
|
145
|
+
* Support for `@cucumber/cucumber` (>= 7.0) added
|
|
146
|
+
* Initial support for C# and NUnit
|
|
147
|
+
|
|
148
|
+
# 0.6.10
|
|
149
|
+
|
|
150
|
+
* Fixed uploading multilpe artifacts in Playwright
|
|
151
|
+
|
|
152
|
+
# 0.6.9
|
|
153
|
+
|
|
154
|
+
* Fixed pending tests reports for Cypress
|
|
155
|
+
|
|
156
|
+
# 0.6.8
|
|
157
|
+
# 0.6.7
|
|
158
|
+
|
|
159
|
+
* Pytest: fixed creating suites from reports
|
|
160
|
+
|
|
161
|
+
# 0.6.6
|
|
162
|
+
|
|
163
|
+
* JUnit reporter: prefer suite title over testcase classname in a report
|
|
164
|
+
|
|
165
|
+
# 0.6.5
|
|
166
|
+
|
|
167
|
+
* Fixed test statuses for runs in JUnit reporter
|
|
168
|
+
|
|
169
|
+
# 0.6.4
|
|
170
|
+
|
|
171
|
+
* Added `TESTOMATIO_PROCEED=1` param to not close current run
|
|
172
|
+
* Fixed priority of commands from `npx @testomatio/reporter`
|
|
173
|
+
|
|
174
|
+
# 0.6.3
|
|
175
|
+
|
|
176
|
+
* Fixed `npx start-test-run` to launch commands
|
|
177
|
+
|
|
178
|
+
# 0.6.2
|
|
179
|
+
|
|
180
|
+
* Added `--env-file` option to load env variables from env file
|
|
181
|
+
|
|
182
|
+
# 0.6.1
|
|
183
|
+
|
|
184
|
+
* Fixed creating RunGroup with JUnit reporter
|
|
185
|
+
|
|
186
|
+
# 0.6.0
|
|
187
|
+
|
|
188
|
+
* JUnit reporter support
|
|
189
|
+
|
|
190
|
+
# 0.5.10
|
|
191
|
+
|
|
192
|
+
* Fixed reporting Scenario Outline in Cypress-Cucumber
|
|
193
|
+
* Fixed error reports for Cypress when running in Chrome
|
|
194
|
+
|
|
195
|
+
# 0.5.9
|
|
196
|
+
|
|
197
|
+
* Added environment on Cypress report
|
|
198
|
+
|
|
199
|
+
# 0.5.8
|
|
200
|
+
|
|
201
|
+
* Fixed Cypress.io reporting
|
|
202
|
+
|
|
203
|
+
# 0.5.7
|
|
204
|
+
|
|
205
|
+
* Fixed webdriverio artifacts
|
|
206
|
+
|
|
207
|
+
# 0.5.6
|
|
208
|
+
|
|
209
|
+
* Unmark failed CodeceptJS tests as skipped
|
|
210
|
+
|
|
211
|
+
# 0.5.5
|
|
212
|
+
|
|
213
|
+
* Fixed `BeforeSuite` failures in CodeceptJS
|
|
214
|
+
|
|
215
|
+
# 0.5.4
|
|
216
|
+
|
|
217
|
+
Added `TESTOMATIO_CREATE=1` option to create unmatched tests on report
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
TESTOMATIO_CREATE=1 TESTOMATIO=apiKey npx codeceptjs run
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
# 0.5.3
|
|
224
|
+
|
|
225
|
+
* Fixed parsing suites
|
|
226
|
+
|
|
227
|
+
# 0.5.2
|
|
228
|
+
|
|
229
|
+
* Fixed multiple upload of artifacts in Cypress.io
|
|
230
|
+
|
|
231
|
+
# 0.5.1
|
|
232
|
+
|
|
233
|
+
* Fixed Cypress.io to report tests inside nested suites
|
|
234
|
+
|
|
235
|
+
# 0.5.0
|
|
236
|
+
|
|
237
|
+
* Added Cypress.io plugin
|
|
238
|
+
* Added artifacts upload to webdriverio
|
|
239
|
+
|
|
240
|
+
# 0.4.6
|
|
241
|
+
|
|
242
|
+
- Fixed CodeceptJS reporter to report tests failed in hooks
|
|
243
|
+
|
|
244
|
+
# 0.4.5
|
|
245
|
+
|
|
246
|
+
- Fixed "Total XX artifacts publicly uploaded to S3 bucket" when no S3 bucket is configured
|
|
247
|
+
- Improved S3 connection error messages
|
|
248
|
+
|
|
249
|
+
# 0.4.4
|
|
250
|
+
|
|
251
|
+
- Fixed returning 0 exit code when a process fails when running tests in parallel via `start-test-run`. Previously was using the last exit code returned by a process. Currently prefers the highest exit code that was returned by a process.
|
|
252
|
+
|
|
253
|
+
# 0.4.3
|
|
254
|
+
|
|
255
|
+
- Added `TESTOMATIO_DISABLE_ARTIFACTS` env variable to disable publishing artifacts.
|
|
256
|
+
|
|
257
|
+
# 0.4.2
|
|
258
|
+
|
|
259
|
+
- print version of reporter
|
|
260
|
+
- print number of uploaded artifacts
|
|
261
|
+
- print access mode for uploaded artifacts
|
|
262
|
+
|
|
263
|
+
# 0.4.1
|
|
264
|
+
|
|
265
|
+
Added `global.testomatioArtifacts = []` array which can be used to add arbitrary artifacts to a report.
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
// inside a running test:
|
|
269
|
+
global.testomatioArtifacts.push('file/to/upload.png');
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
# 0.4.0
|
|
273
|
+
|
|
274
|
+
- Playwright: Introduced playwright/test support with screenshots and video artifacts
|
|
275
|
+
|
|
276
|
+
> Known issues: reporting using projects configured in Playwright does not work yet
|
|
277
|
+
|
|
278
|
+
- CodeceptJS: added video uploads
|
|
279
|
+
|
|
280
|
+
# 0.3.16
|
|
281
|
+
|
|
282
|
+
- CodeceptJS: fixed reporting tests with empty steps (on retry)
|
|
283
|
+
|
|
284
|
+
# 0.3.15
|
|
285
|
+
|
|
286
|
+
- Finish Run via API:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
TESTOMATIO={apiKey} TESTOMATIO_RUN={runId} npx @testomatio/reporter@latest --finish
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
# 0.3.14
|
|
293
|
+
|
|
294
|
+
- Create an empty Run via API:
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
TESTOMATIO={apiKey} npx @testomatio/reporter@latest --launch
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
# 0.3.13
|
|
301
|
+
|
|
302
|
+
- Checking for a valid report URL
|
|
303
|
+
- Sending unlimited data on test report
|
|
304
|
+
|
|
305
|
+
# 0.3.12
|
|
306
|
+
|
|
307
|
+
- Fixed submitting arbitrary data on a test run
|
|
308
|
+
- Jest: fixed sending errors with stack traces
|
|
309
|
+
- Cypress: fixed sending reports
|
|
310
|
+
|
|
311
|
+
# 0.3.11
|
|
312
|
+
|
|
313
|
+
- Fixed circular JSON reference when submitting data to Testomatio
|
|
314
|
+
|
|
315
|
+
# 0.3.10
|
|
316
|
+
|
|
317
|
+
- Minor fixes
|
|
318
|
+
|
|
319
|
+
# 0.3.9
|
|
320
|
+
|
|
321
|
+
- Making all reporters to run without API key
|
|
322
|
+
|
|
323
|
+
# 0.3.8
|
|
324
|
+
|
|
325
|
+
- Fixed `npx start-test-run` to work with empty API keys
|
|
326
|
+
|
|
327
|
+
# 0.3.7
|
|
328
|
+
|
|
329
|
+
- Fixed release
|
|
330
|
+
|
|
331
|
+
# 0.3.6
|
|
332
|
+
|
|
333
|
+
- Update title and rungroup on start for scheduled runs.
|
|
334
|
+
|
|
335
|
+
# 0.3.5
|
|
336
|
+
|
|
337
|
+
- Added `TESTOMATIO_RUN` environment variable to pass id of a specific run to report
|
|
338
|
+
|
|
339
|
+
# 0.3.4
|
|
340
|
+
|
|
341
|
+
- Minor fixes
|
|
342
|
+
|
|
343
|
+
# 0.3.3
|
|
344
|
+
|
|
345
|
+
- [CodeceptJS] Fixed stack trace reporting
|
|
346
|
+
- [CodeceptJS] Fixed displaying of nested steps
|
|
347
|
+
- [CodeceptJS][mocha] Added assertion diff to report
|
|
348
|
+
|
|
349
|
+
# 0.3.2
|
|
350
|
+
|
|
351
|
+
- Fixed error message for S3 uploading
|
|
352
|
+
|
|
353
|
+
# 0.3.1
|
|
354
|
+
|
|
355
|
+
- [CodeceptJS] Better formatter for nested structures and BDD tests
|
|
356
|
+
|
|
357
|
+
# 0.3.0
|
|
358
|
+
|
|
359
|
+
- Added `TESTOMATIO_TITLE` env variable to set a name for Run
|
|
360
|
+
- Added `TESTOMATIO_RUNGROUP_TITLE` env variable to attach Run to RunGroup
|
|
361
|
+
- Added `TESTOMATIO_ENV` env variable to attach additional env values to report
|
|
362
|
+
- [CodeceptJS] **CodeceptJS v3 support**
|
|
363
|
+
- [CodeceptJS] Dropped support for CodeceptJS 2
|
|
364
|
+
- [CodeceptJS] Added support for before hooks
|
|
365
|
+
- [CodeceptJS] Log of steps
|
|
366
|
+
- [CodeceptJS] Upload screenshots of failed tests to S3
|
|
367
|
+
- [CodeceptJS] Updated to use with parallel execution
|
|
@@ -6,6 +6,7 @@ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
7
|
const logger = require('../../storages/logger');
|
|
8
8
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
9
|
+
const { TESTOMATIO } = require('../../config');
|
|
9
10
|
|
|
10
11
|
const { GherkinDocumentParser, PickleParser } = formatterHelpers;
|
|
11
12
|
const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
|
|
@@ -29,7 +30,7 @@ class CucumberReporter extends Formatter {
|
|
|
29
30
|
this.failures = [];
|
|
30
31
|
this.cases = [];
|
|
31
32
|
|
|
32
|
-
this.client = new TestomatClient({ apiKey: options.apiKey ||
|
|
33
|
+
this.client = new TestomatClient({ apiKey: options.apiKey || TESTOMATIO });
|
|
33
34
|
this.status = STATUS.PASSED;
|
|
34
35
|
|
|
35
36
|
global.testomatioRunningEnvironment = 'cucumber:current';
|
|
@@ -4,6 +4,7 @@ const chalk = require('chalk');
|
|
|
4
4
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
+
const { TESTOMATIO } = require('../../config');
|
|
7
8
|
|
|
8
9
|
const createTestomatFormatter = apiKey => {
|
|
9
10
|
if (!apiKey || apiKey === '') {
|
|
@@ -151,4 +152,4 @@ const createTestomatFormatter = apiKey => {
|
|
|
151
152
|
};
|
|
152
153
|
};
|
|
153
154
|
|
|
154
|
-
module.exports = createTestomatFormatter(
|
|
155
|
+
module.exports = createTestomatFormatter(TESTOMATIO);
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
const { STATUS } = require('../../constants');
|
|
2
2
|
const { parseTest, parseSuite } = require('../../utils/utils');
|
|
3
3
|
const TestomatClient = require('../../client');
|
|
4
|
+
const { TESTOMATIO } = require('../../config');
|
|
4
5
|
|
|
5
6
|
const testomatioReporter = on => {
|
|
6
|
-
if (!
|
|
7
|
+
if (!TESTOMATIO) {
|
|
7
8
|
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
8
|
-
return
|
|
9
|
+
return;
|
|
9
10
|
}
|
|
10
|
-
const client = new TestomatClient({ apiKey:
|
|
11
|
+
const client = new TestomatClient({ apiKey: TESTOMATIO });
|
|
11
12
|
|
|
12
|
-
on('before:run', async
|
|
13
|
+
on('before:run', async run => {
|
|
13
14
|
// TODO: looks like client.env does not exist
|
|
14
15
|
if (!client.env) {
|
|
15
|
-
client.env = `${run.browser.displayName},${run.system.osName}
|
|
16
|
+
client.env = `${run.browser.displayName},${run.system.osName}`;
|
|
16
17
|
}
|
|
17
18
|
await client.createRun();
|
|
18
19
|
});
|
|
@@ -43,7 +44,8 @@ const testomatioReporter = on => {
|
|
|
43
44
|
|
|
44
45
|
if (!error && test.displayError) {
|
|
45
46
|
error = { message: test.displayError };
|
|
46
|
-
error.inspect = function() {
|
|
47
|
+
error.inspect = function () {
|
|
48
|
+
// eslint-disable-line func-names
|
|
47
49
|
return this.message;
|
|
48
50
|
};
|
|
49
51
|
}
|
|
@@ -51,18 +53,17 @@ const testomatioReporter = on => {
|
|
|
51
53
|
const formattedError = error
|
|
52
54
|
? {
|
|
53
55
|
message: error.message,
|
|
54
|
-
inspect:
|
|
56
|
+
inspect:
|
|
57
|
+
error.inspect ||
|
|
58
|
+
function () {
|
|
59
|
+
return this.message;
|
|
60
|
+
},
|
|
55
61
|
}
|
|
56
62
|
: '';
|
|
57
63
|
|
|
58
64
|
const screenshots = Array.isArray(results.screenshots)
|
|
59
65
|
? results.screenshots
|
|
60
|
-
.filter(
|
|
61
|
-
screenshot =>
|
|
62
|
-
screenshot?.path &&
|
|
63
|
-
screenshot?.path.includes(title) &&
|
|
64
|
-
screenshot?.takenAt
|
|
65
|
-
)
|
|
66
|
+
.filter(screenshot => screenshot?.path && screenshot?.path.includes(title) && screenshot?.takenAt)
|
|
66
67
|
.map(screenshot => screenshot.path)
|
|
67
68
|
: [];
|
|
68
69
|
|
|
@@ -70,25 +71,30 @@ const testomatioReporter = on => {
|
|
|
70
71
|
|
|
71
72
|
let state;
|
|
72
73
|
switch (test.state) {
|
|
73
|
-
case 'passed':
|
|
74
|
-
|
|
74
|
+
case 'passed':
|
|
75
|
+
state = STATUS.PASSED;
|
|
76
|
+
break;
|
|
77
|
+
case 'failed':
|
|
78
|
+
state = STATUS.FAILED;
|
|
79
|
+
break;
|
|
75
80
|
case 'skipped':
|
|
76
|
-
case 'pending':
|
|
81
|
+
case 'pending':
|
|
77
82
|
default:
|
|
78
83
|
state = STATUS.SKIPPED;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
addSpecTestsPromises.push(
|
|
82
87
|
client.addTestRun(state, {
|
|
83
|
-
title,
|
|
84
|
-
time,
|
|
85
|
-
example,
|
|
86
|
-
error: formattedError,
|
|
87
|
-
files,
|
|
88
|
-
suite_title: suiteTitle,
|
|
89
|
-
test_id: testId,
|
|
90
|
-
suite_id: suiteId
|
|
91
|
-
|
|
88
|
+
title,
|
|
89
|
+
time,
|
|
90
|
+
example,
|
|
91
|
+
error: formattedError,
|
|
92
|
+
files,
|
|
93
|
+
suite_title: suiteTitle,
|
|
94
|
+
test_id: testId,
|
|
95
|
+
suite_id: suiteId,
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
await Promise.all(addSpecTestsPromises);
|
package/lib/adapter/mocha.js
CHANGED
|
@@ -4,6 +4,7 @@ const chalk = require('chalk');
|
|
|
4
4
|
const TestomatClient = require('../client');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
6
6
|
const { parseTest, fileSystem } = require('../utils/utils');
|
|
7
|
+
const { TESTOMATIO } = require('../config');
|
|
7
8
|
|
|
8
9
|
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
|
|
9
10
|
Mocha.Runner.constants;
|
|
@@ -15,7 +16,7 @@ function MochaReporter(runner, opts) {
|
|
|
15
16
|
let skipped = 0;
|
|
16
17
|
// let artifactStore;
|
|
17
18
|
|
|
18
|
-
const apiKey = opts?.reporterOptions?.apiKey ||
|
|
19
|
+
const apiKey = opts?.reporterOptions?.apiKey || TESTOMATIO;
|
|
19
20
|
|
|
20
21
|
const client = new TestomatClient({ apiKey });
|
|
21
22
|
|
package/lib/bin/startTest.js
CHANGED
|
@@ -5,6 +5,7 @@ const chalk = require('chalk');
|
|
|
5
5
|
const TestomatClient = require('../client');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
7
7
|
const { version } = require('../../package.json');
|
|
8
|
+
const { TESTOMATIO } = require('../config');
|
|
8
9
|
|
|
9
10
|
console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ program
|
|
|
20
21
|
|
|
21
22
|
if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
|
|
22
23
|
|
|
23
|
-
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] ||
|
|
24
|
+
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || TESTOMATIO;
|
|
24
25
|
const title = process.env.TESTOMATIO_TITLE;
|
|
25
26
|
|
|
26
27
|
if (launch) {
|
package/lib/config.js
CHANGED
|
@@ -8,6 +8,10 @@ const debug = require('debug')('@testomatio/reporter:config');
|
|
|
8
8
|
const dotenv = require('dotenv');
|
|
9
9
|
const envFileVars = dotenv.config({ path: '.env' }).parsed; */
|
|
10
10
|
|
|
11
|
+
const TESTOMATIO = process.env.TESTOMATIO || process.env.TESTOMATIO_API_KEY || process.env.TESTOMATIO_TOKEN || '';
|
|
12
|
+
if (TESTOMATIO === 'undefined') console.error('TESTOMATIO is "undefined". Something went wrong. Contact dev team.');
|
|
13
|
+
process.env.TESTOMATIO = TESTOMATIO;
|
|
14
|
+
|
|
11
15
|
// select only TESTOMATIO related variables (only to print them in debug)
|
|
12
16
|
const testomatioEnvVars =
|
|
13
17
|
Object.keys(process.env)
|
|
@@ -22,3 +26,4 @@ debug('TESTOMATIO variables:', testomatioEnvVars);
|
|
|
22
26
|
const config = process.env;
|
|
23
27
|
|
|
24
28
|
module.exports = config;
|
|
29
|
+
module.exports.TESTOMATIO = TESTOMATIO;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -5,6 +5,7 @@ const JsonCycle = require('json-cycle');
|
|
|
5
5
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
6
6
|
const { isValidUrl, foundedTestLog } = require('../utils/utils');
|
|
7
7
|
const { parseFilterParams, generateFilterRequestParams, setS3Credentials, } = require('../utils/pipe_utils');
|
|
8
|
+
const { TESTOMATIO } = require('../config');
|
|
8
9
|
|
|
9
10
|
if (process.env.TESTOMATIO_RUN) {
|
|
10
11
|
process.env.runId = process.env.TESTOMATIO_RUN;
|
|
@@ -20,7 +21,7 @@ class TestomatioPipe {
|
|
|
20
21
|
constructor(params, store) {
|
|
21
22
|
this.isEnabled = false;
|
|
22
23
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
23
|
-
this.apiKey = params.apiKey ||
|
|
24
|
+
this.apiKey = params.apiKey || TESTOMATIO;
|
|
24
25
|
debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
25
26
|
if (!this.apiKey) {
|
|
26
27
|
return;
|
package/lib/utils/utils.js
CHANGED
|
@@ -141,7 +141,12 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
141
141
|
// remove special chars from title
|
|
142
142
|
if (!lineIndex && opts.title) {
|
|
143
143
|
const title = opts.title.replace(/[([@].*/g, '');
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
if (opts.lang === 'java') {
|
|
146
|
+
lineIndex = lines.findIndex(l => l.includes(`test${title}`));
|
|
147
|
+
} else {
|
|
148
|
+
lineIndex = lines.findIndex(l => l.includes(title));
|
|
149
|
+
}
|
|
145
150
|
}
|
|
146
151
|
|
|
147
152
|
if (opts.prepend) {
|
package/lib/xmlReader.js
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:xml');
|
|
2
|
-
const path = require(
|
|
2
|
+
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const fs = require(
|
|
5
|
-
const { XMLParser } = require(
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('./constants');
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const {
|
|
8
|
+
fetchFilesFromStackTrace,
|
|
9
|
+
fetchIdFromOutput,
|
|
10
|
+
fetchSourceCode,
|
|
11
|
+
fetchSourceCodeFromStackTrace,
|
|
12
|
+
fetchIdFromCode,
|
|
13
|
+
humanize,
|
|
14
|
+
} = require('./utils/utils');
|
|
14
15
|
const upload = require('./fileUploader');
|
|
15
16
|
const pipesFactory = require('./pipe');
|
|
16
17
|
const adapterFactory = require('./junit-adapter');
|
|
18
|
+
const { TESTOMATIO } = require('./config');
|
|
17
19
|
|
|
18
|
-
const TESTOMATIO_URL = process.env.TESTOMATIO_URL ||
|
|
19
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN
|
|
20
|
+
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
21
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
20
22
|
|
|
21
23
|
const options = {
|
|
22
24
|
ignoreDeclaration: true,
|
|
23
25
|
ignoreAttributes: false,
|
|
24
26
|
alwaysCreateTextNode: false,
|
|
25
|
-
attributeNamePrefix:
|
|
27
|
+
attributeNamePrefix: '',
|
|
26
28
|
parseTagValue: true,
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const reduceOptions = {};
|
|
30
32
|
|
|
31
33
|
class XmlReader {
|
|
32
|
-
|
|
33
34
|
constructor(opts = {}) {
|
|
34
35
|
this.requestParams = {
|
|
35
36
|
apiKey: opts.apiKey || TESTOMATIO,
|
|
@@ -39,18 +40,18 @@ class XmlReader {
|
|
|
39
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
40
41
|
};
|
|
41
42
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
42
|
-
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
|
|
43
|
+
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
43
44
|
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
44
45
|
|
|
45
46
|
this.opts = opts || {};
|
|
46
|
-
this.store = {}
|
|
47
|
+
this.store = {};
|
|
47
48
|
this.pipes = pipesFactory(opts, this.store);
|
|
48
49
|
|
|
49
50
|
this.parser = new XMLParser(options);
|
|
50
|
-
this.tests = []
|
|
51
|
-
this.stats = {}
|
|
51
|
+
this.tests = [];
|
|
52
|
+
this.stats = {};
|
|
52
53
|
this.stats.language = opts.lang?.toLowerCase();
|
|
53
|
-
this.filesToUpload = {}
|
|
54
|
+
this.filesToUpload = {};
|
|
54
55
|
|
|
55
56
|
this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
|
|
56
57
|
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
@@ -81,8 +82,8 @@ class XmlReader {
|
|
|
81
82
|
} else if (jsonResult.assemblies) {
|
|
82
83
|
return this.processXUnit(jsonResult.assemblies);
|
|
83
84
|
} else {
|
|
84
|
-
console.log(jsonResult)
|
|
85
|
-
throw new Error("Format can't be parsed")
|
|
85
|
+
console.log(jsonResult);
|
|
86
|
+
throw new Error("Format can't be parsed");
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
return this.processJUnit(jsonSuite);
|
|
@@ -95,7 +96,7 @@ class XmlReader {
|
|
|
95
96
|
const resultTests = processTestSuite(testsuite);
|
|
96
97
|
|
|
97
98
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
98
|
-
const status =
|
|
99
|
+
const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
|
|
99
100
|
|
|
100
101
|
this.tests = this.tests.concat(resultTests);
|
|
101
102
|
|
|
@@ -134,21 +135,22 @@ class XmlReader {
|
|
|
134
135
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
135
136
|
if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
|
|
136
137
|
|
|
137
|
-
const tests =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
138
|
+
const tests =
|
|
139
|
+
defs.map(td => {
|
|
140
|
+
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
141
|
+
let example = td.name.match(/\((.*?)\)/);
|
|
142
|
+
if (example) example = { ...example[1].split(',') };
|
|
143
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
144
|
+
const suite_title = suite.pop();
|
|
145
|
+
return {
|
|
146
|
+
title,
|
|
147
|
+
example,
|
|
148
|
+
file: suite.join('/'),
|
|
149
|
+
description: td.Description,
|
|
150
|
+
suite_title,
|
|
151
|
+
id: td.Execution.id,
|
|
152
|
+
};
|
|
153
|
+
}) || [];
|
|
152
154
|
|
|
153
155
|
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
154
156
|
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
@@ -159,10 +161,9 @@ class XmlReader {
|
|
|
159
161
|
run_time: parseFloat(td.duration) * 1000,
|
|
160
162
|
status: td.outcome,
|
|
161
163
|
stack: td.Output.StdOut,
|
|
162
|
-
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
164
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
163
165
|
}));
|
|
164
166
|
|
|
165
|
-
|
|
166
167
|
results.forEach(r => {
|
|
167
168
|
const test = tests.find(t => t.id === r.id) || {};
|
|
168
169
|
r.suite_title = test.suite_title;
|
|
@@ -222,7 +223,7 @@ class XmlReader {
|
|
|
222
223
|
|
|
223
224
|
if (testCase.failure) {
|
|
224
225
|
message = testCase.failure.message;
|
|
225
|
-
stack = testCase.failure['stack-trace']
|
|
226
|
+
stack = testCase.failure['stack-trace'];
|
|
226
227
|
}
|
|
227
228
|
if (testCase.reason) {
|
|
228
229
|
message = testCase.reason.message;
|
|
@@ -249,7 +250,6 @@ class XmlReader {
|
|
|
249
250
|
suite_title,
|
|
250
251
|
run_time,
|
|
251
252
|
});
|
|
252
|
-
|
|
253
253
|
});
|
|
254
254
|
});
|
|
255
255
|
});
|
|
@@ -282,12 +282,12 @@ class XmlReader {
|
|
|
282
282
|
passed_count: 0,
|
|
283
283
|
failed_count: 0,
|
|
284
284
|
skipped_count: 0,
|
|
285
|
-
}
|
|
285
|
+
};
|
|
286
286
|
this.tests.forEach(t => {
|
|
287
287
|
this.stats.tests_count++;
|
|
288
288
|
if (t.status === 'passed') this.stats.passed_count++;
|
|
289
289
|
if (t.status === 'failed') this.stats.failed_count++;
|
|
290
|
-
})
|
|
290
|
+
});
|
|
291
291
|
if (this.stats.failed_count) this.stats.status = 'failed';
|
|
292
292
|
|
|
293
293
|
return this.stats;
|
|
@@ -296,7 +296,7 @@ class XmlReader {
|
|
|
296
296
|
fetchSourceCode() {
|
|
297
297
|
this.tests.forEach(t => {
|
|
298
298
|
try {
|
|
299
|
-
const file = this.adapter.getFilePath(t)
|
|
299
|
+
const file = this.adapter.getFilePath(t);
|
|
300
300
|
if (!file) return;
|
|
301
301
|
|
|
302
302
|
if (!this.stats.language) {
|
|
@@ -309,16 +309,16 @@ class XmlReader {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
if (!fs.existsSync(file)) {
|
|
312
|
-
debug('Failed to open file with the source code', file)
|
|
312
|
+
debug('Failed to open file with the source code', file);
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
315
|
const contents = fs.readFileSync(file).toString();
|
|
316
|
-
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
|
|
316
|
+
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language });
|
|
317
317
|
if (t.code) debug('Fetched code for test %s', t.title);
|
|
318
|
-
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language })
|
|
318
|
+
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language });
|
|
319
319
|
if (t.test_id) debug('Fetched test id %s for test %s', t.test_id, t.title);
|
|
320
320
|
} catch (err) {
|
|
321
|
-
debug(err)
|
|
321
|
+
debug(err);
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
324
|
}
|
|
@@ -326,21 +326,22 @@ class XmlReader {
|
|
|
326
326
|
formatTests() {
|
|
327
327
|
this.tests.forEach(t => {
|
|
328
328
|
if (t.file) {
|
|
329
|
-
t.file = t.file.replace(process.cwd() + path.sep, '')
|
|
329
|
+
t.file = t.file.replace(process.cwd() + path.sep, '');
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
this.adapter.formatTest(t)
|
|
332
|
+
this.adapter.formatTest(t);
|
|
333
333
|
|
|
334
334
|
t.title = humanize(t.title);
|
|
335
335
|
});
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
formatErrors() {
|
|
339
|
-
this.tests
|
|
340
|
-
t
|
|
341
|
-
t
|
|
342
|
-
|
|
343
|
-
|
|
339
|
+
this.tests
|
|
340
|
+
.filter(t => !!t.stack)
|
|
341
|
+
.forEach(t => {
|
|
342
|
+
t.stack = this.formatStack(t);
|
|
343
|
+
t.message = this.adapter.formatMessage(t);
|
|
344
|
+
});
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
formatStack(t) {
|
|
@@ -358,7 +359,7 @@ class XmlReader {
|
|
|
358
359
|
async uploadArtifacts() {
|
|
359
360
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
360
361
|
let files = [];
|
|
361
|
-
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f))
|
|
362
|
+
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f));
|
|
362
363
|
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
363
364
|
|
|
364
365
|
if (!files.length) continue;
|
|
@@ -377,7 +378,7 @@ class XmlReader {
|
|
|
377
378
|
group_title: this.requestParams.group_title,
|
|
378
379
|
};
|
|
379
380
|
|
|
380
|
-
debug(
|
|
381
|
+
debug('Run', runParams);
|
|
381
382
|
|
|
382
383
|
return Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
383
384
|
}
|
|
@@ -390,12 +391,10 @@ class XmlReader {
|
|
|
390
391
|
this.formatErrors();
|
|
391
392
|
this.formatTests();
|
|
392
393
|
|
|
393
|
-
debug(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
tests: this.tests,
|
|
398
|
-
})
|
|
394
|
+
debug('Uploading data', {
|
|
395
|
+
...this.stats,
|
|
396
|
+
tests: this.tests,
|
|
397
|
+
});
|
|
399
398
|
|
|
400
399
|
const dataString = {
|
|
401
400
|
...this.stats,
|
|
@@ -410,59 +409,62 @@ class XmlReader {
|
|
|
410
409
|
|
|
411
410
|
module.exports = XmlReader;
|
|
412
411
|
|
|
413
|
-
|
|
414
412
|
function reduceTestCases(prev, item) {
|
|
415
413
|
let testCases = item.testcase;
|
|
416
414
|
if (!testCases) testCases = item['test-case'];
|
|
417
415
|
if (!Array.isArray(testCases)) {
|
|
418
|
-
testCases = [testCases]
|
|
416
|
+
testCases = [testCases];
|
|
419
417
|
}
|
|
420
418
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
421
419
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
422
|
-
testCases
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
420
|
+
testCases
|
|
421
|
+
.filter(t => !!t)
|
|
422
|
+
.forEach(testCaseItem => {
|
|
423
|
+
const file = testCaseItem.file || item.filepath || '';
|
|
424
|
+
|
|
425
|
+
let stack = '';
|
|
426
|
+
let message = '';
|
|
427
|
+
if (testCaseItem.error) stack = testCaseItem.error;
|
|
428
|
+
if (testCaseItem.failure) stack = testCaseItem.failure;
|
|
429
|
+
if (testCaseItem?.failure?.['stack-trace']) stack = testCaseItem.failure['stack-trace'];
|
|
430
|
+
if (testCaseItem?.failure?.message) message = testCaseItem.failure.message;
|
|
431
|
+
if (testCaseItem?.error?.message) message = testCaseItem.error.message;
|
|
432
|
+
|
|
433
|
+
if (testCaseItem.failure && testCaseItem.failure['#text']) stack = testCaseItem.failure['#text'];
|
|
434
|
+
if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
|
|
435
|
+
if (!message) message = stack.trim().split('\n')[0];
|
|
436
|
+
|
|
437
|
+
// eslint-disable-next-line
|
|
438
|
+
stack = `${
|
|
439
|
+
testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
|
|
440
|
+
}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
441
|
+
const testId = fetchIdFromOutput(stack);
|
|
442
|
+
|
|
443
|
+
let status = STATUS.PASSED.toString();
|
|
444
|
+
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
445
|
+
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
446
|
+
|
|
447
|
+
prev.push({
|
|
448
|
+
create: true,
|
|
449
|
+
file,
|
|
450
|
+
stack,
|
|
451
|
+
test_id: testId,
|
|
452
|
+
message,
|
|
453
|
+
line: testCaseItem.lineno,
|
|
454
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
455
|
+
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
456
|
+
status,
|
|
457
|
+
title: testCaseItem.name,
|
|
458
|
+
suite_title: reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname,
|
|
459
|
+
});
|
|
460
|
+
});
|
|
459
461
|
return prev;
|
|
460
462
|
}
|
|
461
463
|
|
|
462
464
|
function processTestSuite(testsuite) {
|
|
463
465
|
if (!testsuite) return [];
|
|
464
|
-
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite)
|
|
465
|
-
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite'])
|
|
466
|
+
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite);
|
|
467
|
+
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite']);
|
|
466
468
|
|
|
467
469
|
let suites = testsuite;
|
|
468
470
|
if (!Array.isArray(testsuite)) {
|
package/package.json
CHANGED