77 */
88
99import { ZoneType } from '../zone-impl' ;
10+ import { type ProxyZoneSpec } from './proxy' ;
1011
1112const global : any =
1213 ( typeof window === 'object' && window ) || ( typeof self === 'object' && self ) || globalThis . global ;
@@ -789,15 +790,13 @@ class FakeAsyncTestZoneSpec implements ZoneSpec {
789790
790791let _fakeAsyncTestZoneSpec : FakeAsyncTestZoneSpec | null = null ;
791792
792- type ProxyZoneSpecType = {
793- setDelegate ( delegateSpec : ZoneSpec ) : void ;
794- getDelegate ( ) : ZoneSpec ;
795- resetDelegate ( ) : void ;
796- } ;
797- function getProxyZoneSpec ( ) : { get ( ) : ProxyZoneSpecType ; assertPresent : ( ) => ProxyZoneSpecType } {
793+ function getProxyZoneSpec ( ) : typeof ProxyZoneSpec | undefined {
798794 return Zone && ( Zone as any ) [ 'ProxyZoneSpec' ] ;
799795}
800796
797+ let _sharedProxyZoneSpec : ProxyZoneSpec | null = null ;
798+ let _sharedProxyZone : Zone | null = null ;
799+
801800/**
802801 * Clears out the shared fake async zone for a test.
803802 * To be called in a global `beforeEach`.
@@ -809,8 +808,8 @@ export function resetFakeAsyncZone() {
809808 _fakeAsyncTestZoneSpec . unlockDatePatch ( ) ;
810809 }
811810 _fakeAsyncTestZoneSpec = null ;
812- // in node.js testing we may not have ProxyZoneSpec in which case there is nothing to reset.
813- getProxyZoneSpec ( ) && getProxyZoneSpec ( ) . assertPresent ( ) . resetDelegate ( ) ;
811+ getProxyZoneSpec ( ) ?. get ( ) ?. resetDelegate ( ) ;
812+ _sharedProxyZoneSpec ? .resetDelegate ( ) ;
814813}
815814
816815/**
@@ -841,8 +840,8 @@ export function fakeAsync(fn: Function, options: {flush?: boolean} = {}): (...ar
841840 const ProxyZoneSpec = getProxyZoneSpec ( ) ;
842841 if ( ! ProxyZoneSpec ) {
843842 throw new Error (
844- 'ProxyZoneSpec is needed for the async () test helper but could not be found. ' +
845- 'Please make sure that your environment includes zone.js/plugins/proxy ' ,
843+ 'ProxyZoneSpec is needed for the fakeAsync () test helper but could not be found. ' +
844+ 'Make sure that your environment includes zone-testing .js' ,
846845 ) ;
847846 }
848847 const proxyZoneSpec = ProxyZoneSpec . assertPresent ( ) ;
@@ -894,7 +893,7 @@ export function fakeAsync(fn: Function, options: {flush?: boolean} = {}): (...ar
894893 resetFakeAsyncZone ( ) ;
895894 }
896895 } ;
897- ( fakeAsyncFn as any ) . isFakeAsync = true ;
896+ fakeAsyncFn . isFakeAsync = true ;
898897 return fakeAsyncFn ;
899898}
900899
@@ -949,6 +948,51 @@ export function discardPeriodicTasks(): void {
949948 zoneSpec . pendingPeriodicTimers . length = 0 ;
950949}
951950
951+ /**
952+ * Wraps a function to be executed in a shared ProxyZone.
953+ *
954+ * If no shared ProxyZone exists, one is created and reused for subsequent calls.
955+ * Useful for wrapping test setup (beforeEach) and test execution (it) when test
956+ * runner patching isn't available or desired for setting up the ProxyZone.
957+ *
958+ * @param fn The function to wrap.
959+ * @returns A function that executes the original function within the shared ProxyZone.
960+ *
961+ * @experimental
962+ */
963+ export function withProxyZone < T extends Function > ( fn : T ) : T {
964+ const autoProxyFn : any = function ( this : unknown , ...args : any [ ] ) {
965+ const proxyZoneSpec = getProxyZoneSpec ( ) ;
966+ if ( proxyZoneSpec === undefined ) {
967+ throw new Error (
968+ 'ProxyZoneSpec is needed for the withProxyZone() test helper but could not be found. ' +
969+ 'Make sure that your environment includes zone-testing.js' ,
970+ ) ;
971+ }
972+
973+ const proxyZone = proxyZoneSpec . get ( ) !== undefined ? Zone . current : getOrCreateRootProxy ( ) ;
974+ return proxyZone . run ( fn , this , args ) ;
975+ } ;
976+ return autoProxyFn as T ;
977+ }
978+
979+ function getOrCreateRootProxy ( ) {
980+ const ProxyZoneSpec = getProxyZoneSpec ( ) ;
981+ if ( ProxyZoneSpec === undefined ) {
982+ throw new Error (
983+ 'ProxyZoneSpec is needed for withProxyZone but could not be found. ' +
984+ 'Make sure that your environment includes zone-testing.js' ,
985+ ) ;
986+ }
987+ // Ensure the shared ProxyZoneSpec instance exists
988+ if ( _sharedProxyZoneSpec === null ) {
989+ _sharedProxyZoneSpec = new ProxyZoneSpec ( ) as ProxyZoneSpec ;
990+ }
991+
992+ _sharedProxyZone = Zone . root . fork ( _sharedProxyZoneSpec ) ;
993+ return _sharedProxyZone ;
994+ }
995+
952996/**
953997 * Flush any pending microtasks.
954998 *
@@ -973,6 +1017,7 @@ export function patchFakeAsyncTest(Zone: ZoneType): void {
9731017 tick,
9741018 flush,
9751019 fakeAsync,
1020+ withProxyZone,
9761021 } ;
9771022 } ,
9781023 true ,
0 commit comments