Commit 41cad53868bba4e95c0f03dcc37a93b8b8a5c006

Authored by Michel Felipe
1 parent 4c39bd75

Refactory into unit tests to use EventEmitter mocks

src/app/index.ts
... ... @@ -5,7 +5,7 @@ import {noosferoAngularRunBlock} from "./index.run";
5 5 import {MainComponent} from "./main/main.component";
6 6 import {bootstrap, bundle} from "ng-forward";
7 7  
8   -import {AUTH_EVENTS} from "./login/auth-events";
  8 +import {AuthEvents} from "./login/auth-events";
9 9 import {AuthController} from "./login/auth.controller";
10 10  
11 11 import {Navbar} from "./layout/navbar/navbar";
... ... @@ -23,7 +23,7 @@ NoosferoApp.angularModule = noosferoApp;
23 23  
24 24  
25 25 NoosferoApp.addConstants("moment", moment);
26   -NoosferoApp.addConstants("AUTH_EVENTS", AUTH_EVENTS);
  26 +NoosferoApp.addConstants("AuthEvents", AuthEvents);
27 27  
28 28 NoosferoApp.addConfig(noosferoModuleConfig);
29 29 NoosferoApp.run(noosferoAngularRunBlock);
... ...
src/app/layout/boxes/boxes.component.spec.ts
... ... @@ -7,7 +7,6 @@ import {
7 7 quickCreateComponent,
8 8 provideEmptyObjects,
9 9 createProviderToValue,
10   - getAngularServiceFactory,
11 10 provideFilters
12 11 } from "../../../spec/helpers";
13 12  
... ...
src/app/layout/navbar/navbar.spec.ts
1 1 import * as helpers from "./../../../spec/helpers";
2 2 import {Navbar} from "./navbar";
3 3  
4   -import {Injectable, Provider, provide} from "ng-forward";
  4 +import {Injectable, Provider, provide, EventEmitter} from "ng-forward";
