diff --git a/modules/articles/client/articles.client.module.js b/modules/articles/client/articles.client.module.js index 4f2b96b347..85ff0c3fb1 100644 --- a/modules/articles/client/articles.client.module.js +++ b/modules/articles/client/articles.client.module.js @@ -1,7 +1,7 @@ (function (app) { 'use strict'; - app.registerModule('articles'); + app.registerModule('articles', ['core']);// The core module is required for special route handling; see /core/client/config/core.client.routes app.registerModule('articles.services'); app.registerModule('articles.routes', ['ui.router', 'articles.services']); })(ApplicationConfiguration); diff --git a/modules/articles/tests/client/articles.client.routes.tests.js b/modules/articles/tests/client/articles.client.routes.tests.js index 32608c2af2..c850a2541e 100644 --- a/modules/articles/tests/client/articles.client.routes.tests.js +++ b/modules/articles/tests/client/articles.client.routes.tests.js @@ -38,6 +38,25 @@ }); }); + describe('List Route', function () { + var liststate; + beforeEach(inject(function ($state) { + liststate = $state.get('articles.list'); + })); + + it('Should have the correct URL', function () { + expect(liststate.url).toEqual(''); + }); + + it('Should not be abstract', function () { + expect(liststate.abstract).toBe(undefined); + }); + + it('Should have template', function () { + expect(liststate.templateUrl).toBe('modules/articles/client/views/list-articles.client.view.html'); + }); + }); + describe('View Route', function () { var viewstate, ArticlesController, @@ -190,6 +209,21 @@ }); }); + describe('Handle Trailing Slash', function () { + beforeEach(inject(function ($state, $rootScope) { + $state.go('articles.list'); + $rootScope.$digest(); + })); + + it('Should remove trailing slash', inject(function ($state, $location, $rootScope) { + $location.path('articles/'); + $rootScope.$digest(); + + expect($location.path()).toBe('/articles'); + expect($state.current.templateUrl).toBe('modules/articles/client/views/list-articles.client.view.html'); + })); + }); + }); }); })(); diff --git a/modules/chat/client/chat.client.module.js b/modules/chat/client/chat.client.module.js index 5e7669f891..e9a138d8fe 100644 --- a/modules/chat/client/chat.client.module.js +++ b/modules/chat/client/chat.client.module.js @@ -1,6 +1,6 @@ (function (app) { 'use strict'; - app.registerModule('chat'); + app.registerModule('chat', ['core']); app.registerModule('chat.routes', ['ui.router']); })(ApplicationConfiguration); diff --git a/modules/chat/tests/client/chat.client.routes.tests.js b/modules/chat/tests/client/chat.client.routes.tests.js new file mode 100644 index 0000000000..4e72f4f960 --- /dev/null +++ b/modules/chat/tests/client/chat.client.routes.tests.js @@ -0,0 +1,63 @@ +(function () { + 'use strict'; + + describe('Chat Route Tests', function () { + // Initialize global variables + var $scope, + Authentication; + + //We can start by loading the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_). + // This allows us to inject a service but then attach it to a variable + // with the same name as the service. + beforeEach(inject(function ($rootScope, _Authentication_) { + // Set a new global scope + $scope = $rootScope.$new(); + Authentication = _Authentication_; + })); + + describe('Route Config', function () { + describe('Main Route', function () { + var mainstate; + beforeEach(inject(function ($state) { + mainstate = $state.get('chat'); + })); + + it('Should have the correct URL', function () { + expect(mainstate.url).toEqual('/chat'); + }); + + it('Should not be abstract', function () { + expect(mainstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(mainstate.templateUrl).toBe('modules/chat/client/views/chat.client.view.html'); + }); + }); + + describe('Handle Trailing Slash', function () { + beforeEach(inject(function ($state, $rootScope, _Authentication_) { + Authentication.user = { + name: 'user', + roles: ['user'] + }; + + $state.go('chat'); + $rootScope.$digest(); + })); + + it('Should remove trailing slash', inject(function ($state, $location, $rootScope) { + $location.path('chat/'); + $rootScope.$digest(); + + expect($location.path()).toBe('/chat'); + expect($state.current.templateUrl).toBe('modules/chat/client/views/chat.client.view.html'); + })); + }); + + }); + }); +})(); diff --git a/modules/core/client/config/core.client.routes.js b/modules/core/client/config/core.client.routes.js index e67e3335d8..49714cb628 100644 --- a/modules/core/client/config/core.client.routes.js +++ b/modules/core/client/config/core.client.routes.js @@ -4,6 +4,17 @@ angular.module('core').config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) { + $urlRouterProvider.rule(function ($injector, $location) { + var path = $location.path(); + var hasTrailingSlash = path.length > 1 && path[path.length - 1] === '/'; + + if (hasTrailingSlash) { + //if last character is a slash, return the same url without the slash + var newPath = path.substr(0, path.length - 1); + $location.replace().path(newPath); + } + }); + // Redirect to 404 when route not found $urlRouterProvider.otherwise(function ($injector, $location) { $injector.get('$state').transitionTo('not-found', null, { diff --git a/modules/users/tests/client/users-admin.client.routes.tests.js b/modules/users/tests/client/users-admin.client.routes.tests.js new file mode 100644 index 0000000000..0a3f5ce8c2 --- /dev/null +++ b/modules/users/tests/client/users-admin.client.routes.tests.js @@ -0,0 +1,101 @@ +(function () { + 'use strict'; + + describe('Users Admin Route Tests', function () { + // Initialize global variables + var $scope, + Authentication; + + //We can start by loading the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_). + // This allows us to inject a service but then attach it to a variable + // with the same name as the service. + beforeEach(inject(function ($rootScope, _Authentication_) { + // Set a new global scope + $scope = $rootScope.$new(); + Authentication = _Authentication_; + })); + + describe('Route Config', function () { + describe('Main Route', function () { + var mainstate; + beforeEach(inject(function ($state) { + mainstate = $state.get('admin.users'); + })); + + it('Should have the correct URL', function () { + expect(mainstate.url).toEqual('/users'); + }); + + it('Should not be abstract', function () { + expect(mainstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(mainstate.templateUrl).toBe('modules/users/client/views/admin/list-users.client.view.html'); + }); + }); + + describe('View Route', function () { + var viewstate; + beforeEach(inject(function ($state) { + viewstate = $state.get('admin.user'); + })); + + it('Should have the correct URL', function () { + expect(viewstate.url).toEqual('/users/:userId'); + }); + + it('Should not be abstract', function () { + expect(viewstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(viewstate.templateUrl).toBe('modules/users/client/views/admin/view-user.client.view.html'); + }); + }); + + describe('Edit Route', function () { + var editstate; + beforeEach(inject(function ($state) { + editstate = $state.get('admin.user-edit'); + })); + + it('Should have the correct URL', function () { + expect(editstate.url).toEqual('/users/:userId/edit'); + }); + + it('Should not be abstract', function () { + expect(editstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(editstate.templateUrl).toBe('modules/users/client/views/admin/edit-user.client.view.html'); + }); + }); + + describe('Handle Trailing Slash', function () { + beforeEach(inject(function ($state, $rootScope, _Authentication_) { + Authentication.user = { + name: 'user', + roles: ['admin'] + }; + + $state.go('admin.users'); + $rootScope.$digest(); + })); + + it('Should remove trailing slash', inject(function ($state, $location, $rootScope) { + $location.path('admin/users/'); + $rootScope.$digest(); + + expect($location.path()).toBe('/admin/users'); + expect($state.current.templateUrl).toBe('modules/users/client/views/admin/list-users.client.view.html'); + })); + }); + + }); + }); +})(); diff --git a/modules/users/tests/client/users.client.routes.tests.js b/modules/users/tests/client/users.client.routes.tests.js new file mode 100644 index 0000000000..0861de02e3 --- /dev/null +++ b/modules/users/tests/client/users.client.routes.tests.js @@ -0,0 +1,319 @@ +(function () { + 'use strict'; + + describe('Users Route Tests', function () { + // Initialize global variables + var $scope, + Authentication; + + //We can start by loading the main application module + beforeEach(module(ApplicationConfiguration.applicationModuleName)); + + // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_). + // This allows us to inject a service but then attach it to a variable + // with the same name as the service. + beforeEach(inject(function ($rootScope, _Authentication_) { + // Set a new global scope + $scope = $rootScope.$new(); + Authentication = _Authentication_; + })); + + describe('Settings Route Config', function () { + describe('Main Route', function () { + var mainstate; + beforeEach(inject(function ($state) { + mainstate = $state.get('settings'); + })); + + it('Should have the correct URL', function () { + expect(mainstate.url).toEqual('/settings'); + }); + + it('Should be abstract', function () { + expect(mainstate.abstract).toBe(true); + }); + + it('Should have templateUrl', function () { + expect(mainstate.templateUrl).toBe('modules/users/client/views/settings/settings.client.view.html'); + }); + }); + + describe('Profile Route', function () { + var profilestate; + beforeEach(inject(function ($state) { + profilestate = $state.get('settings.profile'); + })); + + it('Should have the correct URL', function () { + expect(profilestate.url).toEqual('/profile'); + }); + + it('Should not be abstract', function () { + expect(profilestate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(profilestate.templateUrl).toBe('modules/users/client/views/settings/edit-profile.client.view.html'); + }); + }); + + describe('Password Route', function () { + var passwordstate; + beforeEach(inject(function ($state) { + passwordstate = $state.get('settings.password'); + })); + + it('Should have the correct URL', function () { + expect(passwordstate.url).toEqual('/password'); + }); + + it('Should not be abstract', function () { + expect(passwordstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(passwordstate.templateUrl).toBe('modules/users/client/views/settings/change-password.client.view.html'); + }); + }); + + describe('Accounts Route', function () { + var accountsstate; + beforeEach(inject(function ($state) { + accountsstate = $state.get('settings.accounts'); + })); + + it('Should have the correct URL', function () { + expect(accountsstate.url).toEqual('/accounts'); + }); + + it('Should not be abstract', function () { + expect(accountsstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(accountsstate.templateUrl).toBe('modules/users/client/views/settings/manage-social-accounts.client.view.html'); + }); + }); + + describe('Picture Route', function () { + var picturestate; + beforeEach(inject(function ($state) { + picturestate = $state.get('settings.picture'); + })); + + it('Should have the correct URL', function () { + expect(picturestate.url).toEqual('/picture'); + }); + + it('Should not be abstract', function () { + expect(picturestate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(picturestate.templateUrl).toBe('modules/users/client/views/settings/change-profile-picture.client.view.html'); + }); + }); + + describe('Handle Trailing Slash', function () { + beforeEach(inject(function ($state, $rootScope, _Authentication_) { + Authentication.user = { + name: 'user', + roles: ['user'] + }; + + $state.go('settings.profile'); + $rootScope.$digest(); + })); + + it('Should remove trailing slash', inject(function ($state, $location, $rootScope) { + $location.path('settings/profile/'); + $rootScope.$digest(); + + expect($location.path()).toBe('/settings/profile'); + expect($state.current.templateUrl).toBe('modules/users/client/views/settings/edit-profile.client.view.html'); + })); + }); + + }); + + describe('Authentication Route Config', function () { + describe('Main Route', function () { + var mainstate; + beforeEach(inject(function ($state) { + mainstate = $state.get('authentication'); + })); + + it('Should have the correct URL', function () { + expect(mainstate.url).toEqual('/authentication'); + }); + + it('Should be abstract', function () { + expect(mainstate.abstract).toBe(true); + }); + + it('Should have templateUrl', function () { + expect(mainstate.templateUrl).toBe('modules/users/client/views/authentication/authentication.client.view.html'); + }); + }); + + describe('Signup Route', function () { + var signupstate; + beforeEach(inject(function ($state) { + signupstate = $state.get('authentication.signup'); + })); + + it('Should have the correct URL', function () { + expect(signupstate.url).toEqual('/signup'); + }); + + it('Should not be abstract', function () { + expect(signupstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(signupstate.templateUrl).toBe('modules/users/client/views/authentication/signup.client.view.html'); + }); + }); + + describe('Signin Route', function () { + var signinstate; + beforeEach(inject(function ($state) { + signinstate = $state.get('authentication.signin'); + })); + + it('Should have the correct URL', function () { + expect(signinstate.url).toEqual('/signin?err'); + }); + + it('Should not be abstract', function () { + expect(signinstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(signinstate.templateUrl).toBe('modules/users/client/views/authentication/signin.client.view.html'); + }); + }); + + }); + + describe('Password Route Config', function () { + describe('Main Route', function () { + var mainstate; + beforeEach(inject(function ($state) { + mainstate = $state.get('password'); + })); + + it('Should have the correct URL', function () { + expect(mainstate.url).toEqual('/password'); + }); + + it('Should be abstract', function () { + expect(mainstate.abstract).toBe(true); + }); + + it('Should have template', function () { + expect(mainstate.template).toBe(''); + }); + }); + + describe('Forgot Route', function () { + var forgotstate; + beforeEach(inject(function ($state) { + forgotstate = $state.get('password.forgot'); + })); + + it('Should have the correct URL', function () { + expect(forgotstate.url).toEqual('/forgot'); + }); + + it('Should not be abstract', function () { + expect(forgotstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(forgotstate.templateUrl).toBe('modules/users/client/views/password/forgot-password.client.view.html'); + }); + }); + + }); + + describe('Password Reset Route Config', function () { + describe('Main Route', function () { + var mainstate; + beforeEach(inject(function ($state) { + mainstate = $state.get('password.reset'); + })); + + it('Should have the correct URL', function () { + expect(mainstate.url).toEqual('/reset'); + }); + + it('Should be abstract', function () { + expect(mainstate.abstract).toBe(true); + }); + + it('Should have template', function () { + expect(mainstate.template).toBe(''); + }); + }); + + describe('Invalid Route', function () { + var invalidstate; + beforeEach(inject(function ($state) { + invalidstate = $state.get('password.reset.invalid'); + })); + + it('Should have the correct URL', function () { + expect(invalidstate.url).toEqual('/invalid'); + }); + + it('Should not be abstract', function () { + expect(invalidstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(invalidstate.templateUrl).toBe('modules/users/client/views/password/reset-password-invalid.client.view.html'); + }); + }); + + describe('Success Route', function () { + var successstate; + beforeEach(inject(function ($state) { + successstate = $state.get('password.reset.success'); + })); + + it('Should have the correct URL', function () { + expect(successstate.url).toEqual('/success'); + }); + + it('Should not be abstract', function () { + expect(successstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(successstate.templateUrl).toBe('modules/users/client/views/password/reset-password-success.client.view.html'); + }); + }); + + describe('Form Route', function () { + var formstate; + beforeEach(inject(function ($state) { + formstate = $state.get('password.reset.form'); + })); + + it('Should have the correct URL', function () { + expect(formstate.url).toEqual('/:token'); + }); + + it('Should not be abstract', function () { + expect(formstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(formstate.templateUrl).toBe('modules/users/client/views/password/reset-password.client.view.html'); + }); + }); + + }); + }); +})();