@reporters/mocha 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 +32 -0
  2. package/index.js +237 -0
  3. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ [![npm version](https://img.shields.io/npm/v/@reporters/mocha)](https://www.npmjs.com/package/@reporters/mocha) ![tests](https://github.com/MoLow/reporters/actions/workflows/test.yaml/badge.svg?branch=main) [![codecov](https://codecov.io/gh/MoLow/reporters/branch/main/graph/badge.svg?token=0LFVC8SCQV)](https://codecov.io/gh/MoLow/reporters)
2
+
3
+ # Mocha reporters for `node:test`
4
+ Use this custom reporter to use [mocha reporters](https://mochajs.org/#reporters) with `node:test`.
5
+ Both built-in and custom mocha reporters such as [mochawesome](https://www.npmjs.com/package/mochawesome) are supported.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install --save-dev @reporters/mocha
11
+ ```
12
+ or
13
+ ```bash
14
+ yarn add --dev @reporters/mocha
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Specify the desired mocha reporter inside the [mocha configuration file](https://mochajs.org/#configuring-mocha-nodejs), e.g. `.mocharc.js`:
20
+ ```js
21
+ module.exports = {
22
+ reporter: '@reporters/mocha'
23
+ }
24
+ ```
25
+
26
+ Then run the tests with `node:test`:
27
+
28
+ ```bash
29
+ node --test --test-reporter=@reporters/mocha
30
+ ```
31
+
32
+
package/index.js ADDED
@@ -0,0 +1,237 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ /* eslint-disable no-underscore-dangle */
3
+ /* eslint-disable max-classes-per-file */
4
+
5
+ 'use strict';
6
+
7
+ const { EventEmitter } = require('node:events');
8
+ const Mocha = require('mocha');
9
+ const { loadOptions } = require('mocha/lib/cli');
10
+
11
+ const {
12
+ EVENT_RUN_BEGIN,
13
+ EVENT_RUN_END,
14
+ EVENT_TEST_FAIL,
15
+ EVENT_TEST_PENDING,
16
+ EVENT_TEST_PASS,
17
+ EVENT_TEST_BEGIN,
18
+ EVENT_TEST_END,
19
+ EVENT_SUITE_BEGIN,
20
+ EVENT_SUITE_END,
21
+ } = Mocha.Runner.constants;
22
+
23
+ const {
24
+ STATE_FAILED,
25
+ STATE_PASSED,
26
+ STATE_PENDING,
27
+ } = Mocha.Runnable.constants;
28
+
29
+ class Test {
30
+ #mochaOptions;
31
+
32
+ constructor(parent = null, mochaOptions = {}) {
33
+ this.title = '';
34
+ this.file = '';
35
+ this.pending = false;
36
+ this.duration = 0;
37
+ this.err = null;
38
+ this.nesting = 0;
39
+ this.parent = parent;
40
+ this.root = !parent;
41
+ this.children = [];
42
+ this.#mochaOptions = mochaOptions;
43
+
44
+ this.body = '';
45
+ this._beforeAll = [];
46
+ this._beforeEach = [];
47
+ this._afterAll = [];
48
+ this._afterEach = [];
49
+ }
50
+
51
+ applyTestEvent(event, passed) {
52
+ this.title = event.data.name;
53
+ this.file = event.data.file;
54
+ this.pending = Boolean(event.data.skip || event.data.todo);
55
+ this.duration = event.data.details?.duration_ms;
56
+ this.err = event.data.details?.error;
57
+ this.passed = passed;
58
+ this.nesting = event.data.nesting;
59
+ }
60
+
61
+ get state() {
62
+ if (this.pending) {
63
+ return STATE_PENDING;
64
+ }
65
+ if (this.passed) {
66
+ return STATE_PASSED;
67
+ }
68
+ return STATE_FAILED;
69
+ }
70
+
71
+ isPending() {
72
+ return this.pending;
73
+ }
74
+
75
+ slow() {
76
+ return this.#mochaOptions.slow ?? 75;
77
+ }
78
+
79
+ currentRetry() {
80
+ return 0;
81
+ }
82
+
83
+ titlePath() {
84
+ const parentTitle = this.parent?.titlePath() ?? [];
85
+ return parentTitle.concat(this.title);
86
+ }
87
+
88
+ fullTitle() {
89
+ return this.titlePath().join(' ');
90
+ }
91
+
92
+ finalize() {
93
+ this.suites = this._suites;
94
+ this.tests = this._tests;
95
+ }
96
+
97
+ get _suites() {
98
+ return this.children.filter((child) => child.children.length > 0);
99
+ }
100
+
101
+ get _tests() {
102
+ return this.children.filter((child) => child.children.length === 0);
103
+ }
104
+ }
105
+
106
+ class Runner extends EventEmitter {
107
+ stats = {
108
+ suites: 0,
109
+ tests: 0,
110
+ passes: 0,
111
+ pending: 0,
112
+ failures: 0,
113
+ start: new Date(),
114
+ end: null,
115
+ duration: null,
116
+ };
117
+
118
+ #reporter;
119
+
120
+ #root = new Test();
121
+
122
+ #current = this.#root;
123
+
124
+ #mochaOptions = loadOptions([]);
125
+
126
+ async init() {
127
+ const reporterName = this.#mochaOptions.reporter ?? 'spec';
128
+ let Reporter;
129
+ if (typeof reporterName === 'function') {
130
+ Reporter = reporterName;
131
+ } else if (Mocha.reporters[reporterName]) {
132
+ Reporter = Mocha.reporters[reporterName];
133
+ } else {
134
+ try {
135
+ Reporter = await import(reporterName).then((m) => m.default || m);
136
+ } catch (err) {
137
+ console.error(err);
138
+ }
139
+ }
140
+ if (!Reporter) {
141
+ console.error(new Error(`invalid reporter "${reporterName}"`));
142
+ return;
143
+ }
144
+ this.#reporter = new Reporter(this, this.#mochaOptions);
145
+ this.emit(EVENT_RUN_BEGIN);
146
+ }
147
+
148
+ end() {
149
+ if (!this.#reporter) {
150
+ return;
151
+ }
152
+ this.stats.end = new Date();
153
+ this.stats.duration = this.stats.end - this.stats.start;
154
+ this.#report();
155
+ this.emit(EVENT_RUN_END);
156
+ if (typeof this.#reporter.done === 'function') {
157
+ this.#reporter.done(this.stats.failures, () => {});
158
+ }
159
+ }
160
+
161
+ #report(suite = this.#root) {
162
+ /* Not like mocha, node:test runs tests as soon as they are encountered
163
+ so the exact structure is unkown until all suites end */
164
+ this.emit(EVENT_SUITE_BEGIN, suite);
165
+ suite.finalize();
166
+ for (const s of suite.suites) {
167
+ this.#report(s);
168
+ }
169
+ for (const test of suite.tests) {
170
+ this.emit(EVENT_TEST_BEGIN, test);
171
+ if (test.pending) {
172
+ this.emit(EVENT_TEST_PENDING, test);
173
+ } else if (!test.passed) {
174
+ this.emit(EVENT_TEST_FAIL, test, test.err);
175
+ } else {
176
+ this.emit(EVENT_TEST_PASS, test);
177
+ }
178
+ this.emit(EVENT_TEST_END, test);
179
+ }
180
+ this.emit(EVENT_SUITE_END, suite);
181
+ }
182
+
183
+ addChild(event, passed) {
184
+ const current = this.#current;
185
+ this.#current = new Test(current);
186
+ this.#current.applyTestEvent(event, passed);
187
+ current.children.push(this.#current);
188
+ }
189
+
190
+ isNewTest(event) {
191
+ return this.#current.title !== event.data.name || this.#current.nesting !== event.data.nesting;
192
+ }
193
+
194
+ childCompleted(event, passed) {
195
+ this.#current.applyTestEvent(event, passed);
196
+ if (this.#current?.nesting === event.data.nesting) {
197
+ if (this.#current.children.length > 0) {
198
+ this.stats.suites += 1;
199
+ } else if (this.#current.pending) {
200
+ this.stats.tests += 1;
201
+ this.stats.pending += 1;
202
+ } else if (!this.#current.passed) {
203
+ this.stats.tests += 1;
204
+ this.stats.failures += 1;
205
+ } else {
206
+ this.stats.tests += 1;
207
+ this.stats.passes += 1;
208
+ }
209
+ this.#current = this.#current.parent;
210
+ }
211
+ }
212
+ }
213
+
214
+ module.exports = async function mochaReporter(source) {
215
+ const runner = new Runner();
216
+ await runner.init();
217
+
218
+ for await (const event of source) {
219
+ switch (event.type) {
220
+ case 'test:start':
221
+ runner.addChild(event, false);
222
+ break;
223
+ case 'test:pass':
224
+ case 'test:fail': {
225
+ if (runner.isNewTest(event)) {
226
+ runner.addChild(event, event.type === 'test:pass');
227
+ }
228
+ runner.childCompleted(event, event.type === 'test:pass');
229
+ break;
230
+ }
231
+ default:
232
+ break;
233
+ }
234
+ }
235
+
236
+ runner.end();
237
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@reporters/mocha",
3
+ "version": "1.0.0",
4
+ "description": "use any mocha reporter with `node:test`",
5
+ "type": "commonjs",
6
+ "keywords": [
7
+ "node:test",
8
+ "test",
9
+ "mocha",
10
+ "reporter",
11
+ "reporters"
12
+ ],
13
+ "files": [
14
+ "./index.js"
15
+ ],
16
+ "scripts": {
17
+ "test": "node --test-reporter=spec --test-reporter-destination=stdout --test-reporter=../github/index.js --test-reporter-destination=stdout --test"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/MoLow/reporters/issues"
21
+ },
22
+ "main": "index.js",
23
+ "homepage": "https://github.com/MoLow/reporters/tree/main/packages/mocha",
24
+ "repository": "https://github.com/MoLow/reporters.git",
25
+ "author": "Moshe Atlow",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "mocha": "^10.3.0"
29
+ }
30
+ }