mocha-xray-junit-reporter 1.0.0
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 +169 -0
- package/index.js +201 -0
- package/package.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Mocha Xray JUnit Reporter
|
|
2
|
+
|
|
3
|
+
A Mocha reporter that generates Xray-compatible JUnit XML with proper CDATA sections for test properties. Perfect for integrating automated tests with Jira Xray test management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install mocha-xray-junit-reporter --save-dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Basic Usage
|
|
14
|
+
|
|
15
|
+
Update your `package.json`:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "mocha --reporter mocha-xray-junit-reporter --reporter-options output=./junit.xml ./test/**/*.test.js"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Writing Tests with Properties
|
|
26
|
+
|
|
27
|
+
Add custom properties to your tests for Xray integration:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
describe("User Authentication", function () {
|
|
31
|
+
it("should successfully login with valid credentials", async function () {
|
|
32
|
+
// Add test properties for Xray
|
|
33
|
+
this.test.properties = [
|
|
34
|
+
{
|
|
35
|
+
name: "test_description",
|
|
36
|
+
value: "PROJ-123 | Verifies that users can login with valid credentials"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "test_type",
|
|
40
|
+
value: "Automated"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "priority",
|
|
44
|
+
value: "High"
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Your test code
|
|
49
|
+
const result = await login(username, password);
|
|
50
|
+
expect(result.success).to.be.true;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Multiple Reporters
|
|
56
|
+
|
|
57
|
+
Use with other reporters (e.g., HTML report):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install mocha-multi-reporters mochawesome --save-dev
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Create `reporter-config.json`:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"reporterEnabled": "mocha-xray-junit-reporter, mochawesome",
|
|
68
|
+
"mocha-xray-junit-reporter": {
|
|
69
|
+
"output": "junit.xml"
|
|
70
|
+
},
|
|
71
|
+
"mochawesome": {
|
|
72
|
+
"reportDir": "html-report",
|
|
73
|
+
"reportFilename": "index.html"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Update test script:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"scripts": {
|
|
83
|
+
"test": "mocha --reporter mocha-multi-reporters --reporter-options configFile=reporter-config.json ./test/**/*.test.js"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Output Format
|
|
89
|
+
|
|
90
|
+
The reporter generates JUnit XML with proper CDATA sections:
|
|
91
|
+
|
|
92
|
+
```xml
|
|
93
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
94
|
+
<testsuites name="Mocha Tests" tests="1" failures="0" skipped="0" time="0.025">
|
|
95
|
+
<testsuite name="User Authentication" timestamp="2026-01-28T10:30:00.000Z" tests="1" failures="0" skipped="0" time="0.025">
|
|
96
|
+
<testcase name="should successfully login with valid credentials" classname="User Authentication" time="0.025">
|
|
97
|
+
<properties>
|
|
98
|
+
<property name="test_description">
|
|
99
|
+
<![CDATA[PROJ-123 | Verifies that users can login with valid credentials]]>
|
|
100
|
+
</property>
|
|
101
|
+
<property name="test_type">
|
|
102
|
+
<![CDATA[Automated]]>
|
|
103
|
+
</property>
|
|
104
|
+
<property name="priority">
|
|
105
|
+
<![CDATA[High]]>
|
|
106
|
+
</property>
|
|
107
|
+
</properties>
|
|
108
|
+
</testcase>
|
|
109
|
+
</testsuite>
|
|
110
|
+
</testsuites>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Configuration Options
|
|
114
|
+
|
|
115
|
+
Reporter options can be passed via `--reporter-options`:
|
|
116
|
+
|
|
117
|
+
| Option | Description | Default |
|
|
118
|
+
|--------|-------------|---------|
|
|
119
|
+
| `output` | Path to output XML file | `./junit.xml` |
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
mocha --reporter mocha-xray-junit-reporter --reporter-options output=./test-results/results.xml
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Xray Integration Tips
|
|
128
|
+
|
|
129
|
+
### Common Properties for Xray
|
|
130
|
+
|
|
131
|
+
- `test_description` - Test description (use format: `TICKET-ID | Description`)
|
|
132
|
+
- `test_type` - Test type (e.g., "Automated", "Manual")
|
|
133
|
+
- `test_key` - Existing Xray test key
|
|
134
|
+
- `requirement` - Requirement ticket ID
|
|
135
|
+
- `priority` - Test priority
|
|
136
|
+
|
|
137
|
+
## Troubleshooting
|
|
138
|
+
|
|
139
|
+
### Properties Not Showing in Xray
|
|
140
|
+
|
|
141
|
+
Make sure you're using the JUnit import endpoint in Xray, not the standard JUnit parser. Properties in CDATA sections require the Xray-specific importer.
|
|
142
|
+
|
|
143
|
+
### Special Characters in Properties
|
|
144
|
+
|
|
145
|
+
All property values are automatically wrapped in CDATA sections, so special characters (like `<`, `>`, `&`) are handled correctly.
|
|
146
|
+
|
|
147
|
+
## Contributing
|
|
148
|
+
|
|
149
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
150
|
+
|
|
151
|
+
1. Fork the repository
|
|
152
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
153
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
154
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
155
|
+
5. Open a Pull Request
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|
|
160
|
+
|
|
161
|
+
## Author
|
|
162
|
+
|
|
163
|
+
Created for the testing community to simplify Xray integration with Mocha tests.
|
|
164
|
+
|
|
165
|
+
## Related Projects
|
|
166
|
+
|
|
167
|
+
- [Mocha](https://mochajs.org/) - JavaScript test framework
|
|
168
|
+
- [Xray](https://www.getxray.app/) - Test management for Jira
|
|
169
|
+
- [Mochawesome](https://github.com/adamgruber/mochawesome) - HTML reporter for Mocha
|
package/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Mocha = require('mocha');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const Base = Mocha.reporters.Base;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Custom Mocha reporter for Xray-compatible JUnit XML
|
|
11
|
+
*/
|
|
12
|
+
function XrayJUnitReporter(runner, options) {
|
|
13
|
+
Base.call(this, runner);
|
|
14
|
+
|
|
15
|
+
var self = this;
|
|
16
|
+
var tests = [];
|
|
17
|
+
var suites = {};
|
|
18
|
+
var currentSuite = null;
|
|
19
|
+
|
|
20
|
+
// Get output file from options
|
|
21
|
+
var output = (options.reporterOptions && options.reporterOptions.output) || './junit.xml';
|
|
22
|
+
|
|
23
|
+
runner.on('suite', function(suite) {
|
|
24
|
+
if (suite.root) return;
|
|
25
|
+
currentSuite = suite.fullTitle();
|
|
26
|
+
if (!suites[currentSuite]) {
|
|
27
|
+
suites[currentSuite] = {
|
|
28
|
+
name: suite.fullTitle(),
|
|
29
|
+
tests: [],
|
|
30
|
+
timestamp: new Date().toISOString(),
|
|
31
|
+
file: suite.file
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
runner.on('pass', function(test) {
|
|
37
|
+
var testData = createTestData(test, 'passed');
|
|
38
|
+
suites[currentSuite].tests.push(testData);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
runner.on('fail', function(test, err) {
|
|
42
|
+
var testData = createTestData(test, 'failed', err);
|
|
43
|
+
suites[currentSuite].tests.push(testData);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
runner.on('pending', function(test) {
|
|
47
|
+
var testData = createTestData(test, 'skipped');
|
|
48
|
+
suites[currentSuite].tests.push(testData);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
runner.on('end', function() {
|
|
52
|
+
self.writeXml(suites, output);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createTestData(test, status, err) {
|
|
57
|
+
var testData = {
|
|
58
|
+
title: test.title,
|
|
59
|
+
fullTitle: test.fullTitle(),
|
|
60
|
+
duration: test.duration || 0,
|
|
61
|
+
status: status,
|
|
62
|
+
properties: {}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Extract custom properties from test context
|
|
66
|
+
if (test.properties && Array.isArray(test.properties)) {
|
|
67
|
+
test.properties.forEach(function(prop) {
|
|
68
|
+
testData.properties[prop.name] = prop.value;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (err) {
|
|
73
|
+
testData.error = {
|
|
74
|
+
message: err.message,
|
|
75
|
+
stack: err.stack,
|
|
76
|
+
type: err.name || 'Error'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return testData;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
XrayJUnitReporter.prototype.writeXml = function(suites, outputPath) {
|
|
84
|
+
var totalTests = 0;
|
|
85
|
+
var totalFailures = 0;
|
|
86
|
+
var totalSkipped = 0;
|
|
87
|
+
var totalTime = 0;
|
|
88
|
+
|
|
89
|
+
var lines = [];
|
|
90
|
+
lines.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
91
|
+
|
|
92
|
+
Object.keys(suites).forEach(function(suiteName) {
|
|
93
|
+
var suite = suites[suiteName];
|
|
94
|
+
var suiteTests = 0;
|
|
95
|
+
var suiteFailures = 0;
|
|
96
|
+
var suiteSkipped = 0;
|
|
97
|
+
var suiteTime = 0;
|
|
98
|
+
|
|
99
|
+
suite.tests.forEach(function(test) {
|
|
100
|
+
suiteTests++;
|
|
101
|
+
totalTests++;
|
|
102
|
+
var time = test.duration / 1000;
|
|
103
|
+
suiteTime += time;
|
|
104
|
+
totalTime += time;
|
|
105
|
+
|
|
106
|
+
if (test.status === 'failed') {
|
|
107
|
+
suiteFailures++;
|
|
108
|
+
totalFailures++;
|
|
109
|
+
}
|
|
110
|
+
if (test.status === 'skipped') {
|
|
111
|
+
suiteSkipped++;
|
|
112
|
+
totalSkipped++;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
lines.push(' <testsuite' +
|
|
117
|
+
' name="' + escapeXml(suite.name) + '"' +
|
|
118
|
+
' timestamp="' + suite.timestamp + '"' +
|
|
119
|
+
' tests="' + suiteTests + '"' +
|
|
120
|
+
' failures="' + suiteFailures + '"' +
|
|
121
|
+
' skipped="' + suiteSkipped + '"' +
|
|
122
|
+
' time="' + suiteTime.toFixed(3) + '"' +
|
|
123
|
+
(suite.file ? ' file="' + escapeXml(suite.file) + '"' : '') +
|
|
124
|
+
'>');
|
|
125
|
+
|
|
126
|
+
suite.tests.forEach(function(test) {
|
|
127
|
+
var time = test.duration / 1000;
|
|
128
|
+
|
|
129
|
+
lines.push(' <testcase' +
|
|
130
|
+
' name="' + escapeXml(test.title) + '"' +
|
|
131
|
+
' classname="' + escapeXml(suite.name) + '"' +
|
|
132
|
+
' time="' + time.toFixed(3) + '"' +
|
|
133
|
+
'>');
|
|
134
|
+
|
|
135
|
+
// Add properties with CDATA
|
|
136
|
+
if (Object.keys(test.properties).length > 0) {
|
|
137
|
+
lines.push(' <properties>');
|
|
138
|
+
Object.keys(test.properties).forEach(function(propName) {
|
|
139
|
+
lines.push(' <property name="' + escapeXml(propName) + '">');
|
|
140
|
+
lines.push(' <![CDATA[' + test.properties[propName] + ']]>');
|
|
141
|
+
lines.push(' </property>');
|
|
142
|
+
});
|
|
143
|
+
lines.push(' </properties>');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add failure element if test failed
|
|
147
|
+
if (test.error) {
|
|
148
|
+
lines.push(' <failure' +
|
|
149
|
+
' message="' + escapeXml(test.error.message) + '"' +
|
|
150
|
+
' type="' + escapeXml(test.error.type) + '"' +
|
|
151
|
+
'>');
|
|
152
|
+
lines.push(' <![CDATA[' + test.error.stack + ']]>');
|
|
153
|
+
lines.push(' </failure>');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Add skipped element if test was skipped
|
|
157
|
+
if (test.status === 'skipped') {
|
|
158
|
+
lines.push(' <skipped/>');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
lines.push(' </testcase>');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
lines.push(' </testsuite>');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
var xmlString = lines.join('\n');
|
|
168
|
+
|
|
169
|
+
// Wrap in testsuites tag
|
|
170
|
+
xmlString = '<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
171
|
+
'<testsuites' +
|
|
172
|
+
' name="Mocha Tests"' +
|
|
173
|
+
' tests="' + totalTests + '"' +
|
|
174
|
+
' failures="' + totalFailures + '"' +
|
|
175
|
+
' skipped="' + totalSkipped + '"' +
|
|
176
|
+
' time="' + totalTime.toFixed(3) + '"' +
|
|
177
|
+
'>\n' +
|
|
178
|
+
lines.slice(1).join('\n') + '\n' +
|
|
179
|
+
'</testsuites>';
|
|
180
|
+
|
|
181
|
+
// Ensure directory exists
|
|
182
|
+
var dir = path.dirname(outputPath);
|
|
183
|
+
if (!fs.existsSync(dir)) {
|
|
184
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fs.writeFileSync(outputPath, xmlString, 'utf8');
|
|
188
|
+
console.log('\nXray JUnit report written to:', outputPath);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
function escapeXml(str) {
|
|
192
|
+
if (!str) return '';
|
|
193
|
+
return String(str)
|
|
194
|
+
.replace(/&/g, '&')
|
|
195
|
+
.replace(/</g, '<')
|
|
196
|
+
.replace(/>/g, '>')
|
|
197
|
+
.replace(/"/g, ''')
|
|
198
|
+
.replace(/'/g, ''');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = XrayJUnitReporter;
|
package/package.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mocha-xray-junit-reporter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Mocha reporter that generates Xray-compatible JUnit XML with CDATA properties",
|
|
5
|
+
"author": "Gergo Adanyi <gergo.adanyi@gmail.com>",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/adanyi-gergo/mocha-xray-junit-reporter.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["mocha", "reporter", "xray", "junit", "xml", "jira", "test-automation"],
|
|
11
|
+
"license": "MIT"
|
|
12
|
+
}
|
|
13
|
+
|