Skip to content

Commit

Permalink
feat: 🎸 support for overriding modules (#302) (#313)
Browse files Browse the repository at this point in the history
Co-authored-by: Mehmet Ozan Turhan <[email protected]>
  • Loading branch information
ozanturhan and Mehmet Ozan Turhan authored May 27, 2020
1 parent 2d7a19e commit 81deb6e
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const createComponent = createComponentFactory({
entryComponents: [],
componentProviders: [], // Override the component's providers
componentViewProviders: [], // Override the component's view providers
overrideModules: [], // Override modules
mocks: [], // Providers that will automatically be mocked
componentMocks: [], // Component providers that will automatically be mocked
componentViewProvidersMocks: [], // Component view providers that will be automatically mocked
Expand Down
19 changes: 19 additions & 0 deletions docs/docs/testing-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const createComponent = createComponentFactory({
entryComponents: [],
componentProviders: [], // Override the component's providers
componentViewProviders: [], // Override the component's view providers
overrideModules: [], // Override modules
mocks: [], // Providers that will automatically be mocked
componentMocks: [], // Component providers that will automatically be mocked
componentViewProvidersMocks: [], // Component view providers that will be automatically mocked
Expand Down Expand Up @@ -192,3 +193,21 @@ spectator.get(FooService, true)
In the same way you can also override the component view providers by using the `componentViewProviders` and `componentViewProvidersMocks`.
The same rules also apply to directives using the `directiveProviders` and `directiveMocks` parameters.
## Override Modules
Use `overrideModules` option to override modules.
For Example:
```ts
createComponentFactory({
component: SomeComponent,
overrideModules: [
[SomeModule, {set: {declarations: [SomeOtherComponent]}],
[SomeOtherModule, {set: {declarations: [SomeOtherComponent]}]
]
})
```

cf. https://angular.io/api/core/testing/TestBed#overrideModule
72 changes: 72 additions & 0 deletions projects/spectator/jest/test/override-module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Component, Directive, HostBinding, NgModule } from '@angular/core';
import { Spectator, SpectatorDirective, SpectatorHost } from '@ngneat/spectator';
import { createComponentFactory, createDirectiveFactory, createHostFactory } from '@ngneat/spectator/jest';

import { AveragePipe } from '../../test/pipe/average.pipe';

@Component({ selector: 'test-comp', template: '<div someDirective>{{ prop | avg }}</div>' })
class TestComponent {
public prop = [1, 2, 3];
}

@Directive({ selector: '[someDirective]' })
class SomeDirective {
@HostBinding('class') public someClass = 'someClass';
}

@NgModule()
class SomeModule {}

describe('Override Module With Component Factory', () => {
let spectator: Spectator<TestComponent>;
const createComponent = createComponentFactory({
component: TestComponent,
imports: [SomeModule],
overrideModules: [[SomeModule, { set: { declarations: [AveragePipe], exports: [AveragePipe] } }]]
});

beforeEach(() => {
spectator = createComponent();
});

it('should be declared with override modules', () => {
expect(spectator.component).toBeTruthy();
expect(spectator.query('div')?.textContent).toEqual('2');
});
});

describe('Override Module With Directive Factory', () => {
let spectator: SpectatorDirective<SomeDirective, TestComponent>;
const createDirective = createDirectiveFactory({
directive: SomeDirective,
host: TestComponent,
imports: [SomeModule],
overrideModules: [[SomeModule, { set: { declarations: [AveragePipe], exports: [AveragePipe] } }]]
});

beforeEach(() => {
spectator = createDirective(`<div someDirective>{{ prop | avg }}</div>`);
});

it('should be declared with override modules', () => {
expect(spectator.query('div')?.classList).toContain('someClass');
expect(spectator.query('div')?.textContent).toEqual('2');
});
});

describe('Override Module With Host Factory', () => {
let spectator: SpectatorHost<TestComponent>;
const createHost = createHostFactory({
component: TestComponent,
imports: [SomeModule],
overrideModules: [[SomeModule, { set: { declarations: [AveragePipe], exports: [AveragePipe] } }]]
});

beforeEach(() => {
spectator = createHost(`<test-comp></test-comp>`);
});

it('should be declared with override modules', () => {
expect(spectator.query('div')?.textContent).toEqual('2');
});
});
7 changes: 5 additions & 2 deletions projects/spectator/src/lib/base/options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Provider, SchemaMetadata, Type } from '@angular/core';
import { NgModule, Provider, SchemaMetadata, Type } from '@angular/core';
import { MetadataOverride } from '@angular/core/testing';

import { merge } from '../internals/merge';
import { mockProvider, MockProvider } from '../mock';
Expand All @@ -16,6 +17,7 @@ export interface BaseSpectatorOptions {
declarations?: any[];
imports?: any[];
schemas?: (SchemaMetadata | any[])[];
overrideModules?: [Type<any>, MetadataOverride<NgModule>][];
}

/**
Expand All @@ -33,7 +35,8 @@ const defaultOptions: OptionalsRequired<BaseSpectatorOptions> = {
providers: [],
declarations: [],
imports: [],
schemas: []
schemas: [],
overrideModules: []
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { nodeByDirective } from '../internals/node-by-directive';
import { initialSpectatorDirectiveModule } from './initial-module';
import { getSpectatorDirectiveDefaultOptions, SpectatorDirectiveOptions } from './options';
import { SpectatorDirective } from './spectator-directive';
import { overrideModules } from '../spectator/create-factory';

/**
* @publicApi
Expand Down Expand Up @@ -64,6 +65,7 @@ export function createDirectiveFactory<D, H = HostComponent>(
beforeEach(async(() => {
jasmine.addMatchers(customMatchers as any);
TestBed.configureTestingModule(moduleMetadata);
overrideModules(options);
}));

return <HP>(template?: string, overrides?: SpectatorDirectiveOverrides<D, H, HP>) => {
Expand Down
4 changes: 3 additions & 1 deletion projects/spectator/src/lib/spectator-host/create-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/t

import { setProps } from '../internals/query';
import * as customMatchers from '../matchers';
import { SpectatorOverrides, overrideComponentIfProviderOverridesSpecified } from '../spectator/create-factory';
import { overrideComponentIfProviderOverridesSpecified, overrideModules, SpectatorOverrides } from '../spectator/create-factory';
import { isType } from '../types';
import { nodeByDirective } from '../internals/node-by-directive';

Expand Down Expand Up @@ -67,6 +67,8 @@ export function createHostFactory<C, H = HostComponent>(typeOrOptions: Type<C> |
jasmine.addMatchers(customMatchers as any);
TestBed.configureTestingModule(moduleMetadata);

overrideModules(options);

overrideComponentIfProviderOverridesSpecified(options);
}));

Expand Down
2 changes: 2 additions & 0 deletions projects/spectator/src/lib/spectator-http/create-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { isType } from '../types';
import { initialHttpModule } from './initial-module';
import { getDefaultHttpOptions, isDeprecated, SpectatorHttpOptions } from './options';
import { SpectatorHttp } from './spectator-http';
import { overrideModules } from '../spectator/create-factory';

/**
* @publicApi
Expand All @@ -31,6 +32,7 @@ export function createHttpFactory<S>(typeOrOptions: Type<S> | SpectatorHttpOptio

beforeEach(() => {
TestBed.configureTestingModule(moduleMetadata);
overrideModules(options);
});

afterEach(() => {
Expand Down
2 changes: 2 additions & 0 deletions projects/spectator/src/lib/spectator-pipe/create-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { HostComponent } from '../spectator-host/host-component';
import { initialSpectatorPipeModule } from './initial-module';
import { getSpectatorPipeDefaultOptions, SpectatorPipeOptions } from './options';
import { SpectatorPipe } from './spectator-pipe';
import { overrideModules } from '../spectator/create-factory';

/**
* @publicApi
Expand Down Expand Up @@ -41,6 +42,7 @@ export function createPipeFactory<P, H = HostComponent>(typeOrOptions: Type<P> |
beforeEach(async(() => {
jasmine.addMatchers(customMatchers as any);
TestBed.configureTestingModule(moduleMetadata);
overrideModules(options);
}));

return <HP>(templateOrOverrides?: string | SpectatorPipeOverrides<H, HP>, overrides?: SpectatorPipeOverrides<H, HP>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';

import { setProps } from '../internals/query';
import * as customMatchers from '../matchers';
import { SpectatorOverrides, overrideComponentIfProviderOverridesSpecified } from '../spectator/create-factory';
import { SpectatorOverrides, overrideComponentIfProviderOverridesSpecified, overrideModules } from '../spectator/create-factory';
import { isType } from '../types';

import { ActivatedRouteStub } from './activated-route-stub';
Expand Down Expand Up @@ -37,6 +37,8 @@ export function createRoutingFactory<C>(typeOrOptions: Type<C> | SpectatorRoutin
jasmine.addMatchers(customMatchers as any);
TestBed.configureTestingModule(moduleMetadata);

overrideModules(options);

overrideComponentIfProviderOverridesSpecified(options);

TestBed.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { isType, doesServiceImplementsOnDestroy } from '../types';
import { initialServiceModule } from './initial-module';
import { getDefaultServiceOptions, SpectatorServiceOptions } from './options';
import { SpectatorService } from './spectator-service';
import { overrideModules } from '../spectator/create-factory';

/**
* @publicApi
Expand All @@ -30,6 +31,7 @@ export function createServiceFactory<S>(typeOrOptions: Type<S> | SpectatorServic

beforeEach(() => {
TestBed.configureTestingModule(moduleMetadata);
overrideModules(options);
});

afterEach(() => {
Expand Down
17 changes: 16 additions & 1 deletion projects/spectator/src/lib/spectator/create-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Provider, Type } from '@angular/core';
import { async, TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';

import { BaseSpectatorOverrides } from '../base/options';
import { BaseSpectatorOptions, BaseSpectatorOverrides } from '../base/options';
import { setProps } from '../internals/query';
import * as customMatchers from '../matchers';
import { isType } from '../types';
Expand Down Expand Up @@ -49,6 +49,19 @@ export function overrideComponentIfProviderOverridesSpecified<C>(options: Requir
}
}

/**
* @internal
*/
export function overrideModules(options: Required<BaseSpectatorOptions>): void {
if (options.overrideModules.length) {
options.overrideModules.forEach(overrideModule => {
const [ngModule, override] = overrideModule;

TestBed.overrideModule(ngModule, override);
});
}
}

/**
* @deprecated Use createComponentFactory instead. To be removed in v5.
*/
Expand All @@ -74,6 +87,8 @@ export function createComponentFactory<C>(typeOrOptions: Type<C> | SpectatorOpti
}
});

overrideModules(options);

overrideComponentIfProviderOverridesSpecified(options);

TestBed.compileComponents();
Expand Down
78 changes: 78 additions & 0 deletions projects/spectator/test/override-module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Component, Directive, HostBinding, NgModule } from '@angular/core';
import {
createComponentFactory,
createDirectiveFactory,
createHostFactory,
Spectator,
SpectatorDirective,
SpectatorHost
} from '@ngneat/spectator';

