Skip to content

Commit 497f50f

Browse files
XstoudiAugustinMauroybrunocrohJakobJingleheimer
authored
feat(mocha-to-node-test-runner): migrate mocha@8.x to node:test (v22, v24+) (#313)
Co-authored-by: Augustin Mauroy <97875033+AugustinMauroy@users.noreply.github.com> Co-authored-by: Bruno Rodrigues <swe@brunocroh.com> Co-authored-by: Jacob Smith <3012099+JakobJingleheimer@users.noreply.github.com>
1 parent d1ad539 commit 497f50f

26 files changed

Lines changed: 1034 additions & 1 deletion

File tree

package-lock.json

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

recipes/fs-access-mode-constants/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Handle DEP0176 via transforming imports of `fs.F_OK`, `fs.R_OK`, `fs.W_OK`, `fs.X_OK` from the root `fs` module to `fs.constants`.",
55
"type": "module",
66
"scripts": {
7-
"test": "npx codemod jssg test -l typescript ./src/workflow.ts ./tests"
7+
"test": "npx codemod jssg test --allow-fs -l typescript ./src/workflow.ts ./tests"
88
},
99
"repository": {
1010
"type": "git",
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Mocha to Node.js Test Runner
2+
3+
This migrates Mocha 8.x tests to Node.js (22.x, 24.x) test runner
4+
5+
## Features
6+
7+
- Automatically adds `node:test` imports/requires
8+
- Converts global `describe`, `it`, and hooks to imported versions
9+
- Transforms `done` callbacks to `(t, done)` signature
10+
- Converts `this.skip()` to `t.skip()`
11+
- Converts `this.timeout(N)` to `{ timeout: N }` options
12+
- Preserves function styles (doesn't convert between `function()` and arrow functions)
13+
- Supports both CommonJS and ESM
14+
15+
## Examples
16+
17+
### Example 1: Basic
18+
19+
```diff
20+
const assert = require('assert');
21+
+const { describe, it } = require('node:test');
22+
23+
describe('Array', function() {
24+
describe.skip('#indexOf()', function() {
25+
it('should return -1 when the value is not present', function() {
26+
const arr = [1, 2, 3];
27+
assert.strictEqual(arr.indexOf(4), -1);
28+
});
29+
});
30+
});
31+
```
32+
33+
### Example 2: Async
34+
35+
```diff
36+
const assert = require('assert');
37+
+const { describe, it } = require('node:test');
38+
describe('Async Test', function() {
39+
-it('should complete after a delay', async function(done) {
40+
+it('should complete after a delay', async function(t, done) {
41+
const result = await new Promise(resolve => setTimeout(() => resolve(42), 100));
42+
assert.strictEqual(result, 42);
43+
});
44+
});
45+
```
46+
47+
### Example 3: Hooks
48+
49+
```diff
50+
const assert = require('assert');
51+
const fs = require('fs');
52+
+const { describe, before, after, it } = require('node:test');
53+
describe('File System', () => {
54+
before(function() {
55+
fs.writeFileSync('test.txt', 'Hello, World!');
56+
});
57+
58+
after(() => {
59+
fs.unlinkSync('test.txt');
60+
});
61+
62+
it('should read the file', () => {
63+
const content = fs.readFileSync('test.txt', 'utf8');
64+
assert.strictEqual(content, 'Hello, World!');
65+
});
66+
});
67+
```
68+
69+
### Example 4: Done
70+
71+
```diff
72+
const assert = require('assert');
73+
+const { describe, it } = require('node:test');
74+
describe('Callback Test', function() {
75+
- it('should call done when complete', function(done) {
76+
+ it('should call done when complete', function(t, done) {
77+
setTimeout(() => {
78+
assert.strictEqual(1 + 1, 2);
79+
done();
80+
}, 100);
81+
});
82+
})
83+
```
84+
85+
### Example 5: Skipped
86+
87+
```diff
88+
const assert = require('assert');
89+
+const { describe, it } = require('node:test');
90+
describe('Skipped Test', () => {
91+
it.skip('should not run this test', () => {
92+
assert.strictEqual(1 + 1, 3);
93+
});
94+
- it('should also be skipped', () => {
95+
- this.skip();
96+
+ it('should also be skipped', (t) => {
97+
+ t.skip();
98+
assert.strictEqual(1 + 1, 3);
99+
});
100+
101+
- it('should also be skipped 2', (done) => {
102+
- this.skip();
103+
+ it('should also be skipped 2', (t, done) => {
104+
+ t.skip();
105+
assert.strictEqual(1 + 1, 3);
106+
});
107+
108+
- it('should also be skipped 3', x => {
109+
- this.skip();
110+
+ it('should also be skipped 3', (t, x) => {
111+
+ t.skip();
112+
assert.strictEqual(1 + 1, 3);
113+
});
114+
})
115+
```
116+
117+
### Example 6: Dynamic
118+
119+
```diff
120+
const assert = require('assert');
121+
+const { describe, it } = require('node:test');
122+
describe('Dynamic Tests', () => {
123+
const tests = [1, 2, 3];
124+
125+
tests.forEach((test) => {
126+
it(`should handle test ${test}`, () => {
127+
assert.strictEqual(test % 2, 0);
128+
});
129+
});
130+
});
131+
```
132+
133+
### Example 7: Timeouts
134+
135+
```diff
136+
const assert = require('assert');
137+
-describe('Timeout Test', function() {
138+
- this.timeout(500);
139+
+const { describe, it } = require('node:test');
140+
+describe('Timeout Test', { timeout: 500 }, function() {
141+
+
142+
+
143+
+ it('should complete within 100ms', { timeout: 100 }, (t, done) => {
144+
145+
- it('should complete within 100ms', (done) => {
146+
- this.timeout(100);
147+
setTimeout(done, 500); // This will fail
148+
});
149+
150+
- it('should complete within 200ms', function(done) {
151+
- this.timeout(200);
152+
+ it('should complete within 200ms', { timeout: 200 }, function(t, done) {
153+
+
154+
setTimeout(done, 100); // This will pass
155+
});
156+
});
157+
```
158+
159+
## Caveats
160+
161+
* `node:test` doesn't support the `retry` option that Mocha has, so any tests using that will need to be handled separately.
162+
163+
## References
164+
- [Node Test Runner](https://nodejs.org/api/test.html)
165+
- [Mocha](https://mochajs.org/)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
schema_version: "1.0"
2+
name: "@nodejs/mocha-to-node-test-runner"
3+
version: 1.0.0
4+
capabilities:
5+
- fs
6+
- child_process
7+
description: Migrate Mocha 8.x tests to Node.js (22.x, 24.x) test runner
8+
author: Xavier Stouder
9+
license: MIT
10+
workflow: workflow.yaml
11+
category: migration
12+
13+
targets:
14+
languages:
15+
- javascript
16+
- typescript
17+
18+
keywords:
19+
- transformation
20+
- migration
21+
- mocha
22+
- test
23+
24+
registry:
25+
access: public
26+
visibility: public
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@nodejs/mocha-to-node-test-runner",
3+
"version": "1.0.0",
4+
"description": "Migrate Mocha 8.x tests to Node.js (22.x, 24.x) test runner",
5+
"type": "module",
6+
"scripts": {
7+
"test": "npx codemod jssg test --strictness cst -l typescript ./src/workflow.ts"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/nodejs/userland-migrations.git",
12+
"directory": "recipes/mocha-to-node-test-runner",
13+
"bugs": "https://github.com/nodejs/userland-migrations/issues"
14+
},
15+
"author": "Richie McColl",
16+
"license": "MIT",
17+
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/mocha-to-node-test-runner/README.md",
18+
"devDependencies": {
19+
"@codemod.com/jssg-types": "^1.3.1"
20+
},
21+
"dependencies": {
22+
"@nodejs/codemod-utils": "*"
23+
}
24+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { Transform } from '@codemod.com/jssg-types/main';
2+
import type Json from '@codemod.com/jssg-types/langs/json';
3+
import removeDependencies from '@nodejs/codemod-utils/remove-dependencies';
4+
5+
const transform: Transform<Json> = async (root) => {
6+
return removeDependencies(['mocha', '@types/mocha'], {
7+
packageJsonPath: root.filename(),
8+
runInstall: false,
9+
persistFileWrite: false,
10+
});
11+
};
12+
13+
export default transform;

0 commit comments

Comments
 (0)