Skip to content

Commit

Permalink
fix(MockBuilder): respect extention of classes with different decorat…
Browse files Browse the repository at this point in the history
…ors #2646
  • Loading branch information
satanTime committed Jun 2, 2022
1 parent c4578dc commit d069a90
Show file tree
Hide file tree
Showing 13 changed files with 369 additions and 134 deletions.
8 changes: 4 additions & 4 deletions docs/articles/guides/component-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const service = fixture.point.injector.get(TargetService);
```ts title="https://github.com/ike18t/ng-mocks/blob/master/examples/TestProviderInComponent/test.spec.ts"
import { Component, Injectable } from '@angular/core';

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

// A simple service, might have contained more logic,
// but it is redundant for the test demonstration.
Expand Down Expand Up @@ -61,9 +61,9 @@ describe('TestProviderInComponent', () => {
// to access the service.
const fixture = MockRender(TargetComponent);

// The root element is fixture.point and it is the TargetComponent
// with its injector for extracting internal services.
const service = fixture.point.injector.get(TargetService);
// The component's element is fixture.point.
// Now we can use ngMocks.get to extract internal services.
const service = ngMocks.get(fixture.point, TargetService);

// Here we go, now we can assert everything about the service.
expect(service.value).toEqual('target');
Expand Down
28 changes: 16 additions & 12 deletions docs/articles/guides/directive-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,26 +82,30 @@ describe('TestProviderInDirective', () => {
beforeEach(() => MockBuilder(TargetService, TargetDirective));

it('has access to the service via a directive', () => {
// Let's render a div with the directive. It provides a point
// to access the service.
const fixture = MockRender('<div target></div>');
// Let's render a div with the directive.
MockRender('<div target></div>');

// The root element is fixture.point and it has access to the
// context of the directive. Its injector can extract the service.
const service = fixture.point.injector.get(TargetService);
// Let's find the debugElement with the directive.
// Please note, that we use ngMocks.find here.
const el = ngMocks.find(TargetDirective);

// Let's extract the service.
const service = ngMocks.get(el, TargetService);

// Here we go, now we can assert everything about the service.
expect(service.value).toEqual(true);
});

it('has access to the service via a structural directive', () => {
// Let's render a div with the directive. It provides a point to
// access the service.
const fixture = MockRender('<div *target></div>');
// Let's render a div with the directive.
MockRender('<div *target></div>');

// Let's find the debugNode with the directive.
// Please note, that we use ngMocks.reveal here.
const node = ngMocks.reveal(TargetDirective);

// The root element is fixture.point and it has access to the
// context of the directive. Its injector can extract the service.
const service = fixture.point.injector.get(TargetService);
// Let's extract the service.
const service = ngMocks.get(node, TargetService);

// Here we go, now we can assert everything about the service.
expect(service.value).toEqual(true);
Expand Down
8 changes: 4 additions & 4 deletions examples/TestProviderInComponent/test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Injectable } from '@angular/core';

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

// A simple service, might have contained more logic,
// but it is redundant for the test demonstration.
Expand Down Expand Up @@ -31,9 +31,9 @@ describe('TestProviderInComponent', () => {
// to access the service.
const fixture = MockRender(TargetComponent);

// The root element is fixture.point and it is the TargetComponent
// with its injector for extracting internal services.
const service = fixture.point.injector.get(TargetService);
// The component's element is fixture.point.
// Now we can use ngMocks.get to extract internal services.
const service = ngMocks.get(fixture.point, TargetService);

// Here we go, now we can assert everything about the service.
expect(service.value).toEqual('target');
Expand Down
28 changes: 16 additions & 12 deletions examples/TestProviderInDirective/test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,30 @@ describe('TestProviderInDirective', () => {
beforeEach(() => MockBuilder(TargetService, TargetDirective));

it('has access to the service via a directive', () => {
// Let's render a div with the directive. It provides a point
// to access the service.
const fixture = MockRender('<div target></div>');
// Let's render a div with the directive.
MockRender('<div target></div>');

// The root element is fixture.point and it has access to the
// context of the directive. Its injector can extract the service.
const service = fixture.point.injector.get(TargetService);
// Let's find the debugElement with the directive.
// Please note, that we use ngMocks.find here.
const el = ngMocks.find(TargetDirective);

// Let's extract the service.
const service = ngMocks.get(el, TargetService);

// Here we go, now we can assert everything about the service.
expect(service.value).toEqual(true);
});

it('has access to the service via a structural directive', () => {
// Let's render a div with the directive. It provides a point to
// access the service.
const fixture = MockRender('<div *target></div>');
// Let's render a div with the directive.
MockRender('<div *target></div>');

// Let's find the debugNode with the directive.
// Please note, that we use ngMocks.reveal here.
const node = ngMocks.reveal(TargetDirective);

// The root element is fixture.point and it has access to the
// context of the directive. Its injector can extract the service.
const service = fixture.point.injector.get(TargetService);
// Let's extract the service.
const service = ngMocks.get(node, TargetService);

// Here we go, now we can assert everything about the service.
expect(service.value).toEqual(true);
Expand Down
4 changes: 0 additions & 4 deletions libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { isNgModuleDefWithProviders } from '../common/func.is-ng-module-def-with
import ngMocksUniverse from '../common/ng-mocks-universe';

import { MockBuilderStash } from './mock-builder-stash';
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 applyPlatformModules from './promise/apply-platform-modules';
import createNgMocksOverridesToken from './promise/create-ng-mocks-overrides-token';
Expand Down Expand Up @@ -80,8 +78,6 @@ export class MockBuilderPromise implements IMockBuilder {

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

This file was deleted.

This file was deleted.

This file was deleted.

31 changes: 21 additions & 10 deletions libs/ng-mocks/src/lib/mock-builder/promise/init-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { isNgDef } from '../../common/func.is-ng-def';
import ngMocksUniverse from '../../common/ng-mocks-universe';
import { MockModule } from '../../mock-module/mock-module';
import mockNgDef from '../../mock-module/mock-ng-def';
import collectDeclarations from '../../resolve/collect-declarations';

export default (
keepDef: Set<any>,
Expand All @@ -13,21 +14,31 @@ export default (
const loProviders = new Map();

for (const def of [...mapValues(keepDef), ...mapValues(mockDef), ...mapValues(replaceDef)]) {
if (!isNgDef(def, 'm')) {
continue;
const meta = collectDeclarations(def);
const providers = [
...(defProviders.get(def) ?? []),
...(meta.Component?.providers ?? []),
...(meta.Directive?.providers ?? []),
];

const deleteTouch = !ngMocksUniverse.touches.has(def);
if (!mockDef.has(def)) {
ngMocksUniverse.flags.add('skipMock');
}

if (defProviders.has(def)) {
if (!mockDef.has(def)) {
ngMocksUniverse.flags.add('skipMock');
}
const [, loDef] = mockNgDef({ providers: defProviders.get(def) });
const isModule = isNgDef(def, 'm');
if (providers.length > 0) {
const [, loDef] = mockNgDef({ providers, skipMarkProviders: !isModule });
loProviders.set(def, loDef.providers);
ngMocksUniverse.flags.delete('skipMock');
}
if (isModule) {
ngMocksUniverse.builtDeclarations.set(def, MockModule(def));
}

ngMocksUniverse.builtDeclarations.set(def, MockModule(def));
ngMocksUniverse.touches.delete(def);
ngMocksUniverse.flags.delete('skipMock');
if (deleteTouch) {
ngMocksUniverse.touches.delete(def);
}
}

return loProviders;
Expand Down
35 changes: 28 additions & 7 deletions libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { flatten, mapValues } from '../../common/core.helpers';
import funcGetProvider from '../../common/func.get-provider';
import { isNgDef } from '../../common/func.is-ng-def';
import ngMocksUniverse from '../../common/ng-mocks-universe';
import markProviders from '../../mock-module/mark-providers';

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

const handleDef = ({ imports, declarations }: NgMeta, def: any, defProviders: Map<any, any>): void => {
const handleDef = ({ imports, declarations, providers }: NgMeta, def: any, defProviders: Map<any, any>): void => {
let touched = false;

if (isNgDef(def, 'm')) {
const extendedDef = initModule(def, defProviders);
imports.push(extendedDef);
Expand All @@ -18,22 +20,41 @@ const handleDef = ({ imports, declarations }: NgMeta, def: any, defProviders: Ma
ngMocksUniverse.touches.add(funcGetProvider(provider));
}
}
} else {
touched = true;
}

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

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

ngMocksUniverse.touches.add(def);
if (touched) {
ngMocksUniverse.touches.add(def);
}
};

export default ({ configDef, keepDef, mockDef, replaceDef }: BuilderData, defProviders: Map<any, any>): NgMeta => {
const meta: NgMeta = { imports: [], declarations: [], providers: [] };

// Adding suitable leftovers.
for (const def of [...mapValues(mockDef), ...mapValues(keepDef), ...mapValues(replaceDef)]) {
if (skipInitModule(def, configDef)) {
continue;
const configInstance = ngMocksUniverse.configInstance.get(def);
const config = configDef.get(def);

if (!config?.dependency && config?.export && !configInstance?.exported && (isNgDef(def, 'i') || !isNgDef(def))) {
handleDef(meta, def, defProviders);
markProviders([def]);
} else if (!ngMocksUniverse.touches.has(def) && !config?.dependency) {
handleDef(meta, def, defProviders);
}
handleDef(meta, def, defProviders);
}

return meta;
Expand Down
17 changes: 0 additions & 17 deletions libs/ng-mocks/src/lib/mock-builder/promise/skip-init-module.ts

This file was deleted.

17 changes: 13 additions & 4 deletions libs/ng-mocks/src/lib/mock-module/mock-ng-def.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ const configureProcessMetaKeys = (
];

const processMeta = (
ngModule: Partial<NgModule & Component & Directive & Pipe>,
ngModule: Partial<NgModule & Component & Directive & Pipe> & {
skipMarkProviders?: boolean;
},
resolve: (def: any) => any,
resolveProvider: (def: Provider) => any,
): NgModule => {
Expand All @@ -52,8 +54,10 @@ const processMeta = (
mockModuleDef[key] = flatToExisting(ngModule[key], callback);
}
}
markProviders(mockModuleDef.providers);
markProviders(mockModuleDef.viewProviders);
if (!ngModule.skipMarkProviders) {
markProviders(mockModuleDef.providers);
markProviders(mockModuleDef.viewProviders);
}

if (!cachePipe) {
ngMocksUniverse.flags.delete('cachePipe');
Expand Down Expand Up @@ -116,7 +120,12 @@ const addExports = (
}
};

export default (ngModuleDef: NgModule, ngModule?: Type<any>): [boolean, NgModule] => {
export default (
ngModuleDef: NgModule & {
skipMarkProviders?: boolean;
},
ngModule?: Type<any>,
): [boolean, NgModule] => {
const hasResolver = ngMocksUniverse.config.has('mockNgDefResolver');
if (!hasResolver) {
ngMocksUniverse.config.set('mockNgDefResolver', new Map());
Expand Down
Loading

0 comments on commit d069a90

Please sign in to comment.