How do I unit test further .then and .catch branching in an Angular controller? - angularjs

(function() {
'use strict';
angular
.module('walletInformation')
.controller('WalletInformationController', WalletInformationController);
/* #ngInject */
function WalletInformationController(
$q,
$scope,
config,
logger,
session,
$interpolate,
flowstack,
profileService,
walletInformationFormlyService,
postMsg
) {
// jshint validthis: true
var vm = this;
var app = $scope.app;
var user = session.get('profile').profile || {};
vm.saveWalletInformation = saveWalletInformation;
vm.onClickCancel = onClickCancel;
vm.deleteWallet = deleteWallet;
vm.model = {
walletInformation : {
firstName: user.name.first,
lastName: user.name.last,
emailAddress: user.emailAddress,
phoneNumber: user.mobilePhone.phoneNumber,
keepMeUpdated: user.preferences.receiveEmailNotification
}
};
activate();
////////////////
/**
* #function activate
* #description init function for the controller
* Calls paymentCard to get the
* */
function activate() {
createFormFields();
logger.info('Activated the Wallet Information View.');
}
function createFormFields() {
vm.fields = walletInformationFormlyService.getFormlyFields($scope.app.text);
}
function saveWalletInformation() {
var updatedProfile = createLegacyProfileInformation();
profileService.updateProfileInformation(updatedProfile).then(onSuccess).catch(onError);
function onSuccess(response) {
session.set('profile', {
profile: response.profile
});
if (vm.model.walletInformation.currentPassword && vm.model.walletInformation.newPassword) {
changePasswordRequest();
} else {
flowstack.add('accountManagement');
flowstack.next();
}
}
function onError(error) {
logger.error('Verify save Wallet Information Error: ', error);
}
//changePassword
function changePasswordRequest() {
var updatedPassword = {
data: {
currentPassword: vm.model.walletInformation.currentPassword,
newPassword: vm.model.walletInformation.newPassword
}
};
profileService
.changePassword(updatedPassword)
.then(onChangePasswordSuccess, onChangePasswordError);
function onChangePasswordSuccess(response) {
flowstack.add('accountManagement');
flowstack.next();
logger.info('Change password request: ', response);
}
function onChangePasswordError(response) {
logger.error('Change Password Error: ', response);
}
}
}
function deleteWallet() {
var appText = app.text.walletInformation;
console.log(appText);
flowstack.add('confirmation');
flowstack.next({
data: {
title: appText.deleteWalletConfirmationTitle,
body: appText.deleteWalletConfirmationBody,
cancelButton: appText.deleteWalletConfirmationCancelButton,
confirmButton: appText.deleteWalletConfirmationConfirmButton,
onConfirm: function() {
profileService.deleteWallet().then(deleteProfileSuccess, deleteProfileFailure);
function deleteProfileSuccess(response) {
if (response.profile) {
logger.info('profile is successfully deleted.', response);
postMsg.send('closeSwitch');
}
}
function deleteProfileFailure(error) {
logger.error('something went wrong while deleting profile.', error);
}
},
onCancel: function() {
flowstack.add('walletInformation');
flowstack.next();
}
}
});
}
function createLegacyProfileInformation() {
var updatedProfile = user;
var formlyField = vm.model.walletInformation;
//Update the profile
updatedProfile.name = {
first: formlyField.firstName,
last: formlyField.lastName
};
updatedProfile.mobilePhone.phoneNumber = formlyField.phoneNumber;
updatedProfile.preferences.receiveEmailNotification = formlyField.keepMeUpdated;
return {
data: updatedProfile
};
}
function onClickCancel() {
flowstack.back();
}
}
})();
Coverage is saying that onSuccess of saveWalletInformation and changePasswordRequest isnt't covered but I'm not sure exactly how to test it, the only thing I've tested now is that the saveWalletInformation function is calling the profile service:
describe.only('WalletInformationController ---', function() {
var controller, scope;
var userProfile = {
profile: {
name: {
first: 'someonefirstname',
last: 'someoneslastname'
},
emailAddress: 'someone#something.com',
mobilePhone: {
countryCode: 'US+1',
phoneNumber: '123 212 2342'
},
preferences: {
receiveEmailNotification: true
}
}
};
beforeEach(function() {
bard.appModule('walletInformation');
bard.inject(
'$q',
'$controller',
'$rootScope',
'api',
'flowstack',
'logger',
'session',
'profileService'
);
session.set('profile', userProfile);
});
beforeEach(function() {
sandbox = sinon.sandbox.create();
scope = $rootScope.$new();
scope.app = {
text: {
global: {},
walletInformation: {
deleteWalletConfirmationTitle: 'Confirm Wallet Deletion?'
},
userInformation: {
emailValidation: 'Please enter valid email'
},
signin: {
rememberMeLabel: 'remember me'
}
}
};
controller = $controller('WalletInformationController', {
$scope: scope
});
loggerErrorStub = sandbox.stub(logger, 'error');
sandbox.stub(logger, 'info');
controller = $controller('WalletInformationController', {
$scope: scope
});
});
describe('saveWalletInformation method', function() {
beforeEach(function() {
// apiStub.restore();
sandbox.stub(profileService, 'updateProfileInformation', function() {
return $q.when(userProfile);
});
});
it('should saveWalletInformation successfully', function() {
controller.saveWalletInformation();
expect(profileService.updateProfileInformation).to.have.been.called;
});
it('should log error msg when saveWalletInformation call fails', function() {
profileService.updateProfileInformation.restore();
sandbox.stub(profileService, 'updateProfileInformation', function() {
return $q.reject();
});
controller.saveWalletInformation();
scope.$apply();
expect(loggerErrorStub).to.have.been.calledOnce;
});
it('should call changePasswordRequest when currentPassword and newPassword are set', function() {
sandbox.stub(profileService, 'changePassword', function() {
return $q.when({
success: true,
extensionPoint: null
});
});
sandbox.stub(flowstack, 'add');
sandbox.stub(flowstack, 'next');
controller.saveWalletInformation();
// scope.$apply();
// expect(flowstack.next).to.have.been.calledOnce;
// expect(flowstack.add).to.have.been.calledOnce;
expect(profileService.updateProfileInformation).to.have.been.called;
// expect(profileService.changePassword).to.have.been.called;
});
});

You can let the service call go a step further and instead of verifying that service function was called, as you do here:
expect(profileService.updateProfileInformation).to.have.been.called;
You can instead mock the response from the api call with $httpBackend:
httpBackend.expectGET('your/url/here').respond(200, {response object...});
or
httpBackend.expectGET('your/url/here').respond(500, {error object...});
Then you'll be hitting both the error and success functions when your tests run

Related

WebRTC remote video stream not working despite having src

I'm trying to make a simple AngularJS WebRTC video chat app based on this tutorial.
I'm able to connect to connect clients, add streams and play my own stream, but somehow I can't play the remote stream.
When I check the elements I can see that the videoplayer does in fact have a blob source, but it won't play it.
Can anyone tell me why it won't show?
HTML element:
Room controller:
angular.module('publicApp').controller('RoomCtrl', function ($sce, VideoStream, $location, $routeParams, $scope, Room)
{
if (!window.RTCPeerConnection || !navigator.getUserMedia) {
$scope.error = 'WebRTC is not supported by your browser. You can try the app with Chrome and Firefox.';
return;
}
var stream;
VideoStream.get()
.then(function (s) {
stream = s;
Room.init(stream);
stream = URL.createObjectURL(stream);
if (!$routeParams.roomId) {
Room.createRoom()
.then(function (roomId) {
$location.path('/room/' + roomId);
});
} else {
Room.joinRoom($routeParams.roomId);
}
}, function () {
$scope.error = 'No audio/video permissions. Please refresh your browser and allow the audio/video capturing.';
});
$scope.peers = [];
Room.on('peer.stream', function (peer) {
console.log('Client connected, adding new stream');
$scope.peers.push({
id: peer.id,
stream: URL.createObjectURL(peer.stream)
});
console.log($scope.peers);
});
Room.on('peer.disconnected', function (peer) {
console.log('Client disconnected, removing stream');
$scope.peers = $scope.peers.filter(function (p) {
return p.id !== peer.id;
});
});
$scope.getLocalVideo = function () {
return $sce.trustAsResourceUrl(stream);
};
});
Room factory:
angular.module('publicApp').factory('Room', function ($rootScope, $q, Io, config)
{
var iceConfig = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]},
peerConnections = {},
currentId, roomId,
stream;
function getPeerConnection(id)
{
if (peerConnections[id]) {
return peerConnections[id];
}
var pc = new RTCPeerConnection(iceConfig);
peerConnections[id] = pc;
pc.addStream(stream);
pc.onicecandidate = function (evnt) {
socket.emit('msg', { by: currentId, to: id, ice: evnt.candidate, type: 'ice' });
};
pc.onaddstream = function (evnt) {
console.log('Received new stream');
api.trigger('peer.stream', [{
id: id,
stream: evnt.stream
}]);
if (!$rootScope.$$digest) {
$rootScope.$apply();
}
};
return pc;
}
function makeOffer(id)
{
var pc = getPeerConnection(id);
pc.createOffer(function (sdp) {
pc.setLocalDescription(sdp);
console.log('Creating an offer for', id);
socket.emit('msg', { by: currentId, to: id, sdp: sdp, type: 'sdp-offer' });
}, function (e) {
console.log(e);
},
{ mandatory: { OfferToReceiveVideo: true, OfferToReceiveAudio: true }});
}
function handleMessage(data)
{
var pc = getPeerConnection(data.by);
switch (data.type) {
case 'sdp-offer':
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
console.log('Setting remote description by offer');
pc.createAnswer(function (sdp) {
pc.setLocalDescription(sdp);
socket.emit('msg', { by: currentId, to: data.by, sdp: sdp, type: 'sdp-answer' });
});
});
break;
case 'sdp-answer':
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
console.log('Setting remote description by answer');
}, function (e) {
console.error(e);
});
break;
case 'ice':
if (data.ice) {
console.log('Adding ice candidates');
pc.addIceCandidate(new RTCIceCandidate(data.ice));
}
break;
}
}
var socket = Io.connect(config.SIGNALIG_SERVER_URL),
connected = false;
function addHandlers(socket)
{
socket.on('peer.connected', function (params) {
makeOffer(params.id);
});
socket.on('peer.disconnected', function (data) {
api.trigger('peer.disconnected', [data]);
if (!$rootScope.$$digest) {
$rootScope.$apply();
}
});
socket.on('msg', function (data) {
handleMessage(data);
});
}
var api = {
joinRoom: function (r) {
if (!connected) {
socket.emit('init', { room: r }, function (roomid, id) {
currentId = id;
roomId = roomid;
});
connected = true;
}
},
createRoom: function () {
var d = $q.defer();
socket.emit('init', null, function (roomid, id) {
d.resolve(roomid);
roomId = roomid;
currentId = id;
connected = true;
});
return d.promise;
},
init: function (s) {
stream = s;
}
};
EventEmitter.call(api);
Object.setPrototypeOf(api, EventEmitter.prototype);
addHandlers(socket);
return api;
});
Directive:
angular.module('publicApp').directive('videoPlayer', function ($sce) {
return {
template: '<div><video ng-src="{{trustSrc()}}" autoplay></video></div>',
restrict: 'E',
replace: true,
scope: {
vidSrc: '#'
},
link: function (scope) {
console.log('Initializing video-player');
scope.trustSrc = function () {
if (!scope.vidSrc) {
console.log('No vidSrc found');
return undefined;
}
return $sce.trustAsResourceUrl(scope.vidSrc);
};
}
};
});
Room.html:
<div class="video-wrapper">
<video-player class="col-md-4" ng-repeat="peer in peers" vid-src="{{peer.stream}}"></video-player>
</div>
<div class="video-wrapper">
<div class="col-md-2">
<video ng-src="{{getLocalVideo()}}" autoplay muted></video>
</div>
</div>

