UNPKG

@angular/core

Version:

Angular - the core framework

2,560 lines 88.4 kB
/**
 * @license Angular v22.1.3
 * (c) 2010-2026 Google LLC. https://angular.dev/
 * License: MIT
 */

import { Subscription } from 'rxjs';
import { inject as inject$1, ErrorHandler, NgZone, EnvironmentInjector, CONTAINER_HEADER_OFFSET, InjectionToken, NoopNgZone, PendingTasksInternal, ZONELESS_ENABLED, ChangeDetectionScheduler, EffectScheduler, stringify, resolveForwardRef, NG_COMP_DEF, NG_DIR_DEF, NG_PIPE_DEF, NG_INJ_DEF, NG_MOD_DEF, ENVIRONMENT_INITIALIZER, INTERNAL_APPLICATION_ERROR_HANDLER, Injector, getInjectableDef, isEnvironmentProviders, runInInjectionContext, getComponentDef as getComponentDef$1 } from './_pending_tasks-chunk.mjs';
import * as i0 from '@angular/core';
import { Injectable, DeferBlockState, triggerResourceLoading, renderDeferBlockState, getDeferBlocks, DeferBlockBehavior, getDebugNode, RendererFactory2, ApplicationRef, Pipe, Directive, Component, NgModule, ReflectionCapabilities, depsTracker, isComponentDefPendingResolution, resolveComponentResources, NgModuleRef, ApplicationInitStatus, LOCALE_ID, DEFAULT_LOCALE_ID, setLocaleId, ComponentFactory, getAsyncClassMetadataFn, compileComponent, compileDirective, compilePipe, patchComponentDefWithScope, compileNgModuleDefs, clearResolutionOfComponentResourcesQueue, restoreComponentResolutionQueue, provideZonelessChangeDetectionInternal, Compiler, DEFER_BLOCK_CONFIG, ANIMATIONS_DISABLED, COMPILER_OPTIONS, transitiveScopesFor, generateStandaloneInDeclarationsError, NgModuleFactory, resetCompiledComponents, ɵsetUnknownElementStrictMode as _setUnknownElementStrictMode, ɵsetUnknownPropertyStrictMode as _setUnknownPropertyStrictMode, ɵgetUnknownElementStrictMode as _getUnknownElementStrictMode, ɵgetUnknownPropertyStrictMode as _getUnknownPropertyStrictMode, inferTagNameFromDefinition, flushModuleScopingQueueAsMuchAsPossible, setAllowDuplicateNgModuleIdsForTest } from './_debug_node-chunk.mjs';
import { ResourceLoader } from '@angular/compiler';
import './_effect-chunk.mjs';
import './_not_found-chunk.mjs';
import '@angular/core/primitives/signals';
import '@angular/core/primitives/di';
import 'rxjs/operators';
import './_attribute-chunk.mjs';

function waitForAsync(fn) {
  const _Zone = typeof Zone !== 'undefined' ? Zone : null;
  if (!_Zone) {
    return function () {
      return Promise.reject('Zone is needed for the waitForAsync() test helper but could not be found. ' + 'Please make sure that your environment includes zone.js');
    };
  }
  const asyncTest = _Zone && _Zone[_Zone.__symbol__('asyncTest')];
  if (typeof asyncTest === 'function') {
    return asyncTest(fn);
  }
  return function () {
    return Promise.reject('zone-testing.js is needed for the async() test helper but could not be found. ' + 'Please make sure that your environment includes zone.js/testing');
  };
}

const RETHROW_APPLICATION_ERRORS_DEFAULT = true;
class TestBedApplicationErrorHandler {
  zone = inject$1(NgZone);
  injector = inject$1(EnvironmentInjector);
  userErrorHandler;
  whenStableRejectFunctions = new Set();
  handleError(e) {
    try {
      this.zone.runOutsideAngular(() => {
        this.userErrorHandler ??= this.injector.get(ErrorHandler);
        this.userErrorHandler.handleError(e);
      });
    } catch (userError) {
      e = userError;
    }
    if (this.whenStableRejectFunctions.size > 0) {
      for (const fn of this.whenStableRejectFunctions.values()) {
        fn(e);
      }
      this.whenStableRejectFunctions.clear();
    } else {
      throw e;
    }
  }
  static ɵfac = function TestBedApplicationErrorHandler_Factory(__ngFactoryType__) {
    return new (__ngFactoryType__ || TestBedApplicationErrorHandler)();
  };
  static ɵprov = /*@__PURE__*/i0.ɵɵdefineInjectable({
    token: TestBedApplicationErrorHandler,
    factory: TestBedApplicationErrorHandler.ɵfac
  });
}
(() => {
  (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestBedApplicationErrorHandler, [{
    type: Injectable
  }], null, null);
})();

class DeferBlockFixture {
  block;
  componentFixture;
  constructor(block, componentFixture) {
    this.block = block;
    this.componentFixture = componentFixture;
  }
  async render(state) {
    if (!hasStateTemplate(state, this.block)) {
      const stateAsString = getDeferBlockStateNameFromEnum(state);
      throw new Error(`Tried to render this defer block in the \`${stateAsString}\` state, ` + `but there was no @${stateAsString.toLowerCase()} block defined in a template.`);
    }
    if (state === DeferBlockState.Complete) {
      await triggerResourceLoading(this.block.tDetails, this.block.lView, this.block.tNode);
    }
    const skipTimerScheduling = true;
    renderDeferBlockState(state, this.block.tNode, this.block.lContainer, skipTimerScheduling);
    this.componentFixture.detectChanges();
  }
  getDeferBlocks() {
    const deferBlocks = [];
    const deferBlockFixtures = [];
    if (this.block.lContainer.length >= CONTAINER_HEADER_OFFSET) {
      const lView = this.block.lContainer[CONTAINER_HEADER_OFFSET];
      getDeferBlocks(lView, deferBlocks);
      for (const block of deferBlocks) {
        deferBlockFixtures.push(new DeferBlockFixture(block, this.componentFixture));
      }
    }
    return Promise.resolve(deferBlockFixtures);
  }
}
function hasStateTemplate(state, block) {
  switch (state) {
    case DeferBlockState.Placeholder:
      return block.tDetails.placeholderTmplIndex !== null;
    case DeferBlockState.Loading:
      return block.tDetails.loadingTmplIndex !== null;
    case DeferBlockState.Error:
      return block.tDetails.errorTmplIndex !== null;
    case DeferBlockState.Complete:
      return true;
    default:
      return false;
  }
}
function getDeferBlockStateNameFromEnum(state) {
  switch (state) {
    case DeferBlockState.Placeholder:
      return 'Placeholder';
    case DeferBlockState.Loading:
      return 'Loading';
    case DeferBlockState.Error:
      return 'Error';
    default:
      return 'Main';
  }
}

const TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT = true;
const THROW_ON_UNKNOWN_ELEMENTS_DEFAULT = false;
const THROW_ON_UNKNOWN_PROPERTIES_DEFAULT = false;
const DEFER_BLOCK_DEFAULT_BEHAVIOR = DeferBlockBehavior.Playthrough;
const ANIMATIONS_ENABLED_DEFAULT = false;
class TestComponentRenderer {
  insertRootElement(rootElementId, tagName) {}
  removeAllRootElements() {}
}
const ComponentFixtureAutoDetect = new InjectionToken('ComponentFixtureAutoDetect');
const ComponentFixtureNoNgZone = new InjectionToken('ComponentFixtureNoNgZone');

class ComponentFixture {
  componentRef;
  debugElement;
  componentInstance;
  nativeElement;
  elementRef;
  changeDetectorRef;
  _renderer;
  _isDestroyed = false;
  _noZoneOptionIsSet = inject$1(ComponentFixtureNoNgZone, {
    optional: true
  });
  _ngZone = this._noZoneOptionIsSet ? new NoopNgZone() : inject$1(NgZone);
  _appRef = inject$1(ApplicationRef);
  _testAppRef = this._appRef;
  pendingTasks = inject$1(PendingTasksInternal);
  appErrorHandler = inject$1(TestBedApplicationErrorHandler);
  zonelessEnabled = inject$1(ZONELESS_ENABLED);
  scheduler = inject$1(ChangeDetectionScheduler);
  rootEffectScheduler = inject$1(EffectScheduler);
  autoDetectDefault = this.zonelessEnabled ? true : false;
  autoDetect = inject$1(ComponentFixtureAutoDetect, {
    optional: true
  }) ?? this.autoDetectDefault;
  subscriptions = new Subscription();
  ngZone = this._noZoneOptionIsSet ? null : this._ngZone;
  constructor(componentRef) {
    this.componentRef = componentRef;
    this.changeDetectorRef = componentRef.changeDetectorRef;
    this.elementRef = componentRef.location;
    this.debugElement = getDebugNode(this.elementRef.nativeElement);
    this.componentInstance = componentRef.instance;
    this.nativeElement = this.elementRef.nativeElement;
    this.componentRef = componentRef;
    this._testAppRef.allTestViews.add(this.componentRef.hostView);
    if (this.autoDetect) {
      this._testAppRef.autoDetectTestViews.add(this.componentRef.hostView);
      this.scheduler?.notify(8);
      this.scheduler?.notify(0);
    }
    this.componentRef.hostView.onDestroy(() => {
      this._testAppRef.allTestViews.delete(this.componentRef.hostView);
      this._testAppRef.autoDetectTestViews.delete(this.componentRef.hostView);
    });
    this._ngZone.runOutsideAngular(() => {
      this.subscriptions.add(this._ngZone.onError.subscribe({
        next: error => {
          if (typeof Zone === 'undefined' || Zone.current.get('FakeAsyncTestZoneSpec')) {
            return;
          }
          throw error;
        }
      }));
    });
  }
  detectChanges(checkNoChanges = true) {
    const originalCheckNoChanges = this.componentRef.changeDetectorRef.checkNoChanges;
    try {
      if (!checkNoChanges) {
        this.componentRef.changeDetectorRef.checkNoChanges = () => {};
      }
      if (this.zonelessEnabled) {
        try {
          this._testAppRef.includeAllTestViews = true;
          this._appRef.tick();
        } finally {
          this._testAppRef.includeAllTestViews = false;
        }
      } else {
        this._ngZone.run(() => {
          this.rootEffectScheduler.flush();
          this.changeDetectorRef.detectChanges();
          this.checkNoChanges();
        });
      }
    } finally {
      this.componentRef.changeDetectorRef.checkNoChanges = originalCheckNoChanges;
    }
  }
  checkNoChanges() {
    this.changeDetectorRef.checkNoChanges();
  }
  autoDetectChanges(autoDetect = true) {
    if (!autoDetect && this.zonelessEnabled) {
      throw new Error('Cannot set autoDetect to false with zoneless change detection.');
    }
    if (this._noZoneOptionIsSet && !this.zonelessEnabled) {
      throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set.');
    }
    if (autoDetect) {
      this._testAppRef.autoDetectTestViews.add(this.componentRef.hostView);
    } else {
      this._testAppRef.autoDetectTestViews.delete(this.componentRef.hostView);
    }
    this.autoDetect = autoDetect;
    this.detectChanges();
  }
  isStable() {
    return !this.pendingTasks.hasPendingTasks;
  }
  whenStable() {
    if (this.isStable()) {
      return Promise.resolve(false);
    }
    return new Promise((resolve, reject) => {
      this.appErrorHandler.whenStableRejectFunctions.add(reject);
      this._appRef.whenStable().then(() => {
        this.appErrorHandler.whenStableRejectFunctions.delete(reject);
        resolve(true);
      });
    });
  }
  getDeferBlocks() {
    const deferBlocks = [];
    const lView = this.componentRef.hostView['_lView'];
    getDeferBlocks(lView, deferBlocks);
    const deferBlockFixtures = [];
    for (const block of deferBlocks) {
      deferBlockFixtures.push(new DeferBlockFixture(block, this));
    }
    return Promise.resolve(deferBlockFixtures);
  }
  _getRenderer() {
    if (this._renderer === undefined) {
      this._renderer = this.componentRef.injector.get(RendererFactory2, null);
    }
    return this._renderer;
  }
  whenRenderingDone() {
    const renderer = this._getRenderer();
    if (renderer && renderer.whenRenderingDone) {
      return renderer.whenRenderingDone();
    }
    return this.whenStable();
  }
  destroy() {
    this.subscriptions.unsubscribe();
    this._testAppRef.autoDetectTestViews.delete(this.componentRef.hostView);
    this._testAppRef.allTestViews.delete(this.componentRef.hostView);
    if (!this._isDestroyed) {
      this.componentRef.destroy();
      this._isDestroyed = true;
    }
  }
}

