Skip to content

Commit 3bc6039

Browse files
committed
add reporter options (json, junit)
1 parent 94dd533 commit 3bc6039

File tree

9 files changed

+301
-31
lines changed

9 files changed

+301
-31
lines changed

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
1212
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
1313
"@typescript-eslint/no-var-requires": "off",
14-
"@typescript-eslint/no-explicit-any": "off"
14+
"@typescript-eslint/no-explicit-any": "off",
15+
"@typescript-eslint/explicit-module-boundary-types": "off"
1516
}
1617
};

package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
"@typescript-eslint/parser": "^3.8.0",
3737
"eslint": "^7.6.0",
3838
"prettier": "^2.0.5",
39-
"typescript": "^3.9.7"
39+
"typescript": "^3.9.7",
40+
"xmlbuilder2": "^2.4.0"
4041
},
4142
"dependencies": {
4243
"archiver": "^5.2.0",

src/commands/run.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Tunnel from '../utils/tunnel';
88
import ora from 'ora';
99
import chalk from 'chalk';
1010
import io from 'socket.io-client';
11+
import Reporter, { IFormatType } from '../utils/reporter';
1112

1213
interface Arguments {
1314
[x: string]: unknown;
@@ -17,6 +18,8 @@ interface Arguments {
1718
parallel?: number;
1819
e?: string;
1920
s?: string;
21+
r?: string;
22+
o?: string;
2023
}
2124

2225
interface ISocketData {
@@ -45,13 +48,18 @@ export default class RunProject {
4548
this.stopJob();
4649
if (this.config && this.config.run_settings.start_tunnel) {
4750
if (this.tunnel) {
48-
this.tunnel
49-
.stop()
50-
.then(() => {
51-
process.exit();
52-
})
53-
.catch(log.error);
54-
this.tunnel = undefined;
51+
try {
52+
this.tunnel
53+
.stop()
54+
.then(() => {
55+
process.exit();
56+
})
57+
.catch(log.error);
58+
this.tunnel = undefined;
59+
} catch (err) {
60+
log.error(chalk.white.bgRed.bold(err));
61+
process.exit();
62+
}
5563
}
5664
} else {
5765
process.exit();
@@ -230,6 +238,7 @@ export default class RunProject {
230238
}
231239

232240
this.registerCloseHandlers();
241+
let success = false;
233242

234243
try {
235244
const response = await this.uploader.start(zipFile);
@@ -238,22 +247,28 @@ export default class RunProject {
238247

239248
const poller = await this.poller.check(response.id, uploadSpinner);
240249
const testCases = this.parseTestCases(poller.runs);
241-
const success = this.parseSuccess(poller.runs);
250+
success = this.parseSuccess(poller.runs);
242251

243252
if (process.env.TESTINGBOT_CI && testCases.length > 0) {
244253
for (let i = 0; i < testCases.length; i++) {
245254
const testCase = testCases[i];
246255
console.log(`TestingBotSessionId=${testCase.sessionId}`);
247256
}
248257
}
249-
250-
process.exit(success === true ? 0 : 1);
251258
} catch (err) {
252-
log.error(chalk.white.bgRed.bold(err));
259+
if (err) {
260+
log.error(chalk.white.bgRed.bold(err));
261+
}
262+
} finally {
253263
if (config.run_settings.start_tunnel) {
254264
await this.tunnel.stop();
255265
}
256-
process.exit(1);
266+
if (this.argv.r && this.projectId) {
267+
const reporter = new Reporter(this.config, this.projectId, this.argv.o);
268+
await reporter.format(this.argv.r as IFormatType);
269+
}
270+
271+
process.exit(success === true ? 0 : 1);
257272
}
258273
}
259274
}

src/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ yargs
6767
type: 'string',
6868
default: undefined,
6969
},
70+
r: {
71+
alias: 'reporter',
72+
describe:
73+
'Runs a specific reporter. valid reporters: "json", "junit"',
74+
type: 'string',
75+
default: undefined,
76+
},
77+
o: {
78+
alias: 'reporter-options',
79+
describe: 'Options for a reporter',
80+
type: 'string',
81+
default: undefined,
82+
},
7083
})
7184
.help('help')
7285
.wrap(null).argv;

src/utils/formatters/junit.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { IReportOptions } from '../reporter';
2+
import fs from 'fs';
3+
import { create } from 'xmlbuilder2';
4+
const fsPromises = fs.promises;
5+
6+
export default class JUnitFormatter {
7+
private reportData: any;
8+
private reportOptions: IReportOptions;
9+
10+
constructor(reportData: any, reportOptions: IReportOptions) {
11+
this.reportData = JSON.parse(reportData.report);
12+
this.reportOptions = reportOptions;
13+
}
14+
15+
public async format(): Promise<any> {
16+
const doc = create().ele('testsuites', {
17+
name: 'Cypress Tests',
18+
tests: this.reportData.results.length,
19+
});
20+
21+
for (let i = 0; i < this.reportData.results.length; i++) {
22+
for (let j = 0; j < this.reportData.results[i].suites.length; j++) {
23+
const suite = this.reportData.results[i].suites[j];
24+
const failureCount = suite.tests.filter((index: number, value: any) => {
25+
return value.pass === true;
26+
}).length;
27+
const suiteElement = doc.root().ele('testsuite', {
28+
name: suite.title,
29+
timestamp: new Date().toUTCString(),
30+
tests: suite.tests.length,
31+
failures: failureCount,
32+
});
33+
34+
for (let k = 0; k < suite.tests.length; k++) {
35+
const testCase = suite.tests[k];
36+
const testElement = suiteElement.ele('testcase', {
37+
name: testCase.fullTitle,
38+
classname: testCase.fullTitle,
39+
time: testCase.duration / 1000,
40+
});
41+
42+
if (testCase.err && testCase.err.message) {
43+
testElement.ele('failure', {
44+
message: testCase.err.message,
45+
});
46+
}
47+
48+
if (testCase.err && testCase.err.message) {
49+
testElement.ele('failure', {
50+
message: testCase.err.message,
51+
});
52+
}
53+
}
54+
}
55+
}
56+
57+
const xml = doc.end({ prettyPrint: true });
58+
59+
let outputFileName = 'test-results.xml';
60+
if (this.reportOptions && this.reportOptions.mochaFile) {
61+
outputFileName = this.reportOptions.mochaFile;
62+
}
63+
64+
await fsPromises.writeFile(outputFileName, xml);
65+
66+
return this.reportData;
67+
}
68+
}

src/utils/poller.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ interface IPollResponse {
3535
version: string;
3636
build?: string;
3737
build_id?: number;
38+
success: boolean;
3839
}
3940

4041
export default class Poller {
@@ -58,25 +59,20 @@ export default class Poller {
5859
const status = this.getStatus(response);
5960

6061
if (status === IStatus.DONE) {
61-
const errors = this.getErrors(response);
62-
if (errors.length === 0) {
62+
response.success = this.isSuccess(response);
63+
if (response.success) {
6364
spinner.succeed(
6465
'Cypress Project has finished running on TestingBot',
6566
);
66-
if (this.intervalId) {
67-
clearInterval(this.intervalId);
68-
this.intervalId = undefined;
69-
}
70-
return resolve(response);
67+
} else {
68+
spinner.fail('Cypress Project has finished running on TestingBot');
7169
}
7270

73-
spinner.fail('Cypress Project has finished running on TestingBot');
74-
7571
if (this.intervalId) {
7672
clearInterval(this.intervalId);
7773
this.intervalId = undefined;
7874
}
79-
return reject(errors);
75+
return resolve(response);
8076
}
8177

8278
if (
@@ -120,16 +116,14 @@ View results on https://testingbot.com/members/builds/${response.build_id}`);
120116
});
121117
}
122118

123-
private getErrors(response: IPollResponse): string[] {
124-
const errors: string[] = [];
119+
private isSuccess(response: IPollResponse): boolean {
120+
let success = true;
125121
for (let i = 0; i < response.runs.length; i++) {
126122
const testRun = response.runs[i];
127-
if (testRun.errors.length > 0) {
128-
errors.concat(testRun.errors);
129-
}
123+
success = success && testRun.success;
130124
}
131125

132-
return errors;
126+
return success;
133127
}
134128

135129
private getStatus(response: IPollResponse): IStatus {

0 commit comments

Comments
 (0)