Error: $controller:ctrlreg A controller with this name is not registered

I see this error when I press F12 in chrome, but strangely things still work. There seems to be no problems.
The controller with the name 'AccountUpdateViewModel' is not registered.
Here is my js file. Any idea why? The version of angualr I am using is 1.6.0 from nuget(I develop on Visual Studio)
var accountUpdateModule = angular.module('accountUpdate', ['common', 'ngRoute'])
.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
$routeProvider.when(Avts.rootPath + 'account/update/step1', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Step1.html', controller: 'AccountUpdateStep1ViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/step2', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Step2.html', controller: 'AccountUpdateStep2ViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/step3', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Step3.html', controller: 'AccountUpdateStep3ViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/confirm', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Confirm.html', controller: 'AccountUpdateConfirmViewModel' });
$routeProvider.when(Avts.rootPath + 'account/update/successfullyupdated', { templateUrl: Avts.rootPath + 'Templates/AccountUpdate/Successfull.html', controller: 'AccountUpdateSuccessfullyUpdatedViewModel' });
// 5:40 Sec. If it does not find any of these steps in this little spa silo, then redirect to step1
$routeProvider.otherwise({ redirectTo: Avts.rootPath + 'account/update/step1' });
});
accountUpdateModule.controller("AccountUpdateViewModel", function ($scope, $http, $location, $window, viewModelHelper) {
// Nested ViewModels or sub viewmodels. See Video 138 3:40 Sec
// Things that are set up here in this view model(AccountRegisterViewModel) is bound to the priticular view Register.cshtml.
// see 4:50 Sec
$scope.viewModelHelper = viewModelHelper;
$scope.accountModelStep1 = new Avts.AccountUpdateModelStep1();
$scope.accountModelStep2 = new Avts.AccountUpdateModelStep2();
$scope.accountModelStep3 = new Avts.AccountUpdateModelStep3();
$scope.fetchDataToUpdate = function () {
viewModelHelper.apiGet('api/account/update', null,
function (result) {
$scope.accountModelStep1.Email = result.data.Email;
$scope.accountModelStep1.UserName = result.data.UserName;
$scope.accountModelStep2.FirstName = result.data.FirstName;
$scope.accountModelStep2.LastName = result.data.LastName;
$scope.accountModelStep3.Address = result.data.Address;
$scope.accountModelStep3.City = result.data.City;
$scope.accountModelStep3.State = result.data.State;
$scope.accountModelStep3.PostalCode = result.data.PostalCode;
});
}
$scope.fetchDataToUpdate();
//$scope.previous = function () {
// // 6:00 Sec
// $window.history.back();
//}
});
accountUpdateModule.controller("AccountUpdateStep1ViewModel", function ($scope, $http, $location, viewModelHelper, validator) {
viewModelHelper.modelIsValid = true;
viewModelHelper.modelErrors = [];
// No setpup rules for step 1 edit. Just showing Email and userName
//var accountModelStep1Rules = [];
//var setupRules = function () {
// accountModelStep1Rules.push(new validator.PropertyRule("Email", {
// required: {
// message: "Email is required",
// email: { message: "Email is not an email." }
// }
// }));
// accountModelStep1Rules.push(new validator.PropertyRule("Password", {
// required: { message: "Password is required" },
// minLength: { message: "Password must be at least 6 characters", params: 6 }
// }));
//accountModelStep1Rules.push(new validator.PropertyRule("PasswordConfirm", {
// required: { message: "Password confirmation is required" },
// custom: {
// validator: Avts.mustEqual, // See 143, 2:30
// message: "Password do not match",
// params: function () { return $scope.accountModelStep1.Password; } // must be function so it can be obtained on-demand
// }
//}));
//}
//$scope.fetchData = function () {
// viewModelHelper.apiGet('api/account/edit', null,
// function (result) {
// $scope.accountModelStep1.Email = result.data.Email;
// $scope.accountModelStep1.UserName = result.data.UserName;
// });
//}
$scope.step2 = function () {
$location.path(Avts.rootPath + 'account/update/step2');
// Video 144
// Pressing should just do validation and proceed to step 2.
//validator.ValidateModel($scope.accountModelStep1, accountModelStep1Rules);
//viewModelHelper.modelIsValid = $scope.accountModelStep1.isValid;
//viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
//if (viewModelHelper.modelIsValid) {
// viewModelHelper.apiPost('api/account/register/validate1', $scope.accountModelStep1,
// function (result) {
// // See the video 144 3:35 Sec
// $scope.accountModelStep1.Initialized = true;
// $location.path(Avts.rootPath + 'account/edit/step2');
// },
// function (failureResult) {
// $scope.accountModelStep1.errors = failureResult.data;
// viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
// viewModelHelper.modelIsValid = false;
// }
// );
//}
//else
// viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
}
//setupRules();
//$scope.fetchData();
});
accountUpdateModule.controller("AccountUpdateStep2ViewModel", function ($scope, $http, $location, viewModelHelper, validator) {
viewModelHelper.modelIsValid = true;
viewModelHelper.modelErrors = [];
var accountModelStep2Rules = [];
var setupRules = function () {
accountModelStep2Rules.push(new validator.PropertyRule("FirstName", {
required: { message: "First name is required" }
}));
accountModelStep2Rules.push(new validator.PropertyRule("LastName", {
required: { message: "Last name is required" }
}));
}
// Video 146
$scope.step3 = function () {
validator.ValidateModel($scope.accountModelStep2, accountModelStep2Rules);
viewModelHelper.modelIsValid = $scope.accountModelStep2.isValid;
viewModelHelper.modelErrors = $scope.accountModelStep2.errors;
if (viewModelHelper.modelIsValid) {
viewModelHelper.apiPost('api/account/update/validate2', $scope.accountModelStep2,
function (result) {
$scope.accountModelStep2.Initialized = true;
$location.path(Avts.rootPath + 'account/update/step3');
},
function (failureResult) {
$scope.accountModelStep2.errors = failureResult.data;
viewModelHelper.modelErrors = $scope.accountModelStep2.errors;
viewModelHelper.modelIsValid = false;
}
);
}
else
viewModelHelper.modelErrors = $scope.accountModelStep2.errors;
}
$scope.backToStep1 = function () {
$location.path(Avts.rootPath + 'account/update/step1');
}
setupRules();
});
accountUpdateModule.controller("AccountUpdateStep3ViewModel", function ($scope, $http, $location, viewModelHelper, validator) {
if (!$scope.accountModelStep2.Initialized) {
// got to this controller before going through step 2
$location.path(Avts.rootPath + 'account/update/step2');
}
var accountModelStep3Rules = [];
var setupRules = function () {
accountModelStep3Rules.push(new validator.PropertyRule("Address", {
required: { message: "Address is required" }
}));
accountModelStep3Rules.push(new validator.PropertyRule("City", {
required: { message: "City is required" }
}));
accountModelStep3Rules.push(new validator.PropertyRule("State", {
required: { message: "State is required" }
}));
accountModelStep3Rules.push(new validator.PropertyRule("PostalCode", {
required: { message: "Postal code is required" },
pattern: { message: "Postal code is in invalid format", params: /^[1-9][0-9]{5}$/ }
}));
//accountModelStep3Rules.push(new validator.PropertyRule("CreditCard", {
// required: { message: "Credit Card # is required" },
// pattern: { message: "Credit card is in invalid format (16 digits)", params: /^\d{16}$/ }
//}));
//accountModelStep3Rules.push(new validator.PropertyRule("ExpiryDate", {
// required: { message: "Expiration Date is required" },
// pattern: { message: "Expiration Date is in invalid format (MM/YY)", params: /^(0[1-9]|1[0-2])\/[0-9]{2}$/ }
//}));
}
$scope.confirm = function () {
validator.ValidateModel($scope.accountModelStep3, accountModelStep3Rules);
viewModelHelper.modelIsValid = $scope.accountModelStep3.isValid;
viewModelHelper.modelErrors = $scope.accountModelStep3.errors;
if (viewModelHelper.modelIsValid) {
viewModelHelper.apiPost('api/account/update/validate3', $scope.accountModelStep3,
function (result) {
$scope.accountModelStep3.Initialized = true;
$location.path(Avts.rootPath + 'account/update/confirm');
},
function (failureResult) {
$scope.accountModelStep3.errors = failureResult.data;
viewModelHelper.modelErrors = $scope.accountModelStep3.errors;
viewModelHelper.modelIsValid = false;
}
);
}
else
viewModelHelper.modelErrors = $scope.accountModelStep3.errors;
}
$scope.backToStep2 = function () {
$location.path(Avts.rootPath + 'account/update/step2');
}
setupRules();
});
accountUpdateModule.controller("AccountUpdateConfirmViewModel", function ($scope, $http, $location, $window, viewModelHelper) {
if (!$scope.accountModelStep3.Initialized) {
// got to this controller before going through step 3
$location.path(Avts.rootPath + 'account/update/step3');
}
$scope.updateAccount = function () {
// Video 147 2:00 Sec
var accountModel;
accountModel = $.extend(accountModel, $scope.accountModelStep1);
accountModel = $.extend(accountModel, $scope.accountModelStep2);
accountModel = $.extend(accountModel, $scope.accountModelStep3);
viewModelHelper.apiPost('api/account/update', accountModel,
function (result) {
//$location.path(Avts.rootPath);
$location.path(Avts.rootPath + 'account/update/successfullyupdated');
//account/update/successfullyupdateded
//account/update/successfullyupdated
//$window.location.href = Avts.rootPath;
},
function (failureResult) {
$scope.accountModelStep1.errors = failureResult.data;
viewModelHelper.modelErrors = $scope.accountModelStep1.errors;
viewModelHelper.modelIsValid = false;
}
);
}
$scope.backToStep3 = function () {
$location.path(Avts.rootPath + 'account/update/step3');
}
});
//AccountUpdateSuccessfullyUpdatedViewModel
accountUpdateModule.controller("AccountUpdateSuccessfullyUpdatedViewModel", function ($scope, $http, $location, $window, viewModelHelper) {
});
You had 'AccountUpdateViewModel controller. but you didn't registered it in your route.config. if you didn't config means you never use it anywhere.
please remove unwanted model. or register it in the rout config section.
i import the file, in index.html and my problem solved,
<script src=".../AccountUpdateStep1ViewModel.js"></script>