5 5  
6 6 import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
7 7  
8   -import {SessionService, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../../login";
  8 +import {SessionService, AuthService, AuthController, AuthEvents} from "./../../login";
9 9  
  10 +import events from 'ng-forward/cjs/events/events';
10 11  
11 12 describe("Components", () => {
12 13  
... ... @@ -53,22 +54,19 @@ describe("Components", () => {
53 54 provide('$uibModal', {
54 55 useValue: $modal
55 56 }),
56   - provide('AuthService', {
  57 + provide(AuthService, {
57 58 useValue: authService
58 59 }),
59 60 helpers.provideEmptyObjects('moment'),
60 61 provide('$state', {
61 62 useValue: stateService
62 63 }),
63   - provide("$scope", {
64   - useValue: scope
65   - }),
66 64 provide('SessionService', {
67 65 useValue: sessionService
68 66 }),
69   - provide('AUTH_EVENTS', {
  67 + provide('AuthEvents', {
70 68 useValue: {
71   - AUTH_EVENTS
  69 + AuthEvents
72 70 }
73 71 }),
74 72 provide('TranslatorService', {
... ... @@ -146,7 +144,7 @@ describe("Components", () => {
146 144  
147 145  
148 146 it('closes the modal after login', (done: Function) => {
149   - modalInstance = jasmine.createSpyObj("modalInstance", ["close"]);
  147 + modalInstance = {};
150 148 modalInstance.close = jasmine.createSpy("close");
151 149  
152 150 $modal.open = () => {
... ... @@ -155,19 +153,21 @@ describe("Components", () => {
155 153  
156 154 buildComponent().then((fixture: ComponentFixture) => {
157 155 let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance;
158   - let localScope: ng.IScope = navbarComp["$scope"];
159 156  
160 157 navbarComp.openLogin();
161   - localScope.$emit(AUTH_EVENTS.loginSuccess);
  158 + let successEvent: string = AuthEvents[AuthEvents.loginSuccess];
  159 +
  160 + (<any>navbarComp.authService)[successEvent].next(user);
  161 +
162 162 expect(modalInstance.close).toHaveBeenCalled();
163 163 done();
164 164 });
  165 +
165 166 });
166 167  
167 168 it('updates current user on logout', (done: Function) => {
168 169 buildComponent().then((fixture: ComponentFixture) => {
169 170 let navbarComp: Navbar = <Navbar>fixture.debugElement.componentViewChildren[0].componentInstance;
170   - let localScope: ng.IScope = navbarComp["$scope"];
171 171  
172 172 // init navbar currentUser with some user
173 173 navbarComp["currentUser"] = user;
... ... @@ -176,12 +176,14 @@ describe(&quot;Components&quot;, () =&gt; {
176 176 // and emmit the 'logoutSuccess' event
177 177 // just what happens when user logsout
178 178 sessionService.currentUser = () => { return null; };
179   - localScope.$emit(AUTH_EVENTS.logoutSuccess);
180   - expect(navbarComp["currentUser"]).toBeNull();
  179 + let successEvent: string = AuthEvents[AuthEvents.logoutSuccess];
  180 +
  181 + (<any>navbarComp.authService)[successEvent].next(user);
  182 +
181 183 done();
  184 + expect(navbarComp["currentUser"]).toBeNull();
  185 +
182 186 });
183 187 });
184   -
185   -
186 188 });
187 189 });
... ...
src/app/layout/navbar/navbar.ts
... ... @@ -22,7 +22,7 @@ export class Navbar {
22 22 */
23 23 constructor(
24 24 private $uibModal: any,
25   - private authService: AuthService,
  25 + public authService: AuthService,
26 26 private session: SessionService,
27 27 private $state: ng.ui.IStateService,
28 28 private sidebarNotificationService: SidebarNotificationService
... ... @@ -31,7 +31,7 @@ export class Navbar {
31 31  
32 32 this.showHamburguer = this.authService.isAuthenticated();
33 33  
34   - this.authService.loginSuccess.subscribe(() => {
  34 + this.authService.subscribe(AuthEvents[AuthEvents.loginSuccess], () => {
35 35 if (this.modalInstance) {
36 36 this.modalInstance.close();
37 37 this.modalInstance = null;
... ...
src/app/layout/services/body-state-classes.service.spec.ts
1 1 import * as helpers from '../../../spec/helpers';
2 2 import {BodyStateClassesService} from "./body-state-classes.service";
3 3 import {AuthService} from "./../../login/auth.service";
4   -import {AUTH_EVENTS} from "./../../login/auth-events";
  4 +import {AuthEvents} from "./../../login/auth-events";
  5 +
  6 +import {EventEmitter} from 'ng-forward';
  7 +
5 8  
6 9 describe("BodyStateClasses Service", () => {
  10 +
7 11 let currentStateName = "main";
8 12 let bodyStateClasseService: BodyStateClassesService;
9 13 let $rootScope: ng.IRootScopeService = <any>{},
... ... @@ -13,17 +17,15 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
13 17 name: currentStateName
14 18 }
15 19 },
16   - authService: AuthService,
  20 + authService: any = helpers.mocks.authService,
17 21 bodyEl: { className: string },
18 22 bodyElJq: any;
19 23  
20   -
21 24 let getService = (): BodyStateClassesService => {
22 25 return new BodyStateClassesService($rootScope, $document, $state, authService);
23 26 };
24 27  
25 28 beforeEach(() => {
26   - authService = <any>{};
27 29 authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(true);
28 30 bodyEl = { className: "" };
29 31 bodyElJq = [bodyEl];
... ... @@ -58,8 +60,9 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
58 60  
59 61 it("should capture loginSuccess event and add noosfero-user-logged class to the body element", () => {
60 62 let userLoggedClassName = BodyStateClassesService.USER_LOGGED_CLASSNAME;
61   - $rootScope = <any>helpers.mocks.scopeWithEvents();
  63 +
62 64 bodyElJq.addClass = jasmine.createSpy("addClass");
  65 + bodyElJq.removeClass = jasmine.createSpy("removeClass");
63 66 authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(false);
64 67 let service = getService();
65 68  
... ... @@ -68,11 +71,11 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
68 71 // triggers the service start
69 72 service.start();
70 73 // check if the the body element addClass was not called yet,
71   - // because the user is not authenticated
  74 + // because the user is not authenticated
72 75 expect(bodyElJq.addClass).not.toHaveBeenCalledWith(userLoggedClassName);
73 76  
74 77 // emit the event loginSuccess
75   - $rootScope.$emit(AUTH_EVENTS.loginSuccess);
  78 + authService.loginSuccess.next(null);
76 79  
77 80 // and check now if the addClass was called passing the userLogged class
78 81 // to the body Element
... ... @@ -83,7 +86,6 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
83 86 let userLoggedClassName = BodyStateClassesService.USER_LOGGED_CLASSNAME;
84 87  
85 88 authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(true);
86   - $rootScope = <any>helpers.mocks.scopeWithEvents();
87 89  
88 90 bodyElJq.addClass = jasmine.createSpy("addClass");
89 91 bodyElJq.removeClass = jasmine.createSpy("removeClass");
... ... @@ -95,11 +97,11 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
95 97 service.start();
96 98  
97 99 // check if the the body element addClass was called
98   - // because the user is already authenticated
  100 + // because the user is already authenticated
99 101 expect(bodyElJq.addClass).toHaveBeenCalledWith(userLoggedClassName);
100 102  
101 103 // emit the event logoutSuccess
102   - $rootScope.$emit(AUTH_EVENTS.logoutSuccess);
  104 + authService.logoutSuccess.next(null);
103 105  
104 106 // and check now if the removeClass was called passing the userLogged class
105 107 // to the body Element
... ... @@ -126,9 +128,9 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
126 128 expect(bodyEl.className).toEqual(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + currentStateName);
127 129  
128 130 // emit the event $stateChangeSuccess
129   - $rootScope.$emit("$stateChangeSuccess", null, {name: "new-route"});
  131 + $rootScope.$emit("$stateChangeSuccess", null, { name: "new-route" });
130 132  
131 133 // and check now if the bodyEl has a class indicating the new state route
132 134 expect(bodyEl.className).toEqual(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + "new-route");
133 135 });
134   -});
135 136 \ No newline at end of file
  137 +});
... ...
src/app/layout/services/body-state-classes.service.ts
... ... @@ -83,7 +83,7 @@ export class BodyStateClassesService {
83 83  
84 84 this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => {
85 85 bodyElement.removeClass(BodyStateClassesService.USER_LOGGED_CLASSNAME);
86   - })
  86 + });
87 87 }
88 88  
89 89 /**
... ...
src/app/layout/sidebar/sidebar.component.ts
... ... @@ -18,6 +18,6 @@ export class SidebarComponent {
18 18 this.notificationService.setVisibility(this.isVisible);
19 19 this.notificationService.subscribe((visible) => {
20 20 this.isVisible = visible;
21   - })
  21 + });
22 22 }
23 23 }
... ...
src/app/login/auth.service.spec.ts
1   -import {AuthService, AUTH_EVENTS} from "./";
  1 +import {AuthService, AuthEvents} from "./";
  2 +
  3 +import {Injectable, Provider, provide, EventEmitter} from "ng-forward";
  4 +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
  5 +
  6 +import {getAngularServiceFactory, AngularServiceFactory} from "../../spec/helpers";
2 7  
3 8 describe("Services", () => {
4 9  
... ... @@ -15,23 +20,26 @@ describe(&quot;Services&quot;, () =&gt; {
15 20 $translateProvider.translations('en', {});
16 21 }));
17 22  
18   - beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService, _AuthService_: AuthService) => {
19   - $httpBackend = _$httpBackend_;
20   - authService = _AuthService_;
21   - $rootScope = _$rootScope_;
  23 + beforeEach(() => {
22 24  
23 25 user = <noosfero.User>{
24 26 id: 1,
25 27 login: "user"
26 28 };
27   - }));
28   -
  29 + });
29 30  
30 31 describe("Succesffull login", () => {
31 32  
  33 + let factory: AngularServiceFactory;
  34 + let authService: AuthService;
  35 + let $log: ng.ILogService;
  36 + let $http: ng.IHttpBackendService;
  37 +
32 38 beforeEach(() => {
33 39 credentials = { username: "user", password: "password" };
34   -
  40 + factory = getAngularServiceFactory();
  41 + authService = factory.getAngularService("AuthService");
  42 + $httpBackend = factory.getHttpBackendService();
35 43 $httpBackend.expectPOST("/api/v1/login", "login=user&password=password").respond(200, { user: user });
36 44 });
37 45  
... ... @@ -44,20 +52,15 @@ describe(&quot;Services&quot;, () =&gt; {
44 52 expect($httpBackend.verifyNoOutstandingRequest());
45 53 });
46 54  
47   -
48   - it("should emit event loggin successful with user logged data", () => {
49   -
50   - authService.login(credentials);
51   -
52   - let eventEmmited: boolean = false;
53   - $rootScope.$on(AUTH_EVENTS.loginSuccess, (event: ng.IAngularEvent, userThroughEvent: noosfero.User) => {
54   - eventEmmited = true;
  55 + it("should emit event loggin successful with user logged data", (done: Function) => {
  56 + let successEvent: any = AuthEvents[AuthEvents.loginSuccess];
  57 + (<any>authService)[successEvent].subscribe((userThroughEvent: noosfero.User): any => {
55 58 expect(userThroughEvent).toEqual(user);
  59 + done();
56 60 });
57   -
  61 + authService.login(credentials);
58 62 $httpBackend.flush();
59 63  
60   - expect(eventEmmited).toBeTruthy(AUTH_EVENTS.loginSuccess + " was not emmited!");
61 64 });
62 65  
63 66 it("should return the current logged in user", () => {
... ... @@ -74,6 +77,5 @@ describe(&quot;Services&quot;, () =&gt; {
74 77 });
75 78 });
76 79  
77   -
78 80 });
79 81 });
... ...
src/app/login/auth.service.ts
... ... @@ -6,18 +6,16 @@ import {SessionService} from &quot;./session.service&quot;;
6 6 import {AuthEvents} from "./auth-events";
7 7  
8 8 @Injectable()
9   -@Inject("$q", "$http", "SessionService", "$log")
  9 +@Inject("$http", "SessionService", "$log")
10 10 export class AuthService {
11 11  
12 12 public loginSuccess: EventEmitter<noosfero.User> = new EventEmitter<noosfero.User>();
13   - private loginFailed: EventEmitter<ng.IHttpPromiseCallbackArg<any>> = new EventEmitter<ng.IHttpPromiseCallbackArg<any>>();
14   - private logoutSuccess: EventEmitter<noosfero.User> = new EventEmitter<noosfero.User>();
  13 + public loginFailed: EventEmitter<ng.IHttpPromiseCallbackArg<any>> = new EventEmitter<ng.IHttpPromiseCallbackArg<any>>();
  14 + public logoutSuccess: EventEmitter<noosfero.User> = new EventEmitter<noosfero.User>();
15 15  
16   - constructor(private $q: ng.IQService,
17   - private $http: ng.IHttpService,
  16 + constructor(private $http: ng.IHttpService,
18 17 private sessionService: SessionService,
19 18 private $log: ng.ILogService) {
20   -
21 19 }
22 20  
23 21 loginFromCookie() {
... ... @@ -43,7 +41,6 @@ export class AuthService {
43 41 private loginFailedCallback(response: ng.IHttpPromiseCallbackArg<any>): any {
44 42 this.$log.debug('AuthService.login [FAIL] response', response);
45 43 this.loginFailed.next(response);
46   - // return $q.reject(response);
47 44 return null;
48 45 }
49 46  
... ... @@ -72,8 +69,9 @@ export class AuthService {
72 69  
73 70 subscribe(eventName: string, fn: Function) {
74 71  
75   - if (this[eventName]) {
76   - this[eventName].subscribe(fn);
  72 + let event: EventEmitter<any> = <EventEmitter<any>>(<any>this)[eventName];
  73 + if (event) {
  74 + event.subscribe(fn);
77 75 } else {
78 76 throw new Error(`The event: ${eventName} not exists`);
79 77 }
... ...
src/spec/component-test-helper.ts
... ... @@ -6,12 +6,12 @@ import { ComponentFixture } from &#39;ng-forward/cjs/testing/test-component-builder&#39;
6 6 /**
7 7 * @ngdoc object
8 8 * @name spec.ComponentTestHelper
9   - * @description
10   - *
  9 + * @description
  10 + *
11 11 * Helper class for creating tests. It encapsulates the TestComponentBuilder initialization,
12 12 * allowing the test to be DRY. To use, one must declare a beforeEach function in the
13 13 * test, and inside construct this object like:
14   - *
  14 + *
15 15 * <pre>
16 16 * let helper = let helper : ComponentTestHelper;
17 17 * beforeEach( (done) => {
... ... @@ -35,7 +35,7 @@ export class ComponentTestHelper&lt;T extends any&gt; {
35 35 * @propertyOf spec.ComponentTestHelper
36 36 * @description
37 37 * The NgForward TestComponentBuilder
38   - */
  38 + */
39 39 tcb: TestComponentBuilder;
40 40 /**
41 41 * @ngdoc property
... ... @@ -52,7 +52,7 @@ export class ComponentTestHelper&lt;T extends any&gt; {
52 52 * @description
53 53 * The debugElement representing a JQuery element attached to the component
54 54 * on mock page.
55   - */
  55 + */
56 56 debugElement: INgForwardJQuery;
57 57  
58 58 /**
... ... @@ -145,3 +145,4 @@ export function createClass({
145 145 }
146 146 return Test;
147 147 }
  148 +
... ...
src/spec/helpers.ts
1 1  
2 2 import {ngClass, TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
3 3 import {providers} from 'ng-forward/cjs/testing/providers';
4   -import {Injectable, Inject, Provider, Input, Output, EventEmitter, provide, Component} from 'ng-forward';
  4 +import {Injectable, Inject, Provider, Input, provide, Component} from 'ng-forward';
5 5  
6 6  
7 7 export var ngforward = {
... ... @@ -62,6 +62,7 @@ export function provideFilters(...filters: string[]) {
62 62 return providers;
63 63 }
64 64  
  65 +
65 66 @Component({
66 67 selector: 'helper_get_angular_service',
67 68 template: 'not-used',
... ... @@ -77,11 +78,9 @@ class AngularServiceHookComponent {
77 78 * This helper class allows get angular services to be used in integration tests
78 79 * i.e: '$http', '$q', '$location', etc...
79 80 */
80   -export class AngularServiceFactory {
81   -
82   - private fixtureComponentHookPoint: ComponentFixture;
  81 +class AngularServiceFactory {
83 82  
84   - constructor() {
  83 + constructor(private fixtureComponentHookPoint: ComponentFixture) {
85 84 this.fixtureComponentHookPoint = (<any>tcb)["create"](AngularServiceHookComponent);
86 85 }
87 86  
... ... @@ -98,8 +97,8 @@ export class AngularServiceFactory {
98 97 }
99 98 }
100 99  
101   -export function getAngularServiceFactory() {
102   - return new AngularServiceFactory();
  100 +export function getAngularServiceFactory(fixture: ComponentFixture) {
  101 + return new AngularServiceFactory(fixture);
103 102 }
104 103  
105 104 export {mocks} from "./mocks";
... ...
src/spec/mocks.ts
... ... @@ -30,7 +30,7 @@ class ScopeWithEvents {
30 30 }
31 31 }
32 32 }
33   -export var mocks = {
  33 +export var mocks: any = {
34 34 scopeWithEvents: (): ScopeWithEvents => new ScopeWithEvents(),
35 35 modalInstance: {
36 36 close: () => { }
... ... @@ -41,7 +41,38 @@ export var mocks = {
41 41 }
42 42 },
43 43 authService: {
44   - logout: () => { }
  44 + loginSuccess: {
  45 + event: Function,
  46 + subscribe: (fn: Function) => {
  47 + mocks.authService['loginSuccess'].event = fn;
  48 + },
  49 + next: (param: any) => {
  50 + mocks.authService['loginSuccess'].event(param);
  51 + }
  52 + },
  53 + loginFailed: {
  54 + event: Function,
  55 + subscribe: (fn: Function) => {
  56 + mocks.authService['loginFailed'].event = fn;
  57 + },
  58 + next: (param: any) => {
  59 + mocks.authService['loginFailed'].event(param);
  60 + }
  61 + },
  62 + logoutSuccess: {
  63 + event: Function,
  64 + subscribe: (fn: Function) => {
  65 + mocks.authService['logoutSuccess'].event = fn;
  66 + },
  67 + next: (param: any) => {
  68 + mocks.authService['logoutSuccess'].event(param);
  69 + }
  70 + },
  71 + logout: () => { },
  72 + subscribe: (eventName: string, fn: Function) => {
  73 + mocks.authService[eventName].subscribe(fn);
  74 + },
  75 + isAuthenticated: () => { }
45 76 },
46 77 articleService: {
47 78 getByProfile: (profileId: number, params?: any) => {
... ...