Skip to content

Commit

Permalink
fix(mock-builder): provides globally exported providers from directiv…
Browse files Browse the repository at this point in the history
…es and components #623
  • Loading branch information
satanTime committed Jun 19, 2021
1 parent 1d15570 commit 58ee0d8
Show file tree
Hide file tree
Showing 11 changed files with 450 additions and 149 deletions.
8 changes: 4 additions & 4 deletions libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts
Expand Up @@ -8,8 +8,8 @@ import { isNgModuleDefWithProviders } from '../common/func.is-ng-module-def-with
import ngMocksUniverse from '../common/ng-mocks-universe';

import { MockBuilderStash } from './mock-builder-stash';
import addMissedKeepDeclarationsAndModules from './promise/add-missed-keep-declarations-and-modules';
import addMissedMockDeclarationsAndModules from './promise/add-missed-mock-declarations-and-modules';
import addMissingKeepDeclarationsAndModules from './promise/add-missing-keep-declarations-and-modules';
import addMissingMockDeclarationsAndModules from './promise/add-missing-mock-declarations-and-modules';
import addRequestedProviders from './promise/add-requested-providers';
import createNgMocksOverridesToken from './promise/create-ng-mocks-overrides-token';
import createNgMocksToken from './promise/create-ng-mocks-token';
Expand Down Expand Up @@ -75,8 +75,8 @@ export class MockBuilderPromise implements IMockBuilder {
const params = this.combineParams();

const ngModule = initNgModules(params, initUniverse(params));
addMissedKeepDeclarationsAndModules(ngModule, params);
addMissedMockDeclarationsAndModules(ngModule, params);
addMissingKeepDeclarationsAndModules(ngModule, params);
addMissingMockDeclarationsAndModules(ngModule, params);
addRequestedProviders(ngModule, params);
handleRootProviders(ngModule, params);
handleEntryComponents(ngModule);
Expand Down
3 changes: 2 additions & 1 deletion libs/ng-mocks/src/lib/mock-builder/mock-builder.ts
Expand Up @@ -225,7 +225,8 @@ export function MockBuilder(
}
if (itsModuleToMock) {
for (const declaration of flatten(itsModuleToMock)) {
instance.mock(declaration, {
instance.mock(declaration, declaration, {
export: true,
exportAll: true,
});
}
Expand Down
@@ -0,0 +1,20 @@
import { isNgDef } from '../../common/func.is-ng-def';
import ngMocksUniverse from '../../common/ng-mocks-universe';

export default (def: any, configDef: Map<any, any>): boolean => {
if (!isNgDef(def, 'i') && isNgDef(def)) {
return true;
}

const config = configDef.get(def);
if (config?.dependency) {
return true;
}

const configInstance = ngMocksUniverse.configInstance.get(def);
if (ngMocksUniverse.touches.has(def) && (configInstance?.exported || !config?.export)) {
return true;
}

return false;
};
@@ -1,23 +1,14 @@
import { mapValues } from '../../common/core.helpers';
import { isNgDef } from '../../common/func.is-ng-def';
import { isNgInjectionToken } from '../../common/func.is-ng-injection-token';
import ngMocksUniverse from '../../common/ng-mocks-universe';

import addMissingDefinition from './add-missing-definition';
import { BuilderData, NgMeta } from './types';

export default (ngModule: NgMeta, { keepDef, configDef }: BuilderData): void => {
// Adding missed kept providers to test bed.
for (const def of mapValues(keepDef)) {
if (!isNgDef(def, 'i') && isNgDef(def)) {
continue;
}

if (ngMocksUniverse.touches.has(def)) {
continue;
}

const config = configDef.get(def);
if (config && config.dependency) {
if (addMissingDefinition(def, configDef)) {
continue;
}

Expand Down
@@ -1,22 +1,13 @@
import { mapValues } from '../../common/core.helpers';
import { isNgDef } from '../../common/func.is-ng-def';
import ngMocksUniverse from '../../common/ng-mocks-universe';

import addMissingDefinition from './add-missing-definition';
import { BuilderData, NgMeta } from './types';

export default (ngModule: NgMeta, { mockDef, configDef }: BuilderData): void => {
// Adding missed mock providers to test bed.
for (const def of mapValues(mockDef)) {
if (!isNgDef(def, 'i') && isNgDef(def)) {
continue;
}

if (ngMocksUniverse.touches.has(def)) {
continue;
}

const config = configDef.get(def);
if (config && config.dependency) {
if (addMissingDefinition(def, configDef)) {
continue;
}

Expand Down
Expand Up @@ -11,7 +11,7 @@ export default (
} => {
let mock: any = def === a1 ? defaultMockValue : a1;
let config: any = a2 ? a2 : a1 !== defaultMockValue ? a1 : undefined;
if (isNgDef(def, 'p') && typeof a1 === 'function') {
if (isNgDef(def, 'p') && typeof a1 === 'function' && a1 !== def) {
mock = a1;
config = a2;
} else if (isNgDef(def, 'i') || !isNgDef(def)) {
Expand Down
126 changes: 126 additions & 0 deletions libs/ng-mocks/src/lib/mock-module/create-resolvers.ts
@@ -0,0 +1,126 @@
import { Provider } from '@angular/core';

import { isNgDef } from '../common/func.is-ng-def';
import { isNgModuleDefWithProviders } from '../common/func.is-ng-module-def-with-providers';
import ngMocksUniverse from '../common/ng-mocks-universe';
import { MockComponent } from '../mock-component/mock-component';
import { MockDirective } from '../mock-directive/mock-directive';
import { MockPipe } from '../mock-pipe/mock-pipe';
import helperMockService from '../mock-service/helper.mock-service';

import { MockModule } from './mock-module';

// tslint:disable-next-line variable-name
let BrowserAnimationsModule: any;
// tslint:disable-next-line variable-name
let NoopAnimationsModule: any;
// istanbul ignore next
let replaceWithNoop: (def: any) => boolean = () => false;
try {
// tslint:disable-next-line no-require-imports no-var-requires
const imports = require('@angular/platform-browser/animations');
BrowserAnimationsModule = imports.BrowserAnimationsModule;
NoopAnimationsModule = imports.NoopAnimationsModule;
replaceWithNoop = (def: any) =>
def === BrowserAnimationsModule &&
!!BrowserAnimationsModule &&
!!NoopAnimationsModule &&
!ngMocksUniverse.getResolution(def);
} catch {
// nothing to do
}

const processDefMap: Array<[any, any]> = [
['c', MockComponent],
['d', MockDirective],
['p', MockPipe],
];

const processDef = (def: any) => {
// BrowserAnimationsModule is a very special case.
// If it is not resolved manually, we simply replace it with NoopAnimationsModule.
if (replaceWithNoop(def)) {
return NoopAnimationsModule;
}

if (isNgDef(def, 'm') || isNgModuleDefWithProviders(def)) {
return MockModule(def as any);
}
if (ngMocksUniverse.hasBuildDeclaration(def)) {
return ngMocksUniverse.getBuildDeclaration(def);
}
if (ngMocksUniverse.flags.has('skipMock') && ngMocksUniverse.getResolution(def) !== 'mock') {
return def;
}
for (const [flag, func] of processDefMap) {
if (isNgDef(def, flag)) {
return func(def);
}
}
};

// resolveProvider is a special case because of the def structure.
const createResolveProvider =
(resolutions: Map<any, any>, change: () => void): ((def: Provider) => any) =>
(def: Provider) =>
helperMockService.resolveProvider(def, resolutions, change);

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 createResolveExisting(def, resolutions, change);
}

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

const mockDef = processDef(def);
if (createResolveWithProviders(def, mockDef)) {
resolutions.set(def.ngModule, mockDef.ngModule);
}
if (ngMocksUniverse.flags.has('skipMock')) {
ngMocksUniverse.config.get('ngMocksDepsSkip')?.add(mockDef);
}
resolutions.set(def, mockDef);
change(mockDef !== def);

return mockDef;
};

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

return {
resolve,
resolveProvider,
};
};
13 changes: 13 additions & 0 deletions libs/ng-mocks/src/lib/mock-module/mark-providers.ts
@@ -0,0 +1,13 @@
import { flatten } from '../common/core.helpers';
import funcGetProvider from '../common/func.get-provider';
import ngMocksUniverse from '../common/ng-mocks-universe';

export default (providers?: any[]): void => {
for (const provider of flatten(providers ?? [])) {
const provide = funcGetProvider(provider);

const config = ngMocksUniverse.configInstance.get(provide) ?? {};
config.exported = true;
ngMocksUniverse.configInstance.set(provide, config);
}
};

0 comments on commit 58ee0d8

Please sign in to comment.