From fda714edf85db1c0378dc9ec8136524f798add3c Mon Sep 17 00:00:00 2001 From: MG Date: Mon, 28 Jun 2021 16:30:53 +0200 Subject: [PATCH] fix(core): allowing spies on ComponentFactoryResolver.resolveComponentFactory #736 --- .../promise/handle-entry-components.ts | 9 ++- .../lib/mock-service/helper.create-clone.ts | 4 +- tests/issue-736/test.spec.ts | 75 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 tests/issue-736/test.spec.ts diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/handle-entry-components.ts b/libs/ng-mocks/src/lib/mock-builder/promise/handle-entry-components.ts index c5bca6305b..a22a228b2d 100644 --- a/libs/ng-mocks/src/lib/mock-builder/promise/handle-entry-components.ts +++ b/libs/ng-mocks/src/lib/mock-builder/promise/handle-entry-components.ts @@ -13,6 +13,7 @@ import coreDefineProperty from '../../common/core.define-property'; import { extendClass } from '../../common/core.helpers'; import { NG_MOCKS } from '../../common/core.tokens'; import { isNgDef } from '../../common/func.is-ng-def'; +import helperCreateClone from '../../mock-service/helper.create-clone'; import { NgMeta } from './types'; @@ -21,8 +22,12 @@ class EntryComponentsModule { public constructor(map: Map, protected componentFactoryResolver: ComponentFactoryResolver) { this.origin = componentFactoryResolver.resolveComponentFactory; - componentFactoryResolver.resolveComponentFactory = component => - this.origin.call(componentFactoryResolver, map.get(component) ?? component) as any; + componentFactoryResolver.resolveComponentFactory = helperCreateClone( + this.origin, + undefined, + undefined, + (component: any) => this.origin.call(componentFactoryResolver, map.get(component) ?? component) as any, + ); } } coreDefineProperty(EntryComponentsModule, 'parameters', [[NG_MOCKS], [ComponentFactoryResolver]]); diff --git a/libs/ng-mocks/src/lib/mock-service/helper.create-clone.ts b/libs/ng-mocks/src/lib/mock-service/helper.create-clone.ts index d8c3fd8d24..4114b38006 100644 --- a/libs/ng-mocks/src/lib/mock-service/helper.create-clone.ts +++ b/libs/ng-mocks/src/lib/mock-service/helper.create-clone.ts @@ -3,11 +3,11 @@ import helperExtractMethodsFromPrototype from './helper.extract-methods-from-pro import helperExtractPropertiesFromPrototype from './helper.extract-properties-from-prototype'; import helperExtractPropertyDescriptor from './helper.extract-property-descriptor'; -export default (service: any, bindFrom?: object, bindTo?: object): any => { +export default (service: any, bindFrom?: object, bindTo?: object, mock?: any): any => { const instance = function () { // tslint:disable-next-line:ban-ts-ignore // @ts-ignore - return service.apply(bindFrom === this ? bindTo : this, arguments); + return (mock || service).apply(bindFrom === this ? bindTo : this, arguments); }; for (const prop of [ diff --git a/tests/issue-736/test.spec.ts b/tests/issue-736/test.spec.ts new file mode 100644 index 0000000000..6eefa6bbb3 --- /dev/null +++ b/tests/issue-736/test.spec.ts @@ -0,0 +1,75 @@ +// tslint:disable strict-type-predicates + +import { + Component, + ComponentFactoryResolver, + OnInit, +} from '@angular/core'; +import { getMockedNgDefOf, MockBuilder, MockRender } from 'ng-mocks'; + +@Component({ + selector: 'modal', + template: 'modal', +}) +class ModalComponent {} + +@Component({ + selector: 'target', + template: 'target', +}) +class TargetComponent implements OnInit { + public constructor( + public readonly componentFactoryResolver: ComponentFactoryResolver, + ) {} + + public ngOnInit(): void { + this.componentFactoryResolver.resolveComponentFactory( + ModalComponent, + ); + } +} + +describe('issue-736', () => { + beforeEach(() => + MockBuilder(TargetComponent) + .mock(ModalComponent) + .provide({ + provide: ComponentFactoryResolver, + useValue: { + resolveComponentFactory: + typeof jest !== 'undefined' + ? jest + .fn() + .mockName( + 'ComponentFactoryResolver.resolveComponentFactory', + ) + : jasmine.createSpy( + 'ComponentFactoryResolver.resolveComponentFactory', + ), + }, + }), + ); + + it('allows to mock resolveComponentFactory', () => { + // creating fixture without a render + const fixture = MockRender(TargetComponent, undefined, false); + + // getting current instance of mock ComponentFactoryResolver + const componentFactoryResolver = + fixture.debugElement.injector.get(ComponentFactoryResolver); + + // its spied resolveComponentFactory shouldn't be called + // the bug was that it is not a spy anymore. + expect( + componentFactoryResolver.resolveComponentFactory, + ).not.toHaveBeenCalled(); + + // triggering ngOnInit + fixture.detectChanges(); + + // resolveComponentFactory should have been called + expect( + componentFactoryResolver.resolveComponentFactory, + ).toHaveBeenCalledWith(getMockedNgDefOf(ModalComponent)); + }); +});