Skip to content

Commit

Permalink
fix(MockBuilder): add undecorated classes to providers #2845
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jun 19, 2022
1 parent 7422c10 commit 29b6591
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 24 deletions.
1 change: 1 addition & 0 deletions libs/ng-mocks/src/lib/common/func.is-ng-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AnyType } from './core.types';
* isNgType(MockModule, 'NgModule'); // returns true
* isNgType(RealComponent, 'Component'); // returns true
* isNgType(ArbitraryClass, 'Directive'); // returns false
* isNgType(ArbitraryClass, 'Injectable'); // returns false
* ```
*/
export const isNgType = (declaration: AnyType<any>, type: string): boolean => {
Expand Down
2 changes: 0 additions & 2 deletions libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import applyPlatformModules from './promise/apply-platform-modules';
import createNgMocksOverridesToken from './promise/create-ng-mocks-overrides-token';
import createNgMocksToken from './promise/create-ng-mocks-token';
import createNgMocksTouchesToken from './promise/create-ng-mocks-touches-token';
import detectWrongDeclarations from './promise/detect-wrong-declarations';
import handleEntryComponents from './promise/handle-entry-components';
import handleRootProviders from './promise/handle-root-providers';
import initNgModules from './promise/init-ng-modules';
Expand Down Expand Up @@ -77,7 +76,6 @@ export class MockBuilderPromise implements IMockBuilder {
const params = this.combineParams();

const ngModule = initNgModules(params, initUniverse(params));
detectWrongDeclarations(params);
addRequestedProviders(ngModule, params);
handleRootProviders(ngModule, params);
handleEntryComponents(ngModule);
Expand Down

This file was deleted.

15 changes: 12 additions & 3 deletions libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,52 @@
import { flatten, mapValues } from '../../common/core.helpers';
import coreReflectProvidedIn from '../../common/core.reflect.provided-in';
import { AnyDeclaration } from '../../common/core.types';
import errorJestMock from '../../common/error.jest-mock';
import funcGetName from '../../common/func.get-name';
import funcGetProvider from '../../common/func.get-provider';
import { isNgDef } from '../../common/func.is-ng-def';
import { isNgInjectionToken } from '../../common/func.is-ng-injection-token';
import { isStandalone } from '../../common/func.is-standalone';
import ngMocksUniverse from '../../common/ng-mocks-universe';
import markProviders from '../../mock-module/mark-providers';

import initModule from './init-module';
import { BuilderData, NgMeta } from './types';

const skipDef = (def: any): boolean =>
ngMocksUniverse.touches.has(def) || isNgDef(def) || isNgInjectionToken(def) || typeof def === 'string';

const handleDef = ({ imports, declarations, providers }: NgMeta, def: any, defProviders: Map<any, any>): void => {
if (!skipDef(def)) {
errorJestMock(def);
}

let touched = false;

if (isNgDef(def, 'm')) {
const extendedDef = initModule(def, defProviders);
imports.push(extendedDef);
touched = true;

// adding providers to touches
if (typeof extendedDef === 'object' && extendedDef.providers) {
for (const provider of flatten(extendedDef.providers)) {
ngMocksUniverse.touches.add(funcGetProvider(provider));
}
}
touched = true;
}

if (isNgDef(def, 'c') || isNgDef(def, 'd') || isNgDef(def, 'p')) {
(isStandalone(def) ? imports : declarations).push(ngMocksUniverse.getBuildDeclaration(def));
touched = true;
}

if (isNgDef(def, 'i') || isNgDef(def, 't') || typeof def === 'string') {
if (isNgDef(def, 'i') || !isNgDef(def)) {
const mock = ngMocksUniverse.builtProviders.get(def);
if (mock && typeof mock !== 'string' && isNgDef(mock, 't') === false) {
providers.push(mock);
touched = true;
}
touched = true;
}

if (touched) {
Expand Down
30 changes: 30 additions & 0 deletions tests/issue-2845/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Component, Optional } from '@angular/core';

import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';

// Like ActivatedRoute, it's not decorated too.
class UndecoratedService {}

@Component({
selector: 'target',
template: `{{ service ? 'provided' : 'missing' }}`,
})
class TargetComponent {
constructor(
@Optional() public readonly service: UndecoratedService,
) {}
}

// @see https://github.com/ike18t/ng-mocks/issues/2845
// MockBuilder should add undecorated classes, such as ActivatedRoute, to providers.
describe('issue-2845', () => {
beforeEach(() =>
MockBuilder(TargetComponent).mock(UndecoratedService),
);

it('provides UndecoratedService', () => {
const fixture = MockRender(TargetComponent);

expect(ngMocks.formatText(fixture)).toEqual('provided');
});
});

0 comments on commit 29b6591

Please sign in to comment.