Resolve dependencies not available in Jasmine test - angularjs

I am currently setting up a resolve property on one of my routes in my Angular app. I am using ngRoute. Also, I inject a service called Session into my resolve function.
My route looks like this:
$routeProvider.when('/projects', {
templateUrl: 'views/projects/index.html',
controller: 'ProjectsCtrl',
resolve: {
beforeAction: function (Session) { // <-- here's the injection
// this logs an object in the browser,
// but in my test it logs as undefined
console.log('Session', Session);
}
}
});
In my browser, this logs Session, Object {} to my console, as expected.
However, when I run my tests, the same line prints Session, undefined to my console.
My test looks like this:
beforeEach(module('visibilityApp'));
var route;
describe('/projects', function () {
beforeEach(inject(function ($route) {
route = $route;
}));
it('checks if the user is logged in', function () {
// Here I just invoke the function that's assigned to the
// route's resolve property, but Session then seems
// to be undefined.
route.routes['/projects'].resolve.beforeAction();
// then more of the test...
});
});
I've already found out that it doesn't really matter what I inject into the resolve function. If I inject $location and log that, it's the same spiel: it works in my browser, but is undefined when I run it as a test.
My tests on Jasmine and Karma. The app was generated with Yeoman.
Why are the resolve dependencies undefined in my test? Is there some additional setup my tests need?

I guess this was one of those "I need to step away from it for an hour and come back to it" situations. It turns out that I have to inject the Session service myself if I invoke the resolve function manually.
So instead of
route.routes['/projects'].resolve.beforeAction();
I need to pass in the Session
route.routes['/projects'].resolve.beforeAction(Session);
Otherwise, the Session parameter of obviously going to be undefined. To do that, I injected the Session service into my test as such:
beforeEach(module('visibilityApp'));
var route,
Session;
describe('/projects', function () {
beforeEach(inject(function ($route, _Session_) {
route = $route;
Session = _Session_;
}));
it('checks if the user is logged in', function () {
route.routes['/projects'].resolve.beforeAction(Session);
// then more of the test...
});
});

Related

Redirect to 404 if unauthorized angular

I am using the rootscope event in run function to check for entitlements like so
$rootScope.$on('$routeChangeStart', function() {
$http.get('auth/urlentitlementcheck').then(function(response) {
if (!response) {
$state.go('app.unauth');
}
});
});
But the problem is my controller functions are still getting called before this redirect happens.Is there another way to check for entitlement so that is happens before anything is called or loaded or am i doing something wrong?

Is there a way to see if the angular mock modules are loaded?

I have the following code in my spec file
beforeEach(function () {
module('app');
inject(function ($injector) {
user = $injector.get('app.user');
});
});
user is undefined, and isn't being injected. So I want to make sure that the app module actually loaded.
If the module is not loaded, you get $injector:nomod error. If the module is loaded but the service cannot be found, you get $injector:unpr error. It is as easy as that. There is always a breadcrumb trail, no need to probe Angular to know if it fails silently or not.
Just make sure you're using the right module name. You can use beforeEach to load your module. Also, with $injector you can get an instance of your service or controller you're trying to test:
'use strict';
describe('MyControllerName', function () {
var MyControllerName;
beforeEach(module('myAppMomduleName'));
beforeEach(inject(function ($injector) {
MyControllerName = $injector.get('MyControllerName');
}));
it('should create an instance of the controller', function () {
expect(MyControllerName).toBeDefined();
});
});

Mock service injected into Angular module run block

In my module.run block it is calling a method on a service I have made. When running my tests I want it to reference a mock service instead of the real one which is making http requests. I am currently trying to test a controller, not the actual run block itself - how can I inject the mock service into the run function? I have tried using $provide.factory but it doesn't seem to do anything and is still loading the service as normal.
I am using Jasmine to write my tests.
app.js
angular.module("app")
.run(function(MyService) {
MyService.log("starting app");
});
test.js
describe("MyController", function() {
beforeEach(function() {
module(function ($provide) {
$provide.factory("MyService", { log: function(){} });
});
});
// I want module 'app' to execute its run function using injected value for MyService
beforeEach(module("app"));
beforeEach(inject(function($controller, $rootScope) {
MyController = $controller("MyController", { $scope: $rootScope.$new() });
}));
...........
});
In this case is important order.
You need load your app first
beforeEach(module("app"));
and then overwrite MyService definition.
beforeEach(
module({
"MyService": {
log: function(message) {
console.log("MyFakeService called: " + message);
}
}
})
);
Otherwise app service implementation is last registred and used.
working example is here - look to the console http://plnkr.co/edit/BYQpbY?p=preview