import { AveragePipe } from './pipe/average.pipe';

@Component({ selector: 'test-comp', template: '<div someDirective>{{ prop | avg }}</div>' })
class TestComponent {
public prop = [1, 2, 3];
}

@Directive({ selector: '[someDirective]' })
class SomeDirective {
@HostBinding('class') public someClass = 'someClass';
}

@NgModule()
class SomeModule {}

describe('Override Module With Component Factory', () => {
let spectator: Spectator<TestComponent>;
const createComponent = createComponentFactory({
component: TestComponent,
imports: [SomeModule],
overrideModules: [[SomeModule, { set: { declarations: [AveragePipe], exports: [AveragePipe] } }]]
});

beforeEach(() => {
spectator = createComponent();
});

it('should be declared with override modules', () => {
expect(spectator.component).toBeTruthy();
expect(spectator.query('div')?.textContent).toEqual('2');
});
});

describe('Override Module With Directive Factory', () => {
let spectator: SpectatorDirective<SomeDirective, TestComponent>;
const createDirective = createDirectiveFactory({
directive: SomeDirective,
host: TestComponent,
imports: [SomeModule],
overrideModules: [[SomeModule, { set: { declarations: [AveragePipe], exports: [AveragePipe] } }]]
});

beforeEach(() => {
spectator = createDirective(`<div someDirective>{{ prop | avg }}</div>`);
});

it('should be declared with override modules', () => {
expect(spectator.query('div')?.classList).toContain('someClass');
expect(spectator.query('div')?.textContent).toEqual('2');
});
});

describe('Override Module With Host Factory', () => {
let spectator: SpectatorHost<TestComponent>;
const createHost = createHostFactory({
component: TestComponent,
imports: [SomeModule],
overrideModules: [[SomeModule, { set: { declarations: [AveragePipe], exports: [AveragePipe] } }]]
});

beforeEach(() => {
spectator = createHost(`<test-comp></test-comp>`);
});

it('should be declared with override modules', () => {
expect(spectator.query('div')?.textContent).toEqual('2');
});
});

0 comments on commit 81deb6e

Please sign in to comment.