@reporters/junit 1.0.1 → 1.0.3

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.3](https://github.com/MoLow/reporters/compare/junit-v1.0.2...junit-v1.0.3) (2023-03-12)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **junit:** support more usecases ([72359c3](https://github.com/MoLow/reporters/commit/72359c3983334d7712c4d404a0ae297b9ef9e859))
9
+
10
+ ## [1.0.2](https://github.com/MoLow/reporters/compare/junit-v1.0.1...junit-v1.0.2) (2022-12-25)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * some package.json links ([c51a616](https://github.com/MoLow/reporters/commit/c51a61648e29f5baca539ded1b09c2af3f5e0a4a))
16
+
3
17
  ## [1.0.1](https://github.com/MoLow/reporters/compare/junit-v1.0.0...junit-v1.0.1) (2022-12-20)
4
18
 
5
19
 
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
- [![npm version](https://img.shields.io/npm/v/@reporters/github)](https://www.npmjs.com/package/@reporters/github) ![tests](https://github.com/MoLow/reporters/actions/workflows/test.yaml/badge.svg?branch=main)
1
+ [![npm version](https://img.shields.io/npm/v/@reporters/junit)](https://www.npmjs.com/package/@reporters/junit) ![tests](https://github.com/MoLow/reporters/actions/workflows/test.yaml/badge.svg?branch=main)
2
2
  # Junit Reporter
3
- A Junit reporter for `node:test`
3
+ A Junit reporter for `node:test`.
4
+ intendend for use with major CI tools like Jenkins, CircleCI, etc that consume Junit reports.
4
5
 
5
6
  ## Installation
6
7
 
@@ -22,3 +23,39 @@ node --test \
22
23
 
23
24
  ## Example
24
25
 
26
+ Ouput of the following test file:
27
+
28
+ ```js
29
+ const { describe, it } = require('node:test');
30
+
31
+ describe('tests', () => {
32
+ it('is ok', () => {});
33
+ it('fails', () => {
34
+ throw new Error('this is an error');
35
+ });
36
+ });
37
+ ```
38
+
39
+ ```xml
40
+ <testsuite name="tests" time="0.00239" disabled="0" errors="0" tests="2" failures="1" skipped="0" hostname="PC.localdomain">
41
+ <testcase name="is ok" time="0.00057" classname="test"></testcase>
42
+ <testcase name="fails" time="0.00017" classname="test" failure="this is an error">
43
+ <failure message="this is an error" type="testCodeFailure">
44
+ [Error [ERR_TEST_FAILURE]: this is an error] {
45
+ failureType: 'testCodeFailure',
46
+ cause: Error: this is an error
47
+ at Object.&lt;anonymous&gt; (/Users/test/reporters/tests/example.js:6:11)
48
+ at ItTest.runInAsyncScope (node:async_hooks:204:9)
49
+ at ItTest.run (node:internal/test_runner/test:547:25)
50
+ at Suite.processPendingSubtests (node:internal/test_runner/test:302:27)
51
+ at ItTest.postRun (node:internal/test_runner/test:632:19)
52
+ at ItTest.run (node:internal/test_runner/test:575:10)
53
+ at async Promise.all (index 0)
54
+ at async Suite.run (node:internal/test_runner/test:798:7),
55
+ code: 'ERR_TEST_FAILURE'
56
+ }
57
+ </failure>
58
+ </testcase>
59
+ </testsuite>
60
+ </testsuites>
61
+ ```
package/index.js CHANGED
@@ -1,4 +1,3 @@
1
- const assert = require('node:assert');
2
1
  const util = require('node:util');
3
2
  const { hostname } = require('node:os');
4
3
 
@@ -14,7 +13,7 @@ function escapeContent(s = '') {
14
13
 
15
14
  function treeToXML(tree) {
16
15
  if (typeof tree === 'string') {
17
- return escapeContent(tree);
16
+ return `${escapeContent(tree)}\n`;
18
17
  }
19
18
  const {
20
19
  tag, props, nesting, children,
@@ -22,12 +21,12 @@ function treeToXML(tree) {
22
21
  const propsString = Object.entries(props)
23
22
  .map(([key, value]) => `${key}="${escapeProperty(String(value))}"`)
24
23
  .join(' ');
25
- const indent = ' '.repeat(nesting + 1);
26
- const newLine = children?.length ? '\n' : '';
27
- const postIndent = newLine ? indent : '';
28
- const postNewLine = newLine ? '' : '\n';
29
- const childrenString = `${newLine}${(children ?? []).map(treeToXML).join('')}${newLine}${postIndent}`;
30
- return `${indent}<${tag} ${propsString}>${childrenString}</${tag}>${postNewLine}`;
24
+ const indent = '\t'.repeat(nesting + 1);
25
+ if (!children?.length) {
26
+ return `${indent}<${tag} ${propsString}/>\n`;
27
+ }
28
+ const childrenString = `${(children ?? []).map(treeToXML).join('')}`;
29
+ return `${indent}<${tag} ${propsString}>\n${childrenString}${indent}</${tag}>\n`;
31
30
  }
32
31
 
33
32
  function isFailure(node) {
@@ -42,27 +41,38 @@ module.exports = async function* junitReporter(source) {
42
41
  yield '<?xml version="1.0" encoding="utf-8"?>\n';
43
42
  yield '<testsuites>\n';
44
43
  let currentSuite = null;
44
+
45
+ function startTest(event) {
46
+ const originalSuite = currentSuite;
47
+ currentSuite = {
48
+ props: { name: event.data.name },
49
+ nesting: event.data.nesting,
50
+ parent: currentSuite,
51
+ children: [],
52
+ };
53
+ originalSuite?.children.push(currentSuite);
54
+ }
55
+
45
56
  for await (const event of source) {
46
57
  switch (event.type) {
47
58
  case 'test:start': {
48
- const originalSuite = currentSuite;
49
- currentSuite = {
50
- props: { name: event.data.name },
51
- nesting: event.data.nesting,
52
- parent: currentSuite,
53
- children: [],
54
- };
55
- originalSuite?.children.push(currentSuite);
59
+ startTest(event);
56
60
  break;
57
61
  }
58
62
  case 'test:pass':
59
63
  case 'test:fail': {
64
+ if (!currentSuite) {
65
+ startTest({ data: { name: 'root', nesting: 0 } });
66
+ }
67
+ if (currentSuite.props.name !== event.data.name
68
+ || currentSuite.nesting !== event.data.nesting) {
69
+ startTest(event);
70
+ }
60
71
  const currentTest = currentSuite;
61
72
  if (currentSuite?.nesting === event.data.nesting && currentSuite?.parent) {
62
73
  currentSuite = currentSuite.parent;
63
74
  }
64
- assert.strictEqual(currentTest.props.name, event.data.name);
65
- currentTest.props.time = (event.data.details.duration_ms / 1000).toFixed(5);
75
+ currentTest.props.time = (event.data.details.duration_ms / 1000).toFixed(6);
66
76
  if (currentTest.children.length > 0) {
67
77
  currentTest.tag = 'testsuite';
68
78
  currentTest.props.disabled = 0;
@@ -73,19 +83,19 @@ module.exports = async function* junitReporter(source) {
73
83
  currentTest.props.hostname = HOSTNAME;
74
84
  } else {
75
85
  currentTest.tag = 'testcase';
76
- currentTest.props.classname = 'test';
86
+ currentTest.props.classname = event.data.classname ?? 'test';
77
87
  if (event.data.skip) {
78
- currentTest.children.push({ nesting: event.data.nesting + 1, tag: 'skipped', props: { message: event.data.skip } });
88
+ currentTest.children.push({ nesting: event.data.nesting + 1, tag: 'skipped', props: { type: 'skipped', message: event.data.skip } });
79
89
  }
80
90
  if (event.data.todo) {
81
- currentTest.children.push({ nesting: event.data.nesting + 1, tag: 'skipped', props: { message: event.data.todo } });
91
+ currentTest.children.push({ nesting: event.data.nesting + 1, tag: 'skipped', props: { type: 'todo', message: event.data.todo } });
82
92
  }
83
93
  if (event.type === 'test:fail') {
84
94
  const error = event.data.details?.error;
85
95
  currentTest.children.push({
86
96
  nesting: event.data.nesting + 1,
87
97
  tag: 'failure',
88
- props: { message: error?.message ?? '', type: error.failureType || error.code },
98
+ props: { type: error?.failureType || error?.code, message: error?.message ?? '' },
89
99
  children: [util.inspect(
90
100
  event.data.details?.error,
91
101
  { colors: false, breakLength: Infinity },
@@ -104,5 +114,5 @@ module.exports = async function* junitReporter(source) {
104
114
  }
105
115
  }
106
116
  yield treeToXML(currentSuite);
107
- yield '\n</testsuites>\n';
117
+ yield '</testsuites>\n';
108
118
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reporters/junit",
3
- "version": "1.0.1",
4
- "description": "A junit reporter for `node:test`",
3
+ "version": "1.0.3",
4
+ "description": "A jUnit reporter for `node:test`",
5
5
  "keywords": [
6
6
  "junit",
7
7
  "node:test",
@@ -10,9 +10,13 @@
10
10
  "reporters"
11
11
  ],
12
12
  "scripts": {
13
- "test": "echo ''"
13
+ "test": "node --test-reporter=spec --test-reporter-destination=stdout --test-reporter=../github/index.js --test-reporter-destination=stdout --test"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/MoLow/reporters/issues"
14
17
  },
15
18
  "main": "index.js",
19
+ "homepage": "https://github.com/MoLow/reporters/tree/main/packages/junit",
16
20
  "repository": "https://github.com/MoLow/reporters.git",
17
21
  "author": "Moshe Atlow",
18
22
  "license": "MIT"
@@ -0,0 +1,9 @@
1
+ const { spawnSync } = require('child_process');
2
+ const assert = require('assert');
3
+ const { compareLines } = require('../../../tests/utils');
4
+ const output = require('./output');
5
+
6
+ const child = spawnSync(process.execPath, ['--test-reporter', './index.js', '../../tests/example']);
7
+
8
+ assert.strictEqual(child.stderr?.toString(), '');
9
+ compareLines(child.stdout?.toString(), output.stdout);
@@ -0,0 +1,27 @@
1
+ module.exports = {
2
+ stdout: `<\\?xml version="1.0" encoding="utf-8"\\?>
3
+ <testsuites>
4
+ \t<testsuite name="tests" time=".*" disabled="0" errors="0" tests="2" failures="1" skipped="0" hostname=".*">
5
+ \t\t<testcase name="is ok" time=".*" classname="test"/>
6
+ \t\t<testcase name="fails" time=".*" classname="test" failure="this is an error">
7
+ \t\t\t<failure type="testCodeFailure" message="this is an error">
8
+ \\[Error \\[ERR_TEST_FAILURE\\]: this is an error\\] {
9
+ failureType: 'testCodeFailure',
10
+ cause: Error: this is an error
11
+ at Object.&lt;anonymous&gt; (.*)
12
+ .*
13
+ .*
14
+ .*
15
+ .*
16
+ .*
17
+ .*
18
+ .*
19
+ .*
20
+ code: 'ERR_TEST_FAILURE'
21
+ }
22
+ \t\t\t</failure>
23
+ \t\t</testcase>
24
+ \t</testsuite>
25
+ </testsuites>
26
+ `,
27
+ };