Angularjs need to vary a parameter for each tick of angular-poller

I'm following the angular-poller demo here:
https://emmaguo.github.io/angular-poller/
I need to be able to pass an argument into the factory to construct a dynamic url so that 'greet' is called with a newTime arg.
How can I modify poller.get() to pass in 'newTime'?
poller1 = poller.get(greet, {action: 'jsonp_get', delay: 1000});
.factory('greet', function ($resource) {
return $resource('https://www.example.com?time=' + newTime,
{
},
{
jsonp_get: { method: 'JSONP' }
});
})
/-- Demo --/
angular.module('myApp', ['ngResource', 'emguo.poller'])
.factory('greet1', function ($resource) {
return $resource('https://angularjs.org/greet.php',
{
callback: 'JSON_CALLBACK',
name: 'Emma'
},
{
jsonp_get: { method: 'JSONP' }
});
})
.factory('greet2', function ($resource) {
return $resource('https://angularjs.org/greet.php',
{
callback: 'JSON_CALLBACK',
name: 'You'
},
{
jsonp_get: { method: 'JSONP' }
});
})
.controller('myController', function ($scope, poller, greet1, greet2) {
var poller1, poller2;
/*-- Automatically start poller1 on page load --*/
poller1 = poller.get(greet1, {action: 'jsonp_get', delay: 1000});
poller1.promise.then(null, null, function (data) {
$scope.data1 = data;
});
/*-- Actions --*/
$scope.stop = function () {
poller1.stop();
};
$scope.restart = function () {
poller1 = poller.get(greet1);
};
$scope.faster = function () {
poller1 = poller.get(greet1, {delay: 300});
};
$scope.slower = function () {
poller1 = poller.get(greet1, {delay: 1500});
};
$scope.startPoller2 = function () {
poller2 = poller.get(greet2, {action: 'jsonp_get', delay: 1000});
poller2.promise.then(null, null, function (data) {
$scope.data2 = data;
});
};
$scope.stopBoth = function () {
poller.stopAll();
};
$scope.restartBoth = function () {
poller.restartAll();
};
});
Didn't use angular-poller, but seems you should be able to get what you want with the property argumentsArray:
var poller1 = poller.get(greet, {
action: 'jsonp_get',
delay: 1000,
argumentsArray: [{
time: newTime
}]
});
The array argumentsArray, therefore the time, is evaluated when the poller is retrieved.
Edit: After clarification, it appears you need to vary a parameter for each tick of the polling, and not when you get the poller. You could solve that by adding the parameter in a request interceptor:
// define interceptor
app.factory('requestInterceptor', function() {
return {
'request': function(request) {
// if needed, wrap the following in a condition
request.params = request.params || {};
request.params.time = moment();
return request;
}
}
})
// register interceptor
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('requestInterceptor');
})

