diff --git a/modules/articles/client/articles.client.module.js b/modules/articles/client/articles.client.module.js
index 4f2b96b347..c0ce87fb67 100644
--- a/modules/articles/client/articles.client.module.js
+++ b/modules/articles/client/articles.client.module.js
@@ -2,6 +2,8 @@
'use strict';
app.registerModule('articles');
+ app.registerModule('articles.admin', ['core.admin']);
+ app.registerModule('articles.admin.routes', ['core.admin.routes']);
app.registerModule('articles.services');
app.registerModule('articles.routes', ['ui.router', 'articles.services']);
})(ApplicationConfiguration);
diff --git a/modules/articles/client/config/articles-admin.client.config.js b/modules/articles/client/config/articles-admin.client.config.js
new file mode 100644
index 0000000000..0bd3b63fe2
--- /dev/null
+++ b/modules/articles/client/config/articles-admin.client.config.js
@@ -0,0 +1,17 @@
+(function () {
+ 'use strict';
+
+ // Configuring the Articles Admin module
+ angular
+ .module('articles.admin')
+ .run(menuConfig);
+
+ menuConfig.$inject = ['Menus'];
+
+ function menuConfig(Menus) {
+ Menus.addSubMenuItem('topbar', 'admin', {
+ title: 'Manage Articles',
+ state: 'admin.articles.list'
+ });
+ }
+})();
diff --git a/modules/articles/client/config/articles-admin.client.routes.js b/modules/articles/client/config/articles-admin.client.routes.js
new file mode 100644
index 0000000000..934e131a2f
--- /dev/null
+++ b/modules/articles/client/config/articles-admin.client.routes.js
@@ -0,0 +1,65 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('articles.admin.routes')
+ .config(routeConfig);
+
+ routeConfig.$inject = ['$stateProvider'];
+
+ function routeConfig($stateProvider) {
+ $stateProvider
+ .state('admin.articles', {
+ abstract: true,
+ url: '/articles',
+ template: ''
+ })
+ .state('admin.articles.list', {
+ url: '',
+ templateUrl: 'modules/articles/client/views/admin/list-articles.client.view.html',
+ controller: 'ArticlesListController',
+ controllerAs: 'vm',
+ data: {
+ roles: ['admin']
+ }
+ })
+ .state('admin.articles.create', {
+ url: '/create',
+ templateUrl: 'modules/articles/client/views/admin/form-article.client.view.html',
+ controller: 'ArticlesController',
+ controllerAs: 'vm',
+ data: {
+ roles: ['admin']
+ },
+ resolve: {
+ articleResolve: newArticle
+ }
+ })
+ .state('admin.articles.edit', {
+ url: '/:articleId/edit',
+ templateUrl: 'modules/articles/client/views/admin/form-article.client.view.html',
+ controller: 'ArticlesController',
+ controllerAs: 'vm',
+ data: {
+ roles: ['admin']
+ },
+ resolve: {
+ articleResolve: getArticle
+ }
+ });
+ }
+
+ getArticle.$inject = ['$stateParams', 'ArticlesService'];
+
+ function getArticle($stateParams, ArticlesService) {
+ return ArticlesService.get({
+ articleId: $stateParams.articleId
+ }).$promise;
+ }
+
+ newArticle.$inject = ['ArticlesService'];
+
+ function newArticle(ArticlesService) {
+ return new ArticlesService();
+ }
+})();
diff --git a/modules/articles/client/config/articles.client.config.js b/modules/articles/client/config/articles.client.config.js
index 1f8853e494..7d5b1ee7fd 100644
--- a/modules/articles/client/config/articles.client.config.js
+++ b/modules/articles/client/config/articles.client.config.js
@@ -18,14 +18,8 @@
// Add the dropdown list item
Menus.addSubMenuItem('topbar', 'articles', {
title: 'List Articles',
- state: 'articles.list'
- });
-
- // Add the dropdown create item
- Menus.addSubMenuItem('topbar', 'articles', {
- title: 'Create Article',
- state: 'articles.create',
- roles: ['user']
+ state: 'articles.list',
+ roles: ['*']
});
}
})();
diff --git a/modules/articles/client/config/articles.client.routes.js b/modules/articles/client/config/articles.client.routes.js
index b7671b1180..0daf23566d 100644
--- a/modules/articles/client/config/articles.client.routes.js
+++ b/modules/articles/client/config/articles.client.routes.js
@@ -23,32 +23,6 @@
pageTitle: 'Articles List'
}
})
- .state('articles.create', {
- url: '/create',
- templateUrl: 'modules/articles/client/views/form-article.client.view.html',
- controller: 'ArticlesController',
- controllerAs: 'vm',
- resolve: {
- articleResolve: newArticle
- },
- data: {
- roles: ['user', 'admin'],
- pageTitle : 'Articles Create'
- }
- })
- .state('articles.edit', {
- url: '/:articleId/edit',
- templateUrl: 'modules/articles/client/views/form-article.client.view.html',
- controller: 'ArticlesController',
- controllerAs: 'vm',
- resolve: {
- articleResolve: getArticle
- },
- data: {
- roles: ['user', 'admin'],
- pageTitle: 'Edit Article {{ articleResolve.title }}'
- }
- })
.state('articles.view', {
url: '/:articleId',
templateUrl: 'modules/articles/client/views/view-article.client.view.html',
@@ -70,10 +44,4 @@
articleId: $stateParams.articleId
}).$promise;
}
-
- newArticle.$inject = ['ArticlesService'];
-
- function newArticle(ArticlesService) {
- return new ArticlesService();
- }
})();
diff --git a/modules/articles/client/controllers/admin/article.client.controller.js b/modules/articles/client/controllers/admin/article.client.controller.js
new file mode 100644
index 0000000000..9ecabffd0c
--- /dev/null
+++ b/modules/articles/client/controllers/admin/article.client.controller.js
@@ -0,0 +1,50 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('articles.admin')
+ .controller('ArticlesController', ArticlesController);
+
+ ArticlesController.$inject = ['$scope', '$state', 'articleResolve', 'Authentication'];
+
+ function ArticlesController($scope, $state, article, Authentication) {
+ var vm = this;
+
+ vm.article = article;
+ vm.authentication = Authentication;
+ vm.error = null;
+ vm.form = {};
+ vm.remove = remove;
+ vm.save = save;
+
+ // Remove existing Article
+ function remove() {
+ if (confirm('Are you sure you want to delete?')) {
+ vm.article.$remove($state.go('admin.articles.list'));
+ }
+ }
+
+ // Save Article
+ function save(isValid) {
+ if (!isValid) {
+ $scope.$broadcast('show-errors-check-validity', 'vm.form.articleForm');
+ return false;
+ }
+
+ // TODO: move create/update logic to service
+ if (vm.article._id) {
+ vm.article.$update(successCallback, errorCallback);
+ } else {
+ vm.article.$save(successCallback, errorCallback);
+ }
+
+ function successCallback(res) {
+ $state.go('admin.articles.list'); // should we send the User to the list or the updated Article's view?
+ }
+
+ function errorCallback(res) {
+ vm.error = res.data.message;
+ }
+ }
+ }
+})();
diff --git a/modules/articles/client/controllers/admin/list-articles.client.controller.js b/modules/articles/client/controllers/admin/list-articles.client.controller.js
new file mode 100644
index 0000000000..934e521de2
--- /dev/null
+++ b/modules/articles/client/controllers/admin/list-articles.client.controller.js
@@ -0,0 +1,15 @@
+(function () {
+ 'use strict';
+
+ angular
+ .module('articles')
+ .controller('ArticlesListController', ArticlesListController);
+
+ ArticlesListController.$inject = ['ArticlesService'];
+
+ function ArticlesListController(ArticlesService) {
+ var vm = this;
+
+ vm.articles = ArticlesService.query();
+ }
+})();
diff --git a/modules/articles/client/views/form-article.client.view.html b/modules/articles/client/views/admin/form-article.client.view.html
similarity index 89%
rename from modules/articles/client/views/form-article.client.view.html
rename to modules/articles/client/views/admin/form-article.client.view.html
index 145a1b70a0..cc118cc0ff 100644
--- a/modules/articles/client/views/form-article.client.view.html
+++ b/modules/articles/client/views/admin/form-article.client.view.html
@@ -2,6 +2,11 @@
+
-
- No articles yet, why don't you
create one?
-
diff --git a/modules/articles/client/views/view-article.client.view.html b/modules/articles/client/views/view-article.client.view.html
index 1dd88c662d..89b313a569 100644
--- a/modules/articles/client/views/view-article.client.view.html
+++ b/modules/articles/client/views/view-article.client.view.html
@@ -2,14 +2,6 @@
-
Posted on
diff --git a/modules/articles/server/policies/articles.server.policy.js b/modules/articles/server/policies/articles.server.policy.js
index f18d96ffa5..f27396216f 100644
--- a/modules/articles/server/policies/articles.server.policy.js
+++ b/modules/articles/server/policies/articles.server.policy.js
@@ -25,7 +25,7 @@ exports.invokeRolesPolicies = function () {
roles: ['user'],
allows: [{
resources: '/api/articles',
- permissions: ['get', 'post']
+ permissions: ['get']
}, {
resources: '/api/articles/:articleId',
permissions: ['get']
diff --git a/modules/articles/tests/client/admin.articles.client.controller.tests.js b/modules/articles/tests/client/admin.articles.client.controller.tests.js
new file mode 100644
index 0000000000..536a0c9526
--- /dev/null
+++ b/modules/articles/tests/client/admin.articles.client.controller.tests.js
@@ -0,0 +1,168 @@
+(function () {
+ 'use strict';
+
+ describe('Articles Controller Tests', function () {
+ // Initialize global variables
+ var ArticlesController,
+ $scope,
+ $httpBackend,
+ $state,
+ Authentication,
+ ArticlesService,
+ mockArticle;
+
+ // 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
+ // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.
+ // When the toEqualData matcher compares two objects, it takes only object properties into
+ // account and ignores methods.
+ beforeEach(function () {
+ jasmine.addMatchers({
+ toEqualData: function (util, customEqualityTesters) {
+ return {
+ compare: function (actual, expected) {
+ return {
+ pass: angular.equals(actual, expected)
+ };
+ }
+ };
+ }
+ });
+ });
+
+ // Then 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 ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_) {
+ // Set a new global scope
+ $scope = $rootScope.$new();
+
+ // Point global variables to injected services
+ $httpBackend = _$httpBackend_;
+ $state = _$state_;
+ Authentication = _Authentication_;
+ ArticlesService = _ArticlesService_;
+
+ // create mock article
+ mockArticle = new ArticlesService({
+ _id: '525a8422f6d0f87f0e407a33',
+ title: 'An Article about MEAN',
+ content: 'MEAN rocks!'
+ });
+
+ // Mock logged in user
+ Authentication.user = {
+ roles: ['user']
+ };
+
+ // Initialize the Articles controller.
+ ArticlesController = $controller('ArticlesController as vm', {
+ $scope: $scope,
+ articleResolve: {}
+ });
+
+ //Spy on state go
+ spyOn($state, 'go');
+ }));
+
+ describe('vm.save() as create', function () {
+ var sampleArticlePostData;
+
+ beforeEach(function () {
+ // Create a sample article object
+ sampleArticlePostData = new ArticlesService({
+ title: 'An Article about MEAN',
+ content: 'MEAN rocks!'
+ });
+
+ $scope.vm.article = sampleArticlePostData;
+ });
+
+ 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);
+
+ // Run controller functionality
+ $scope.vm.save(true);
+ $httpBackend.flush();
+
+ // Test URL redirection after the article was created
+ expect($state.go).toHaveBeenCalledWith('admin.articles.list');
+ }));
+
+ it('should set $scope.vm.error if error', function () {
+ var errorMessage = 'this is an error message';
+ $httpBackend.expectPOST('api/articles', sampleArticlePostData).respond(400, {
+ message: errorMessage
+ });
+
+ $scope.vm.save(true);
+ $httpBackend.flush();
+
+ expect($scope.vm.error).toBe(errorMessage);
+ });
+ });
+
+ describe('vm.save() as update', function () {
+ beforeEach(function () {
+ // Mock article in $scope
+ $scope.vm.article = mockArticle;
+ });
+
+ it('should update a valid article', inject(function (ArticlesService) {
+ // Set PUT response
+ $httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond();
+
+ // Run controller functionality
+ $scope.vm.save(true);
+ $httpBackend.flush();
+
+ // Test URL location to new object
+ expect($state.go).toHaveBeenCalledWith('admin.articles.list');
+ }));
+
+ it('should set $scope.vm.error if error', inject(function (ArticlesService) {
+ var errorMessage = 'error';
+ $httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond(400, {
+ message: errorMessage
+ });
+
+ $scope.vm.save(true);
+ $httpBackend.flush();
+
+ expect($scope.vm.error).toBe(errorMessage);
+ }));
+ });
+
+ describe('vm.remove()', function () {
+ beforeEach(function () {
+ //Setup articles
+ $scope.vm.article = mockArticle;
+ });
+
+ it('should delete the article and redirect to articles', function () {
+ //Return true on confirm message
+ spyOn(window, 'confirm').and.returnValue(true);
+
+ $httpBackend.expectDELETE(/api\/articles\/([0-9a-fA-F]{24})$/).respond(204);
+
+ $scope.vm.remove();
+ $httpBackend.flush();
+
+ expect($state.go).toHaveBeenCalledWith('admin.articles.list');
+ });
+
+ it('should should not delete the article and not redirect', function () {
+ //Return false on confirm message
+ spyOn(window, 'confirm').and.returnValue(false);
+
+ $scope.vm.remove();
+
+ expect($state.go).not.toHaveBeenCalled();
+ });
+ });
+ });
+})();
diff --git a/modules/articles/tests/client/admin.articles.client.routes.tests.js b/modules/articles/tests/client/admin.articles.client.routes.tests.js
new file mode 100644
index 0000000000..40005b75af
--- /dev/null
+++ b/modules/articles/tests/client/admin.articles.client.routes.tests.js
@@ -0,0 +1,163 @@
+(function () {
+ 'use strict';
+
+ describe('Articles Route Tests', function () {
+ // Initialize global variables
+ var $scope,
+ ArticlesService;
+
+ //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, _ArticlesService_) {
+ // Set a new global scope
+ $scope = $rootScope.$new();
+ ArticlesService = _ArticlesService_;
+ }));
+
+ describe('Route Config', function () {
+ describe('Main Route', function () {
+ var mainstate;
+ beforeEach(inject(function ($state) {
+ mainstate = $state.get('admin.articles');
+ }));
+
+ it('Should have the correct URL', function () {
+ expect(mainstate.url).toEqual('/articles');
+ });
+
+ it('Should be abstract', function () {
+ expect(mainstate.abstract).toBe(true);
+ });
+
+ it('Should have template', function () {
+ expect(mainstate.template).toBe('');
+ });
+ });
+
+ describe('List Route', function () {
+ var liststate;
+ beforeEach(inject(function ($state) {
+ liststate = $state.get('admin.articles.list');
+ }));
+
+ it('Should have the correct URL', function () {
+ expect(liststate.url).toEqual('');
+ });
+
+ it('Should be not abstract', function () {
+ expect(liststate.abstract).toBe(undefined);
+ });
+
+ it('Should have templateUrl', function () {
+ expect(liststate.templateUrl).toBe('modules/articles/client/views/admin/list-articles.client.view.html');
+ });
+ });
+
+ describe('Create Route', function () {
+ var createstate,
+ ArticlesController,
+ mockArticle;
+
+ beforeEach(inject(function ($controller, $state, $templateCache) {
+ createstate = $state.get('admin.articles.create');
+ $templateCache.put('modules/articles/client/views/admin/form-article.client.view.html', '');
+
+ // create mock article
+ mockArticle = new ArticlesService();
+
+ //Initialize Controller
+ ArticlesController = $controller('ArticlesController as vm', {
+ $scope: $scope,
+ articleResolve: mockArticle
+ });
+ }));
+
+ it('Should have the correct URL', function () {
+ expect(createstate.url).toEqual('/create');
+ });
+
+ it('Should have a resolve function', function () {
+ expect(typeof createstate.resolve).toEqual('object');
+ expect(typeof createstate.resolve.articleResolve).toEqual('function');
+ });
+
+ it('should respond to URL', inject(function ($state) {
+ expect($state.href(createstate)).toEqual('/admin/articles/create');
+ }));
+
+ it('should attach an article to the controller scope', function () {
+ expect($scope.vm.article._id).toBe(mockArticle._id);
+ expect($scope.vm.article._id).toBe(undefined);
+ });
+
+ it('Should not be abstract', function () {
+ expect(createstate.abstract).toBe(undefined);
+ });
+
+ it('Should have templateUrl', function () {
+ expect(createstate.templateUrl).toBe('modules/articles/client/views/admin/form-article.client.view.html');
+ });
+ });
+
+ describe('Edit Route', function () {
+ var editstate,
+ ArticlesController,
+ mockArticle;
+
+ beforeEach(inject(function ($controller, $state, $templateCache) {
+ editstate = $state.get('admin.articles.edit');
+ $templateCache.put('modules/articles/client/views/admin/form-article.client.view.html', '');
+
+ // create mock article
+ mockArticle = new ArticlesService({
+ _id: '525a8422f6d0f87f0e407a33',
+ title: 'An Article about MEAN',
+ content: 'MEAN rocks!'
+ });
+
+ //Initialize Controller
+ ArticlesController = $controller('ArticlesController as vm', {
+ $scope: $scope,
+ articleResolve: mockArticle
+ });
+ }));
+
+ it('Should have the correct URL', function () {
+ expect(editstate.url).toEqual('/:articleId/edit');
+ });
+
+ it('Should have a resolve function', function () {
+ expect(typeof editstate.resolve).toEqual('object');
+ expect(typeof editstate.resolve.articleResolve).toEqual('function');
+ });
+
+ it('should respond to URL', inject(function ($state) {
+ expect($state.href(editstate, {
+ articleId: 1
+ })).toEqual('/admin/articles/1/edit');
+ }));
+
+ it('should attach an article to the controller scope', function () {
+ expect($scope.vm.article._id).toBe(mockArticle._id);
+ });
+
+ it('Should not be abstract', function () {
+ expect(editstate.abstract).toBe(undefined);
+ });
+
+ it('Should have templateUrl', function () {
+ 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
new file mode 100644
index 0000000000..241c928e31
--- /dev/null
+++ b/modules/articles/tests/client/admin.list.articles.client.controller.tests.js
@@ -0,0 +1,92 @@
+(function () {
+ 'use strict';
+
+ describe('Admin Articles List Controller Tests', function () {
+ // Initialize global variables
+ var ArticlesListController,
+ $scope,
+ $httpBackend,
+ $state,
+ Authentication,
+ ArticlesService,
+ mockArticle;
+
+ // 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
+ // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.
+ // When the toEqualData matcher compares two objects, it takes only object properties into
+ // account and ignores methods.
+ beforeEach(function () {
+ jasmine.addMatchers({
+ toEqualData: function (util, customEqualityTesters) {
+ return {
+ compare: function (actual, expected) {
+ return {
+ pass: angular.equals(actual, expected)
+ };
+ }
+ };
+ }
+ });
+ });
+
+ // Then 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 ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_) {
+ // Set a new global scope
+ $scope = $rootScope.$new();
+
+ // Point global variables to injected services
+ $httpBackend = _$httpBackend_;
+ $state = _$state_;
+ Authentication = _Authentication_;
+ ArticlesService = _ArticlesService_;
+
+ // create mock article
+ mockArticle = new ArticlesService({
+ _id: '525a8422f6d0f87f0e407a33',
+ title: 'An Article about MEAN',
+ content: 'MEAN rocks!'
+ });
+
+ // Mock logged in user
+ Authentication.user = {
+ roles: ['user', 'admin']
+ };
+
+ // Initialize the Articles List controller.
+ ArticlesListController = $controller('ArticlesListController as vm', {
+ $scope: $scope
+ });
+
+ //Spy on state go
+ spyOn($state, 'go');
+ }));
+
+ describe('Instantiate', function () {
+ var mockArticleList;
+
+ beforeEach(function () {
+ mockArticleList = [mockArticle, mockArticle];
+ });
+
+ it('should send a GET request and return all articles', inject(function (ArticlesService) {
+ // Set POST response
+ $httpBackend.expectGET('api/articles').respond(mockArticleList);
+
+
+ $httpBackend.flush();
+
+ // Test form inputs are reset
+ expect($scope.vm.articles.length).toEqual(2);
+ expect($scope.vm.articles[0]).toEqual(mockArticle);
+ expect($scope.vm.articles[1]).toEqual(mockArticle);
+
+ }));
+ });
+ });
+})();
diff --git a/modules/articles/tests/client/articles.client.controller.tests.js b/modules/articles/tests/client/articles.client.controller.tests.js
index d9107e6856..854acc30f3 100644
--- a/modules/articles/tests/client/articles.client.controller.tests.js
+++ b/modules/articles/tests/client/articles.client.controller.tests.js
@@ -67,106 +67,5 @@
//Spy on state go
spyOn($state, 'go');
}));
-
- describe('vm.save() as create', function () {
- var sampleArticlePostData;
-
- beforeEach(function () {
- // Create a sample article object
- sampleArticlePostData = new ArticlesService({
- title: 'An Article about MEAN',
- content: 'MEAN rocks!'
- });
-
- $scope.vm.article = sampleArticlePostData;
- });
-
- 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);
-
- // Run controller functionality
- $scope.vm.save(true);
- $httpBackend.flush();
-
- // Test URL redirection after the article was created
- expect($state.go).toHaveBeenCalledWith('articles.view', {
- articleId: mockArticle._id
- });
- }));
-
- it('should set $scope.vm.error if error', function () {
- var errorMessage = 'this is an error message';
- $httpBackend.expectPOST('api/articles', sampleArticlePostData).respond(400, {
- message: errorMessage
- });
-
- $scope.vm.save(true);
- $httpBackend.flush();
-
- expect($scope.vm.error).toBe(errorMessage);
- });
- });
-
- describe('vm.save() as update', function () {
- beforeEach(function () {
- // Mock article in $scope
- $scope.vm.article = mockArticle;
- });
-
- it('should update a valid article', inject(function (ArticlesService) {
- // Set PUT response
- $httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond();
-
- // Run controller functionality
- $scope.vm.save(true);
- $httpBackend.flush();
-
- // Test URL location to new object
- expect($state.go).toHaveBeenCalledWith('articles.view', {
- articleId: mockArticle._id
- });
- }));
-
- it('should set $scope.vm.error if error', inject(function (ArticlesService) {
- var errorMessage = 'error';
- $httpBackend.expectPUT(/api\/articles\/([0-9a-fA-F]{24})$/).respond(400, {
- message: errorMessage
- });
-
- $scope.vm.save(true);
- $httpBackend.flush();
-
- expect($scope.vm.error).toBe(errorMessage);
- }));
- });
-
- describe('vm.remove()', function () {
- beforeEach(function () {
- //Setup articles
- $scope.vm.article = mockArticle;
- });
-
- it('should delete the article and redirect to articles', function () {
- //Return true on confirm message
- spyOn(window, 'confirm').and.returnValue(true);
-
- $httpBackend.expectDELETE(/api\/articles\/([0-9a-fA-F]{24})$/).respond(204);
-
- $scope.vm.remove();
- $httpBackend.flush();
-
- expect($state.go).toHaveBeenCalledWith('articles.list');
- });
-
- it('should should not delete the article and not redirect', function () {
- //Return false on confirm message
- spyOn(window, 'confirm').and.returnValue(false);
-
- $scope.vm.remove();
-
- expect($state.go).not.toHaveBeenCalled();
- });
- });
});
})();
diff --git a/modules/articles/tests/client/articles.client.routes.tests.js b/modules/articles/tests/client/articles.client.routes.tests.js
index 32608c2af2..14097093ca 100644
--- a/modules/articles/tests/client/articles.client.routes.tests.js
+++ b/modules/articles/tests/client/articles.client.routes.tests.js
@@ -38,111 +38,33 @@
});
});
- describe('View Route', function () {
- var viewstate,
- ArticlesController,
- mockArticle;
-
- beforeEach(inject(function ($controller, $state, $templateCache) {
- viewstate = $state.get('articles.view');
- $templateCache.put('modules/articles/client/views/view-article.client.view.html', '');
-
- // create mock article
- mockArticle = new ArticlesService({
- _id: '525a8422f6d0f87f0e407a33',
- title: 'An Article about MEAN',
- content: 'MEAN rocks!'
- });
-
- //Initialize Controller
- ArticlesController = $controller('ArticlesController as vm', {
- $scope: $scope,
- articleResolve: mockArticle
- });
- }));
-
- it('Should have the correct URL', function () {
- expect(viewstate.url).toEqual('/:articleId');
- });
-
- it('Should have a resolve function', function () {
- expect(typeof viewstate.resolve).toEqual('object');
- expect(typeof viewstate.resolve.articleResolve).toEqual('function');
- });
-
- it('should respond to URL', inject(function ($state) {
- expect($state.href(viewstate, {
- articleId: 1
- })).toEqual('/articles/1');
- }));
-
- it('should attach an article to the controller scope', function () {
- expect($scope.vm.article._id).toBe(mockArticle._id);
- });
-
- it('Should not be abstract', function () {
- expect(viewstate.abstract).toBe(undefined);
- });
-
- it('Should have templateUrl', function () {
- expect(viewstate.templateUrl).toBe('modules/articles/client/views/view-article.client.view.html');
- });
- });
-
- describe('Create Route', function () {
- var createstate,
- ArticlesController,
- mockArticle;
-
- beforeEach(inject(function ($controller, $state, $templateCache) {
- createstate = $state.get('articles.create');
- $templateCache.put('modules/articles/client/views/form-article.client.view.html', '');
-
- // create mock article
- mockArticle = new ArticlesService();
-
- //Initialize Controller
- ArticlesController = $controller('ArticlesController as vm', {
- $scope: $scope,
- articleResolve: mockArticle
- });
+ describe('List Route', function () {
+ var liststate;
+ beforeEach(inject(function ($state) {
+ liststate = $state.get('articles.list');
}));
it('Should have the correct URL', function () {
- expect(createstate.url).toEqual('/create');
- });
-
- it('Should have a resolve function', function () {
- expect(typeof createstate.resolve).toEqual('object');
- expect(typeof createstate.resolve.articleResolve).toEqual('function');
- });
-
- it('should respond to URL', inject(function ($state) {
- expect($state.href(createstate)).toEqual('/articles/create');
- }));
-
- it('should attach an article to the controller scope', function () {
- expect($scope.vm.article._id).toBe(mockArticle._id);
- expect($scope.vm.article._id).toBe(undefined);
+ expect(liststate.url).toEqual('');
});
it('Should not be abstract', function () {
- expect(createstate.abstract).toBe(undefined);
+ expect(liststate.abstract).toBe(undefined);
});
it('Should have templateUrl', function () {
- expect(createstate.templateUrl).toBe('modules/articles/client/views/form-article.client.view.html');
+ expect(liststate.templateUrl).toBe('modules/articles/client/views/list-articles.client.view.html');
});
});
- describe('Edit Route', function () {
- var editstate,
+ describe('View Route', function () {
+ var viewstate,
ArticlesController,
mockArticle;
beforeEach(inject(function ($controller, $state, $templateCache) {
- editstate = $state.get('articles.edit');
- $templateCache.put('modules/articles/client/views/form-article.client.view.html', '');
+ viewstate = $state.get('articles.view');
+ $templateCache.put('modules/articles/client/views/view-article.client.view.html', '');
// create mock article
mockArticle = new ArticlesService({
@@ -159,18 +81,18 @@
}));
it('Should have the correct URL', function () {
- expect(editstate.url).toEqual('/:articleId/edit');
+ expect(viewstate.url).toEqual('/:articleId');
});
it('Should have a resolve function', function () {
- expect(typeof editstate.resolve).toEqual('object');
- expect(typeof editstate.resolve.articleResolve).toEqual('function');
+ expect(typeof viewstate.resolve).toEqual('object');
+ expect(typeof viewstate.resolve.articleResolve).toEqual('function');
});
it('should respond to URL', inject(function ($state) {
- expect($state.href(editstate, {
+ expect($state.href(viewstate, {
articleId: 1
- })).toEqual('/articles/1/edit');
+ })).toEqual('/articles/1');
}));
it('should attach an article to the controller scope', function () {
@@ -178,18 +100,13 @@
});
it('Should not be abstract', function () {
- expect(editstate.abstract).toBe(undefined);
+ expect(viewstate.abstract).toBe(undefined);
});
it('Should have templateUrl', function () {
- expect(editstate.templateUrl).toBe('modules/articles/client/views/form-article.client.view.html');
- });
-
- xit('Should go to unauthorized route', function () {
-
+ expect(viewstate.templateUrl).toBe('modules/articles/client/views/view-article.client.view.html');
});
});
-
});
});
})();
diff --git a/modules/articles/tests/server/admin.article.server.routes.tests.js b/modules/articles/tests/server/admin.article.server.routes.tests.js
new file mode 100644
index 0000000000..6b88dbc4c6
--- /dev/null
+++ b/modules/articles/tests/server/admin.article.server.routes.tests.js
@@ -0,0 +1,278 @@
+'use strict';
+
+var should = require('should'),
+ request = require('supertest'),
+ path = require('path'),
+ mongoose = require('mongoose'),
+ User = mongoose.model('User'),
+ Article = mongoose.model('Article'),
+ express = require(path.resolve('./config/lib/express'));
+
+/**
+ * Globals
+ */
+var app, agent, credentials, user, article;
+
+/**
+ * Article routes tests
+ */
+describe('Article Admin CRUD tests', function () {
+ before(function (done) {
+ // Get application
+ app = express.init(mongoose);
+ agent = request.agent(app);
+
+ done();
+ });
+
+ beforeEach(function (done) {
+ // Create user credentials
+ credentials = {
+ username: 'username',
+ password: 'M3@n.jsI$Aw3$0m3'
+ };
+
+ // Create a new user
+ user = new User({
+ firstName: 'Full',
+ lastName: 'Name',
+ displayName: 'Full Name',
+ email: 'test@test.com',
+ roles: ['user', 'admin'],
+ username: credentials.username,
+ password: credentials.password,
+ provider: 'local'
+ });
+
+ // Save a user to the test db and create new article
+ user.save(function () {
+ article = {
+ title: 'Article Title',
+ content: 'Article Content'
+ };
+
+ done();
+ });
+ });
+
+ it('should be able to save an article if logged in', function (done) {
+ agent.post('/api/auth/signin')
+ .send(credentials)
+ .expect(200)
+ .end(function (signinErr, signinRes) {
+ // Handle signin error
+ if (signinErr) {
+ return done(signinErr);
+ }
+
+ // Get the userId
+ var userId = user.id;
+
+ // Save a new article
+ agent.post('/api/articles')
+ .send(article)
+ .expect(200)
+ .end(function (articleSaveErr, articleSaveRes) {
+ // Handle article save error
+ if (articleSaveErr) {
+ return done(articleSaveErr);
+ }
+
+ // Get a list of articles
+ agent.get('/api/articles')
+ .end(function (articlesGetErr, articlesGetRes) {
+ // Handle article save error
+ if (articlesGetErr) {
+ return done(articlesGetErr);
+ }
+
+ // Get articles list
+ var articles = articlesGetRes.body;
+
+ // Set assertions
+ (articles[0].user._id).should.equal(userId);
+ (articles[0].title).should.match('Article Title');
+
+ // Call the assertion callback
+ done();
+ });
+ });
+ });
+ });
+
+ it('should be able to update an article if signed in', function (done) {
+ agent.post('/api/auth/signin')
+ .send(credentials)
+ .expect(200)
+ .end(function (signinErr, signinRes) {
+ // Handle signin error
+ if (signinErr) {
+ return done(signinErr);
+ }
+
+ // Get the userId
+ var userId = user.id;
+
+ // Save a new article
+ agent.post('/api/articles')
+ .send(article)
+ .expect(200)
+ .end(function (articleSaveErr, articleSaveRes) {
+ // Handle article save error
+ if (articleSaveErr) {
+ return done(articleSaveErr);
+ }
+
+ // Update article title
+ article.title = 'WHY YOU GOTTA BE SO MEAN?';
+
+ // Update an existing article
+ agent.put('/api/articles/' + articleSaveRes.body._id)
+ .send(article)
+ .expect(200)
+ .end(function (articleUpdateErr, articleUpdateRes) {
+ // Handle article update error
+ if (articleUpdateErr) {
+ return done(articleUpdateErr);
+ }
+
+ // Set assertions
+ (articleUpdateRes.body._id).should.equal(articleSaveRes.body._id);
+ (articleUpdateRes.body.title).should.match('WHY YOU GOTTA BE SO MEAN?');
+
+ // Call the assertion callback
+ done();
+ });
+ });
+ });
+ });
+
+ it('should not be able to save an article if no title is provided', function (done) {
+ // Invalidate title field
+ article.title = '';
+
+ agent.post('/api/auth/signin')
+ .send(credentials)
+ .expect(200)
+ .end(function (signinErr, signinRes) {
+ // Handle signin error
+ if (signinErr) {
+ return done(signinErr);
+ }
+
+ // Get the userId
+ var userId = user.id;
+
+ // Save a new article
+ agent.post('/api/articles')
+ .send(article)
+ .expect(400)
+ .end(function (articleSaveErr, articleSaveRes) {
+ // Set message assertion
+ (articleSaveRes.body.message).should.match('Title cannot be blank');
+
+ // Handle article save error
+ done(articleSaveErr);
+ });
+ });
+ });
+
+ it('should be able to delete an article if signed in', function (done) {
+ agent.post('/api/auth/signin')
+ .send(credentials)
+ .expect(200)
+ .end(function (signinErr, signinRes) {
+ // Handle signin error
+ if (signinErr) {
+ return done(signinErr);
+ }
+
+ // Get the userId
+ var userId = user.id;
+
+ // Save a new article
+ agent.post('/api/articles')
+ .send(article)
+ .expect(200)
+ .end(function (articleSaveErr, articleSaveRes) {
+ // Handle article save error
+ if (articleSaveErr) {
+ return done(articleSaveErr);
+ }
+
+ // Delete an existing article
+ agent.delete('/api/articles/' + articleSaveRes.body._id)
+ .send(article)
+ .expect(200)
+ .end(function (articleDeleteErr, articleDeleteRes) {
+ // Handle article error error
+ if (articleDeleteErr) {
+ return done(articleDeleteErr);
+ }
+
+ // Set assertions
+ (articleDeleteRes.body._id).should.equal(articleSaveRes.body._id);
+
+ // Call the assertion callback
+ done();
+ });
+ });
+ });
+ });
+
+ it('should be able to get a single article if signed in and verify the custom "isCurrentUserOwner" field is set to "true"', function (done) {
+ // Create new article model instance
+ article.user = user;
+ var articleObj = new Article(article);
+
+ agent.post('/api/auth/signin')
+ .send(credentials)
+ .expect(200)
+ .end(function (signinErr, signinRes) {
+ // Handle signin error
+ if (signinErr) {
+ return done(signinErr);
+ }
+
+ // Get the userId
+ var userId = user.id;
+
+ // Save a new article
+ agent.post('/api/articles')
+ .send(article)
+ .expect(200)
+ .end(function (articleSaveErr, articleSaveRes) {
+ // Handle article save error
+ if (articleSaveErr) {
+ return done(articleSaveErr);
+ }
+
+ // Get the article
+ agent.get('/api/articles/' + articleSaveRes.body._id)
+ .expect(200)
+ .end(function (articleInfoErr, articleInfoRes) {
+ // Handle article error
+ if (articleInfoErr) {
+ return done(articleInfoErr);
+ }
+
+ // Set assertions
+ (articleInfoRes.body._id).should.equal(articleSaveRes.body._id);
+ (articleInfoRes.body.title).should.equal(article.title);
+
+ // Assert that the "isCurrentUserOwner" field is set to true since the current User created it
+ (articleInfoRes.body.isCurrentUserOwner).should.equal(true);
+
+ // Call the assertion callback
+ done();
+ });
+ });
+ });
+ });
+
+ afterEach(function (done) {
+ User.remove().exec(function () {
+ Article.remove().exec(done);
+ });
+ });
+});
diff --git a/modules/articles/tests/server/article.server.routes.tests.js b/modules/articles/tests/server/article.server.routes.tests.js
index 1dcf5da3bc..ef5138024c 100644
--- a/modules/articles/tests/server/article.server.routes.tests.js
+++ b/modules/articles/tests/server/article.server.routes.tests.js
@@ -55,7 +55,7 @@ describe('Article CRUD tests', function () {
});
});
- it('should be able to save an article if logged in', function (done) {
+ it('should not be able to save an article if logged in without the "admin" role', function (done) {
agent.post('/api/auth/signin')
.send(credentials)
.expect(200)
@@ -65,38 +65,14 @@ describe('Article CRUD tests', function () {
return done(signinErr);
}
- // Get the userId
- var userId = user.id;
-
- // Save a new article
agent.post('/api/articles')
.send(article)
- .expect(200)
+ .expect(403)
.end(function (articleSaveErr, articleSaveRes) {
- // Handle article save error
- if (articleSaveErr) {
- return done(articleSaveErr);
- }
-
- // Get a list of articles
- agent.get('/api/articles')
- .end(function (articlesGetErr, articlesGetRes) {
- // Handle article save error
- if (articlesGetErr) {
- return done(articlesGetErr);
- }
-
- // Get articles list
- var articles = articlesGetRes.body;
-
- // Set assertions
- (articles[0].user._id).should.equal(userId);
- (articles[0].title).should.match('Article Title');
-
- // Call the assertion callback
- done();
- });
+ // Call the assertion callback
+ done(articleSaveErr);
});
+
});
});
@@ -110,10 +86,7 @@ describe('Article CRUD tests', function () {
});
});
- it('should not be able to save an article if no title is provided', function (done) {
- // Invalidate title field
- article.title = '';
-
+ it('should not be able to update an article if signed in without the "admin" role', function (done) {
agent.post('/api/auth/signin')
.send(credentials)
.expect(200)
@@ -123,70 +96,16 @@ describe('Article CRUD tests', function () {
return done(signinErr);
}
- // Get the userId
- var userId = user.id;
-
- // Save a new article
agent.post('/api/articles')
.send(article)
- .expect(400)
+ .expect(403)
.end(function (articleSaveErr, articleSaveRes) {
- // Set message assertion
- (articleSaveRes.body.message).should.match('Title cannot be blank');
-
- // Handle article save error
+ // Call the assertion callback
done(articleSaveErr);
});
});
});
- it('should be able to update an article if signed in', function (done) {
- agent.post('/api/auth/signin')
- .send(credentials)
- .expect(200)
- .end(function (signinErr, signinRes) {
- // Handle signin error
- if (signinErr) {
- return done(signinErr);
- }
-
- // Get the userId
- var userId = user.id;
-
- // Save a new article
- agent.post('/api/articles')
- .send(article)
- .expect(200)
- .end(function (articleSaveErr, articleSaveRes) {
- // Handle article save error
- if (articleSaveErr) {
- return done(articleSaveErr);
- }
-
- // Update article title
- article.title = 'WHY YOU GOTTA BE SO MEAN?';
-
- // Update an existing article
- agent.put('/api/articles/' + articleSaveRes.body._id)
- .send(article)
- .expect(200)
- .end(function (articleUpdateErr, articleUpdateRes) {
- // Handle article update error
- if (articleUpdateErr) {
- return done(articleUpdateErr);
- }
-
- // Set assertions
- (articleUpdateRes.body._id).should.equal(articleSaveRes.body._id);
- (articleUpdateRes.body.title).should.match('WHY YOU GOTTA BE SO MEAN?');
-
- // Call the assertion callback
- done();
- });
- });
- });
- });
-
it('should be able to get a list of articles if not signed in', function (done) {
// Create new article model instance
var articleObj = new Article(article);
@@ -247,7 +166,7 @@ describe('Article CRUD tests', function () {
});
});
- it('should be able to delete an article if signed in', function (done) {
+ it('should not be able to delete an article if signed in without the "admin" role', function (done) {
agent.post('/api/auth/signin')
.send(credentials)
.expect(200)
@@ -257,35 +176,12 @@ describe('Article CRUD tests', function () {
return done(signinErr);
}
- // Get the userId
- var userId = user.id;
-
- // Save a new article
agent.post('/api/articles')
.send(article)
- .expect(200)
+ .expect(403)
.end(function (articleSaveErr, articleSaveRes) {
- // Handle article save error
- if (articleSaveErr) {
- return done(articleSaveErr);
- }
-
- // Delete an existing article
- agent.delete('/api/articles/' + articleSaveRes.body._id)
- .send(article)
- .expect(200)
- .end(function (articleDeleteErr, articleDeleteRes) {
- // Handle article error error
- if (articleDeleteErr) {
- return done(articleDeleteErr);
- }
-
- // Set assertions
- (articleDeleteRes.body._id).should.equal(articleSaveRes.body._id);
-
- // Call the assertion callback
- done();
- });
+ // Call the assertion callback
+ done(articleSaveErr);
});
});
});
@@ -328,7 +224,8 @@ describe('Article CRUD tests', function () {
email: 'orphan@test.com',
username: _creds.username,
password: _creds.password,
- provider: 'local'
+ provider: 'local',
+ roles: ['admin']
});
_orphan.save(function (err, orphan) {
@@ -400,58 +297,7 @@ describe('Article CRUD tests', function () {
});
});
- it('should be able to get a single article if signed in and verify the custom "isCurrentUserOwner" field is set to "true"', function (done) {
- // Create new article model instance
- article.user = user;
- var articleObj = new Article(article);
-
- // Save the article
- articleObj.save(function () {
- agent.post('/api/auth/signin')
- .send(credentials)
- .expect(200)
- .end(function (signinErr, signinRes) {
- // Handle signin error
- if (signinErr) {
- return done(signinErr);
- }
-
- // Get the userId
- var userId = user.id;
-
- // Save a new article
- agent.post('/api/articles')
- .send(article)
- .expect(200)
- .end(function (articleSaveErr, articleSaveRes) {
- // Handle article save error
- if (articleSaveErr) {
- return done(articleSaveErr);
- }
-
- // Get the article
- agent.get('/api/articles/' + articleSaveRes.body._id)
- .expect(200)
- .end(function (articleInfoErr, articleInfoRes) {
- // Handle article error
- if (articleInfoErr) {
- return done(articleInfoErr);
- }
-
- // Set assertions
- (articleInfoRes.body._id).should.equal(articleSaveRes.body._id);
- (articleInfoRes.body.title).should.equal(article.title);
-
- // Assert that the "isCurrentUserOwner" field is set to true since the current User created it
- (articleInfoRes.body.isCurrentUserOwner).should.equal(true);
-
- // Call the assertion callback
- done();
- });
- });
- });
- });
- });
+
it('should be able to get a single article if not signed in and verify the custom "isCurrentUserOwner" field is set to "false"', function (done) {
// Create new article model instance
@@ -474,22 +320,23 @@ describe('Article CRUD tests', function () {
it('should be able to get single article, that a different user created, if logged in & verify the "isCurrentUserOwner" field is set to "false"', function (done) {
// Create temporary user creds
var _creds = {
- username: 'temp',
+ username: 'articleowner',
password: 'M3@n.jsI$Aw3$0m3'
};
- // Create temporary user
- var _user = new User({
+ // Create user that will create the Article
+ var _articleOwner = new User({
firstName: 'Full',
lastName: 'Name',
displayName: 'Full Name',
email: 'temp@test.com',
username: _creds.username,
password: _creds.password,
- provider: 'local'
+ provider: 'local',
+ roles: ['admin', 'user']
});
- _user.save(function (err, _user) {
+ _articleOwner.save(function (err, _user) {
// Handle save error
if (err) {
return done(err);
@@ -497,7 +344,7 @@ describe('Article CRUD tests', function () {
// Sign in with the user that will create the Article
agent.post('/api/auth/signin')
- .send(credentials)
+ .send(_creds)
.expect(200)
.end(function (signinErr, signinRes) {
// Handle signin error
@@ -506,7 +353,7 @@ describe('Article CRUD tests', function () {
}
// Get the userId
- var userId = user._id;
+ var userId = _user._id;
// Save a new article
agent.post('/api/articles')
@@ -523,9 +370,9 @@ describe('Article CRUD tests', function () {
should.exist(articleSaveRes.body.user);
should.equal(articleSaveRes.body.user._id, userId);
- // now signin with the temporary user
+ // now signin with the test suite user
agent.post('/api/auth/signin')
- .send(_creds)
+ .send(credentials)
.expect(200)
.end(function (err, res) {
// Handle signin error