Skip to content

Commit

Permalink
fix(mock-module): excludes modules with providers correctly
Browse files Browse the repository at this point in the history
closes #271
  • Loading branch information
satanTime committed Jan 10, 2021
1 parent a0c930c commit b5cb39c
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 7 deletions.
3 changes: 3 additions & 0 deletions lib/mock-builder/mock-builder.promise.ts
@@ -1,5 +1,6 @@
import { NgModule, Provider } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import ngMocksUniverse from 'ng-mocks/dist/lib/common/ng-mocks-universe';

import { flatten, mapValues } from '../common/core.helpers';
import { Type } from '../common/core.types';
Expand Down Expand Up @@ -72,6 +73,7 @@ export class MockBuilderPromise implements IMockBuilder {

public build(): NgModule {
this.stash.backup();
ngMocksUniverse.config.set('mockNgDefResolver', new Map());

const params = this.combineParams();

Expand All @@ -85,6 +87,7 @@ export class MockBuilderPromise implements IMockBuilder {
ngModule.providers.push(createNgMocksTouchesToken());
ngModule.providers.push(createNgMocksOverridesToken(this.replaceDef, this.defValue));

ngMocksUniverse.config.delete('mockNgDefResolver');
this.stash.restore();

return ngModule;
Expand Down
37 changes: 30 additions & 7 deletions lib/mock-module/mock-ng-def.ts
Expand Up @@ -86,18 +86,33 @@ const createResolveProvider = (resolutions: Map<any, any>, change: () => void):
const createResolveWithProviders = (def: any, mockDef: any): boolean =>
mockDef && mockDef.ngModule && isNgModuleDefWithProviders(def);

const createResolveExisting = (def: any, resolutions: Map<any, any>, change: (flag?: boolean) => void): any => {
const mockDef = resolutions.get(def);
if (def !== mockDef) {
change();
}

return mockDef;
};

const createResolveExcluded = (def: any, resolutions: Map<any, any>, change: (flag?: boolean) => void): void => {
resolutions.set(def, undefined);

change();
};

const createResolve = (resolutions: Map<any, any>, change: (flag?: boolean) => void): ((def: any) => any) => (
def: any,
) => {
if (resolutions.has(def)) {
return resolutions.get(def);
return createResolveExisting(def, resolutions, change);
}
if (ngMocksUniverse.isExcludedDef(def)) {
resolutions.set(def, undefined);

return change();
const detectedDef = isNgModuleDefWithProviders(def) ? def.ngModule : def;
if (ngMocksUniverse.isExcludedDef(detectedDef)) {
return createResolveExcluded(def, resolutions, change);
}
ngMocksUniverse.touches.add(isNgModuleDefWithProviders(def) ? def.ngModule : def);
ngMocksUniverse.touches.add(detectedDef);

const mockDef = processDef(def);
if (createResolveWithProviders(def, mockDef)) {
Expand Down Expand Up @@ -145,11 +160,11 @@ const resolveDefForExport = (

const createResolvers = (
change: () => void,
resolutions: Map<any, any>,
): {
resolve: (def: any) => any;
resolveProvider: (def: Provider) => any;
} => {
const resolutions = new Map();
const resolve = createResolve(resolutions, change);
const resolveProvider = createResolveProvider(resolutions, change);

Expand Down Expand Up @@ -185,13 +200,21 @@ const addExports = (
};

export default (ngModuleDef: NgModule, ngModule?: Type<any>): [boolean, NgModule] => {
const hasResolver = ngMocksUniverse.config.has('mockNgDefResolver');
if (!hasResolver) {
ngMocksUniverse.config.set('mockNgDefResolver', new Map());
}
let changed = !ngMocksUniverse.flags.has('skipMock');
const change = (flag = true) => {
changed = changed || flag;
};
const { resolve, resolveProvider } = createResolvers(change);
const { resolve, resolveProvider } = createResolvers(change, ngMocksUniverse.config.get('mockNgDefResolver'));
const mockModuleDef = processMeta(ngModuleDef, resolve, resolveProvider);
addExports(resolve, change, ngModuleDef, mockModuleDef, ngModule);

if (!hasResolver) {
ngMocksUniverse.config.delete('mockNgDefResolver');
}

return [changed, mockModuleDef];
};
38 changes: 38 additions & 0 deletions tests/issue-271/test.spec.ts
@@ -0,0 +1,38 @@
import { Injectable, NgModule } from '@angular/core';
import {
MockBuilder,
MockRender,
NgModuleWithProviders,
} from 'ng-mocks';

@Injectable()
class TargetService {
public readonly name = 'target';
}

@NgModule()
class TargetModule {
public static forRoot(): NgModuleWithProviders<TargetModule> {
return {
ngModule: TargetModule,
providers: [TargetService],
};
}
}

@NgModule({
imports: [TargetModule.forRoot()],
})
class AppModule {}

describe('issue-271', () => {
beforeEach(() =>
MockBuilder(null, AppModule).exclude(TargetModule),
);

it('excludes modules with providers', () => {
expect(() => MockRender(TargetService)).toThrowError(
/No provider for TargetService/,
);
});
});

0 comments on commit b5cb39c

Please sign in to comment.