Skip to content

Commit

Permalink
feat: MockHelper with find, findAll and OrFail
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed May 9, 2020
1 parent a319c38 commit ecc4ac7
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 6 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
106 changes: 103 additions & 3 deletions lib/mock-helper/mock-helper.spec.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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,
],
});
}));

Expand All @@ -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(`
<div id="example-structural-directive" *exampleStructuralDirective="false">hi</div>
`);
Expand All @@ -83,4 +102,85 @@ describe('MockHelper:getDirective', () => {
// Verification.
expect(elementFromHelper.exampleStructuralDirective).toEqual(false);
});

it('should return right structural directive via getDirectiveOrFail', () => {
const fixture = MockRender(`
<div id="example-structural-directive" *exampleStructuralDirective="false">hi</div>
`);

// we need to render mocked structural directives manually
MockHelper.findDirectives(fixture.debugElement, ExampleStructuralDirective).forEach(
(item: MockedDirective<ExampleStructuralDirective>) => {
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(`<component-a></component-a>`);
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(`<component-b></component-b>`);
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(`<component-a></component-a>`);
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(`<component-b></component-b>`);
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(`<component-a></component-a><component-a></component-a>`);
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(`<component-b></component-b><component-b></component-b>`);
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);
});
});
51 changes: 50 additions & 1 deletion lib/mock-helper/mock-helper.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -19,7 +21,28 @@ function nestedCheck<T>(result: T[], node: INestedNodes, callback: (node: INeste
});
}

export const MockHelper = {
export const MockHelper: {
find<T>(debugElement: DebugElementType<any>, component: Type<T>): null | DebugElementType<T>;
find(debugElement: DebugElementType<any>, cssSelector: string): null | DebugElementType<any>;
findAll<T>(debugElement: DebugElementType<any>, component: Type<T>): Array<DebugElementType<T>>;
findAll(debugElement: DebugElementType<any>, cssSelector: string): Array<DebugElementType<any>>;
findDirective<T>(debugNode: DebugNode, directive: Type<T>): undefined | T;
findDirectiveOrFail<T>(debugNode: DebugNode, directive: Type<T>): T;
findDirectives<T>(debugNode: DebugNode, directive: Type<T>): T[];
findOrFail<T>(debugElement: DebugElementType<any>, component: Type<T>): DebugElementType<T>;
findOrFail(debugElement: DebugElementType<any>, cssSelector: string): DebugElementType<any>;
getDirective<T>(debugNode: DebugNode, directive: Type<T>): undefined | T;
getDirectiveOrFail<T>(debugNode: DebugNode, directive: Type<T>): T;
mockService<T = MockedFunction>(instance: any, name: string, style?: 'get' | 'set'): T;
} = {
getDirectiveOrFail: <T>(debugNode: DebugNode, directive: Type<T>): T => {
const result = MockHelper.getDirective(debugNode, directive);
if (!result) {
throw new Error(`Cannot find a directive via MockHelper.getDirectiveOrFail`);
}
return result;
},

getDirective: <T>(debugNode: DebugNode, directive: Type<T>): undefined | T => {
// Looking for related attribute directive.
try {
Expand Down Expand Up @@ -49,6 +72,14 @@ export const MockHelper = {
}
},

findDirectiveOrFail: <T>(debugNode: DebugNode, directive: Type<T>): T => {
const result = MockHelper.findDirective(debugNode, directive);
if (!result) {
throw new Error(`Cannot find a directive via MockHelper.findDirectiveOrFail`);
}
return result;
},

findDirective: <T>(debugNode: DebugNode, directive: Type<T>): undefined | T => {
const result: T[] = [];
nestedCheck<T>(result, debugNode, node => {
Expand All @@ -73,6 +104,24 @@ export const MockHelper = {
return result;
},

findOrFail: (el: DebugElementType<any>, 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<any>, sel: any) => {
const term = typeof sel === 'string' ? By.css(sel) : By.directive(sel);
return el.query(term);
},

findAll: (el: DebugElementType<any>, sel: any) => {
const term = typeof sel === 'string' ? By.css(sel) : By.directive(sel);
return el.queryAll(term);
},

mockService: <T = MockedFunction>(instance: any, name: string, style?: 'get' | 'set'): T =>
mockServiceHelper.mock(instance, name, style),
};

0 comments on commit ecc4ac7

Please sign in to comment.