From b3ad56efa38f4acf52c70ae47d989ffaff8c57cd Mon Sep 17 00:00:00 2001 From: Ryan Hutchison Date: Fri, 1 Jan 2016 14:58:42 -0500 Subject: [PATCH] feat(articles): Modify articles module to implement style guidelines. Update the articles module to implement the style guidelines. Much of this work is from @trainerbill Closes #874 Closes #339 --- .../articles/client/articles.client.module.js | 9 +- .../client/config/articles.client.config.js | 18 +- .../client/config/articles.client.routes.js | 61 ++++-- .../controllers/articles.client.controller.js | 106 ++++------ .../list.articles.client.controller.js | 15 ++ .../services/articles.client.service.js | 15 +- .../views/create-article.client.view.html | 28 --- .../views/edit-article.client.view.html | 28 --- .../views/form-article.client.view.html | 28 +++ .../views/list-articles.client.view.html | 6 +- .../views/view-article.client.view.html | 18 +- .../articles.client.controller.tests.js | 146 +++++-------- .../client/articles.client.routes.tests.js | 195 ++++++++++++++++++ .../list.articles.client.controller.tests.js | 92 +++++++++ 14 files changed, 508 insertions(+), 257 deletions(-) create mode 100644 modules/articles/client/controllers/list.articles.client.controller.js delete mode 100644 modules/articles/client/views/create-article.client.view.html delete mode 100644 modules/articles/client/views/edit-article.client.view.html create mode 100644 modules/articles/client/views/form-article.client.view.html create mode 100644 modules/articles/tests/client/articles.client.routes.tests.js create mode 100644 modules/articles/tests/client/list.articles.client.controller.tests.js diff --git a/modules/articles/client/articles.client.module.js b/modules/articles/client/articles.client.module.js index 3c94d0cb57..4f2b96b347 100644 --- a/modules/articles/client/articles.client.module.js +++ b/modules/articles/client/articles.client.module.js @@ -1,4 +1,7 @@ -'use strict'; +(function (app) { + 'use strict'; -// Use Applicaion configuration module to register a new module -ApplicationConfiguration.registerModule('articles'); + app.registerModule('articles'); + app.registerModule('articles.services'); + app.registerModule('articles.routes', ['ui.router', 'articles.services']); +})(ApplicationConfiguration); diff --git a/modules/articles/client/config/articles.client.config.js b/modules/articles/client/config/articles.client.config.js index c44990dc9e..1f8853e494 100644 --- a/modules/articles/client/config/articles.client.config.js +++ b/modules/articles/client/config/articles.client.config.js @@ -1,9 +1,13 @@ -'use strict'; +(function () { + 'use strict'; -// Configuring the Articles module -angular.module('articles').run(['Menus', - function (Menus) { - // Add the articles dropdown item + angular + .module('articles') + .run(menuConfig); + + menuConfig.$inject = ['Menus']; + + function menuConfig(Menus) { Menus.addMenuItem('topbar', { title: 'Articles', state: 'articles', @@ -19,9 +23,9 @@ angular.module('articles').run(['Menus', // Add the dropdown create item Menus.addSubMenuItem('topbar', 'articles', { - title: 'Create Articles', + title: 'Create Article', state: 'articles.create', roles: ['user'] }); } -]); +})(); diff --git a/modules/articles/client/config/articles.client.routes.js b/modules/articles/client/config/articles.client.routes.js index 6938867d90..f254b5c12d 100644 --- a/modules/articles/client/config/articles.client.routes.js +++ b/modules/articles/client/config/articles.client.routes.js @@ -1,9 +1,13 @@ -'use strict'; +(function () { + 'use strict'; -// Setting up route -angular.module('articles').config(['$stateProvider', - function ($stateProvider) { - // Articles state routing + angular + .module('articles.routes') + .config(routeConfig); + + routeConfig.$inject = ['$stateProvider']; + + function routeConfig($stateProvider) { $stateProvider .state('articles', { abstract: true, @@ -12,25 +16,56 @@ angular.module('articles').config(['$stateProvider', }) .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' }) .state('articles.create', { url: '/create', - templateUrl: 'modules/articles/client/views/create-article.client.view.html', + templateUrl: 'modules/articles/client/views/form-article.client.view.html', + controller: 'ArticlesController', + controllerAs: 'vm', + resolve: { + articleResolve: newArticle + }, data: { roles: ['user', 'admin'] } }) - .state('articles.view', { - url: '/:articleId', - templateUrl: 'modules/articles/client/views/view-article.client.view.html' - }) .state('articles.edit', { url: '/:articleId/edit', - templateUrl: 'modules/articles/client/views/edit-article.client.view.html', + templateUrl: 'modules/articles/client/views/form-article.client.view.html', + controller: 'ArticlesController', + controllerAs: 'vm', + resolve: { + articleResolve: getArticle + }, data: { roles: ['user', 'admin'] } + }) + .state('articles.view', { + url: '/:articleId', + templateUrl: 'modules/articles/client/views/view-article.client.view.html', + controller: 'ArticlesController', + controllerAs: 'vm', + 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/controllers/articles.client.controller.js b/modules/articles/client/controllers/articles.client.controller.js index f36ea4f328..398379988e 100644 --- a/modules/articles/client/controllers/articles.client.controller.js +++ b/modules/articles/client/controllers/articles.client.controller.js @@ -1,84 +1,52 @@ -'use strict'; +(function () { + 'use strict'; -// Articles controller -angular.module('articles').controller('ArticlesController', ['$scope', '$stateParams', '$location', 'Authentication', 'Articles', - function ($scope, $stateParams, $location, Authentication, Articles) { - $scope.authentication = Authentication; + angular + .module('articles') + .controller('ArticlesController', ArticlesController); - // Create new Article - $scope.create = function (isValid) { - $scope.error = null; + ArticlesController.$inject = ['$scope', '$state', 'articleResolve', 'Authentication']; - if (!isValid) { - $scope.$broadcast('show-errors-check-validity', 'articleForm'); - - return false; - } - - // Create new Article object - var article = new Articles({ - title: this.title, - content: this.content - }); + function ArticlesController($scope, $state, article, Authentication) { + var vm = this; - // Redirect after save - article.$save(function (response) { - $location.path('articles/' + response._id); - - // Clear form fields - $scope.title = ''; - $scope.content = ''; - }, function (errorResponse) { - $scope.error = errorResponse.data.message; - }); - }; + vm.article = article; + vm.authentication = Authentication; + vm.error = null; + vm.form = {}; + vm.remove = remove; + vm.save = save; // Remove existing Article - $scope.remove = function (article) { - if (article) { - article.$remove(); - - for (var i in $scope.articles) { - if ($scope.articles[i] === article) { - $scope.articles.splice(i, 1); - } - } - } else { - $scope.article.$remove(function () { - $location.path('articles'); - }); + function remove() { + if (confirm('Are you sure you want to delete?')) { + vm.article.$remove($state.go('articles.list')); } - }; - - // Update existing Article - $scope.update = function (isValid) { - $scope.error = null; + } + // Save Article + function save(isValid) { if (!isValid) { - $scope.$broadcast('show-errors-check-validity', 'articleForm'); - + $scope.$broadcast('show-errors-check-validity', 'vm.form.articleForm'); return false; } - var article = $scope.article; - - article.$update(function () { - $location.path('articles/' + article._id); - }, function (errorResponse) { - $scope.error = errorResponse.data.message; - }); - }; + // TODO: move create/update logic to service + if (vm.article._id) { + vm.article.$update(successCallback, errorCallback); + } else { + vm.article.$save(successCallback, errorCallback); + } - // Find a list of Articles - $scope.find = function () { - $scope.articles = Articles.query(); - }; + function successCallback(res) { + $state.go('articles.view', { + articleId: res._id + }); + } - // Find existing Article - $scope.findOne = function () { - $scope.article = Articles.get({ - articleId: $stateParams.articleId - }); - }; + function errorCallback(res) { + vm.error = res.data.message; + } + } } -]); +})(); diff --git a/modules/articles/client/controllers/list.articles.client.controller.js b/modules/articles/client/controllers/list.articles.client.controller.js new file mode 100644 index 0000000000..4b593ae9f9 --- /dev/null +++ b/modules/articles/client/controllers/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/services/articles.client.service.js b/modules/articles/client/services/articles.client.service.js index 73a8251221..420d271448 100644 --- a/modules/articles/client/services/articles.client.service.js +++ b/modules/articles/client/services/articles.client.service.js @@ -1,8 +1,13 @@ -'use strict'; +(function () { + 'use strict'; -//Articles service used for communicating with the articles REST endpoints -angular.module('articles').factory('Articles', ['$resource', - function ($resource) { + angular + .module('articles.services') + .factory('ArticlesService', ArticlesService); + + ArticlesService.$inject = ['$resource']; + + function ArticlesService($resource) { return $resource('api/articles/:articleId', { articleId: '@_id' }, { @@ -11,4 +16,4 @@ angular.module('articles').factory('Articles', ['$resource', } }); } -]); +})(); diff --git a/modules/articles/client/views/create-article.client.view.html b/modules/articles/client/views/create-article.client.view.html deleted file mode 100644 index 76148bf19e..0000000000 --- a/modules/articles/client/views/create-article.client.view.html +++ /dev/null @@ -1,28 +0,0 @@ -
- -
-
-
-
- - -
-

