From 41cad53868bba4e95c0f03dcc37a93b8b8a5c006 Mon Sep 17 00:00:00 2001 From: Michel Felipe Date: Wed, 20 Apr 2016 10:27:25 -0300 Subject: [PATCH] Refactory into unit tests to use EventEmitter mocks --- src/app/index.ts | 4 ++-- src/app/layout/boxes/boxes.component.spec.ts | 1 - src/app/layout/navbar/navbar.spec.ts | 34 ++++++++++++++++++---------------- src/app/layout/navbar/navbar.ts | 4 ++-- src/app/layout/services/body-state-classes.service.spec.ts | 26 ++++++++++++++------------ src/app/layout/services/body-state-classes.service.ts | 2 +- src/app/layout/sidebar/sidebar.component.ts | 2 +- src/app/login/auth.service.spec.ts | 40 +++++++++++++++++++++------------------- src/app/login/auth.service.ts | 16 +++++++--------- src/spec/component-test-helper.ts | 11 ++++++----- src/spec/helpers.ts | 13 ++++++------- src/spec/mocks.ts | 35 +++++++++++++++++++++++++++++++++-- 12 files changed, 111 insertions(+), 77 deletions(-) diff --git a/src/app/index.ts b/src/app/index.ts index c2271ed..da0ae09 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -5,7 +5,7 @@ import {noosferoAngularRunBlock} from "./index.run"; import {MainComponent} from "./main/main.component"; import {bootstrap, bundle} from "ng-forward"; -import {AUTH_EVENTS} from "./login/auth-events"; +import {AuthEvents} from "./login/auth-events"; import {AuthController} from "./login/auth.controller"; import {Navbar} from "./layout/navbar/navbar"; @@ -23,7 +23,7 @@ NoosferoApp.angularModule = noosferoApp; NoosferoApp.addConstants("moment", moment); -NoosferoApp.addConstants("AUTH_EVENTS", AUTH_EVENTS); +NoosferoApp.addConstants("AuthEvents", AuthEvents); NoosferoApp.addConfig(noosferoModuleConfig); NoosferoApp.run(noosferoAngularRunBlock); diff --git a/src/app/layout/boxes/boxes.component.spec.ts b/src/app/layout/boxes/boxes.component.spec.ts index 6d6602c..b7e0293 100644 --- a/src/app/layout/boxes/boxes.component.spec.ts +++ b/src/app/layout/boxes/boxes.component.spec.ts @@ -7,7 +7,6 @@ import { quickCreateComponent, provideEmptyObjects, createProviderToValue, - getAngularServiceFactory, provideFilters } from "../../../spec/helpers"; diff --git a/src/app/layout/navbar/navbar.spec.ts b/src/app/layout/navbar/navbar.spec.ts index a3ba358..59f309b 100644 --- a/src/app/layout/navbar/navbar.spec.ts +++ b/src/app/layout/navbar/navbar.spec.ts @@ -1,12 +1,13 @@ import * as helpers from "./../../../spec/helpers"; import {Navbar} from "./navbar"; -import {Injectable, Provider, provide} from "ng-forward"; +import {Injectable, Provider, provide, EventEmitter} from "ng-forward"; import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; -import {SessionService, AuthService, AuthController, IAuthEvents, AUTH_EVENTS} from "./../../login"; +import {SessionService, AuthService, AuthController, AuthEvents} from "./../../login"; +import events from 'ng-forward/cjs/events/events'; describe("Components", () => { @@ -53,22 +54,19 @@ describe("Components", () => { provide('$uibModal', { useValue: $modal }), - provide('AuthService', { + provide(AuthService, { useValue: authService }), helpers.provideEmptyObjects('moment'), provide('$state', { useValue: stateService }), - provide("$scope", { - useValue: scope - }), provide('SessionService', { useValue: sessionService }), - provide('AUTH_EVENTS', { + provide('AuthEvents', { useValue: { - AUTH_EVENTS + AuthEvents } }), provide('TranslatorService', { @@ -146,7 +144,7 @@ describe("Components", () => { it('closes the modal after login', (done: Function) => { - modalInstance = jasmine.createSpyObj("modalInstance", ["close"]); + modalInstance = {}; modalInstance.close = jasmine.createSpy("close"); $modal.open = () => { @@ -155,19 +153,21 @@ describe("Components", () => { buildComponent().then((fixture: ComponentFixture) => { let navbarComp: Navbar = fixture.debugElement.componentViewChildren[0].componentInstance; - let localScope: ng.IScope = navbarComp["$scope"]; navbarComp.openLogin(); - localScope.$emit(AUTH_EVENTS.loginSuccess); + let successEvent: string = AuthEvents[AuthEvents.loginSuccess]; + + (navbarComp.authService)[successEvent].next(user); + expect(modalInstance.close).toHaveBeenCalled(); done(); }); + }); it('updates current user on logout', (done: Function) => { buildComponent().then((fixture: ComponentFixture) => { let navbarComp: Navbar = fixture.debugElement.componentViewChildren[0].componentInstance; - let localScope: ng.IScope = navbarComp["$scope"]; // init navbar currentUser with some user navbarComp["currentUser"] = user; @@ -176,12 +176,14 @@ describe("Components", () => { // and emmit the 'logoutSuccess' event // just what happens when user logsout sessionService.currentUser = () => { return null; }; - localScope.$emit(AUTH_EVENTS.logoutSuccess); - expect(navbarComp["currentUser"]).toBeNull(); + let successEvent: string = AuthEvents[AuthEvents.logoutSuccess]; + + (navbarComp.authService)[successEvent].next(user); + done(); + expect(navbarComp["currentUser"]).toBeNull(); + }); }); - - }); }); diff --git a/src/app/layout/navbar/navbar.ts b/src/app/layout/navbar/navbar.ts index c002406..6d03903 100644 --- a/src/app/layout/navbar/navbar.ts +++ b/src/app/layout/navbar/navbar.ts @@ -22,7 +22,7 @@ export class Navbar { */ constructor( private $uibModal: any, - private authService: AuthService, + public authService: AuthService, private session: SessionService, private $state: ng.ui.IStateService, private sidebarNotificationService: SidebarNotificationService @@ -31,7 +31,7 @@ export class Navbar { this.showHamburguer = this.authService.isAuthenticated(); - this.authService.loginSuccess.subscribe(() => { + this.authService.subscribe(AuthEvents[AuthEvents.loginSuccess], () => { if (this.modalInstance) { this.modalInstance.close(); this.modalInstance = null; diff --git a/src/app/layout/services/body-state-classes.service.spec.ts b/src/app/layout/services/body-state-classes.service.spec.ts index 8dfe619..1444509 100644 --- a/src/app/layout/services/body-state-classes.service.spec.ts +++ b/src/app/layout/services/body-state-classes.service.spec.ts @@ -1,9 +1,13 @@ import * as helpers from '../../../spec/helpers'; import {BodyStateClassesService} from "./body-state-classes.service"; import {AuthService} from "./../../login/auth.service"; -import {AUTH_EVENTS} from "./../../login/auth-events"; +import {AuthEvents} from "./../../login/auth-events"; + +import {EventEmitter} from 'ng-forward'; + describe("BodyStateClasses Service", () => { + let currentStateName = "main"; let bodyStateClasseService: BodyStateClassesService; let $rootScope: ng.IRootScopeService = {}, @@ -13,17 +17,15 @@ describe("BodyStateClasses Service", () => { name: currentStateName } }, - authService: AuthService, + authService: any = helpers.mocks.authService, bodyEl: { className: string }, bodyElJq: any; - let getService = (): BodyStateClassesService => { return new BodyStateClassesService($rootScope, $document, $state, authService); }; beforeEach(() => { - authService = {}; authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(true); bodyEl = { className: "" }; bodyElJq = [bodyEl]; @@ -58,8 +60,9 @@ describe("BodyStateClasses Service", () => { it("should capture loginSuccess event and add noosfero-user-logged class to the body element", () => { let userLoggedClassName = BodyStateClassesService.USER_LOGGED_CLASSNAME; - $rootScope = helpers.mocks.scopeWithEvents(); + bodyElJq.addClass = jasmine.createSpy("addClass"); + bodyElJq.removeClass = jasmine.createSpy("removeClass"); authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(false); let service = getService(); @@ -68,11 +71,11 @@ describe("BodyStateClasses Service", () => { // triggers the service start service.start(); // check if the the body element addClass was not called yet, - // because the user is not authenticated + // because the user is not authenticated expect(bodyElJq.addClass).not.toHaveBeenCalledWith(userLoggedClassName); // emit the event loginSuccess - $rootScope.$emit(AUTH_EVENTS.loginSuccess); + authService.loginSuccess.next(null); // and check now if the addClass was called passing the userLogged class // to the body Element @@ -83,7 +86,6 @@ describe("BodyStateClasses Service", () => { let userLoggedClassName = BodyStateClassesService.USER_LOGGED_CLASSNAME; authService.isAuthenticated = jasmine.createSpy("isAuthenticated").and.returnValue(true); - $rootScope = helpers.mocks.scopeWithEvents(); bodyElJq.addClass = jasmine.createSpy("addClass"); bodyElJq.removeClass = jasmine.createSpy("removeClass"); @@ -95,11 +97,11 @@ describe("BodyStateClasses Service", () => { service.start(); // check if the the body element addClass was called - // because the user is already authenticated + // because the user is already authenticated expect(bodyElJq.addClass).toHaveBeenCalledWith(userLoggedClassName); // emit the event logoutSuccess - $rootScope.$emit(AUTH_EVENTS.logoutSuccess); + authService.logoutSuccess.next(null); // and check now if the removeClass was called passing the userLogged class // to the body Element @@ -126,9 +128,9 @@ describe("BodyStateClasses Service", () => { expect(bodyEl.className).toEqual(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + currentStateName); // emit the event $stateChangeSuccess - $rootScope.$emit("$stateChangeSuccess", null, {name: "new-route"}); + $rootScope.$emit("$stateChangeSuccess", null, { name: "new-route" }); // and check now if the bodyEl has a class indicating the new state route expect(bodyEl.className).toEqual(BodyStateClassesService.ROUTE_STATE_CLASSNAME_PREFIX + "new-route"); }); -}); \ No newline at end of file +}); diff --git a/src/app/layout/services/body-state-classes.service.ts b/src/app/layout/services/body-state-classes.service.ts index f170c99..3cf35d2 100644 --- a/src/app/layout/services/body-state-classes.service.ts +++ b/src/app/layout/services/body-state-classes.service.ts @@ -83,7 +83,7 @@ export class BodyStateClassesService { this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => { bodyElement.removeClass(BodyStateClassesService.USER_LOGGED_CLASSNAME); - }) + }); } /** diff --git a/src/app/layout/sidebar/sidebar.component.ts b/src/app/layout/sidebar/sidebar.component.ts index ff1fae8..485d8e5 100644 --- a/src/app/layout/sidebar/sidebar.component.ts +++ b/src/app/layout/sidebar/sidebar.component.ts @@ -18,6 +18,6 @@ export class SidebarComponent { this.notificationService.setVisibility(this.isVisible); this.notificationService.subscribe((visible) => { this.isVisible = visible; - }) + }); } } diff --git a/src/app/login/auth.service.spec.ts b/src/app/login/auth.service.spec.ts index 0d7a3bc..2cc9089 100644 --- a/src/app/login/auth.service.spec.ts +++ b/src/app/login/auth.service.spec.ts @@ -1,4 +1,9 @@ -import {AuthService, AUTH_EVENTS} from "./"; +import {AuthService, AuthEvents} from "./"; + +import {Injectable, Provider, provide, EventEmitter} from "ng-forward"; +import {ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder'; + +import {getAngularServiceFactory, AngularServiceFactory} from "../../spec/helpers"; describe("Services", () => { @@ -15,23 +20,26 @@ describe("Services", () => { $translateProvider.translations('en', {}); })); - beforeEach(inject((_$httpBackend_: ng.IHttpBackendService, _$rootScope_: ng.IRootScopeService, _AuthService_: AuthService) => { - $httpBackend = _$httpBackend_; - authService = _AuthService_; - $rootScope = _$rootScope_; + beforeEach(() => { user = { id: 1, login: "user" }; - })); - + }); describe("Succesffull login", () => { + let factory: AngularServiceFactory; + let authService: AuthService; + let $log: ng.ILogService; + let $http: ng.IHttpBackendService; + beforeEach(() => { credentials = { username: "user", password: "password" }; - + factory = getAngularServiceFactory(); + authService = factory.getAngularService("AuthService"); + $httpBackend = factory.getHttpBackendService(); $httpBackend.expectPOST("/api/v1/login", "login=user&password=password").respond(200, { user: user }); }); @@ -44,20 +52,15 @@ describe("Services", () => { expect($httpBackend.verifyNoOutstandingRequest()); }); - - it("should emit event loggin successful with user logged data", () => { - - authService.login(credentials); - - let eventEmmited: boolean = false; - $rootScope.$on(AUTH_EVENTS.loginSuccess, (event: ng.IAngularEvent, userThroughEvent: noosfero.User) => { - eventEmmited = true; + it("should emit event loggin successful with user logged data", (done: Function) => { + let successEvent: any = AuthEvents[AuthEvents.loginSuccess]; + (authService)[successEvent].subscribe((userThroughEvent: noosfero.User): any => { expect(userThroughEvent).toEqual(user); + done(); }); - + authService.login(credentials); $httpBackend.flush(); - expect(eventEmmited).toBeTruthy(AUTH_EVENTS.loginSuccess + " was not emmited!"); }); it("should return the current logged in user", () => { @@ -74,6 +77,5 @@ describe("Services", () => { }); }); - }); }); diff --git a/src/app/login/auth.service.ts b/src/app/login/auth.service.ts index f449dbd..4caae3e 100644 --- a/src/app/login/auth.service.ts +++ b/src/app/login/auth.service.ts @@ -6,18 +6,16 @@ import {SessionService} from "./session.service"; import {AuthEvents} from "./auth-events"; @Injectable() -@Inject("$q", "$http", "SessionService", "$log") +@Inject("$http", "SessionService", "$log") export class AuthService { public loginSuccess: EventEmitter = new EventEmitter(); - private loginFailed: EventEmitter> = new EventEmitter>(); - private logoutSuccess: EventEmitter = new EventEmitter(); + public loginFailed: EventEmitter> = new EventEmitter>(); + public logoutSuccess: EventEmitter = new EventEmitter(); - constructor(private $q: ng.IQService, - private $http: ng.IHttpService, + constructor(private $http: ng.IHttpService, private sessionService: SessionService, private $log: ng.ILogService) { - } loginFromCookie() { @@ -43,7 +41,6 @@ export class AuthService { private loginFailedCallback(response: ng.IHttpPromiseCallbackArg): any { this.$log.debug('AuthService.login [FAIL] response', response); this.loginFailed.next(response); - // return $q.reject(response); return null; } @@ -72,8 +69,9 @@ export class AuthService { subscribe(eventName: string, fn: Function) { - if (this[eventName]) { - this[eventName].subscribe(fn); + let event: EventEmitter = >(this)[eventName]; + if (event) { + event.subscribe(fn); } else { throw new Error(`The event: ${eventName} not exists`); } diff --git a/src/spec/component-test-helper.ts b/src/spec/component-test-helper.ts index 2441994..75b8474 100644 --- a/src/spec/component-test-helper.ts +++ b/src/spec/component-test-helper.ts @@ -6,12 +6,12 @@ import { ComponentFixture } from 'ng-forward/cjs/testing/test-component-builder' /** * @ngdoc object * @name spec.ComponentTestHelper - * @description - * + * @description + * * Helper class for creating tests. It encapsulates the TestComponentBuilder initialization, * allowing the test to be DRY. To use, one must declare a beforeEach function in the * test, and inside construct this object like: - * + * *
  * let helper = let helper : ComponentTestHelper;
  * beforeEach( (done) => {
@@ -35,7 +35,7 @@ export class ComponentTestHelper {
      * @propertyOf spec.ComponentTestHelper
      * @description
      *  The NgForward TestComponentBuilder
-     */
+     */    
     tcb: TestComponentBuilder;
     /**
      * @ngdoc property
@@ -52,7 +52,7 @@ export class ComponentTestHelper {
      * @description
      *  The debugElement representing a JQuery element attached to the component
      * on mock page.
-     */
+     */    
     debugElement: INgForwardJQuery;
 
     /**
@@ -145,3 +145,4 @@ export function createClass({
     }
     return Test;
 }
+
diff --git a/src/spec/helpers.ts b/src/spec/helpers.ts
index aa1babb..48f7538 100644
--- a/src/spec/helpers.ts
+++ b/src/spec/helpers.ts
@@ -1,7 +1,7 @@
 
 import {ngClass, TestComponentBuilder, ComponentFixture} from 'ng-forward/cjs/testing/test-component-builder';
 import {providers} from  'ng-forward/cjs/testing/providers';
-import {Injectable, Inject, Provider, Input, Output, EventEmitter, provide, Component} from 'ng-forward';
+import {Injectable, Inject, Provider, Input, provide, Component} from 'ng-forward';
 
 
 export var ngforward = {
@@ -62,6 +62,7 @@ export function provideFilters(...filters: string[]) {
     return providers;
 }
 
+
 @Component({
     selector: 'helper_get_angular_service',
     template: 'not-used',
@@ -77,11 +78,9 @@ class AngularServiceHookComponent {
  * This helper class allows get angular services to be used in integration tests
  * i.e: '$http', '$q', '$location', etc...
  */
-export class AngularServiceFactory {
-
-    private fixtureComponentHookPoint: ComponentFixture;
+class AngularServiceFactory {
 
-    constructor() {
+    constructor(private fixtureComponentHookPoint: ComponentFixture) {
         this.fixtureComponentHookPoint = (tcb)["create"](AngularServiceHookComponent);
     }
 
@@ -98,8 +97,8 @@ export class AngularServiceFactory {
     }
 }
 
-export function getAngularServiceFactory() {
-    return new AngularServiceFactory();
+export function getAngularServiceFactory(fixture: ComponentFixture) {
+    return new AngularServiceFactory(fixture);
 }
 
 export {mocks} from "./mocks";
diff --git a/src/spec/mocks.ts b/src/spec/mocks.ts
index e4d6c5d..bd9bbe5 100644
--- a/src/spec/mocks.ts
+++ b/src/spec/mocks.ts
@@ -30,7 +30,7 @@ class ScopeWithEvents {
         }
     }
 }
-export var mocks = {
+export var mocks: any = {
     scopeWithEvents: (): ScopeWithEvents => new ScopeWithEvents(),
     modalInstance: {
         close: () => { }
@@ -41,7 +41,38 @@ export var mocks = {
         }
     },
     authService: {
-        logout: () => { }
+        loginSuccess: {
+            event: Function,
+            subscribe: (fn: Function) => {
+                mocks.authService['loginSuccess'].event = fn;
+            },
+            next: (param: any) => {
+                mocks.authService['loginSuccess'].event(param);
+            }
+        },
+        loginFailed: {
+            event: Function,
+            subscribe: (fn: Function) => {
+                mocks.authService['loginFailed'].event = fn;
+            },
+            next: (param: any) => {
+                mocks.authService['loginFailed'].event(param);
+            }
+        },
+        logoutSuccess: {
+            event: Function,
+            subscribe: (fn: Function) => {
+                mocks.authService['logoutSuccess'].event = fn;
+            },
+            next: (param: any) => {
+                mocks.authService['logoutSuccess'].event(param);
+            }
+        },
+        logout: () => { },
+        subscribe: (eventName: string, fn: Function) => {
+            mocks.authService[eventName].subscribe(fn);
+        },
+        isAuthenticated: () => { }
     },
     articleService: {
         getByProfile: (profileId: number, params?: any) => {
--
libgit2 0.21.2