UNPKG

@angular/core

Version:

Angular - the core framework

1 lines 148 kB
{"version":3,"file":"testing.mjs","sources":["../../../../../../packages/core/testing/src/async.ts","../../../../../../packages/core/testing/src/component_fixture.ts","../../../../../../packages/core/testing/src/fake_async.ts","../../../../../../packages/core/src/metadata/resource_loading.ts","../../../../../../packages/core/testing/src/metadata_overrider.ts","../../../../../../packages/core/testing/src/resolvers.ts","../../../../../../packages/core/testing/src/r3_test_bed_compiler.ts","../../../../../../packages/core/testing/src/test_bed_common.ts","../../../../../../packages/core/testing/src/r3_test_bed.ts","../../../../../../packages/core/testing/src/test_bed.ts","../../../../../../packages/core/testing/src/test_hooks.ts","../../../../../../packages/core/testing/src/metadata_override.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 {ChangeDetectorRef, ComponentRef, DebugElement, ElementRef, getDebugNode, NgZone, RendererFactory2} from '@angular/core';\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: any) => void)|null = null;\n private _promise: Promise<any>|null = null;\n private _onUnstableSubscription: any /** TODO #9100 */ = null;\n private _onStableSubscription: any /** TODO #9100 */ = null;\n private _onMicrotaskEmptySubscription: any /** TODO #9100 */ = null;\n private _onErrorSubscription: any /** TODO #9100 */ = null;\n\n constructor(\n public componentRef: ComponentRef<T>, public ngZone: NgZone|null,\n 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 scheduleMicroTask(() => {\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 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 }\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 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\nfunction scheduleMicroTask(fn: Function) {\n Zone.current.scheduleMicroTask('scheduleMicrotask', 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 */\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 `fakeAysnc` 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 * Simulates the asynchronous passage of time for the timers in 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 {Type} from '../interface/type';\nimport {Component} from './directives';\n\n\n/**\n * Used to resolve resource URLs on `@Component` when used with JIT compilation.\n *\n * Example:\n * ```\n * @Component({\n * selector: 'my-comp',\n * templateUrl: 'my-comp.html', // This requires asynchronous resolution\n * })\n * class MyComponent{\n * }\n *\n * // Calling `renderComponent` will fail because `renderComponent` is a synchronous process\n * // and `MyComponent`'s `@Component.templateUrl` needs to be resolved asynchronously.\n *\n * // Calling `resolveComponentResources()` will resolve `@Component.templateUrl` into\n * // `@Component.template`, which allows `renderComponent` to proceed in a synchronous manner.\n *\n * // Use browser's `fetch()` function as the default resource resolution strategy.\n * resolveComponentResources(fetch).then(() => {\n * // After resolution all URLs have been converted into `template` strings.\n * renderComponent(MyComponent);\n * });\n *\n * ```\n *\n * NOTE: In AOT the resolution happens during compilation, and so there should be no need\n * to call this method outside JIT mode.\n *\n * @param resourceResolver a function which is responsible for returning a `Promise` to the\n * contents of the resolved URL. Browser's `fetch()` method is a good default implementation.\n */\nexport function resolveComponentResources(\n resourceResolver: (url: string) => (Promise<string|{text(): Promise<string>}>)): Promise<void> {\n // Store all promises which are fetching the resources.\n const componentResolved: Promise<void>[] = [];\n\n // Cache so that we don't fetch the same resource more than once.\n const urlMap = new Map<string, Promise<string>>();\n function cachedResourceResolve(url: string): Promise<string> {\n let promise = urlMap.get(url);\n if (!promise) {\n const resp = resourceResolver(url);\n urlMap.set(url, promise = resp.then(unwrapResponse));\n }\n return promise;\n }\n\n componentResourceResolutionQueue.forEach((component: Component, type: Type<any>) => {\n const promises: Promise<void>[] = [];\n if (component.templateUrl) {\n promises.push(cachedResourceResolve(component.templateUrl).then((template) => {\n component.template = template;\n }));\n }\n const styleUrls = component.styleUrls;\n const styles = component.styles || (component.styles = []);\n const styleOffset = component.styles.length;\n styleUrls && styleUrls.forEach((styleUrl, index) => {\n styles.push(''); // pre-allocate array.\n promises.push(cachedResourceResolve(styleUrl).then((style) => {\n styles[styleOffset + index] = style;\n styleUrls.splice(styleUrls.indexOf(styleUrl), 1);\n if (styleUrls.length == 0) {\n component.styleUrls = undefined;\n }\n }));\n });\n const fullyResolved = Promise.all(promises).then(() => componentDefResolved(type));\n componentResolved.push(fullyResolved);\n });\n clearResolutionOfComponentResourcesQueue();\n return Promise.all(componentResolved).then(() => undefined);\n}\n\nlet componentResourceResolutionQueue = new Map<Type<any>, Component>();\n\n// Track when existing ɵcmp for a Type is waiting on resources.\nconst componentDefPendingResolution = new Set<Type<any>>();\n\nexport function maybeQueueResolutionOfComponentResources(type: Type<any>, metadata: Component) {\n if (componentNeedsResolution(metadata)) {\n componentResourceResolutionQueue.set(type, metadata);\n componentDefPendingResolution.add(type);\n }\n}\n\nexport function isComponentDefPendingResolution(type: Type<any>): boolean {\n return componentDefPendingResolution.has(type);\n}\n\nexport function componentNeedsResolution(component: Component): boolean {\n return !!(\n (component.templateUrl && !component.hasOwnProperty('template')) ||\n component.styleUrls && component.styleUrls.length);\n}\nexport function clearResolutionOfComponentResourcesQueue(): Map<Type<any>, Component> {\n const old = componentResourceResolutionQueue;\n componentResourceResolutionQueue = new Map();\n return old;\n}\n\nexport function restoreComponentResolutionQueue(queue: Map<Type<any>, Component>): void {\n componentDefPendingResolution.clear();\n queue.forEach((_, type) => componentDefPendingResolution.add(type));\n componentResourceResolutionQueue = queue;\n}\n\nexport function isComponentResourceResolutionQueueEmpty() {\n return componentResourceResolutionQueue.size === 0;\n}\n\nfunction unwrapResponse(response: string|{text(): Promise<string>}): string|Promise<string> {\n return typeof response == 'string' ? response : response.text();\n}\n\nfunction componentDefResolved(type: Type<any>): void {\n componentDefPendingResolution.delete(type);\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 T : null;\n }\n }\n return null;\n }\n\n resolve(type: Type<any>): T|null {\n let resolved = 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, resolveForwardRef, Type, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID, ɵDirectiveDef as DirectiveDef, ɵgetInjectableDef as getInjectableDef, ɵ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, ɵsetLocaleId as setLocaleId, ɵtransitiveScopesFor as transitiveScopesFor, ɵɵInjectableDeclaration as InjectableDeclaration} from '@angular/core';\n\nimport {clearResolutionOfComponentResourcesQueue, isComponentDefPendingResolution, resolveComponentResources, restoreComponentResolutionQueue} from '../../src/metadata/resource_loading';\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\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 R3TestBedCompiler {\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 // TODO: we should support the case with multiple defs on a type\n private initialNgDefs = new Map<Type<any>, [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 moduleProvidersOverridden = new Set<Type<any>>();\n\n private testModuleType: NgModuleType<any>;\n private testModuleRef: NgModuleRef<any>|null = null;\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 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\n overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {\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.resolvers.component.addOverride(component, override);\n this.pendingComponents.add(component);\n }\n\n overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {\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.resolvers.pipe.addOverride(pipe, override);\n this.pendingPipes.add(pipe);\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.styleUrls && metadata.styleUrls.length > 0;\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 = overrideStyleUrls ? {template, styles: [], styleUrls: []} : {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 async compileComponents(): Promise<void> {\n this.clearComponentResolutionQueue();\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.applyProviderOverridesToModule(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.applyProviderOverridesToModule(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 needsAsyncResources = needsAsyncResources || isComponentDefPendingResolution(declaration);\n const metadata = this.resolvers.component.resolve(declaration);\n if (metadata === null) {\n throw invalidTypeError(declaration.name, 'Component');\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(moduleType => {\n this.storeFieldOfDefOnType(moduleType as any, NG_MOD_DEF, 'transitiveCompileScopes');\n (moduleType as any)[NG_MOD_DEF].transitiveCompileScopes = null;\n });\n }\n }\n\n const moduleToScope = new Map<Type<any>|TestingModuleOverride, NgModuleTransitiveScopes>();\n const getScopeOfModule =\n (moduleType: Type<any>|TestingModuleOverride): NgModuleTransitiveScopes => {\n if (!moduleToScope.has(moduleType)) {\n const isTestingModule = isTestingModuleOverride(moduleType);\n const realType = isTestingModule ? this.testModuleType : moduleType as Type<any>;\n moduleToScope.set(moduleType, transitiveScopesFor(realType));\n }\n return moduleToScope.get(moduleType)!;\n };\n\n this.componentToModuleScope.forEach((moduleType, componentType) => {\n const moduleScope = getScopeOfModule(moduleType);\n this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'directiveDefs');\n this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'pipeDefs');\n // `tView` that is stored on component def contains information about directives and pipes\n // that are in the scope of this component. Patching component scope will cause `tView` to be\n // changed. Store original `tView` before patching scope, so the `tView` (including scope\n // information) is restored back to its previous/original state before running next test.\n this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'tView');\n patchComponentDefWithScope((componentType as any).ɵcmp, moduleScope);\n });\n\n this.componentToModuleScope.clear();\n }\n\n private applyProviderOverrides(): void {\n const maybeApplyOverrides = (field: string) => (type: Type<any>) => {\n const resolver = field === NG_COMP_DEF ? this.resolvers.component : this.resolvers.directive;\n const metadata = resolver.resolve(type)!;\n if (this.hasProviderOverrides(metadata.providers)) {\n this.patchDefWithProviderOverrides(type, field);\n }\n };\n this.seenComponents.forEach(maybeApplyOverrides(NG_COMP_DEF));\n this.seenDirectives.forEach(maybeApplyOverrides(NG_DIR_DEF));\n\n this.seenComponents.clear();\n this.seenDirectives.clear();\n }\n\n private applyProviderOverridesToModule(moduleType: Type<any>): void {\n if (this.moduleProvidersOverridden.has(moduleType)) {\n return;\n }\n this.moduleProvidersOverridden.add(moduleType);\n\n const injectorDef: any = (moduleType as any)[NG_INJ_DEF];\n if (this.providerOverridesByToken.size > 0) {\n const providers = [\n ...injectorDef.providers,\n ...(this.providerOverridesByModule.get(moduleType as InjectorType<any>) || [])\n ];\n if (this.hasProviderOverrides(providers)) {\n this.maybeStoreNgDef(NG_INJ_DEF, moduleType);\n\n this.storeFieldOfDefOnType(moduleType, NG_INJ_DEF, 'providers');\n injectorDef.providers = this.getOverriddenProviders(providers);\n }\n\n // Apply provider overrides to imported modules recursively\n const moduleDef = (moduleType as any)[NG_MOD_DEF];\n const imports = maybeUnwrapFn(moduleDef.imports);\n for (const importedModule of imports) {\n this.applyProviderOverridesToModule(importedModule);\n }\n // Also override the providers on any ModuleWithProviders imports since those don't appear in\n // the moduleDef.\n for (const importedModule of flatten(injectorDef.imports)) {\n if (isModuleWithProviders(importedModule)) {\n this.defCleanupOps.push({\n object: importedModule,\n fieldName: 'providers',\n originalValue: importedModule.providers\n });\n importedModule.providers = this.getOverriddenProviders(importedModule.providers);\n }\n }\n }\n }\n\n private patchComponentsWithExistingStyles(): void {\n this.existingComponentStyles.forEach(\n (styles, type) => (type as any)[NG_COMP_DEF].styles = styles);\n this.existingComponentStyles.clear();\n }\n\n private queueTypeArray(arr: any[], moduleType: Type<any>|TestingModuleOverride): void {\n for (const value of arr) {\n if (Array.isArray(value)) {\n this.queueTypeArray(value, moduleType);\n } else {\n this.queueType(value, moduleType);\n }\n }\n }\n\n private recompileNgModule(ngModule: Type<any>, metadata: NgModule): void {\n // Cache the initial ngModuleDef as it will be overwritten.\n this.maybeStoreNgDef(NG_MOD_DEF, ngModule);\n this.maybeStoreNgDef(NG_INJ_DEF, ngModule);\n\n compileNgModuleDefs(ngModule as NgModuleType<any>, metadata);\n }\n\n private queueType(type: Type<any>, moduleType: Type<any>|TestingModuleOverride): void {\n const component = this.resolvers.component.resolve(type);\n if (component) {\n // Check whether a give Type has respective NG def (ɵcmp) and compile if def is\n // missing. That might happen in case a class without any Angular decorators extends another\n // class where Component/Directive/Pipe decorator is defined.\n if (isComponentDefPendingResolution(type) || !type.hasOwnProperty(NG_COMP_DEF)) {\n this.pendingComponents.add(type);\n }\n this.seenComponents.add(type);\n\n // Keep track of the module which declares this component, so later the component's scope\n // can be set correctly. If the component has already been recorded here, then one of several\n // cases is true:\n // * the module containing the component was imported multiple times (common).\n // * the component is declared in multiple modules (which is an error).\n // * the component was in 'declarations' of the testing module, and also in an imported module\n // in which case the module scope will be TestingModuleOverride.DECLARATION.\n // * overrideTemplateUsingTestingModule was called for the component in which case the module\n // scope will be TestingModuleOverride.OVERRIDE_TEMPLATE.\n //\n // If the component was previously in the testing module's 'declarations' (meaning the\n // current value is TestingModuleOverride.DECLARATION), then `moduleType` is the component's\n // real module, which was imported. This pattern is understood to mean that the component\n // should use its original sc