Article title is required.

-
-
-
- - -
-
- -
-
- -
-
-
-
-
diff --git a/modules/articles/client/views/edit-article.client.view.html b/modules/articles/client/views/edit-article.client.view.html deleted file mode 100644 index 1834fb3d17..0000000000 --- a/modules/articles/client/views/edit-article.client.view.html +++ /dev/null @@ -1,28 +0,0 @@ -
- -
-
-
-
- - -
-

Article title is required.

-
-
-
- - -
-
- -
-
- -
-
-
-
-
diff --git a/modules/articles/client/views/form-article.client.view.html b/modules/articles/client/views/form-article.client.view.html new file mode 100644 index 0000000000..145a1b70a0 --- /dev/null +++ b/modules/articles/client/views/form-article.client.view.html @@ -0,0 +1,28 @@ +
+ +
+
+
+
+ + +
+

Article title is required.

+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
diff --git a/modules/articles/client/views/list-articles.client.view.html b/modules/articles/client/views/list-articles.client.view.html index cd6ab94520..e0e964d05c 100644 --- a/modules/articles/client/views/list-articles.client.view.html +++ b/modules/articles/client/views/list-articles.client.view.html @@ -1,9 +1,9 @@ -
+
-
+
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 f70d6307f3..007a6c64d5 100644 --- a/modules/articles/client/views/view-article.client.view.html +++ b/modules/articles/client/views/view-article.client.view.html @@ -1,23 +1,23 @@ -
+
-
- + Posted on - + by - - Deleted User + + Deleted User -

