Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,354 changes: 1,351 additions & 3 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions recipes/jest-to-node-test/.codemodrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://codemod-utils.s3.us-west-1.amazonaws.com/configuration_schema.json",
"name": "jest-to-node-test-runner",
"description": "Convert Jest tests to Node Test Runner tests",
"version": "1.0.0",
"engine": "ast-grep",
"private": false,
"arguments": [],
"meta": {
"git": "https://github.com/nodejs/userland-migrations/tree/main/recipe/jest-to-node-test-runner",
"tags": ["jest", "import", "node", "typescript"]
}
}
20 changes: 20 additions & 0 deletions recipes/jest-to-node-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Jest to Node Test

This package transforms Jest test files into `node:test` files.

This is a one-and-done process, and the updated source-code should be committed to your version control (eg git); thereafter, source-code import statements should be authored compliant with the ECMAScript (JavaScript) standard.

## TODO:

- Remove `jest`, `@types/jest`, `@jest/globals` and related modules from `package.json`
- Check/install `expect`
- Update `package.json` scripts that use `jest`
- remove `jest` config files
- remove `jest` config from `package.json` and `tsconfig.json`
- Migrate snapshots ✅
- Convert mocks ✅
- Convert setup/teardown files
- Migrate assertions
- Migrate test/it config (e.g. timeout)
- Migrate suite methods (e.g. `it.each`)
- Check non-TS CJS/ESM support
43 changes: 43 additions & 0 deletions recipes/jest-to-node-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "jest-to-node-test",
"version": "1.0.0-rc.1",
"description": "Convert Jest tests to node:test tests",
"type": "module",
"main": "./src/workflow.ts",
"engines": {
"node": ">=22.6.0"
},
"scripts": {
"start": "node --no-warnings --experimental-import-meta-resolve --experimental-strip-types ./src/workflow.ts",
"test": "node --no-warnings --experimental-import-meta-resolve --experimental-test-module-mocks --experimental-test-snapshots --experimental-strip-types --import='../../test/snapshots.ts' --test --experimental-test-coverage --test-coverage-include='src/**/*' --test-coverage-exclude='**/*.test.ts' './**/*.test.ts'"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nodejs/userland-migrations.git",
"directory": "recipes/jest-to-node-test-runner"
},
"files": [
"README.md",
".codemodrc.json",
"bundle.js"
],
"keywords": [
"codemod",
"esm",
"typescript"
],
"author": "Carlos Espa",
"license": "MIT",
"bugs": {
"url": "https://github.com/nodejs/userland-migrations/issues"
},
"homepage": "https://github.com/nodejs/userland-migrations/tree/main/jest-to-node-test-runner#readme",
"devDependencies": {
"@jest/globals": "^29.7.0",
"@types/node": "^22.15.5",
"expect": "^29.7.0"
},
"dependencies": {
"@ast-grep/napi": "^0.38.1"
}
}
125 changes: 125 additions & 0 deletions recipes/jest-to-node-test/src/fixtures/e2e/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { describe, it, expect, jest } from "@jest/globals";

