Skip to content

Commit 011871e

Browse files
committed
test: Add tests for register script + built tests
1 parent 8c36b09 commit 011871e

File tree

12 files changed

+367
-14
lines changed

12 files changed

+367
-14
lines changed

jest.config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Config } from '@jest/types';
2+
3+
const config: Config.InitialOptions = {
4+
testEnvironment: "node",
5+
preset: 'ts-jest',
6+
testRegex: '.*(test|spec)\\.tsx?$',
7+
moduleFileExtensions: [ 'ts', 'tsx', 'js', 'jsx', 'json', 'node' ],
8+
globals: {
9+
'ts-jest': {
10+
tsconfig: '<rootDir>/test/tsconfig.json'
11+
}
12+
},
13+
modulePaths: [ "<rootDir>" ],
14+
roots: [ '<rootDir>' ],
15+
}
16+
17+
export default config;

test/projects/extras/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"private": true,
3+
"name": "@tests/extras",
4+
"version": "0.0.0",
5+
"dependencies": {
6+
"typescript-transform-paths": "link:../../../"
7+
}
8+
}

test/projects/extras/src/id.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const b = null;

test/projects/extras/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from "#identifier";
2+
import { b } from "#identifier";
3+
4+
console.log(b);

test/projects/extras/tsconfig.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"include": [ "src" ],
3+
4+
"ts-node": {
5+
"transpileOnly": true,
6+
"require": [ "typescript-transform-paths/register" ],
7+
},
8+
9+
"compilerOptions": {
10+
"noEmit": true,
11+
12+
"rootDir": ".",
13+
"module": "CommonJS",
14+
"esModuleInterop": true,
15+
"moduleResolution": "node",
16+
"declaration": true,
17+
18+
"baseUrl": "./src",
19+
"paths": {
20+
"#identifier": [ "./id.ts" ]
21+
},
22+
23+
"plugins": [
24+
{
25+
"transform": "typescript-transform-paths"
26+
},
27+
{
28+
"transform": "typescript-transform-paths",
29+
"afterDeclarations": true
30+
}
31+
]
32+
}
33+
}

test/tests/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ export const tsModules = <const>[
2222
export const projectsPaths = path.join(__dirname, "../projects");
2323
Error.stackTraceLimit = 120;
2424

25+
export const transformerPath = path.resolve(__dirname, "../../src/index.ts");
26+
export const builtTransformerPath = path.resolve(__dirname, "../../dist/index.js");
27+
2528
// endregion

test/tests/extras.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createTsProgram, getEmitResultFromProgram } from "../utils";
2+
import { projectsPaths } from "./config";
3+
import path from "path";
4+
import ts from "typescript";
5+
import * as config from "./config";
6+
import { execSync } from "child_process";
7+
8+
/* ****************************************************************************************************************** *
9+
* Tests
10+
* ****************************************************************************************************************** */
11+
12+
describe(`Extra Tests`, () => {
13+
const projectRoot = ts.normalizePath(path.join(projectsPaths, "extras"));
14+
const indexFile = ts.normalizePath(path.join(projectRoot, "src/index.ts"));
15+
const tsConfigFile = ts.normalizePath(path.join(projectRoot, "tsconfig.json"));
16+
17+
describe(`Built Tests`, () => {
18+
// see: https://github.com/LeDDGroup/typescript-transform-paths/issues/130
19+
test(`Transformer works without ts-node being present`, () => {
20+
jest.doMock(
21+
"ts-node",
22+
() => {
23+
require("sdf0s39rf3333d@fake-module");
24+
},
25+
{ virtual: true }
26+
);
27+
try {
28+
const program = createTsProgram({ tsInstance: ts, tsConfigFile }, config.builtTransformerPath);
29+
const res = getEmitResultFromProgram(program);
30+
expect(res[indexFile].js).toMatch(`var _identifier_1 = require("./id")`);
31+
} finally {
32+
jest.dontMock("ts-node");
33+
}
34+
});
35+
36+
test(`Register script transforms with ts-node`, () => {
37+
const res = execSync("ts-node src/index.ts", { cwd: projectRoot }).toString();
38+
expect(res).toMatch(/^null($|\r?\n)/);
39+
});
40+
});
41+
});