const _Zone = typeof Zone !== 'undefined' ? Zone : null;
function getFakeAsyncTestModule() {
  return _Zone && _Zone[_Zone.__symbol__('fakeAsyncTest')];
}
function withFakeAsyncTestModule(fn) {
  const fakeAsyncTestModule = getFakeAsyncTestModule();
  if (!fakeAsyncTestModule) {
    throw new Error(`zone-testing.js is needed for the fakeAsync() test helper but could not be found.
        Please make sure that your environment includes zone.js/testing`);
  }
  return fn(fakeAsyncTestModule);
}
function resetFakeAsyncZone() {
  withFakeAsyncTestModule(v => v.resetFakeAsyncZone());
}
function resetFakeAsyncZoneIfExists() {
  if (getFakeAsyncTestModule() && Zone['ProxyZoneSpec']?.isLoaded()) {
    getFakeAsyncTestModule().resetFakeAsyncZone();
  }
}
function fakeAsync(fn, options) {
  return withFakeAsyncTestModule(v => v.fakeAsync(fn, options));
}
function tick(millis = 0, tickOptions = {
  processNewMacroTasksSynchronously: true
}) {
  return withFakeAsyncTestModule(m => m.tick(millis, tickOptions));
}
function flush(maxTurns) {
  return withFakeAsyncTestModule(m => m.flush(maxTurns));
}
function discardPeriodicTasks() {
  return withFakeAsyncTestModule(m => m.discardPeriodicTasks());
}
function flushMicrotasks() {
  return withFakeAsyncTestModule(m => m.flushMicrotasks());
}

let _nextReferenceId = 0;
class MetadataOverrider {
  _references = new Map();
  overrideMetadata(metadataClass, oldMetadata, override) {
    const props = {};
    if (oldMetadata) {
      _valueProps(oldMetadata).forEach(prop => props[prop] = oldMetadata[prop]);
    }
    if (override.set) {
      if (override.remove || override.add) {
        throw new Error(`Cannot set and add/remove ${stringify(metadataClass)} at the same time!`);
      }
      setMetadata(props, override.set);
    }
    if (override.remove) {
      removeMetadata(props, override.remove, this._references);
    }
    if (override.add) {
      addMetadata(props, override.add);
    }
    return new metadataClass(props);
  }
}
function removeMetadata(metadata, remove, references) {
  const removeObjects = new Set();
  for (const prop in remove) {
    const removeValue = remove[prop];
    if (Array.isArray(removeValue)) {
      removeValue.forEach(value => {
        removeObjects.add(_propHashKey(prop, value, references));
      });
    } else {
      removeObjects.add(_propHashKey(prop, removeValue, references));
    }
  }
  for (const prop in metadata) {
    const propValue = metadata[prop];
    if (Array.isArray(propValue)) {
      metadata[prop] = propValue.filter(value => !removeObjects.has(_propHashKey(prop, value, references)));
    } else {
      if (removeObjects.has(_propHashKey(prop, propValue, references))) {
        metadata[prop] = undefined;
      }
    }
  }
}
function addMetadata(metadata, add) {
  for (const prop in add) {
    const addValue = add[prop];
    const propValue = metadata[prop];
    if (propValue != null && Array.isArray(propValue)) {
      metadata[prop] = propValue.concat(addValue);
    } else {
      metadata[prop] = addValue;
    }
  }
}
function setMetadata(metadata, set) {
  for (const prop in set) {
    metadata[prop] = set[prop];
  }
}
function _propHashKey(propName, propValue, references) {
  let nextObjectId = 0;
  const objectIds = new Map();
  const replacer = (key, value) => {
    if (value !== null && typeof value === 'object') {
      if (objectIds.has(value)) {
        return objectIds.get(value);
      }
      objectIds.set(value, `ɵobj#${nextObjectId++}`);
      return value;
    } else if (typeof value === 'function') {
      value = _serializeReference(value, references);
    }
    return value;
  };
  return `${propName}:${JSON.stringify(propValue, replacer)}`;
}
function _serializeReference(ref, references) {
  let id = references.get(ref);
  if (!id) {
    id = `${stringify(ref)}${_nextReferenceId++}`;
    references.set(ref, id);
  }
  return id;
}
function _valueProps(obj) {
  const props = [];
  Object.keys(obj).forEach(prop => {
    if (!prop.startsWith('_')) {
      props.push(prop);
    }
  });
  let proto = obj;
  while (proto = Object.getPrototypeOf(proto)) {
    Object.keys(proto).forEach(protoProp => {
      const desc = Object.getOwnPropertyDescriptor(proto, protoProp);
      if (!protoProp.startsWith('_') && desc && 'get' in desc) {
        props.push(protoProp);
      }
    });
  }
  return props;
}

const reflection = new ReflectionCapabilities();
class OverrideResolver {
  overrides = new Map();
  resolved = new Map();
  addOverride(type, override) {
    const overrides = this.overrides.get(type) || [];
    overrides.push(override);
    this.overrides.set(type, overrides);
    this.resolved.delete(type);
  }
  setOverrides(overrides) {
    this.overrides.clear();
    overrides.forEach(([type, override]) => {
      this.addOverride(type, override);
    });
  }
  getOverrides(type) {
    return this.overrides.get(type) || null;
  }
  hasOverrides(type) {
    return this.overrides.has(type);
  }
  getAnnotation(type) {
    const annotations = reflection.annotations(type);
    for (let i = annotations.length - 1; i >= 0; i--) {
      const annotation = annotations[i];
      const isKnownType = annotation instanceof Directive || annotation instanceof Component || annotation instanceof Pipe || annotation instanceof NgModule;
      if (isKnownType) {
        return annotation instanceof this.type ? annotation : null;
      }
    }
    return null;
  }
  resolve(type) {
    let resolved = this.resolved.get(type) || null;
    if (!resolved) {
      resolved = this.getAnnotation(type);
      if (resolved) {
        const overrides = this.overrides.get(type);
        if (overrides) {
          const overrider = new MetadataOverrider();
          overrides.forEach(override => {
            resolved = overrider.overrideMetadata(this.type, resolved, override);
          });
        }
      }
      this.resolved.set(type, resolved);
    }
    return resolved;
  }
}
class DirectiveResolver extends OverrideResolver {
  get type() {
    return Directive;
  }
}
class ComponentResolver extends OverrideResolver {
  get type() {
    return Component;
  }
}
class PipeResolver extends OverrideResolver {
  get type() {
    return Pipe;
  }
}
class NgModuleResolver extends OverrideResolver {
  get type() {
    return NgModule;
  }
}