Unavailable Module Error: Overriding an AngularJS Factory with Protractor for E2E testing

I am trying to mock a factory within one of my angularjs modules. The full angularjs application is
angular.module('administration', ['administrationServices'])
The dependency:
var module = angular.module('administrationServices')
contains a factory service:
module.factory('Example', [function(){
return{
list:function(){
return ['This is real']
}
}
}])
This is the service I am attempting to override in my protractor e2e test. The actual test looks something like this:
describe('Example page', function(){
beforeEach(function() {
var mock = function(){
// get the module and provide the mock
var module = angular.module('administrationServices').config(['$provide', function($provide){
$provide.factory('Example',[function(){
return {
list: function(){
return ['This is a Mock Test']
}
}
}])
}])
}
// override the mock service
browser.addMockModule('administrationServices', mock)
})
it('should go to the page and see mock text', function() {
// code that goes to page and checks if the service works
// ...
})
})
The issue I'm having occurs when I $ protractor conf.js, I get the error:
Error while running module script administrationServices: [$injector:nomod] http://errors.angularjs.org/1.3.4/$injector/nomod?p0=administrationServices
This is where I'm confused. Every blog post about protractor's addMockModule uses similar syntax it seems. There are other factory services in administrationServices and those seem to get overwritten because the app can't open to the page due to those services (user and group services) not working.
Anyways, if somebody has seen this and can help direct me in the right direction, that would help; I am fairly new to mock services, protractor and e2e testing.
I think the problem is that your mock function does not return anything. It doesn't share the new module outside the function scope.
var mock = function(){
// get the module and provide the mock
return angular.module('administrationServices').config(['$provide', function($provide){
$provide.factory('Example',[function(){
return {
list: function(){
return ['This is a Mock Test'];
}
}
}])
}])
};
// override the mock service
browser.addMockModule('administrationServices', mock)
Just make it return the new module and it should be fine.

AngularJS: Custom Providers, undefined functions?

I have a provider like so:
angular.module('myApp').provider('test', function(){
this.$get = function($http){
return {
test2: function(){
}
}
};
});
I then use the provider in my app.config:
app.config(function($urlRouterProvider, $stateProvider, testProvider){
testProvider.test2();
$urlRouterProvider.otherwise('/home');
$stateProvider.state('home', {url: '/home', templateUrl: 'template/guide.html'})
.state('cost', {url:'/cost', templateUrl:'template/cost.html'})
});
Im trying to use a provider to get all my pages from a database and return them to the stateProvider... (Which I cant do in app.config because I cant inject the $http service)
This is the error I'm getting:
Failed to instantiate module myApp due to: TypeError: undefined is
not a function
You are getting the error, because yu are trying to call test2() on the provider (TestProvider).
test2() is not a method of the provider, but of the provided service (and thus is only accessible at "runtime", not during config).
If I understand correctly what you are trying to do, here is a possible approach:
Create a DelayedRoutes service (using provider()), which has a method for fetching the routes from the server and then registering them with the routing module (be it ngRoute or ui.router or whatever).
This configRoutes() methods needs to be invoked at runtime (so it has access to $http etc).
It also needs access to the routing module's provider (e.g. $routeProvider, $stateProvider etc), which are only available during the configuration time.
Your provider will have a method (setRouteProvider()), which get's executed during config and stores the $whateverProvider for later use (i.e. when the configRoutes() gets executed at runtime).
Here is a sample implementation (using ngRoute for simplicity). You can modify it according to your needs:
app.provider('DelayedRoutes', function () {
var routeProvider;
// This will be called during `config`
this.setRouteProvider = function (rp) {
routeProvider = rp;
};
// This will return the actual service
this.$get = function ($http, $route) {
// This will be called at runtime, to fetch the routes
// from the server and configure client-side routing
function configRoutes() {
return $http.get('/gimme/ma/routes').then(
function onResponse(response) {
var routes = response.data.routes;
angular.forEach(routes, function (config, path) {
if (path === 'otherwise') {
routeProvider.otherwise(config);
} else {
routeProvider.when(path, config);
}
});
$route.reload();
},
function onError(error) {
// Handle the error...
}
);
}
return {
configRoutes: configRoutes
};
};
});
// During `config`, store `$routeProvider` for later use
app.config(function ($routeProvider, DelayedRoutesProvider) {
DelayedRoutesProvider.setRouteProvider($routeProvider);
});
// At runtime, config the routes
app.run(function (DelayedRoutes) {
DelayedRoutes.configRoutes();
});
See, also, this short demo.

Resources