diff --git a/bower.json b/bower.json index 9738f46d..ecfe2fc4 100644 --- a/bower.json +++ b/bower.json @@ -10,6 +10,7 @@ "angular-messages": "~1.5.0", "angular-mocks": "~1.5.0", "angular-resource": "~1.5.0", + "angular-ui-notification": "~0.2.0", "angular-ui-router": "~0.2.18", "bootstrap": "~3.3.6", "ng-file-upload": "^12.1.0", diff --git a/config/assets/default.js b/config/assets/default.js index a2ea678f..ff8cb81f 100644 --- a/config/assets/default.js +++ b/config/assets/default.js @@ -9,7 +9,8 @@ module.exports = { // bower:css 'public/lib/bootstrap/dist/css/bootstrap.css', 'public/lib/bootstrap/dist/css/bootstrap-theme.css', - 'public/lib/ng-img-crop/compile/unminified/ng-img-crop.css' + 'public/lib/ng-img-crop/compile/unminified/ng-img-crop.css', + 'public/lib/angular-ui-notification/dist/angular-ui-notification.css' // endbower ], js: [ @@ -22,6 +23,7 @@ module.exports = { 'public/lib/angular-messages/angular-messages.js', 'public/lib/angular-mocks/angular-mocks.js', 'public/lib/angular-resource/angular-resource.js', + 'public/lib/angular-ui-notification/dist/angular-ui-notification.js', 'public/lib/angular-ui-router/release/angular-ui-router.js', 'public/lib/owasp-password-strength-test/owasp-password-strength-test.js', // endbower diff --git a/config/assets/production.js b/config/assets/production.js index 7ad2b2f1..ed52e98e 100644 --- a/config/assets/production.js +++ b/config/assets/production.js @@ -10,6 +10,7 @@ module.exports = { 'public/lib/bootstrap/dist/css/bootstrap.min.css', 'public/lib/bootstrap/dist/css/bootstrap-theme.min.css', 'public/lib/ng-img-crop/compile/minified/ng-img-crop.css', + 'public/lib/angular-ui-notification/dist/angular-ui-notification.min.css' // endbower ], js: [ @@ -20,6 +21,7 @@ module.exports = { 'public/lib/angular-messages/angular-messages.min.js', 'public/lib/angular-mocks/angular-mocks.js', 'public/lib/angular-resource/angular-resource.min.js', + 'public/lib/angular-ui-notification/dist/angular-ui-notification.min.js', 'public/lib/angular-ui-router/release/angular-ui-router.min.js', 'public/lib/ng-file-upload/ng-file-upload.min.js', 'public/lib/ng-img-crop/compile/minified/ng-img-crop.js', diff --git a/gulpfile.js b/gulpfile.js index deca1aef..2f8dc8f5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -68,29 +68,29 @@ gulp.task('nodemon-nodebug', function () { // Watch Files For Changes gulp.task('watch', function () { // Start livereload - plugins.livereload.listen(); + plugins.refresh.listen(); // Add watch rules - gulp.watch(defaultAssets.server.views).on('change', plugins.livereload.changed); - gulp.watch(defaultAssets.server.allJS, ['eslint']).on('change', plugins.livereload.changed); - gulp.watch(defaultAssets.client.js, ['eslint']).on('change', plugins.livereload.changed); - gulp.watch(defaultAssets.client.css, ['csslint']).on('change', plugins.livereload.changed); - gulp.watch(defaultAssets.client.sass, ['sass', 'csslint']).on('change', plugins.livereload.changed); - gulp.watch(defaultAssets.client.less, ['less', 'csslint']).on('change', plugins.livereload.changed); + gulp.watch(defaultAssets.server.views).on('change', plugins.refresh.changed); + gulp.watch(defaultAssets.server.allJS, ['eslint']).on('change', plugins.refresh.changed); + gulp.watch(defaultAssets.client.js, ['eslint']).on('change', plugins.refresh.changed); + gulp.watch(defaultAssets.client.css, ['csslint']).on('change', plugins.refresh.changed); + gulp.watch(defaultAssets.client.sass, ['sass', 'csslint']).on('change', plugins.refresh.changed); + gulp.watch(defaultAssets.client.less, ['less', 'csslint']).on('change', plugins.refresh.changed); if (process.env.NODE_ENV === 'production') { gulp.watch(defaultAssets.server.gulpConfig, ['templatecache', 'eslint']); - gulp.watch(defaultAssets.client.views, ['templatecache']).on('change', plugins.livereload.changed); + gulp.watch(defaultAssets.client.views, ['templatecache']).on('change', plugins.refresh.changed); } else { gulp.watch(defaultAssets.server.gulpConfig, ['eslint']); - gulp.watch(defaultAssets.client.views).on('change', plugins.livereload.changed); + gulp.watch(defaultAssets.client.views).on('change', plugins.refresh.changed); } }); // Watch server test files gulp.task('watch:server:run-tests', function () { // Start livereload - plugins.livereload.listen(); + plugins.refresh.listen(); // Add Server Test file rules gulp.watch([testAssets.tests.server, defaultAssets.server.allJS], ['test:server']).on('change', function (file) { @@ -108,7 +108,7 @@ gulp.task('watch:server:run-tests', function () { }); }); - plugins.livereload.changed(); + plugins.refresh.changed(); }); }); diff --git a/modules/articles/client/config/articles-admin.client.routes.js b/modules/articles/client/config/articles-admin.client.routes.js index b15d918c..b493d0e0 100644 --- a/modules/articles/client/config/articles-admin.client.routes.js +++ b/modules/articles/client/config/articles-admin.client.routes.js @@ -16,7 +16,7 @@ }) .state('admin.articles.list', { url: '', - templateUrl: 'modules/articles/client/views/admin/list-articles.client.view.html', + templateUrl: '/modules/articles/client/views/admin/list-articles.client.view.html', controller: 'ArticlesAdminListController', controllerAs: 'vm', data: { @@ -25,7 +25,7 @@ }) .state('admin.articles.create', { url: '/create', - templateUrl: 'modules/articles/client/views/admin/form-article.client.view.html', + templateUrl: '/modules/articles/client/views/admin/form-article.client.view.html', controller: 'ArticlesAdminController', controllerAs: 'vm', data: { @@ -37,7 +37,7 @@ }) .state('admin.articles.edit', { url: '/:articleId/edit', - templateUrl: 'modules/articles/client/views/admin/form-article.client.view.html', + templateUrl: '/modules/articles/client/views/admin/form-article.client.view.html', controller: 'ArticlesAdminController', controllerAs: 'vm', data: { diff --git a/modules/articles/client/config/articles.client.routes.js b/modules/articles/client/config/articles.client.routes.js index a16926e5..4a94fa2e 100644 --- a/modules/articles/client/config/articles.client.routes.js +++ b/modules/articles/client/config/articles.client.routes.js @@ -16,7 +16,7 @@ }) .state('articles.list', { url: '', - templateUrl: 'modules/articles/client/views/list-articles.client.view.html', + templateUrl: '/modules/articles/client/views/list-articles.client.view.html', controller: 'ArticlesListController', controllerAs: 'vm', data: { @@ -25,7 +25,7 @@ }) .state('articles.view', { url: '/:articleId', - templateUrl: 'modules/articles/client/views/view-article.client.view.html', + templateUrl: '/modules/articles/client/views/view-article.client.view.html', controller: 'ArticlesController', controllerAs: 'vm', resolve: { diff --git a/modules/articles/client/controllers/admin/article.client.controller.js b/modules/articles/client/controllers/admin/article.client.controller.js index 219390df..97f730d3 100644 --- a/modules/articles/client/controllers/admin/article.client.controller.js +++ b/modules/articles/client/controllers/admin/article.client.controller.js @@ -5,14 +5,13 @@ .module('articles.admin') .controller('ArticlesAdminController', ArticlesAdminController); - ArticlesAdminController.$inject = ['$scope', '$state', '$window', 'articleResolve', 'Authentication']; + ArticlesAdminController.$inject = ['$scope', '$state', '$window', 'articleResolve', 'Authentication', 'Notification']; - function ArticlesAdminController($scope, $state, $window, article, Authentication) { + function ArticlesAdminController($scope, $state, $window, article, Authentication, Notification) { var vm = this; vm.article = article; vm.authentication = Authentication; - vm.error = null; vm.form = {}; vm.remove = remove; vm.save = save; @@ -22,6 +21,7 @@ if ($window.confirm('Are you sure you want to delete?')) { vm.article.$remove(function() { $state.go('admin.articles.list'); + Notification.success({ message: ' Article deleted successfully!' }); }); } } @@ -40,10 +40,11 @@ function successCallback(res) { $state.go('admin.articles.list'); // should we send the User to the list or the updated Article's view? + Notification.success({ message: ' Article saved successfully!' }); } function errorCallback(res) { - vm.error = res.data.message; + Notification.error({ message: res.data.message, title: ' Article save error!' }); } } } diff --git a/modules/articles/client/controllers/articles.client.controller.js b/modules/articles/client/controllers/articles.client.controller.js index 58d2c7d5..40708dd9 100644 --- a/modules/articles/client/controllers/articles.client.controller.js +++ b/modules/articles/client/controllers/articles.client.controller.js @@ -12,7 +12,6 @@ vm.article = article; vm.authentication = Authentication; - vm.error = null; } }()); diff --git a/modules/articles/client/services/articles.client.service.js b/modules/articles/client/services/articles.client.service.js index e10e00fa..5e653ca4 100644 --- a/modules/articles/client/services/articles.client.service.js +++ b/modules/articles/client/services/articles.client.service.js @@ -5,10 +5,10 @@ .module('articles.services') .factory('ArticlesService', ArticlesService); - ArticlesService.$inject = ['$resource']; + ArticlesService.$inject = ['$resource', '$log']; - function ArticlesService($resource) { - var Article = $resource('api/articles/:articleId', { + function ArticlesService($resource, $log) { + var Article = $resource('/api/articles/:articleId', { articleId: '@_id' }, { update: { @@ -47,7 +47,7 @@ function handleError(error) { // Log error - console.log(error); + $log.error(error); } } }()); diff --git a/modules/articles/client/views/admin/form-article.client.view.html b/modules/articles/client/views/admin/form-article.client.view.html index f7d20e8f..15f05dbe 100644 --- a/modules/articles/client/views/admin/form-article.client.view.html +++ b/modules/articles/client/views/admin/form-article.client.view.html @@ -24,9 +24,6 @@
-
- -
diff --git a/modules/articles/server/controllers/articles.server.controller.js b/modules/articles/server/controllers/articles.server.controller.js index 18d6a3b9..ddba5fe1 100644 --- a/modules/articles/server/controllers/articles.server.controller.js +++ b/modules/articles/server/controllers/articles.server.controller.js @@ -17,7 +17,7 @@ exports.create = function (req, res) { article.save(function (err) { if (err) { - return res.status(400).send({ + return res.status(422).send({ message: errorHandler.getErrorMessage(err) }); } else { @@ -51,7 +51,7 @@ exports.update = function (req, res) { article.save(function (err) { if (err) { - return res.status(400).send({ + return res.status(422).send({ message: errorHandler.getErrorMessage(err) }); } else { @@ -68,7 +68,7 @@ exports.delete = function (req, res) { article.remove(function (err) { if (err) { - return res.status(400).send({ + return res.status(422).send({ message: errorHandler.getErrorMessage(err) }); } else { @@ -83,7 +83,7 @@ exports.delete = function (req, res) { exports.list = function (req, res) { Article.find().sort('-created').populate('user', 'displayName').exec(function (err, articles) { if (err) { - return res.status(400).send({ + return res.status(422).send({ message: errorHandler.getErrorMessage(err) }); } else { diff --git a/modules/articles/tests/client/admin.articles.client.controller.tests.js b/modules/articles/tests/client/admin.articles.client.controller.tests.js index 2b99aa48..32964a8f 100644 --- a/modules/articles/tests/client/admin.articles.client.controller.tests.js +++ b/modules/articles/tests/client/admin.articles.client.controller.tests.js @@ -9,7 +9,8 @@ $state, Authentication, ArticlesService, - mockArticle; + mockArticle, + Notification; // The $resource service augments the response object with methods for updating and deleting the resource. // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match @@ -36,7 +37,7 @@ // 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 ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_) { + beforeEach(inject(function ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_, _Notification_) { // Set a new global scope $scope = $rootScope.$new(); @@ -45,6 +46,10 @@ $state = _$state_; Authentication = _Authentication_; ArticlesService = _ArticlesService_; + Notification = _Notification_; + + // Ignore parent template get on state transitions + $httpBackend.whenGET('/modules/core/client/views/home.client.view.html').respond(200, ''); // create mock article mockArticle = new ArticlesService({ @@ -66,6 +71,8 @@ // Spy on state go spyOn($state, 'go'); + spyOn(Notification, 'error'); + spyOn(Notification, 'success'); })); describe('vm.save() as create', function () { @@ -83,26 +90,28 @@ it('should send a POST request with the form input values and then locate to new object URL', inject(function (ArticlesService) { // Set POST response - $httpBackend.expectPOST('api/articles', sampleArticlePostData).respond(mockArticle); + $httpBackend.expectPOST('/api/articles', sampleArticlePostData).respond(mockArticle); // Run controller functionality $scope.vm.save(true); $httpBackend.flush(); + // Test Notification success was called + expect(Notification.success).toHaveBeenCalledWith({ message: ' Article saved successfully!' }); // Test URL redirection after the article was created expect($state.go).toHaveBeenCalledWith('admin.articles.list'); })); - it('should set $scope.vm.error if error', function () { + it('should call Notification.error if error', function () { var errorMessage = 'this is an error message'; - $httpBackend.expectPOST('api/articles', sampleArticlePostData).respond(400, { + $httpBackend.expectPOST('/api/articles', sampleArticlePostData).respond(400, { message: errorMessage }); $scope.vm.save(true); $httpBackend.flush(); - expect($scope.vm.error).toBe(errorMessage); + expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: ' Article save error!' }); }); }); @@ -120,11 +129,13 @@ $scope.vm.save(true); $httpBackend.flush(); + // Test Notification success was called + expect(Notification.success).toHaveBeenCalledWith({ message: ' Article saved successfully!' }); // Test URL location to new object expect($state.go).toHaveBeenCalledWith('admin.articles.list'); })); - it('should set $scope.vm.error if error', inject(function (ArticlesService) { + it('should call Notification.error if error', inject(function (ArticlesService) { var errorMessage = 'error'; $httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond(400, { message: errorMessage @@ -133,7 +144,7 @@ $scope.vm.save(true); $httpBackend.flush(); - expect($scope.vm.error).toBe(errorMessage); + expect(Notification.error).toHaveBeenCalledWith({ message: errorMessage, title: ' Article save error!' }); })); }); @@ -152,6 +163,7 @@ $scope.vm.remove(); $httpBackend.flush(); + expect(Notification.success).toHaveBeenCalledWith({ message: ' Article deleted successfully!' }); expect($state.go).toHaveBeenCalledWith('admin.articles.list'); }); diff --git a/modules/articles/tests/client/admin.articles.client.routes.tests.js b/modules/articles/tests/client/admin.articles.client.routes.tests.js index e699ae45..0a7873e4 100644 --- a/modules/articles/tests/client/admin.articles.client.routes.tests.js +++ b/modules/articles/tests/client/admin.articles.client.routes.tests.js @@ -53,7 +53,7 @@ }); it('Should have templateUrl', function () { - expect(liststate.templateUrl).toBe('modules/articles/client/views/admin/list-articles.client.view.html'); + expect(liststate.templateUrl).toBe('/modules/articles/client/views/admin/list-articles.client.view.html'); }); }); @@ -64,7 +64,7 @@ beforeEach(inject(function ($controller, $state, $templateCache) { createstate = $state.get('admin.articles.create'); - $templateCache.put('modules/articles/client/views/admin/form-article.client.view.html', ''); + $templateCache.put('/modules/articles/client/views/admin/form-article.client.view.html', ''); // Create mock article mockArticle = new ArticlesService(); @@ -99,7 +99,7 @@ }); it('Should have templateUrl', function () { - expect(createstate.templateUrl).toBe('modules/articles/client/views/admin/form-article.client.view.html'); + expect(createstate.templateUrl).toBe('/modules/articles/client/views/admin/form-article.client.view.html'); }); }); @@ -110,7 +110,7 @@ beforeEach(inject(function ($controller, $state, $templateCache) { editstate = $state.get('admin.articles.edit'); - $templateCache.put('modules/articles/client/views/admin/form-article.client.view.html', ''); + $templateCache.put('/modules/articles/client/views/admin/form-article.client.view.html', ''); // Create mock article mockArticle = new ArticlesService({ @@ -150,7 +150,7 @@ }); it('Should have templateUrl', function () { - expect(editstate.templateUrl).toBe('modules/articles/client/views/admin/form-article.client.view.html'); + expect(editstate.templateUrl).toBe('/modules/articles/client/views/admin/form-article.client.view.html'); }); xit('Should go to unauthorized route', function () { diff --git a/modules/articles/tests/client/admin.list.articles.client.controller.tests.js b/modules/articles/tests/client/admin.list.articles.client.controller.tests.js index e4e99dbb..90bd19f1 100644 --- a/modules/articles/tests/client/admin.list.articles.client.controller.tests.js +++ b/modules/articles/tests/client/admin.list.articles.client.controller.tests.js @@ -46,6 +46,10 @@ Authentication = _Authentication_; ArticlesService = _ArticlesService_; + // Ignore parent template get on state transitions + $httpBackend.whenGET('/modules/articles/client/views/list-articles.client.view.html').respond(200, ''); + $httpBackend.whenGET('/modules/core/client/views/home.client.view.html').respond(200, ''); + // create mock article mockArticle = new ArticlesService({ _id: '525a8422f6d0f87f0e407a33', @@ -76,7 +80,7 @@ it('should send a GET request and return all articles', inject(function (ArticlesService) { // Set POST response - $httpBackend.expectGET('api/articles').respond(mockArticleList); + $httpBackend.expectGET('/api/articles').respond(mockArticleList); $httpBackend.flush(); diff --git a/modules/articles/tests/client/articles.client.routes.tests.js b/modules/articles/tests/client/articles.client.routes.tests.js index 650223d8..7b724a5c 100644 --- a/modules/articles/tests/client/articles.client.routes.tests.js +++ b/modules/articles/tests/client/articles.client.routes.tests.js @@ -53,7 +53,7 @@ }); it('Should have templateUrl', function () { - expect(liststate.templateUrl).toBe('modules/articles/client/views/list-articles.client.view.html'); + expect(liststate.templateUrl).toBe('/modules/articles/client/views/list-articles.client.view.html'); }); }); @@ -64,7 +64,7 @@ beforeEach(inject(function ($controller, $state, $templateCache) { viewstate = $state.get('articles.view'); - $templateCache.put('modules/articles/client/views/view-article.client.view.html', ''); + $templateCache.put('/modules/articles/client/views/view-article.client.view.html', ''); // create mock article mockArticle = new ArticlesService({ @@ -104,12 +104,14 @@ }); it('Should have templateUrl', function () { - expect(viewstate.templateUrl).toBe('modules/articles/client/views/view-article.client.view.html'); + expect(viewstate.templateUrl).toBe('/modules/articles/client/views/view-article.client.view.html'); }); }); describe('Handle Trailing Slash', function () { - beforeEach(inject(function ($state, $rootScope) { + beforeEach(inject(function ($state, $rootScope, $templateCache) { + $templateCache.put('/modules/articles/client/views/list-articles.client.view.html', ''); + $state.go('articles.list'); $rootScope.$digest(); })); @@ -119,7 +121,7 @@ $rootScope.$digest(); expect($location.path()).toBe('/articles'); - expect($state.current.templateUrl).toBe('modules/articles/client/views/list-articles.client.view.html'); + expect($state.current.templateUrl).toBe('/modules/articles/client/views/list-articles.client.view.html'); })); }); }); diff --git a/modules/articles/tests/client/list-articles.client.controller.tests.js b/modules/articles/tests/client/list-articles.client.controller.tests.js index ee654f47..b5e37c9f 100644 --- a/modules/articles/tests/client/list-articles.client.controller.tests.js +++ b/modules/articles/tests/client/list-articles.client.controller.tests.js @@ -76,8 +76,10 @@ it('should send a GET request and return all articles', inject(function (ArticlesService) { // Set POST response - $httpBackend.expectGET('api/articles').respond(mockArticleList); + $httpBackend.expectGET('/api/articles').respond(mockArticleList); + // Ignore parent template get on state transition + $httpBackend.whenGET('/modules/core/client/views/home.client.view.html').respond(200, ''); $httpBackend.flush(); diff --git a/modules/articles/tests/server/admin.article.server.routes.tests.js b/modules/articles/tests/server/admin.article.server.routes.tests.js index e56119da..0322dd14 100644 --- a/modules/articles/tests/server/admin.article.server.routes.tests.js +++ b/modules/articles/tests/server/admin.article.server.routes.tests.js @@ -170,7 +170,7 @@ describe('Article Admin CRUD tests', function () { // Save a new article agent.post('/api/articles') .send(article) - .expect(400) + .expect(422) .end(function (articleSaveErr, articleSaveRes) { // Set message assertion (articleSaveRes.body.message).should.match('Title cannot be blank'); diff --git a/modules/chat/client/config/chat.client.routes.js b/modules/chat/client/config/chat.client.routes.js index 4ec07d8b..f69aff61 100644 --- a/modules/chat/client/config/chat.client.routes.js +++ b/modules/chat/client/config/chat.client.routes.js @@ -11,7 +11,7 @@ $stateProvider .state('chat', { url: '/chat', - templateUrl: 'modules/chat/client/views/chat.client.view.html', + templateUrl: '/modules/chat/client/views/chat.client.view.html', controller: 'ChatController', controllerAs: 'vm', data: { diff --git a/modules/chat/client/views/chat.client.view.html b/modules/chat/client/views/chat.client.view.html index 3b9174c6..ad45efa5 100644 --- a/modules/chat/client/views/chat.client.view.html +++ b/modules/chat/client/views/chat.client.view.html @@ -18,7 +18,7 @@
  • - {{message.username}} + {{message.username}}

    diff --git a/modules/chat/tests/client/chat.client.controller.tests.js b/modules/chat/tests/client/chat.client.controller.tests.js index 50139f68..693ceb3b 100644 --- a/modules/chat/tests/client/chat.client.controller.tests.js +++ b/modules/chat/tests/client/chat.client.controller.tests.js @@ -11,7 +11,8 @@ ChatController, $timeout, $state, - Authentication; + Authentication, + $httpBackend; // Load the main application module beforeEach(module(ApplicationConfiguration.applicationModuleName)); @@ -39,12 +40,17 @@ }); describe('when user logged in', function () { - beforeEach(inject(function ($controller, $rootScope, _Socket_, _Authentication_, _$timeout_, _$state_) { + beforeEach(inject(function ($controller, $rootScope, _$httpBackend_, _Socket_, _Authentication_, _$timeout_, _$state_) { Authentication.user = { name: 'user', roles: ['user'] }; + $httpBackend = _$httpBackend_; + + // Ignore parent template get on state transitions + $httpBackend.whenGET('/modules/core/client/views/home.client.view.html').respond(200, ''); + ChatController = $controller('ChatController as vm', { $scope: $scope }); diff --git a/modules/chat/tests/client/chat.client.routes.tests.js b/modules/chat/tests/client/chat.client.routes.tests.js index bbd447ee..2f16e8db 100644 --- a/modules/chat/tests/client/chat.client.routes.tests.js +++ b/modules/chat/tests/client/chat.client.routes.tests.js @@ -4,7 +4,8 @@ describe('Chat Route Tests', function () { // Initialize global variables var $scope, - Authentication; + Authentication, + $httpBackend; // We can start by loading the main application module beforeEach(module(ApplicationConfiguration.applicationModuleName)); @@ -34,27 +35,35 @@ }); it('Should have templateUrl', function () { - expect(mainstate.templateUrl).toBe('modules/chat/client/views/chat.client.view.html'); + expect(mainstate.templateUrl).toBe('/modules/chat/client/views/chat.client.view.html'); }); }); describe('Handle Trailing Slash', function () { - beforeEach(inject(function ($state, $rootScope, _Authentication_) { + beforeEach(inject(function ($state, $rootScope, _$httpBackend_, _Authentication_) { Authentication.user = { name: 'user', roles: ['user'] }; + $httpBackend = _$httpBackend_; + + // Ignore parent template get on state transition + $httpBackend.whenGET('/modules/chat/client/views/chat.client.view.html').respond(200); + $httpBackend.whenGET('/modules/core/client/views/home.client.view.html').respond(200, ''); + $state.go('chat'); $rootScope.$digest(); })); - it('Should remove trailing slash', inject(function ($state, $location, $rootScope) { + it('Should remove trailing slash', inject(function ($state, $location, $rootScope, $templateCache) { + $templateCache.put('/modules/chat/client/views/chat.client.view.html', ''); + $location.path('chat/'); $rootScope.$digest(); expect($location.path()).toBe('/chat'); - expect($state.current.templateUrl).toBe('modules/chat/client/views/chat.client.view.html'); + expect($state.current.templateUrl).toBe('/modules/chat/client/views/chat.client.view.html'); })); }); diff --git a/modules/core/client/app/config.js b/modules/core/client/app/config.js index 754a6cbc..a8a093a5 100644 --- a/modules/core/client/app/config.js +++ b/modules/core/client/app/config.js @@ -6,7 +6,7 @@ var service = { applicationEnvironment: window.env, applicationModuleName: applicationModuleName, - applicationModuleVendorDependencies: ['ngResource', 'ngAnimate', 'ngMessages', 'ui.router', 'ui.bootstrap', 'ngFileUpload', 'ngImgCrop'], + applicationModuleVendorDependencies: ['ngResource', 'ngAnimate', 'ngMessages', 'ui.router', 'ui.bootstrap', 'ngFileUpload', 'ngImgCrop', 'ui-notification'], registerModule: registerModule }; @@ -20,4 +20,17 @@ // Add the module to the AngularJS configuration file angular.module(applicationModuleName).requires.push(moduleName); } + + // Angular-ui-notification configuration + angular.module('ui-notification').config(function(NotificationProvider) { + NotificationProvider.setOptions({ + delay: 2000, + startTop: 20, + startRight: 10, + verticalSpacing: 20, + horizontalSpacing: 20, + positionX: 'right', + positionY: 'bottom' + }); + }); }(window)); diff --git a/modules/core/client/app/init.js b/modules/core/client/app/init.js index 45c5b06b..8ffaef33 100644 --- a/modules/core/client/app/init.js +++ b/modules/core/client/app/init.js @@ -10,17 +10,22 @@ .module(app.applicationModuleName) .config(bootstrapConfig); - function bootstrapConfig($compileProvider, $locationProvider, $httpProvider) { - $locationProvider.html5Mode(true).hashPrefix('!'); + bootstrapConfig.$inject = ['$compileProvider', '$locationProvider', '$httpProvider', '$logProvider']; + + function bootstrapConfig($compileProvider, $locationProvider, $httpProvider, $logProvider) { + $locationProvider.html5Mode({ + enabled: true, + requireBase: false + }).hashPrefix('!'); $httpProvider.interceptors.push('authInterceptor'); // Disable debug data for production environment // @link https://docs.angularjs.org/guide/production $compileProvider.debugInfoEnabled(app.applicationEnvironment !== 'production'); + $logProvider.debugEnabled(app.applicationEnvironment !== 'production'); } - bootstrapConfig.$inject = ['$compileProvider', '$locationProvider', '$httpProvider']; // Then define the init function for starting up the application angular.element(document).ready(init); diff --git a/modules/core/client/config/core.client.routes.js b/modules/core/client/config/core.client.routes.js index 6fc0d79f..c00f8807 100644 --- a/modules/core/client/config/core.client.routes.js +++ b/modules/core/client/config/core.client.routes.js @@ -29,29 +29,43 @@ $stateProvider .state('home', { url: '/', - templateUrl: 'modules/core/client/views/home.client.view.html', + templateUrl: '/modules/core/client/views/home.client.view.html', controller: 'HomeController', controllerAs: 'vm' }) .state('not-found', { url: '/not-found', - templateUrl: 'modules/core/client/views/404.client.view.html', + templateUrl: '/modules/core/client/views/404.client.view.html', + controller: 'ErrorController', + controllerAs: 'vm', + params: { + message: function($stateParams) { + return $stateParams.message; + } + }, data: { ignoreState: true, - pageTitle: 'Not-Found' + pageTitle: 'Not Found' } }) .state('bad-request', { url: '/bad-request', - templateUrl: 'modules/core/client/views/400.client.view.html', + templateUrl: '/modules/core/client/views/400.client.view.html', + controller: 'ErrorController', + controllerAs: 'vm', + params: { + message: function($stateParams) { + return $stateParams.message; + } + }, data: { ignoreState: true, - pageTitle: 'Bad-Request' + pageTitle: 'Bad Request' } }) .state('forbidden', { url: '/forbidden', - templateUrl: 'modules/core/client/views/403.client.view.html', + templateUrl: '/modules/core/client/views/403.client.view.html', data: { ignoreState: true, pageTitle: 'Forbidden' diff --git a/modules/core/client/controllers/error.client.controller.js b/modules/core/client/controllers/error.client.controller.js new file mode 100644 index 00000000..5968f343 --- /dev/null +++ b/modules/core/client/controllers/error.client.controller.js @@ -0,0 +1,18 @@ +(function () { + 'use strict'; + + angular + .module('core') + .controller('ErrorController', ErrorController); + + ErrorController.$inject = ['$stateParams']; + + function ErrorController($stateParams) { + var vm = this; + vm.errorMessage = null; + + // Display custom message if it was set + if ($stateParams.message) vm.errorMessage = $stateParams.message; + } +}()); + diff --git a/modules/core/client/directives/autofocus.client.directives.js b/modules/core/client/directives/auto-focus.client.directive.js similarity index 100% rename from modules/core/client/directives/autofocus.client.directives.js rename to modules/core/client/directives/auto-focus.client.directive.js diff --git a/modules/core/client/directives/show-errors.client.directives.js b/modules/core/client/directives/show-errors.client.directive.js similarity index 100% rename from modules/core/client/directives/show-errors.client.directives.js rename to modules/core/client/directives/show-errors.client.directive.js diff --git a/modules/core/client/services/interceptors/auth-interceptor.client.service.js b/modules/core/client/services/interceptors/auth-interceptor.client.service.js index 29294028..89bae6f0 100644 --- a/modules/core/client/services/interceptors/auth-interceptor.client.service.js +++ b/modules/core/client/services/interceptors/auth-interceptor.client.service.js @@ -17,6 +17,9 @@ function responseError(rejection) { if (!rejection.config.ignoreAuthModule) { switch (rejection.status) { + case 400: + $injector.get('$state').go('bad-request', { message: rejection.data.message }); + break; case 401: // Deauthenticate the global user Authentication.user = null; @@ -25,6 +28,13 @@ case 403: $injector.get('$state').transitionTo('forbidden'); break; + case 404: + $injector.get('$state').go('not-found', { message: rejection.data.message }); + break; + case -1: // Handle error if no response from server(Network Lost or Server not responding) + var Notification = $injector.get('Notification'); + Notification.error({ message: 'No response received from server. Please try again later.', title: 'Error processing request!', delay: 5000 }); + break; } } // otherwise, default behaviour diff --git a/modules/core/client/views/400.client.view.html b/modules/core/client/views/400.client.view.html index efc28045..f1192d49 100644 --- a/modules/core/client/views/400.client.view.html +++ b/modules/core/client/views/400.client.view.html @@ -1,6 +1,9 @@ -

    Bad Request

    + diff --git a/modules/core/client/views/403.client.view.html b/modules/core/client/views/403.client.view.html index 151968d5..320980c9 100644 --- a/modules/core/client/views/403.client.view.html +++ b/modules/core/client/views/403.client.view.html @@ -1,4 +1,6 @@ -

    Forbidden

    +