@orchestrator/ngx-testing
Version:
Testing utilities for Angular projects
521 lines (505 loc) • 17.9 kB
JavaScript
import { __decorate, __metadata, __awaiter } from 'tslib';
import { CommonModule } from '@angular/common';
import { InjectionToken, NgModule, ViewChild, Component, ComponentFactoryResolver, Injector, ɵɵdefineInjectable, ɵɵinject, INJECTOR, Injectable, Compiler, CompilerFactory } from '@angular/core';
import { TestBed, getTestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
var TestTypeKind;
(function (TestTypeKind) {
TestTypeKind[TestTypeKind["Component"] = 0] = "Component";
TestTypeKind[TestTypeKind["Directive"] = 1] = "Directive";
})(TestTypeKind || (TestTypeKind = {}));
let outputMockFactory = () => (() => null);
const ɵ0 = outputMockFactory;
function setOutputMock(mock) {
outputMockFactory = mock;
}
function getOutputMock() {
return outputMockFactory;
}
/**
* @internal
*/
const TestModuleToken = new InjectionToken('TestModuleToken');
/**
* @internal
*/
const TestTypeToken = new InjectionToken('TestTypeToken');
/**
* @internal
*/
const TestTypeKindToken = new InjectionToken('TestTypeKindToken');
/**
* @internal
*/
const ExtraConfigToken = new InjectionToken('ExtraConfigToken');
/**
* @internal
*/
function getDirectiveIO(dirType) {
const propMeta = dirType['__prop__metadata__'];
const inputsMeta = filterPropMeta(propMeta, 'Input');
const outputsMeta = filterPropMeta(propMeta, 'Output');
return {
inputs: propMetaToTemplateBindings(inputsMeta),
outputs: propMetaToTemplateBindings(outputsMeta),
};
}
function propMetaToTemplateBindings(meta) {
return Object.keys(meta).map(propName => ({
propName,
templateName: meta[propName].bindingPropertyName || propName,
}));
}
function filterPropMeta(obj, type) {
return Object.keys(obj).reduce((acc, k) => {
const meta = obj[k].find(m => m.ngMetadataName === type);
return meta ? Object.assign(Object.assign({}, acc), { [k]: meta }) : acc;
}, {});
}
/**
* @internal
*/
function get(type) {
return TestBed.get(type);
}
/**
* @internal
*/
function mergeArrays(...arrays) {
return arrays.reduce((acc, arr) => [...acc, ...toArray(arr)], []);
}
/**
* @internal
*/
function toArray(arr) {
return arr ? arr : [];
}
let NgxTestingModule = class NgxTestingModule {
static forComponent(compType, extras = {}) {
const testModule = getTestingModuleFor(compType, compType, extras.ngModule);
return {
ngModule: testModule,
providers: [
{ provide: TestModuleToken, useValue: testModule },
{ provide: TestTypeToken, useValue: compType },
{ provide: TestTypeKindToken, useValue: TestTypeKind.Component },
{ provide: ExtraConfigToken, useValue: extras },
],
};
}
static forDirective(dirType, extras = {}) {
const testModule = getTestingModuleFor(dirType, extras.hostComponent, extras.ngModule);
return {
ngModule: testModule,
providers: [
{ provide: TestModuleToken, useValue: testModule },
{ provide: TestTypeToken, useValue: dirType },
{ provide: TestTypeKindToken, useValue: TestTypeKind.Directive },
{ provide: ExtraConfigToken, useValue: extras },
],
};
}
};
NgxTestingModule = __decorate([
NgModule({
imports: [CommonModule],
exports: [CommonModule],
})
], NgxTestingModule);
function getTestingModuleFor(type, entryType, extra = {}) {
let TestingModule = class TestingModule {
};
TestingModule = __decorate([
NgModule({
imports: mergeArrays(extra.imports, [NgxTestingModule]),
exports: mergeArrays(extra.exports, [NgxTestingModule, type]),
declarations: mergeArrays(extra.declarations, [type]),
entryComponents: entryType
? mergeArrays(extra.entryComponents, [entryType])
: extra.entryComponents,
schemas: extra.schemas,
})
], TestingModule);
return TestingModule;
}
/**
* @internal
*/
function genHostCompTpl(tag, io, content = '', bindings) {
const inputsTpl = genInputsTpl(io.inputs);
const outputsTpl = genOutputsTpl(io.outputs);
const bindingsTpl = bindings
? typeof bindings === 'string'
? `let-${bindings}`
: Object.keys(bindings)
.map(key => `let-${key}${bindings[key] ? `="${bindings[key]}"` : ''}`)
.join(' ')
: '';
return `<${tag} ${inputsTpl} ${outputsTpl} ${bindingsTpl}>${content}</${tag}>`;
}
/**
* @internal
*/
function genHostCompTplStar(binding, tag, io, content = '', bindings) {
const inputsTpl = genInputsTplStar(binding, io.inputs, bindings);
return `<${tag} ${inputsTpl}>${content}</${tag}>`;
}
/**
* @internal
*/
function genInputsTplStar(binding, inputs, bindings) {
const inputsTpl = inputs
.filter(({ templateName }) => templateName !== binding)
.map(({ templateName, propName }) => `${templateName.replace(binding, '')}: ${propName}`)
.join('; ');
const bindingsTpl = bindings
? typeof bindings === 'string'
? `let ${bindings}`
: Object.keys(bindings)
.map(key => `let ${key}${bindings[key] ? `: ${bindings[key]}` : ''}`)
.join(', ')
: '';
return `*${binding}="${binding}; ${inputsTpl}; ${bindingsTpl}"`;
}
/**
* @internal
*/
function genInputsTpl(inputs) {
return inputs
.map(({ templateName, propName }) => `[${templateName}]="${propName}"`)
.join(' ');
}
/**
* @internal
*/
function genOutputsTpl(outputs) {
return outputs
.map(({ templateName, propName }) => `(${templateName})="${propName}($event)"`)
.join(' ');
}
/**
* @internal
*/
let HostGeneratorService = class HostGeneratorService {
constructor(cfr, injector) {
this.cfr = cfr;
this.injector = injector;
this.testModule = this.injector.get(TestModuleToken);
this.typeToken = this.injector.get(TestTypeToken);
this.typeKind = this.injector.get(TestTypeKindToken);
this.extraConfig = this.injector.get(ExtraConfigToken);
}
generateModuleFor(host) {
const testModule = this.testModule;
const ngModule = this.extraConfig.ngModule;
let TestModule = class TestModule {
};
TestModule = __decorate([
NgModule({
imports: [testModule],
declarations: [host],
exports: [host],
entryComponents: [host],
schemas: ngModule ? ngModule.schemas : [],
})
], TestModule);
return TestModule;
}
generate() {
switch (this.typeKind) {
case TestTypeKind.Component:
return this.generateForComponent();
case TestTypeKind.Directive:
return this.generateForDirective();
default:
throw Error(`HostGeneratorService: Cannot generate host component for unknown kind: ${this.typeKind}`);
}
}
generateForComponent() {
return this.genForComponent(this.typeToken);
}
generateForDirective() {
return this.genForDirective(this.typeToken);
}
genForComponent(compType) {
const factory = this.cfr.resolveComponentFactory(compType);
const selector = `host-${factory.selector}`;
const template = this.extraConfig.template ||
genHostCompTpl(factory.selector, factory, this.extraConfig.projectContent);
return this.genComponent({ selector, template }, compType, factory);
}
genForDirective(dirType) {
const io = getDirectiveIO(dirType);
const selector = 'host-directive';
const templateTag = this.extraConfig.template
? ''
: this.extraConfig.hostComponent
? this.cfr.resolveComponentFactory(this.extraConfig.hostComponent)
.selector
: this.extraConfig.hostTag;
const template = this.extraConfig.template ||
(templateTag ? this.genDirectiveTemplate(templateTag, io) : '');
return this.genComponent({ selector, template }, dirType, io);
}
genDirectiveTemplate(tag, io) {
return this.extraConfig.useStarSyntax
? genHostCompTplStar(io.inputs[0].templateName, tag, io, this.extraConfig.projectContent, this.extraConfig.templateBindings)
: genHostCompTpl(tag, io, this.extraConfig.projectContent, this.extraConfig.templateBindings);
}
genComponent(meta, type, io) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const hostGeneratorService = this;
let TestHostComponent = class TestHostComponent {
constructor() {
hostGeneratorService.initComponent(type, this, io);
}
};
__decorate([
ViewChild(type),
__metadata("design:type", Object)
], TestHostComponent.prototype, "instance", void 0);
TestHostComponent = __decorate([
Component(meta),
__metadata("design:paramtypes", [])
], TestHostComponent);
return TestHostComponent;
}
initComponent(compType, hostComp, io) {
io.inputs.forEach(({ propName }) => (hostComp[propName] = undefined));
io.outputs.forEach(({ propName }) => (hostComp[propName] = getOutputMock()(propName, compType.name, compType, () => hostComp.instance)));
}
};
HostGeneratorService.ctorParameters = () => [
{ type: ComponentFactoryResolver },
{ type: Injector }
];
HostGeneratorService.ɵprov = ɵɵdefineInjectable({ factory: function HostGeneratorService_Factory() { return new HostGeneratorService(ɵɵinject(ComponentFactoryResolver), ɵɵinject(INJECTOR)); }, token: HostGeneratorService, providedIn: "root" });
HostGeneratorService = __decorate([
Injectable({
providedIn: 'root',
}),
__metadata("design:paramtypes", [ComponentFactoryResolver,
Injector])
], HostGeneratorService);
class Host {
constructor(injector) {
this.injector = injector;
this.hostGeneratorService = this.injector.get(HostGeneratorService);
this.componentType = this.injector.get(TestTypeToken);
this.hostComponentType = this.hostGeneratorService.generate();
this.compiler = this.injector.get(Compiler);
this._didCompilation = false;
}
get fixture() {
return this._fixture;
}
get hostElement() {
return this._fixture.debugElement;
}
get hostHtmlElement() {
return this._fixture.nativeElement;
}
get hostComponent() {
return this._fixture.componentInstance;
}
ngOnDestroy() {
if (this._fixture) {
try {
this._fixture.destroy();
}
catch (_a) { }
}
}
compileComponents() {
return __awaiter(this, void 0, void 0, function* () {
if (!this._didCompilation) {
this._didCompilation = true;
const hostModule = this.hostGeneratorService.generateModuleFor(this.hostComponentType);
yield this.compiler.compileModuleAndAllComponentsAsync(hostModule);
return yield TestBed.compileComponents();
}
});
}
createComponent(inputs, detectChanges = false) {
return __awaiter(this, void 0, void 0, function* () {
yield this.compileComponents();
this._fixture = TestBed.createComponent(this.hostComponentType);
if (inputs) {
this.setInputs(inputs);
}
if (detectChanges) {
this.detectChanges();
}
return this._fixture;
});
}
setInputs(inputs, detectChanges = false) {
Object.assign(this.hostComponent, inputs);
if (detectChanges) {
this.detectChanges();
}
}
overrideHostTemplate(tpl) {
TestBed.overrideTemplate(this.hostComponentType, tpl);
}
detectChanges(checkNoChanges) {
this._fixture.detectChanges(checkNoChanges);
}
query(compOrDir) {
return this.hostElement.query(By.directive(compOrDir));
}
queryComponent(compOrDir) {
const elem = this.query(compOrDir);
return elem ? elem.componentInstance : undefined;
}
queryComponentHtml(compOrDir) {
const elem = this.query(compOrDir);
return elem ? elem.nativeElement : undefined;
}
queryCss(css) {
return this.hostElement.query(By.css(css));
}
}
let HostComponentService = class HostComponentService extends Host {
constructor(injector) {
super(injector);
}
get element() {
return this.query(this.componentType);
}
get htmlElement() {
return this.queryComponentHtml(this.componentType);
}
get component() {
return this.element.componentInstance;
}
};
HostComponentService.ctorParameters = () => [
{ type: Injector }
];
HostComponentService.ɵprov = ɵɵdefineInjectable({ factory: function HostComponentService_Factory() { return new HostComponentService(ɵɵinject(INJECTOR)); }, token: HostComponentService, providedIn: "root" });
HostComponentService = __decorate([
Injectable({
providedIn: 'root',
}),
__metadata("design:paramtypes", [Injector])
], HostComponentService);
let HostDirectiveService = class HostDirectiveService extends Host {
constructor(injector) {
super(injector);
}
get directive() {
return this.hostComponent.instance;
}
};
HostDirectiveService.ctorParameters = () => [
{ type: Injector }
];
HostDirectiveService.ɵprov = ɵɵdefineInjectable({ factory: function HostDirectiveService_Factory() { return new HostDirectiveService(ɵɵinject(INJECTOR)); }, token: HostDirectiveService, providedIn: "root" });
HostDirectiveService = __decorate([
Injectable({
providedIn: 'root',
}),
__metadata("design:paramtypes", [Injector])
], HostDirectiveService);
function getTestingForComponent(type, extras) {
const testModule = NgxTestingModule.forComponent(type, extras);
const getHost = () => TestBed.inject(HostComponentService);
const createComponent = (inputs, detectChanges) => getHost()
.createComponent(inputs, detectChanges)
.then(getHost);
return { testModule, getHost, createComponent };
}
function getTestingForDirective(type, extras) {
const testModule = NgxTestingModule.forDirective(type, extras);
const getHost = () => TestBed.inject(HostDirectiveService);
const createComponent = (inputs, detectChanges) => getHost()
.createComponent(inputs, detectChanges)
.then(getHost);
return { testModule, getHost, createComponent };
}
let compilerFactory;
let compiler;
afterEach(resetModuleCompiler);
/**
* Allows to compile and instantiate any {@link NgModule}
* with optional custom {@link Injector} or {@link StaticProvider}s
*
* _NOTE:_ Compiler is reused between test cases unless `options` are passed -
* in which case a new compiler will be created and reused for the current test case
*
* **Example:**
* ```ts
* import { NgModule } from '@angular/core';
*
* @NgModule({...})
* class MyModule {}
*
* it('should instantiate MyModule', () => {
* const myModule = createModule(MuModule);
* expect(myModule instanceof MyModule).toBeTruthy();
* });
* ```
*
* For async module compilation use {@link createModuleAsync()}
*/
function createModule(moduleType, injectorOrProviders = Injector.NULL, options) {
const moduleDef = createCompiler(options).compileModuleAndAllComponentsSync(moduleType);
return instantiateModule(moduleDef.ngModuleFactory, injectorOrProviders);
}
/**
* Allows to compile and instantiate any {@link NgModule}
* with optional custom {@link Injector} or {@link StaticProvider}s
* in asynchronous manner
*
* Works same as {@link createModule()} but compiles module asynchronously
* @see createModule()
*/
function createModuleAsync(moduleType, injectorOrProviders = Injector.NULL, options) {
return __awaiter(this, void 0, void 0, function* () {
const moduleDef = yield createCompiler(options).compileModuleAndAllComponentsAsync(moduleType);
return instantiateModule(moduleDef.ngModuleFactory, injectorOrProviders);
});
}
/**
* Allows to reset {@link Compiler} used to compile modules for current test case
* created via {@link createModule()} and {@link createModuleAsync()}
*/
function resetModuleCompiler() {
compiler = undefined;
}
function instantiateModule(moduleFactory, injectorOrProviders = Injector.NULL) {
if (Array.isArray(injectorOrProviders)) {
injectorOrProviders = Injector.create({ providers: injectorOrProviders });
}
return moduleFactory.create(injectorOrProviders);
}
function createCompiler(options) {
if (!compiler || options) {
compiler = getCompilerFactory().createCompiler(options);
}
return compiler;
}
function getCompilerFactory() {
if (!compilerFactory) {
compilerFactory = getTestBed().platform.injector.get(CompilerFactory);
}
return compilerFactory;
}
/**
* @internal
*
* Used to simulate complete reset inside unit tests
*/
function _reset() {
compilerFactory = undefined;
compiler = undefined;
}
/*
* Public API Surface of ngx-testing
*/
/**
* Generated bundle index. Do not edit.
*/
export { Host, HostComponentService, HostDirectiveService, NgxTestingModule, TestTypeKind, createModule, createModuleAsync, getOutputMock, getTestingForComponent, getTestingForDirective, resetModuleCompiler, setOutputMock, ɵ0 };
//# sourceMappingURL=orchestrator-ngx-testing.js.map