Skip to content

Commit

Permalink
fix(MockBuilder): configuration first, process later #5239
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Mar 26, 2023
1 parent 979d42b commit 992ef6a
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 4 deletions.
2 changes: 1 addition & 1 deletion libs/ng-mocks/src/lib/common/func.extract-deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const funcExtractDeps = (def: any, result: Set<AnyDeclaration<any>>): Set
const meta = collectDeclarations(def);
const type = getNgType(def);
// istanbul ignore if
if (!type) {
if (!type || type === 'Injectable') {
return result;
}

Expand Down
10 changes: 9 additions & 1 deletion libs/ng-mocks/src/lib/common/ng-mocks-universe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ const globalMap = (key: string) => () => {
};

interface NgMocksUniverse {
/**
* the value can be:
* - null - exclude
* - undefined - delayed initialization
* - value - the definition which should be used in tests: real value, replacement, mock.
*/
builtDeclarations: Map<any, any>;
builtProviders: Map<any, any>;
cacheDeclarations: Map<any, any>;
Expand Down Expand Up @@ -109,7 +115,9 @@ ngMocksUniverse.getBuildDeclaration = (def: any): undefined | null | any => {

ngMocksUniverse.hasBuildDeclaration = (def: any): boolean => {
if (ngMocksUniverse.builtDeclarations.has(def)) {
return true;
// undefined means that we know about this declaration,
// but its initialization is postponed at the moment.
return ngMocksUniverse.builtDeclarations.get(def) !== undefined;
}
const [mode] = getDefaults(def);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { mapValues } from '../../common/core.helpers';
import ngMocksUniverse from '../../common/ng-mocks-universe';

import tryMockDeclaration from './try-mock-declaration';
import tryMockProvider from './try-mock-provider';

export default (mockDef: Set<any>, defValue: Map<any, any>): void => {
const builtDeclarations = ngMocksUniverse.builtDeclarations;
const resolutions: Map<any, string> = ngMocksUniverse.config.get('ngMocksDepsResolution');
for (const def of mapValues(mockDef)) {
const deleteTouch = !ngMocksUniverse.touches.has(def);

resolutions.set(def, 'mock');
tryMockDeclaration(def);
builtDeclarations.set(def, undefined);
tryMockProvider(def, defValue);

if (deleteTouch) {
Expand Down
5 changes: 5 additions & 0 deletions libs/ng-mocks/src/lib/mock-builder/promise/init-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { MockModule } from '../../mock-module/mock-module';
import mockNgDef from '../../mock-module/mock-ng-def';
import collectDeclarations from '../../resolve/collect-declarations';

import tryMockDeclaration from './try-mock-declaration';

export default (
keepDef: Set<any>,
mockDef: Set<any>,
Expand Down Expand Up @@ -40,6 +42,9 @@ export default (
ngMocksUniverse.touches.delete(def);
}
}
for (const def of mapValues(mockDef)) {
tryMockDeclaration(def);
}

return loProviders;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { MockDirective } from '../../mock-directive/mock-directive';
import { MockPipe } from '../../mock-pipe/mock-pipe';

export default (def: any): void => {
if (ngMocksUniverse.builtDeclarations.get(def) !== undefined) {
return;
}

if (isNgDef(def, 'c')) {
ngMocksUniverse.builtDeclarations.set(def, MockComponent(def));
}
Expand Down
73 changes: 73 additions & 0 deletions tests-e2e/src/issue-5239/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Component } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatMenuModule } from '@angular/material/menu';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import {
isMockOf,
MockComponent,
MockRender,
ngMocks,
} from 'ng-mocks';

// A simple dependency component we are going to mock that imports the standalone pipe.
@Component({
selector: 'dependency',
template: 'dependency',
standalone: true,
imports: [MatMenuModule],
})
class DependencyComponent {
public dependency5239e2e() {}
}

// A standalone component we are going to test.
@Component({
selector: 'target',
template: `
<dependency></dependency>
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
This is the expansion title
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
Some deferred content
</ng-template>
</mat-expansion-panel>
`,
})
class TargetComponent {
public target5239e2e() {}
}

// @see https://github.com/help-me-mom/ng-mocks/issues/5239
describe('issue-5239', () => {
beforeEach(() => {
return TestBed.configureTestingModule({
declarations: [
// our component for testing
TargetComponent,
],
imports: [
NoopAnimationsModule,

// imports PortalModule, therefore it should be kept.
MatExpansionModule,

// the dependent component we want to mock,
// but internally uses MatMenuModule > OverlayModule > PortalModule,
// and mocks it, but should not.
MockComponent(DependencyComponent),
],
}).compileComponents();
});

it('renders dependencies', () => {
const fixture = MockRender(TargetComponent);
const html = ngMocks.formatHtml(fixture);
expect(html).toContain('Some deferred content');

const dependency = ngMocks.findInstance(DependencyComponent);
expect(isMockOf(dependency, DependencyComponent)).toEqual(true);
});
});

0 comments on commit 992ef6a

Please sign in to comment.