var TestingModuleOverride;
(function (TestingModuleOverride) {
  TestingModuleOverride[TestingModuleOverride["DECLARATION"] = 0] = "DECLARATION";
  TestingModuleOverride[TestingModuleOverride["OVERRIDE_TEMPLATE"] = 1] = "OVERRIDE_TEMPLATE";
})(TestingModuleOverride || (TestingModuleOverride = {}));
function isTestingModuleOverride(value) {
  return value === TestingModuleOverride.DECLARATION || value === TestingModuleOverride.OVERRIDE_TEMPLATE;
}
function assertNoStandaloneComponents(types, resolver, location) {
  types.forEach(type => {
    if (!getAsyncClassMetadataFn(type)) {
      const component = resolver.resolve(type);
      if (component && (component.standalone == null || component.standalone)) {
        throw new Error(generateStandaloneInDeclarationsError(type, location));
      }
    }
  });
}
class TestBedCompiler {
  platform;
  additionalModuleTypes;
  originalComponentResolutionQueue = null;
  declarations = [];
  imports = [];
  providers = [];
  schemas = [];
  pendingComponents = new Set();
  pendingDirectives = new Set();
  pendingPipes = new Set();
  componentsWithAsyncMetadata = new Set();
  seenComponents = new Set();
  seenDirectives = new Set();
  overriddenModules = new Set();
  existingComponentStyles = new Map();
  resolvers = initResolvers();
  componentToModuleScope = new Map();
  initialNgDefs = new Map();
  defCleanupOps = [];
  _injector = null;
  compilerProviders = null;
  providerOverrides = [];
  rootProviderOverrides = [];
  providerOverridesByModule = new Map();
  providerOverridesByToken = new Map();
  scopesWithOverriddenProviders = new Set();
  testModuleType;
  testModuleRef = null;
  animationsEnabled = ANIMATIONS_ENABLED_DEFAULT;
  deferBlockBehavior = DEFER_BLOCK_DEFAULT_BEHAVIOR;
  rethrowApplicationTickErrors = RETHROW_APPLICATION_ERRORS_DEFAULT;
  constructor(platform, additionalModuleTypes) {
    this.platform = platform;
    this.additionalModuleTypes = additionalModuleTypes;
    class DynamicTestModule {}
    this.testModuleType = DynamicTestModule;
  }
  setCompilerProviders(providers) {
    this.compilerProviders = providers;
    this._injector = null;
  }
  configureTestingModule(moduleDef) {
    if (moduleDef.declarations !== undefined) {
      assertNoStandaloneComponents(moduleDef.declarations, this.resolvers.component, '"TestBed.configureTestingModule" call');
      this.queueTypeArray(moduleDef.declarations, TestingModuleOverride.DECLARATION);
      this.declarations.push(...moduleDef.declarations);
    }
    if (moduleDef.imports !== undefined) {
      this.queueTypesFromModulesArray(moduleDef.imports);
      this.imports.push(...moduleDef.imports);
    }
    if (moduleDef.providers !== undefined) {
      this.providers.push(...moduleDef.providers);
    }
    if (moduleDef.schemas !== undefined) {
      this.schemas.push(...moduleDef.schemas);
    }
    this.deferBlockBehavior = moduleDef.deferBlockBehavior ?? DEFER_BLOCK_DEFAULT_BEHAVIOR;
    this.animationsEnabled = moduleDef.animationsEnabled ?? ANIMATIONS_ENABLED_DEFAULT;
    this.rethrowApplicationTickErrors = moduleDef.rethrowApplicationErrors ?? RETHROW_APPLICATION_ERRORS_DEFAULT;
  }
  overrideModule(ngModule, override) {
    depsTracker.clearScopeCacheFor(ngModule);
    this.overriddenModules.add(ngModule);
    this.resolvers.module.addOverride(ngModule, override);
    const metadata = this.resolvers.module.resolve(ngModule);
    if (metadata === null) {
      throw invalidTypeError(ngModule.name, 'NgModule');
    }
    this.recompileNgModule(ngModule, metadata);
    this.queueTypesFromModulesArray([ngModule]);
  }
  overrideComponent(component, override) {
    this.verifyNoStandaloneFlagOverrides(component, override);
    this.resolvers.component.addOverride(component, override);
    this.pendingComponents.add(component);
    this.maybeRegisterComponentWithAsyncMetadata(component);
  }
  overrideDirective(directive, override) {
    this.verifyNoStandaloneFlagOverrides(directive, override);
    this.resolvers.directive.addOverride(directive, override);
    this.pendingDirectives.add(directive);
  }
  overridePipe(pipe, override) {
    this.verifyNoStandaloneFlagOverrides(pipe, override);
    this.resolvers.pipe.addOverride(pipe, override);
    this.pendingPipes.add(pipe);
  }
  hasComponentOverrides(type) {
    return this.resolvers.component.hasOverrides(type);
  }
  verifyNoStandaloneFlagOverrides(type, override) {
    if (override.add && Object.hasOwn(override.add, 'standalone') || override.set && Object.hasOwn(override.set, 'standalone') || override.remove && Object.hasOwn(override.remove, 'standalone')) {
      throw new Error(`An override for the ${type.name} class has the \`standalone\` flag. ` + `Changing the \`standalone\` flag via TestBed overrides is not supported.`);
    }
  }
  overrideProvider(token, provider) {
    let providerDef;
    if (provider.useFactory !== undefined) {
      providerDef = {
        provide: token,
        useFactory: provider.useFactory,
        deps: provider.deps || [],
        multi: provider.multi
      };
    } else if (provider.useValue !== undefined) {
      providerDef = {
        provide: token,
        useValue: provider.useValue,
        multi: provider.multi
      };
    } else {
      providerDef = {
        provide: token
      };
    }
    const injectableDef = typeof token !== 'string' ? getInjectableDef(token) : null;
    const providedIn = injectableDef === null ? null : resolveForwardRef(injectableDef.providedIn);
    const overridesBucket = providedIn === 'root' ? this.rootProviderOverrides : this.providerOverrides;
    overridesBucket.push(providerDef);
    this.providerOverridesByToken.set(token, providerDef);
    if (injectableDef !== null && providedIn !== null && typeof providedIn !== 'string') {
      const existingOverrides = this.providerOverridesByModule.get(providedIn);
      if (existingOverrides !== undefined) {
        existingOverrides.push(providerDef);
      } else {
        this.providerOverridesByModule.set(providedIn, [providerDef]);
      }
    }
  }
  overrideTemplateUsingTestingModule(type, template) {
    const def = type[NG_COMP_DEF];
    const hasStyleUrls = () => {
      const metadata = this.resolvers.component.resolve(type);
      return !!metadata.styleUrl || !!metadata.styleUrls?.length;
    };
    const overrideStyleUrls = !!def && !isComponentDefPendingResolution(type) && hasStyleUrls();
    const override = overrideStyleUrls ? {
      template,
      styles: [],
      styleUrls: [],
      styleUrl: undefined
    } : {
      template
    };
    this.overrideComponent(type, {
      set: override
    });
    if (overrideStyleUrls && def.styles && def.styles.length > 0) {
      this.existingComponentStyles.set(type, def.styles);
    }
    this.componentToModuleScope.set(type, TestingModuleOverride.OVERRIDE_TEMPLATE);
  }
  async resolvePendingComponentsWithAsyncMetadata() {
    if (this.componentsWithAsyncMetadata.size === 0) return;
    const promises = [];
    for (const component of this.componentsWithAsyncMetadata) {
      const asyncMetadataFn = getAsyncClassMetadataFn(component);
      if (asyncMetadataFn) {
        promises.push(asyncMetadataFn());
      }
    }
    this.componentsWithAsyncMetadata.clear();
    const resolvedDeps = await Promise.all(promises);
    const flatResolvedDeps = resolvedDeps.flat(2);
    this.queueTypesFromModulesArray(flatResolvedDeps);
    for (const component of flatResolvedDeps) {
      this.applyProviderOverridesInScope(component);
    }
  }
  async compileComponents() {
    this.clearComponentResolutionQueue();
    await this.resolvePendingComponentsWithAsyncMetadata();
    assertNoStandaloneComponents(this.declarations, this.resolvers.component, '"TestBed.configureTestingModule" call');
    let needsAsyncResources = this.compileTypesSync();
    if (needsAsyncResources) {
      let resourceLoader;
      let resolver = url => {
        if (!resourceLoader) {
          resourceLoader = this.injector.get(ResourceLoader);
        }
        return Promise.resolve(resourceLoader.get(url));
      };
      await resolveComponentResources(resolver);
    }
  }
  finalize() {
    this.compileTypesSync();
    this.compileTestModule();
    this.applyTransitiveScopes();
    this.applyProviderOverrides();
    this.patchComponentsWithExistingStyles();
    this.componentToModuleScope.clear();
    const parentInjector = this.platform.injector;
    this.testModuleRef = new NgModuleRef(this.testModuleType, parentInjector, []);
    this.testModuleRef.injector.get(ApplicationInitStatus).runInitializers();
    const localeId = this.testModuleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID);
    setLocaleId(localeId);
    return this.testModuleRef;
  }
  _compileNgModuleSync(moduleType) {
    this.queueTypesFromModulesArray([moduleType]);
    this.compileTypesSync();
    this.applyProviderOverrides();
    this.applyProviderOverridesInScope(moduleType);
    this.applyTransitiveScopes();
  }
  async _compileNgModuleAsync(moduleType) {
    this.queueTypesFromModulesArray([moduleType]);
    await this.compileComponents();
    this.applyProviderOverrides();
    this.applyProviderOverridesInScope(moduleType);
    this.applyTransitiveScopes();
  }
  _getModuleResolver() {
    return this.resolvers.module;
  }
  _getComponentFactories(moduleType) {
    return maybeUnwrapFn(moduleType.ɵmod.declarations).reduce((factories, declaration) => {
      const componentDef = declaration.ɵcmp;
      componentDef && factories.push(new ComponentFactory(componentDef, this.testModuleRef));
      return factories;
    }, []);
  }
  compileTypesSync() {
    let needsAsyncResources = false;
    this.pendingComponents.forEach(declaration => {
      if (getAsyncClassMetadataFn(declaration)) {
        throw new Error(`Component '${declaration.name}' has unresolved metadata. ` + `Please call \`await TestBed.compileComponents()\` before running this test.`);
      }
      needsAsyncResources = needsAsyncResources || isComponentDefPendingResolution(declaration);
      let metadata = this.resolvers.component.resolve(declaration);
      if (metadata === null) {
        if (this.resolvers.component.hasOverrides(declaration)) {
          const componentDef = getComponentDef(declaration);
          if (componentDef) {
            const overrider = new MetadataOverrider();
            metadata = new Component({
              selector: componentDef.selectors[0][0],
              template: componentDef.template,
              standalone: componentDef.standalone
            });
            const overrides = this.resolvers.component.getOverrides(declaration);
            if (overrides) {
              overrides.forEach(override => {
                metadata = overrider.overrideMetadata(Component, metadata, override);
              });
            }
            if (metadata.providers) {
              const providers = metadata.providers;
              const providersResolver = (ndef, processProvidersFn) => {
                return processProvidersFn ? processProvidersFn(providers) : providers;
              };
              componentDef.providersResolver = providersResolver;
            }
            if (metadata.viewProviders) {
              const viewProviders = metadata.viewProviders;
              const viewProvidersResolver = (ndef, processProvidersFn) => {
                return processProvidersFn ? processProvidersFn(viewProviders) : viewProviders;
              };
              componentDef.viewProvidersResolver = viewProvidersResolver;
            }
            return;
          }
        }
        throw invalidTypeError(declaration.name, 'Component');
      }
      this.maybeStoreNgDef(NG_COMP_DEF, declaration);
      depsTracker.clearScopeCacheFor(declaration);
      compileComponent(declaration, metadata);
    });
    this.pendingComponents.clear();
    this.pendingDirectives.forEach(declaration => {
      const metadata = this.resolvers.directive.resolve(declaration);
      if (metadata === null) {
        throw invalidTypeError(declaration.name, 'Directive');
      }
      this.maybeStoreNgDef(NG_DIR_DEF, declaration);
      compileDirective(declaration, metadata);
    });
    this.pendingDirectives.clear();
    this.pendingPipes.forEach(declaration => {
      const metadata = this.resolvers.pipe.resolve(declaration);
      if (metadata === null) {
        throw invalidTypeError(declaration.name, 'Pipe');
      }
      this.maybeStoreNgDef(NG_PIPE_DEF, declaration);
      compilePipe(declaration, metadata);
    });
    this.pendingPipes.clear();
    return needsAsyncResources;
  }
  applyTransitiveScopes() {
    if (this.overriddenModules.size > 0) {
      const testingModuleDef = this.testModuleType[NG_MOD_DEF];
      const affectedModules = this.collectModulesAffectedByOverrides(testingModuleDef.imports);
      if (affectedModules.size > 0) {
        affectedModules.forEach(moduleType => {
          depsTracker.clearScopeCacheFor(moduleType);
        });
      }
    }
    const moduleToScope = new Map();
    const getScopeOfModule = moduleType => {
      if (!moduleToScope.has(moduleType)) {
        const isTestingModule = isTestingModuleOverride(moduleType);
        const realType = isTestingModule ? this.testModuleType : moduleType;
        moduleToScope.set(moduleType, transitiveScopesFor(realType));
      }
      return moduleToScope.get(moduleType);
    };
    this.componentToModuleScope.forEach((moduleType, componentType) => {
      if (moduleType !== null) {
        const moduleScope = getScopeOfModule(moduleType);
        this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'directiveDefs');
        this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'pipeDefs');
        patchComponentDefWithScope(getComponentDef(componentType), moduleScope);
      }
      this.storeFieldOfDefOnType(componentType, NG_COMP_DEF, 'tView');
    });
    this.componentToModuleScope.clear();
  }
  applyProviderOverrides() {
    const maybeApplyOverrides = field => type => {
      const resolver = field === NG_COMP_DEF ? this.resolvers.component : this.resolvers.directive;
      const metadata = resolver.resolve(type);
      if (this.hasProviderOverrides(metadata.providers)) {
        this.patchDefWithProviderOverrides(type, field);
      }
    };
    this.seenComponents.forEach(maybeApplyOverrides(NG_COMP_DEF));
    this.seenDirectives.forEach(maybeApplyOverrides(NG_DIR_DEF));
    this.seenComponents.clear();
    this.seenDirectives.clear();
  }
  applyProviderOverridesInScope(type) {
    const hasScope = isStandaloneComponent(type) || isNgModule(type);
    if (!hasScope || this.scopesWithOverriddenProviders.has(type)) {
      return;
    }
    this.scopesWithOverriddenProviders.add(type);
    const injectorDef = type[NG_INJ_DEF];
    if (this.providerOverridesByToken.size === 0) return;
    if (isStandaloneComponent(type)) {
      const def = getComponentDef(type);
      const dependencies = maybeUnwrapFn(def.dependencies ?? []);
      for (const dependency of dependencies) {
        this.applyProviderOverridesInScope(dependency);
      }
    } else {
      const providers = [...injectorDef.providers, ...(this.providerOverridesByModule.get(type) || [])];
      if (this.hasProviderOverrides(providers)) {
        this.maybeStoreNgDef(NG_INJ_DEF, type);
        this.storeFieldOfDefOnType(type, NG_INJ_DEF, 'providers');
        injectorDef.providers = this.getOverriddenProviders(providers);
      }
      const moduleDef = type[NG_MOD_DEF];
      const imports = maybeUnwrapFn(moduleDef.imports);
      for (const importedModule of imports) {
        this.applyProviderOverridesInScope(importedModule);
      }
      for (const importedModule of flatten(injectorDef.imports)) {
        if (isModuleWithProviders(importedModule)) {
          this.defCleanupOps.push({
            object: importedModule,
            fieldName: 'providers',
            originalValue: importedModule.providers
          });
          importedModule.providers = this.getOverriddenProviders(importedModule.providers);
        }
      }
    }
  }
  patchComponentsWithExistingStyles() {
    this.existingComponentStyles.forEach((styles, type) => type[NG_COMP_DEF].styles = styles);
    this.existingComponentStyles.clear();
  }
  queueTypeArray(arr, moduleType) {
    for (const value of arr) {
      if (Array.isArray(value)) {
        this.queueTypeArray(value, moduleType);
      } else {
        this.queueType(value, moduleType);
      }
    }
  }
  recompileNgModule(ngModule, metadata) {
    this.maybeStoreNgDef(NG_MOD_DEF, ngModule);
    this.maybeStoreNgDef(NG_INJ_DEF, ngModule);
    compileNgModuleDefs(ngModule, metadata);
  }
  maybeRegisterComponentWithAsyncMetadata(type) {
    const asyncMetadataFn = getAsyncClassMetadataFn(type);
    if (asyncMetadataFn) {
      this.componentsWithAsyncMetadata.add(type);
    }
  }
  queueType(type, moduleType) {
    this.maybeRegisterComponentWithAsyncMetadata(type);
    const component = this.resolvers.component.resolve(type);
    if (component) {
      if (isComponentDefPendingResolution(type) || !Object.hasOwn(type, NG_COMP_DEF)) {
        this.pendingComponents.add(type);
      }
      this.seenComponents.add(type);
      if (!this.componentToModuleScope.has(type) || this.componentToModuleScope.get(type) === TestingModuleOverride.DECLARATION) {
        this.componentToModuleScope.set(type, moduleType);
      }
      return;
    }
    const directive = this.resolvers.directive.resolve(type);
    if (directive) {
      if (!Object.hasOwn(type, NG_DIR_DEF)) {
        this.pendingDirectives.add(type);
      }
      this.seenDirectives.add(type);
      return;
    }
    const pipe = this.resolvers.pipe.resolve(type);
    if (pipe && !Object.hasOwn(type, NG_PIPE_DEF)) {
      this.pendingPipes.add(type);
      return;
    }
  }
  queueTypesFromModulesArray(arr) {
    const processedDefs = new Set();
    const queueTypesFromModulesArrayRecur = arr => {
      for (const value of arr) {
        if (Array.isArray(value)) {
          queueTypesFromModulesArrayRecur(value);
        } else if (hasNgModuleDef(value)) {
          const def = value.ɵmod;
          if (processedDefs.has(def)) {
            continue;
          }
          processedDefs.add(def);
          this.queueTypeArray(maybeUnwrapFn(def.declarations), value);
          queueTypesFromModulesArrayRecur(maybeUnwrapFn(def.imports));
          queueTypesFromModulesArrayRecur(maybeUnwrapFn(def.exports));
        } else if (isModuleWithProviders(value)) {
          queueTypesFromModulesArrayRecur([value.ngModule]);
        } else if (isStandaloneComponent(value)) {
          this.queueType(value, null);
          const def = getComponentDef(value);
          if (processedDefs.has(def)) {
            continue;
          }
          processedDefs.add(def);
          const dependencies = maybeUnwrapFn(def.dependencies ?? []);
          dependencies.forEach(dependency => {
            if (isStandaloneComponent(dependency) || hasNgModuleDef(dependency)) {
              queueTypesFromModulesArrayRecur([dependency]);
            } else {
              this.queueType(dependency, null);
            }
          });
        }
      }
    };
    queueTypesFromModulesArrayRecur(arr);
  }
  collectModulesAffectedByOverrides(arr) {
    const seenModules = new Set();
    const affectedModules = new Set();
    const calcAffectedModulesRecur = (arr, path) => {
      for (const value of arr) {
        if (Array.isArray(value)) {
          calcAffectedModulesRecur(value, path);
        } else if (hasNgModuleDef(value)) {
          if (seenModules.has(value)) {
            if (affectedModules.has(value)) {
              path.forEach(item => affectedModules.add(item));
            }
            continue;
          }
          seenModules.add(value);
          if (this.overriddenModules.has(value)) {
            path.forEach(item => affectedModules.add(item));
          }
          const moduleDef = value[NG_MOD_DEF];
          calcAffectedModulesRecur(maybeUnwrapFn(moduleDef.imports), path.concat(value));
        }
      }
    };
    calcAffectedModulesRecur(arr, []);
    return affectedModules;
  }
  maybeStoreNgDef(prop, type) {
    if (!this.initialNgDefs.has(type)) {
      this.initialNgDefs.set(type, new Map());
    }
    const currentDefs = this.initialNgDefs.get(type);
    if (!currentDefs.has(prop)) {
      const currentDef = Object.getOwnPropertyDescriptor(type, prop);
      currentDefs.set(prop, currentDef);
    }
  }
  storeFieldOfDefOnType(type, defField, fieldName) {
    const def = type[defField];
    const originalValue = def[fieldName];
    this.defCleanupOps.push({
      object: def,
      fieldName,
      originalValue
    });
  }
  clearComponentResolutionQueue() {
    if (this.originalComponentResolutionQueue === null) {
      this.originalComponentResolutionQueue = new Map();
    }
    clearResolutionOfComponentResourcesQueue().forEach((value, key) => this.originalComponentResolutionQueue.set(key, value));
  }
  restoreComponentResolutionQueue() {
    if (this.originalComponentResolutionQueue !== null) {
      restoreComponentResolutionQueue(this.originalComponentResolutionQueue);
      this.originalComponentResolutionQueue = null;
    }
  }
  restoreOriginalState() {
    forEachRight(this.defCleanupOps, op => {
      op.object[op.fieldName] = op.originalValue;
    });
    this.initialNgDefs.forEach((defs, type) => {
      depsTracker.clearScopeCacheFor(type);
      defs.forEach((descriptor, prop) => {
        if (!descriptor) {
          delete type[prop];
        } else {
          Object.defineProperty(type, prop, descriptor);
        }
      });
    });
    this.initialNgDefs.clear();
    this.scopesWithOverriddenProviders.clear();
    this.restoreComponentResolutionQueue();
    setLocaleId(DEFAULT_LOCALE_ID);
  }
  compileTestModule() {
    class RootScopeModule {}
    compileNgModuleDefs(RootScopeModule, {
      providers: [...this.rootProviderOverrides, provideZonelessChangeDetectionInternal(), TestBedApplicationErrorHandler, {
        provide: ENVIRONMENT_INITIALIZER,
        multi: true,
        useValue: () => {
          inject$1(ErrorHandler);
        }
      }]
    });
    const providers = [{
      provide: Compiler,
      useFactory: () => new R3TestCompiler(this)
    }, {
      provide: DEFER_BLOCK_CONFIG,
      useValue: {
        behavior: this.deferBlockBehavior
      }
    }, {
      provide: ANIMATIONS_DISABLED,
      useValue: !this.animationsEnabled
    }, {
      provide: INTERNAL_APPLICATION_ERROR_HANDLER,
      useFactory: () => {
        if (this.rethrowApplicationTickErrors) {
          const handler = inject$1(TestBedApplicationErrorHandler);
          return e => {
            handler.handleError(e);
          };
        } else {
          const userErrorHandler = inject$1(ErrorHandler);
          const ngZone = inject$1(NgZone);
          return e => ngZone.runOutsideAngular(() => userErrorHandler.handleError(e));
        }
      }
    }, ...this.providers, ...this.providerOverrides];
    const imports = [RootScopeModule, this.additionalModuleTypes, this.imports || []];
    compileNgModuleDefs(this.testModuleType, {
      declarations: this.declarations,
      imports,
      schemas: this.schemas,
      providers
    }, true);
    this.applyProviderOverridesInScope(this.testModuleType);
  }
  get injector() {
    if (this._injector !== null) {
      return this._injector;
    }
    const providers = [];
    const compilerOptions = this.platform.injector.get(COMPILER_OPTIONS, []);
    compilerOptions.forEach(opts => {
      if (opts.providers) {
        providers.push(opts.providers);
      }
    });
    if (this.compilerProviders !== null) {
      providers.push(...this.compilerProviders);
    }
    this._injector = Injector.create({
      providers,
      parent: this.platform.injector
    });
    return this._injector;
  }
  getSingleProviderOverrides(provider) {
    const token = getProviderToken(provider);
    return this.providerOverridesByToken.get(token) || null;
  }
  getProviderOverrides(providers) {
    if (!providers || !providers.length || this.providerOverridesByToken.size === 0) return [];
    return flatten(flattenProviders(providers, provider => this.getSingleProviderOverrides(provider) || []));
  }
  getOverriddenProviders(providers) {
    if (!providers || !providers.length || this.providerOverridesByToken.size === 0) return [];
    const flattenedProviders = flattenProviders(providers);
    const overrides = this.getProviderOverrides(flattenedProviders);
    const overriddenProviders = [...flattenedProviders, ...overrides];
    const final = [];
    const seenOverriddenProviders = new Set();
    forEachRight(overriddenProviders, provider => {
      const token = getProviderToken(provider);
      if (this.providerOverridesByToken.has(token)) {
        if (!seenOverriddenProviders.has(token)) {
          seenOverriddenProviders.add(token);
          final.unshift({
            ...provider,
            multi: false
          });
        }
      } else {
        final.unshift(provider);
      }
    });
    return final;
  }
  hasProviderOverrides(providers) {
    return this.getProviderOverrides(providers).length > 0;
  }
  patchDefWithProviderOverrides(declaration, field) {
    const def = declaration[field];
    if (!def) {
      return;
    }
    if (def.viewProvidersResolver) {
      this.maybeStoreNgDef(field, declaration);
      const viewProvidersResolver = def.viewProvidersResolver;
      this.storeFieldOfDefOnType(declaration, field, 'viewProvidersResolver');
      def.viewProvidersResolver = ngDef => viewProvidersResolver(ngDef, this.processProviderOverrides);
    }
    if (def.providersResolver) {
      this.maybeStoreNgDef(field, declaration);
      const providersResolver = def.providersResolver;
      this.storeFieldOfDefOnType(declaration, field, 'providersResolver');
      def.providersResolver = ngDef => providersResolver(ngDef, this.processProviderOverrides);
    }
  }
  processProviderOverrides = providers => this.getOverriddenProviders(providers);
}
function initResolvers() {
  return {
    module: new NgModuleResolver(),
    component: new ComponentResolver(),
    directive: new DirectiveResolver(),
    pipe: new PipeResolver()
  };
}
function isStandaloneComponent(value) {
  const def = getComponentDef(value);
  return !!def?.standalone;
}
function getComponentDef(value) {
  return value.ɵcmp ?? null;
}
function hasNgModuleDef(value) {
  return Object.hasOwn(value, 'ɵmod');
}
function isNgModule(value) {
  return hasNgModuleDef(value);
}
function maybeUnwrapFn(maybeFn) {
  return maybeFn instanceof Function ? maybeFn() : maybeFn;
}
function flatten(values) {
  const out = [];
  values.forEach(value => {
    if (Array.isArray(value)) {
      out.push(...flatten(value));
    } else {
      out.push(value);
    }
  });
  return out;
}
function identityFn(value) {
  return value;
}
function flattenProviders(providers, mapFn = identityFn) {
  const out = [];
  for (let provider of providers) {
    if (isEnvironmentProviders(provider)) {
      provider = provider.ɵproviders;
    }
    if (Array.isArray(provider)) {
      out.push(...flattenProviders(provider, mapFn));
    } else {
      out.push(mapFn(provider));
    }
  }
  return out;
}
function getProviderField(provider, field) {
  return provider && typeof provider === 'object' && provider[field];
}
function getProviderToken(provider) {
  return getProviderField(provider, 'provide') || provider;
}
function isModuleWithProviders(value) {
  return Object.hasOwn(value, 'ngModule');
}
function forEachRight(values, fn) {
  for (let idx = values.length - 1; idx >= 0; idx--) {
    fn(values[idx], idx);
  }
}
function invalidTypeError(name, expectedType) {
  return new Error(`${name} class doesn't have @${expectedType} decorator or is missing metadata.`);
}
class R3TestCompiler {
  testBed;
  constructor(testBed) {
    this.testBed = testBed;
  }
  compileModuleSync(moduleType) {
    this.testBed._compileNgModuleSync(moduleType);
    return new NgModuleFactory(moduleType);
  }
  async compileModuleAsync(moduleType) {
    await this.testBed._compileNgModuleAsync(moduleType);
    return new NgModuleFactory(moduleType);
  }
  clearCache() {}
  clearCacheFor(type) {}
  getModuleId(moduleType) {
    const meta = this.testBed._getModuleResolver().resolve(moduleType);
    return meta && meta.id || undefined;
  }
}

