Skip to content

Commit

Permalink
feat: ngMocks.defaultExclude
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jan 10, 2021
1 parent b5cb39c commit bdd2821
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 13 deletions.
57 changes: 55 additions & 2 deletions lib/common/ng-mocks-universe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ interface NgMocksUniverse {
config: Map<any, any>;
configInstance: Map<any, any>;
flags: Set<string>;
getBuildDeclaration: (def: any) => any | undefined;
getDefaults: () => Map<any, any>;
getLocalMocks: () => Array<[any, any]>;
getOverrides: () => Map<any, any>;
getResolution: (def: any) => undefined | 'mock' | 'keep' | 'replace' | 'exclude';
global: Map<any, any>;
hasBuildDeclaration: (def: any) => boolean;
isExcludedDef: (def: any) => boolean;
isProvidedDef: (def: any) => boolean;
touches: Set<AnyType<any> | InjectionToken<any> | string>;
Expand Down Expand Up @@ -51,8 +55,57 @@ ngMocksUniverse.getOverrides = () => {
return ngMocksUniverse.global.get('overrides');
};

const hasBuildDeclaration = (def: any): boolean => ngMocksUniverse.builtDeclarations.has(def);
const getBuildDeclaration = (def: any): any => ngMocksUniverse.builtDeclarations.get(def);
ngMocksUniverse.getDefaults = () => {
if (!ngMocksUniverse.global.has('defaults')) {
ngMocksUniverse.global.set('defaults', new Map());
}

return ngMocksUniverse.global.get('defaults');
};

ngMocksUniverse.getResolution = (def: any) => {
const set = ngMocksUniverse.config.get('ngMocksDepsResolution');
if (set?.has(def)) {
return set.get(def);
}

if (!ngMocksUniverse.getDefaults().has(def)) {
return undefined;
}

const value = ngMocksUniverse.getDefaults().get(def);
if (!value) {
return 'exclude';
}
if (def === value) {
return 'keep';
}

return 'replace';
};

ngMocksUniverse.getBuildDeclaration = (def: any) => {
if (ngMocksUniverse.builtDeclarations.has(def)) {
return ngMocksUniverse.builtDeclarations.get(def);
}
if (ngMocksUniverse.getDefaults().has(def)) {
return ngMocksUniverse.getDefaults().get(def);
}
};

ngMocksUniverse.hasBuildDeclaration = (def: any) => {
if (ngMocksUniverse.builtDeclarations.has(def)) {
return true;
}
if (ngMocksUniverse.getDefaults().has(def)) {
return true;
}

return false;
};

const hasBuildDeclaration = (def: any): boolean => ngMocksUniverse.hasBuildDeclaration(def);
const getBuildDeclaration = (def: any): any => ngMocksUniverse.getBuildDeclaration(def);

ngMocksUniverse.isExcludedDef = (def: any): boolean => hasBuildDeclaration(def) && getBuildDeclaration(def) === null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default (replaceDef: Set<any>, defValue: Map<any, any>): ValueProvider =>
const overrides: Map<Type<any>, MetadataOverride<any>> = new Map();
for (const proto of mapValues(ngMocksUniverse.touches)) {
const source: any = proto;
const value = ngMocksUniverse.builtDeclarations.get(source) || source;
const value = ngMocksUniverse.getBuildDeclaration(source) || source;
if (skipOverride(replaceDef, defValue, source, value)) {
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/mock-builder/promise/create-ng-mocks-touches-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default (): ValueProvider => {
const touches = new Set();
for (const proto of mapValues(ngMocksUniverse.touches)) {
const source: any = proto;
let value = ngMocksUniverse.builtDeclarations.get(source);
let value = ngMocksUniverse.getBuildDeclaration(source);

// kept declarations should be based on their source.
if (value === undefined) {
Expand Down
2 changes: 1 addition & 1 deletion lib/mock-builder/promise/init-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ngMocksUniverse from '../../common/ng-mocks-universe';
import { BuilderData } from './types';

export default (def: Type<any>, defProviders: BuilderData['defProviders']): Type<any> | ModuleWithProviders<any> => {
const loModule = ngMocksUniverse.builtDeclarations.get(def);
const loModule = ngMocksUniverse.getBuildDeclaration(def);
const loProviders = defProviders.has(def) ? defProviders.get(def) : undefined;

return loProviders
Expand Down
2 changes: 1 addition & 1 deletion lib/mock-builder/promise/init-ng-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default ({ configDef, keepDef, mockDef, replaceDef }: BuilderData, defPro
if (isNgDef(def, 'm')) {
imports.push(initModule(def, defProviders));
} else {
declarations.push(ngMocksUniverse.builtDeclarations.get(def));
declarations.push(ngMocksUniverse.getBuildDeclaration(def));
}

ngMocksUniverse.touches.add(def);
Expand Down
8 changes: 8 additions & 0 deletions lib/mock-helper/mock-helper.default-exclude.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { InjectionToken } from '@angular/core';

import { AnyType } from '../common/core.types';
import ngMocksUniverse from '../common/ng-mocks-universe';

export default (source: AnyType<any> | InjectionToken<any>): void => {
ngMocksUniverse.getDefaults().set(source, null);
};
7 changes: 7 additions & 0 deletions lib/mock-helper/mock-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { MockedDebugElement, MockedDebugNode } from '../mock-render/types';
import { CustomMockFunction, MockedFunction } from '../mock-service/types';

import mockHelperAutoSpy from './mock-helper.auto-spy';
import mockHelperDefaultExclude from './mock-helper.default-exclude';
import mockHelperDefaultMock from './mock-helper.default-mock';
import mockHelperFaster from './mock-helper.faster';
import mockHelperFind from './mock-helper.find';
Expand Down Expand Up @@ -37,6 +38,11 @@ export const ngMocks: {
*/
autoSpy(type: CustomMockFunction): void;

/**
* @see https://github.com/ike18t/ng-mocks#ngmocksdefaultexclude
*/
defaultExclude(source: AnyType<any> | InjectionToken<any>): void;

/**
* @see https://github.com/ike18t/ng-mocks#ngmocksdefaultmock
*/
Expand Down Expand Up @@ -244,6 +250,7 @@ export const ngMocks: {
stub<I extends object>(instance: I, overrides: Partial<I>): I;
} = {
autoSpy: mockHelperAutoSpy,
defaultExclude: mockHelperDefaultExclude,
defaultMock: mockHelperDefaultMock,
faster: mockHelperFaster,
find: mockHelperFind,
Expand Down
8 changes: 3 additions & 5 deletions lib/mock-module/mock-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ const flagNever = (ngModule?: any): boolean =>
const preprocessToggleFlag = (ngModule: Type<any>): boolean => {
let toggleSkipMockFlag = false;

const resolution: undefined | 'mock' | 'keep' | 'replace' | 'exclude' = ngMocksUniverse.config
.get('ngMocksDepsResolution')
?.get(ngModule);
const resolution = ngMocksUniverse.getResolution(ngModule);
if (flagMock(resolution)) {
toggleSkipMockFlag = true;
ngMocksUniverse.flags.delete('skipMock');
Expand Down Expand Up @@ -82,8 +80,8 @@ const getExistingMockModule = (ngModule: Type<any>): Type<any> | undefined => {
}

// Now we check if we need to keep the original module or to replace it with some other.
if (ngMocksUniverse.builtDeclarations.has(ngModule)) {
const instance = ngMocksUniverse.builtDeclarations.get(ngModule);
if (ngMocksUniverse.hasBuildDeclaration(ngModule)) {
const instance = ngMocksUniverse.getBuildDeclaration(ngModule);
if (isNgDef(instance, 'm') && instance !== ngModule) {
return instance;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/mock-module/mock-ng-def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const processDef = (def: any) => {
if (isNgDef(def, 'm') || isNgModuleDefWithProviders(def)) {
return MockModule(def as any);
}
if (ngMocksUniverse.builtDeclarations.has(def)) {
return ngMocksUniverse.builtDeclarations.get(def);
if (ngMocksUniverse.hasBuildDeclaration(def)) {
return ngMocksUniverse.getBuildDeclaration(def);
}
if (ngMocksUniverse.flags.has('skipMock')) {
return def;
Expand Down
15 changes: 15 additions & 0 deletions tests/ng-mocks-default-exclude-modules/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, NgModule } from '@angular/core';

@Component({
selector: 'target',
template: '{{ name }}',
})
export class Target1Component {
public readonly name = 'target1';
}

@NgModule({
declarations: [Target1Component],
exports: [Target1Component],
})
export class Target1Module {}
22 changes: 22 additions & 0 deletions tests/ng-mocks-default-exclude-modules/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { TestBed } from '@angular/core/testing';
import { MockModule, MockRender, ngMocks } from 'ng-mocks';

import { Target1Module } from './fixtures';

ngMocks.defaultExclude(Target1Module);

// The root module we pass into MockModule isn't excluded despite the setting.
describe('ng-mocks-default-exclude-modules', () => {
beforeEach(() => {
return TestBed.configureTestingModule({
imports: [MockModule(Target1Module)],
});
});

it('mocks Target1Component', () => {
const fixture = MockRender('<target></target>');
expect(fixture.nativeElement.innerHTML).toEqual(
'<target></target>',
);
});
});
112 changes: 112 additions & 0 deletions tests/ng-mocks-default-exclude/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Component, NgModule } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import {
MockBuilder,
MockComponent,
MockModule,
MockRender,
ngMocks,
} from 'ng-mocks';

@Component({
selector: 'target',
template: '{{ name }}',
})
class TargetComponent {
public readonly name = 'target';
}

@Component({
selector: 'target',
template: '{{ name }}',
})
class FakeComponent {
public readonly name = 'fake';
}

@NgModule({
declarations: [TargetComponent],
exports: [TargetComponent],
})
class TargetModule {}

ngMocks.defaultExclude(TargetComponent);

describe('ng-mocks-default-exclude', () => {
describe('MockComponent', () => {
beforeEach(() =>
TestBed.configureTestingModule({
declarations: [MockComponent(TargetComponent)],
}),
);

it('works as usual', () => {
const fixture = MockRender('<target></target>');
expect(fixture.nativeElement.innerHTML).toEqual(
'<target></target>',
);
});
});

describe('MockModule', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [MockModule(TargetModule)],
}),
);

it('excludes out of the box', () => {
expect(() => MockRender('<target></target>')).toThrow();
});
});

describe('MockBuilder:default', () => {
beforeEach(() => MockBuilder(null, TargetModule));

it('excludes out of the box', () => {
expect(() => MockRender('<target></target>')).toThrow();
});
});

describe('MockBuilder:keep', () => {
beforeEach(() =>
MockBuilder(null, TargetModule).keep(TargetComponent),
);

it('switches to keep', () => {
const fixture = MockRender('<target></target>');
expect(fixture.nativeElement.innerHTML).toEqual(
'<target>target</target>',
);
});
});

describe('MockBuilder:mock', () => {
beforeEach(() =>
MockBuilder(null, TargetModule).mock(TargetComponent),
);

it('switches to mock', () => {
const fixture = MockRender('<target></target>');
expect(fixture.nativeElement.innerHTML).toEqual(
'<target></target>',
);
});
});

describe('MockBuilder:replace', () => {
beforeEach(() =>
MockBuilder(null, TargetModule).replace(
TargetComponent,
FakeComponent,
),
);

it('switches to replace', () => {
const fixture = MockRender('<target></target>');
expect(fixture.nativeElement.innerHTML).toEqual(
'<target>fake</target>',
);
});
});
});

0 comments on commit bdd2821

Please sign in to comment.