mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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
This commit is contained in:
committed by
Alex Rickabaugh
parent
6aa40f6c04
commit
62c6e3b7ee
@@ -119,4 +119,7 @@ BUNDLES_ENTRY_POINTS = {
|
|||||||
"zone-testing": {
|
"zone-testing": {
|
||||||
"entrypoint": _DIR + "testing/rollup-zone-testing",
|
"entrypoint": _DIR + "testing/rollup-zone-testing",
|
||||||
},
|
},
|
||||||
|
"vitest-patch": {
|
||||||
|
"entrypoint": _DIR + "vitest/rollup-vitest",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.dev/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {patchVitest} from './vitest';
|
||||||
|
|
||||||
|
patchVitest(Zone);
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google LLC All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.dev/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ZoneType} from '../zone-impl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ZoneType with additional testing related members added.
|
||||||
|
* The additional members must be patched onto the type via the
|
||||||
|
* `zone.js/testing` package entry point.
|
||||||
|
*/
|
||||||
|
interface TestingZoneType extends ZoneType {
|
||||||
|
ProxyZoneSpec?: typeof import('../zone-spec/proxy').ProxyZoneSpec;
|
||||||
|
SyncTestZoneSpec?: {new (namePrefix: string): ZoneSpec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of method names for the describe/suite factories.
|
||||||
|
* Example: `describe.skip('...', () => { ... });`
|
||||||
|
* Sourced from https://vitest.dev/api/#describe
|
||||||
|
*/
|
||||||
|
const DESCRIBE_FACTORY_NAMES = [
|
||||||
|
'skip',
|
||||||
|
'skipIf',
|
||||||
|
'runIf',
|
||||||
|
'only',
|
||||||
|
'concurrent',
|
||||||
|
'sequential',
|
||||||
|
'shuffle',
|
||||||
|
'todo',
|
||||||
|
'each',
|
||||||
|
'for',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of method names for the test/it factories.
|
||||||
|
* Example: `test.skip('...', () => { ... });`
|
||||||
|
* Sourced from https://vitest.dev/api/#test
|
||||||
|
*/
|
||||||
|
const TEST_FACTORY_NAMES = [
|
||||||
|
'skip',
|
||||||
|
'skipIf',
|
||||||
|
'runIf',
|
||||||
|
'only',
|
||||||
|
'concurrent',
|
||||||
|
'sequential',
|
||||||
|
'shuffle',
|
||||||
|
'todo',
|
||||||
|
'each',
|
||||||
|
'for',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function patchVitest(Zone: ZoneType): void {
|
||||||
|
Zone.__load_patch('vitest', (context: any, Zone: TestingZoneType) => {
|
||||||
|
// Vitest global variable set by the Vitest runner during test execution
|
||||||
|
const vitestGlobal = context['vitest'] as {['__zone_patch__']?: boolean} | undefined;
|
||||||
|
|
||||||
|
// Skip patching if vitest is not present or has already been patched
|
||||||
|
if (typeof vitestGlobal === 'undefined' || vitestGlobal['__zone_patch__']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vitestGlobal['__zone_patch__'] = true;
|
||||||
|
|
||||||
|
// Ensure other testing related Zone.js patches have been applied
|
||||||
|
if (!Zone.ProxyZoneSpec) {
|
||||||
|
throw new Error('Missing ProxyZoneSpec');
|
||||||
|
}
|
||||||
|
if (!Zone.SyncTestZoneSpec) {
|
||||||
|
throw new Error('Missing SyncTestZoneSpec');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup testing related Zone instances
|
||||||
|
const rootZone = Zone.current;
|
||||||
|
const syncZone = rootZone.fork(new Zone.SyncTestZoneSpec('vitest.describe'));
|
||||||
|
const proxyZone = rootZone.fork(new Zone.ProxyZoneSpec());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a function wrapping the body of a vitest `describe` block to execute in a
|
||||||
|
* synchronous-only zone.
|
||||||
|
*/
|
||||||
|
function wrapDescribeInZone(describeBody: Function): Function {
|
||||||
|
return function (this: unknown, ...args: unknown[]) {
|
||||||
|
return syncZone.run(describeBody, this, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a function wrapping the body of a vitest `it/beforeEach/afterEach` block to
|
||||||
|
* execute in a ProxyZone zone.
|
||||||
|
* This will run in the `proxyZone`.
|
||||||
|
*/
|
||||||
|
function wrapTestInZone(testBody: Function): Function {
|
||||||
|
if (typeof testBody !== 'function') {
|
||||||
|
return testBody;
|
||||||
|
}
|
||||||
|
const wrappedFunc = function () {
|
||||||
|
return proxyZone.run(testBody, null, arguments as any);
|
||||||
|
};
|
||||||
|
// Update the length of wrappedFunc to be the same as the length of the testBody
|
||||||
|
// So vitest core can handle whether the test function has `done()` or not correctly
|
||||||
|
Object.defineProperty(wrappedFunc, 'length', {
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
});
|
||||||
|
wrappedFunc.length = testBody.length;
|
||||||
|
return wrappedFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
['suite', 'describe'].forEach((methodName) => {
|
||||||
|
let originalVitestFn: Function & Record<(typeof DESCRIBE_FACTORY_NAMES)[number], Function> =
|
||||||
|
context[methodName];
|
||||||
|
// Skip if already patched
|
||||||
|
if (context[Zone.__symbol__(methodName)]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context[Zone.__symbol__(methodName)] = originalVitestFn;
|
||||||
|
context[methodName] = function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
|
||||||
|
args[1] = wrapDescribeInZone(args[1]);
|
||||||
|
return originalVitestFn.apply(this, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const factoryName of DESCRIBE_FACTORY_NAMES) {
|
||||||
|
context[methodName][factoryName] = function (this: unknown, ...factoryArgs: unknown[]) {
|
||||||
|
const originalDescribeFn = originalVitestFn.apply(this, factoryArgs);
|
||||||
|
return function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
|
||||||
|
args[1] = wrapDescribeInZone(args[1]);
|
||||||
|
return originalDescribeFn.apply(this, args);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
['it', 'test'].forEach((methodName) => {
|
||||||
|
let originalVitestFn: Function & Record<(typeof TEST_FACTORY_NAMES)[number], Function> =
|
||||||
|
context[methodName];
|
||||||
|
// Skip if already patched
|
||||||
|
if (context[Zone.__symbol__(methodName)]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context[Zone.__symbol__(methodName)] = originalVitestFn;
|
||||||
|
context[methodName] = function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
|
||||||
|
args[1] = wrapTestInZone(args[1]);
|
||||||
|
return originalVitestFn.apply(this, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const factoryName of TEST_FACTORY_NAMES) {
|
||||||
|
context[methodName][factoryName] = function (this: unknown, ...factoryArgs: unknown[]) {
|
||||||
|
return function (this: unknown, ...args: [unknown, Function, ...unknown[]]) {
|
||||||
|
args[1] = wrapTestInZone(args[1]);
|
||||||
|
return originalVitestFn.apply(this, factoryArgs).apply(this, args);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach((methodName) => {
|
||||||
|
const originalVitestFn: Function = context[methodName];
|
||||||
|
if (context[Zone.__symbol__(methodName)]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context[Zone.__symbol__(methodName)] = originalVitestFn;
|
||||||
|
context[methodName] = function (this: unknown, ...args: [Function, ...unknown[]]) {
|
||||||
|
args[0] = wrapTestInZone(args[0]);
|
||||||
|
return originalVitestFn.apply(this, args);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -123,6 +123,8 @@ describe('Zone.js npm_package', () => {
|
|||||||
'sync-test.min.js',
|
'sync-test.min.js',
|
||||||
'task-tracking.js',
|
'task-tracking.js',
|
||||||
'task-tracking.min.js',
|
'task-tracking.min.js',
|
||||||
|
'vitest-patch.js',
|
||||||
|
'vitest-patch.min.js',
|
||||||
'webapis-media-query.js',
|
'webapis-media-query.js',
|
||||||
'webapis-media-query.min.js',
|
'webapis-media-query.min.js',
|
||||||
'webapis-notification.js',
|
'webapis-notification.js',
|
||||||
|
|||||||
Reference in New Issue
Block a user