Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 62c6e3b

Browse filesBrowse files
clydinalxhub
authored andcommitted
feat(zone.js): support vitest patching in zone.js/testing (#68395)
To support `fakeAsync` usage while using `vitest` as a test runner, Zone.js now provides patching when using the `zone.js/testing` package import. This patching is similar to that of the existing jasmine, mocha, and jest functionality. PR Close #68395
1 parent 6aa40f6 commit 62c6e3b
Copy full SHA for 62c6e3b

4 files changed

+192Lines changed: 192 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/zone.js/bundles.bzl‎

Copy file name to clipboardExpand all lines: packages/zone.js/bundles.bzl
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,7 @@ BUNDLES_ENTRY_POINTS = {
119119
"zone-testing": {
120120
"entrypoint": _DIR + "testing/rollup-zone-testing",
121121
},
122+
"vitest-patch": {
123+
"entrypoint": _DIR + "vitest/rollup-vitest",
124+
},
122125
}
Collapse file
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {patchVitest} from './vitest';
10+
11+
patchVitest(Zone);
Collapse file
+176Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {ZoneType} from '../zone-impl';
10+
11+
/**
12+
* The ZoneType with additional testing related members added.
13+
* The additional members must be patched onto the type via the
14+
* `zone.js/testing` package entry point.
15+
*/
16+
interface TestingZoneType extends ZoneType {
17+
ProxyZoneSpec?: typeof import('../zone-spec/proxy').ProxyZoneSpec;
18+
SyncTestZoneSpec?: {new (namePrefix: string): ZoneSpec};
19+
}
20+
21+
/**
22+
* The list of method names for the describe/suite factories.
23+
* Example: `describe.skip('...', () => { ... });`
24+
* Sourced from https://vitest.dev/api/#describe
25+
*/
26+
const DESCRIBE_FACTORY_NAMES = [
27+
'skip',
28+
'skipIf',
29+
'runIf',
30+
'only',
31+
'concurrent',
32+
'sequential',
33+
'shuffle',
34+
'todo',
35+
'each',
36+
'for',
37+
] as const;
38+
39+
/**
40+
* The list of method names for the test/it factories.
41+
* Example: `test.skip('...', () => { ... });`
42+
* Sourced from https://vitest.dev/api/#test
43+
*/
44+
const TEST_FACTORY_NAMES = [
45+
'skip',
46+
'skipIf',
47+
'runIf',
48+
'only',
49+
'concurrent',
50+
'sequential',
51+
'shuffle',
52+
'todo',
53+
'each',
54+
'for',
55+
] as const;
56+
57+
export function patchVitest(Zone: ZoneType): void {
58+
Zone.__load_patch('vitest', (context: any, Zone: TestingZoneType) => {
59+
// Vitest global variable set by the Vitest runner during test execution
60+
const vitestGlobal = context['vitest'] as {['__zone_patch__']?: boolean} | undefined;
61+
62+
// Skip patching if vitest is not present or has already been patched
63+
if (typeof vitestGlobal === 'undefined' || vitestGlobal['__zone_patch__']) {
64+
return;
65+
}
66+
vitestGlobal['__zone_patch__'] = true;
67+
68+
// Ensure other testing related Zone.js patches have been applied
69+
if (!Zone.ProxyZoneSpec) {
70+
throw new Error('Missing ProxyZoneSpec');
71+
}
72+
if (!Zone.SyncTestZoneSpec) {
73+
throw new Error('Missing SyncTestZoneSpec');
74+
}
75+
76+
// Setup testing related Zone instances
77+
const rootZone = Zone.current;
78+
const syncZone = rootZone.fork(new Zone.SyncTestZoneSpec('vitest.describe'));
79+
const proxyZone = rootZone.fork(new Zone.ProxyZoneSpec());
80+
81+
/**
82+
* Gets a function wrapping the body of a vitest `describe` block to execute in a
83+
* synchronous-only zone.
84+
*/
85+
function wrapDescribeInZone(describeBody: Function): Function {
86+
return function (this: unknown, ...args: unknown[]) {
87+
return syncZone.run(describeBody, this, args);
88+
};
89+
}
90+
91+
/**
92+
* Gets a function wrapping the body of a vitest `it/beforeEach/afterEach` block to
93+
* execute in a ProxyZone zone.
94+
* This will run in the `proxyZone`.
95+
*/
96+
function wrapTestInZone(testBody: Function): Function {
97+
if (typeof testBody !== 'function') {
98+
return testBody;
99+
}
100+
const wrappedFunc = function () {
101+
return proxyZone.run(testBody, null, arguments as any);
102+
};
103+
// Update the length of wrappedFunc to be the same as the length of the testBody
104+
// So vitest core can handle whether the test function has `done()` or not correctly
105+
Object.defineProperty(wrappedFunc, 'length', {
106+
configurable: true,
107+
writable: true,
108+
enumerable: false,
109+
});
110+
wrappedFunc.length = testBody.length;
111+
return wrappedFunc;
112+
}
113+
114+
['suite', 'describe'].forEach((methodName) => {
115+
let originalVitestFn: Function & Record<(typeof DESCRIBE_FACTORY_NAMES)[number], Function> =
116+
context[methodName];
117+
// Skip if already patched
118+
if (context[Zone.__symbol__(methodName)]) {
119+
return;
120+
}
121+
122+
context[Zone.__symbol__(methodName)] = originalVitestFn;
123+
context[methodName] = function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
124+
args[1] = wrapDescribeInZone(args[1]);
125+
return originalVitestFn.apply(this, args);
126+
};
127+
128+
for (const factoryName of DESCRIBE_FACTORY_NAMES) {
129+
context[methodName][factoryName] = function (this: unknown, ...factoryArgs: unknown[]) {
130+
const originalDescribeFn = originalVitestFn.apply(this, factoryArgs);
131+
return function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
132+
args[1] = wrapDescribeInZone(args[1]);
133+
return originalDescribeFn.apply(this, args);
134+
};
135+
};
136+
}
137+
});
138+
139+
['it', 'test'].forEach((methodName) => {
140+
let originalVitestFn: Function & Record<(typeof TEST_FACTORY_NAMES)[number], Function> =
141+
context[methodName];
142+
// Skip if already patched
143+
if (context[Zone.__symbol__(methodName)]) {
144+
return;
145+
}
146+
147+
context[Zone.__symbol__(methodName)] = originalVitestFn;
148+
context[methodName] = function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
149+
args[1] = wrapTestInZone(args[1]);
150+
return originalVitestFn.apply(this, args);
151+
};
152+
153+
for (const factoryName of TEST_FACTORY_NAMES) {
154+
context[methodName][factoryName] = function (this: unknown, ...factoryArgs: unknown[]) {
155+
return function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
156+
args[1] = wrapTestInZone(args[1]);
157+
return originalVitestFn.apply(this, factoryArgs).apply(this, args);
158+
};
159+
};
160+
}
161+
});
162+
163+
['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach((methodName) => {
164+
const originalVitestFn: Function = context[methodName];
165+
if (context[Zone.__symbol__(methodName)]) {
166+
return;
167+
}
168+
169+
context[Zone.__symbol__(methodName)] = originalVitestFn;
170+
context[methodName] = function (this: unknown, ...args: [Function, ...unknown[]]) {
171+
args[0] = wrapTestInZone(args[0]);
172+
return originalVitestFn.apply(this, args);
173+
};
174+
});
175+
});
176+
}
Collapse file

‎packages/zone.js/test/npm_package/npm_package.spec.ts‎

Copy file name to clipboardExpand all lines: packages/zone.js/test/npm_package/npm_package.spec.ts
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ describe('Zone.js npm_package', () => {
123123
'sync-test.min.js',
124124
'task-tracking.js',
125125
'task-tracking.min.js',
126+
'vitest-patch.js',
127+
'vitest-patch.min.js',
126128
'webapis-media-query.js',
127129
'webapis-media-query.min.js',
128130
'webapis-notification.js',

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.