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.
Files changed (3) hide show
  1. package/README.md +169 -0
  2. package/index.js +201 -0
  3. 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, '&amp;')
195
+ .replace(/</g, '&lt;')
196
+ .replace(/>/g, '&gt;')
197
+ .replace(/"/g, '&apos;')
198
+ .replace(/'/g, '&apos;');
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
+