Commit 6d10324a90f16eccd759ac21919c5390c803f85e

Authored by Carlos Purificação
1 parent efa64a13

People block refactoring. Created component-test-helper

src/app/layout/blocks/members-block/members-block.component.spec.ts
1 1 import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2 2 import {Provider, Input, provide, Component} from 'ng-forward';
3   -
4 3 import {MembersBlockComponent} from './members-block.component';
  4 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
5 5  
6 6 const htmlTemplate: string = '<noosfero-members-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-members-block>';
7 7  
... ... @@ -10,13 +10,9 @@ const tcb = new TestComponentBuilder();
10 10 describe("Components", () => {
11 11 describe("Members Block Component", () => {
12 12  
13   - beforeEach(angular.mock.module("templates"));
  13 + let helper: ComponentTestHelper;
14 14  
15   - let state = jasmine.createSpyObj("state", ["go"]);
16 15 let providers = [
17   - new Provider('truncateFilter', { useValue: () => { } }),
18   - new Provider('stripTagsFilter', { useValue: () => { } }),
19   - new Provider('$state', { useValue: state }),
20 16 new Provider('ProfileService', {
21 17 useValue: {
22 18 getProfileMembers: (profileId: number, filters: any): any => {
... ... @@ -25,28 +21,23 @@ describe(&quot;Components&quot;, () =&gt; {
25 21 }
26 22 }),
27 23 ];
28   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [MembersBlockComponent], providers: providers })
29   - class BlockContainerComponent {
30   - block = { type: 'Block', settings: {} };
31   - owner = { name: 'profile-name' };
32   - constructor() {
33   - }
34   - }
35   -
36   - it("get members of the block owner", done => {
37   - tcb.createAsync(BlockContainerComponent).then(fixture => {
38   - let block: MembersBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
39   - expect(block.members).toEqual([{ identifier: "person1" }]);
40   - done();
41   - });
  24 +
  25 + beforeEach(angular.mock.module("templates"));
  26 +
  27 + beforeEach( (done) => {
  28 + // Custom properties for the component
  29 + let properties = { owner: { id: 1 } };
  30 + // Create the component bed for the test.
  31 + let cls = createClass(htmlTemplate, [MembersBlockComponent], providers, properties);
  32 + helper = new ComponentTestHelper(cls, done);
  33 + });
  34 +
  35 + it("get members of the block owner", () => {
  36 + expect(helper.component.members[0].identifier).toEqual("person1");
42 37 });
43 38  
44   - it("render the profile image for each member", done => {
45   - tcb.createAsync(BlockContainerComponent).then(fixture => {
46   - fixture.debugElement.getLocal("$rootScope").$apply();
47   - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1);
48   - done();
49   - });
  39 + it("render the profile image for each member", () => {
  40 + expect(helper.all("noosfero-profile-image").length).toEqual(1);
50 41 });
51 42  
52 43 });
... ...
src/app/layout/blocks/people-block/people-block.component.spec.ts
1 1 import {TestComponentBuilder} from 'ng-forward/cjs/testing/test-component-builder';
2   -import {Provider, Input, provide, Component} from 'ng-forward';
3   -
  2 +import {Provider, provide} from 'ng-forward';
  3 +import {ComponentTestHelper, createClass} from './../../../../spec/component-test-helper';
  4 +import {providers} from 'ng-forward/cjs/testing/providers';
1
4 5 import {PeopleBlockComponent} from './people-block.component';
5 6  
6   -const htmlTemplate: string = '<noosfero-people-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-people-block>';
  7 +import { INgForwardJQuery } from "ng-forward/cjs/util/jqlite-extensions";
7 8  
8   -const tcb = new TestComponentBuilder();
  9 +const htmlTemplate: string = '<noosfero-people-block [block]="ctrl.block" [owner]="ctrl.owner"></noosfero-people-block>';
9 10  
10 11 describe("Components", () => {
11   - describe("People Block Component", () => {
12 12  
13   - beforeEach(angular.mock.module("templates"));
14   -
15   - let state = jasmine.createSpyObj("state", ["go"]);
16   - let providers = [
17   - new Provider('truncateFilter', { useValue: () => { } }),
18   - new Provider('stripTagsFilter', { useValue: () => { } }),
19   - new Provider('$state', { useValue: state }),
20   - new Provider('EnvironmentService', {
21   - useValue: {
  13 + describe("People Block Component", () => {
  14 + let serviceMock = {
2
  • Me
    Michel Felipe @mfdeveloper

    Why did you use this mock here? Did you not create a reusable environmentService into mocks.ts file? You need choice which these implementations will be here!

    Choose File ...   File name...
    Cancel
  • 2c5c4299d62769e3da7d432cd2823dd6?s=40&d=identicon
    Carlos Purificação @carloseugenio

    The mock in mocks.ts it is an empty generic mock, like others in this file. It does not return a value and this test needs one value. I think it is more direct approach then using the promiseResultTemplate in this test.

    Choose File ...   File name...
    Cancel
22 15 getEnvironmentPeople: (filters: any): any => {
23 16 return Promise.resolve([{ identifier: "person1" }]);
24 17 }
25   - }
26   - }),
27   - ];
28   - @Component({ selector: 'test-container-component', template: htmlTemplate, directives: [PeopleBlockComponent], providers: providers })
29   - class BlockContainerComponent {
30   - block = { type: 'Block', settings: {} };
31   - owner = { name: 'profile-name' };
32   - constructor() {
33   - }
34   - }
35   -
36   - it("get people of the block owner", done => {
37   - tcb.createAsync(BlockContainerComponent).then(fixture => {
38   - let block: PeopleBlockComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
39   - expect(block.people).toEqual([{ identifier: "person1" }]);
40   - done();
41   - });
  18 + };
  19 + let providers = [new Provider('EnvironmentService', { useValue: serviceMock })];
  20 +
  21 + let helper: ComponentTestHelper;
  22 +
  23 + beforeEach( angular.mock.module("templates") );
  24 +
  25 + /**
  26 + * The beforeEach procedure will initialize the helper and parse
  27 + * the component according to the given providers. Unfortunetly, in
  28 + * this mode, the providers and properties given to the construtor
  29 + * can't be overriden.
  30 + */
  31 + beforeEach( (done) => {
  32 + // Create the component bed for the test. Optionally, this could be done
  33 + // in each test if one needs customization of these parameters per test
  34 + let cls = createClass(htmlTemplate, [PeopleBlockComponent], providers, {});
  35 + helper = new ComponentTestHelper(cls, done);
  36 + });
  37 +
  38 + /**
  39 + * By default the helper will have the component, with all properties
  40 + * ready to be used. Here the mock provider 'EnvironmentService' will
  41 + * return the given array with one person.
  42 + */
  43 + it("get block with one people", () => {
  44 + expect(helper.component.people[0].identifier).toEqual("person1");
42 45 });
43 46  
44   - it("render the profile image for each person", done => {
45   - tcb.createAsync(BlockContainerComponent).then(fixture => {
46   - fixture.debugElement.getLocal("$rootScope").$apply();
47   - expect(fixture.debugElement.queryAll("noosfero-profile-image").length).toEqual(1);
48   - done();
49   - });
  47 + /**
  48 + * There are helper functions to access the JQuery DOM like this.
  49 + */
  50 + it("render the profile image for each person", () => {
  51 + expect(helper.all("noosfero-profile-image").length).toEqual(1);
50 52 });
51 53  
  54 + /**
  55 + * The main debugElement element is also available
  56 + */
  57 + it("render the main noosfero people block", () => {
  58 + expect(helper.debugElement.children().length).toEqual(1, "The people-block should have a div children");
  59 + });
  60 +
  61 + /**
  62 + * Just another example of a JQuery DOM helper function
  63 + */
  64 + it("render the noosfero people block div", () => {
  65 + let div = helper.findChildren("noosfero-people-block", "div");
  66 + expect(div.className).toBe('people-block', "The class should be people-block");
  67 + });
52 68 });
53 69 });
54 70 \ No newline at end of file
... ...
src/app/layout/blocks/people-block/people-block.component.ts
... ... @@ -10,6 +10,7 @@ export class PeopleBlockComponent {
10 10  
11 11 @Input() block: noosfero.Block;
12 12 @Input() owner: noosfero.Environment;
  13 + private type: string = "people";
13 14  
14 15 people: noosfero.Person[] = [];
15 16  
... ... @@ -21,4 +22,5 @@ export class PeopleBlockComponent {
21 22 this.people = people;
22 23 });
23 24 }
  25 +
24 26 }
... ...
src/app/layout/blocks/people-block/people-block.html
1   -<div class="peoples-block">
  1 +<div class="{{ctrl.type}}-block">
2 2 <a ng-repeat="person in ctrl.people" ui-sref="main.profile.home({profile: person.identifier})" class="person">
3 3 <noosfero-profile-image [profile]="person"></noosfero-profile-image>
4 4 </a>
... ...
src/app/profile/data/profile-data.component.spec.ts
... ... @@ -52,7 +52,7 @@ describe(&#39;Profile data component&#39;, () =&gt; {
52 52 profileMock.additional_data = {
53 53 'Address': 'Street A, Number 102'
54 54 };
55   -
  55 +
56 56 buildComponent().then((fixture: ComponentFixture) => {
57 57 let profileData: ProfileDataComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
58 58 profileData.profile = profileMock;
... ...
src/spec/component-test-helper.ts 0 → 100644
... ... @@ -0,0 +1,74 @@
  1 +import { Component } from "ng-forward";
  2 +import { TestComponentBuilder } from 'ng-forward/cjs/testing/test-component-builder';
  3 +import { INgForwardJQuery } from "ng-forward/cjs/util/jqlite-extensions";
  4 +import { ComponentFixture } from 'ng-forward/cjs/testing/test-component-builder';
  5 +
  6 +export function createClass(template: any, directives: any, providers: any, properties: any): any {
1
  • Me
    Michel Felipe @mfdeveloper

    This shorthand createClass function needs a refactory into your parameters declaration. In this case, will be more legibility if you define like key => value params. Please, see the quickCreateComponent() implementation on helpers.ts file :)

    Choose File ...   File name...
    Cancel
  7 + @Component({ selector: 'component-test-helper-container', template, directives, providers })
  8 + class Test {
  9 + constructor() {
  10 + Object.keys(properties).forEach((key: any) => {
  11 + (<any>this)[key] = <any>properties[key];
  12 + });
  13 + }
  14 + }
  15 + return Test;
  16 +}
  17 +
  18 +export function rebuild(factory: any, done: any): any {
1
  • Me
    Michel Felipe @mfdeveloper (Edited )

    Please, remove the unused rebuild() global function. The feature to redo the tcb.createAsync() is not working. I tried some implementations to do that, but Angular 1.x use the $injector singleton to register mocks. Because of this, if you try recreate a component, one error is thrown: Injector already created. can not register a module.

    My suggestion is: Remove this implementation for now, and use the another approach implemented by @abner that use quickCreateComponent() when you need override providers(filters, services...).

    See this link like a reference: http://stackoverflow.com/questions/24900067/injector-already-created-can-not-register-a-module

    Choose File ...   File name...
    Cancel
  19 + return new ComponentTestHelper(factory, done);
  20 +}
  21 +
  22 +/**
  23 + * Helper class for creating tests. It encapsulates the TestComponentBuilder initialization,
  24 + * allowing the test to be DRY. To use, one must declare a beforeEach function in the
  25 + * test, and inside construct this object like:
  26 + *
  27 + * let helper = let helper : ComponentTestHelper;
1
  • Me
    Michel Felipe @mfdeveloper (Edited )

    For create this usage example, Can you use your solution for angular classes docs?

    Choose File ...   File name...
    Cancel
  28 + * beforeEach( (done) => {
  29 + * helper = new ComponentTestHelper(cls, tcb);
  30 + * }
  31 + */
  32 +export class ComponentTestHelper {
  33 +
  34 + mockComponent: any;
  35 + tcb: TestComponentBuilder;
  36 + component: any;
2
  37 + debugElement: INgForwardJQuery;
  38 +
  39 + constructor(mockComponent: any, done: any) {
1
  • Me
    Michel Felipe @mfdeveloper (Edited )

    In mockComponent param, replace the any type to ngClass. For recognize this type into this file, add the import {ngClass} from "...test-component-builder.ts" together with the others import declarations.

    Replace too done param type to Function

    Choose File ...   File name...
    Cancel
  40 + this.mockComponent = mockComponent;
  41 + this.tcb = new TestComponentBuilder();
  42 + this.init(done);
  43 + }
  44 +
  45 + init(done: any): any {
1
  46 + let promisse = this.tcb.createAsync(this.mockComponent) as any;
1
  47 + return promisse.then((fixture: any) => {
  48 + // Fire all angular events and parsing
  49 + fixture.detectChanges();
  50 + // The main debug element
  51 + this.debugElement = fixture.debugElement;
  52 + this.component = this.debugElement.componentViewChildren[0].componentInstance;
  53 + }).then(() => {
  54 + // Force the resolution of components and sync
  55 + done();
  56 + });
  57 + }
  58 +
  59 + /**
  60 + * Return all elements matching the given selector
  61 + */
  62 + all(selector: string): INgForwardJQuery[] {
  63 + return this.debugElement.queryAll(selector);
  64 + }
  65 +
  66 + find(selector: string): INgForwardJQuery {
  67 + return this.all(selector)[0];
  68 + }
  69 +
  70 + findChildren(parentSelector: string, childSelector: string) {
  71 + let parentComponent = this.find(parentSelector);
2
  72 + return parentComponent.find(childSelector)[0];
  73 + }
  74 +}
0 75 \ No newline at end of file
... ...
src/spec/mocks.ts
... ... @@ -72,6 +72,13 @@ export var mocks = {
72 72 };
73 73 }
74 74 },
  75 + environmentService: {
  76 + getEnvironmentPeople: (params: any) => {
  77 + return mocks.promiseResultTemplate({
  78 + people: {}
1
  • Me
    Michel Felipe @mfdeveloper

    Can you use the params value into people on promise, instead a empty object({}) ?

    Choose File ...   File name...
    Cancel
  79 + });
  80 + }
  81 + },
75 82 profileService: {
76 83 getCurrentProfile: (profile: any) => {
77 84 return mocks.promiseResultTemplate({
... ...