I'm probably missing something obvious here, but I'm out of ideas, so:
I have the following code:
app.run(function ($rootScope, $location, $anchorScroll, $stateParams, $timeout, $anchorScrollProvider) {
$rootScope.$on('$stateChangeStart',
function(newRoute, oldRoute) {
$timeout(function () {
$anchorScrollProvider.disableAutoScrolling();
$location.hash($stateParams.scrollTo);
$anchorScroll();
},
100);
});
});
I added the $anchorScrollProvider code today, and I'm getting the following error:
Error: [$injector:unpr] Unknown provider: anchorScrollProviderProvider <- anchorScrollProvider
Reading the documentation, it looks like $anchorScrollProvider is part of the base ng modules, which to me means the above should work, but why doesn't it?
The anchorScrollProvider can be injected in the config phase for calling the disableAutoScrolling function behavior.
anchorScrollProvider is part of the built-in ng-modules. Use $anchorScrollProvider to disable automatic scrolling whenever $location.hash() changes.
Some of the methods are related to the providers in configuration phase and should be invoked during the configuration of the app.
During the config phase, the providers have been registered but not run yet.
app.config(function ( $anchorScrollProvider) {
$anchorScrollProvider.disableAutoScrolling();
})
The rest of the code can be done in the run method:
app.run(function ($rootScope, $location, $anchorScroll, $stateParams, $timeout) {
$rootScope.$on('$stateChangeStart',
function(newRoute, oldRoute) {
$timeout(function () {
$location.hash($stateParams.scrollTo);
$anchorScroll();
},
100);
});
});
Related
I'm testing a directive ('planListing') that has a dependency on a service called 'planListingService'. This service has a dependency to another service called 'ajax' (don't shoot the messenger for the bad names).
I'm able to compile the directive, load its scope and get the controller WITH A CAVEAT. As of now I am being forced to mock both services 'planListingService' and 'ajax' otherwise I will get an error like this:
Error: [$injector:unpr] Unknown provider: ajaxProvider <- ajax <- planListingService
http://errors.angularjs.org/1.3.20/$injector/unpr?p0=ajaxProvider%20%3C-%20ajax%20%3C-%20planListingService
I thought that because I was mocking up the 'planListingService' that I wouldn't have to actually bother with any implementation nor any dependencies of this service. Am I expecting too much?
Here is the code in a nutshell:
planListing.js
angular.module('myApp')
.directive('planListing', planListing)
.controller('planListingCtrl', PlanListingCtrl);
function planListing() {
var varDirective = {
restrict: 'E',
controller: PlanListingCtrl,
controllerAs: 'vm',
templateUrl: "scripts/directives/planListing/planListing.html";
}
};
return varDirective;
}
PlanListingCtrl.$inject = ['planListingService'];
function PlanListingCtrl(planListingService) {
...
}
planListingService.js
angular.module('myApp')
.factory('planListingService', planListingService);
planListingService.$inject = ['$q', 'ajax'];
function planListingService($q, ajax) {
...
}
ajax.js
angular.module('myApp')
.factory('ajax', ['backend', '$browser', 'settings', '$http', '$log',
function (backend, $browser, settings, $http, $log) {
...
planListing.spec.js
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
module('myApp');
module('my.templates');
beforeEach(module(function ($provide){
// This seems to have no effect at all, why?
$provide.service('planListingService', function () {
this.getAllPricePlans=function(){};
});
// I don't get the error if I uncomment this:
// $provide.service('ajax', function ($q) {
// this.getAllPricePlans=function(){};
// });
}));
beforeEach(function() {
module('myApp');
module('my.templates');
});
beforeEach(angular.mock.inject(function (_$compile_,_$rootScope_,_$controller_){
$compile=_$compile_;
$rootScope = _$rootScope_;
$controller = _$controller_;
el = angular.element('<plan-listing></plan-listing>');
scope = $rootScope.$new();
$compile(el)(scope);
scope.$digest();
ctrl = el.controller('planListing');
scope = el.isolateScope() || el.scope();
vm = scope.vm;
}));
describe('testing compilation / linking', function (){
it('should have found directive and compiled template', function () {
expect(el).toBeDefined();
expect(el.html()).not.toEqual('');
expect(el.html()).toContain("plan-listing-section");
});
});
it('should have a defined controller',function(){
expect(ctrl).toBeDefined();
});
it('should have a defined scope',function(){
expect(ctrl).toBeDefined();
});
});
So why is that I need to mock up the 'ajax' service even though I am mocking up 'planListingService' which is the one calling the 'ajax' service?
Thanks!
I have been there... feels like bad start But i think your directive is depend on the service and you need to inject it in order to directive can work with this, Just by calling directive it doesn't mean that it's going to inject it in your test. It will look for it and if it's not injected it will give you error
you could do so before testing your directive
beforeEach(inject(function ($injector) {
yourService = $injector.get('yourService');
})
For documentation purposes, here is the answer (thanks #estus for noticing this):
Indeed the problem was related to the incorrect initialization of my modules. Instead of this:
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
module('myApp');
module('my.templates');
...
I should've done this:
describe('testing planListing.js',function(){
var el,ctrl,scope,vm;
var service;
beforeEach(module('myApp'));
beforeEach(module('my.templates'));
...
After that things started working again as expected.
This code works; the getData() function is invoked:
var app = angular.module('POC', []);
app.controller('POCCtrl', ['$scope','$timeout', function ($scope, $timeout) {
<snip>
$timeout(function () {
$scope.getData()
}, 250, $scope);
]);
But the code below , which references localStorageService, causes a native error in IE when I try to run and debug the page. The getData() function is never invoked. What am I missing? Does the local storage service have to be included in the module too?
var app = angular.module('POC', []);
app.controller('POCCtrl', ['$scope','$timeout', 'localStorageService',
function ($scope, $timeout, localStorageService) {
$timeout(function () {
$scope.getData()
}, 250, $scope);
]);
use
app.controller('POCCtrl',
function ($scope, $timeout, localStorageService) {
);
I don't know why but the controller just doesn't take localStorageService in the way you are using, I had the same error today. See my question (I am myself looking for an explanation though)
Error in angular-js while using angular-local-storage
I'm new to angular-ui-router and I've been trying to do some unit testing for a basic authentication, its working fine until I hit a maximum call stack error.
I've narrowed the error down to the $state.go call in the app.run section.
I remove this and the test works. However it breaks my app
What can I do to resolve this? so I can test this section and make it work as well?
Why does this work normally but causes a test error?
Error:
RangeError: Maximum call stack size exceeded
at Scope.$broadcast (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular/angular.js:12876:15)
at Object.transitionTo (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2584:24)
at Object.go (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2454:21)
at /Users/paulrobinson/Workspace/contactCachePOC/dev/js/core.js:9:5889
at Scope.$broadcast (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular/angular.js:12874:28)
at Object.transitionTo (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2584:24)
at Object.go (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2454:21)
at /Users/paulrobinson/Workspace/contactCachePOC/dev/js/core.js:9:5889
at Scope.$broadcast (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular/angular.js:12874:28)
at Object.transitionTo (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2584:24)
Code:
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('/');
var access = routingConfig.roles;
$stateProvider
.state('start', {
url : '/',
templateUrl : 'partials/decide.html',
controller : 'decideController',
data: {
access: access.anon
}
});
}]);
app.run(['$rootScope', '$state', 'AuthService', '$log', '$location',
function ($rootScope, $state, AuthService, $log, $location) {
$rootScope.$on("$stateChangeStart",
function (event, toState, toParams, fromState, fromParams) {
if (!AuthService.isAuthorized(toState.data.access)) {
event.preventDefault();
$rootScope.error = null;
//STATE.GO is causing the error
$state.go('start');
//$location.path('/#/');
return;
}
});
}]);
describe('Test as an anonymous user', function () {
var $templateCache, $state, $stateParams, $rootScope, $httpBackend,
AuthService,, $location;
var roles = {
anon: { id: 0, value: 'Public'},
user: { id: 1, value: 'User'}
};
beforeEach(module('app'));
beforeEach(inject(function(_$templateCache_, _$state_, _$stateParams_, _$rootScope_, _$httpBackend_,
_AuthService_, _sessionService_, _$location_) {
$templateCache = _$templateCache_;
$state = _$state_;
$stateParams = _$stateParams_;
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
AuthService = _AuthService_;
$location = _$location_;
//Fake it and say we're not authorized.
spyOn(AuthService, "isAuthorized").andCallFake(function (state){
return false;
});
}));
describe('View page.', function () {
beforeEach(function () {
$state.go('start', { });
$rootScope.$apply();
});
it('Should view page.', function () {
expect($state.current.name).toEqual('start');
});
});
});
Maximum call stack exceeded suggests that something is happening recursively. I'd guess that the state that you are going to "start" is also not authorised, so you get redirected to "start" again, and so on.
You need to make your AuthService.IsAuthorized return true at some point
Ok so it appears to be a bug with ui-router.
Here is a solution
https://github.com/angular-ui/ui-router/issues/178#issuecomment-49156829
$state.go(toState.name, toParams, {notify: false}).then(function() {
$rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});
The above code will deal with the problem without breaking the browser or unit tests.
Just beware of https://github.com/angular-ui/ui-router/issues/1348 when using this fix.
Use $state.go("main", {}, {notify:false}); for notify to $stateChangeStart event.
$state.go("main", {}, {notify:false});
This code gives me Error: [$injector:unpr] Unknown provider: $scope, $locationProvider <- $scope, $location.
var app = angular.module('myApp.controllers', []);
app.controller('Signup', ['$scope, $location', function($scope, $location) {
$scope.checkEmailValid = function(){
//TODO make a decision about whether to go somewhere, if true do this:
$location.path('/view2');
};
}]);
Am I missing something about how to inject the location service?
I haven't configured $locationProvider, but doing so doesn't seem to help.
You forgot the quotes around $scope and $location :
var app = angular.module('myApp.controllers', []);
app.controller('Signup', ['$scope', '$location', function($scope, $location) {
$scope.checkEmailValid = function(){
//TODO make a decision about whether to go somewhere, if true do this:
$location.path('/view2');
};
}]);
This should to the trick !
Try simpler form (without array form - probably missing quote is your problem):
app.controller('Signup', function($scope, $location) {
$scope.checkEmailValid = function(){
//TODO make a decision about whether to go somewhere, if true do this:
$location.path('/view2');
};
});
Minification can be solved in build time using ngMin and this form is less error prone and more readable
within a controller i have a function which uses $state.transitionTo to "redirect" to another state.
now i am stuck in testing this function, i get always the error Error: No such state 'state-two'. how can i test this? it its totally clear to me that the controller does not know anything about the other states, but how can i mock this state?
some code:
angular.module( 'mymodule.state-one', [
'ui.state'
])
.config(function config($stateProvider) {
$stateProvider.state('state-one', {
url: '/state-one',
views: {
'main': {
controller: 'MyCtrl',
templateUrl: 'mytemplate.tpl.html'
}
}
});
})
.controller('MyCtrl',
function ($scope, $state) {
$scope.testVar = false;
$scope.myFunc = function () {
$scope.testVar = true;
$state.transitionTo('state-two');
};
}
);
describe('- mymodule.state-one', function () {
var MyCtrl, scope
beforeEach(module('mymodule.state-one'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
MyCtrl = $controller('MyCtrl', {
$scope: scope
});
}));
describe('- myFunc function', function () {
it('- should be a function', function () {
expect(typeof scope.myFunc).toBe('function');
});
it('- should test scope.testVar to true', function () {
scope.myFunc();
expect(scope.testVar).toBe(true);
expect(scope.testVar).not.toBe(false);
});
});
});
Disclaimer: I haven't done this myself, so I totally don't know if it will work and is what your are after.
From the top of my head, two solutions come to my mind.
1.) In your tests pre configure the $stateProvider to return a mocked state for the state-two That's also what the ui-router project itself does to test state transitions.
See: https://github.com/angular-ui/ui-router/blob/04d02d087b31091868c7fd64a33e3dfc1422d485/test/stateSpec.js#L29-L42
2.) catch and parse the exception and interpret it as fulfilled test if tries to get to state-two
The second approach seems very hackish, so I would vote for the first.
However, chances are that I totally got you wrong and should probably get some rest.
Solution code:
beforeEach(module(function ($stateProvider) {
$stateProvider.state('state-two', { url: '/' });
}));
I recently asked this question as a github issue and it was answered very helpfully.
https://github.com/angular-ui/ui-router/issues/537
You should do a $rootScope.$apply() and then be able to test. Note that by default if you use templateUrl you will get an "unexpected GET request" for the view, but you can resolve this by including your templates into your test.
'use strict';
describe('Controller: CourseCtrl', function () {
// load the controller's module
beforeEach(module('myApp'));
// load controller widgets/views/partials
var views = [
'views/course.html',
'views/main.html'
];
views.forEach(function(view) {
beforeEach(module(view));
});
var CourseCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
CourseCtrl = $controller('CourseCtrl', {
$scope: scope
});
}));
it('should should transition to main.course', inject(function ($state, $rootScope) {
$state.transitionTo('main.course');
$rootScope.$apply();
expect($state.current.name).toBe('main.course');
}));
});
Also if you want to expect on that the transition was made like so
expect(state.current.name).toEqual('state-two')
then you need to scope.$apply before the expect() for it to work