test/tests/register.test.ts

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import { register } from "../../src";
2+
import { PluginConfig } from "ts-patch";
3+
import * as tsNode from "ts-node";
4+
import * as transformerModule from "../../src/transformer";
5+
import { REGISTER_INSTANCE } from "ts-node";
6+
import { CustomTransformers, PluginImport, Program } from "typescript";
7+
8+
/* ****************************************************************************************************************** *
9+
* Config
10+
* ****************************************************************************************************************** */
11+
12+
const pluginOptions = { opt1: true, opt2: 3 };
13+
const otherTransformer = { transform: "fake-transformer@23904" };
14+
const configs = {
15+
"Implicit before": {},
16+
"Explicit before": { before: true },
17+
afterDeclarations: { afterDeclarations: true },
18+
"Implicit before + afterDeclarations": [{}, { afterDeclarations: true }],
19+
"Explicit before + afterDeclarations": [{ before: true }, { afterDeclarations: true }],
20+
} as const;
21+
const configMap = Object.entries(configs).map(([label, cfg]) => {
22+
let hasBefore: boolean = false;
23+
let hasAfterDeclarations: boolean = false;
24+
const transformers = [cfg]
25+
.flat()
26+
.map((c) => {
27+
if ((<any>c).before || !(<any>c).afterDeclarations) hasBefore = true;
28+
if ((<any>c).afterDeclarations) hasAfterDeclarations = true;
29+
return { transform: "typescript-transform-paths", ...c, ...pluginOptions } as PluginConfig;
30+
})
31+
.concat([otherTransformer]);
32+
33+
return { label, transformers, hasBefore, hasAfterDeclarations };
34+
});
35+
36+
const instanceSymbol: typeof REGISTER_INSTANCE = tsNode["REGISTER_INSTANCE"];
37+
38+
/* ****************************************************************************************************************** *
39+
* Tests
40+
* ****************************************************************************************************************** */
41+
42+
describe(`Register script`, () => {
43+
describe(`Initialize`, () => {
44+
test(`Registers initial ts-node if none found`, () => {
45+
const originalTsNodeInstance = global.process[instanceSymbol];
46+
global.process[instanceSymbol] = void 0;
47+
let registerSpy: jest.SpyInstance | undefined;
48+
try {
49+
registerSpy = jest.spyOn(tsNode, "register");
50+
expect(global.process[instanceSymbol]).toBeUndefined();
51+
52+
register.initialize();
53+
54+
expect(registerSpy).toBeCalledTimes(1);
55+
expect(registerSpy.mock.calls[0]).toHaveLength(0);
56+
expect(global.process[instanceSymbol]).not.toBeUndefined();
57+
} finally {
58+
global.process[instanceSymbol] = originalTsNodeInstance;
59+
registerSpy?.mockRestore();
60+
}
61+
});
62+
test(`Uses existing ts-node if found`, () => {
63+
const fakeInstance: any = {};
64+
65+
const originalTsNodeInstance = global.process[instanceSymbol];
66+
global.process[instanceSymbol] = fakeInstance;
67+
let registerSpy: jest.SpyInstance | undefined;
68+
try {
69+
registerSpy = jest.spyOn(tsNode, "register");
70+
71+
const { tsNodeInstance } = register.initialize();
72+
73+
expect(registerSpy).not.toBeCalled();
74+
expect(tsNodeInstance).toBe(fakeInstance);
75+
} finally {
76+
global.process[instanceSymbol] = originalTsNodeInstance;
77+
registerSpy?.mockRestore();
78+
}
79+
});
80+
81+
test(`Returns instance, tsNode, and symbol`, () => {
82+
const res = register.initialize();
83+
expect(res.tsNode).toBe(tsNode);
84+
expect(res.tsNodeInstance).toBe(global.process[instanceSymbol]);
85+
expect(res.instanceSymbol).toBe(instanceSymbol);
86+
});
87+
});
88+
89+
describe(`Register`, () => {
90+
test(`Throws without ts-node`, () => {
91+
jest.doMock(
92+
"ts-node",
93+
() => {
94+
require("sdf0s39rf3333d@fake-module");
95+
},
96+
{ virtual: true }
97+
);
98+
expect(() => register()).toThrow(`Cannot resolve ts-node`);
99+
jest.dontMock("ts-node");
100+
});
101+
102+
test(`Throws if can't register ts-node`, () => {
103+
jest.doMock("ts-node", () => ({ register: () => {} }), { virtual: true });
104+
expect(() => register()).toThrow(`Could not register ts-node instance!`);
105+
jest.dontMock("ts-node");
106+
});
107+
108+
test(`No transformers in tsConfig exits quietly`, () => {
109+
const originalInitialize = register.initialize;
110+
const initializeSpy = jest.spyOn(register, "initialize");
111+
try {
112+
initializeSpy.mockImplementation(() => {
113+
const res = originalInitialize();
114+
delete res.tsNodeInstance.config.options.plugins;
115+
return res;
116+
});
117+
expect(register()).toBeUndefined();
118+
} finally {
119+
initializeSpy.mockRestore();
120+
}
121+
});
122+
123+
describe.each([
124+
"Existing Transformer Config",
125+
"Existing Transformer Config Factory",
126+
"No Existing Transformers",
127+
] as const)(`%s`, (configKind) => {
128+
const fakeExistingTransformer = function fakeExistingTransformer(): any {};
129+
const fakeTransformer = function fakeTransformer(): any {};
130+
const fakeTransformerConfig = {
131+
before: [fakeExistingTransformer],
132+
after: [fakeExistingTransformer],
133+
afterDeclarations: [fakeExistingTransformer],
134+
};
135+
const transformerFactoryFn = jest.fn().mockReturnValue(fakeTransformerConfig);
136+
const fakeProgram: any = {};
137+
138+
let existingTransformers: CustomTransformers | ((p: Program) => CustomTransformers) | undefined;
139+
switch (configKind) {
140+
case "Existing Transformer Config Factory":
141+
existingTransformers = transformerFactoryFn;
142+
break;
143+
case "Existing Transformer Config":
144+
existingTransformers = { ...fakeTransformerConfig };
145+
break;
146+
case "No Existing Transformers":
147+
existingTransformers = void 0;
148+
}
149+
150+
describe.each(configMap)(`$label`, ({ transformers, hasBefore, hasAfterDeclarations }) => {
151+
let mockTransformer: jest.SpyInstance;
152+
let initializeSpy: jest.SpyInstance;
153+
let registerResult: tsNode.RegisterOptions;
154+
let instanceRegistrationResult: tsNode.Service;
155+
let mergedTransformers: CustomTransformers;
156+
157+
beforeAll(() => {
158+
mockTransformer = jest.spyOn(transformerModule, "default").mockReturnValue(fakeTransformer);
159+
160+
global.process[instanceSymbol] = void 0;
161+
162+
const originalInitialize = register.initialize;
163+
initializeSpy = jest.spyOn(register, "initialize");
164+
initializeSpy.mockImplementation(() => {
165+
const res = originalInitialize();
166+
if (existingTransformers) res.tsNodeInstance.options.transformers = existingTransformers;
167+
else delete res.tsNodeInstance.options.transformers;
168+
169+
res.tsNodeInstance.config.options.plugins = transformers as PluginImport[];
170+
return res;
171+
});
172+
173+
const originalTsNodeInstance = global.process[instanceSymbol];
174+
registerResult = register()!;
175+
instanceRegistrationResult = global.process[instanceSymbol]!;
176+
global.process[instanceSymbol] = originalTsNodeInstance;
177+
178+
mergedTransformers =
179+
typeof registerResult.transformers === "function"
180+
? registerResult.transformers(fakeProgram)
181+
: registerResult.transformers!;
182+
});
183+
afterAll(() => {
184+
initializeSpy.mockRestore();
185+
mockTransformer.mockRestore();
186+
transformerFactoryFn.mockClear();
187+
});
188+
189+
test(`Registers with ts-node`, () => {
190+
expect(registerResult?.transformers).not.toBeUndefined();
191+
expect(instanceRegistrationResult.options).toStrictEqual(registerResult);
192+
});
193+
194+
if (existingTransformers === transformerFactoryFn)
195+
test(`Factory config instantiated with program`, () => {
196+
expect(transformerFactoryFn).toBeCalledTimes(1);
197+
expect(transformerFactoryFn).toBeCalledWith(fakeProgram);
198+
});
199+
200+
test(`Registers correct transformers`, () => {
201+
const expectedBefore = [
202+
...(hasBefore ? [fakeTransformer] : []),
203+
...(existingTransformers ? fakeTransformerConfig.before : []),
204+
];
205+
const expectedAfter = existingTransformers ? fakeTransformerConfig.after : [];
206+
const expectedAfterDeclarations = [
207+
...(hasAfterDeclarations ? [fakeTransformer] : []),
208+
...(existingTransformers ? fakeTransformerConfig.afterDeclarations : []),
209+
];
210+
211+
const expected = {
212+
...(expectedBefore.length && { before: expectedBefore }),
213+
...(expectedAfter.length && { after: expectedAfter }),
214+
...(expectedAfterDeclarations.length && { afterDeclarations: expectedAfterDeclarations }),
215+
};
216+
217+
expect(mergedTransformers).toStrictEqual(expected);
218+
});
219+
220+
test(`Transformer instantiated w/ proper config${
221+
existingTransformers === transformerFactoryFn ? " & Program" : ""
222+
}`, () => {
223+
const callTimes = +hasBefore + +hasAfterDeclarations;
224+
expect(mockTransformer).toBeCalledTimes(callTimes);
225+
226+
const afterDeclarationsConfig = transformers.find(
227+
(t) => t.transform === "typescript-transform-paths" && t.afterDeclarations
228+
);
229+
const beforeConfig = transformers.find(
230+
(t) => t.transform === "typescript-transform-paths" && !t.afterDeclarations
231+
);
232+
233+
if (hasBefore) expect(beforeConfig).not.toBeUndefined();
234+
if (hasAfterDeclarations) expect(afterDeclarationsConfig).not.toBeUndefined();
235+
236+
const expectedCfg = [beforeConfig, afterDeclarationsConfig].filter((c) => !!c);
237+
for (let i = 0; i < callTimes; i++) {
238+
expect(mockTransformer.mock.calls[i][0]).toBe(
239+
existingTransformers === transformerFactoryFn ? fakeProgram : void 0
240+
);
241+
expect(mockTransformer.mock.calls[i][1]).toBe(expectedCfg[i]);
242+
}
243+
});
244+
});
245+
});
246+
});
247+
});

test/tests/transformer/specific.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
getTsNodeEmitResult,
1010
} from "../../utils";
1111
import { projectsPaths, ts, tsModules } from "../config";
12-
import { TsTransformPathsConfig } from "../../../src/types";
12+
import { TsTransformPathsConfig } from '../../../src';
1313
import TS from "typescript";
1414

1515
/* ****************************************************************************************************************** *
@@ -123,7 +123,7 @@ describe(`Specific Tests`, () => {
123123
failed = true;
124124
messages.push(
125125
`File: ${fileName}\nKind: ${kind}\nrootDirs: ${base === normalEmit}\n\n` +
126-
`Expected: \`${expected}\`\nReceived:\n\t${content.replace(/(\r?\n)+/g, "$1\t")}`
126+
`Expected: \`${expected}\`\nReceived:\n\t${content.replace(/(\r?\n)+/g, "$1\t")}`
127127
);
128128
}
129129
}

test/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"extends": "../tsconfig.base",
23
"include": [ "tests", "utils" ],
34

45
"compilerOptions": {

0 commit comments

Comments
 (0)