From ecc4ac79d2beb1b89645925d35a6a60595ba64aa Mon Sep 17 00:00:00 2001 From: Michael Gusev Date: Sat, 9 May 2020 19:31:19 +0200 Subject: [PATCH] feat: MockHelper with find, findAll and OrFail --- README.md | 12 +++- lib/mock-helper/mock-helper.spec.ts | 106 +++++++++++++++++++++++++++- lib/mock-helper/mock-helper.ts | 51 ++++++++++++- 3 files changed, 163 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 82411de2f6..7beaa6dcb3 100644 --- a/README.md +++ b/README.md @@ -442,10 +442,18 @@ MockHelper provides 3 methods to get attribute and structural directives from an returns attribute or structural directive which belongs to current element. `MockHelper.findDirective(fixture.debugElement, Directive)` - -returns first found attribute or structural directive which belongs to current element or any child. +returns the first found attribute or structural directive which belongs to current element or any child. `MockHelper.findDirectives(fixture.debugElement, Directive)` - -returns all found attribute or structural directives which belong to current element and all its child. +returns an array of all found attribute or structural directives which belong to current element and all its child. + +`MockHelper.find(fixture.debugElement, Component)` - +returns a found DebugElement which belongs to the Component with the correctly typed componentInstance or null. + +`MockHelper.findAll(fixture.debugElement, Component)` - +returns an array of found DebugElements which belong to the Component with the correctly typed componentInstance. + +`getDirective`, `findDirective` and `find` have `OrFail` version that throws an error if the desired element wasn't found. `MockHelper.mockService(instance, methodName)` - returns a mocked function / spy of the method. If the method hasn't been mocked yet - mocks it. diff --git a/lib/mock-helper/mock-helper.spec.ts b/lib/mock-helper/mock-helper.spec.ts index 8e8f3b679e..ff3261a3a7 100644 --- a/lib/mock-helper/mock-helper.spec.ts +++ b/lib/mock-helper/mock-helper.spec.ts @@ -1,6 +1,6 @@ // tslint:disable:max-classes-per-file -import { Directive, EventEmitter, Input, Output } from '@angular/core'; +import { Component, Directive, EventEmitter, Input, Output } from '@angular/core'; import { async, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; @@ -30,10 +30,29 @@ export class ExampleStructuralDirective { @Input() exampleStructuralDirective = true; } +@Component({ + selector: 'component-a', + template: 'body-a', +}) +export class AComponent { +} + +@Component({ + selector: 'component-b', + template: 'body-b', +}) +export class BComponent { +} + describe('MockHelper:getDirective', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [MockDirective(ExampleDirective), MockDirective(ExampleStructuralDirective)], + declarations: [ + MockDirective(ExampleDirective), + MockDirective(ExampleStructuralDirective), + AComponent, + BComponent, + ], }); })); @@ -57,7 +76,7 @@ describe('MockHelper:getDirective', () => { expect(elementFromHelper).toBe(element); }); - it('should return right structural directive', () => { + it('should return right structural directive via getDirective', () => { const fixture = MockRender(`
hi
`); @@ -83,4 +102,85 @@ describe('MockHelper:getDirective', () => { // Verification. expect(elementFromHelper.exampleStructuralDirective).toEqual(false); }); + + it('should return right structural directive via getDirectiveOrFail', () => { + const fixture = MockRender(` +
hi
+ `); + + // we need to render mocked structural directives manually + MockHelper.findDirectives(fixture.debugElement, ExampleStructuralDirective).forEach( + (item: MockedDirective) => { + item.__render(); + } + ); + fixture.detectChanges(); + + // Using helper. + const elementFromHelper = MockHelper.getDirectiveOrFail( + fixture.debugElement.query(By.css('div')), + ExampleStructuralDirective + ); + + // Verification. + expect(elementFromHelper.exampleStructuralDirective).toEqual(false); + }); + + it('find selector: T', () => { + const fixture = MockRender(``); + const componentA = MockHelper.findOrFail(fixture.debugElement, AComponent); + expect(componentA.componentInstance).toEqual(jasmine.any(AComponent)); + + expect(() => MockHelper.findOrFail(componentA, BComponent)) + .toThrowError('Cannot find an element via MockHelper.findOrFail'); + }); + + it('find selector: string', () => { + const fixture = MockRender(``); + const componentB = MockHelper.findOrFail(fixture.debugElement, 'component-b'); + expect(componentB.componentInstance).toEqual(jasmine.any(BComponent)); + + expect(() => MockHelper.findOrFail(componentB, AComponent)) + .toThrowError('Cannot find an element via MockHelper.findOrFail'); + }); + + it('find selector: T', () => { + const fixture = MockRender(``); + const componentA = MockHelper.find(fixture.debugElement, AComponent); + expect(componentA && componentA.componentInstance).toEqual(jasmine.any(AComponent)); + + const componentB = MockHelper.find(fixture.debugElement, BComponent); + expect(componentB).toBe(null); // tslint:disable-line:no-null-keyword + }); + + it('find selector: string', () => { + const fixture = MockRender(``); + const componentB = MockHelper.find(fixture.debugElement, 'component-b'); + expect(componentB && componentB.componentInstance).toEqual(jasmine.any(BComponent)); + + const componentA = MockHelper.find(fixture.debugElement, 'component-a'); + expect(componentA).toBe(null); // tslint:disable-line:no-null-keyword + }); + + it('findAll selector: T', () => { + const fixture = MockRender(``); + const componentA = MockHelper.findAll(fixture.debugElement, AComponent); + expect(componentA.length).toBe(2); // tslint:disable-line:no-magic-numbers + expect(componentA[0].componentInstance).toEqual(jasmine.any(AComponent)); + expect(componentA[1].componentInstance).toEqual(jasmine.any(AComponent)); + + const componentB = MockHelper.findAll(fixture.debugElement, BComponent); + expect(componentB.length).toBe(0); + }); + + it('findAll selector: string', () => { + const fixture = MockRender(``); + const componentB = MockHelper.findAll(fixture.debugElement, 'component-b'); + expect(componentB.length).toEqual(2); // tslint:disable-line:no-magic-numbers + expect(componentB[0].componentInstance).toEqual(jasmine.any(BComponent)); + expect(componentB[0].componentInstance).toEqual(jasmine.any(BComponent)); + + const componentA = MockHelper.findAll(fixture.debugElement, 'component-a'); + expect(componentA.length).toBe(0); + }); }); diff --git a/lib/mock-helper/mock-helper.ts b/lib/mock-helper/mock-helper.ts index 910a474774..ebc9b75c69 100644 --- a/lib/mock-helper/mock-helper.ts +++ b/lib/mock-helper/mock-helper.ts @@ -1,7 +1,9 @@ /* tslint:disable:variable-name */ import { DebugNode, Type } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { DebugElementType } from '../mock-render'; import { MockedFunction, mockServiceHelper } from '../mock-service'; interface INestedNodes extends DebugNode { @@ -19,7 +21,28 @@ function nestedCheck(result: T[], node: INestedNodes, callback: (node: INeste }); } -export const MockHelper = { +export const MockHelper: { + find(debugElement: DebugElementType, component: Type): null | DebugElementType; + find(debugElement: DebugElementType, cssSelector: string): null | DebugElementType; + findAll(debugElement: DebugElementType, component: Type): Array>; + findAll(debugElement: DebugElementType, cssSelector: string): Array>; + findDirective(debugNode: DebugNode, directive: Type): undefined | T; + findDirectiveOrFail(debugNode: DebugNode, directive: Type): T; + findDirectives(debugNode: DebugNode, directive: Type): T[]; + findOrFail(debugElement: DebugElementType, component: Type): DebugElementType; + findOrFail(debugElement: DebugElementType, cssSelector: string): DebugElementType; + getDirective(debugNode: DebugNode, directive: Type): undefined | T; + getDirectiveOrFail(debugNode: DebugNode, directive: Type): T; + mockService(instance: any, name: string, style?: 'get' | 'set'): T; +} = { + getDirectiveOrFail: (debugNode: DebugNode, directive: Type): T => { + const result = MockHelper.getDirective(debugNode, directive); + if (!result) { + throw new Error(`Cannot find a directive via MockHelper.getDirectiveOrFail`); + } + return result; + }, + getDirective: (debugNode: DebugNode, directive: Type): undefined | T => { // Looking for related attribute directive. try { @@ -49,6 +72,14 @@ export const MockHelper = { } }, + findDirectiveOrFail: (debugNode: DebugNode, directive: Type): T => { + const result = MockHelper.findDirective(debugNode, directive); + if (!result) { + throw new Error(`Cannot find a directive via MockHelper.findDirectiveOrFail`); + } + return result; + }, + findDirective: (debugNode: DebugNode, directive: Type): undefined | T => { const result: T[] = []; nestedCheck(result, debugNode, node => { @@ -73,6 +104,24 @@ export const MockHelper = { return result; }, + findOrFail: (el: DebugElementType, sel: any) => { + const result = MockHelper.find(el, sel); + if (!result) { + throw new Error(`Cannot find an element via MockHelper.findOrFail`); + } + return result; + }, + + find: (el: DebugElementType, sel: any) => { + const term = typeof sel === 'string' ? By.css(sel) : By.directive(sel); + return el.query(term); + }, + + findAll: (el: DebugElementType, sel: any) => { + const term = typeof sel === 'string' ? By.css(sel) : By.directive(sel); + return el.queryAll(term); + }, + mockService: (instance: any, name: string, style?: 'get' | 'set'): T => mockServiceHelper.mock(instance, name, style), };