UNPKG

@angular/core

Version:

Angular - the core framework

1 lines 171 kB
{"version":3,"file":"testing.mjs","sources":["../../../../../../packages/core/testing/src/async.ts","../../../../../../packages/core/testing/src/defer.ts","../../../../../../packages/core/testing/src/component_fixture.ts","../../../../../../packages/core/testing/src/fake_async.ts","../../../../../../packages/core/testing/src/test_bed_common.ts","../../../../../../packages/core/testing/src/metadata_overrider.ts","../../../../../../packages/core/testing/src/resolvers.ts","../../../../../../packages/core/testing/src/test_bed_compiler.ts","../../../../../../packages/core/testing/src/test_bed.ts","../../../../../../packages/core/testing/src/test_hooks.ts","../../../../../../packages/core/testing/src/testing.ts","../../../../../../packages/core/testing/public_api.ts","../../../../../../packages/core/testing/index.ts","../../../../../../packages/core/testing/testing.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n/**\n * Wraps a test function in an asynchronous test zone. The test will automatically\n * complete when all asynchronous calls within this zone are done. Can be used\n * to wrap an {@link inject} call.\n *\n * Example:\n *\n * ```\n * it('...', waitForAsync(inject([AClass], (object) => {\n * object.doSomething.then(() => {\n * expect(...);\n * })\n * });\n * ```\n *\n * @publicApi\n */\nexport function waitForAsync(fn: Function): (done: any) => any {\n const _Zone: any = typeof Zone !== 'undefined' ? Zone : null;\n if (!_Zone) {\n return function() {\n return Promise.reject(\n 'Zone is needed for the waitForAsync() test helper but could not be found. ' +\n 'Please make sure that your environment includes zone.js');\n };\n }\n const asyncTest = _Zone && _Zone[_Zone.__symbol__('asyncTest')];\n if (typeof asyncTest === 'function') {\n return asyncTest(fn);\n }\n return function() {\n return Promise.reject(\n 'zone-testing.js is needed for the async() test helper but could not be found. ' +\n 'Please make sure that your environment includes zone.js/testing');\n };\n}\n\n/**\n * @deprecated use `waitForAsync()`, (expected removal in v12)\n * @see {@link waitForAsync}\n * @publicApi\n * */\nexport function async(fn: Function): (done: any) => any {\n return waitForAsync(fn);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nimport {ɵCONTAINER_HEADER_OFFSET as CONTAINER_HEADER_OFFSET, ɵDeferBlockDetails as DeferBlockDetails, ɵDeferBlockState as DeferBlockState, ɵgetDeferBlocks as getDeferBlocks, ɵrenderDeferBlockState as renderDeferBlockState, ɵtriggerResourceLoading as triggerResourceLoading} from '@angular/core';\n\nimport type {ComponentFixture} from './component_fixture';\n\n/**\n * Represents an individual defer block for testing purposes.\n *\n * @publicApi\n * @developerPreview\n */\nexport class DeferBlockFixture {\n /** @nodoc */\n constructor(\n private block: DeferBlockDetails, private componentFixture: ComponentFixture<unknown>) {}\n\n /**\n * Renders the specified state of the defer fixture.\n * @param state the defer state to render\n */\n async render(state: DeferBlockState): Promise<void> {\n if (!hasStateTemplate(state, this.block)) {\n const stateAsString = getDeferBlockStateNameFromEnum(state);\n throw new Error(\n `Tried to render this defer block in the \\`${stateAsString}\\` state, ` +\n `but there was no @${stateAsString.toLowerCase()} block defined in a template.`);\n }\n if (state === DeferBlockState.Complete) {\n await triggerResourceLoading(this.block.tDetails, this.block.lView, this.block.tNode);\n }\n // If the `render` method is used explicitly - skip timer-based scheduling for\n // `@placeholder` and `@loading` blocks and render them immediately.\n const skipTimerScheduling = true;\n renderDeferBlockState(state, this.block.tNode, this.block.lContainer, skipTimerScheduling);\n this.componentFixture.detectChanges();\n return this.componentFixture.whenStable();\n }\n\n /**\n * Retrieves all nested child defer block fixtures\n * in a given defer block.\n */\n getDeferBlocks(): Promise<DeferBlockFixture[]> {\n const deferBlocks: DeferBlockDetails[] = [];\n // An LContainer that represents a defer block has at most 1 view, which is\n // located right after an LContainer header. Get a hold of that view and inspect\n // it for nested defer blocks.\n const deferBlockFixtures = [];\n if (this.block.lContainer.length >= CONTAINER_HEADER_OFFSET) {\n const lView = this.block.lContainer[CONTAINER_HEADER_OFFSET];\n getDeferBlocks(lView, deferBlocks);\n for (const block of deferBlocks) {\n deferBlockFixtures.push(new DeferBlockFixture(block, this.componentFixture));\n }\n }\n return Promise.resolve(deferBlockFixtures);\n }\n}\n\nfunction hasStateTemplate(state: DeferBlockState, block: DeferBlockDetails) {\n switch (state) {\n case DeferBlockState.Placeholder:\n return block.tDetails.placeholderTmplIndex !== null;\n case DeferBlockState.Loading:\n return block.tDetails.loadingTmplIndex !== null;\n case DeferBlockState.Error:\n return block.tDetails.errorTmplIndex !== null;\n case DeferBlockState.Complete:\n return true;\n default:\n return false;\n }\n}\n\nfunction getDeferBlockStateNameFromEnum(state: DeferBlockState) {\n switch (state) {\n case DeferBlockState.Placeholder:\n return 'Placeholder';\n case DeferBlockState.Loading:\n return 'Loading';\n case DeferBlockState.Error:\n return 'Error';\n default:\n return 'Main';\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nimport {ChangeDetectorRef, ComponentRef, DebugElement, ElementRef, getDebugNode, NgZone, RendererFactory2, ɵDeferBlockDetails as DeferBlockDetails, ɵFlushableEffectRunner as FlushableEffectRunner, ɵgetDeferBlocks as getDeferBlocks} from '@angular/core';\nimport {Subscription} from 'rxjs';\n\nimport {DeferBlockFixture} from './defer';\n\n\n/**\n * Fixture for debugging and testing a component.\n *\n * @publicApi\n */\nexport class ComponentFixture<T> {\n /**\n * The DebugElement associated with the root element of this component.\n */\n debugElement: DebugElement;\n\n /**\n * The instance of the root component class.\n */\n componentInstance: T;\n\n /**\n * The native element at the root of the component.\n */\n nativeElement: any;\n\n /**\n * The ElementRef for the element at the root of the component.\n */\n elementRef: ElementRef;\n\n /**\n * The ChangeDetectorRef for the component\n */\n changeDetectorRef: ChangeDetectorRef;\n\n private _renderer: RendererFactory2|null|undefined;\n private _isStable: boolean = true;\n private _isDestroyed: boolean = false;\n private _resolve: ((result: boolean) => void)|null = null;\n private _promise: Promise<boolean>|null = null;\n private _onUnstableSubscription: Subscription|null = null;\n private _onStableSubscription: Subscription|null = null;\n private _onMicrotaskEmptySubscription: Subscription|null = null;\n private _onErrorSubscription: Subscription|null = null;\n\n /** @nodoc */\n constructor(\n public componentRef: ComponentRef<T>, public ngZone: NgZone|null,\n private effectRunner: FlushableEffectRunner|null, private _autoDetect: boolean) {\n this.changeDetectorRef = componentRef.changeDetectorRef;\n this.elementRef = componentRef.location;\n this.debugElement = <DebugElement>getDebugNode(this.elementRef.nativeElement);\n this.componentInstance = componentRef.instance;\n this.nativeElement = this.elementRef.nativeElement;\n this.componentRef = componentRef;\n this.ngZone = ngZone;\n\n if (ngZone) {\n // Create subscriptions outside the NgZone so that the callbacks run oustide\n // of NgZone.\n ngZone.runOutsideAngular(() => {\n this._onUnstableSubscription = ngZone.onUnstable.subscribe({\n next: () => {\n this._isStable = false;\n }\n });\n this._onMicrotaskEmptySubscription = ngZone.onMicrotaskEmpty.subscribe({\n next: () => {\n if (this._autoDetect) {\n // Do a change detection run with checkNoChanges set to true to check\n // there are no changes on the second run.\n this.detectChanges(true);\n }\n }\n });\n this._onStableSubscription = ngZone.onStable.subscribe({\n next: () => {\n this._isStable = true;\n // Check whether there is a pending whenStable() completer to resolve.\n if (this._promise !== null) {\n // If so check whether there are no pending macrotasks before resolving.\n // Do this check in the next tick so that ngZone gets a chance to update the state of\n // pending macrotasks.\n queueMicrotask(() => {\n if (!ngZone.hasPendingMacrotasks) {\n if (this._promise !== null) {\n this._resolve!(true);\n this._resolve = null;\n this._promise = null;\n }\n }\n });\n }\n }\n });\n\n this._onErrorSubscription = ngZone.onError.subscribe({\n next: (error: any) => {\n throw error;\n }\n });\n });\n }\n }\n\n private _tick(checkNoChanges: boolean) {\n this.changeDetectorRef.detectChanges();\n if (checkNoChanges) {\n this.checkNoChanges();\n }\n }\n\n /**\n * Trigger a change detection cycle for the component.\n */\n detectChanges(checkNoChanges: boolean = true): void {\n this.effectRunner?.flush();\n if (this.ngZone != null) {\n // Run the change detection inside the NgZone so that any async tasks as part of the change\n // detection are captured by the zone and can be waited for in isStable.\n this.ngZone.run(() => {\n this._tick(checkNoChanges);\n });\n } else {\n // Running without zone. Just do the change detection.\n this._tick(checkNoChanges);\n }\n // Run any effects that were created/dirtied during change detection. Such effects might become\n // dirty in response to input signals changing.\n this.effectRunner?.flush();\n }\n\n /**\n * Do a change detection run to make sure there were no changes.\n */\n checkNoChanges(): void {\n this.changeDetectorRef.checkNoChanges();\n }\n\n /**\n * Set whether the fixture should autodetect changes.\n *\n * Also runs detectChanges once so that any existing change is detected.\n */\n autoDetectChanges(autoDetect: boolean = true) {\n if (this.ngZone == null) {\n throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set');\n }\n this._autoDetect = autoDetect;\n this.detectChanges();\n }\n\n /**\n * Return whether the fixture is currently stable or has async tasks that have not been completed\n * yet.\n */\n isStable(): boolean {\n return this._isStable && !this.ngZone!.hasPendingMacrotasks;\n }\n\n /**\n * Get a promise that resolves when the fixture is stable.\n *\n * This can be used to resume testing after events have triggered asynchronous activity or\n * asynchronous change detection.\n */\n whenStable(): Promise<any> {\n if (this.isStable()) {\n return Promise.resolve(false);\n } else if (this._promise !== null) {\n return this._promise;\n } else {\n this._promise = new Promise(res => {\n this._resolve = res;\n });\n return this._promise;\n }\n }\n\n /**\n * Retrieves all defer block fixtures in the component fixture.\n *\n * @developerPreview\n */\n getDeferBlocks(): Promise<DeferBlockFixture[]> {\n const deferBlocks: DeferBlockDetails[] = [];\n const lView = (this.componentRef.hostView as any)['_lView'];\n getDeferBlocks(lView, deferBlocks);\n\n const deferBlockFixtures = [];\n for (const block of deferBlocks) {\n deferBlockFixtures.push(new DeferBlockFixture(block, this));\n }\n\n return Promise.resolve(deferBlockFixtures);\n }\n\n\n private _getRenderer() {\n if (this._renderer === undefined) {\n this._renderer = this.componentRef.injector.get(RendererFactory2, null);\n }\n return this._renderer as RendererFactory2 | null;\n }\n\n /**\n * Get a promise that resolves when the ui state is stable following animations.\n */\n whenRenderingDone(): Promise<any> {\n const renderer = this._getRenderer();\n if (renderer && renderer.whenRenderingDone) {\n return renderer.whenRenderingDone();\n }\n return this.whenStable();\n }\n\n /**\n * Trigger component destruction.\n */\n destroy(): void {\n if (!this._isDestroyed) {\n this.componentRef.destroy();\n if (this._onUnstableSubscription != null) {\n this._onUnstableSubscription.unsubscribe();\n this._onUnstableSubscription = null;\n }\n if (this._onStableSubscription != null) {\n this._onStableSubscription.unsubscribe();\n this._onStableSubscription = null;\n }\n if (this._onMicrotaskEmptySubscription != null) {\n this._onMicrotaskEmptySubscription.unsubscribe();\n this._onMicrotaskEmptySubscription = null;\n }\n if (this._onErrorSubscription != null) {\n this._onErrorSubscription.unsubscribe();\n this._onErrorSubscription = null;\n }\n this._isDestroyed = true;\n }\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\nconst _Zone: any = typeof Zone !== 'undefined' ? Zone : null;\nconst fakeAsyncTestModule = _Zone && _Zone[_Zone.__symbol__('fakeAsyncTest')];\n\nconst fakeAsyncTestModuleNotLoadedErrorMessage =\n `zone-testing.js is needed for the fakeAsync() test helper but could not be found.\n Please make sure that your environment includes zone.js/testing`;\n\n/**\n * Clears out the shared fake async zone for a test.\n * To be called in a global `beforeEach`.\n *\n * @publicApi\n */\nexport function resetFakeAsyncZone(): void {\n if (fakeAsyncTestModule) {\n return fakeAsyncTestModule.resetFakeAsyncZone();\n }\n throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);\n}\n\n/**\n * Wraps a function to be executed in the `fakeAsync` zone:\n * - Microtasks are manually executed by calling `flushMicrotasks()`.\n * - Timers are synchronous; `tick()` simulates the asynchronous passage of time.\n *\n * If there are any pending timers at the end of the function, an exception is thrown.\n *\n * Can be used to wrap `inject()` calls.\n *\n * @param fn The function that you want to wrap in the `fakeAsync` zone.\n *\n * @usageNotes\n * ### Example\n *\n * {@example core/testing/ts/fake_async.ts region='basic'}\n *\n *\n * @returns The function wrapped to be executed in the `fakeAsync` zone.\n * Any arguments passed when calling this returned function will be passed through to the `fn`\n * function in the parameters when it is called.\n *\n * @publicApi\n */\nexport function fakeAsync(fn: Function): (...args: any[]) => any {\n if (fakeAsyncTestModule) {\n return fakeAsyncTestModule.fakeAsync(fn);\n }\n throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);\n}\n\n/**\n * Simulates the asynchronous passage of time for the timers in the `fakeAsync` zone.\n *\n * The microtasks queue is drained at the very start of this function and after any timer callback\n * has been executed.\n *\n * @param millis The number of milliseconds to advance the virtual timer.\n * @param tickOptions The options to pass to the `tick()` function.\n *\n * @usageNotes\n *\n * The `tick()` option is a flag called `processNewMacroTasksSynchronously`,\n * which determines whether or not to invoke new macroTasks.\n *\n * If you provide a `tickOptions` object, but do not specify a\n * `processNewMacroTasksSynchronously` property (`tick(100, {})`),\n * then `processNewMacroTasksSynchronously` defaults to true.\n *\n * If you omit the `tickOptions` parameter (`tick(100))`), then\n * `tickOptions` defaults to `{processNewMacroTasksSynchronously: true}`.\n *\n * ### Example\n *\n * {@example core/testing/ts/fake_async.ts region='basic'}\n *\n * The following example includes a nested timeout (new macroTask), and\n * the `tickOptions` parameter is allowed to default. In this case,\n * `processNewMacroTasksSynchronously` defaults to true, and the nested\n * function is executed on each tick.\n *\n * ```\n * it ('test with nested setTimeout', fakeAsync(() => {\n * let nestedTimeoutInvoked = false;\n * function funcWithNestedTimeout() {\n * setTimeout(() => {\n * nestedTimeoutInvoked = true;\n * });\n * };\n * setTimeout(funcWithNestedTimeout);\n * tick();\n * expect(nestedTimeoutInvoked).toBe(true);\n * }));\n * ```\n *\n * In the following case, `processNewMacroTasksSynchronously` is explicitly\n * set to false, so the nested timeout function is not invoked.\n *\n * ```\n * it ('test with nested setTimeout', fakeAsync(() => {\n * let nestedTimeoutInvoked = false;\n * function funcWithNestedTimeout() {\n * setTimeout(() => {\n * nestedTimeoutInvoked = true;\n * });\n * };\n * setTimeout(funcWithNestedTimeout);\n * tick(0, {processNewMacroTasksSynchronously: false});\n * expect(nestedTimeoutInvoked).toBe(false);\n * }));\n * ```\n *\n *\n * @publicApi\n */\nexport function tick(\n millis: number = 0, tickOptions: {processNewMacroTasksSynchronously: boolean} = {\n processNewMacroTasksSynchronously: true\n }): void {\n if (fakeAsyncTestModule) {\n return fakeAsyncTestModule.tick(millis, tickOptions);\n }\n throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);\n}\n\n/**\n * Flushes any pending microtasks and simulates the asynchronous passage of time for the timers in\n * the `fakeAsync` zone by\n * draining the macrotask queue until it is empty.\n *\n * @param maxTurns The maximum number of times the scheduler attempts to clear its queue before\n * throwing an error.\n * @returns The simulated time elapsed, in milliseconds.\n *\n * @publicApi\n */\nexport function flush(maxTurns?: number): number {\n if (fakeAsyncTestModule) {\n return fakeAsyncTestModule.flush(maxTurns);\n }\n throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);\n}\n\n/**\n * Discard all remaining periodic tasks.\n *\n * @publicApi\n */\nexport function discardPeriodicTasks(): void {\n if (fakeAsyncTestModule) {\n return fakeAsyncTestModule.discardPeriodicTasks();\n }\n throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);\n}\n\n/**\n * Flush any pending microtasks.\n *\n * @publicApi\n */\nexport function flushMicrotasks(): void {\n if (fakeAsyncTestModule) {\n return fakeAsyncTestModule.flushMicrotasks();\n }\n throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nimport {InjectionToken, SchemaMetadata, ɵDeferBlockBehavior as DeferBlockBehavior} from '@angular/core';\n\n\n/** Whether test modules should be torn down by default. */\nexport const TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT = true;\n\n/** Whether unknown elements in templates should throw by default. */\nexport const THROW_ON_UNKNOWN_ELEMENTS_DEFAULT = false;\n\n/** Whether unknown properties in templates should throw by default. */\nexport const THROW_ON_UNKNOWN_PROPERTIES_DEFAULT = false;\n\n/**\n * An abstract class for inserting the root test component element in a platform independent way.\n *\n * @publicApi\n */\nexport class TestComponentRenderer {\n insertRootElement(rootElementId: string) {}\n removeAllRootElements?() {}\n}\n\n/**\n * @publicApi\n */\nexport const ComponentFixtureAutoDetect = new InjectionToken<boolean>('ComponentFixtureAutoDetect');\n\n/**\n * @publicApi\n */\nexport const ComponentFixtureNoNgZone = new InjectionToken<boolean>('ComponentFixtureNoNgZone');\n\n/**\n * @publicApi\n */\nexport interface TestModuleMetadata {\n providers?: any[];\n declarations?: any[];\n imports?: any[];\n schemas?: Array<SchemaMetadata|any[]>;\n teardown?: ModuleTeardownOptions;\n /**\n * Whether NG0304 runtime errors should be thrown when unknown elements are present in component's\n * template. Defaults to `false`, where the error is simply logged. If set to `true`, the error is\n * thrown.\n * @see [NG8001](/errors/NG8001) for the description of the problem and how to fix it\n */\n errorOnUnknownElements?: boolean;\n /**\n * Whether errors should be thrown when unknown properties are present in component's template.\n * Defaults to `false`, where the error is simply logged.\n * If set to `true`, the error is thrown.\n * @see [NG8002](/errors/NG8002) for the description of the error and how to fix it\n */\n errorOnUnknownProperties?: boolean;\n\n /**\n * Whether defer blocks should behave with manual triggering or play through normally.\n * Defaults to `manual`.\n */\n deferBlockBehavior?: DeferBlockBehavior;\n}\n\n/**\n * @publicApi\n */\nexport interface TestEnvironmentOptions {\n /**\n * Configures the test module teardown behavior in `TestBed`.\n */\n teardown?: ModuleTeardownOptions;\n /**\n * Whether errors should be thrown when unknown elements are present in component's template.\n * Defaults to `false`, where the error is simply logged.\n * If set to `true`, the error is thrown.\n * @see [NG8001](/errors/NG8001) for the description of the error and how to fix it\n */\n errorOnUnknownElements?: boolean;\n /**\n * Whether errors should be thrown when unknown properties are present in component's template.\n * Defaults to `false`, where the error is simply logged.\n * If set to `true`, the error is thrown.\n * @see [NG8002](/errors/NG8002) for the description of the error and how to fix it\n */\n errorOnUnknownProperties?: boolean;\n}\n\n/**\n * Configures the test module teardown behavior in `TestBed`.\n * @publicApi\n */\nexport interface ModuleTeardownOptions {\n /** Whether the test module should be destroyed after every test. Defaults to `true`. */\n destroyAfterEach: boolean;\n\n /** Whether errors during test module destruction should be re-thrown. Defaults to `true`. */\n rethrowErrors?: boolean;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nimport {ɵstringify as stringify} from '@angular/core';\n\nimport {MetadataOverride} from './metadata_override';\n\ntype StringMap = {\n [key: string]: any\n};\n\nlet _nextReferenceId = 0;\n\nexport class MetadataOverrider {\n private _references = new Map<any, string>();\n /**\n * Creates a new instance for the given metadata class\n * based on an old instance and overrides.\n */\n overrideMetadata<C extends T, T>(\n metadataClass: {new(options: T): C;}, oldMetadata: C, override: MetadataOverride<T>): C {\n const props: StringMap = {};\n if (oldMetadata) {\n _valueProps(oldMetadata).forEach((prop) => props[prop] = (<any>oldMetadata)[prop]);\n }\n\n if (override.set) {\n if (override.remove || override.add) {\n throw new Error(`Cannot set and add/remove ${stringify(metadataClass)} at the same time!`);\n }\n setMetadata(props, override.set);\n }\n if (override.remove) {\n removeMetadata(props, override.remove, this._references);\n }\n if (override.add) {\n addMetadata(props, override.add);\n }\n return new metadataClass(<any>props);\n }\n}\n\nfunction removeMetadata(metadata: StringMap, remove: any, references: Map<any, string>) {\n const removeObjects = new Set<string>();\n for (const prop in remove) {\n const removeValue = remove[prop];\n if (Array.isArray(removeValue)) {\n removeValue.forEach((value: any) => {\n removeObjects.add(_propHashKey(prop, value, references));\n });\n } else {\n removeObjects.add(_propHashKey(prop, removeValue, references));\n }\n }\n\n for (const prop in metadata) {\n const propValue = metadata[prop];\n if (Array.isArray(propValue)) {\n metadata[prop] = propValue.filter(\n (value: any) => !removeObjects.has(_propHashKey(prop, value, references)));\n } else {\n if (removeObjects.has(_propHashKey(prop, propValue, references))) {\n metadata[prop] = undefined;\n }\n }\n }\n}\n\nfunction addMetadata(metadata: StringMap, add: any) {\n for (const prop in add) {\n const addValue = add[prop];\n const propValue = metadata[prop];\n if (propValue != null && Array.isArray(propValue)) {\n metadata[prop] = propValue.concat(addValue);\n } else {\n metadata[prop] = addValue;\n }\n }\n}\n\nfunction setMetadata(metadata: StringMap, set: any) {\n for (const prop in set) {\n metadata[prop] = set[prop];\n }\n}\n\nfunction _propHashKey(propName: any, propValue: any, references: Map<any, string>): string {\n let nextObjectId = 0;\n const objectIds = new Map<object, string>();\n const replacer = (key: any, value: any) => {\n if (value !== null && typeof value === 'object') {\n if (objectIds.has(value)) {\n return objectIds.get(value);\n }\n // Record an id for this object such that any later references use the object's id instead\n // of the object itself, in order to break cyclic pointers in objects.\n objectIds.set(value, `ɵobj#${nextObjectId++}`);\n\n // The first time an object is seen the object itself is serialized.\n return value;\n } else if (typeof value === 'function') {\n value = _serializeReference(value, references);\n }\n return value;\n };\n\n return `${propName}:${JSON.stringify(propValue, replacer)}`;\n}\n\nfunction _serializeReference(ref: any, references: Map<any, string>): string {\n let id = references.get(ref);\n if (!id) {\n id = `${stringify(ref)}${_nextReferenceId++}`;\n references.set(ref, id);\n }\n return id;\n}\n\n\nfunction _valueProps(obj: any): string[] {\n const props: string[] = [];\n // regular public props\n Object.keys(obj).forEach((prop) => {\n if (!prop.startsWith('_')) {\n props.push(prop);\n }\n });\n\n // getters\n let proto = obj;\n while (proto = Object.getPrototypeOf(proto)) {\n Object.keys(proto).forEach((protoProp) => {\n const desc = Object.getOwnPropertyDescriptor(proto, protoProp);\n if (!protoProp.startsWith('_') && desc && 'get' in desc) {\n props.push(protoProp);\n }\n });\n }\n return props;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nimport {Component, Directive, NgModule, Pipe, Type, ɵReflectionCapabilities as ReflectionCapabilities} from '@angular/core';\n\nimport {MetadataOverride} from './metadata_override';\nimport {MetadataOverrider} from './metadata_overrider';\n\nconst reflection = new ReflectionCapabilities();\n\n/**\n * Base interface to resolve `@Component`, `@Directive`, `@Pipe` and `@NgModule`.\n */\nexport interface Resolver<T> {\n addOverride(type: Type<any>, override: MetadataOverride<T>): void;\n setOverrides(overrides: Array<[Type<any>, MetadataOverride<T>]>): void;\n resolve(type: Type<any>): T|null;\n}\n\n/**\n * Allows to override ivy metadata for tests (via the `TestBed`).\n */\nabstract class OverrideResolver<T> implements Resolver<T> {\n private overrides = new Map<Type<any>, MetadataOverride<T>[]>();\n private resolved = new Map<Type<any>, T|null>();\n\n abstract get type(): any;\n\n addOverride(type: Type<any>, override: MetadataOverride<T>) {\n const overrides = this.overrides.get(type) || [];\n overrides.push(override);\n this.overrides.set(type, overrides);\n this.resolved.delete(type);\n }\n\n setOverrides(overrides: Array<[Type<any>, MetadataOverride<T>]>) {\n this.overrides.clear();\n overrides.forEach(([type, override]) => {\n this.addOverride(type, override);\n });\n }\n\n getAnnotation(type: Type<any>): T|null {\n const annotations = reflection.annotations(type);\n // Try to find the nearest known Type annotation and make sure that this annotation is an\n // instance of the type we are looking for, so we can use it for resolution. Note: there might\n // be multiple known annotations found due to the fact that Components can extend Directives (so\n // both Directive and Component annotations would be present), so we always check if the known\n // annotation has the right type.\n for (let i = annotations.length - 1; i >= 0; i--) {\n const annotation = annotations[i];\n const isKnownType = annotation instanceof Directive || annotation instanceof Component ||\n annotation instanceof Pipe || annotation instanceof NgModule;\n if (isKnownType) {\n return annotation instanceof this.type ? annotation as unknown as T : null;\n }\n }\n return null;\n }\n\n resolve(type: Type<any>): T|null {\n let resolved: T|null = this.resolved.get(type) || null;\n\n if (!resolved) {\n resolved = this.getAnnotation(type);\n if (resolved) {\n const overrides = this.overrides.get(type);\n if (overrides) {\n const overrider = new MetadataOverrider();\n overrides.forEach(override => {\n resolved = overrider.overrideMetadata(this.type, resolved!, override);\n });\n }\n }\n this.resolved.set(type, resolved);\n }\n\n return resolved;\n }\n}\n\n\nexport class DirectiveResolver extends OverrideResolver<Directive> {\n override get type() {\n return Directive;\n }\n}\n\nexport class ComponentResolver extends OverrideResolver<Component> {\n override get type() {\n return Component;\n }\n}\n\nexport class PipeResolver extends OverrideResolver<Pipe> {\n override get type() {\n return Pipe;\n }\n}\n\nexport class NgModuleResolver extends OverrideResolver<NgModule> {\n override get type() {\n return NgModule;\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.io/license\n */\n\nimport {ResourceLoader} from '@angular/compiler';\nimport {ApplicationInitStatus, Compiler, COMPILER_OPTIONS, Component, Directive, Injector, InjectorType, LOCALE_ID, ModuleWithComponentFactories, ModuleWithProviders, NgModule, NgModuleFactory, NgZone, Pipe, PlatformRef, Provider, provideZoneChangeDetection, resolveForwardRef, StaticProvider, Type, ɵclearResolutionOfComponentResourcesQueue, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵDEFER_BLOCK_CONFIG as DEFER_BLOCK_CONFIG, ɵDeferBlockBehavior as DeferBlockBehavior, ɵdepsTracker as depsTracker, ɵDirectiveDef as DirectiveDef, ɵgenerateStandaloneInDeclarationsError, ɵgetAsyncClassMetadata as getAsyncClassMetadata, ɵgetInjectableDef as getInjectableDef, ɵInternalEnvironmentProviders as InternalEnvironmentProviders, ɵisComponentDefPendingResolution, ɵisEnvironmentProviders as isEnvironmentProviders, ɵNG_COMP_DEF as NG_COMP_DEF, ɵNG_DIR_DEF as NG_DIR_DEF, ɵNG_INJ_DEF as NG_INJ_DEF, ɵNG_MOD_DEF as NG_MOD_DEF, ɵNG_PIPE_DEF as NG_PIPE_DEF, ɵNgModuleFactory as R3NgModuleFactory, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵNgModuleType as NgModuleType, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵresolveComponentResources, ɵrestoreComponentResolutionQueue, ɵsetLocaleId as setLocaleId, ɵtransitiveScopesFor as transitiveScopesFor, ɵUSE_RUNTIME_DEPS_TRACKER_FOR_JIT as USE_RUNTIME_DEPS_TRACKER_FOR_JIT, ɵɵInjectableDeclaration as InjectableDeclaration} from '@angular/core';\n\nimport {ComponentDef, ComponentType} from '../../src/render3';\n\nimport {MetadataOverride} from './metadata_override';\nimport {ComponentResolver, DirectiveResolver, NgModuleResolver, PipeResolver, Resolver} from './resolvers';\nimport {TestModuleMetadata} from './test_bed_common';\n\nenum TestingModuleOverride {\n DECLARATION,\n OVERRIDE_TEMPLATE,\n}\n\nfunction isTestingModuleOverride(value: unknown): value is TestingModuleOverride {\n return value === TestingModuleOverride.DECLARATION ||\n value === TestingModuleOverride.OVERRIDE_TEMPLATE;\n}\n\nfunction assertNoStandaloneComponents(\n types: Type<any>[], resolver: Resolver<any>, location: string) {\n types.forEach(type => {\n if (!getAsyncClassMetadata(type)) {\n const component = resolver.resolve(type);\n if (component && component.standalone) {\n throw new Error(ɵgenerateStandaloneInDeclarationsError(type, location));\n }\n }\n });\n}\n\n// Resolvers for Angular decorators\ntype Resolvers = {\n module: Resolver<NgModule>,\n component: Resolver<Directive>,\n directive: Resolver<Component>,\n pipe: Resolver<Pipe>,\n};\n\ninterface CleanupOperation {\n fieldName: string;\n object: any;\n originalValue: unknown;\n}\n\nexport class TestBedCompiler {\n private originalComponentResolutionQueue: Map<Type<any>, Component>|null = null;\n\n // Testing module configuration\n private declarations: Type<any>[] = [];\n private imports: Type<any>[] = [];\n private providers: Provider[] = [];\n private schemas: any[] = [];\n\n // Queues of components/directives/pipes that should be recompiled.\n private pendingComponents = new Set<Type<any>>();\n private pendingDirectives = new Set<Type<any>>();\n private pendingPipes = new Set<Type<any>>();\n\n // Keep track of all components and directives, so we can patch Providers onto defs later.\n private seenComponents = new Set<Type<any>>();\n private seenDirectives = new Set<Type<any>>();\n\n // Keep track of overridden modules, so that we can collect all affected ones in the module tree.\n private overriddenModules = new Set<NgModuleType<any>>();\n\n // Store resolved styles for Components that have template overrides present and `styleUrls`\n // defined at the same time.\n private existingComponentStyles = new Map<Type<any>, string[]>();\n\n private resolvers: Resolvers = initResolvers();\n\n private componentToModuleScope = new Map<Type<any>, Type<any>|TestingModuleOverride>();\n\n // Map that keeps initial version of component/directive/pipe defs in case\n // we compile a Type again, thus overriding respective static fields. This is\n // required to make sure we restore defs to their initial states between test runs.\n // Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of an\n // NgModule), store all of them in a map.\n private initialNgDefs = new Map<Type<any>, Map<string, PropertyDescriptor|undefined>>();\n\n // Array that keeps cleanup operations for initial versions of component/directive/pipe/module\n // defs in case TestBed makes changes to the originals.\n private defCleanupOps: CleanupOperation[] = [];\n\n private _injector: Injector|null = null;\n private compilerProviders: Provider[]|null = null;\n\n private providerOverrides: Provider[] = [];\n private rootProviderOverrides: Provider[] = [];\n // Overrides for injectables with `{providedIn: SomeModule}` need to be tracked and added to that\n // module's provider list.\n private providerOverridesByModule = new Map<InjectorType<any>, Provider[]>();\n private providerOverridesByToken = new Map<any, Provider>();\n private scopesWithOverriddenProviders = new Set<Type<any>>();\n\n private testModuleType: NgModuleType<any>;\n private testModuleRef: NgModuleRef<any>|null = null;\n\n private deferBlockBehavior = DeferBlockBehavior.Manual;\n\n constructor(private platform: PlatformRef, private additionalModuleTypes: Type<any>|Type<any>[]) {\n class DynamicTestModule {}\n this.testModuleType = DynamicTestModule as any;\n }\n\n setCompilerProviders(providers: Provider[]|null): void {\n this.compilerProviders = providers;\n this._injector = null;\n }\n\n configureTestingModule(moduleDef: TestModuleMetadata): void {\n // Enqueue any compilation tasks for the directly declared component.\n if (moduleDef.declarations !== undefined) {\n // Verify that there are no standalone components\n assertNoStandaloneComponents(\n moduleDef.declarations, this.resolvers.component,\n '\"TestBed.configureTestingModule\" call');\n this.queueTypeArray(moduleDef.declarations, TestingModuleOverride.DECLARATION);\n this.declarations.push(...moduleDef.declarations);\n }\n\n // Enqueue any compilation tasks for imported modules.\n if (moduleDef.imports !== undefined) {\n this.queueTypesFromModulesArray(moduleDef.imports);\n this.imports.push(...moduleDef.imports);\n }\n\n if (moduleDef.providers !== undefined) {\n this.providers.push(...moduleDef.providers);\n }\n\n if (moduleDef.schemas !== undefined) {\n this.schemas.push(...moduleDef.schemas);\n }\n\n this.deferBlockBehavior = moduleDef.deferBlockBehavior ?? DeferBlockBehavior.Manual;\n }\n\n overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {\n if (USE_RUNTIME_DEPS_TRACKER_FOR_JIT) {\n depsTracker.clearScopeCacheFor(ngModule);\n }\n this.overriddenModules.add(ngModule as NgModuleType<any>);\n\n // Compile the module right away.\n this.resolvers.module.addOverride(ngModule, override);\n const metadata = this.resolvers.module.resolve(ngModule);\n if (metadata === null) {\n throw invalidTypeError(ngModule.name, 'NgModule');\n }\n\n this.recompileNgModule(ngModule, metadata);\n\n // At this point, the module has a valid module def (ɵmod), but the override may have introduced\n // new declarations or imported modules. Ingest any possible new types and add them to the\n // current queue.\n this.queueTypesFromModulesArray([ngModule]);\n }\n\n overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {\n this.verifyNoStandaloneFlagOverrides(component, override);\n this.resolvers.component.addOverride(component, override);\n this.pendingComponents.add(component);\n }\n\n overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {\n this.verifyNoStandaloneFlagOverrides(directive, override);\n this.resolvers.directive.addOverride(directive, override);\n this.pendingDirectives.add(directive);\n }\n\n overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {\n this.verifyNoStandaloneFlagOverrides(pipe, override);\n this.resolvers.pipe.addOverride(pipe, override);\n this.pendingPipes.add(pipe);\n }\n\n private verifyNoStandaloneFlagOverrides(\n type: Type<any>, override: MetadataOverride<Component|Directive|Pipe>) {\n if (override.add?.hasOwnProperty('standalone') || override.set?.hasOwnProperty('standalone') ||\n override.remove?.hasOwnProperty('standalone')) {\n throw new Error(\n `An override for the ${type.name} class has the \\`standalone\\` flag. ` +\n `Changing the \\`standalone\\` flag via TestBed overrides is not supported.`);\n }\n }\n\n overrideProvider(\n token: any,\n provider: {useFactory?: Function, useValue?: any, deps?: any[], multi?: boolean}): void {\n let providerDef: Provider;\n if (provider.useFactory !== undefined) {\n providerDef = {\n provide: token,\n useFactory: provider.useFactory,\n deps: provider.deps || [],\n multi: provider.multi\n };\n } else if (provider.useValue !== undefined) {\n providerDef = {provide: token, useValue: provider.useValue, multi: provider.multi};\n } else {\n providerDef = {provide: token};\n }\n\n const injectableDef: InjectableDeclaration<any>|null =\n typeof token !== 'string' ? getInjectableDef(token) : null;\n const providedIn = injectableDef === null ? null : resolveForwardRef(injectableDef.providedIn);\n const overridesBucket =\n providedIn === 'root' ? this.rootProviderOverrides : this.providerOverrides;\n overridesBucket.push(providerDef);\n\n // Keep overrides grouped by token as well for fast lookups using token\n this.providerOverridesByToken.set(token, providerDef);\n if (injectableDef !== null && providedIn !== null && typeof providedIn !== 'string') {\n const existingOverrides = this.providerOverridesByModule.get(providedIn);\n if (existingOverrides !== undefined) {\n existingOverrides.push(providerDef);\n } else {\n this.providerOverridesByModule.set(providedIn, [providerDef]);\n }\n }\n }\n\n overrideTemplateUsingTestingModule(type: Type<any>, template: string): void {\n const def = (type as any)[NG_COMP_DEF];\n const hasStyleUrls = (): boolean => {\n const metadata = this.resolvers.component.resolve(type)! as Component;\n return !!metadata.styleUrl || !!metadata.styleUrls?.length;\n };\n const overrideStyleUrls = !!def && !ɵisComponentDefPendingResolution(type) && hasStyleUrls();\n\n // In Ivy, compiling a component does not require knowing the module providing the\n // component's scope, so overrideTemplateUsingTestingModule can be implemented purely via\n // overrideComponent. Important: overriding template requires full Component re-compilation,\n // which may fail in case styleUrls are also present (thus Component is considered as required\n // resolution). In order to avoid this, we preemptively set styleUrls to an empty array,\n // preserve current styles available on Component def and restore styles back once compilation\n // is complete.\n const override =\n overrideStyleUrls ? {template, styles: [], styleUrls: [], styleUrl: undefined} : {template};\n this.overrideComponent(type, {set: override});\n\n if (overrideStyleUrls && def.styles && def.styles.length > 0) {\n this.existingComponentStyles.set(type, def.styles);\n }\n\n // Set the component's scope to be the testing module.\n this.componentToModuleScope.set(type, TestingModuleOverride.OVERRIDE_TEMPLATE);\n }\n\n private async resolvePendingComponentsWithAsyncMetadata() {\n if (this.pendingComponents.size === 0) return;\n\n const promises = [];\n for (const component of this.pendingComponents) {\n const asyncMetadataPromise = getAsyncClassMetadata(component);\n if (asyncMetadataPromise) {\n promises.push(asyncMetadataPromise);\n }\n }\n\n const resolvedDeps = await Promise.all(promises);\n this.queueTypesFromModulesArray(resolvedDeps.flat(2));\n }\n\n async compileComponents(): Promise<void> {\n this.clearComponentResolutionQueue();\n\n // Wait for all async metadata for components that were\n // overridden, we need resolved metadata to perform an override\n // and re-compile a component.\n await this.resolvePendingComponentsWithAsyncMetadata();\n\n // Verify that there were no standalone components present in the `declarations` field\n // during the `TestBed.configureTestingModule` call. We perform this check here in addition\n // to the logic in the `configureTestingModule` function, since at this point we have\n // all async metadata resolved.\n assertNoStandaloneComponents(\n this.declarations, this.resolvers.component, '\"TestBed.configureTestingModule\" call');\n\n // Run compilers for all queued types.\n let needsAsyncResources = this.compileTypesSync();\n\n // compileComponents() should not be async unless it needs to be.\n if (needsAsyncResources) {\n let resourceLoader: ResourceLoader;\n let resolver = (url: string): Promise<string> => {\n if (!resourceLoader) {\n resourceLoader = this.injector.get(ResourceLoader);\n }\n return Promise.resolve(resourceLoader.get(url));\n };\n await ɵresolveComponentResources(resolver);\n }\n }\n\n finalize(): NgModuleRef<any> {\n // One last compile\n this.compileTypesSync();\n\n // Create the testing module itself.\n this.compileTestModule();\n\n this.applyTransitiveScopes();\n\n this.applyProviderOverrides();\n\n // Patch previously stored `styles` Component values (taken from ɵcmp), in case these\n // Components have `styleUrls` fields defined and template override was requested.\n this.patchComponentsWithExistingStyles();\n\n // Clear the componentToModuleScope map, so that future compilations don't reset the scope of\n // every component.\n this.componentToModuleScope.clear();\n\n const parentInjector = this.platform.injector;\n this.testModuleRef = new NgModuleRef(this.testModuleType, parentInjector, []);\n\n // ApplicationInitStatus.runInitializers() is marked @internal to core.\n // Cast it to any before accessing it.\n (this.testModuleRef.injector.get(ApplicationInitStatus) as any).runInitializers();\n\n // Set locale ID after running app initializers, since locale information might be updated while\n // running initializers. This is also consistent with the execution order while bootstrapping an\n // app (see `packages/core/src/application_ref.ts` file).\n const localeId = this.testModuleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID);\n setLocaleId(localeId);\n\n return this.testModuleRef;\n }\n\n /**\n * @internal\n */\n _compileNgModuleSync(moduleType: Type<any>): void {\n this.queueTypesFromModulesArray([moduleType]);\n this.compileTypesSync();\n this.applyProviderOverrides();\n this.applyProviderOverridesInScope(moduleType);\n this.applyTransitiveScopes();\n }\n\n /**\n * @internal\n */\n async _compileNgModuleAsync(moduleType: Type<any>): Promise<void> {\n this.queueTypesFromModulesArray([moduleType]);\n await this.compileComponents();\n this.applyProviderOverrides();\n this.applyProviderOverridesInScope(moduleType);\n this.applyTransitiveScopes();\n }\n\n /**\n * @internal\n */\n _getModuleResolver(): Resolver<NgModule> {\n return this.resolvers.module;\n }\n\n /**\n * @internal\n */\n _getComponentFactories(moduleType: NgModuleType): ComponentFactory<any>[] {\n return maybeUnwrapFn(moduleType.ɵmod.declarations).reduce((factories, declaration) => {\n const componentDef = (declaration as any).ɵcmp;\n componentDef && factories.push(new ComponentFactory(componentDef, this.testModuleRef!));\n return factories;\n }, [] as ComponentFactory<any>[]);\n }\n\n private compileTypesSync(): boolean {\n // Compile all queued components, directives, pipes.\n let needsAsyncResources = false;\n this.pendingComponents.forEach(declaration => {\n if (getAsyncClassMetadata(declaration)) {\n throw new Error(\n `Component '${declaration.name}' has unresolved metadata. ` +\n `Please call \\`await TestBed.compileComponents()\\` before running this test.`);\n }\n\n needsAsyncResources = needsAsyncResources || ɵisComponentDefPendingResolution(declaration);\n\n const metadata = this.resolvers.component.resolve(declaration);\n if (metadata === null) {\n throw invalidTypeError(declaration.name, 'Component');\n }\n\n this.maybeStoreNgDef(NG_COMP_DEF, declaration);\n compileComponent(declaration, metadata);\n });\n this.pendingComponents.clear();\n\n this.pendingDirectives.forEach(declaration => {\n const metadata = this.resolvers.directive.resolve(declaration);\n if (metadata === null) {\n throw invalidTypeError(declaration.name, 'Directive');\n }\n this.maybeStoreNgDef(NG_DIR_DEF, declaration);\n compileDirective(declaration, metadata);\n });\n this.pendingDirectives.clear();\n\n this.pendingPipes.forEach(declaration => {\n const metadata = this.resolvers.pipe.resolve(declaration);\n if (metadata === null) {\n throw invalidTypeError(declaration.name, 'Pipe');\n }\n this.maybeStoreNgDef(NG_PIPE_DEF, declaration);\n compilePipe(declaration, metadata);\n });\n this.pendingPipes.clear();\n\n return needsAsyncResources;\n }\n\n private applyTransitiveScopes(): void {\n if (this.overriddenModules.size > 0) {\n // Module overrides (via `TestBed.overrideModule`) might affect scopes that were previously\n // calculated and stored in `transitiveCompileScopes`. If module overrides are present,\n // collect all affected modules and reset scopes to force their re-calculation.\n const testingModuleDef = (this.testModuleType as any)[NG_MOD_DEF];\n const affectedModules = this.collectModulesAffectedByOverrides(testingModuleDef.imports);\n if (affectedModules.size > 0) {\n affectedModules.forEach(moduleTy