ngDescribe Won't Complete Second Spec

I'm having trouble with a chained spec when using ngDescribe. The second spec never finishes and times out. If I comment out the first spec, the second spec completes successfully. So, I know each spec is valid and works.
Here is the whole test with both specs.
var baseNotes = window.__mocks__['base-data/notes'];
var baseUsers = window.__mocks__['base-data/users'];
var baseDepartments = window.__mocks__['base-data/departments'];
// Create object to describe all the mocks
var mock = {
app: {
// Mock out all required DB functionality
LocalDb: {
'service': {
dbs: [
{
notes: true
}
]
},
'getDbInfo': function($q) {
return $q.when()
},
'allDocs': function($q) {
return $q.when(baseNotes);
},
'putDoc': function($q) {
var note = {};
note._id = 'xyz123';
console.log("Here is the note now");
console.log(note);
return $q.when(note);
}
},
Users: {
getUsers: function($q) {
return $q.when(baseUsers)
}
},
Departments: {
getDepartments: function($q) {
return $q.when(baseDepartments);
}
}
}
};
ngDescribe
({
name: 'Process Generic Notes Requests',
modules: ['app'],
// Must include $rootScope so that deps.step() is available
inject: ['Notes', 'NoteFactory', '$rootScope'],
mock: mock,
tests: function(deps){
beforeEach(function() {
// Preload the service with notes
deps.Notes.notes = baseNotes;
});
it('should refresh notes from db', function(done) {
// remove the notes from the service
deps.Notes.notes = [];
expect(deps.Notes.notes.length).toEqual(0);
deps.Notes.getNotes(true).then(
function(result) {
expect(result.length).toEqual(3);
done();
}
);
deps.step();
});
it('should fetch notes from memory', function(done) {
deps.Notes.getNotes().then(
function(result) {
expect(result.length).toEqual(3);
done();
}
);
deps.step();
});
it('should get specific note', function(done) {
deps.Notes.getNote(baseNotes[0]._id).then(
function(result) {
expect(result._id).toEqual(baseNotes[0]._id);
done();
}
);
deps.step();
});
it('should get last note', function(done) {
deps.Notes.getLastNote().then(
function(result) {
expect(result._id).toEqual('1436880052000-34lekd');
done();
}
);
deps.step();
});
it('should summarize notes', function() {
var summary = deps.Notes.summarizeNotes();
expect(summary.total).toEqual(3);
expect(summary.finished).toEqual(2);
expect(summary.remaining).toEqual(1);
console.log("Done processing summarize at " + Date.now());
});
}
})({
name: 'Process Update Note Requests',
modules: ['app'],
// Must include $rootScope so that deps.step() is available
inject: ['Notes', 'NoteFactory', '$rootScope'],
mock: mock,
tests: function (deps) {
var newNote;
beforeEach(function () {
// Preload the service with notes
deps.Notes.notes = baseNotes;
// Create a new note
newNote = deps.NoteFactory.createNote();
});
it('should save a new note', function (done) {
console.log("Starting save a note at " + Date.now());
console.log("Here is the note about to be saved");
console.log(newNote);
deps.Notes.updateNote(newNote).then(
function (response) {
dump('Response from update note!');
dump(response);
expect(response._id).toEqual('xyz123');
done();
},
function (error) {
dump('Failed to update note!');
dump(error);
expect('good').toEqual('bad');
done();
}
);
deps.step();
});
}
});