let _nextRootElementId = 0;
function getTestBed() {
  return TestBedImpl.INSTANCE;
}
class TestBedImpl {
  static _INSTANCE = null;
  static get INSTANCE() {
    return TestBedImpl._INSTANCE = TestBedImpl._INSTANCE || new TestBedImpl();
  }
  static _environmentTeardownOptions;
  static _environmentErrorOnUnknownElementsOption;
  static _environmentErrorOnUnknownPropertiesOption;
  _instanceTeardownOptions;
  _instanceDeferBlockBehavior = DEFER_BLOCK_DEFAULT_BEHAVIOR;
  _instanceAnimationsEnabled = ANIMATIONS_ENABLED_DEFAULT;
  _instanceErrorOnUnknownElementsOption;
  _instanceErrorOnUnknownPropertiesOption;
  _previousErrorOnUnknownElementsOption;
  _previousErrorOnUnknownPropertiesOption;
  _instanceInferTagName;
  static initTestEnvironment(ngModule, platform, options) {
    const testBed = TestBedImpl.INSTANCE;
    testBed.initTestEnvironment(ngModule, platform, options);
    return testBed;
  }
  static resetTestEnvironment() {
    TestBedImpl.INSTANCE.resetTestEnvironment();
  }
  static configureCompiler(config) {
    return TestBedImpl.INSTANCE.configureCompiler(config);
  }
  static configureTestingModule(moduleDef) {
    return TestBedImpl.INSTANCE.configureTestingModule(moduleDef);
  }
  static compileComponents() {
    return TestBedImpl.INSTANCE.compileComponents();
  }
  static overrideModule(ngModule, override) {
    return TestBedImpl.INSTANCE.overrideModule(ngModule, override);
  }
  static overrideComponent(component, override) {
    return TestBedImpl.INSTANCE.overrideComponent(component, override);
  }
  static overrideDirective(directive, override) {
    return TestBedImpl.INSTANCE.overrideDirective(directive, override);
  }
  static overridePipe(pipe, override) {
    return TestBedImpl.INSTANCE.overridePipe(pipe, override);
  }
  static overrideTemplate(component, template) {
    return TestBedImpl.INSTANCE.overrideTemplate(component, template);
  }
  static overrideTemplateUsingTestingModule(component, template) {
    return TestBedImpl.INSTANCE.overrideTemplateUsingTestingModule(component, template);
  }
  static overrideProvider(token, provider) {
    return TestBedImpl.INSTANCE.overrideProvider(token, provider);
  }
  static inject(token, notFoundValue, options) {
    return TestBedImpl.INSTANCE.inject(token, notFoundValue, options);
  }
  static runInInjectionContext(fn) {
    return TestBedImpl.INSTANCE.runInInjectionContext(fn);
  }
  static createComponent(component, options) {
    return TestBedImpl.INSTANCE.createComponent(component, options);
  }
  static getLastFixture() {
    return TestBedImpl.INSTANCE.getLastFixture();
  }
  static resetTestingModule() {
    return TestBedImpl.INSTANCE.resetTestingModule();
  }
  static execute(tokens, fn, context) {
    return TestBedImpl.INSTANCE.execute(tokens, fn, context);
  }
  static get platform() {
    return TestBedImpl.INSTANCE.platform;
  }
  static get ngModule() {
    return TestBedImpl.INSTANCE.ngModule;
  }
  static flushEffects() {
    return TestBedImpl.INSTANCE.tick();
  }
  static tick() {
    return TestBedImpl.INSTANCE.tick();
  }
  platform = null;
  ngModule = null;
  _compiler = null;
  _testModuleRef = null;
  _activeFixtures = [];
  globalCompilationChecked = false;
  initTestEnvironment(ngModule, platform, options) {
    if (this.platform || this.ngModule) {
      throw new Error('Cannot set base providers because it has already been called');
    }
    TestBedImpl._environmentTeardownOptions = options?.teardown;
    TestBedImpl._environmentErrorOnUnknownElementsOption = options?.errorOnUnknownElements;
    TestBedImpl._environmentErrorOnUnknownPropertiesOption = options?.errorOnUnknownProperties;
    this.platform = platform;
    this.ngModule = ngModule;
    this._compiler = new TestBedCompiler(this.platform, this.ngModule);
    setAllowDuplicateNgModuleIdsForTest(true);
  }
  resetTestEnvironment() {
    this.resetTestingModule();
    this._compiler = null;
    this.platform = null;
    this.ngModule = null;
    TestBedImpl._environmentTeardownOptions = undefined;
    setAllowDuplicateNgModuleIdsForTest(false);
  }
  resetTestingModule() {
    this.checkGlobalCompilationFinished();
    resetCompiledComponents();
    if (this._compiler !== null) {
      this.compiler.restoreOriginalState();
    }
    this._compiler = new TestBedCompiler(this.platform, this.ngModule);
    _setUnknownElementStrictMode(this._previousErrorOnUnknownElementsOption ?? THROW_ON_UNKNOWN_ELEMENTS_DEFAULT);
    _setUnknownPropertyStrictMode(this._previousErrorOnUnknownPropertiesOption ?? THROW_ON_UNKNOWN_PROPERTIES_DEFAULT);
    try {
      this.destroyActiveFixtures();
    } finally {
      try {
        if (this.shouldTearDownTestingModule()) {
          this.tearDownTestingModule();
        }
      } finally {
        this._testModuleRef = null;
        this._instanceTeardownOptions = undefined;
        this._instanceErrorOnUnknownElementsOption = undefined;
        this._instanceErrorOnUnknownPropertiesOption = undefined;
        this._instanceInferTagName = undefined;
        this._instanceDeferBlockBehavior = DEFER_BLOCK_DEFAULT_BEHAVIOR;
        this._instanceAnimationsEnabled = ANIMATIONS_ENABLED_DEFAULT;
      }
    }
    return this;
  }
  configureCompiler(config) {
    if (config.useJit != null) {
      throw new Error('JIT compiler is not configurable via TestBed APIs.');
    }
    if (config.providers !== undefined) {
      this.compiler.setCompilerProviders(config.providers);
    }
    return this;
  }
  configureTestingModule(moduleDef) {
    this.assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
    this.checkGlobalCompilationFinished();
    this._instanceTeardownOptions = moduleDef.teardown;
    this._instanceErrorOnUnknownElementsOption = moduleDef.errorOnUnknownElements;
    this._instanceErrorOnUnknownPropertiesOption = moduleDef.errorOnUnknownProperties;
    this._instanceInferTagName = moduleDef.inferTagName;
    this._instanceDeferBlockBehavior = moduleDef.deferBlockBehavior ?? DEFER_BLOCK_DEFAULT_BEHAVIOR;
    this._instanceAnimationsEnabled = moduleDef.animationsEnabled ?? ANIMATIONS_ENABLED_DEFAULT;
    this._previousErrorOnUnknownElementsOption = _getUnknownElementStrictMode();
    _setUnknownElementStrictMode(this.shouldThrowErrorOnUnknownElements());
    this._previousErrorOnUnknownPropertiesOption = _getUnknownPropertyStrictMode();
    _setUnknownPropertyStrictMode(this.shouldThrowErrorOnUnknownProperties());
    this.compiler.configureTestingModule(moduleDef);
    return this;
  }
  compileComponents() {
    return this.compiler.compileComponents();
  }
  inject(token, notFoundValue, options) {
    if (token === TestBed) {
      return this;
    }
    const UNDEFINED = {};
    const result = this.testModuleRef.injector.get(token, UNDEFINED, options);
    return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, options) : result;
  }
  runInInjectionContext(fn) {
    return runInInjectionContext(this.inject(EnvironmentInjector), fn);
  }
  execute(tokens, fn, context) {
    const params = tokens.map(t => this.inject(t));
    return fn.apply(context, params);
  }
  overrideModule(ngModule, override) {
    this.assertNotInstantiated('overrideModule', 'override module metadata');
    this.compiler.overrideModule(ngModule, override);
    return this;
  }
  overrideComponent(component, override) {
    this.assertNotInstantiated('overrideComponent', 'override component metadata');
    this.compiler.overrideComponent(component, override);
    return this;
  }
  overrideTemplateUsingTestingModule(component, template) {
    this.assertNotInstantiated('TestBed.overrideTemplateUsingTestingModule', 'Cannot override template when the test module has already been instantiated');
    this.compiler.overrideTemplateUsingTestingModule(component, template);
    return this;
  }
  overrideDirective(directive, override) {
    this.assertNotInstantiated('overrideDirective', 'override directive metadata');
    this.compiler.overrideDirective(directive, override);
    return this;
  }
  overridePipe(pipe, override) {
    this.assertNotInstantiated('overridePipe', 'override pipe metadata');
    this.compiler.overridePipe(pipe, override);
    return this;
  }
  overrideProvider(token, provider) {
    this.assertNotInstantiated('overrideProvider', 'override provider');
    this.compiler.overrideProvider(token, provider);
    return this;
  }
  overrideTemplate(component, template) {
    return this.overrideComponent(component, {
      set: {
        template,
        templateUrl: null
      }
    });
  }
  createComponent(type, options) {
    if (getAsyncClassMetadataFn(type)) {
      const isCompiled = !!getComponentDef$1(type);
      if (!isCompiled) {
        throw new Error(`Component '${type.name}' has unresolved metadata. ` + `Please call \`await TestBed.compileComponents()\` before running this test.`);
      }
    }
    const testComponentRenderer = this.inject(TestComponentRenderer);
    const shouldInferTagName = options?.inferTagName ?? this._instanceInferTagName ?? false;
    const componentDef = getComponentDef$1(type);
    const rootElId = `root${_nextRootElementId++}`;
    if (!componentDef) {
      throw new Error(`It looks like '${stringify(type)}' has not been compiled.`);
    }
    testComponentRenderer.insertRootElement(rootElId, shouldInferTagName ? inferTagNameFromDefinition(componentDef) : undefined);
    const componentFactory = new ComponentFactory(componentDef);
    const initComponent = () => {
      const componentRef = componentFactory.create(Injector.NULL, [], `#${rootElId}`, this.testModuleRef, undefined, options?.bindings);
      return this.runInInjectionContext(() => new ComponentFixture(componentRef));
    };
    const noNgZone = this.inject(ComponentFixtureNoNgZone, false);
    const ngZone = noNgZone ? null : this.inject(NgZone, null);
    const fixture = ngZone ? ngZone.run(initComponent) : initComponent();
    this._activeFixtures.push(fixture);
    return fixture;
  }
  getLastFixture() {
    if (this._activeFixtures.length === 0) {
      throw new Error('No fixture has been created yet.');
    }
    return this._activeFixtures[this._activeFixtures.length - 1];
  }
  get compiler() {
    if (this._compiler === null) {
      throw new Error(`Need to call TestBed.initTestEnvironment() first`);
    }
    return this._compiler;
  }
  get testModuleRef() {
    if (this._testModuleRef === null) {
      this._testModuleRef = this.compiler.finalize();
    }
    return this._testModuleRef;
  }
  assertNotInstantiated(methodName, methodDescription) {
    if (this._testModuleRef !== null) {
      throw new Error(`Cannot ${methodDescription} when the test module has already been instantiated. ` + `Make sure you are not using \`inject\` before \`${methodName}\`.`);
    }
  }
  checkGlobalCompilationFinished() {
    if (!this.globalCompilationChecked && this._testModuleRef === null) {
      flushModuleScopingQueueAsMuchAsPossible();
    }
    this.globalCompilationChecked = true;
  }
  destroyActiveFixtures() {
    let errorCount = 0;
    this._activeFixtures.forEach(fixture => {
      try {
        fixture.destroy();
      } catch (e) {
        errorCount++;
        console.error('Error during cleanup of component', {
          component: fixture.componentInstance,
          stacktrace: e
        });
      }
    });
    this._activeFixtures = [];
    if (errorCount > 0 && this.shouldRethrowTeardownErrors()) {
      throw Error(`${errorCount} ${errorCount === 1 ? 'component' : 'components'} ` + `threw errors during cleanup`);
    }
  }
  shouldRethrowTeardownErrors() {
    const instanceOptions = this._instanceTeardownOptions;
    const environmentOptions = TestBedImpl._environmentTeardownOptions;
    if (!instanceOptions && !environmentOptions) {
      return TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT;
    }
    return instanceOptions?.rethrowErrors ?? environmentOptions?.rethrowErrors ?? this.shouldTearDownTestingModule();
  }
  shouldThrowErrorOnUnknownElements() {
    return this._instanceErrorOnUnknownElementsOption ?? TestBedImpl._environmentErrorOnUnknownElementsOption ?? THROW_ON_UNKNOWN_ELEMENTS_DEFAULT;
  }
  shouldThrowErrorOnUnknownProperties() {
    return this._instanceErrorOnUnknownPropertiesOption ?? TestBedImpl._environmentErrorOnUnknownPropertiesOption ?? THROW_ON_UNKNOWN_PROPERTIES_DEFAULT;
  }
  shouldTearDownTestingModule() {
    return this._instanceTeardownOptions?.destroyAfterEach ?? TestBedImpl._environmentTeardownOptions?.destroyAfterEach ?? TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT;
  }
  getDeferBlockBehavior() {
    return this._instanceDeferBlockBehavior;
  }
  getAnimationsEnabled() {
    return this._instanceAnimationsEnabled;
  }
  tearDownTestingModule() {
    if (this._testModuleRef === null) {
      return;
    }
    const testRenderer = this.inject(TestComponentRenderer);
    try {
      this._testModuleRef.destroy();
    } catch (e) {
      if (this.shouldRethrowTeardownErrors()) {
        throw e;
      } else {
        console.error('Error during cleanup of a testing module', {
          component: this._testModuleRef.instance,
          stacktrace: e
        });
      }
    } finally {
      testRenderer.removeAllRootElements?.();
    }
  }
  flushEffects() {
    this.tick();
  }
  tick() {
    const appRef = this.inject(ApplicationRef);
    try {
      appRef.includeAllTestViews = true;
      appRef.tick();
    } finally {
      appRef.includeAllTestViews = false;
    }
  }
}
const TestBed = TestBedImpl;
function inject(tokens, fn) {
  const testBed = TestBedImpl.INSTANCE;
  return function () {
    return testBed.execute(tokens, fn, this);
  };
}
class InjectSetupWrapper {
  _moduleDef;
  constructor(_moduleDef) {
    this._moduleDef = _moduleDef;
  }
  _addModule() {
    const moduleDef = this._moduleDef();
    if (moduleDef) {
      TestBedImpl.configureTestingModule(moduleDef);
    }
  }
  inject(tokens, fn) {
    const self = this;
    return function () {
      self._addModule();
      return inject(tokens, fn).call(this);
    };
  }
}
function withModule(moduleDef, fn) {
  if (fn) {
    return function () {
      const testBed = TestBedImpl.INSTANCE;
      if (moduleDef) {
        testBed.configureTestingModule(moduleDef);
      }
      return fn.apply(this);
    };
  }
  return new InjectSetupWrapper(() => moduleDef);
}