+

diff --git a/modules/articles/tests/client/articles.client.controller.tests.js b/modules/articles/tests/client/articles.client.controller.tests.js index 934acdf03c..d9107e6856 100644 --- a/modules/articles/tests/client/articles.client.controller.tests.js +++ b/modules/articles/tests/client/articles.client.controller.tests.js @@ -1,16 +1,14 @@ -'use strict'; - (function () { - // Articles Controller Spec + 'use strict'; + describe('Articles Controller Tests', function () { // Initialize global variables var ArticlesController, - scope, + $scope, $httpBackend, - $stateParams, - $location, + $state, Authentication, - Articles, + ArticlesService, mockArticle; // The $resource service augments the response object with methods for updating and deleting the resource. @@ -38,19 +36,18 @@ // 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, _$location_, _$stateParams_, _$httpBackend_, _Authentication_, _Articles_) { + beforeEach(inject(function ($controller, $rootScope, _$state_, _$httpBackend_, _Authentication_, _ArticlesService_) { // Set a new global scope - scope = $rootScope.$new(); + $scope = $rootScope.$new(); // Point global variables to injected services - $stateParams = _$stateParams_; $httpBackend = _$httpBackend_; - $location = _$location_; + $state = _$state_; Authentication = _Authentication_; - Articles = _Articles_; + ArticlesService = _ArticlesService_; // create mock article - mockArticle = new Articles({ + mockArticle = new ArticlesService({ _id: '525a8422f6d0f87f0e407a33', title: 'An Article about MEAN', content: 'MEAN rocks!' @@ -62,149 +59,114 @@ }; // Initialize the Articles controller. - ArticlesController = $controller('ArticlesController', { - $scope: scope + ArticlesController = $controller('ArticlesController as vm', { + $scope: $scope, + articleResolve: {} }); - })); - - it('$scope.find() should create an array with at least one article object fetched from XHR', inject(function (Articles) { - // Create a sample articles array that includes the new article - var sampleArticles = [mockArticle]; - // Set GET response - $httpBackend.expectGET('api/articles').respond(sampleArticles); - - // Run controller functionality - scope.find(); - $httpBackend.flush(); - - // Test scope value - expect(scope.articles).toEqualData(sampleArticles); + //Spy on state go + spyOn($state, 'go'); })); - it('$scope.findOne() should create an array with one article object fetched from XHR using a articleId URL parameter', inject(function (Articles) { - // Set the URL parameter - $stateParams.articleId = mockArticle._id; - - // Set GET response - $httpBackend.expectGET(/api\/articles\/([0-9a-fA-F]{24})$/).respond(mockArticle); - - // Run controller functionality - scope.findOne(); - $httpBackend.flush(); - - // Test scope value - expect(scope.article).toEqualData(mockArticle); - })); - - describe('$scope.create()', function () { + describe('vm.save() as create', function () { var sampleArticlePostData; beforeEach(function () { // Create a sample article object - sampleArticlePostData = new Articles({ + sampleArticlePostData = new ArticlesService({ title: 'An Article about MEAN', content: 'MEAN rocks!' }); - // Fixture mock form input values - scope.title = 'An Article about MEAN'; - scope.content = 'MEAN rocks!'; - - spyOn($location, 'path'); + $scope.vm.article = sampleArticlePostData; }); - it('should send a POST request with the form input values and then locate to new object URL', inject(function (Articles) { + 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.create(true); + $scope.vm.save(true); $httpBackend.flush(); - // Test form inputs are reset - expect(scope.title).toEqual(''); - expect(scope.content).toEqual(''); - // Test URL redirection after the article was created - expect($location.path.calls.mostRecent().args[0]).toBe('articles/' + mockArticle._id); + expect($state.go).toHaveBeenCalledWith('articles.view', { + articleId: mockArticle._id + }); })); - it('should set scope.error if save error', function () { + 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.create(true); + $scope.vm.save(true); $httpBackend.flush(); - expect(scope.error).toBe(errorMessage); + expect($scope.vm.error).toBe(errorMessage); }); }); - describe('$scope.update()', function () { + describe('vm.save() as update', function () { beforeEach(function () { - // Mock article in scope - scope.article = mockArticle; + // Mock article in $scope + $scope.vm.article = mockArticle; }); - it('should update a valid article', inject(function (Articles) { + 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.update(true); + $scope.vm.save(true); $httpBackend.flush(); // Test URL location to new object - expect($location.path()).toBe('/articles/' + mockArticle._id); + expect($state.go).toHaveBeenCalledWith('articles.view', { + articleId: mockArticle._id + }); })); - it('should set scope.error to error response message', inject(function (Articles) { + 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.update(true); + $scope.vm.save(true); $httpBackend.flush(); - expect(scope.error).toBe(errorMessage); + expect($scope.vm.error).toBe(errorMessage); })); }); - describe('$scope.remove(article)', function () { + describe('vm.remove()', function () { beforeEach(function () { - // Create new articles array and include the article - scope.articles = [mockArticle, {}]; - - // Set expected DELETE response - $httpBackend.expectDELETE(/api\/articles\/([0-9a-fA-F]{24})$/).respond(204); - - // Run controller functionality - scope.remove(mockArticle); + //Setup articles + $scope.vm.article = mockArticle; }); - it('should send a DELETE request with a valid articleId and remove the article from the scope', inject(function (Articles) { - expect(scope.articles.length).toBe(1); - })); - }); - - describe('scope.remove()', function () { - beforeEach(function () { - spyOn($location, 'path'); - scope.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.remove(); + $scope.vm.remove(); $httpBackend.flush(); + + expect($state.go).toHaveBeenCalledWith('articles.list'); }); - it('should redirect to articles', function () { - expect($location.path).toHaveBeenCalledWith('articles'); + 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 new file mode 100644 index 0000000000..32608c2af2 --- /dev/null +++ b/modules/articles/tests/client/articles.client.routes.tests.js @@ -0,0 +1,195 @@ +(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('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('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 + }); + })); + + 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); + }); + + it('Should not be abstract', function () { + expect(createstate.abstract).toBe(undefined); + }); + + it('Should have templateUrl', function () { + expect(createstate.templateUrl).toBe('modules/articles/client/views/form-article.client.view.html'); + }); + }); + + describe('Edit Route', function () { + var editstate, + ArticlesController, + mockArticle; + + beforeEach(inject(function ($controller, $state, $templateCache) { + editstate = $state.get('articles.edit'); + $templateCache.put('modules/articles/client/views/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('/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/form-article.client.view.html'); + }); + + xit('Should go to unauthorized route', function () { + + }); + }); + + }); + }); +})(); diff --git a/modules/articles/tests/client/list.articles.client.controller.tests.js b/modules/articles/tests/client/list.articles.client.controller.tests.js new file mode 100644 index 0000000000..9f93f9fa2c --- /dev/null +++ b/modules/articles/tests/client/list.articles.client.controller.tests.js @@ -0,0 +1,92 @@ +(function () { + 'use strict'; + + describe('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'] + }; + + // 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); + + })); + }); + }); +})();