AngularJS Error: d[h].apply is not a function

When I navagate away from a page in my angular site the error console fills up (occurs 108 times) with the error:
Error: d[h].apply is not a function
jf/this.$get</n.prototype.$broadcast#http://localhost:9885/Scripts/angular.min.js:137:355
jf/this.$get</n.prototype.$destroy#http://localhost:9885/Scripts/angular.min.js:133:254
ye</<.link/<#http://localhost:9885/Scripts/angular.min.js:252:477
jf/this.$get</n.prototype.$digest#http://localhost:9885/Scripts/angular.min.js:132:257
jf/this.$get</n.prototype.$apply#http://localhost:9885/Scripts/angular.min.js:135:267
Kc[c]</<.compile/</<#http://localhost:9885/Scripts/angular.min.js:252:124
n.event.dispatch#http://localhost:9885/Scripts/jquery-bundle.js:3:6414
n.event.add/r.handle#http://localhost:9885/Scripts/jquery-bundle.js:3:3224
It only occurs on this one page but I have no $watches or $broadcast events on it. Could someone help me by suggesting where I should look to find the trigger for this error?
I appreciate not having the code makes this difficult but I am keen to have some suggestions on what things cause errors like this and/or the best way to debug it.
UPDATE
app.controller('ticketController', ['$scope', '$state', 'Page', 'globals', 'localStorageService', 'Ticket', 'User', 'ticketData', 'ELearning', 'dialogs', 'Notification', 'Payment','Note', 'DTOptionsBuilder', 'DTColumnDefBuilder', 'History', 'Correspondance',function ($scope, $state, Page, globals, localStorageService, Ticket, User, ticketData, ELearning, dialogs, Notification, Payment,Note, DTOptionsBuilder, DTColumnDefBuilder, History, Correspondance) {
if (globals.debug) { console.log('Ticket controller loaded'); }
$scope.globals = globals;
Page.setTitle(ticketData.forename + ' ' + ticketData.surname);
$scope.authData = localStorageService.get('authorizationData');
$scope.ticket = ticketData;
$scope.user = User.admindata({ id: ticketData.ticketGUID });
$scope.person = {};
$scope.reOpenTicket = {
isOpen: false,
previousChanges: [],
newCriticalDate: moment(new Date($scope.ticket.criticalDate)).add(1, 'M').format('DD MMM YYYY'),
minCriticalDate: moment(new Date($scope.ticket.criticalDate)).add(-1, 'd').format('DD MMM YYYY'),
maxCriticalDate: moment(new Date($scope.ticket.criticalDate)).add(1, 'M').add(1, 'd').format('DD MMM YYYY'),
minErrorDate: moment(new Date($scope.ticket.criticalDate)).format('DD MMM YYYY'),
maxErrorDate: moment(new Date($scope.ticket.criticalDate)).add(1, 'M').format('DD MMM YYYY'),
reason: '',
form: {},
saving: false,
saveError: ''
};
$scope.notes = {
data: [],
newNote: '',
loading: false,
loadError: ''
};
$scope.payments = {
data: [],
loading: true,
dtOptions: {},
dtColumnDefs: {},
ticketGUID: ''
};
$scope.learning = {
data: [],
loading: true,
ticketGUID: '',
dtOptions: {},
dtColumnDefs: {}
};
$scope.history = {
data: [],
loading: true,
loadError: ''
};
$scope.letters = {
data: [],
loading: true,
laddaResendLetter: false,
laddaCancelLetter: false
};
$scope.dob = {
minDate: moment(new Date()).add(-90, 'y').format(),
maxDate: moment(new Date()).add(-10, 'y').format()
};
$scope.titles = ['Mr', 'Miss', 'Mrs', 'Ms', 'Dr', 'Rev'];
$scope.savePersonTab = function (validity) {
if (validity) {
$scope.ticket.dateOfBirth = $scope.dob.chosenDate;
Ticket.personTabSave({ 'id': $scope.ticket.ticketGUID }, $scope.ticket, function (success) {
Notification.success('Record updated successfully');
Ticket.getAdmin({ id: success.ticketGUID });
$scope.person.form.$setPristine();
$scope.getHistory();
}, function (error) {
});
} else {
console.log('skip Save');
}
};
//#region Tickets
$scope.reopenTicket = function () {
$scope.reOpenTicket.isOpen = true;
$scope.reOpenTicket.previousChanges = Ticket.getCriticalDateChanges({ id: $scope.ticket.ticketGUID });
// Reset
$scope.reOpenTicket.saveError = '';
$scope.reOpenTicket.reason = '';
};
$scope.saveReopen = function (validity) {
if (validity) {
$scope.reOpenTicket.saving = true;
var data = {
ChangeTo: $scope.reOpenTicket.newCriticalDate,
ChangeReason: $scope.reOpenTicket.reason
};
Ticket.reOpenTicket({ id: $scope.ticket.ticketGUID }, data, function (response) {
$scope.reOpenTicket.saving = false;
if (response.isSuccessful) {
$scope.getNotes();
$scope.getHistory();
$scope.ticket = Ticket.getAdmin({ id: $scope.ticket.ticketGUID });
$scope.reOpenTicket.isOpen = false;
} else {
$scope.reOpenTicket.saveError = response.errorMessage;
}
});
}
};
$scope.closeNewCriticalDate = function () {
$scope.reOpenTicket.isOpen = false;
};
$scope.confirmTCs = function () {
var opts = {
'keyboard': true,
'size': 'lg' //small or large modal size
};
// Checks
if ($scope.person.form.$dirty) {
dialogs.notify('Cannot Confirm!', 'Unsaved changes to personal details detected.', opts);
return;
}
// email address is complete
if (!$scope.ticket.eMailAddress) {
dialogs.notify('Cannot confirm!', 'An Email address must be entered and saved before confirming Terms and Conditions.', opts);
return;
} else {
if ($scope.ticket.status != 'AwaitingPayment' && $scope.ticket.status != 'Referred') {
dialogs.notify('Cannot confirm!', 'Ticket status must be Awaiting Payment or Referred before confirming Terms and Conditions. The current ticket status is ' + $scope.ticket.status, opts);
return;
}
}
var dlg = dialogs.confirm('Confirm terms and conditions', 'Please confirm that this delegate has read and agreed to the Terms and Conditions and also the details and offence relate to them.', opts);
dlg.result.then(function (btn) {
Ticket.confirmation({ 'id': $scope.ticket.ticketGUID }, $scope.ticket, function (success) {
Notification.success('Record updated successfully');
$scope.ticket = success;
$scope.getHistory();
}, function (error) {
});
});
};
$scope.lockTicket = function () {
Ticket.lock({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been locked');
}, function (error) {
console.log(error);
});
};
$scope.unlockTicket = function () {
Ticket.unlock({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been unlocked');
}, function (error) {
console.log(error);
});
};
$scope.cancelTicket = function () {
Ticket.cancelTicket({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been cancelled');
}, function (error) {
console.log(error);
});
};
$scope.restoreTicket = function () {
Ticket.restoreTicket({ id: $scope.ticket.ticketGUID }, function (success) {
$scope.ticket = success;
$scope.getHistory();
Notification.success('Ticket has been restored');
}, function (error) {
console.log(error);
});
};
//#endregion
//#region Payments
$scope.markAsPaid = function () {
var opts = {
'keyboard': true,
'size': 'lg' //small or large modal size
};
var dlg = dialogs.confirm('Mark as paid', 'Please confirm that you would like to manually mark this delegate as having paid.', opts);
dlg.result.then(function (btn) {
Payment.markAsPaid({ 'id': $scope.ticket.ticketGUID }, $scope.ticket, function (success) {
Notification.success('Record updated successfully');
$scope.ticket = Ticket.getAdmin({ id: success.ticketGUID });
}, function (error) {
console.info(error);
});
});
};
$scope.payments.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withDOM('tr');
$scope.payments.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(0),
DTColumnDefBuilder.newColumnDef(1).withOption('width', '180'),
DTColumnDefBuilder.newColumnDef(2),
DTColumnDefBuilder.newColumnDef(3),
DTColumnDefBuilder.newColumnDef(4)
];
$scope.getPaymentData = function () {
$scope.payments.loading = true;
Payment.query({ id: $scope.ticket.ticketGUID }, function (result) {
$scope.payments.loading = false;
$scope.payments.data = result;
});
};
//#endregion
//#region Notes
$scope.addNote = function () {
Note.add({ id: $scope.ticket.ticketGUID }, '"' + $scope.notes.newNote + '"', function (successResponse) {
$scope.notes.data.push(successResponse);
$scope.notes.newNote = '';
Notification.success('Note added');
}, function (err) {
console.log(err);
});
};
$scope.getNotes = function () {
$scope.notes.loading = true;
$scope.notes.data = Note.query({ id: $scope.ticket.ticketGUID }, function (successResponse) {
$scope.notes.loading = false;
$scope.notes.loadError = '';
}, function (error) {
$scope.notes.loading = false;
$scope.notes.loadError = error.data;
});
};
//#endregion
//#region ELearning
$scope.learning.dtOptions = DTOptionsBuilder.newOptions()
.withPaginationType('full_numbers')
.withDOM('tr');
$scope.learning.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(0),
DTColumnDefBuilder.newColumnDef(1),
DTColumnDefBuilder.newColumnDef(2),
DTColumnDefBuilder.newColumnDef(3).notSortable()
];
$scope.getLearningData = function () {
$scope.learning.loading = true;
ELearning.query({ id: $scope.ticket.ticketGUID }, function (result) {
$scope.learning.loading = false;
$scope.learning.data = result;
});
};
$scope.markAsCompleted = function () {
ELearning.MarkAsCompleted({ id: $scope.ticket.ticketGUID }, function (successResponse) {
$scope.ticket = successResponse;
$scope.getHistory();
$scope.getLearningData();
Notification.success('Ticket has been marked as completed');
});
};
$scope.getLearningHistory = function (learningData) {
var dlg = dialogs.create('app/elearning/ResultDialog.html', 'learningDialogController', { data: learningData.onlineLearningResultId }, 'lg');
};
//#endregion
//#region History
$scope.getHistory = function () {
$scope.history.loading = true;
History.query({ id: $scope.ticket.ticketGUID }, function (successResponse) {
$scope.history.data = successResponse;
$scope.history.loading = false;
$scope.history.loadError = '';
}, function (error) {
$scope.history.loading = false;
$scope.history.loadError = error.data;
});
};
$scope.animateElementIn = function ($el) {
$el.removeClass('hidden');
$el.addClass('bounce-in');
};
// optional: not mandatory (uses angular-scroll-animate)
$scope.animateElementOut = function ($el) {
$el.addClass('hidden');
$el.removeClass('bounce-in');
};
//#endregion
//#region Correspondance
$scope.getCorrespondanceData = function () {
$scope.letters.loading = true;
Correspondance.query({ id: $scope.ticket.ticketGUID }, function (result) {
$scope.letters.loading = false;
$scope.letters.data = result;
});
};
$scope.resendLetter = function (letter) {
$scope.letters.laddaResendLetter = true;
Correspondance.resend({ id: letter.correspondanceID }, function (result) {
Notification.success('New ' + result.correspondanceType.toLowerCase() + ' ' + result.deliveryMethod.toLowerCase() + ' has been requested');
$scope.getCorrespondanceData();
$scope.letters.laddaResendLetter = false;
}, function (error) {
$scope.letters.laddaResendLetter = false;
});
};
$scope.cancelLetter = function (letter) {
$scope.letters.laddaCancelLetter = true;
Correspondance.cancelLetterRequest({ id: letter.correspondanceID }, function (result) {
Notification.success(result.correspondanceType.toLowerCase() + ' ' + result.deliveryMethod.toLowerCase() + ' has been cancelled');
$scope.getCorrespondanceData();
$scope.letters.laddaCancelLetter = false;
}, function (error) {
$scope.letters.laddaCancelLetter = false;
});
};
//#endregion
$scope.getPaymentData();
$scope.getNotes();
$scope.getLearningData();
$scope.getHistory();
$scope.getCorrespondanceData();}]);
Found the problem to be a third party module (angular-timeline) for animating the history

Resources