ngx-testing-tools
Version:
Makes Angular testing easier
1,310 lines (1,209 loc) • 47.9 kB
JavaScript
import * as i0 from '@angular/core';
import { isStandalone, Injector, InjectionToken, ElementRef, Injectable, Inject, ViewContainerRef, Component, ɵisInjectable as _isInjectable, inject, signal } from '@angular/core';
import * as i1 from '@angular/core/testing';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { provideNoopAnimations } from '@angular/platform-browser/animations';
import { provideHttpClient, HttpClient, HttpErrorResponse, HttpRequest, HttpResponse, HTTP_INTERCEPTORS, withInterceptorsFromDi, withInterceptors } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
import { Subscription, throwError, of, filter } from 'rxjs';
import { By } from '@angular/platform-browser';
import { provideLocationMocks } from '@angular/common/testing';
import { NavigationEnd, Router, provideRouter, ActivatedRouteSnapshot } from '@angular/router';
import { RouterTestingHarness } from '@angular/router/testing';
async function postAsync(value, postAction) {
await value;
postAction();
}
function buildJasmineDone(done, postAction) {
const doneWrapper = (() => {
done();
postAction();
});
doneWrapper.fail = (message) => {
try {
done.fail(message);
}
catch (err) {
postAction();
throw err;
}
};
return doneWrapper;
}
function buildJasmineCallback(args) {
const { callback, deferredTools, preTest, postTest } = args;
const callbackWrapper = (done) => {
const tools = deferredTools();
preTest?.(tools);
return (done)
? callback(tools, buildJasmineDone(done, () => postTest?.(tools)))
: postAsync(callback(tools, null), () => postTest?.(tools));
};
return (callback.length > 1)
? (done) => callbackWrapper(done)
: () => callbackWrapper(null);
}
function mergeBaseFactory(factory, tb) {
tb.import = (imports) => {
factory.import(imports);
return tb;
};
tb.provide = (providers) => {
factory.provide(providers);
return tb;
};
tb.inject = (name, token) => {
factory.inject(name, token);
return tb;
};
tb._compileEach = factory._compileEach.bind(factory);
tb._shouldCreate = factory._shouldCreate.bind(factory);
tb.compile = factory.compile.bind(factory);
tb.setup = factory.setup.bind(factory);
return tb;
}
function mergeRendererFactory(factory, tb) {
mergeBaseFactory(factory, tb);
tb.declare = (declarations) => {
factory.declare(declarations);
return tb;
};
return tb;
}
function getAnnotation(AnyCtor, metadataName) {
const annotations = AnyCtor['__annotations__'];
if (!annotations)
return null;
for (let i = annotations.length - 1; i >= 0; i--) {
const annotation = annotations[i];
if (isSameMetadataName(annotation, metadataName))
return annotation;
}
return null;
}
function isSameMetadataName(annotation, metadataName) {
return Object.getPrototypeOf(annotation)['ngMetadataName'] === metadataName;
}
function getComponentAnnotation(ComponentCtor) {
return getAnnotation(ComponentCtor, 'Component');
}
function throwCtorError(options) {
const { name, type, testBedName } = options;
throw new ReferenceError(`The provided "${name}" is not a ${type}. The ${testBedName} cannot be created.`);
}
function assertComponentCtor(ComponentCtor) {
const annotation = getComponentAnnotation(ComponentCtor);
if (!annotation)
throwCtorError({ name: ComponentCtor.name ?? ComponentCtor, type: 'Component', testBedName: 'ComponentTestBed' });
}
function throwInstanceError(options) {
const { name } = options;
throw new ReferenceError(`${name} instance is falsy. You need to set \`autoCompile = true\` (default) or set \`beforeEach(() => tb.compile());\` and \`autoCompile = false\` before running expectations.`);
}
function assertInstance(instance, Ctor) {
if (!(instance instanceof Ctor))
throwInstanceError({ name: Ctor.name });
}
function shouldCreate(factory) {
globalThis.it('should create', () => {
expect(factory()).toBeTruthy();
});
}
function makeArray(itemS) {
return (Array.isArray(itemS)) ? itemS : [itemS];
}
function appendSet(set, itemS) {
makeArray(itemS).forEach(v => set.add(v));
}
class BaseTestBedFactory {
constructor(described, options) {
this.described = described;
this.testBed = TestBed;
this._instance = null;
this.injectedMap = new Map();
const { imports = [], providers = [], autoCompile = true, checkCreate = true, } = options;
this.imports = new Set(imports);
this.providers = new Set(providers);
if (autoCompile)
this._compileEach();
if (checkCreate)
this._shouldCreate();
}
get instance() {
assertInstance(this._instance, this.described);
return this._instance;
}
injectDescribed() {
this._instance = this.testBed.inject(this.described);
}
import(oneOrManyImports) {
appendSet(this.imports, oneOrManyImports);
return this;
}
provide(oneOrManyProviders) {
appendSet(this.providers, oneOrManyProviders);
return this;
}
/**
* Injects an instance by token into the custom test bed.
*
* Retrieve it into the tools `injected` by autocompletion.
* @param name the key to access the instance.
* @param token the provider token.
*/
inject(name, token) {
this.injectedMap.set(name, token);
return this;
}
_compileEach() {
globalThis.beforeEach(() => this.compile());
}
/**
* Manually compiles the custom test bed to make enhanced tools available.
*
* To be used when `autoCompile = false`.
*
* **To be called inside jasmine `beforeEach` callback.**
*/
async compile() {
this.testBed.configureTestingModule({
imports: [...this.imports.values()],
providers: [...this.providers.values()],
});
}
/**
* Sets up extra stuffs using the enhanced tools.
*
* **Works only for `beforeEach` and `afterEach`**.
*/
setup(action) {
return buildJasmineCallback({
callback: action,
deferredTools: this.deferredTools,
});
}
_shouldCreate() {
shouldCreate(() => this.instance);
}
}
class RendererTestBedFactory extends BaseTestBedFactory {
constructor(described, host, options) {
super(described, options);
this.host = host;
this._fixture = null;
const { declarations = [], schemas = [], startDetectChanges = true, noopAnimations = true, noTemplate = false, } = options;
this.startDetectChanges = startDetectChanges;
this.noTemplate = noTemplate;
this.declarations = new Set(declarations);
this.schemas = new Set(schemas);
if (noopAnimations)
this.provide(provideNoopAnimations());
(isStandalone(this.host))
? this.import(this.host)
: this.declare(this.host);
(isStandalone(this.described))
? this.import(this.described)
: this.declare(this.described);
}
get fixture() {
assertInstance(this._fixture, ComponentFixture);
return this._fixture;
}
createHostFixture() {
this._fixture = this.testBed.createComponent(this.host);
}
declare(oneOrManyDeclarations) {
appendSet(this.declarations, oneOrManyDeclarations);
return this;
}
async compile() {
await super.compile();
this.testBed.configureTestingModule({
declarations: [...this.declarations.values()],
schemas: [...this.schemas.values()],
});
if (this.noTemplate)
this.testBed.overrideTemplateUsingTestingModule(this.host, '');
await this.testBed.compileComponents();
}
_shouldCreate() {
shouldCreate(() => this.fixture.componentInstance);
}
}
function httpProviders(...features) {
return [
provideHttpClient(...features),
provideHttpClientTesting(),
];
}
const HTTP_PROVIDERS = httpProviders();
function buildInjected(injector, injectedMap) {
const injected = {};
for (const [name, token] of injectedMap.entries()) {
injected[name] = injector.get(token);
}
return injected;
}
class RxBox {
constructor() {
this.subs = [];
this.subjects = [];
}
set remind(value) {
(value instanceof Subscription)
? this.subs.push(value)
: this.subjects.push(value);
}
bigRemind(values) {
values.forEach(v => (this.remind = v));
}
cleanAll() {
this.unsubscribe();
this.complete();
}
unsubscribe() {
this.clean(this.subs, 'unsubscribe');
}
complete() {
this.clean(this.subjects, 'complete');
}
clean(array, cleanFnKey) {
array.forEach(v => v[cleanFnKey]());
array.length = 0;
}
}
function buildBaseTools(factory, options) {
const injector = options?.thisInjector ?? factory['testBed'].inject(Injector);
const injected = buildInjected(injector, factory['injectedMap']);
const rx = new RxBox();
return { injected, injector, rx };
}
function emitFakeErrorResponse(httpController, config) {
const { url, method, status, statusText = 'Error' } = config;
const error = new ProgressEvent(statusText);
httpController
.expectOne({ url, method })
.error(error, { status, statusText });
}
function emitFakeSuccessResponse(httpController, config) {
const { url, method, body, status, headers, statusText } = config;
httpController
.expectOne({ url, method })
.flush(body, { status, headers, statusText });
}
function buildHttpTools(injector, options) {
if (!options.httpTesting)
return unusableHttpTools();
const client = injector.get(HttpClient);
const controller = injector.get(HttpTestingController);
return {
client,
controller,
emitSuccessResponse: (config) => emitFakeSuccessResponse(controller, config),
emitErrorResponse: (config) => emitFakeErrorResponse(controller, config),
};
}
function unusableHttpTools() {
return {
get client() { return throwDisabledHttpTesting('client'); },
get controller() { return throwDisabledHttpTesting('controller'); },
emitSuccessResponse: () => throwDisabledHttpTesting('emitSuccessResponse'),
emitErrorResponse: () => throwDisabledHttpTesting('emitErrorResponse'),
};
}
function throwDisabledHttpTesting(key) {
throw new ReferenceError(`Cannot use \`http.${key}\` because HttpTools is not initialized. You need to set \`httpTesting:true\` into the test bed options.`);
}
function throwCannotFind(selectorOrDirective, quantifier = 'one') {
const input = (typeof selectorOrDirective === 'string')
? `selector "${selectorOrDirective}"`
: `directive "${selectorOrDirective.name}"`;
throw new Error(`Cannot find ${quantifier} DebugElement with : ${input}.`);
}
function findDebugElement(fixture, selectorOrDirective) {
const element = (typeof selectorOrDirective === 'string')
? fixture.debugElement.query(By.css(selectorOrDirective))
: fixture.debugElement.query(By.directive(selectorOrDirective));
if (!element)
throwCannotFind(selectorOrDirective);
return element;
}
function emitEvent(fixture, selectorOrDirective, name, value) {
findDebugElement(fixture, selectorOrDirective)
.triggerEventHandler(name, value);
}
function emitOutput(fixture, selectorOrDirective, name, $event) {
emitEvent(fixture, selectorOrDirective, name, $event);
}
function click(fixture, selectorOrDirective) {
emitOutput(fixture, selectorOrDirective, 'click');
}
function buildActionTools(fixture) {
return {
click: (input) => click(fixture, input),
emitOutput: (directive, name, val) => emitOutput(fixture, directive, name, val),
// setInput: (name: string, $value: any) => {}
};
}
function findAllDebugElements(fixture, selectorOrDirective) {
const debugs = (typeof selectorOrDirective === 'string')
? fixture.debugElement.queryAll(By.css(selectorOrDirective))
: fixture.debugElement.queryAll(By.directive(selectorOrDirective));
if (debugs.length === 0)
throwCannotFind(selectorOrDirective, 'many');
return debugs;
}
function findAllComponents(fixture, selectorOrDirective) {
return findAllDebugElements(fixture, selectorOrDirective).map(e => e.componentInstance);
}
function findAllElements(fixture, selectorOrDirective) {
return findAllDebugElements(fixture, selectorOrDirective).map(e => e.nativeElement);
}
function findComponent(fixture, selectorOrDirective) {
return findDebugElement(fixture, selectorOrDirective).componentInstance;
}
function findElement(fixture, selectorOrDirective) {
return findDebugElement(fixture, selectorOrDirective).nativeElement;
}
function buildQueryTools(fixture) {
return {
findComponent: (input) => findComponent(fixture, input),
findAllComponents: (input) => findAllComponents(fixture, input),
findElement: (input) => findElement(fixture, input),
findAllElements: (input) => findAllElements(fixture, input),
findDebugElement: (input) => findDebugElement(fixture, input),
findAllDebugElements: (input) => findAllDebugElements(fixture, input),
};
}
function buildRendererTools(factory) {
const fixture = factory['fixture'];
const host = fixture.componentInstance;
const element = fixture.debugElement.children.at(0)?.nativeElement;
const query = buildQueryTools(fixture);
const action = buildActionTools(fixture);
return { action, element, fixture, host, query };
}
function buildComponentTools(factory, httpOptions) {
const { action, element, fixture, host: component, query } = buildRendererTools(factory);
const { injected, injector, rx } = buildBaseTools(factory, { thisInjector: fixture.debugElement.injector });
const http = buildHttpTools(injector, httpOptions);
return { action, component, element, fixture, http, injected, injector, query, rx };
}
class ComponentTestBedFactory extends RendererTestBedFactory {
constructor(rootComponent, options) {
assertComponentCtor(rootComponent);
super(rootComponent, rootComponent, options);
this.deferredTools = () => buildComponentTools(this, this.httpOptions);
const { httpTesting = false, verifyHttp = true, } = options;
if (httpTesting)
this.provide(HTTP_PROVIDERS);
this.httpOptions = { httpTesting, verifyHttp };
}
async compile() {
await super.compile();
this.createHostFixture();
}
}
/**
* Creates a new `ComponentTestBed` to configure the custom test bed and wrap the assertion test.
* @param rootComponent - The described Component.
* @param options
*/
function componentTestBed(rootComponent, options = {}) {
const factory = new ComponentTestBedFactory(rootComponent, options);
const { httpTesting, verifyHttp: defaultVerifyHttp, } = factory['httpOptions'];
const defaultStartDetectChanges = factory['startDetectChanges'];
const noTemplate = factory['noTemplate'];
const tb = ((assertion, opts = {}) => {
const { startDetectChanges = defaultStartDetectChanges, verifyHttp = defaultVerifyHttp, } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
preTest: (tools) => {
if (!noTemplate && startDetectChanges)
tools.fixture.detectChanges();
},
postTest: (tools) => {
if (httpTesting && verifyHttp)
tools.http.controller.verify();
tools.rx['cleanAll']();
},
});
});
return mergeRendererFactory(factory, tb);
}
/**
* Only invokes the "should create" test.
*
* To be used when there are no noticeable or relevant tests to be performed on this component.
*
* The usage of this function and `componentTestBed` function must be mutually exclusive.
*
* @param rootComponent - The described Component.
* @param options
*/
function itShouldCreateComponent(rootComponent, options) {
componentTestBed(rootComponent, options);
}
function getDirectiveAnnotation(DirectiveCtor) {
return getAnnotation(DirectiveCtor, 'Directive');
}
function assertDirectiveCtor(DirectiveCtor) {
const annotation = getDirectiveAnnotation(DirectiveCtor);
if (!annotation)
throwCtorError({ name: DirectiveCtor.name ?? DirectiveCtor, type: 'Directive', testBedName: 'DirectiveTestBed' });
}
const HOST_FIXTURE = new InjectionToken('HOST_FIXTURE');
class HostElementRef extends ElementRef {
constructor(fixture) {
// Use `innerHTML` to remove the fixture element wrapper (<div id="rootXX" ng-version="X.X.X">..</div>)
super(fixture.nativeElement.innerHTML);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: HostElementRef, deps: [{ token: HOST_FIXTURE }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: HostElementRef }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: HostElementRef, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1.ComponentFixture, decorators: [{
type: Inject,
args: [HOST_FIXTURE]
}] }] });
function buildDirectiveTools(factory) {
const directive = factory['instance'];
const { action, element, fixture, host, query } = buildRendererTools(factory);
const { injected, injector, rx } = buildBaseTools(factory, { thisInjector: fixture.debugElement.injector });
return { action, directive, element, fixture, host, injected, injector, query, rx };
}
class DirectiveTestBedFactory extends RendererTestBedFactory {
constructor(rootDirective, hostComponent, options) {
assertDirectiveCtor(rootDirective);
super(rootDirective, hostComponent, options);
this.deferredTools = () => buildDirectiveTools(this);
this.provide([
rootDirective,
{ provide: HOST_FIXTURE, useFactory: () => this.fixture },
{ provide: ElementRef, useClass: HostElementRef },
ViewContainerRef,
]);
}
async compile() {
await super.compile();
this.createHostFixture();
this.injectDescribed();
}
}
/**
* Creates a new `DirectiveTestBed` to configure the custom test bed and wrap the assertion test.
* @param rootDirective - The described Directive.
* @param hostComponent - The host component on which the directive is tested.
* @param options
*/
function directiveTestBed(rootDirective, hostComponent, options = {}) {
const factory = new DirectiveTestBedFactory(rootDirective, hostComponent, options);
const defaultStartDetectChanges = factory['startDetectChanges'];
const noTemplate = factory['noTemplate'];
const tb = ((assertion, opts = {}) => {
const { startDetectChanges = defaultStartDetectChanges } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
preTest: (tools) => {
if (!noTemplate && startDetectChanges)
tools.fixture.detectChanges();
},
postTest: (tools) => {
tools.rx['cleanAll']();
},
});
});
return mergeRendererFactory(factory, tb);
}
/**
* Only invokes the "should create" test.
*
* To be used when there are no noticeable or relevant tests to be performed on this directive.
*
* The usage of this function and `directiveTestBed` function must be mutually exclusive.
*
* @param rootDirective - The described Directive.
* @param options
*/
function itShouldCreateDirective(rootDirective, options) {
class HostComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: HostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.1", type: HostComponent, isStandalone: true, selector: "host-component:not(div)", ngImport: i0, template: '', isInline: true }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: HostComponent, decorators: [{
type: Component,
args: [{ template: '', standalone: true, selector: 'host-component:not(div)' }]
}] });
directiveTestBed(rootDirective, HostComponent, options);
}
function getInjectableAnnotation(InjectableCtor) {
return getAnnotation(InjectableCtor, 'Injectable');
}
function assertServiceCtor(ServiceCtor) {
const annotation = getInjectableAnnotation(ServiceCtor);
if (!annotation)
throwCtorError({ name: ServiceCtor.name ?? ServiceCtor, type: 'Injectable Service', testBedName: 'ServiceTestBed' });
}
function buildServiceTools(factory, httpOptions) {
const service = factory['instance'];
const { injected, injector, rx } = buildBaseTools(factory);
const http = buildHttpTools(injector, httpOptions);
return { http, injected, injector, rx, service };
}
class ServiceTestBedFactory extends BaseTestBedFactory {
constructor(rootService, options) {
assertServiceCtor(rootService);
super(rootService, options);
this.deferredTools = () => buildServiceTools(this, this.httpOptions);
const { httpTesting = false, verifyHttp = true, } = options;
this.httpOptions = { httpTesting, verifyHttp };
if (httpTesting)
this.provide(HTTP_PROVIDERS);
this.provide(this.described);
}
async compile() {
await super.compile();
this.injectDescribed();
}
}
/**
* Creates a new `ServiceTestBed` to configure the custom test bed and wrap the assertion test.
* @param rootService - The described Service.
* @param options
*/
function serviceTestBed(rootService, options = {}) {
const factory = new ServiceTestBedFactory(rootService, options);
const { httpTesting, verifyHttp: defaultVerifyHttp, } = factory['httpOptions'];
const tb = ((assertion, opts = {}) => {
const { verifyHttp = defaultVerifyHttp } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
postTest: (tools) => {
if (httpTesting && verifyHttp)
tools.http.controller.verify();
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
/**
* Only invokes the "should create" test.
*
* To be used when there are no noticeable or relevant tests to be performed on this service.
*
* The usage of this function and `serviceTestBed` function must be mutually exclusive.
*
* @param rootService - The described Service.
* @param options
*/
function itShouldCreateService(rootService, options) {
serviceTestBed(rootService, options);
}
function getNgModuleAnnotation(NgModuleCtor) {
return getAnnotation(NgModuleCtor, 'NgModule');
}
function assertModuleCtor(ModuleCtor) {
const annotation = getNgModuleAnnotation(ModuleCtor);
if (!annotation)
throwCtorError({ name: ModuleCtor.name ?? ModuleCtor, type: 'Module', testBedName: 'ModuleTestBed' });
}
function buildModuleTools(factory) {
const module = factory['instance'];
const { injected, injector, rx } = buildBaseTools(factory);
return { injected, injector, module, rx };
}
class ModuleTestBedFactory extends BaseTestBedFactory {
constructor(rootModule, options) {
assertModuleCtor(rootModule);
super(rootModule, options);
this.deferredTools = () => buildModuleTools(this);
this.import(rootModule);
}
async compile() {
await super.compile();
this.injectDescribed();
}
}
/**
* Creates a new `ModuleTestBed` to configure the custom test bed and wrap the assertion test.
* @param rootModule - The described Module.
* @param options
*/
function moduleTestBed(rootModule, options = {}) {
const factory = new ModuleTestBedFactory(rootModule, options);
const tb = ((assertion) => {
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
postTest: (tools) => {
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
/**
* Only invokes the "should create" test.
*
* To be used when there are no noticeable or relevant tests to be performed on this module.
*
* The usage of this function and `moduleTestBed` function must be mutually exclusive.
*
* @param rootModule - The described Module.
* @param options
*/
function itShouldCreateModule(rootModule, options) {
moduleTestBed(rootModule, options);
}
function getPipeAnnotation(PipeCtor) {
return getAnnotation(PipeCtor, 'Pipe');
}
function assertPipeCtor(PipeCtor) {
const annotation = getPipeAnnotation(PipeCtor);
if (!annotation)
throwCtorError({ name: PipeCtor.name ?? PipeCtor, type: 'Pipe', testBedName: 'PipeTestBed' });
}
function buildVerifyTools(pipe) {
const verify = ({ expected, data, parameters }) => {
expect(pipe.transform(data, ...parameters)).toEqual(expected);
};
verify.many = (matchers) => {
matchers.forEach(({ expected, data, parameters }) => {
verify({ expected, data, parameters });
});
};
return verify;
}
function buildPipeTools(factory) {
const pipe = factory['instance'];
const { injected, injector, rx } = buildBaseTools(factory);
const verify = buildVerifyTools(pipe);
return { injected, injector, pipe, rx, verify };
}
class PipeTestBedFactory extends BaseTestBedFactory {
constructor(rootPipe, options) {
assertPipeCtor(rootPipe);
super(rootPipe, options);
this.deferredTools = () => buildPipeTools(this);
this.provide(rootPipe);
}
async compile() {
await super.compile();
this.injectDescribed();
}
}
/**
* Creates a new `PipeTestBed` to configure the custom test bed and wrap the assertion test.
* @param rootPipe - The described Pipe.
* @param options
*/
function pipeTestBed(rootPipe, options = {}) {
const factory = new PipeTestBedFactory(rootPipe, options);
const tb = ((assertion) => {
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
postTest: (tools) => {
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
/**
* Only invokes the "should create" test.
*
* To be used when there are no noticeable or relevant tests to be performed on this pipe.
*
* The usage of this function and `pipeTestBed` function must be mutually exclusive.
*
* @param rootPipe - The described Pipe.
* @param options
*/
function itShouldCreatePipe(rootPipe, options) {
pipeTestBed(rootPipe, options);
}
function assertFn(fn) {
if (typeof fn !== 'function')
throw new ReferenceError(`The provided "${fn?.toString()}" is not a valid function.`);
}
function assertInjectableCtor(InjectableCtor, testBedName) {
const annotation = getInjectableAnnotation(InjectableCtor);
if (!annotation)
throwCtorError({ name: InjectableCtor.name ?? InjectableCtor, type: 'Injectable class', testBedName });
}
function assertInterceptorCtor(InterceptorCtor) {
assertInjectableCtor(InterceptorCtor, 'InterceptorTestBed');
}
function isConstructor(target) {
return _isInjectable(target);
}
const INTERCEPTOR_INFO = new InjectionToken('ROOT_INTERCEPTOR_INFO');
class InterceptorProxy {
constructor() {
this.info = inject(INTERCEPTOR_INFO);
this.isRootCtor = this.info.isRootCtor;
this.instance = (this.isRootCtor)
? inject(this.info.rootInterceptor)
: this.info.rootInterceptor;
}
intercept(req, next) {
return (this.isRootCtor)
? this.instance.intercept(req, next)
: TestBed.runInInjectionContext(() => this.instance(req, next.handle));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: InterceptorProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: InterceptorProxy }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: InterceptorProxy, decorators: [{
type: Injectable
}] });
function mockErrorResponseHandlerFactory(res) {
return { handle: () => throwError(() => res) };
}
function inspectErrorResponse(interceptor, resOrUrl, error) {
const res = (resOrUrl instanceof HttpErrorResponse)
? resOrUrl
: new HttpErrorResponse({ url: resOrUrl, error, status: 500 });
const req = new HttpRequest('GET', (typeof resOrUrl === 'string') ? resOrUrl : '/test');
return interceptor.intercept(req, mockErrorResponseHandlerFactory(res));
}
function mockRequestHandlerFactory() {
return { handle: (req) => of(req) };
}
function inspectRequest(interceptor, reqOrUrl, url, body = null) {
const req = (reqOrUrl instanceof HttpRequest)
? reqOrUrl
: new HttpRequest(reqOrUrl, url, body);
return interceptor.intercept(req, mockRequestHandlerFactory());
}
function mockSuccessResponseHandlerFactory(res) {
return { handle: () => of(res) };
}
function inspectSuccessResponse(interceptor, resOrUrl, body = null) {
const res = (resOrUrl instanceof HttpResponse)
? resOrUrl
: new HttpResponse({ url: resOrUrl, body, status: 200 });
const req = new HttpRequest('GET', (typeof resOrUrl === 'string') ? resOrUrl : '/test');
return interceptor.intercept(req, mockSuccessResponseHandlerFactory(res));
}
function buildInspectTools(interceptor) {
return {
request: (reqOrMethod, url, body) => inspectRequest(interceptor, reqOrMethod, url, body),
successResponse: (resOrUrl, body) => inspectSuccessResponse(interceptor, resOrUrl, body),
errorResponse: (resOrUrl, error) => inspectErrorResponse(interceptor, resOrUrl, error),
};
}
function buildInterceptorTools(factory) {
const interceptorProxy = factory['instance'];
const interceptor = interceptorProxy.instance;
const { injected, injector, rx } = buildBaseTools(factory);
const http = buildHttpTools(injector, { httpTesting: true });
const inspect = buildInspectTools(interceptorProxy);
return { http, injected, injector, inspect, interceptor, rx };
}
class InterceptorTestBedFactory extends BaseTestBedFactory {
constructor(rootInterceptor, options) {
const isRootCtor = isConstructor(rootInterceptor);
if (isRootCtor)
assertInterceptorCtor(rootInterceptor);
else
assertFn(rootInterceptor);
super(InterceptorProxy, options);
this.deferredTools = () => buildInterceptorTools(this);
this.provide([
InterceptorProxy,
{ provide: INTERCEPTOR_INFO, useValue: { rootInterceptor, isRootCtor } },
]);
if (isRootCtor) {
this.provide([
rootInterceptor,
{ provide: HTTP_INTERCEPTORS, useClass: rootInterceptor, multi: true },
httpProviders(withInterceptorsFromDi()),
]);
}
else {
this.provide(httpProviders(withInterceptors([rootInterceptor])));
}
}
async compile() {
await super.compile();
this.injectDescribed();
}
}
function interceptorTestBed(rootInterceptor, options = {}) {
const { verifyHttp: globalVerifyHttp, } = options;
const factory = new InterceptorTestBedFactory(rootInterceptor, options);
const tb = ((assertion, opts = {}) => {
const { verifyHttp = globalVerifyHttp ?? true } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
postTest: (tools) => {
if (verifyHttp)
tools.http.controller.verify();
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
function itShouldCreateInterceptor(rootInterceptor, options) {
interceptorTestBed(rootInterceptor, options);
}
function buildRouterTools(factory) {
const router = factory['instance'];
const routes = factory['_routes'];
const harness = factory['_harness'];
const { injected, injector, rx } = buildBaseTools(factory);
const urlSignal = signal(router.url);
rx.remind = router.events
.pipe(filter((event) => (event instanceof NavigationEnd)))
.subscribe({
next: (event) => urlSignal.set(event.url),
});
const $url = urlSignal.asReadonly();
const navigateByUrl = harness.navigateByUrl.bind(harness);
return { $url, harness, injected, injector, navigateByUrl, routes, rx };
}
class RouterTestBedFactory extends BaseTestBedFactory {
constructor(routes, options) {
super(Router, options);
this._harness = null;
this.deferredTools = () => buildRouterTools(this);
const { initialUrl = '', startDetectChanges = true, } = options;
this._routes = routes;
this.initialUrl = initialUrl;
this.startDetectChanges = startDetectChanges;
this.provide([
provideRouter(routes),
provideLocationMocks(),
]);
this.createHarnessBeforeEachTest();
}
async compile() {
await super.compile();
this.injectDescribed();
}
createHarnessBeforeEachTest() {
globalThis.beforeEach(async () => {
this._harness = await RouterTestingHarness.create(this.initialUrl);
});
}
}
/**
* Creates a new `RouterTestBed` to configure the custom test bed and wrap the assertion test.
* @param routes - the routes to be tested
* @param options - check `RouterTestBedOptions`
*/
function routerTestBed(routes, options = {}) {
const factory = new RouterTestBedFactory(routes, options);
const defaultStartDetectChanges = factory['startDetectChanges'];
const tb = ((assertion, opts = {}) => {
const { startDetectChanges = defaultStartDetectChanges, } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
preTest: (tools) => {
if (startDetectChanges)
tools.harness.detectChanges();
},
postTest: (tools) => {
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
function assertGuardCto(GuardCtor) {
assertInjectableCtor(GuardCtor, 'GuardTestBed');
}
const GUARD_INFO = new InjectionToken('ROOT_GUARD_INFO');
class GuardProxy {
constructor() {
this.info = inject(GUARD_INFO);
this.isRootCtor = this.info.isRootCtor;
this.instance = (this.isRootCtor)
? inject(this.info.rootGuard)
: this.info.rootGuard;
}
canActivate(route, state) {
return (this.isRootCtor)
? this.instance.canActivate(route, state)
: TestBed.runInInjectionContext(() => this.instance(route, state));
}
canActivateChild(childRoute, state) {
return (this.isRootCtor)
? this.instance.canActivateChild(childRoute, state)
: TestBed.runInInjectionContext(() => this.instance(childRoute, state));
}
canDeactivate(component, currentRoute, currentState, nextState) {
return (this.isRootCtor)
? this.instance.canDeactivate(component, currentRoute, currentState, nextState)
: TestBed.runInInjectionContext(() => this.instance(component, currentRoute, currentState, nextState));
}
canLoad(route, segments) {
return (this.isRootCtor)
? this.instance.canLoad(route, segments)
: TestBed.runInInjectionContext(() => this.instance(route, segments));
}
canMatch(route, segments) {
return (this.isRootCtor)
? this.instance.canMatch(route, segments)
: TestBed.runInInjectionContext(() => this.instance(route, segments));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: GuardProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: GuardProxy }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: GuardProxy, decorators: [{
type: Injectable
}] });
function getRouterState(injector) {
return injector.get(Router).routerState.snapshot;
}
function buildRouteSnapshot(config = {}) {
const { data = {}, params = {}, queryParams = {}, } = config;
const route = new ActivatedRouteSnapshot();
route.data = data;
route.params = params;
route.queryParams = queryParams;
return route;
}
function isCanActivateGuard(guard, type) {
return (type === 'CanActivate')
|| (type === 'ctor' && !!guard.canActivate);
}
function isCanActivateChildGuard(guard, type) {
return (type === 'CanActivateChild')
|| (type === 'ctor' && !!guard.canActivateChild);
}
function isCanDeactivateGuard(guard, type) {
return (type === 'CanDeactivate')
|| (type === 'ctor' && !!guard.canDeactivate);
}
function isCanLoadGuard(guard, type) {
return (type === 'CanLoad')
|| (type === 'ctor' && !!guard.canLoad);
}
function isCanMatchGuard(guard, type) {
return (type === 'CanMatch')
|| (type === 'ctor' && !!guard.canMatch);
}
function buildChallengeTools(guardProxy, injector, guardType) {
const guard = guardProxy.instance;
switch (true) {
case isCanActivateGuard(guard, guardType):
return buildChallengeToolsForActivate(guardProxy, injector, 'canActivate');
case isCanActivateChildGuard(guard, guardType):
return buildChallengeToolsForActivate(guardProxy, injector, 'canActivateChild');
case isCanDeactivateGuard(guard, guardType):
return buildChallengeToolsForDeactivate(guardProxy, injector);
case isCanLoadGuard(guard, guardType):
return buildChallengeToolsForLoad(guardProxy, 'canLoad');
case isCanMatchGuard(guard, guardType):
return buildChallengeToolsForLoad(guardProxy, 'canMatch');
default:
throw new Error(`Unknown guard type (${guardType}) or invalid passed guard (${guard.name ?? guard}).`);
}
}
function buildChallengeToolsForActivate(guardProxy, injector, key) {
const challenge = () => {
const route = buildRouteSnapshot();
const state = getRouterState(injector);
return guardProxy[key](route, state);
};
challenge.withInfo = (info) => {
const { currentState: state = getRouterState(injector), data, params, queryParams, } = info;
const route = buildRouteSnapshot({ data, params, queryParams });
return guardProxy[key](route, state);
};
return challenge;
}
function buildChallengeToolsForDeactivate(guardProxy, injector) {
const challenge = () => {
const currentRoute = buildRouteSnapshot();
const state = getRouterState(injector);
return guardProxy.canDeactivate({}, currentRoute, state, state);
};
challenge.withInfo = (info) => {
const { currentState = getRouterState(injector), nextState = currentState, component: ComponentCtor, data, params, queryParams, } = info;
const component = (isConstructor(ComponentCtor)) ? TestBed.runInInjectionContext(() => inject(ComponentCtor)) : ComponentCtor;
const currentRoute = buildRouteSnapshot({ data, params, queryParams });
return guardProxy.canDeactivate(component ?? {}, currentRoute, currentState, nextState);
};
return challenge;
}
function buildChallengeToolsForLoad(guardProxy, key) {
const challenge = () => {
return guardProxy[key]({ data: {} }, []);
};
challenge.withInfo = (info) => {
const { route = {}, data = {}, segments = [], } = info;
route.data ??= data;
return guardProxy[key](route, segments);
};
return challenge;
}
function buildGuardTools(factory, httpOptions) {
const guardProxy = factory['instance'];
const guardType = factory['type'];
const guard = guardProxy.instance;
const { injected, injector, rx } = buildBaseTools(factory);
const http = buildHttpTools(injector, httpOptions);
const challenge = buildChallengeTools(guardProxy, injector, guardType);
return { challenge, guard, http, injected, injector, rx };
}
class GuardTestBedFactory extends BaseTestBedFactory {
constructor(rootGuard, options) {
const isRootCtor = isConstructor(rootGuard);
if (isRootCtor)
assertGuardCto(rootGuard);
else
assertFn(rootGuard);
super(GuardProxy, options);
this.deferredTools = () => buildGuardTools(this, this.httpOptions);
const { httpTesting = false, verifyHttp = true, type = 'ctor', } = options;
this.type = type;
this.provide([
GuardProxy,
{ provide: GUARD_INFO, useValue: { rootGuard, isRootCtor } },
]);
if (isRootCtor)
this.provide(rootGuard);
if (httpTesting)
this.provide(HTTP_PROVIDERS);
this.httpOptions = { httpTesting, verifyHttp };
}
async compile() {
await super.compile();
this.injectDescribed();
}
}
function guardTestBed(rootGuard, options = {}) {
const factory = new GuardTestBedFactory(rootGuard, options);
const { httpTesting, verifyHttp: defaultVerifyHttp, } = factory['httpOptions'];
const tb = ((assertion, opts = {}) => {
const { verifyHttp = defaultVerifyHttp, } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
postTest: (tools) => {
if (httpTesting && verifyHttp)
tools.http.controller.verify();
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
function itShouldCreateGuard(rootGuard, options) {
guardTestBed(rootGuard, options);
}
function assertResolverCtor(ResolverCtor) {
assertInjectableCtor(ResolverCtor, 'ResolverTestBed');
}
const RESOLVER_INFO = new InjectionToken('ROOT_RESOLVER_INFO');
class ResolverProxy {
constructor() {
this.info = inject(RESOLVER_INFO);
this.isRootCtor = this.info.isRootCtor;
this.instance = (this.isRootCtor)
? inject(this.info.rootResolver)
: this.info.rootResolver;
}
resolve(route, state) {
return (this.isRootCtor)
? this.instance.resolve(route, state)
: TestBed.runInInjectionContext(() => this.instance(route, state));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ResolverProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ResolverProxy }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: ResolverProxy, decorators: [{
type: Injectable
}] });
function buildTriggerTools(resolver, injector) {
const tools = () => {
const route = buildRouteSnapshot();
const state = getRouterState(injector);
return resolver.resolve(route, state);
};
tools.withInfo = (config) => {
const { data, params, queryParams } = config;
const route = buildRouteSnapshot({ data, params, queryParams });
const state = getRouterState(injector);
return resolver.resolve(route, state);
};
return tools;
}
function buildResolverTools(factory, httpOptions) {
const resolverProxy = factory['instance'];
const resolver = resolverProxy.instance;
const { injected, injector, rx } = buildBaseTools(factory);
const http = buildHttpTools(injector, httpOptions);
const trigger = buildTriggerTools(resolverProxy, injector);
return { resolver, trigger, http, injected, injector, rx };
}
class ResolverTestBedFactory extends BaseTestBedFactory {
constructor(resolver, options) {
const isRootCtor = isConstructor(resolver);
if (isRootCtor)
assertResolverCtor(resolver);
else
assertFn(resolver);
super(ResolverProxy, options);
this.deferredTools = () => buildResolverTools(this, this.httpOptions);
const { httpTesting = false, verifyHttp = true, } = options;
this.provide([
ResolverProxy,
{ provide: RESOLVER_INFO, useValue: { rootResolver: resolver, isRootCtor } },
]);
if (isRootCtor)
this.provide(resolver);
if (httpTesting)
this.provide(HTTP_PROVIDERS);
this.httpOptions = { httpTesting, verifyHttp };
}
async compile() {
await super.compile();
this.injectDescribed();
}
}
/**
* Creates a new `ResolverTestBed` to configure the custom test bed and wrap the assertion test.
* @param resolver - the described resolver
* @param options - check `ResolverTestBedOptions`
*/
function resolverTestBed(resolver, options = {}) {
const factory = new ResolverTestBedFactory(resolver, options);
const { httpTesting, verifyHttp: defaultVerifyHttp, } = factory['httpOptions'];
const tb = ((assertion, opts = {}) => {
const { verifyHttp = defaultVerifyHttp, } = opts;
return buildJasmineCallback({
callback: assertion,
deferredTools: factory['deferredTools'],
postTest: (tools) => {
if (httpTesting && verifyHttp)
tools.http.controller.verify();
tools.rx['cleanAll']();
},
});
});
return mergeBaseFactory(factory, tb);
}
function itShouldCreateResolver(resolver, options) {
resolverTestBed(resolver, options);
}
/*
* Public API Surface of ngx-testing-tools
*/
/**
* Generated bundle index. Do not edit.
*/
export { componentTestBed, directiveTestBed, guardTestBed, interceptorTestBed, itShouldCreateComponent, itShouldCreateDirective, itShouldCreateGuard, itShouldCreateInterceptor, itShouldCreateModule, itShouldCreatePipe, itShouldCreateResolver, itShouldCreateService, moduleTestBed, pipeTestBed, resolverTestBed, routerTestBed, serviceTestBed };
//# sourceMappingURL=ngx-testing-tools.mjs.map