describe("test", () => {
it("should be a test", () => {
const a = jest.fn((i: number, j: number) => i + j);
const b = jest.fn(async (i: number, j: number) => i - j);
jest.mock("./workflow.ts");

const logSpy = jest.spyOn(console, "log");
console.log("Hello, world!");

expect(logSpy.mock.calls[0][0]).toBe("Hello, world!");

a(1, 1);
a(2, 2);

expect(a).toHaveBeenCalled();
expect(a).toBeCalled();

expect(a).toHaveBeenCalledTimes(2);
expect(a).toBeCalledTimes(2);

expect(a).toHaveBeenCalledWith(1, 1);
expect(a).toBeCalledWith(1, 1);

expect(a).toHaveBeenLastCalledWith(2, 2);
expect(a).lastCalledWith(2, 2);

expect(a).toHaveBeenNthCalledWith(1, 1, 1);
expect(a).nthCalledWith(2, 2, 2);

expect(a).toHaveReturned();
expect(a).toReturn();

expect(a).toHaveReturnedTimes(2);
expect(a).toReturnTimes(2);

expect(a).toHaveReturnedWith(2);
expect(a).toReturnWith(4);

expect(a).toHaveLastReturnedWith(4);
expect(a).lastReturnedWith(4);

expect(a).toHaveNthReturnedWith(1, 2);
expect(a).nthReturnedWith(2, 4);

a.mock.calls;

a.mock.results;

// a.mock.instances;

// a.mock.contexts;

a.mock.lastCall;

a.mockClear();

a.mockReset();

a.mockRestore();

a.mockImplementation((i: number, j: number) => i * j);

a.mockImplementationOnce((i: number, j: number) => i - j);

// a.mockName("myMock");

// a.mockReturnThis();

a.mockReturnValue(42);

a.mockReturnValueOnce(42);

b.mockRejectedValue(new Error("Test error"));

b.mockRejectedValueOnce(new Error("Test error once"));

b.mockResolvedValue(42);

b.mockResolvedValueOnce(42);

expect({ a: 1 }).toMatchSnapshot();

expect({ b: 2 }).toMatchInlineSnapshot(`
{
"b": 2,
}
`);

jest.useFakeTimers();

jest.useRealTimers();

jest.runAllTicks();

jest.runAllTimers();

jest.runAllTimersAsync();

jest.runAllImmediates();

jest.advanceTimersByTime(1000);

jest.advanceTimersByTimeAsync(1000);

jest.runOnlyPendingTimers();

jest.runOnlyPendingTimersAsync();

// jest.advanceTimersToNextTimer(5);

// jest.advanceTimersToNextTimerAsync(5);

jest.clearAllTimers();

// jest.getTimerCount();

jest.now();

jest.setSystemTime(1000);

// jest.getRealSystemTime();
});
});
3 changes: 3 additions & 0 deletions recipes/jest-to-node-test/src/workflow.test.snap.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
exports[`workflow > should migrate jest to node:test 1`] = `
"import { describe, it, mock } from \\"node:test\\";\\nimport { expect } from \\"expect\\";\\n\\ndescribe(\\"test\\", () => {\\n\\tit(\\"should be a test\\", (t) => {\\n\\t\\tconst a = mock.fn((i: number, j: number) => i + j);\\n\\t\\tconst b = mock.fn(async (i: number, j: number) => i - j);\\n\\t\\tmock.module(\\"./workflow.ts\\");\\n\\n\\t\\tconst logSpy = mock.method(console,\\"log\\");\\n\\t\\tconsole.log(\\"Hello, world!\\");\\n\\n\\t\\texpect(logSpy.mock.calls.map((call) => call.arguments)[0][0]).toBe(\\"Hello, world!\\");\\n\\n\\t\\ta(1, 1);\\n\\t\\ta(2, 2);\\n\\n\\t\\texpect(a.mock.callCount()).toBeTruthy();\\n\\t\\texpect(a.mock.callCount()).toBeTruthy();\\n\\n\\t\\texpect(a.mock.callCount()).toBe(2);\\n\\t\\texpect(a.mock.callCount()).toBe(2);\\n\\n\\t\\texpect(a.mock.calls.map(call => call.arguments)).toContainEqual([1,1]);\\n\\t\\texpect(a.mock.calls.map(call => call.arguments)).toContainEqual([1,1]);\\n\\n\\t\\texpect(a.mock.calls.at(-1)?.arguments).toStrictEqual([2,2]);\\n\\t\\texpect(a.mock.calls.at(-1)?.arguments).toStrictEqual([2,2]);\\n\\n\\t\\texpect(a.mock.calls[0].arguments).toStrictEqual([1,1]);\\n\\t\\texpect(a.mock.calls[1].arguments).toStrictEqual([2,2]);\\n\\n\\t\\texpect(a.mock.calls.some(call => call.error === undefined)).toBeTruthy();\\n\\t\\texpect(a.mock.calls.some(call => call.error === undefined)).toBeTruthy();\\n\\n\\t\\texpect(a.mock.calls.filter(call => call.error === undefined)).toHaveLength(2);\\n\\t\\texpect(a.mock.calls.filter(call => call.error === undefined)).toHaveLength(2);\\n\\n\\t\\texpect(a.mock.calls.map(call => call.result)).toContainEqual(2);\\n\\t\\texpect(a.mock.calls.map(call => call.result)).toContainEqual(4);\\n\\n\\t\\texpect(a.mock.calls.at(-1)?.result).toBe(4);\\n\\t\\texpect(a.mock.calls.at(-1)?.result).toBe(4);\\n\\n\\t\\texpect(a.mock.calls[0].result).toBe(2);\\n\\t\\texpect(a.mock.calls[1].result).toBe(4);\\n\\n\\t\\ta.mock.calls.map((call) => call.arguments);\\n\\n\\t\\ta.mock.calls.map((call) => ({\\n\\ttype: call.error ? \\"throw\\" : \\"return\\",\\n\\tvalue: call.error ? call.error : call.result,\\n}));\\n\\n\\t\\t// a.mock.instances;\\n\\n\\t\\t// a.mock.contexts;\\n\\n\\t\\ta.mock.calls.at(-1)?.arguments;\\n\\n\\t\\ta.mock.resetCalls();\\n\\n\\t\\ta.mock.restore();\\n\\n\\t\\ta.mock.restore();\\n\\n\\t\\ta.mock.mockImplementation((i: number, j: number) => i * j);\\n\\n\\t\\ta.mock.mockImplementationOnce((i: number, j: number) => i - j);\\n\\n\\t\\t// a.mockName(\\"myMock\\");\\n\\n\\t\\t// a.mockReturnThis();\\n\\n\\t\\ta.mock.mockImplementation(() => 42);\\n\\n\\t\\ta.mock.mockImplementationOnce(() => 42);\\n\\n\\t\\tb.mock.mockImplementation(async () => {\\n\\tthrow new Error(\\"Test error\\");\\n});\\n\\n\\t\\tb.mock.mockImplementationOnce(async () => {\\n\\tthrow new Error(\\"Test error once\\");\\n});\\n\\n\\t\\tb.mock.mockImplementation(async () => 42);\\n\\n\\t\\tb.mock.mockImplementationOnce(async () => 42);\\n\\n\\t\\tt.assert.snapshot({ a: 1 });\\n\\n\\t\\tt.assert.snapshot({ b: 2 });\\n\\n\\t\\tmock.timers.enable();\\n\\n\\t\\tmock.timers.reset();\\n\\n\\t\\tmock.timers.runAll();\\n\\n\\t\\tmock.timers.runAll();\\n\\n\\t\\tmock.timers.runAll();\\n\\n\\t\\tmock.timers.runAll();\\n\\n\\t\\tmock.timers.tick(1000);\\n\\n\\t\\tmock.timers.tick(1000);\\n\\n\\t\\tmock.timers.runAll();\\n\\n\\t\\tmock.timers.runAll();\\n\\n\\t\\t// jest.advanceTimersToNextTimer(5);\\n\\n\\t\\t// jest.advanceTimersToNextTimerAsync(5);\\n\\n\\t\\tmock.timers.reset();\\n\\n\\t\\tjest.getTimerCount();\\n\\n\\t\\tDate.now();\\n\\n\\t\\tmock.timers.setTime(Number(1000));\\n\\n\\t\\t// jest.getRealSystemTime();\\n\\t});\\n});\\n"
`;
Loading