class FakeNavigation {
  entriesArr = [];
  currentEntryIndex = 0;
  navigateEvent = null;
  traversalQueue = new Map();
  nextTraversal = Promise.resolve();
  propsectiveTraversalDestinations = [];
  synchronousTraversals = false;
  canSetInitialEntry = true;
  eventTarget;
  activation = null;
  nextId = 0;
  nextKey = 0;
  disposed = false;
  get currentEntry() {
    return this.entriesArr[this.currentEntryIndex];
  }
  get canGoBack() {
    return this.currentEntryIndex > 0;
  }
  get canGoForward() {
    return this.currentEntryIndex < this.entriesArr.length - 1;
  }
  createEventTarget;
  _window;
  get window() {
    return this._window;
  }
  constructor(doc, startURL) {
    this.createEventTarget = () => {
      try {
        return doc.createElement('div');
      } catch {
        return new EventTarget();
      }
    };
    this._window = doc.defaultView ?? this.createEventTarget();
    this.eventTarget = this.createEventTarget();
    this.setInitialEntryForTesting(startURL);
  }
  setInitialEntryForTesting(url, options = {
    historyState: null
  }) {
    if (!this.canSetInitialEntry) {
      throw new Error('setInitialEntryForTesting can only be called before any ' + 'navigation has occurred');
    }
    const currentInitialEntry = this.entriesArr[0];
    this.entriesArr[0] = new FakeNavigationHistoryEntry(this.eventTarget, new URL(url).toString(), {
      index: 0,
      key: currentInitialEntry?.key ?? String(this.nextKey++),
      id: currentInitialEntry?.id ?? String(this.nextId++),
      sameDocument: true,
      historyState: options?.historyState,
      state: options.state
    });
  }
  canSetInitialEntryForTesting() {
    return this.canSetInitialEntry;
  }
  setSynchronousTraversalsForTesting(synchronousTraversals) {
    this.synchronousTraversals = synchronousTraversals;
  }
  entries() {
    return this.entriesArr.slice();
  }
  navigate(url, options) {
    const fromUrl = new URL(this.currentEntry.url);
    const toUrl = new URL(url, this.currentEntry.url);
    let navigationType;
    if (!options?.history || options.history === 'auto') {
      if (fromUrl.toString() === toUrl.toString()) {
        navigationType = 'replace';
      } else {
        navigationType = 'push';
      }
    } else {
      navigationType = options.history;
    }
    const hashChange = isHashChange(fromUrl, toUrl);
    const destination = new FakeNavigationDestination({
      url: toUrl.toString(),
      state: options?.state,
      sameDocument: hashChange,
      historyState: null
    });
    const result = new InternalNavigationResult(this);
    const intercepted = this.userAgentNavigate(destination, result, {
      navigationType,
      cancelable: true,
      canIntercept: true,
      userInitiated: false,
      hashChange,
      info: options?.info
    });
    if (!intercepted) {
      this.updateNavigationEntriesForSameDocumentNavigation(this.navigateEvent);
    }
    return {
      committed: result.committed,
      finished: result.finished
    };
  }
  pushState(data, title, url) {
    this.pushOrReplaceState('push', data, title, url);
  }
  replaceState(data, title, url) {
    this.pushOrReplaceState('replace', data, title, url);
  }
  pushOrReplaceState(navigationType, data, _title, url) {
    const fromUrl = new URL(this.currentEntry.url);
    const toUrl = url ? new URL(url, this.currentEntry.url) : fromUrl;
    const hashChange = isHashChange(fromUrl, toUrl);
    const destination = new FakeNavigationDestination({
      url: toUrl.toString(),
      sameDocument: true,
      historyState: data,
      state: undefined
    });
    const result = new InternalNavigationResult(this);
    const intercepted = this.userAgentNavigate(destination, result, {
      navigationType,
      cancelable: true,
      canIntercept: true,
      userInitiated: false,
      hashChange
    });
    if (intercepted) {
      return;
    }
    this.updateNavigationEntriesForSameDocumentNavigation(this.navigateEvent);
  }
  traverseTo(key, options) {
    const fromUrl = new URL(this.currentEntry.url);
    const entry = this.findEntry(key);
    if (!entry) {
      const domException = new DOMException('Invalid key', 'InvalidStateError');
      const committed = Promise.reject(domException);
      const finished = Promise.reject(domException);
      committed.catch(() => {});
      finished.catch(() => {});
      return {
        committed,
        finished
      };
    }
    if (entry === this.currentEntry) {
      return {
        committed: Promise.resolve(this.currentEntry),
        finished: Promise.resolve(this.currentEntry)
      };
    }
    if (this.traversalQueue.has(entry.key)) {
      const existingResult = this.traversalQueue.get(entry.key);
      return {
        committed: existingResult.committed,
        finished: existingResult.finished
      };
    }
    const hashChange = isHashChange(fromUrl, new URL(entry.url, this.currentEntry.url));
    const destination = new FakeNavigationDestination({
      url: entry.url,
      state: entry.getState(),
      historyState: entry.getHistoryState(),
      key: entry.key,
      id: entry.id,
      index: entry.index,
      sameDocument: entry.sameDocument
    });
    this.propsectiveTraversalDestinations.push(entry.index);
    const result = new InternalNavigationResult(this);
    this.traversalQueue.set(entry.key, result);
    this.runTraversal(() => {
      this.traversalQueue.delete(entry.key);
      const intercepted = this.userAgentNavigate(destination, result, {
        navigationType: 'traverse',
        cancelable: true,
        canIntercept: true,
        userInitiated: false,
        hashChange,
        info: options?.info
      });
      if (!intercepted) {
        this.userAgentTraverse(this.navigateEvent);
      }
    });
    return {
      committed: result.committed,
      finished: result.finished
    };
  }
  back(options) {
    if (this.currentEntryIndex === 0) {
      const domException = new DOMException('Cannot go back', 'InvalidStateError');
      const committed = Promise.reject(domException);
      const finished = Promise.reject(domException);
      committed.catch(() => {});
      finished.catch(() => {});
      return {
        committed,
        finished
      };
    }
    const entry = this.entriesArr[this.currentEntryIndex - 1];
    return this.traverseTo(entry.key, options);
  }
  forward(options) {
    if (this.currentEntryIndex === this.entriesArr.length - 1) {
      const domException = new DOMException('Cannot go forward', 'InvalidStateError');
      const committed = Promise.reject(domException);
      const finished = Promise.reject(domException);
      committed.catch(() => {});
      finished.catch(() => {});
      return {
        committed,
        finished
      };
    }
    const entry = this.entriesArr[this.currentEntryIndex + 1];
    return this.traverseTo(entry.key, options);
  }
  go(direction) {
    const targetIndex = (this.propsectiveTraversalDestinations[this.propsectiveTraversalDestinations.length - 1] ?? this.currentEntryIndex) + direction;
    if (targetIndex >= this.entriesArr.length || targetIndex < 0) {
      return;
    }
    this.propsectiveTraversalDestinations.push(targetIndex);
    this.runTraversal(() => {
      if (targetIndex >= this.entriesArr.length || targetIndex < 0) {
        return;
      }
      const fromUrl = new URL(this.currentEntry.url);
      const entry = this.entriesArr[targetIndex];
      const hashChange = isHashChange(fromUrl, new URL(entry.url, this.currentEntry.url));
      const destination = new FakeNavigationDestination({
        url: entry.url,
        state: entry.getState(),
        historyState: entry.getHistoryState(),
        key: entry.key,
        id: entry.id,
        index: entry.index,
        sameDocument: entry.sameDocument
      });
      const result = new InternalNavigationResult(this);
      const intercepted = this.userAgentNavigate(destination, result, {
        navigationType: 'traverse',
        cancelable: true,
        canIntercept: true,
        userInitiated: false,
        hashChange
      });
      if (!intercepted) {
        this.userAgentTraverse(this.navigateEvent);
      }
    });
  }
  runTraversal(traversal) {
    if (this.synchronousTraversals) {
      traversal();
      this.propsectiveTraversalDestinations.shift();
      return;
    }
    this.nextTraversal = this.nextTraversal.then(() => {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve();
          traversal();
          this.propsectiveTraversalDestinations.shift();
        });
      });
    });
  }
  addEventListener(type, callback, options) {
    this.eventTarget.addEventListener(type, callback, options);
  }
  removeEventListener(type, callback, options) {
    this.eventTarget.removeEventListener(type, callback, options);
  }
  dispatchEvent(event) {
    return this.eventTarget.dispatchEvent(event);
  }
  dispose() {
    this.eventTarget = this.createEventTarget();
    this.disposed = true;
  }
  isDisposed() {
    return this.disposed;
  }
  abortOngoingNavigation(eventToAbort, reason) {
    if (this.navigateEvent !== eventToAbort) {
      return;
    }
    if (this.navigateEvent.abortController.signal.aborted) {
      return;
    }
    const abortReason = reason ?? new DOMException('Navigation aborted', 'AbortError');
    this.navigateEvent.abort(abortReason);
  }
  userAgentNavigate(destination, result, options) {
    this.canSetInitialEntry = false;
    if (this.navigateEvent) {
      this.abortOngoingNavigation(this.navigateEvent, new DOMException('Navigation superseded by a new navigation.', 'AbortError'));
    }
    const dispatchResultIsTrueIfNoInterception = dispatchNavigateEvent({
      navigationType: options.navigationType,
      cancelable: options.cancelable,
      canIntercept: options.canIntercept,
      userInitiated: options.userInitiated,
      hashChange: options.hashChange,
      destination,
      info: options.info,
      sameDocument: destination.sameDocument,
      result
    });
    return !dispatchResultIsTrueIfNoInterception;
  }
  urlAndHistoryUpdateSteps(navigateEvent) {
    this.updateNavigationEntriesForSameDocumentNavigation(navigateEvent);
  }
  userAgentTraverse(navigateEvent) {
    const oldUrl = this.currentEntry.url;
    this.updateNavigationEntriesForSameDocumentNavigation(navigateEvent);
    const popStateEvent = createPopStateEvent({
      state: navigateEvent.destination.getHistoryState()
    });
    this._window.dispatchEvent(popStateEvent);
    if (navigateEvent.hashChange) {
      const hashchangeEvent = createHashChangeEvent(oldUrl, this.currentEntry.url);
      this._window.dispatchEvent(hashchangeEvent);
    }
  }
  updateNavigationEntriesForSameDocumentNavigation({
    destination,
    navigationType,
    result
  }) {
    const oldCurrentNHE = this.currentEntry;
    const disposedNHEs = [];
    if (navigationType === 'traverse') {
      this.currentEntryIndex = destination.index;
      if (this.currentEntryIndex === -1) {
        throw new Error('unexpected current entry index');
      }
    } else if (navigationType === 'push') {
      this.currentEntryIndex++;
      this.propsectiveTraversalDestinations = [];
      disposedNHEs.push(...this.entriesArr.splice(this.currentEntryIndex));
    } else if (navigationType === 'replace') {
      disposedNHEs.push(oldCurrentNHE);
    }
    if (navigationType === 'push' || navigationType === 'replace') {
      const index = this.currentEntryIndex;
      const key = navigationType === 'push' ? String(this.nextKey++) : oldCurrentNHE?.key ?? String(this.nextKey++);
      const newNHE = new FakeNavigationHistoryEntry(this.eventTarget, destination.url, {
        id: String(this.nextId++),
        key,
        index,
        sameDocument: true,
        state: destination.getState(),
        historyState: destination.getHistoryState()
      });
      this.entriesArr[this.currentEntryIndex] = newNHE;
    }
    result.committedResolve(this.currentEntry);
    const currentEntryChangeEvent = createFakeNavigationCurrentEntryChangeEvent({
      from: oldCurrentNHE,
      navigationType: navigationType
    });
    this.eventTarget.dispatchEvent(currentEntryChangeEvent);
    for (const disposedNHE of disposedNHEs) {
      disposedNHE.dispose();
    }
  }
  findEntry(key) {
    for (const entry of this.entriesArr) {
      if (entry.key === key) return entry;
    }
    return undefined;
  }
  set onnavigate(_handler) {
    throw new Error('unimplemented');
  }
  get onnavigate() {
    throw new Error('unimplemented');
  }
  set oncurrententrychange(_handler) {
    throw new Error('unimplemented');
  }
  get oncurrententrychange() {
    throw new Error('unimplemented');
  }
  set onnavigatesuccess(_handler) {
    throw new Error('unimplemented');
  }
  get onnavigatesuccess() {
    throw new Error('unimplemented');
  }
  set onnavigateerror(_handler) {
    throw new Error('unimplemented');
  }
  get onnavigateerror() {
    throw new Error('unimplemented');
  }
  _transition = null;
  set transition(t) {
    this._transition = t;
  }
  get transition() {
    return this._transition;
  }
  updateCurrentEntry(_options) {
    throw new Error('unimplemented');
  }
  reload(_options) {
    throw new Error('unimplemented');
  }
}
class FakeNavigationHistoryEntry {
  eventTarget;
  url;
  sameDocument;
  id;
  key;
  index;
  state;
  historyState;
  ondispose = null;
  constructor(eventTarget, url, {
    id,
    key,
    index,
    sameDocument,
    state,
    historyState
  }) {
    this.eventTarget = eventTarget;
    this.url = url;
    this.id = id;
    this.key = key;
    this.index = index;
    this.sameDocument = sameDocument;
    this.state = state;
    this.historyState = historyState;
  }
  getState() {
    return this.state ? JSON.parse(JSON.stringify(this.state)) : this.state;
  }
  getHistoryState() {
    return this.historyState ? JSON.parse(JSON.stringify(this.historyState)) : this.historyState;
  }
  addEventListener(type, callback, options) {
    this.eventTarget.addEventListener(type, callback, options);
  }
  removeEventListener(type, callback, options) {
    this.eventTarget.removeEventListener(type, callback, options);
  }
  dispatchEvent(event) {
    return this.eventTarget.dispatchEvent(event);
  }
  dispose() {
    const disposeEvent = new Event('disposed');
    this.dispatchEvent(disposeEvent);
    this.eventTarget = null;
  }
}
function dispatchNavigateEvent({
  cancelable,
  canIntercept,
  userInitiated,
  hashChange,
  navigationType,
  destination,
  info,
  sameDocument,
  result
}) {
  const {
    navigation
  } = result;
  const eventAbortController = new AbortController();
  const event = new Event('navigate', {
    bubbles: false,
    cancelable
  });
  event.navigationType = navigationType;
  event.destination = destination;
  event.canIntercept = canIntercept;
  event.userInitiated = userInitiated;
  event.hashChange = hashChange;
  event.signal = eventAbortController.signal;
  event.abortController = eventAbortController;
  event.info = info;
  event.focusResetBehavior = null;
  event.scrollBehavior = null;
  event.interceptionState = 'none';
  event.downloadRequest = null;
  event.formData = null;
  event.result = result;
  event.sameDocument = sameDocument;
  let precommitHandlers = [];
  let handlers = [];
  event.intercept = function (options) {
    if (!this.canIntercept) {
      throw new DOMException(`Cannot intercept when canIntercept is 'false'`, 'SecurityError');
    }
    this.interceptionState = 'intercepted';
    event.sameDocument = true;
    const precommitHandler = options?.precommitHandler;
    if (precommitHandler) {
      if (!this.cancelable) {
        throw new DOMException(`Cannot use precommitHandler when cancelable is 'false'`, 'InvalidStateError');
      }
      precommitHandlers.push(precommitHandler);
    }
    if (event.interceptionState !== 'none' && event.interceptionState !== 'intercepted') {
      throw new Error('Event interceptionState should be "none" or "intercepted"');
    }
    event.interceptionState = 'intercepted';
    const handler = options?.handler;
    if (handler) {
      handlers.push(handler);
    }
    event.focusResetBehavior = options?.focusReset ?? event.focusResetBehavior;
    event.scrollBehavior = options?.scroll ?? event.scrollBehavior;
  };
  event.scroll = function () {
    if (event.interceptionState !== 'committed') {
      throw new DOMException(`Failed to execute 'scroll' on 'NavigateEvent': scroll() must be ` + `called after commit() and interception options must specify manual scroll.`, 'InvalidStateError');
    }
    processScrollBehavior(event);
  };
  function redirect(url, options = {}) {
    if (event.interceptionState === 'none') {
      throw new Error('cannot redirect when event is not intercepted');
    }
    if (event.interceptionState !== 'intercepted') {
      throw new DOMException(`cannot redirect when event is not in 'intercepted' state`, 'InvalidStateError');
    }
    if (event.navigationType !== 'push' && event.navigationType !== 'replace') {
      throw new DOMException(`cannot redirect when navigationType is not 'push' or 'replace`, 'InvalidStateError');
    }
    const destinationUrl = new URL(url, navigation.currentEntry.url);
    if (options.history === 'push' || options.history === 'replace') {
      event.navigationType = options.history;
    }
    if (Object.hasOwn(options, 'state')) {
      event.destination.state = options.state;
    }
    event.destination.url = destinationUrl.href;
    if (Object.hasOwn(options, 'info')) {
      event.info = options.info;
    }
  }
  function processNavigateEventHandlerFailure(reason) {
    if (event.abortController.signal.aborted) {
      return;
    }
    if (event !== navigation.navigateEvent) {
      throw new Error('Event is no longer the current navigation event');
    }
    if (event.interceptionState !== 'intercepted') {
      finishNavigationEvent(event, false);
    }
    event.abort(reason);
  }
  function commit() {
    if (result.signal.aborted) {
      return;
    }
    if (event.interceptionState !== 'none') {
      event.interceptionState = 'committed';
      switch (event.navigationType) {
        case 'push':
        case 'replace':
          {
            navigation.urlAndHistoryUpdateSteps(event);
            break;
          }
        case 'reload':
          {
            navigation.updateNavigationEntriesForSameDocumentNavigation(event);
            break;
          }
        case 'traverse':
          {
            navigation.userAgentTraverse(event);
            break;
          }
      }
    }
    navigation.transition?.committedResolve();
    const promisesList = [];
    for (const handler of handlers) {
      const handlerResult = handler();
      if (handlerResult) {
        promisesList.push(handlerResult);
      }
    }
    promisesList.push(result.committed);
    Promise.all(promisesList).then(() => {
      if (result.signal.aborted) {
        return;
      }
      if (event !== navigation.navigateEvent) {
        if (!result.signal.aborted && result.committedTo) {
          result.finishedReject(new DOMException('Navigation superseded before handler completion', 'AbortError'));
        }
        return;
      }
      navigation.navigateEvent = null;
      finishNavigationEvent(event, true);
      result.finishedResolve();
      const navigatesuccessEvent = new Event('navigatesuccess', {
        bubbles: false,
        cancelable: false
      });
      navigation.eventTarget.dispatchEvent(navigatesuccessEvent);
      navigation.transition?.finishedResolve();
      navigation.transition = null;
    }).catch(processNavigateEventHandlerFailure);
  }
  event.abort = function (reason) {
    this.abortController.abort(reason);
    navigation.navigateEvent = null;
    result.finishedReject(reason);
    const navigateerrorEvent = new Event('navigateerror', {
      bubbles: false,
      cancelable
    });
    navigateerrorEvent.error = reason;
    navigation.eventTarget.dispatchEvent(navigateerrorEvent);
    const transition = navigation.transition;
    transition?.committedReject(reason);
    transition?.finishedReject(reason);
    navigation.transition = null;
  };
  function dispatch() {
    navigation.navigateEvent = event;
    const dispatchResult = navigation.eventTarget.dispatchEvent(event);
    if (event.interceptionState === 'intercepted') {
      if (!navigation.currentEntry) {
        event.abort(new DOMException('Cannot create transition without a currentEntry for intercepted navigation.', 'InvalidStateError'));
        return;
      }
      const transition = new InternalNavigationTransition(navigation.currentEntry, event.destination, navigationType);
      navigation.transition = transition;
      transition.finished.catch(() => {});
      transition.committed.catch(() => {});
    }
    if (!dispatchResult && event.cancelable) {
      if (!event.abortController.signal.aborted) {
        event.abort(new DOMException('Navigation prevented by event.preventDefault()', 'AbortError'));
      }
    } else {
      if (precommitHandlers.length === 0) {
        commit();
      } else {
        const precommitController = {
          redirect
        };
        const precommitPromisesList = precommitHandlers.map(handler => {
          let p;
          try {
            p = handler(precommitController);
          } catch (e) {
            p = Promise.reject(e);
          }
          p.catch(() => {});
          return p;
        });
        Promise.all(precommitPromisesList).then(() => commit()).catch(processNavigateEventHandlerFailure);
      }
    }
  }
  dispatch();
  return event.interceptionState === 'none';
}
function finishNavigationEvent(event, didFulfill) {
  if (event.interceptionState === 'finished') {
    throw new Error('Attempting to finish navigation event that was already finished');
  }
  if (event.interceptionState === 'intercepted') {
    if (didFulfill === true) {
      throw new Error('didFulfill should be false');
    }
    event.interceptionState = 'finished';
    return;
  }
  if (event.interceptionState === 'none') {
    return;
  }
  potentiallyResetFocus(event);
  if (didFulfill) {
    potentiallyResetScroll(event);
  }
  event.interceptionState = 'finished';
}
function potentiallyResetFocus(event) {
  if (event.interceptionState !== 'committed' && event.interceptionState !== 'scrolled') {
    throw new Error('cannot reset focus if navigation event is not committed or scrolled');
  }
  if (event.focusResetBehavior === 'manual') {
    return;
  }
}
function potentiallyResetScroll(event) {
  if (event.interceptionState !== 'committed' && event.interceptionState !== 'scrolled') {
    throw new Error('cannot reset scroll if navigation event is not committed or scrolled');
  }
  if (event.interceptionState === 'scrolled' || event.scrollBehavior === 'manual') {
    return;
  }
  processScrollBehavior(event);
}
function processScrollBehavior(event) {
  if (event.interceptionState !== 'committed') {
    throw new Error('invalid event interception state when processing scroll behavior');
  }
  event.interceptionState = 'scrolled';
}
function createFakeNavigationCurrentEntryChangeEvent({
  from,
  navigationType
}) {
  const event = new Event('currententrychange', {
    bubbles: false,
    cancelable: false
  });
  event.from = from;
  event.navigationType = navigationType;
  return event;
}
function createPopStateEvent({
  state
}) {
  const event = new Event('popstate', {
    bubbles: false,
    cancelable: false
  });
  event.state = state;
  return event;
}
function createHashChangeEvent(newURL, oldURL) {
  const event = new Event('hashchange', {
    bubbles: false,
    cancelable: false
  });
  event.newURL = newURL;
  event.oldURL = oldURL;
  return event;
}
class FakeNavigationDestination {
  url;
  sameDocument;
  key;
  id;
  index;
  state;
  historyState;
  constructor({
    url,
    sameDocument,
    historyState,
    state,
    key = null,
    id = null,
    index = -1
  }) {
    this.url = url;
    this.sameDocument = sameDocument;
    this.state = state;
    this.historyState = historyState;
    this.key = key ?? '';
    this.id = id ?? '';
    this.index = index;
  }
  getState() {
    return this.state;
  }
  getHistoryState() {
    return this.historyState;
  }
}
function isHashChange(from, to) {
  return to.hash !== from.hash && to.hostname === from.hostname && to.pathname === from.pathname && to.search === from.search;
}
class InternalNavigationTransition {
  from;
  to;
  navigationType;
  finished;
  committed;
  finishedResolve;
  finishedReject;
  committedResolve;
  committedReject;
  constructor(from, to, navigationType) {
    this.from = from;
    this.to = to;
    this.navigationType = navigationType;
    this.finished = new Promise((resolve, reject) => {
      this.finishedReject = reject;
      this.finishedResolve = resolve;
    });
    this.committed = new Promise((resolve, reject) => {
      this.committedReject = reject;
      this.committedResolve = resolve;
    });
    this.finished.catch(() => {});
    this.committed.catch(() => {});
  }
}
class InternalNavigationResult {
  navigation;
  committedTo = null;
  committedResolve;
  committedReject;
  finishedResolve;
  finishedReject;
  committed;
  finished;
  get signal() {
    return this.abortController.signal;
  }
  abortController = new AbortController();
  constructor(navigation) {
    this.navigation = navigation;
    this.committed = new Promise((resolve, reject) => {
      this.committedResolve = entry => {
        this.committedTo = entry;
        resolve(entry);
      };
      this.committedReject = reject;
    });
    this.finished = new Promise((resolve, reject) => {
      this.finishedResolve = () => {
        if (this.committedTo === null) {
          throw new Error('NavigateEvent should have been committed before resolving finished promise.');
        }
        resolve(this.committedTo);
      };
      this.finishedReject = reason => {
        this.committedReject(reason);
        reject(reason);
      };
    });
    this.committed.catch(() => {});
    this.finished.catch(() => {});
  }
}

