jest-doctor 0.0.5 → 0.0.7
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/dist/createEnvMixin.d.ts +11 -0
- package/dist/createEnvMixin.js +18 -16
- package/dist/env/jsdom.d.ts +11 -0
- package/dist/env/node.d.ts +11 -0
- package/dist/patch/console.d.ts +0 -1
- package/dist/patch/console.js +3 -14
- package/dist/{utils → patch}/createAsyncHookCleaner.js +1 -0
- package/dist/{utils → patch}/createAsyncHookDetector.d.ts +1 -1
- package/dist/patch/createAsyncHookDetector.js +30 -0
- package/dist/patch/fakeTimers.d.ts +1 -1
- package/dist/patch/fakeTimers.js +21 -14
- package/dist/patch/hook.d.ts +2 -2
- package/dist/patch/hook.js +12 -11
- package/dist/patch/it.d.ts +2 -2
- package/dist/patch/it.js +16 -18
- package/dist/patch/processOutput.d.ts +3 -0
- package/dist/patch/processOutput.js +30 -0
- package/dist/patch/promiseConcurrency.js +50 -19
- package/dist/patch/timers.d.ts +1 -1
- package/dist/patch/timers.js +14 -10
- package/dist/reporter.js +16 -1
- package/dist/requireEnvironment.d.ts +11 -0
- package/dist/types.d.ts +45 -20
- package/dist/utils/analyzeCallback.js +3 -0
- package/dist/utils/getStack.d.ts +1 -1
- package/dist/utils/getStack.js +12 -2
- package/dist/utils/initLeakRecord.js +1 -0
- package/dist/utils/initOriginal.d.ts +8 -0
- package/dist/utils/initOriginal.js +2 -0
- package/dist/utils/isIgnored.d.ts +3 -0
- package/dist/utils/isIgnored.js +11 -0
- package/dist/utils/normalizeOptions.js +44 -30
- package/dist/utils/reportLeaks.js +42 -47
- package/package.json +1 -1
- package/readme.md +27 -12
- package/dist/utils/createAsyncHookDetector.js +0 -26
- /package/dist/{utils → patch}/createAsyncHookCleaner.d.ts +0 -0
package/dist/createEnvMixin.d.ts
CHANGED
|
@@ -29,6 +29,14 @@ declare const createEnvMixin: <EnvironmentConstructor extends JestDoctorConstruc
|
|
|
29
29
|
clearTimeout: typeof clearTimeout;
|
|
30
30
|
setInterval: typeof setInterval;
|
|
31
31
|
clearInterval: typeof clearInterval;
|
|
32
|
+
stdout: {
|
|
33
|
+
(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
|
|
34
|
+
(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
|
|
35
|
+
};
|
|
36
|
+
stderr: {
|
|
37
|
+
(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
|
|
38
|
+
(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
|
|
39
|
+
};
|
|
32
40
|
console: {
|
|
33
41
|
log: (message?: any, ...optionalParams: any[]) => void;
|
|
34
42
|
info: (message?: any, ...optionalParams: any[]) => void;
|
|
@@ -53,9 +61,12 @@ declare const createEnvMixin: <EnvironmentConstructor extends JestDoctorConstruc
|
|
|
53
61
|
fakeTimers: number;
|
|
54
62
|
console: number;
|
|
55
63
|
totalDelay: number;
|
|
64
|
+
processOutputs: number;
|
|
56
65
|
};
|
|
57
66
|
tearDownError?: Error;
|
|
58
67
|
asyncIdToPromise: Map<number, Promise<unknown>>;
|
|
68
|
+
asyncRoot: number;
|
|
69
|
+
asyncIdToParentId: Map<number, number>;
|
|
59
70
|
setup(): Promise<void>;
|
|
60
71
|
handleEvent(event: unknown, state: unknown): Promise<void>;
|
|
61
72
|
handleTestEvent: JestEnvironment["handleTestEvent"];
|
package/dist/createEnvMixin.js
CHANGED
|
@@ -12,8 +12,8 @@ const fakeTimers_1 = __importDefault(require("./patch/fakeTimers"));
|
|
|
12
12
|
const consts_1 = require("./consts");
|
|
13
13
|
const timers_1 = __importDefault(require("./patch/timers"));
|
|
14
14
|
const it_1 = __importDefault(require("./patch/it"));
|
|
15
|
-
const createAsyncHookDetector_1 = __importDefault(require("./
|
|
16
|
-
const createAsyncHookCleaner_1 = __importDefault(require("./
|
|
15
|
+
const createAsyncHookDetector_1 = __importDefault(require("./patch/createAsyncHookDetector"));
|
|
16
|
+
const createAsyncHookCleaner_1 = __importDefault(require("./patch/createAsyncHookCleaner"));
|
|
17
17
|
const reportLeaks_1 = __importDefault(require("./utils/reportLeaks"));
|
|
18
18
|
const cleanupAfterTest_1 = __importDefault(require("./utils/cleanupAfterTest"));
|
|
19
19
|
const initLeakRecord_1 = __importDefault(require("./utils/initLeakRecord"));
|
|
@@ -22,6 +22,7 @@ const getAllAfterEach_1 = __importDefault(require("./utils/getAllAfterEach"));
|
|
|
22
22
|
const normalizeOptions_1 = __importDefault(require("./utils/normalizeOptions"));
|
|
23
23
|
const getReporterTmpDir_1 = __importDefault(require("./utils/getReporterTmpDir"));
|
|
24
24
|
const promiseConcurrency_1 = __importDefault(require("./patch/promiseConcurrency"));
|
|
25
|
+
const processOutput_1 = __importDefault(require("./patch/processOutput"));
|
|
25
26
|
const createEnvMixin = (Environment) => {
|
|
26
27
|
// @ts-expect-error strange ts rule where constructor arguments should be any[] where again TypeScript complains
|
|
27
28
|
return class Base extends Environment {
|
|
@@ -39,6 +40,8 @@ const createEnvMixin = (Environment) => {
|
|
|
39
40
|
aggregatedReport;
|
|
40
41
|
tearDownError;
|
|
41
42
|
asyncIdToPromise = new Map();
|
|
43
|
+
asyncRoot = 0;
|
|
44
|
+
asyncIdToParentId = new Map();
|
|
42
45
|
constructor(config, context) {
|
|
43
46
|
super(config, context);
|
|
44
47
|
this.testPath = context.testPath.replace(/\W/g, '_');
|
|
@@ -49,8 +52,9 @@ const createEnvMixin = (Environment) => {
|
|
|
49
52
|
fakeTimers: 0,
|
|
50
53
|
console: 0,
|
|
51
54
|
totalDelay: 0,
|
|
55
|
+
processOutputs: 0,
|
|
52
56
|
};
|
|
53
|
-
const tmpDir = (0, getReporterTmpDir_1.default)(config.projectConfig.reporters);
|
|
57
|
+
const tmpDir = (0, getReporterTmpDir_1.default)(config.projectConfig.reporters || config.globalConfig.reporters);
|
|
54
58
|
this.reporterTmpDir = tmpDir
|
|
55
59
|
? node_path_1.default.join(tmpDir, config.globalConfig.seed.toString(), this.testPath + 'json')
|
|
56
60
|
: '';
|
|
@@ -72,6 +76,9 @@ const createEnvMixin = (Environment) => {
|
|
|
72
76
|
if (report.console) {
|
|
73
77
|
(0, console_1.default)(this, report.console);
|
|
74
78
|
}
|
|
79
|
+
if (report.processOutputs) {
|
|
80
|
+
(0, processOutput_1.default)(this, report.processOutputs);
|
|
81
|
+
}
|
|
75
82
|
if (report.promises) {
|
|
76
83
|
(0, promiseConcurrency_1.default)(this);
|
|
77
84
|
}
|
|
@@ -94,19 +101,11 @@ const createEnvMixin = (Environment) => {
|
|
|
94
101
|
else if (circusEvent.name === 'setup') {
|
|
95
102
|
this.asyncHookCleaner?.enable();
|
|
96
103
|
this.asyncHookDetector?.enable();
|
|
97
|
-
(0, it_1.default)(this);
|
|
98
|
-
(0, hook_1.default)(this, 'beforeEach');
|
|
99
|
-
(0, hook_1.default)(this, 'beforeAll');
|
|
100
|
-
(0, hook_1.default)(this, 'afterEach');
|
|
101
|
-
(0, hook_1.default)(this, 'afterAll');
|
|
102
|
-
Object.assign(circusEvent.runtimeGlobals, {
|
|
103
|
-
it: this.global.it,
|
|
104
|
-
test: this.global.test,
|
|
105
|
-
beforeEach: this.global.beforeEach,
|
|
106
|
-
beforeAll: this.global.beforeAll,
|
|
107
|
-
afterEach: this.global.afterEach,
|
|
108
|
-
afterAll: this.global.afterAll,
|
|
109
|
-
});
|
|
104
|
+
(0, it_1.default)(this, circusEvent.runtimeGlobals);
|
|
105
|
+
(0, hook_1.default)(this, 'beforeEach', circusEvent.runtimeGlobals);
|
|
106
|
+
(0, hook_1.default)(this, 'beforeAll', circusEvent.runtimeGlobals);
|
|
107
|
+
(0, hook_1.default)(this, 'afterEach', circusEvent.runtimeGlobals);
|
|
108
|
+
(0, hook_1.default)(this, 'afterAll', circusEvent.runtimeGlobals);
|
|
110
109
|
}
|
|
111
110
|
await super.handleEvent?.(circusEvent, circusState);
|
|
112
111
|
}
|
|
@@ -135,6 +134,8 @@ const createEnvMixin = (Environment) => {
|
|
|
135
134
|
}
|
|
136
135
|
finally {
|
|
137
136
|
(0, cleanupAfterTest_1.default)(this, leakRecord, consts_1.MAIN_THREAD);
|
|
137
|
+
process.stdout.write = this.original.stdout;
|
|
138
|
+
process.stderr.write = this.original.stderr;
|
|
138
139
|
this.asyncHookCleaner?.disable();
|
|
139
140
|
// this is added here for safety reasons,
|
|
140
141
|
// because jest could abort and dont hit teardown event
|
|
@@ -143,6 +144,7 @@ const createEnvMixin = (Environment) => {
|
|
|
143
144
|
this.aggregatedReport.timers +
|
|
144
145
|
this.aggregatedReport.fakeTimers +
|
|
145
146
|
this.aggregatedReport.console +
|
|
147
|
+
this.aggregatedReport.processOutputs +
|
|
146
148
|
this.aggregatedReport.totalDelay;
|
|
147
149
|
if (this.reporterTmpDir && hasLeak) {
|
|
148
150
|
// needs to be sync or will be terminated by jest when an error occurs
|
package/dist/env/jsdom.d.ts
CHANGED
|
@@ -7,6 +7,14 @@ declare const _default: {
|
|
|
7
7
|
clearTimeout: typeof clearTimeout;
|
|
8
8
|
setInterval: typeof setInterval;
|
|
9
9
|
clearInterval: typeof clearInterval;
|
|
10
|
+
stdout: {
|
|
11
|
+
(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
|
|
12
|
+
(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
|
|
13
|
+
};
|
|
14
|
+
stderr: {
|
|
15
|
+
(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
|
|
16
|
+
(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
|
|
17
|
+
};
|
|
10
18
|
console: {
|
|
11
19
|
log: (message?: any, ...optionalParams: any[]) => void;
|
|
12
20
|
info: (message?: any, ...optionalParams: any[]) => void;
|
|
@@ -31,9 +39,12 @@ declare const _default: {
|
|
|
31
39
|
fakeTimers: number;
|
|
32
40
|
console: number;
|
|
33
41
|
totalDelay: number;
|
|
42
|
+
processOutputs: number;
|
|
34
43
|
};
|
|
35
44
|
tearDownError?: Error;
|
|
36
45
|
asyncIdToPromise: Map<number, Promise<unknown>>;
|
|
46
|
+
asyncRoot: number;
|
|
47
|
+
asyncIdToParentId: Map<number, number>;
|
|
37
48
|
setup(): Promise<void>;
|
|
38
49
|
handleEvent(event: unknown, state: unknown): Promise<void>;
|
|
39
50
|
handleTestEvent: import("@jest/environment").JestEnvironment["handleTestEvent"];
|
package/dist/env/node.d.ts
CHANGED
|
@@ -7,6 +7,14 @@ declare const _default: {
|
|
|
7
7
|
clearTimeout: typeof clearTimeout;
|
|
8
8
|
setInterval: typeof setInterval;
|
|
9
9
|
clearInterval: typeof clearInterval;
|
|
10
|
+
stdout: {
|
|
11
|
+
(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
|
|
12
|
+
(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
|
|
13
|
+
};
|
|
14
|
+
stderr: {
|
|
15
|
+
(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean;
|
|
16
|
+
(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean;
|
|
17
|
+
};
|
|
10
18
|
console: {
|
|
11
19
|
log: (message?: any, ...optionalParams: any[]) => void;
|
|
12
20
|
info: (message?: any, ...optionalParams: any[]) => void;
|
|
@@ -31,9 +39,12 @@ declare const _default: {
|
|
|
31
39
|
fakeTimers: number;
|
|
32
40
|
console: number;
|
|
33
41
|
totalDelay: number;
|
|
42
|
+
processOutputs: number;
|
|
34
43
|
};
|
|
35
44
|
tearDownError?: Error;
|
|
36
45
|
asyncIdToPromise: Map<number, Promise<unknown>>;
|
|
46
|
+
asyncRoot: number;
|
|
47
|
+
asyncIdToParentId: Map<number, number>;
|
|
37
48
|
setup(): Promise<void>;
|
|
38
49
|
handleEvent(event: unknown, state: unknown): Promise<void>;
|
|
39
50
|
handleTestEvent: import("@jest/environment").JestEnvironment["handleTestEvent"];
|
package/dist/patch/console.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { JestDoctorEnvironment, ConsoleOptions } from '../types';
|
|
2
|
-
export declare const isIgnored: (message: string, ignorePatterns: ConsoleOptions["ignore"]) => boolean;
|
|
3
2
|
declare const patchConsole: (that: JestDoctorEnvironment, consoleOptions: ConsoleOptions) => void;
|
|
4
3
|
export default patchConsole;
|
package/dist/patch/console.js
CHANGED
|
@@ -3,30 +3,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.isIgnored = void 0;
|
|
7
6
|
const node_util_1 = require("node:util");
|
|
8
7
|
const getStack_1 = __importDefault(require("../utils/getStack"));
|
|
9
|
-
const
|
|
10
|
-
return ignorePatterns.some((pattern) => {
|
|
11
|
-
if (typeof pattern === 'string') {
|
|
12
|
-
return message.includes(pattern);
|
|
13
|
-
}
|
|
14
|
-
return pattern.test(message);
|
|
15
|
-
});
|
|
16
|
-
};
|
|
17
|
-
exports.isIgnored = isIgnored;
|
|
8
|
+
const isIgnored_1 = __importDefault(require("../utils/isIgnored"));
|
|
18
9
|
const patchConsole = (that, consoleOptions) => {
|
|
19
10
|
const env = that.global;
|
|
20
11
|
for (const consoleMethod of consoleOptions.methods) {
|
|
21
12
|
const originalMethod = env.console[consoleMethod].bind(env.console);
|
|
22
13
|
env.console[consoleMethod] = (...args) => {
|
|
23
14
|
const message = (0, node_util_1.format)(...args);
|
|
24
|
-
if (!(0,
|
|
15
|
+
if (!(0, isIgnored_1.default)(message, consoleOptions.ignore)) {
|
|
25
16
|
that.leakRecords.get(that.currentTestName)?.console.push({
|
|
26
17
|
method: consoleMethod,
|
|
27
|
-
stack: (0, getStack_1.default)(env.console[consoleMethod]
|
|
28
|
-
testName: that.currentTestName,
|
|
29
|
-
message,
|
|
18
|
+
stack: (0, getStack_1.default)(env.console[consoleMethod]),
|
|
30
19
|
});
|
|
31
20
|
}
|
|
32
21
|
return originalMethod(...args);
|
|
@@ -4,6 +4,7 @@ const node_async_hooks_1 = require("node:async_hooks");
|
|
|
4
4
|
const createAsyncHookCleaner = (that) => {
|
|
5
5
|
return (0, node_async_hooks_1.createHook)({
|
|
6
6
|
promiseResolve(asyncId) {
|
|
7
|
+
that.asyncIdToParentId.delete(asyncId);
|
|
7
8
|
const owner = that.promiseOwner.get(asyncId);
|
|
8
9
|
if (!owner) {
|
|
9
10
|
return;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
7
|
+
const getStack_1 = __importDefault(require("../utils/getStack"));
|
|
8
|
+
const isIgnored_1 = __importDefault(require("../utils/isIgnored"));
|
|
9
|
+
const createAsyncHookDetector = (that) => {
|
|
10
|
+
const init = (asyncId, type, parentAsyncId, resource) => {
|
|
11
|
+
if (type !== 'PROMISE') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
that.asyncIdToParentId.set(asyncId, parentAsyncId);
|
|
15
|
+
const stack = (0, getStack_1.default)(init);
|
|
16
|
+
if (!(0, isIgnored_1.default)(stack, that.options.report.promises.ignore)) {
|
|
17
|
+
const owner = that.currentTestName;
|
|
18
|
+
that.promiseOwner.set(asyncId, owner);
|
|
19
|
+
const promise = resource;
|
|
20
|
+
that.asyncIdToPromise.set(asyncId, promise);
|
|
21
|
+
that.leakRecords.get(owner)?.promises.set(promise, {
|
|
22
|
+
stack,
|
|
23
|
+
asyncId,
|
|
24
|
+
parentAsyncId,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return (0, node_async_hooks_1.createHook)({ init });
|
|
29
|
+
};
|
|
30
|
+
exports.default = createAsyncHookDetector;
|
package/dist/patch/fakeTimers.js
CHANGED
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const node_console_1 = __importDefault(require("node:console"));
|
|
7
6
|
const getStack_1 = __importDefault(require("../utils/getStack"));
|
|
7
|
+
const isIgnored_1 = __importDefault(require("../utils/isIgnored"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
9
|
const patchFakeTimers = (that) => {
|
|
9
10
|
const modernFakeTimers = that.fakeTimersModern;
|
|
10
11
|
const fakeTimers = modernFakeTimers?._fakeTimers;
|
|
@@ -27,22 +28,28 @@ const patchFakeTimers = (that) => {
|
|
|
27
28
|
fakeTimeout?.delete(timerId);
|
|
28
29
|
callback();
|
|
29
30
|
}, delay);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
const stack = (0, getStack_1.default)(that.global.setTimeout);
|
|
32
|
+
if (!(0, isIgnored_1.default)(stack, that.options.report.fakeTimers.ignore)) {
|
|
33
|
+
fakeTimeout?.set(timerId, {
|
|
34
|
+
type: 'fakeTimeout',
|
|
35
|
+
delay: delay || 0,
|
|
36
|
+
stack,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
36
39
|
return timerId;
|
|
37
40
|
};
|
|
38
41
|
clock.setInterval = function (callback, delay) {
|
|
39
42
|
const intervalId = originalFakeSetInterval(callback, delay);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const stack = (0, getStack_1.default)(that.global.setInterval);
|
|
44
|
+
if (!(0, isIgnored_1.default)(stack, that.options.report.fakeTimers.ignore)) {
|
|
45
|
+
that.leakRecords
|
|
46
|
+
.get(that.currentTestName)
|
|
47
|
+
?.fakeTimers.set(intervalId, {
|
|
48
|
+
type: 'fakeInterval',
|
|
49
|
+
delay: delay || 0,
|
|
50
|
+
stack,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
46
53
|
return intervalId;
|
|
47
54
|
};
|
|
48
55
|
clock.clearTimeout = (timerId) => {
|
|
@@ -59,7 +66,7 @@ const patchFakeTimers = (that) => {
|
|
|
59
66
|
};
|
|
60
67
|
}
|
|
61
68
|
else {
|
|
62
|
-
|
|
69
|
+
that.original.stderr(chalk_1.default.yellow('\nFake timers could not be mocked!'));
|
|
63
70
|
}
|
|
64
71
|
};
|
|
65
72
|
exports.default = patchFakeTimers;
|
package/dist/patch/hook.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
declare const patchHook: (that: JestDoctorEnvironment, hookName: string) => void;
|
|
1
|
+
import { JestDoctorEnvironment, RuntimeGlobals } from '../types';
|
|
2
|
+
declare const patchHook: (that: JestDoctorEnvironment, hookName: string, runtimeGlobals: RuntimeGlobals) => void;
|
|
3
3
|
export default patchHook;
|
package/dist/patch/hook.js
CHANGED
|
@@ -4,18 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const analyzeCallback_1 = __importDefault(require("../utils/analyzeCallback"));
|
|
7
|
-
const patchHook = (that, hookName) => {
|
|
7
|
+
const patchHook = (that, hookName, runtimeGlobals) => {
|
|
8
8
|
const originalHook = that.global[hookName];
|
|
9
|
-
that.global[hookName] =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
that.global[hookName] = runtimeGlobals[hookName] =
|
|
10
|
+
function (callback, timeout) {
|
|
11
|
+
const hookMock = function () {
|
|
12
|
+
if (hookName === 'afterEach') {
|
|
13
|
+
that.currentAfterEachCount -= 1;
|
|
14
|
+
}
|
|
15
|
+
return (0, analyzeCallback_1.default)(that, callback, this);
|
|
16
|
+
};
|
|
17
|
+
// unfortunately, jest types the return type as any
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
19
|
+
return originalHook(hookMock, timeout);
|
|
15
20
|
};
|
|
16
|
-
// unfortunately, jest types the return type as any
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
18
|
-
return originalHook(hookMock, timeout);
|
|
19
|
-
};
|
|
20
21
|
};
|
|
21
22
|
exports.default = patchHook;
|
package/dist/patch/it.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
declare const patchIt: (that: JestDoctorEnvironment) => void;
|
|
1
|
+
import { JestDoctorEnvironment, RuntimeGlobals } from '../types';
|
|
2
|
+
declare const patchIt: (that: JestDoctorEnvironment, runtimeGlobals: RuntimeGlobals) => void;
|
|
3
3
|
export default patchIt;
|
package/dist/patch/it.js
CHANGED
|
@@ -4,25 +4,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const analyzeCallback_1 = __importDefault(require("../utils/analyzeCallback"));
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const testHandler = function () {
|
|
14
|
-
return (0, analyzeCallback_1.default)(that, testFunction, this);
|
|
15
|
-
};
|
|
16
|
-
return originalFn(testName, testHandler, timeout);
|
|
7
|
+
const patchIt = (that, runtimeGlobals) => {
|
|
8
|
+
const originalIt = runtimeGlobals.it;
|
|
9
|
+
const originalOnly = originalIt.only;
|
|
10
|
+
const createItPatch = (originalFn) => (testName, testFunction, timeout) => {
|
|
11
|
+
const testHandler = function () {
|
|
12
|
+
return (0, analyzeCallback_1.default)(that, testFunction, this);
|
|
17
13
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
return originalFn(testName, testHandler, timeout);
|
|
15
|
+
};
|
|
16
|
+
const itPatch = createItPatch(originalIt);
|
|
17
|
+
const test = Object.assign(itPatch, originalIt, {
|
|
18
|
+
concurrent: itPatch,
|
|
19
|
+
only: createItPatch(originalOnly),
|
|
20
|
+
});
|
|
21
|
+
if (that.global.test) {
|
|
22
|
+
that.global.it = that.global.test = test;
|
|
26
23
|
}
|
|
24
|
+
runtimeGlobals.it = runtimeGlobals.test = test;
|
|
27
25
|
};
|
|
28
26
|
exports.default = patchIt;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const getStack_1 = __importDefault(require("../utils/getStack"));
|
|
7
|
+
const isIgnored_1 = __importDefault(require("../utils/isIgnored"));
|
|
8
|
+
const patchProcessOutput = (that, outputOptions) => {
|
|
9
|
+
const createOutputPatch = (method) => {
|
|
10
|
+
const patch = (...args) => {
|
|
11
|
+
const stack = (0, getStack_1.default)(patch);
|
|
12
|
+
if (!stack.includes('node_modules/@jest/console')) {
|
|
13
|
+
const owner = that.currentTestName;
|
|
14
|
+
const message = args[0].toString();
|
|
15
|
+
if (!(0, isIgnored_1.default)(message, outputOptions.ignore)) {
|
|
16
|
+
that.leakRecords.get(owner)?.processOutputs.push({
|
|
17
|
+
method,
|
|
18
|
+
stack,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return that.original[method](...args);
|
|
23
|
+
};
|
|
24
|
+
return patch;
|
|
25
|
+
};
|
|
26
|
+
outputOptions.methods.forEach((method) => {
|
|
27
|
+
process[method].write = createOutputPatch(method);
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
exports.default = patchProcessOutput;
|
|
@@ -1,32 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
3
4
|
const patchPromiseConcurrency = (that) => {
|
|
4
5
|
const env = that.global;
|
|
6
|
+
const getRootIds = () => {
|
|
7
|
+
let triggerId = (0, node_async_hooks_1.executionAsyncId)();
|
|
8
|
+
const rootIds = [triggerId];
|
|
9
|
+
while (triggerId !== that.asyncRoot && triggerId) {
|
|
10
|
+
triggerId = that.asyncIdToParentId.get(triggerId);
|
|
11
|
+
rootIds.push(triggerId);
|
|
12
|
+
}
|
|
13
|
+
return rootIds;
|
|
14
|
+
};
|
|
15
|
+
const cleanPromise = (promises, promise, asyncId) => {
|
|
16
|
+
that.asyncIdToPromise.delete(asyncId);
|
|
17
|
+
that.promiseOwner.delete(asyncId);
|
|
18
|
+
that.asyncIdToParentId.delete(asyncId);
|
|
19
|
+
promises.delete(promise);
|
|
20
|
+
};
|
|
21
|
+
const removeChildPromises = (promises, concurrentPromises, rootIds) => {
|
|
22
|
+
if (promises) {
|
|
23
|
+
concurrentPromises.forEach((concurrentPromise) => {
|
|
24
|
+
const leak = promises.get(concurrentPromise);
|
|
25
|
+
if (leak) {
|
|
26
|
+
let asyncId = leak.asyncId;
|
|
27
|
+
// Promise.race, Promise.any and Promise.all will create a new Promise for every entry that needs to be deleted
|
|
28
|
+
promises.forEach((childLeak, key) => {
|
|
29
|
+
if (childLeak.parentAsyncId === asyncId) {
|
|
30
|
+
cleanPromise(promises, key, childLeak.asyncId);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
let promiseToDelete = concurrentPromise;
|
|
34
|
+
while (!rootIds.includes(asyncId) && asyncId) {
|
|
35
|
+
const parentId = that.asyncIdToParentId.get(asyncId);
|
|
36
|
+
cleanPromise(promises, promiseToDelete, asyncId);
|
|
37
|
+
asyncId = parentId;
|
|
38
|
+
promiseToDelete = that.asyncIdToPromise.get(asyncId);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
5
44
|
const concurrencyFactor = (fn) => (concurrentPromises) => {
|
|
6
45
|
const promises = that.leakRecords.get(that.currentTestName)?.promises;
|
|
46
|
+
const rootIds = getRootIds();
|
|
7
47
|
return fn(concurrentPromises).finally(() => {
|
|
8
|
-
|
|
9
|
-
concurrentPromises.forEach((concurrentPromise) => {
|
|
10
|
-
const leak = promises.get(concurrentPromise);
|
|
11
|
-
if (leak) {
|
|
12
|
-
const asyncId = leak?.asyncId;
|
|
13
|
-
that.asyncIdToPromise.delete(asyncId);
|
|
14
|
-
that.promiseOwner.delete(asyncId);
|
|
15
|
-
promises.delete(concurrentPromise);
|
|
16
|
-
// Promise.race and Promise.any will create a new Promise for every entry that needs to be deleted
|
|
17
|
-
promises.forEach((childLeak, key) => {
|
|
18
|
-
if (childLeak.parentAsyncId === asyncId) {
|
|
19
|
-
that.asyncIdToPromise.delete(childLeak.asyncId);
|
|
20
|
-
that.promiseOwner.delete(childLeak.asyncId);
|
|
21
|
-
promises.delete(key);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
}
|
|
48
|
+
removeChildPromises(promises, concurrentPromises, rootIds);
|
|
27
49
|
});
|
|
28
50
|
};
|
|
29
51
|
env.Promise.race = concurrencyFactor(env.Promise.race.bind(env.Promise));
|
|
30
52
|
env.Promise.any = concurrencyFactor(env.Promise.any.bind(env.Promise));
|
|
53
|
+
const originalPromiseAll = env.Promise.all.bind(env.Promise);
|
|
54
|
+
env.Promise.all = (concurrentPromises) => {
|
|
55
|
+
const promises = that.leakRecords.get(that.currentTestName)?.promises;
|
|
56
|
+
const rootIds = getRootIds();
|
|
57
|
+
return originalPromiseAll(concurrentPromises).catch((error) => {
|
|
58
|
+
removeChildPromises(promises, concurrentPromises, rootIds);
|
|
59
|
+
throw error;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
31
62
|
};
|
|
32
63
|
exports.default = patchPromiseConcurrency;
|
package/dist/patch/timers.d.ts
CHANGED
package/dist/patch/timers.js
CHANGED
|
@@ -5,13 +5,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const getStack_1 = __importDefault(require("../utils/getStack"));
|
|
7
7
|
const consts_1 = require("../consts");
|
|
8
|
+
const isIgnored_1 = __importDefault(require("../utils/isIgnored"));
|
|
8
9
|
const timers = (that) => {
|
|
9
10
|
const env = that.global;
|
|
11
|
+
const report = that.options.report;
|
|
10
12
|
env.setTimeout = Object.assign(function (callback, delay) {
|
|
11
13
|
const owner = that.currentTestName;
|
|
12
14
|
const leakRecord = that.leakRecords.get(owner);
|
|
15
|
+
const stack = (0, getStack_1.default)(env.setTimeout);
|
|
16
|
+
const isAllowed = report.timers && !(0, isIgnored_1.default)(stack, report.timers.ignore);
|
|
13
17
|
const timerId = that.original.setTimeout(() => {
|
|
14
|
-
if (leakRecord) {
|
|
18
|
+
if (leakRecord && isAllowed) {
|
|
15
19
|
if (owner !== consts_1.MAIN_THREAD && delay) {
|
|
16
20
|
leakRecord.totalDelay += delay;
|
|
17
21
|
}
|
|
@@ -22,27 +26,27 @@ const timers = (that) => {
|
|
|
22
26
|
leakRecord?.timers.set(timerId, {
|
|
23
27
|
type: 'timeout',
|
|
24
28
|
delay: delay || 0,
|
|
25
|
-
stack
|
|
26
|
-
|
|
29
|
+
stack,
|
|
30
|
+
isAllowed,
|
|
27
31
|
});
|
|
28
32
|
return timerId;
|
|
29
33
|
}, that.original.setTimeout);
|
|
30
34
|
env.setInterval = Object.assign(function (callback, delay) {
|
|
31
35
|
const owner = that.currentTestName;
|
|
36
|
+
const leakRecord = that.leakRecords.get(owner);
|
|
37
|
+
const stack = (0, getStack_1.default)(env.setInterval);
|
|
38
|
+
const isAllowed = report.timers && !(0, isIgnored_1.default)(stack, report.timers.ignore);
|
|
32
39
|
const intervalId = that.original.setInterval(() => {
|
|
33
|
-
if (owner !== consts_1.MAIN_THREAD && delay) {
|
|
34
|
-
|
|
35
|
-
if (leakRecord) {
|
|
36
|
-
leakRecord.totalDelay += delay;
|
|
37
|
-
}
|
|
40
|
+
if (isAllowed && owner !== consts_1.MAIN_THREAD && delay && leakRecord) {
|
|
41
|
+
leakRecord.totalDelay += delay;
|
|
38
42
|
}
|
|
39
43
|
callback();
|
|
40
44
|
}, delay);
|
|
41
45
|
that.leakRecords.get(owner)?.timers.set(intervalId, {
|
|
42
46
|
type: 'interval',
|
|
43
47
|
delay: delay || 0,
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
isAllowed,
|
|
49
|
+
stack,
|
|
46
50
|
});
|
|
47
51
|
return intervalId;
|
|
48
52
|
}, that.original.setInterval);
|