Angular testing stateprovider always returns null - angularjs

I'm using Karma to write angular tests: Here's my spec.js file:
'use strict';
var state;
var $rootScope, $state, $injector, CategoriesService;
describe('Categories', function() {
describe('Categories Manage', function() {
beforeEach(function() {
beforeEach(module('ui.router'));
module('CL.Categories', function($provide) {
$provide.value('CategoriesService', CategoriesService = {});
});
inject(function(_$rootScope_, _$state_, _$injector_, $templateCache, _CategoriesService_) {
$rootScope = _$rootScope_;
$state = _$state_;
$injector = _$injector_;
CategoriesService = _CategoriesService_;
// We need add the template entry into the templateCache if we ever
// specify a templateUrl
$templateCache.put('layout/dashboard.html', '');
$templateCache.put('layout/sidebar.html', '');
$templateCache.put('layout/footer.html', '');
$templateCache.put('layout/header.html', '');
$templateCache.put('categories/manage/index.html', '');
});
});
it('should respond to URL with query parameters', function() {
expect($state.href(state)).toEqual('#/categories/manage');
});
});
});
This is my config file :
(function (){
'use strict';
angular.module('CL.Categories')
.config(['$stateProvider', categoriesConfig]);
function categoriesConfig ($stateProvider){
$stateProvider
.state('dashboard.selectCategories', {
url: "/categories/select?criteria&referrer&index",
templateUrl: 'categories/select/index.html',
controller: 'SelectCategoriesController',
controllerAs: 'vm',
resolve: {
categoryRoot: ['CategoriesService', function(CategoriesService){
return CategoriesService.getRootCategories();
}]
}
})
.state('dashboard.manageCategories', {
url: "/categories/manage?active",
templateUrl: 'categories/manage/index.html',
controller: 'ManageCategoriesController',
controllerAs: 'vm',
resolve: {
workCategories: ['CategoriesService', function (CategoriesService) {
return CategoriesService.getCategoriesByOrganisationWithCertificates();
}]
}
});
}
})();
in my karma.config file I have the base path set to './'. the test expect($state.href(state)) always returns null

When you're making your mock call for state, try this instead to get the 'state':
state = $state.get('dashboard.selectCategories');
Then you can test pieces of the state declaration:
expect(state.url).toEqual('/categories/select?criteria&referrer&index');
expect(state.templateUrl).toEqual('categories/select/index.html');
And so on...

Related

Karma test failed when I inject a ui-router resolve in a controller

