I have a custom directive that uses an external template and is passed data from a service. I decided to ensure that the promise was resolved before modifying the data, which was fine in the actual code but broke my unit tests, which is annoying. I have tried a number of variations but am now stuck. I am using 'ng-html2js' preprocessor.
Here is the unit test
describe('ccAccordion', function () {
var elm, scope, deferred, promise, things;
beforeEach(module('ccAccordion'));
// load the templates
beforeEach(module('components/accordion/accordion.html'));
beforeEach(inject(function ($rootScope, $compile, $q) {
elm = angular.element(
'<cc-accordion items="genres"></cc-accordion>'
);
scope = $rootScope;
things = [{
title: 'Scifi',
description: 'Scifi description'
}, {
title: 'Comedy',
description: 'Comedy description'
}];
deferred = $q.defer();
promise = deferred.promise;
promise.then(function (things) {
scope.items = things;
});
// Simulate resolving of promise
deferred.resolve(things);
// Propagate promise resolution to 'then' functions using $apply().
scope.$apply();
// compile the template?
$compile(elm)(scope);
scope.$digest();
}));
it('should create clickable titles', function () {
var titles = elm.find('.cc-accord h2');
expect(titles.length).toBe(2);
expect(titles.eq(0).text().trim()).toBe('Scifi');
expect(titles.eq(1).text().trim()).toBe('Comedy');
});
I have left out the custom addMatchers and the rest of the tests. The error I get is
TypeError: 'undefined' is not an object (evaluating 'scope.items.$promise')
Here is the directive
var ccAccordion = angular.module("ccAccordion", []);
ccAccordion.directive("ccAccordion", function () {
return {
restrict: "AE",
templateUrl: "components/accordion/accordion.html",
scope: {
items: "="
},
link: function (scope) {
scope.items.$promise.then(function (items) {
angular.forEach(scope.items, function (item) {
item.selected = false;
});
items[0].selected = true;
});
scope.select = function (desiredItem) {
(desiredItem.selected === true) ? desiredItem.selected = false : desiredItem.selected = true;
angular.forEach(scope.items, function (item) {
if (item !== desiredItem) {
item.selected = false;
}
});
};
}
};
});
This is where the directive is used in main.html
<cc-accordion items="genres"></cc-accordion>
In the main controller the genres service is passed in ie
angular.module('magicApp')
.controller('GenresCtrl', ['$scope', 'BREAKPOINTS', 'Genre',
function ($scope, BREAKPOINTS, Genre) {
$scope.bp = BREAKPOINTS;
$scope.genres = Genre.query();
}]);
Okay, I would move that code you put in link into the controller. The data processing should probably happen in a service. I know you've been told big controllers are bad, but big linking functions are generally worse, and should never do that kind of data processing.
.controller('GenresCtrl', ['$scope', 'BREAKPOINTS', 'Genre',
function ($scope, BREAKPOINTS, Genre) {
$scope.bp = BREAKPOINTS;
$scope.genres = Genre.query().then(function (items) {
angular.forEach(scope.items, function (item) {
item.selected = false;
});
items[0].selected = true;
});
scope.select = function (desiredItem) {
(desiredItem.selected === true) ? desiredItem.selected = false : desiredItem.selected = true;
angular.forEach(scope.items, function (item) {
if (item !== desiredItem) {
item.selected = false;
}
});
};
});
Your link function is now empty. Define items on the rootScope instead, this ensures that the isolateScope and your directive interface are working correctly.
beforeEach(inject(function ($rootScope, $compile, $q) {
elm = angular.element(
'<cc-accordion items="genres"></cc-accordion>'
);
scope = $rootScope;
things = [{
title: 'Scifi',
description: 'Scifi description'
}, {
title: 'Comedy',
description: 'Comedy description'
}];
scope.items = things; // Tests your directive interface
// compile the template?
$compile(elm)(scope);
scope.$digest();
}));
The behavior of the promise should be tested in a controller test, by mocking the return value of the service. Your problem with the $promise test has been solved.
The actual issue was that you were assuming that $q.defer() gave you the same kind of promise as the angular $http, but that is solved by design instead.
As peter said remove the promise from the directive and add it to the controller
angular.module('magicApp')
.controller('MainCtrl', ['$scope', 'Genre',
function ($scope, Genre) {
$scope.genres = Genre.query();
$scope.genres.$promise.then(function () {
angular.forEach($scope.genres, function (genre) {
genre.selected = false;
});
$scope.genres[0].selected = true;
});
}]);
This will also allow the controller to specify which tab is selected to begin with.
In the directive
var ccAccordion = angular.module("ccAccordion", []);
ccAccordion.directive("ccAccordion", function () {
return {
restrict: "AE",
templateUrl: "components/accordion/accordion.html",
scope: {
items: "="
},
link: function (scope) {
scope.select = function (desiredItem) {
(desiredItem.selected === true) ? desiredItem.selected = false : desiredItem.selected = true;
angular.forEach(scope.items, function (item) {
if (item !== desiredItem) {
item.selected = false;
}
});
};
}
};
});
The directive unit test now looks like this
describe('ccAccordion', function () {
var elm, scope, deferred, promise, things;
beforeEach(module('ccAccordion'));
beforeEach(function () {
jasmine.addMatchers({
toHaveClass: function () {
return {
compare: function (actual, expected) {
var classTest = actual.hasClass(expected);
classTest ? classTest = true : classTest = false;
return {
pass: classTest,
message: 'Expected ' + angular.mock.dump(actual) + ' to have class ' + expected
};
}
};
}
});
});
// load the templates
beforeEach(module('components/accordion/accordion.html'));
beforeEach(inject(function ($rootScope, $compile, $q) {
elm = angular.element(
'<cc-accordion items="genres"></cc-accordion>'
);
scope = $rootScope;
scope.genres = [{
title: 'Scifi',
description: 'Scifi description'
}, {
title: 'Comedy',
description: 'Comedy description'
}];
$compile(elm)(scope);
scope.$digest();
}));
it('should create clickable titles', function () {
var titles = elm.find('.cc-accord h2');
expect(titles.length).toBe(2);
expect(titles.eq(0).text().trim()).toBe('Scifi');
expect(titles.eq(1).text().trim()).toBe('Comedy');
});
it('should bind the content', function () {
var contents = elm.find('.cc-accord-content div:first-child');
expect(contents.length).toBe(2);
expect(contents.eq(0).text().trim()).toBe('Scifi description');
expect(contents.eq(1).text().trim()).toBe('Comedy description');
});
it('should change active content when header clicked', function () {
var titles = elm.find('.cc-accord h2'),
divs = elm.find('.cc-accord');
// click the second header
titles.eq(1).find('a').click();
// second div should be active
expect(divs.eq(0)).not.toHaveClass('active');
expect(divs.eq(1)).toHaveClass('active');
});
});
And the unit test for main controller now has the added property of selected
'use-strict';
describe('magicApp controllers', function () {
// using addMatcher because $resource is not $http and returns a promise
beforeEach(function () {
jasmine.addMatchers({
toEqualData: function () {
return {
compare: function (actual, expected) {
return {
pass: angular.equals(actual, expected)
};
}
};
}
});
});
beforeEach(module('magicApp'));
beforeEach(module('magicServices'));
describe('MainCtrl', function () {
var scope, ctrl, $httpBackend;
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('/api/genres').
respond([{title: 'Scifi', selected: true}, {title: 'Comedy', selected: false}]);
scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {$scope: scope});
}));
it('should create "genres" model with 2 genres fetched from xhr', function () {
expect(scope.genres).toEqualData([]);
$httpBackend.flush();
expect(scope.genres).toEqualData(
[{title: 'Scifi', selected: true}, {title: 'Comedy', selected: false}]);
});
});
});
Related
I'm new to AngularJS and unit testing,
I'm testing a list that gets changing by selected category.
The test is passing but only if I use the httpBackend.expectGET() that expects the XMLHttpRequest from the "getSomethingElse" method.
I also tried to use the scope.$digest() but I got the same results...
The Controller:
app.controller('mainCtrl', ['$scope', 'myService', function($scope,
myService) {
$scope.category = null;
myService.getSomethingElse().then(function(res) {
$scope.somethingElse = res.data;
});
$scope.$watch('category', function() {
if ($scope.category !== null) {
myService.getListByCat($scope.category.name).then(function(res) {
$scope.list = res.data;
});
}
else {
myService.getLongList().then(function(res) {
$scope.list = res.data;
});
}
});
}]);
The Service:
app.service('myService', ['$http', function($http) {
this.getListByCat = function(category) {
return $http.get('getting-list?cat=' + category);
};
this.getLongList = function() {
return $http.get('getting-long-list');
};
this.getSomethingElse = function() {
return $http.get('getting-something-else');
};
}]);
The Test
describe('Testing mainCtrl', function() {
var scope, ctrl;
var myServiceMock = {
getSomethingElse: jasmine.createSpy().and.returnValue(1),
getListByCat: jasmine.createSpy().and.returnValue(2)
};
beforeEach(function() {
module('app');
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('mainCtrl', {
$scope: scope,
myService: myServiceMock
});
});
});
it('should update the list by selected category', function() {
expect(scope.category).toBeNull();
expect(scope.list).toBeUndefined();
scope.category = {
id: 1,
name: 'Jobs'
};
scope.$apply();
expect(myServiceMock.getSomethingElse).toHaveBeenCalled();
expect(myServiceMock.getListByCat).toHaveBeenCalled();
});
});
The test is passing but only if I use the httpBackend.expectGET() that expects the XMLHttpRequest from the "getSomethingElse" method.
This is because your myServiceMock is not replacing the original myService. You have various ways to test this - one of them is given below. Here we are replacing myService with the service mock:-
beforeEach(function() {
module('app');
module(function($provide){
$provide.factory('myServiceMock',
function(){
return myServiceMock;
);
});
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('mainCtrl', {
$scope: scope,
myService: myServiceMock
});
});
});
You can add your watcher like this.
$scope.categoryWatcher = categoryWatcher;
$scope.$watch('category', categoryWatcher);
function categoryWatcher() {
if ($scope.category !== null) {
myService.getListByCat($scope.category.name).then(function(res) {
$scope.list = res.data;
});
}
else {
myService.getLongList().then(function(res) {
$scope.list = res.data;
});
}
}
and in Unit testing just create new it construct for that handler
it('should test categoryWatcher for null value', function(){
$scope.category = null;
$scope.categoryWatcher();
// your expectations
});
it('should test categoryWatcher for "desiredValue" value', function(){
$scope.category = "desiredValue";
$scope.categoryWatcher();
// your expectations
});
that way, if&else clauses will be taken in the test.
APP
(function() {
'use strict';
function ArticlesController($templateCache, $modal, articlesData, Articles, $state) {
var articles = this;
articles.data = articlesData.data;
console.log($modal);//Give me LOG: Object{open: function () { ... }}
articles.open = function (article) {
var modalInstance = $modal.open({ // The unit test point to this line
template: $templateCache.get('articles/templates/modalDestroy.html'),
controller: 'ArticleDestroyController',
controllerAs: 'article',
size: 'sm',
resolve: {
articleData: function(){
return article;
}
}
});
modalInstance.result.then(function (article) {
Articles.destroy(article._id).then(function(response) {
var data = [];
angular.forEach(articles.data, function(value, key) {
if( value._id !== article._id){
this.push(value);
}
}, data);
articles.data = data;
})
.catch(function(response) {
console.log(response);
});
});
};
}
function ArticleDestroyController($modalInstance, articleData, Articles) {
var article = this;
article.data = articleData;
article.ok = function () {
$modalInstance.close(articleData);
};
article.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
angular.module('articles.controllers', [])
.controller('ArticlesController', ArticlesController)
.controller('ArticleDestroyController', ArticleDestroyController);
})();
TEST
'use strict';
/* global describe, beforeEach, it, before, after, afterEach, inject, expect, spyOn */
describe('Unit: ArticlesController', function() {
var $rootScope, $scope, $controller;
beforeEach(function() {
module('articles');
});
var fakeModal = {
result: {
then: function(confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function( item ) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack( item );
},
dismiss: function( type ) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback( type );
}
};
beforeEach(inject(function($modal) {
spyOn($modal, 'open').and.callFake(fakeModal);
}));
beforeEach(inject(function(_$rootScope_,_$controller_, $modal){
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$controller = _$controller_;
var articlesData = {data:[{title:'my title',content:'my content'}]};
$controller('ArticlesController as articles', {$scope: $scope, $modal: $modal, articlesData:articlesData});
}));
it('articles.data should create an array with at least one article object ', function() {
expect($scope.articles.data.length).toBe(1);
$scope.articles.open($scope.articles.data[0]);
});
});
When I run gulp unit
I've got
TypeError: undefined is not a function
at ArticlesController.articles.open
point to line 9
var modalInstance = $modal.open
but if I try to check $modal it gives me
console.log($modal);//Give me LOG: Object{open: function () { ... }}
Do you see what's the problem ?
spyOn($modal, "open").and.callFake(function() {
return fakeModal;
});
Ran into this exact issue at work and resolved it in this way based on the documentation for Jasmine 2.0(http://jasmine.github.io/2.0/introduction.html).
I am testing the directive below that uses an isolated scope. I know the triggerHandler is working but for some reason I keep getting the error
Expected undefined to equal 'http://www.gravatar.com/avatar/12345?s=40&d=identicon'.
Directive:
angular.module('pb.webSites.directives')
.directive('pbOrganizationImagePicker', [ function () {
return {
restrict: "E",
template: '<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />',
scope: {
fileId: '=pbFileId',
defaultSrc: '#pbDefaultSrc',
width: '#pbWidth',
height: '#pbHeight'
},
controller: 'pbOrganizationImagePickerController',
link: function (scope, element, attrs) {
scope.$watch('defaultSrc', function (value) {
if (value !== undefined) {
scope.imageSource = value;
}
});
element.on('click', function () {
scope.pickImage().then(function (image) {
scope.imageSource = image.storageUrl;
scope.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
}
};
}]);
Tests:
describe('pbOrganizationImagePicker', function () {
beforeEach(module('pb.webSites.controllers'));
beforeEach(module('pb.webSites.directives'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var compile;
var scope;
var mockModal = {};
var image;
beforeEach(inject(function ($compile, $rootScope) {
compile = $compile
scope = $rootScope.$new();
}));
beforeEach(inject(function ($q, $injector) {
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET('/app/webSites/directives/OrganizationImagePicker.html').respond(200, '');
scopeObject = {
profileImageUrl: 'http://www.gravatar.com/avatar/12345?s=40&d=identicon',
profileImageId: 54634
};
scope.webSite = {
profileImageId: 6436
};
scope.pickImage = function () {
var defer = $q.defer();
defer.resolve(scopeObject);
return defer.promise;
};
}));
describe('element.click()', function () {
beforeEach(function () {
var html = angular.element('<pb-organization-image-picker data-pb-default-src="{{ webSite.profileImageUrl || \'/content/img/placeholder-lg.jpg\' }}" data-pb-file-id="webSite.profileImageId" data-pb-width="200"></pb-organization-image-picker>');
element = compile(html)(scope);
element.triggerHandler('click');
});
it('should assign value to scope variables', function () {
scope.pickImage();
scope.$digest();
expect(scope.imageSource).toEqual(scopeObject.profileImageUrl);
expect(scope.fileId).toEqual(scopeObject.profileImageId);
});
});
});
I have also tried changing the test to the following since Im pretty sure in the test above I am faking the test a bit. However here I get pickImage() was never called. Even if you dont see the problem which method do you think is better for testing?
describe('element.click()', function () {
it('should assign value to scope variables', function () {
element = compile(html)(scope);
spyOn(scope, 'pickImage');
element.triggerHandler('click');
scope.$apply();
//scope.pickImage();
expect(scope.pickImage).toHaveBeenCalled();
scope.$digest();
expect(scope.imageSource).toEqual(scopeObject.profileImageUrl);
expect(scope.fileId).toEqual(scopeObject.profileImageId);
});
});
element.on('click', function () {
scope.$apply(function() {
scope.pickImage().then(function (image) {
scope.imageSource = image.storageUrl;
scope.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
});
Wrap the code in your click handler in an $apply.
I suspect the real problem is that your directive uses an isolate scope, so it actually won't have a "pickImage()" method and when you assign imageSource and fileId, you are putting them on the directive scope, and not the scope that your test is trying to validate (the parent scope).
Your test assigns pickImage() and webSite to the scope of your test element. Since you use an isolated scope, your directive won't have access to these methods and properties. You should probably move these to a service and inject them into the directive.
It's not "correct", but to test the theory you can change your directive to:
element.on('click', function () {
scope.$apply(function(){
scope.$parent.pickImage().then(function (image) {
scope.$parent.imageSource = image.storageUrl;
scope.$parent.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
});
This IS NOT something you want in production code, but I'm just trying to demonstrate how the different scopes are related to each other.
I am having a ton of trouble testing my directive.
Right now I am stuck with these two errors
Test 'pbImagePicker template:should render HTML based on scope correctly' failed
TypeError: 'undefined' is not a function (evaluating 'element.click')
Test 'pbImagePicker element.click():should assign data from resolved promise when clicked' failed
TypeError: 'undefined' is not a function (evaluating 'scope.$new(true)')
Directive:
angular.module('pb.campaigns.directives')
.directive('pbImagePicker', ['$window', '$document', function ($window, $document) {
return {
restrict: "E",
template: '<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />',
scope: {
fileId: '=pbFileId',
accountId: '=pbAccountId',
defaultSrc: '#pbDefaultSrc',
width: '#pbWidth',
height: '#pbHeight'
},
controller: 'pbImagePickerController',
link: function (scope, element, attrs) {
scope.$watch('defaultSrc', function (value) {
if (value !== undefined) {
scope.imageSource = value;
}
});
element.click(function () {
scope.pickImage(scope.accountId).then(function (image) {
scope.imageSource = image.storageUrl;
scope.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
}
};
}]);
I am also trying to test the element.click by using the triggerHandler but im not having any luck. Testing the link: $watch function has also proven to be impossible for me. Has anyone ever done this? I have found simular examples but none that I seem to be able to get working
this is what I currently have:
describe('pbImagePicker', function () {
beforeEach(module('pb.campaigns.controllers'));
beforeEach(module('pb.campaigns.directives'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var compile;
var scope;
var mockModal = {};
// var element = {
// click: function () { }
//};
beforeEach(inject(function ($compile, $rootScope) {
compile = $compile;
scope = $rootScope.$new();
}));
describe('template', function () {
it('should render HTML based on scope correctly', function () {
//scope = {
scope.fileId = '43253';
scope.accountId = '3874';
scope.imageSource = 'http://www.gravatar.com/avatar/12345?s=40&d=identicon';
scope.width = '250';
scope.height = '250';
//};
var html = angular.element('<pb-image-picker data-pb-default-src="{{ campaign.sponsorSmallImageUrl || \'/content/img/placeholder-sm.jpg\' }}" data-pb-file-id="campaign.sponsorSmallImageId" data-pb-account-id="currentAccountId" data-pb-height="55"></pb-image-picker>');
compiled = compile(html)(scope);
compiled.triggerHandler('click');
scope.$digest();
expect(scope.width).toEqual('250');
expect(scope.accountId).toEqual('3874');
expect(element.hasClass('img-rounded')).toBe(true);
expect(element.attr('width')).toEqual('250');
expect(element.attr('height')).toEqual('250');
expect(element.attr('alt')).toMatch('Image Picker');
//or even expect it to match the scope value
expect(element.attr('width')).toEqual(scope.width);
});
});
describe('element.click()', function () {
var image;
beforeEach(inject(function ($q) {
image = {
storageUrl: 'http://www.pressboard.com/avatar/7453',
fileId: 6432342
};
scope.pickImage = function (accountId) {
var defer = $q.defer();
defer.resolve(image);
return defer.promise;
};
scope = {
fileId: '43253',
accountId: '3874',
imageSource: 'http://www.gravatar.com/avatar/12345?s=40&d=identicon',
width: '250',
height: '250'
};
}));
it('should assign data from resolved promise when clicked', function() {
var element = compile('<pb-image-picker data-pb-default-src="{{ campaign.sponsorSmallImageUrl || \'/content/img/placeholder-sm.jpg\' }}" data-pb-file-id="campaign.sponsorSmallImageId" data-pb-account-id="currentAccountId" data-pb-height="55"></pb-image-picker>')(scope);
element.triggerHandler('click');
scope.$digest();
expect(scope.imageSource).toEqual(image.storageUrl);
expect(scope.fileId).toEqual(image.fileId);
});
});
});
the pickImage function is defined in my controller as
$scope.pickImage = function (accountId) {
var modalOptions = {
templateUrl: '/app/campaigns/directives/ImagePicker.html',
controller: 'pbImagePickerModalController',
resolve: {
accountId: function () {
return accountId;
}
}
};
var modalInstance = $modal.open(modalOptions);
return modalInstance.result;
};
I am stumped. I have written modal tests a few times now but in the controller I am currently testing I keep getting a TypeError: 'undefined' is not a function (evaluating 'pressReleaseCampaignService.deleteCampaign(global.activeOrganizationId, accountId, campaign.entityId) error. I want the test to use my mock however and Im not sure why it keep referencing the original controller. Does anyone see my mistake?
Controller:
angular.module('pb.campaigns.controllers')
.controller('ConfirmDeleteModalController', ['$scope', '$stateParams', '$state', '$modal', '$modalInstance', 'global', 'pbRoles', 'campaign', 'accountId', 'pressReleaseCampaignService',
function ($scope, $stateParams, $state, $modal, $modalInstance, global, pbRoles, pressReleaseCampaignService, campaign, accountId) {
$scope.campaign = campaign;
$scope.currentAccountId = accountId;
$scope.delete = function (accountId, campaign) {
global.setFormSubmitInProgress(true);
pressReleaseCampaignService.deleteCampaign(global.activeOrganizationId, accountId, campaign.entityId).then(function () {
global.setFormSubmitInProgress(false);
$modalInstance.close();
},
function (errorData) {
global.setFormSubmitInProgress(false);
});
};
$scope.cancel = function () {
global.setFormSubmitInProgress(false);
$modalInstance.dismiss('cancel');
};
}]);
Test:
describe('ConfirmDeleteModalController', function () {
beforeEach(module('pb.roles'));
beforeEach(module('pb.campaigns.controllers'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var mockModalInstance = {};
var mockcampaignBuyRequestService = {};
var mockGlobal = {};
var mockStateParams = {};
var mockState = {};
var mockPbRoles = {};
beforeEach(inject(function ($q) {
mockGlobal = {
setFormSubmitInProgress: function (boolean) {
this.formProgress = boolean;
},
formProgress: false,
activeOrganizationId: 0
};
mockStateParams = {
orgId: 1,
accountId: 3,
entityId: 94,
campaignId: 45,
page: 3,
length: 12
};
mockModalInstance = {
close: jasmine.createSpy('mockModalInstance.close'),
dismiss: jasmine.createSpy('mockModalInstance.dismiss'),
result: {
then: jasmine.createSpy('mockModalInstance.result.then')
}
};
mockPressReleaseCampaignService = {
campaign: { entityId: 2, page: 19, length: 200 },
deleteCampaign: function (activeOrganizationId, entityId) {
var defer = $q.defer();
defer.resolve();
return defer.promise;
}
};
}));
beforeEach(inject(function ($rootScope, _$controller_) {
scope = $rootScope.$new();
$controller = _$controller_;
controller = $controller('ConfirmDeleteModalController', {
$modalInstance: mockModalInstance,
$scope: scope,
$stateParams: mockStateParams,
$state: mockState,
global: mockGlobal,
pbRoles: mockPbRoles,
pressReleaseCampaignService: mockPressReleaseCampaignService,
campaign: function () {
return mockPressReleaseCampaignService.campaign
},
accountId: function () {
return mockPressReleaseCampaignService.accountId
}
});
}));
describe("delete() function", function () {
it("calls deleteCampaign with proper params", function () {
spyOn(mockPressReleaseCampaignService, "deleteCampaign").and.callThrough();
scope.delete(mockStateParams.accountId, mockPressReleaseCampaignService.campaign);
expect(mockPressReleaseCampaignService.deleteCampaign).toHaveBeenCalledWith(mockGlobal.activeOrganizationId, mockStateParams.accountId, mockPressReleaseCampaignService.campaign.entityId);
});
it("calls close and toggles setFormSubmitionInProgress status", function () {
scope.delete(mockStateParams.accountId, mockPressReleaseCampaignService.campaign);
expect(mockGlobal.formProgress).toEqual(true);
scope.$digest();
expect(mockModalInstance.close).toHaveBeenCalled();
expect(mockGlobal.formProgress).toEqual(false);
});
});
describe("cancel() function", function () {
it("changes formProgress from true to false", function () {
mockGlobal.setFormSubmitInProgress.formProgress = true;
scope.cancel();
expect(mockModalInstance.dismiss).toHaveBeenCalled();
expect(mockGlobal.formProgress).toEqual(false);
});
});
});
The order of the service names is not the same as the order of the variables:
'campaign', 'accountId', 'pressReleaseCampaignService'
vs.
pressReleaseCampaignService, campaign, accountId
So, when your code calls a method on pressReleaseCampaignService, it actually calls a method on the campaign service.