globalThis.beforeEach?.(getCleanupHook(false));
globalThis.afterEach?.(getCleanupHook(true));
function getCleanupHook(expectedTeardownValue) {
  return () => {
    const testBed = TestBedImpl.INSTANCE;
    if (testBed.shouldTearDownTestingModule() === expectedTeardownValue) {
      testBed.resetTestingModule();
      resetFakeAsyncZoneIfExists();
    }
  };
}

class Log {
  logItems;
  constructor() {
    this.logItems = [];
  }
  add(value) {
    this.logItems.push(value);
  }
  fn(value) {
    return () => {
      this.logItems.push(value);
    };
  }
  clear() {
    this.logItems = [];
  }
  result() {
    return this.logItems.join('; ');
  }
  static ɵfac = function Log_Factory(__ngFactoryType__) {
    return new (__ngFactoryType__ || Log)();
  };
  static ɵprov = /*@__PURE__*/i0.ɵɵdefineInjectable({
    token: Log,
    factory: Log.ɵfac
  });
}
(() => {
  (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(Log, [{
    type: Injectable
  }], () => [], null);
})();

export { ComponentFixture, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, DeferBlockBehavior, DeferBlockFixture, DeferBlockState, InjectSetupWrapper, TestBed, TestComponentRenderer, discardPeriodicTasks, fakeAsync, flush, flushMicrotasks, getTestBed, inject, resetFakeAsyncZone, tick, waitForAsync, withModule, FakeNavigation as ɵFakeNavigation, Log as ɵLog, MetadataOverrider as ɵMetadataOverrider, getCleanupHook as ɵgetCleanupHook };
//# sourceMappingURL=testing.mjs.map