I've been trying to test my controller:
app.js
angular
.module('MyModule', [
'ui.router'
]);
angular
.module('MyModule')
.config(configFn);
configFn.$inject = ['$stateProvider'];
function configFn($stateProvider){
$stateProvider
.state('myState',{
url:'state',
views: {
'main' : {
templateUrl: 'src/views/view.html',
controller: 'MyCtrl',
controllerAs: 'ctrl',
resolve: {
DataResolve: ['MyService', function(MyService){
return MyService.getData();
}]
}
}
}
});
controller.js
angular
.module('MyModule')
.controller('MyCtrl', Controller);
Controller.$inject = ['DataResolve'];
/* #ngInject */
function Controller(DataResolve) {
var vm = this;
vm.data = DataResolve;
}
My spec
controller_spec.js
describe('Controller', function(){
beforeEach(module('MyModule'));
beforeEach(inject(function($controller){
this.myCtrl = $controller('MyCtrl');
}));
it('Controller should be defined', function() {
expect(this.myCtrl).toBeDefined();
});
});
But when the test runs, I get the following error:
Error: [$injector:unpr] Unknown provider: DataResolveProvider <- DataResolve <- MyCtrl
What I have been doing wrong?
In your beforeEach, add a reference to your service :
beforeEach(inject(function($controller, DataResolve){
this.DataResolve = DataResolve;
this.myCtrl = $controller('ParcelasController', {
DataResolve: this.DataResolve;
});
}));

i am trying to test my angular controller using jasmine. But i am getting Error: [$injector:modulerr]

following is the code from my sample angular project.
app.js code:
(function () {
'use strict';
var app = angular.module('actorsDetails', [
// Angular modules
'ngResource',
// 3rd Party Modules
'ui.bootstrap',
'ui.router'
]);
app.config(['$stateProvider', '$urlRouterProvider', configRoutes]);
function configRoutes($stateProvider, $urlRouterProvider) {
$stateProvider
.state('main', {
url: '/main',
templateUrl: 'app/home/home.html',
controller: 'HomeCtrl',
controllerAs: 'vm'
})
.state('form', {
url: '/form',
templateUrl: 'app/form/form.html',
controller: 'FormCtrl',
controllerAs: 'vm',
resolve: {
initialData: ['actorApi', function (actorApi) {
return actorApi.getActorsResource();
}]
}
})
.state('resource', {
url: '/resource',
templateUrl: 'app/resource/resource.html',
controller: 'ResourceCtrl',
controllerAs: 'vm',
resolve: {
initialData: ['actorApi', function (actorApi) {
return actorApi.getActorsResource();
}]
}
});
$urlRouterProvider.otherwise('/main');
}
app.run(['$state', function ($state) {
// Include $route to kick start the router.
}]);
})();
controller code:
(function () {
'use strict';
angular.module('actorsDetails').controller('HomeCtrl', HomeCtrl);
/* #ngInject */
function HomeCtrl($state) {
/* jshint validthis: true */
var vm = this;
vm.activate = activate;
vm.test = true;
vm.navigate = navigate;
activate();
function activate() {
}
function navigate() {
$state.go('form');
}
}
})();
**test.js**
describe('HomeCtrl', function() {
beforeEach(module('actorsDetails'));
beforeEach(inject(function ($rootScope, $controller) {
var scope = $rootScope.$new();
var HomeCtrl = $controller('HomeCtrl', {
$scope: scope
});
}));
it('should have a HomeCtrl controller', function() {
expect(true).toBeDefined();
});
});
there are the files I have included in my karma.config.js
I have added all the angularJS dependent files.
I have also added the controller file that i need to test
files: [
'src/lib/angular/angular.min.js',
'src/lib/angular-mocks/angular-mocks.js',
'src/lib/angular-resource/angular-resource.min.js',
'src/lib/angular-route/angular-route.min.js',
'src/app/app.js',
'src/app/home/home.controller.js',
'src/test/specs/*.js'
],
kindly pinpoint me, what is that I am doing wrong...
Mock out the $state object in your unit test.
var mockState = { go: function() {} };
var HomeCtrl = $controller('HomeCtrl', { $scope: scope, $state: mockState });
The $injector:modulerr is most likely related to your use of $state in your controller. Instead of mocking, you could try adding the library to your karma config, and loading the module in your unit test.
'src/lib/angular-ui-router/release/angular-ui-router.min.js'
beforeEach(module('ui.router'));

Error: Could not resolve 'app.history' from state ' '

I am writing unit test which is using $stateProvider(the code is shown below for both the code and its test file).While executing this, it is giving error- "Error: Could not resolve 'app.history' from state ''".
$stateProvider
.state('app',
{
url: "/app",
templateUrl: "pages/app/index.html",
controller: function($state) {
$state.go('app.history');
}})
.state('app.history',
{ url: "/history",
templateUrl: "pages/app/modules/History/partials/history.html"
})
Unit test code -
describe("Unit tests for config.jst", function() {
var $rootScope, $injector, $state;
beforeEach(module('ui.router'));
beforeEach(inject(function(_$rootScope_, _$state_, _$injector_, $templateCache) {
$rootScope = _$rootScope_;
$injector = _$injector_;
$state = _$state_;
$templateCache.put("pages/app/index.html", "");
$templateCache.put("pages/app/modules/History/partials/history.html", "");
}));
describe("states", function() {
var state = "app.history";
it("verify state configuration", function() {
//var config = $state.get(state);
$state.go(state);
$rootScope.$digest();
//console.log($state);
expect($state.current.name).to.be.equal(state);
});
});
});
You forgot to include application module.
Add
module('your_application_module_name');
below
beforeEach(module('ui.router'));

Loading controller on demand using AngularJS and RequireJS

I'm using Angular UI-router and trying to download/load controller when the routing changes. I used resolve and category, the data.data returns the js file content as string. I'm not sure to make the controller available to angular. Please help
My module.js contains below routing code
state("privacy", {
url: "/privacy",
controllerProvider: function ($stateParams) {
return "PrivacyController";
},
resolve: {
category: ['$http', '$stateParams', function ($http, $stateParams) {
return $http.get("js/privacy.js").then(function (data) {
return data.data;
});
} ]
},
templateUrl: localPath + "templates/privacy.html"
})
The below controller exist in "js/privacy.js"
socialinviter.controller("PrivacyController", function ($scope) {
$scope.me = "Hellow world";
});
I also tried with require js but I'm getting error "http://errors.angularjs.org/1.2.16/ng/areq?p0=PrivacyController&p1=not%20aNaNunction%2C%20got%20undefined"
resolve: {
deps: function ($q, $rootScope) {
var deferred = $q.defer(),
dependencies = ["js/privacy"];
require(dependencies, function () {
$rootScope.$apply(function () {
deferred.resolve();
});
deferred.resolve()
})
return deferred.promise;
}
}
I have resolved the issue and I thought the solution would be helpful for others
Step 1: On your config, include the parameter $controllerProvider
mytestapp.config(function ($stateProvider, $controllerProvider)
Step 2: telling angular to register the downloaded controller as controller, add the below inside the config
mytestapp.config(function ($stateProvider, $controllerProvider) {
mytestapp._controller = mytestapp.controller
mytestapp.controller = function (name, constructor){
$controllerProvider.register(name, constructor);
return (this);
}
......
Step 3: Add the resolve method as below
state("privacy", {
url: "/privacy",
controller: "PrivacyController",
resolve: {
deps : function ($q, $rootScope) {
var deferred = $q.defer();
require(["js/privacy"], function (tt) {
$rootScope.$apply(function () {
deferred.resolve();
});
deferred.resolve()
});
return deferred.promise;
}
},
templateUrl: "templates/privacy.html"
})

Load controller dynamically based on route group

Is it possible to load a controller, it's js file, and a template dynamically based on a route group? Psuedo code which doesn't work:
$routeProvider.when('/:plugin', function(plugin) {
templateUrl: 'plugins/' + plugin + '/index.html',
controller: plugin + 'Ctrl',
resolve: { /* Load the JS file, from 'plugins/' + plugin + '/controller.js' */ }
});
I've seen a lot of questions like this one but none that loads the js file/controller based on a route group.
I managed to solve it inspired by #calebboyd, http://ify.io/lazy-loading-in-angularjs/ and http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx
Using http://dustindiaz.com/scriptjs
app.js
app.config(function($controllerProvider, $compileProvider, $filterProvider, $provide) {
app.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
});
Then i register the "load controller by group" route.
$routeProvider.when('/:plugin', {
templateUrl: function(rd) {
return 'plugin/' + rd.plugin + '/index.html';
},
resolve: {
load: function($q, $route, $rootScope) {
var deferred = $q.defer();
var dependencies = [
'plugin/' + $route.current.params.plugin + '/controller.js'
];
$script(dependencies, function () {
$rootScope.$apply(function() {
deferred.resolve();
});
});
return deferred.promise;
}
}
});
controller.js
app.register.controller('MyPluginCtrl', function ($scope) {
...
});
index.html
<div ng-controller="MyPluginCtrl">
...
</div>
You can use RequireJS to do this. Something like:
$routeProvider.when('/:plugin',{
templateUrl: 'plugins/' + plugin + '/index.html',
controller: plugin + 'Ctrl',
resolve: {myCtrl: function($q){
var deferred = $q.defer();
require('myCtrlFile',function(){
deferred.resolve();
});
return deferred.promise;
}}
});
You will also need to register the controller dynamically. By exposing your providers in the app config.
app.config(function($controllerProvider,$compileProvider,$filterProvider,$provide){
app.register =
{
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
});
You controller file might then look like:
define(['app'],function(app){
app.register.controller('myCtrl',MyCtrlFunction);
});
This is just the general idea. I use a similar implementation to the one described here
I also use ui-router. I'm not certain if behavior is the same with ngRoute.
Here is some solution you can do for this code
$routeProvider.when('/:plugin', function(plugin) {
templateUrl: 'plugins/' + plugin + '/index.html',
controller: fun,
loadFrom:"assets/controller/myJsController"// this is our custom parameter we are passing to controller to identify the remote controller file.
});
We will create a parent function for all controller and will call all controller within this function as per defined in route configuration (in loadFrom key of route configuration).
function fun($scope, $http, $location, $timeout, $route) {
$timeout(function () {
var path = $route.current.loadForm;
$http.get("${pageContext.servletContext.contextPath}/resource/controller/" + path + ".js")
.then(function (rsp) {
eval(rsp.data);
});
});
};
in assets/controller/myJsController.js file the code will be as
(function($scope){
//the whole code for controller will be here.
$scope.message="working."
})($scope)
Only thing you have to remember that in parent function you have to use all dependencies.
Simplest way with active this with less amount of code
require.js config file.
require.config({
urlArgs: 'v=1.0',
baseUrl: '/'
});
app.config(['$controllerProvider', '$compileProvider', '$filterProvider', '$provide','$routeProvider',function($controllerProvider, $compileProvider, $filterProvider, $provide,$routeProvider) {
app.register = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service
};
// Resolver to load controller, service, directive
var resolveController = function(dependencies) {
return {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var defer = $q.defer();
require(dependencies, function () {
defer.resolve();
$rootScope.$apply();
});
return defer.promise;
}]
}
};
$routeProvider
.when("/home", {
templateUrl : "templates/home.html",
controller: 'HomeCtrl',
resolve: resolveController(['controller/HomeCtrl'])
})
.when("/ContactUs", {
templateUrl : "templates/ContactUs.html",
controller: 'ContactUsCtrl',
resolve: resolveController(['controller/ContactUsCtrl'])
})
.when("/About", {
templateUrl : "templates/About.html",
controller: 'AboutCtrl',
resolve: resolveController(['controller/AboutCtrl'])
});
$routeProvider.otherwise('/home');
}]);
Your controllers should look like this.
define(['app'],function(app){
app.register.controller('HomeCtrl',['$scope',function($scope){
// Controller code goes here
}]);
});

Resources