jest-doctor 2.0.8 → 2.1.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.
- package/dist/createEnvMixin.js +0 -4
- package/dist/patch/domListeners.js +18 -6
- package/dist/patch/timers.js +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils/analyzeCallback.js +19 -22
- package/dist/utils/cleanupAfterTest.js +4 -1
- package/dist/utils/normalizeOptions.js +1 -1
- package/package.json +97 -97
package/dist/createEnvMixin.js
CHANGED
|
@@ -43,10 +43,6 @@ const createEnvMixin = (Environment) => {
|
|
|
43
43
|
testPath;
|
|
44
44
|
aggregatedReport;
|
|
45
45
|
tearDownError;
|
|
46
|
-
promiseToAsyncId = new Map();
|
|
47
|
-
asyncRoot = 0;
|
|
48
|
-
asyncIdToParentId = new Map();
|
|
49
|
-
asyncIdToPromise = new Map();
|
|
50
46
|
asyncStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
51
47
|
testTimeout;
|
|
52
48
|
constructor(config, context) {
|
|
@@ -22,6 +22,9 @@ const patchDOMListeners = (that, listenerOptions) => {
|
|
|
22
22
|
recordCapture === capture);
|
|
23
23
|
});
|
|
24
24
|
if (index !== -1) {
|
|
25
|
+
if (options && options.signal) {
|
|
26
|
+
options.signal.removeEventListener('abort', leak.domListeners[index].abort);
|
|
27
|
+
}
|
|
25
28
|
leak.domListeners.splice(index, 1);
|
|
26
29
|
}
|
|
27
30
|
}
|
|
@@ -31,20 +34,29 @@ const patchDOMListeners = (that, listenerOptions) => {
|
|
|
31
34
|
if (leak) {
|
|
32
35
|
const stack = (0, getStack_1.default)(window.addEventListener);
|
|
33
36
|
if (!(0, isIgnored_1.default)(stack, listenerOptions.ignoreStack)) {
|
|
37
|
+
const abort = () => {
|
|
38
|
+
removeEventLeak(event, listener, options);
|
|
39
|
+
};
|
|
34
40
|
leak.domListeners.push({
|
|
35
41
|
event: event,
|
|
36
42
|
listener,
|
|
37
43
|
stack,
|
|
38
44
|
options,
|
|
45
|
+
abort,
|
|
39
46
|
});
|
|
47
|
+
if (typeof options === 'object') {
|
|
48
|
+
if (options.signal) {
|
|
49
|
+
options.signal.addEventListener('abort', abort);
|
|
50
|
+
}
|
|
51
|
+
if (options.once) {
|
|
52
|
+
return originalWindowAddEventListener(event, function (...args) {
|
|
53
|
+
removeEventLeak(event, listener, options);
|
|
54
|
+
listener.call(this, ...args);
|
|
55
|
+
}, options);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
40
58
|
}
|
|
41
59
|
}
|
|
42
|
-
if (typeof options === 'object' && options.once) {
|
|
43
|
-
return originalWindowAddEventListener(event, function (...args) {
|
|
44
|
-
removeEventLeak(event, listener, options);
|
|
45
|
-
listener.call(this, ...args);
|
|
46
|
-
}, options);
|
|
47
|
-
}
|
|
48
60
|
return originalWindowAddEventListener(event, listener, options);
|
|
49
61
|
};
|
|
50
62
|
const originalWindowRemoveEventListner = window.removeEventListener.bind(window);
|
package/dist/patch/timers.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ interface DOMListenerRecord {
|
|
|
37
37
|
capture?: boolean;
|
|
38
38
|
} | false | undefined;
|
|
39
39
|
stack: string;
|
|
40
|
+
abort: () => void;
|
|
40
41
|
}
|
|
41
42
|
export interface LeakRecord {
|
|
42
43
|
promises: Map<Promise<unknown>, PromiseRecord>;
|
|
@@ -129,7 +130,6 @@ export interface JestDoctorEnvironment extends JestEnvironment {
|
|
|
129
130
|
currentAfterEachCount: number;
|
|
130
131
|
options: NormalizedOptions;
|
|
131
132
|
aggregatedReport: AggregatedReport;
|
|
132
|
-
asyncRoot: number;
|
|
133
133
|
asyncStorage: AsyncLocalStorage<string>;
|
|
134
134
|
testTimeout: number;
|
|
135
135
|
}
|
|
@@ -22,28 +22,25 @@ const analyzeCallback = async (that, callback, testContext, timeout, isHook, tra
|
|
|
22
22
|
const leakRecord = (0, initLeakRecord_1.default)(that, testName);
|
|
23
23
|
let isRejected = false;
|
|
24
24
|
let timerId;
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
that.asyncRoot = 0;
|
|
45
|
-
(0, cleanupAfterTest_1.default)(that, leakRecord, testName, isRejected);
|
|
46
|
-
});
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
timerId = setTimeout(() => {
|
|
27
|
+
isRejected = true;
|
|
28
|
+
reject(getTimeoutError(timeout, isHook, trace));
|
|
29
|
+
}, timeout);
|
|
30
|
+
void Promise.resolve(that.asyncStorage.run(testName, () => callback.call(testContext)))
|
|
31
|
+
.then((returnValue) => {
|
|
32
|
+
if (!isRejected) {
|
|
33
|
+
(0, reportLeaks_1.default)(that, leakRecord);
|
|
34
|
+
resolve(returnValue);
|
|
35
|
+
}
|
|
36
|
+
}, (reason) => {
|
|
37
|
+
isRejected = true;
|
|
38
|
+
reject(reason);
|
|
39
|
+
})
|
|
40
|
+
.catch(reject);
|
|
41
|
+
}).finally(() => {
|
|
42
|
+
clearTimeout(timerId);
|
|
43
|
+
(0, cleanupAfterTest_1.default)(that, leakRecord, testName, isRejected);
|
|
47
44
|
});
|
|
48
45
|
};
|
|
49
46
|
exports.default = analyzeCallback;
|
|
@@ -15,9 +15,12 @@ const cleanupAfterTest = (that, leakRecord, testName, ignoreAfterEach = false) =
|
|
|
15
15
|
if (record.type === 'timeout') {
|
|
16
16
|
that.original.timer.clearTimeout(timerId);
|
|
17
17
|
}
|
|
18
|
-
else {
|
|
18
|
+
else if (record.type === 'interval') {
|
|
19
19
|
that.original.timer.clearInterval(timerId);
|
|
20
20
|
}
|
|
21
|
+
else {
|
|
22
|
+
that.original.timer.clearImmediate(timerId);
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
that.leakRecords.delete(testName);
|
|
@@ -104,7 +104,7 @@ const schema = zod
|
|
|
104
104
|
})
|
|
105
105
|
.default(DEFAULTS.report),
|
|
106
106
|
verbose: zod.boolean().default(false),
|
|
107
|
-
delayThreshold: zod.int().gte(0).default(0),
|
|
107
|
+
delayThreshold: zod.int().gte(0).or(zod.literal(Infinity)).default(0),
|
|
108
108
|
timerIsolation: zod
|
|
109
109
|
.enum(['afterEach', 'immediate', 'beforeEach'])
|
|
110
110
|
.default('afterEach'),
|
package/package.json
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "jest-doctor",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "Custom Jest environment that detects async leaks between tests, enforces test isolation, and prevents flaky failures in CI.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Stephan Dum",
|
|
7
|
-
"type": "commonjs",
|
|
8
|
-
"homepage": "https://stephan-dum.github.io/jest-doctor/",
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/stephan-dum/jest-doctor.git",
|
|
12
|
-
"directory": "packages/jest-doctor"
|
|
13
|
-
},
|
|
14
|
-
"exports": {
|
|
15
|
-
"./package.json": "./package.json",
|
|
16
|
-
"./createEnvMixin": "./dist/createEnvMixin.js",
|
|
17
|
-
"./env/node": "./dist/env/node.js",
|
|
18
|
-
"./env/jsdom": "./dist/env/jsdom.js",
|
|
19
|
-
"./reporter": "./dist/reporter.js"
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"dist"
|
|
23
|
-
],
|
|
24
|
-
"keywords": [
|
|
25
|
-
"jest",
|
|
26
|
-
"jest-doctor",
|
|
27
|
-
"leak detection",
|
|
28
|
-
"open handles",
|
|
29
|
-
"issue detection",
|
|
30
|
-
"testing",
|
|
31
|
-
"jest environment",
|
|
32
|
-
"test hygiene",
|
|
33
|
-
"flaky-tests",
|
|
34
|
-
"test-isolation",
|
|
35
|
-
"async-leaks",
|
|
36
|
-
"developer-tools",
|
|
37
|
-
"ci",
|
|
38
|
-
"test diagnostics"
|
|
39
|
-
],
|
|
40
|
-
"types": "./dist/types.d.ts",
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@dev/eslint": "1.0.0",
|
|
43
|
-
"@dev/tsconfig": "1.0.0",
|
|
44
|
-
"@jest/reporters": "30.
|
|
45
|
-
"@swc/core": "^1.15.
|
|
46
|
-
"@swc/jest": "^0.2.39",
|
|
47
|
-
"@testing-library/dom": "^10.4.1",
|
|
48
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
49
|
-
"@testing-library/react": "^16.3.2",
|
|
50
|
-
"@types/cross-spawn": "^6.0.6",
|
|
51
|
-
"@types/jest": "^30.0.0",
|
|
52
|
-
"@types/node": "^25.
|
|
53
|
-
"@types/react": "^19.2.14",
|
|
54
|
-
"@types/react-dom": "^19.2.3",
|
|
55
|
-
"c8": "^
|
|
56
|
-
"cross-spawn": "^7.0.6",
|
|
57
|
-
"eslint": "^
|
|
58
|
-
"jest": "30.
|
|
59
|
-
"jest-environment-jsdom": "^30.
|
|
60
|
-
"jest-environment-node": "^30.
|
|
61
|
-
"memfs": "^4.
|
|
62
|
-
"nyc": "^
|
|
63
|
-
"react": "^19.2.4",
|
|
64
|
-
"react-dom": "^19.2.4",
|
|
65
|
-
"shx": "^0.4.0",
|
|
66
|
-
"ts-jest": "^29.4.6",
|
|
67
|
-
"typescript": "5.9.3"
|
|
68
|
-
},
|
|
69
|
-
"peerDependencies": {
|
|
70
|
-
"jest": ">=28",
|
|
71
|
-
"jest-environment-jsdom": "*",
|
|
72
|
-
"jest-environment-node": "*"
|
|
73
|
-
},
|
|
74
|
-
"peerDependenciesMeta": {
|
|
75
|
-
"jest-environment-jsdom": {
|
|
76
|
-
"optional": true
|
|
77
|
-
},
|
|
78
|
-
"jest-environment-node": {
|
|
79
|
-
"optional": true
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"dependencies": {
|
|
83
|
-
"chalk": "4.1.2",
|
|
84
|
-
"zod": "^4.3.6"
|
|
85
|
-
},
|
|
86
|
-
"scripts": {
|
|
87
|
-
"prepare:publish": "shx rm -fR dist && yarn build && npm
|
|
88
|
-
"typecheck": "tsc",
|
|
89
|
-
"build": "tsc --project tsconfig.build.json",
|
|
90
|
-
"dev": "yarn typecheck --watch & yarn build --watch",
|
|
91
|
-
"lint": "eslint .",
|
|
92
|
-
"test:unit": "jest --coverage",
|
|
93
|
-
"test:matrix": "node ./e2e/runMatrix.mjs",
|
|
94
|
-
"test": "yarn matrix && yarn test:unit && yarn coverage:merge",
|
|
95
|
-
"coverage:merge": "nyc report"
|
|
96
|
-
}
|
|
97
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "jest-doctor",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Custom Jest environment that detects async leaks between tests, enforces test isolation, and prevents flaky failures in CI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Stephan Dum",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"homepage": "https://stephan-dum.github.io/jest-doctor/",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/stephan-dum/jest-doctor.git",
|
|
12
|
+
"directory": "packages/jest-doctor"
|
|
13
|
+
},
|
|
14
|
+
"exports": {
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
"./createEnvMixin": "./dist/createEnvMixin.js",
|
|
17
|
+
"./env/node": "./dist/env/node.js",
|
|
18
|
+
"./env/jsdom": "./dist/env/jsdom.js",
|
|
19
|
+
"./reporter": "./dist/reporter.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"jest",
|
|
26
|
+
"jest-doctor",
|
|
27
|
+
"leak detection",
|
|
28
|
+
"open handles",
|
|
29
|
+
"issue detection",
|
|
30
|
+
"testing",
|
|
31
|
+
"jest environment",
|
|
32
|
+
"test hygiene",
|
|
33
|
+
"flaky-tests",
|
|
34
|
+
"test-isolation",
|
|
35
|
+
"async-leaks",
|
|
36
|
+
"developer-tools",
|
|
37
|
+
"ci",
|
|
38
|
+
"test diagnostics"
|
|
39
|
+
],
|
|
40
|
+
"types": "./dist/types.d.ts",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@dev/eslint": "1.0.0",
|
|
43
|
+
"@dev/tsconfig": "1.0.0",
|
|
44
|
+
"@jest/reporters": "30.3.0",
|
|
45
|
+
"@swc/core": "^1.15.21",
|
|
46
|
+
"@swc/jest": "^0.2.39",
|
|
47
|
+
"@testing-library/dom": "^10.4.1",
|
|
48
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
49
|
+
"@testing-library/react": "^16.3.2",
|
|
50
|
+
"@types/cross-spawn": "^6.0.6",
|
|
51
|
+
"@types/jest": "^30.0.0",
|
|
52
|
+
"@types/node": "^25.5.0",
|
|
53
|
+
"@types/react": "^19.2.14",
|
|
54
|
+
"@types/react-dom": "^19.2.3",
|
|
55
|
+
"c8": "^11.0.0",
|
|
56
|
+
"cross-spawn": "^7.0.6",
|
|
57
|
+
"eslint": "^10.0.3",
|
|
58
|
+
"jest": "30.3.0",
|
|
59
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
60
|
+
"jest-environment-node": "^30.3.0",
|
|
61
|
+
"memfs": "^4.57.1",
|
|
62
|
+
"nyc": "^18.0.0",
|
|
63
|
+
"react": "^19.2.4",
|
|
64
|
+
"react-dom": "^19.2.4",
|
|
65
|
+
"shx": "^0.4.0",
|
|
66
|
+
"ts-jest": "^29.4.6",
|
|
67
|
+
"typescript": "5.9.3"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"jest": ">=28",
|
|
71
|
+
"jest-environment-jsdom": "*",
|
|
72
|
+
"jest-environment-node": "*"
|
|
73
|
+
},
|
|
74
|
+
"peerDependenciesMeta": {
|
|
75
|
+
"jest-environment-jsdom": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"jest-environment-node": {
|
|
79
|
+
"optional": true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"dependencies": {
|
|
83
|
+
"chalk": "4.1.2",
|
|
84
|
+
"zod": "^4.3.6"
|
|
85
|
+
},
|
|
86
|
+
"scripts": {
|
|
87
|
+
"prepare:publish": "shx rm -fR dist && yarn build && npm publish",
|
|
88
|
+
"typecheck": "tsc",
|
|
89
|
+
"build": "tsc --project tsconfig.build.json",
|
|
90
|
+
"dev": "yarn typecheck --watch & yarn build --watch",
|
|
91
|
+
"lint": "eslint .",
|
|
92
|
+
"test:unit": "jest --coverage",
|
|
93
|
+
"test:matrix": "node ./e2e/runMatrix.mjs",
|
|
94
|
+
"test": "yarn matrix && yarn test:unit && yarn coverage:merge",
|
|
95
|
+
"coverage:merge": "nyc report"
|
|
96
|
+
}
|
|
97
|
+
}
|