From 55da7c275d7a87f59ef0715126be90a89b4a7b77 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 11 Apr 2016 18:24:00 +0800 Subject: [PATCH 1/4] Added `signOut()` in AuthService for invalidated the api token --- src/components/auth/auth.service.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/auth/auth.service.js b/src/components/auth/auth.service.js index 22bc81f..5d07e68 100644 --- a/src/components/auth/auth.service.js +++ b/src/components/auth/auth.service.js @@ -13,4 +13,13 @@ export default class AuthService { checkEmail(email) { return this.$fetch.post('/v1/auth/checkEmail', { email }); } + + /** + * Invalidated the API token. + * + * @return {promise} + */ + signOut() { + return this.$fetch.post('/v1/auth/logout'); + } } From 7a7dcb49cf893e7ee292a130dc22fe1d3a364876 Mon Sep 17 00:00:00 2001 From: jigsawye Date: Mon, 11 Apr 2016 18:46:58 +0800 Subject: [PATCH 2/4] Implemented sign out in top-navbar Added some mehtod for sign out flow and Uptated the dialog style. --- .../top-navbar/top-navbar.controller.js | 64 +++++++++++++++---- .../layout/top-navbar/top-navbar.html | 2 +- src/styles/dialog.css | 6 +- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/components/layout/top-navbar/top-navbar.controller.js b/src/components/layout/top-navbar/top-navbar.controller.js index 9e64559..80be162 100644 --- a/src/components/layout/top-navbar/top-navbar.controller.js +++ b/src/components/layout/top-navbar/top-navbar.controller.js @@ -1,27 +1,69 @@ export default class TopNavbarController { /** @ngInject */ - constructor($translate, $auth, $state, $toast) { - const languages = [ + constructor($translate, $auth, $state, $toast, $mdDialog, AuthService) { + Object.assign(this, { + $translate, $auth, $state, $toast, $mdDialog, AuthService, + }); + + this.languages = [ { key: 'EN', name: 'English' }, { key: 'TW', name: '繁體中文' }, { key: 'CN', name: '简体中文' }, ]; - const currentLanguage = $translate.use(); - - Object.assign(this, { - $translate, $auth, $state, $toast, languages, currentLanguage, - }); + this.currentLanguage = $translate.use(); } + /** + * Change the language of UI. + * + * @param {string} key + * @return {void} + */ changeLanguage(key) { this.$translate.use(key); this.currentLanguage = key; } - signout() { - this.$auth.logout(); - this.$state.go('auth.signin'); - this.$toast.show('Sign Out Success!'); + /** + * Do the sign out flow when user click the sign out button. + * + * @param {Object} $event + * @return {void} + */ + signOut($event) { + this.showConfirmMessage($event).then(this.executedSignOut); } + + /** + * Show a confirm message for sign out. + * + * @param {Object} $event + * @return {Promise} + */ + showConfirmMessage($event) { + const confirm = this.$mdDialog.confirm() + .title('Would you like to sign out without your upload?') + .textContent(`You have in progress opreations +or uploads and leaving now will cancel them.Still leaving?`) + .ariaLabel('Sign out') + .targetEvent($event) + .ok('Leave') + .cancel('Stay'); + + return this.$mdDialog.show(confirm); + } + + /** + * Executed sign out when user confirm the message. + * + * @return {Promise} [description] + */ + executedSignOut = () => this.AuthService.signOut() + .then(() => { + this.$auth.logout(); + this.$state.go('auth.signin'); + this.$toast.show('Sign Out Success!'); + }) + .catch(() => this.$toast.show('Sign Out Failure!')); } diff --git a/src/components/layout/top-navbar/top-navbar.html b/src/components/layout/top-navbar/top-navbar.html index ba81e18..6480b4f 100644 --- a/src/components/layout/top-navbar/top-navbar.html +++ b/src/components/layout/top-navbar/top-navbar.html @@ -36,7 +36,7 @@

- + {{ 'SETTINGS.SIGN_OUT' | translate }} diff --git a/src/styles/dialog.css b/src/styles/dialog.css index afd8e1c..a09e8f5 100644 --- a/src/styles/dialog.css +++ b/src/styles/dialog.css @@ -3,6 +3,6 @@ * @author Jamie jamie.h@inwinstack.com */ -.md-confirm-dialog-warn-theme md-dialog-actions button:last-child { - color: #FF6D00; -} \ No newline at end of file +.md-default-theme md-dialog-actions button:last-child { + color: #FF6D00 !important; +} From 2be801c12998af3847598a4903a80cc3eedd7c31 Mon Sep 17 00:00:00 2001 From: chaoen Date: Wed, 13 Apr 2016 09:19:11 +0000 Subject: [PATCH 3/4] Add sign-out unit test Add /src/components/layout/top-navbar/top-navbar.spec.js. Test sign out process. --- .../layout/top-navbar/top-navbar.spec.js | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/components/layout/top-navbar/top-navbar.spec.js diff --git a/src/components/layout/top-navbar/top-navbar.spec.js b/src/components/layout/top-navbar/top-navbar.spec.js new file mode 100644 index 0000000..a8397ae --- /dev/null +++ b/src/components/layout/top-navbar/top-navbar.spec.js @@ -0,0 +1,152 @@ +import layoutModule from '../layout'; +import topNavbarCtrl from './top-navbar.controller'; +import app from '../../../index.js'; + +describe('User log out', function() { + let $rootScope; + let makeController; + let makeDeferred; + let AuthService; + let $translate; + let $mdDialog; + let $toast; + let $state; + let $auth; + + beforeEach(angular.mock.module('app')); + + beforeEach(inject(($q, _$rootScope_, _$toast_, _$state_, _$auth_, _AuthService_, _$mdDialog_, _$translate_) => { + $rootScope = _$rootScope_; + + $toast = _$toast_; + + $state = _$state_; + + $auth = _$auth_; + + $auth.isAuthenticated = () => true; + + AuthService = _AuthService_; + + $mdDialog = _$mdDialog_; + + $translate = _$translate_; + + makeDeferred = () => { + return $q.defer(); + } + + makeController = () => { + return new topNavbarCtrl($translate, $auth, $state, $toast, $mdDialog, AuthService); + }; + })); + describe('when logout', function() { + it('should invoke showConfirmMessage()', function() { + const controller = makeController(); + const showDialog = sinon.spy(controller, 'showConfirmMessage'); + controller.signOut(); + expect(showDialog.called).to.eq(true); + }); + }); + describe('when confirm logout', function() { + it('should invoke controller.executedSignOut', function() { + const controller = makeController(); + const signOutMock = sinon.mock(controller.$mdDialog); + const signOutDeferred = makeDeferred(); + signOutMock.expects('show').returns(signOutDeferred.promise); + signOutDeferred.resolve(); + controller.executedSignOut = () => {}; + + const executedSignOut = sinon.spy(controller, 'executedSignOut'); + controller.signOut(); + $rootScope.$digest(); + expect(executedSignOut.called).to.eq(true); + }); + }); + describe('when un-confirm logout', function() { + it('should not invoke controller.executedSignOut', function() { + const controller = makeController(); + const signOutMock = sinon.mock(controller.$mdDialog); + const signOutDeferred = makeDeferred(); + signOutMock.expects('show').returns(signOutDeferred.promise); + signOutDeferred.reject(); + controller.executedSignOut = () => {}; + + const executedSignOut = sinon.spy(controller, 'executedSignOut'); + controller.signOut(); + $rootScope.$digest(); + expect(executedSignOut.called).to.eq(false); + }); + }); + describe('when confirm logout and logout success', function() { + it('should invoke $auth.logout', function() { + const controller = makeController(); + const signOutMock = sinon.mock(controller.$mdDialog); + const signOutDeferred = makeDeferred(); + const authMock = sinon.mock(AuthService); + const authDeferred = makeDeferred(); + signOutMock.expects('show').returns(signOutDeferred.promise); + signOutDeferred.resolve(); + authMock.expects('signOut').returns(authDeferred.promise); + authDeferred.resolve(); + + const auth = sinon.spy($auth, 'logout'); + controller.signOut(); + $rootScope.$digest(); + expect(auth.called).to.eq(true); + }); + it('should invoke $state.go and called with auth.signin', function() { + const controller = makeController(); + const signOutMock = sinon.mock(controller.$mdDialog); + const signOutDeferred = makeDeferred(); + const authMock = sinon.mock(AuthService); + const authDeferred = makeDeferred(); + signOutMock.expects('show').returns(signOutDeferred.promise); + signOutDeferred.resolve(); + authMock.expects('signOut').returns(authDeferred.promise); + authDeferred.resolve(); + + const state = sinon.spy($state, 'go'); + controller.signOut(); + $rootScope.$digest(); + expect(state).to.have.been.calledWith('auth.signin'); + }); + it('should invoke $toast.show and called with Sign Out Success!', function() { + const controller = makeController(); + const signOutMock = sinon.mock(controller.$mdDialog); + const signOutDeferred = makeDeferred(); + const authMock = sinon.mock(AuthService); + const authDeferred = makeDeferred(); + signOutMock.expects('show').returns(signOutDeferred.promise); + signOutDeferred.resolve(); + authMock.expects('signOut').returns(authDeferred.promise); + authDeferred.resolve(); + + const toast = sinon.spy($toast, 'show'); + controller.signOut(); + $rootScope.$digest(); + expect(toast).to.have.been.calledWith('Sign Out Success!'); + }); + }); + describe('when confirm logout and logout fail', function() { + it('should invoke $toast.show and called with Sign Out Failure!', function() { + const controller = makeController(); + const signOutMock = sinon.mock(controller.$mdDialog); + const signOutDeferred = makeDeferred(); + const authMock = sinon.mock(AuthService); + const authDeferred = makeDeferred(); + signOutMock.expects('show').returns(signOutDeferred.promise); + signOutDeferred.resolve(); + authMock.expects('signOut').returns(authDeferred.promise); + authDeferred.reject(); + + const toast = sinon.spy($toast, 'show'); + controller.signOut(); + $rootScope.$digest(); + expect(toast).to.have.been.calledWith('Sign Out Failure!'); + }) + }) +}); + + + From 433e27e022f8f13d404ea145df182c27dc8f579a Mon Sep 17 00:00:00 2001 From: chaoen Date: Fri, 15 Apr 2016 05:11:22 +0000 Subject: [PATCH 4/4] Edit sign out unit test Test three method in controller signOut() showConfirmMessage() executedSignOut() --- .../layout/top-navbar/top-navbar.spec.js | 94 ++++++------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/src/components/layout/top-navbar/top-navbar.spec.js b/src/components/layout/top-navbar/top-navbar.spec.js index a8397ae..197e5e5 100644 --- a/src/components/layout/top-navbar/top-navbar.spec.js +++ b/src/components/layout/top-navbar/top-navbar.spec.js @@ -48,104 +48,68 @@ describe('User log out', function() { expect(showDialog.called).to.eq(true); }); }); - describe('when confirm logout', function() { - it('should invoke controller.executedSignOut', function() { + describe('when showConfirmMessage', function() { + it('should invoke $mdDialog.confirm', function() { const controller = makeController(); - const signOutMock = sinon.mock(controller.$mdDialog); - const signOutDeferred = makeDeferred(); - signOutMock.expects('show').returns(signOutDeferred.promise); - signOutDeferred.resolve(); - controller.executedSignOut = () => {}; - - const executedSignOut = sinon.spy(controller, 'executedSignOut'); + const dialog = sinon.spy($mdDialog, 'confirm'); controller.signOut(); $rootScope.$digest(); - expect(executedSignOut.called).to.eq(true); + expect(dialog.called).to.eq(true); }); }); - describe('when un-confirm logout', function() { - it('should not invoke controller.executedSignOut', function() { - const controller = makeController(); - const signOutMock = sinon.mock(controller.$mdDialog); - const signOutDeferred = makeDeferred(); - signOutMock.expects('show').returns(signOutDeferred.promise); - signOutDeferred.reject(); - controller.executedSignOut = () => {}; - - const executedSignOut = sinon.spy(controller, 'executedSignOut'); - controller.signOut(); - $rootScope.$digest(); - expect(executedSignOut.called).to.eq(false); - }); - }); - describe('when confirm logout and logout success', function() { + describe('when executedSignOut resolve', function() { it('should invoke $auth.logout', function() { const controller = makeController(); - const signOutMock = sinon.mock(controller.$mdDialog); - const signOutDeferred = makeDeferred(); - const authMock = sinon.mock(AuthService); - const authDeferred = makeDeferred(); - signOutMock.expects('show').returns(signOutDeferred.promise); - signOutDeferred.resolve(); - authMock.expects('signOut').returns(authDeferred.promise); - authDeferred.resolve(); - + const AuthMock = sinon.mock(AuthService); + const deferred = makeDeferred(); + AuthMock.expects('signOut').returns(deferred.promise); + deferred.resolve(); const auth = sinon.spy($auth, 'logout'); - controller.signOut(); + controller.executedSignOut(); $rootScope.$digest(); expect(auth.called).to.eq(true); }); it('should invoke $state.go and called with auth.signin', function() { const controller = makeController(); - const signOutMock = sinon.mock(controller.$mdDialog); - const signOutDeferred = makeDeferred(); - const authMock = sinon.mock(AuthService); - const authDeferred = makeDeferred(); - signOutMock.expects('show').returns(signOutDeferred.promise); - signOutDeferred.resolve(); - authMock.expects('signOut').returns(authDeferred.promise); - authDeferred.resolve(); - + const AuthMock = sinon.mock(AuthService); + const deferred = makeDeferred(); + AuthMock.expects('signOut').returns(deferred.promise); + deferred.resolve(); const state = sinon.spy($state, 'go'); - controller.signOut(); + controller.executedSignOut(); $rootScope.$digest(); expect(state).to.have.been.calledWith('auth.signin'); }); - it('should invoke $toast.show and called with Sign Out Success!', function() { + it('should invoke $toast.show and called with success message', function() { const controller = makeController(); - const signOutMock = sinon.mock(controller.$mdDialog); - const signOutDeferred = makeDeferred(); - const authMock = sinon.mock(AuthService); - const authDeferred = makeDeferred(); - signOutMock.expects('show').returns(signOutDeferred.promise); - signOutDeferred.resolve(); - authMock.expects('signOut').returns(authDeferred.promise); - authDeferred.resolve(); - + const AuthMock = sinon.mock(AuthService); + const deferred = makeDeferred(); + AuthMock.expects('signOut').returns(deferred.promise); + deferred.resolve(); const toast = sinon.spy($toast, 'show'); - controller.signOut(); + controller.executedSignOut(); $rootScope.$digest(); expect(toast).to.have.been.calledWith('Sign Out Success!'); }); }); - describe('when confirm logout and logout fail', function() { - it('should invoke $toast.show and called with Sign Out Failure!', function() { + describe('when executedSignOut reject', function() { + it('should invoke $toast.show and call with fail message', function() { const controller = makeController(); const signOutMock = sinon.mock(controller.$mdDialog); - const signOutDeferred = makeDeferred(); + const deferred = makeDeferred(); const authMock = sinon.mock(AuthService); const authDeferred = makeDeferred(); - signOutMock.expects('show').returns(signOutDeferred.promise); - signOutDeferred.resolve(); + signOutMock.expects('show').returns(deferred.promise); + deferred.reject(); authMock.expects('signOut').returns(authDeferred.promise); authDeferred.reject(); const toast = sinon.spy($toast, 'show'); - controller.signOut(); + controller.executedSignOut(); $rootScope.$digest(); expect(toast).to.have.been.calledWith('Sign Out Failure!'); - }) - }) + }); + }); });