Skip to content

Commit

Permalink
fix(core): supports mocks for viewProviders #726
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jun 19, 2021
1 parent 641f002 commit 68f9946
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 5 deletions.
@@ -1,9 +1,10 @@
import { MockDirectiveResolver } from '@angular/compiler/testing';
import { Directive } from '@angular/core';
import { Component, Directive } from '@angular/core';

import coreReflectBodyCatch from './core.reflect.body-catch';
import coreReflectBodyGlobal from './core.reflect.body-global';

const coreReflectDirective = coreReflectBodyGlobal(MockDirectiveResolver);

export default (def: any): Directive => coreReflectBodyCatch((arg: any) => coreReflectDirective().resolve(arg))(def);
export default (def: any): Directive & Partial<Component> =>
coreReflectBodyCatch((arg: any) => coreReflectDirective().resolve(arg))(def);
4 changes: 2 additions & 2 deletions libs/ng-mocks/src/lib/mock-component/mock-component.ts
Expand Up @@ -202,9 +202,9 @@ coreDefineProperty(ComponentMockBase, 'parameters', [

const decorateClass = (component: Type<any>, mock: Type<any>): void => {
const meta = coreReflectDirectiveResolve(component);
const { exportAs, inputs, outputs, queries, selector, providers } = meta;
const { exportAs, inputs, outputs, queries, selector, providers, viewProviders } = meta;
const template = generateTemplate(queries);
const mockMeta = { inputs, outputs, providers, queries };
const mockMeta = { inputs, outputs, providers, viewProviders, queries };
const mockParams = { exportAs, selector, template };
Component(decorateDeclaration(component, mock, mockMeta, mockParams))(mock);
};
Expand Down
4 changes: 3 additions & 1 deletion libs/ng-mocks/src/lib/mock/decorate-declaration.ts
Expand Up @@ -36,12 +36,14 @@ export default <T extends Component | Directive>(
outputs?: string[];
providers?: Provider[];
queries?: Record<string, ViewChild>;
viewProviders?: Provider[];
},
params: T,
): T => {
const data = cloneProviders(source, mock, meta.providers || []);
const providers = [toExistingProvider(source, mock), ...data.providers];
const options: T = { ...params, providers };
const { providers: viewProviders } = cloneProviders(source, mock, meta.viewProviders || []);
const options: T = { ...params, providers, viewProviders };

if (data.setControlValueAccessor === undefined) {
data.setControlValueAccessor =
Expand Down
90 changes: 90 additions & 0 deletions tests/issue-726/test.spec.ts
@@ -0,0 +1,90 @@
import { Component, Injectable, NgModule } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { MockBuilder, MockRenderFactory, ngMocks } from 'ng-mocks';

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

@Component({
selector: 'target',
template: `{{ service.name }}`,
})
class TargetComponent {
public constructor(public readonly service: TargetService) {}
}

@Component({
selector: 'view',
template: `<ng-content></ng-content>`,
viewProviders: [TargetService],
})
class ViewComponent {}

@Component({
providers: [TargetService],
selector: 'provider',
template: `<ng-content></ng-content>`,
})
class ProviderComponent {}

@NgModule({
declarations: [TargetComponent, ViewComponent, ProviderComponent],
exports: [TargetComponent, ViewComponent, ProviderComponent],
})
class TargetModule {}

describe('issue-726', () => {
const view = MockRenderFactory(`<view><target></target></view>`);
const provider = MockRenderFactory(
`<provider><target></target></provider>`,
);
const viewComponent = MockRenderFactory(ViewComponent);

describe('TestBed', () => {
beforeEach(() =>
TestBed.configureTestingModule({
imports: [TargetModule],
}).compileComponents(),
);
beforeEach(view.configureTestBed);
beforeEach(provider.configureTestBed);
beforeEach(viewComponent.configureTestBed);

it('finds the view provider', () => {
// TargetComponent doesn't have the access to TargetService.
expect(view).toThrowError(/No provider for TargetService/);

// Container knows how to provide TargetService for its views.
expect(provider).not.toThrow();

// TargetService is accessed directly view ViewComponent.
const fixture = viewComponent();
expect(() =>
ngMocks.get(fixture.point, TargetService),
).not.toThrow();
});
});

describe('MockBuilder', () => {
beforeEach(() => MockBuilder(TargetComponent, TargetModule));
beforeEach(view.configureTestBed);
beforeEach(provider.configureTestBed);
beforeEach(viewComponent.configureTestBed);

it('finds the view provider', () => {
// TargetComponent doesn't have the access to TargetService.
expect(view).toThrowError(/No provider for TargetService/);

// Container knows how to provide TargetService for its views.
expect(provider).not.toThrow();

// TargetService is accessed directly view ViewComponent.
const fixture = viewComponent();
expect(() =>
ngMocks.get(fixture.point, TargetService),
).not.toThrow();
});
});
});

0 comments on commit 68f9946

Please sign in to comment.