Skip to content

Commit

Permalink
fix(core): mock of mock will return itself
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jun 28, 2021
1 parent efd93fe commit 4358b99
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 0 deletions.
31 changes: 31 additions & 0 deletions libs/ng-mocks/src/lib/common/func.is-mock-ng-def.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { MockedComponent } from '../mock-component/types';
import { MockedDirective } from '../mock-directive/types';
import { MockedModule } from '../mock-module/types';
import { MockedPipe } from '../mock-pipe/types';

import { Type } from './core.types';
import { isNgDef } from './func.is-ng-def';

export function isMockNgDef<T>(component: Type<T>, ngType: 'c'): component is Type<MockedComponent<T>>;

export function isMockNgDef<T>(directive: Type<T>, ngType: 'd'): directive is Type<MockedDirective<T>>;

export function isMockNgDef<T>(pipe: Type<T>, ngType: 'p'): pipe is Type<MockedPipe<T>>;

export function isMockNgDef<T>(module: Type<T>, ngType: 'm'): module is Type<MockedModule<T>>;

export function isMockNgDef<T>(module: Type<T>): module is Type<T>;

export function isMockNgDef<TComponent>(
component: Type<TComponent> & { mockOf?: any },
type?: 'c' | 'd' | 'p' | 'm',
): component is Type<TComponent> {
if (!(component as any).mockOf) {
return false;
}
if (!type) {
return true;
}

return isNgDef(component.mockOf, type as never);
}
5 changes: 5 additions & 0 deletions libs/ng-mocks/src/lib/mock-component/mock-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Type } from '../common/core.types';
import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of';
import funcImportExists from '../common/func.import-exists';
import funcIsMock from '../common/func.is-mock';
import { isMockNgDef } from '../common/func.is-mock-ng-def';
import { MockConfig } from '../common/mock';
import { LegacyControlValueAccessor } from '../common/mock-control-value-accessor';
import ngMocksUniverse from '../common/ng-mocks-universe';
Expand Down Expand Up @@ -219,6 +220,10 @@ export function MockComponents(...components: Array<Type<any>>): Array<Type<Mock
export function MockComponent<TComponent>(component: Type<TComponent>): Type<MockedComponent<TComponent>> {
funcImportExists(component, 'MockComponent');

if (isMockNgDef(component, 'c')) {
return component;
}

// We are inside of an 'it'. It is fine to to return a mock copy.
if ((getTestBed() as any)._instantiated) {
try {
Expand Down
5 changes: 5 additions & 0 deletions libs/ng-mocks/src/lib/mock-directive/mock-directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import coreReflectDirectiveResolve from '../common/core.reflect.directive-resolv
import { Type } from '../common/core.types';
import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of';
import funcImportExists from '../common/func.import-exists';
import { isMockNgDef } from '../common/func.is-mock-ng-def';
import { LegacyControlValueAccessor } from '../common/mock-control-value-accessor';
import ngMocksUniverse from '../common/ng-mocks-universe';
import decorateDeclaration from '../mock/decorate-declaration';
Expand Down Expand Up @@ -106,6 +107,10 @@ export function MockDirectives(...directives: Array<Type<any>>): Array<Type<Mock
export function MockDirective<TDirective>(directive: Type<TDirective>): Type<MockedDirective<TDirective>> {
funcImportExists(directive, 'MockDirective');

if (isMockNgDef(directive, 'd')) {
return directive;
}

// We are inside of an 'it'.
// It is fine to to return a mock copy or to throw an exception if it was not replaced with its mock copy in TestBed.
if ((getTestBed() as any)._instantiated) {
Expand Down
5 changes: 5 additions & 0 deletions libs/ng-mocks/src/lib/mock-module/mock-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Type } from '../common/core.types';
import decorateMock from '../common/decorate.mock';
import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of';
import funcImportExists from '../common/func.import-exists';
import { isMockNgDef } from '../common/func.is-mock-ng-def';
import { isNgDef } from '../common/func.is-ng-def';
import { isNgModuleDefWithProviders, NgModuleWithProviders } from '../common/func.is-ng-module-def-with-providers';
import { Mock } from '../common/mock';
Expand Down Expand Up @@ -74,6 +75,10 @@ const extractModuleAndProviders = (
};

const getExistingMockModule = (ngModule: Type<any>): Type<any> | undefined => {
if (isMockNgDef(ngModule, 'm')) {
return ngModule;
}

// Every module should be replaced with its mock copy only once to avoid errors like:
// Failed: Type ...Component is part of the declarations of 2 modules: ...Module and ...Module...
if (ngMocksUniverse.flags.has('cacheModule') && ngMocksUniverse.cacheDeclarations.has(ngModule)) {
Expand Down
5 changes: 5 additions & 0 deletions libs/ng-mocks/src/lib/mock-pipe/mock-pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Type } from '../common/core.types';
import decorateMock from '../common/decorate.mock';
import { getMockedNgDefOf } from '../common/func.get-mocked-ng-def-of';
import funcImportExists from '../common/func.import-exists';
import { isMockNgDef } from '../common/func.is-mock-ng-def';
import { Mock } from '../common/mock';
import ngMocksUniverse from '../common/ng-mocks-universe';
import helperMockService from '../mock-service/helper.mock-service';
Expand Down Expand Up @@ -51,6 +52,10 @@ export function MockPipe<TPipe extends PipeTransform>(
): Type<MockedPipe<TPipe>> {
funcImportExists(pipe, 'MockPipe');

if (isMockNgDef(pipe, 'p')) {
return pipe;
}

// We are inside of an 'it'. It is fine to return a mock copy.
if ((getTestBed() as any)._instantiated) {
try {
Expand Down
87 changes: 87 additions & 0 deletions tests/mock-of-mock/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
Component,
Directive,
NgModule,
Pipe,
PipeTransform,
} from '@angular/core';
import {
isMockNgDef,
MockComponent,
MockDirective,
MockModule,
MockPipe,
} from 'ng-mocks';

@Component({
selector: 'target',
template: '',
})
class TargetComponent {}

@Directive({
selector: 'target',
})
class TargetDirective {}

@Pipe({
name: 'target',
})
class TargetPipe implements PipeTransform {
public transform(value: any): any {
return value;
}
}

@NgModule({})
class TargetModule {}

describe('mock-of-mock', () => {
it('returns the same mock component', () => {
const mock1 = MockComponent(TargetComponent);
const mock2 = MockComponent(mock1);
expect(mock1).not.toBe(TargetComponent);
expect(mock2).toBe(mock1);
expect(isMockNgDef(mock2)).toEqual(true);
expect(isMockNgDef(mock2, 'c')).toEqual(true);
expect(isMockNgDef(mock2, 'd')).toEqual(false);
expect(isMockNgDef(mock2, 'p')).toEqual(false);
expect(isMockNgDef(mock2, 'm')).toEqual(false);
});

it('returns the same mock directive', () => {
const mock1 = MockDirective(TargetDirective);
const mock2 = MockDirective(mock1);
expect(mock1).not.toBe(TargetDirective);
expect(mock2).toBe(mock1);
expect(isMockNgDef(mock2)).toEqual(true);
expect(isMockNgDef(mock2, 'c')).toEqual(false);
expect(isMockNgDef(mock2, 'd')).toEqual(true);
expect(isMockNgDef(mock2, 'p')).toEqual(false);
expect(isMockNgDef(mock2, 'm')).toEqual(false);
});

it('returns the same mock pipe', () => {
const mock1 = MockPipe(TargetPipe);
const mock2 = MockPipe(mock1);
expect(mock1).not.toBe(TargetPipe);
expect(mock2).toBe(mock1);
expect(isMockNgDef(mock2)).toEqual(true);
expect(isMockNgDef(mock2, 'c')).toEqual(false);
expect(isMockNgDef(mock2, 'd')).toEqual(false);
expect(isMockNgDef(mock2, 'p')).toEqual(true);
expect(isMockNgDef(mock2, 'm')).toEqual(false);
});

it('returns the same mock module', () => {
const mock1 = MockModule(TargetModule);
const mock2 = MockModule(mock1);
expect(mock1).not.toBe(TargetModule);
expect(mock2).toBe(mock1);
expect(isMockNgDef(mock2)).toEqual(true);
expect(isMockNgDef(mock2, 'c')).toEqual(false);
expect(isMockNgDef(mock2, 'd')).toEqual(false);
expect(isMockNgDef(mock2, 'p')).toEqual(false);
expect(isMockNgDef(mock2, 'm')).toEqual(true);
});
});

0 comments on commit 4358b99

Please sign in to comment.