Angular app test with Karma - angularjs

I'm new to Jasmine tests with Karma. I'm getting following error while executing the tests.
Here are my files.
message.service.js // Service file
(function() {
'use strict';
angular.module("app").factory('MessageService', [MessageService]);
function MessageService() {
var service = {};
//var gui = require('nw.gui');
service.alert = function() {
//gui.Window.open('/');
//$window.open('/message');
return 'hello';
};
return service;
};
})(); // IIFE
message.service.spec.js // Service test file
describe('Message Service', function() {
var messageService;
beforeEach(angular.mock.module('app'));
beforeEach(inject(function(_MessageService_) {
messageService = _MessageService_;
}));
it('should exist', function() {
expect(messageService).toBeDefined();
});
it('should return hello', function() {
expect(messageService.alert()).toEqual('hello');
});
});
app.module.js // Main app file
(function() {
'use strict';
angular.module("app", ['ngRoute', 'ui.bootstrap', 'ui.router']);
/*angular.module("app").run(['$rootScope', 'StartupService', function($rootScope, startupService){
startupService.init($rootScope);
}]);*/
})(); // IIFE
If I change following line in message.service.js
angular.module("app").factory('MessageService', [MessageService]);
to
angular.module("app",[]).factory('MessageService', [MessageService]);
Then tests work fine but application will not work(due to re initiation of module I guess). How can I make my app to work for both tests and normal app execution? Appreciate your help

It was my mistake. I haven't included the js file in Karma config related to the ui.bootstrap module. After that it worked fine.
For anyone who gets such error check whether you have included all the dependency files on Karma config, what you have defined in main app.
ex: assume your main app file is like below.
angular.module("app", ['ngRoute', 'ui.bootstrap', 'ui.router']);
Then your Karma config file should look like this
files: [
'assets/lib/angular/angular.min.js',
'assets/lib/angular-ui-router/angular-ui-router.min.js',
'assets/lib/angular-bootstrap/ui-bootstrap-tpls-1.3.2.min.js'
],
Thanks for your help.

Related

Angular test failing to instantiate module with Jasmine and Karma

I have setted up a test environment with Angular 1.6.6, ng-mock 1.6.6, Jasmine and Karma. But even with the easiest test I'm getting a [$injector:modulerr] when trying to inject the deloreanApp module
Failed to instantiate module deloreanApp due to: Error:
[$injector:nomod]
Theoretically there aren't tipo errors and Angular and ng-mock versions matches.
My files are:
app.js
(function () {
"use strict";
// initialize Angular
angular.module('deloreanApp', ['deloreanApp.controllers', 'deloreanApp.services']);
angular.module('deloreanApp.controllers', []);
angular.module('deloreanApp.services', []);
})();
controllers.js
(function () {
"use strict";
function deloreanController($scope){
$scope.sum = function(a,b){
return a + b;
}
}
angular.module('deloreanApp.controllers', []).controller('DeloreanController', ['$scope', deloreanController] );
})();
DeloreanController.test.js
describe('calculator', function () {
beforeEach(module('deloreanApp'));
var $controller;
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('sum', function () {
it('1 + 2 should equal 3', function () {
var result = 3;
expect(result).toBe(3);
});
});
});
And part of my karma.conf.js file:
// list of files / patterns to load in the browser
files: [
'lib/angular.min.js',
'lib/angular-mocks.js',
'js/app.js',
'js/controllers.js',
'tests/DeloreanController.test.js'
],
Try this,
(function () {
"use strict";
angular.module('deloreanApp.controllers', []);
angular.module('deloreanApp.services', []);
// initialize Angular
angular.module('deloreanApp', ['deloreanApp.controllers', 'deloreanApp.services']);
})();

Angular Unit Testing Factory Injection

I've seen this answered but none of the solutions are working for me. I'm getting an error Error: [ng:areq] Argument 'fn' is not a function, got undefined which is causing my test to fail. What's the correct way to inject a factory into a test spec that isn't a stubbed factory.
login module
angular.module('loginModule', ['ui.bootstrap', 'permission', 'ui.router', 'coreModule', 'utilsModule']);
login controller
(function(){
'use strict';
angular.module('loginModule')
.controller('loginController', login);
login.$inject = [
'$log',
'$uibModal',
'$rootScope',
'storageFactory',
'loginFactory',
'$state',
'RoleStore',
'PermissionStore',
'vsmsCoreFactory'
];
function login($log, $uibModal, $rootScope, storageFactory, loginFactory, $state, RoleStore, PermissionStore, vsmsCoreFactory) {
/* jshint validthis: true */
var vm = this;
vm.loginUser = loginUser;
vm.forgotPassword = forgotPassword;
vm.errorCode = null;
PermissionStore.clearStore();
vsmsCoreFactory.getHashFunction()
.then(function(response) {
if(response) {
storageFactory.setHashFunction(response); // unknown provider
}
});
function loginUser() {
...
login controller spec
describe('loginController', function() {
var $controller;
beforeEach(module('loginModule'));
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController');
expect(loginController.loginUser).toBeDefined();
});
});
});
unit test files
'use strict';
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var wiredep = require('wiredep');
var paths = gulp.paths;
function runTests (singleRun, done) {
var bowerDeps = wiredep({
directory: 'bower_components',
exclude: ['bootstrap-sass-official'],
dependencies: true,
devDependencies: true
});
var testFiles = bowerDeps.js.concat([
'./src/components/scripts/ui-bootstrap-custom-tpls-2.1.3.js',
'./src/app/index.js',
'./src/{app,components}/**/*.module.js',
'./src/{app,components}/**/*.factory.js',
'./src/{app,components}/**/*.controller.js',
'./src/{app,components}/**/*.spec.js'
]);
gulp.src(testFiles)
.pipe($.karma({
configFile: 'karma.conf.js',
action: (singleRun)? 'run': 'watch'
}))
.on('error', function (err) {
// Make sure failed tests cause gulp to exit non-zero
throw err;
});
}
gulp.task('test', function (done) { runTests(true /* singleRun */, done) });
gulp.task('test:auto', function (done) { runTests(false /* singleRun */, done) });
app
'use strict';
angular.module('fotaAdminPortal',
[
'ngAnimate',
'ngCookies',
'ngTouch',
'ngSanitize',
'ngResource',
'ui.bootstrap',
'ui.router',
'ui.router.stateHelper',
'pascalprecht.translate',
'utilsModule',
'loginModule',
...
])
log
Chrome 54.0.2840 (Mac OS X 10.11.6) loginController vm.loginUser should be defined FAILED
Error: [$injector:unpr] Unknown provider: storageFactoryProvider <- storageFactory <- loginController
The fotaAdminPortal module isn't loaded for the test so vsmsCoreFactory never gets registered.
Also there's no need to manually pass services into a controller as the injector will automatically do this when you create it. Passing services manually is useful when you need to pass something that isn't a globally registered service ie $scope or mock a service for a single controller instance.
Atm the mocks for the factories are just an undefined variable which will cause errors when the logic depending on them tries to call the methods that don't exist. You'll need to properly stub any methods on these mocks. But leaving those aside for the moment your test should be something like:
describe('loginController', function() {
var $controller;
beforeEach(module('fotaAdminPortal'));
beforeEach(inject(function(_$controller_){
$controller = _$controller_;
}));
describe('vm.loginUser', function() {
it('should be defined', function() {
var loginController = $controller('loginController');
expect(loginController.loginUser).toBeDefined();
});
});
});
If you want to load loginModule in isolation then you need to extract vsmsCoreFactory into another module and add that as a dependency:
angular.module('vsmsCore', [])
.factory('vsmsCoreFactory', function($http, env, $log, storageFactory) {
});
angular.module('loginModule', ['ui.bootstrap', 'permission', 'ui.router', 'vsmsCore']);
Then you can do beforeEach(module('loginModule')) instead

AngularJS - Minifying modules with gulp

I am trying to set up a structure so I can minify/uglify all of my angular code.
I tried to copy the angular module structure from the Angle Bootstrap Theme
It works fine when all the unmified js files are loaded but when I try to minify all it ends up not finding the modules.
I am getting this error: [$injector:nomod] Module 'app.core' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
This is a MVC 5.2.3 project and the structure of the angular files are like this:
Scripts/angular/app.module.js
Scripts/angular/modules/core/core.config.js
Scripts/angular/modules/core/core.module.js
Scripts/angular/modules/module1/module1.controller.js
Scripts/angular/modules/module1/module1.module.js
So app.module.js looks like this:
(function() {
'use strict';
angular
.module('myApp', [
'app.core',
'app.module1',
...
]);
})();
core.config.js:
(function () {
'use strict';
angular
.module('app.core')
.config(coreConfig);
coreConfig.$inject = ['$controllerProvider', '$compileProvider', '$filterProvider', '$provide', '$animateProvider'];
function coreConfig($controllerProvider, $compileProvider, $filterProvider, $provide, $animateProvider) {
var core = angular.module('app.core');
core.controller = $controllerProvider.register;
core.directive = $compileProvider.directive;
core.filter = $filterProvider.register;
core.factory = $provide.factory;
core.service = $provide.service;
core.constant = $provide.constant;
core.value = $provide.value;
core.config(function (paginationTemplateProvider) {
paginationTemplateProvider.setPath(window.rootUrl + 'scripts/plugins/angular-pagination/dirPagination.tpl.html');
});
$animateProvider.classNameFilter(/^((?!(ng-no-animation)).)*$/);
}
})();
core.module.js:
(function() {
'use strict';
angular
.module('app.core', [
'ngSanitize',
'ngRoute',
'ngAnimate',
'ngResource',
'ngStorage',
'ui.bootstrap'
]);
})();
And all the modules have this structure:
module1.controller.js:
(function () {
'use strict';
angular
.module('app.note')
.controller('module1', module1);
module1.$inject = ['$scope', '$localStorage'];
function module1($scope, $localStorage) {
.....
};
})();
and the module1.module.js:
(function () {
'use strict';
angular
.module('app.module1', []);
})();
gulpfile.js
/// <binding Clean='clean' />
"use strict";
var gulp = require("gulp"),
rimraf = require("rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
uglify = require("gulp-uglify");
var paths = {
webroot: "./"
};
paths.js = paths.webroot + "scripts/angular/**/*.js";
paths.minJs = paths.webroot + "scripts/angular/**/*.min.js";
..
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
..
probably is an order problem.
You have a good naming conventio so you can order your input stream taking the *.module.js files first.
you have to include
var order = require("gulp-order");
than after the gulp.src you have to order your input the risult should be likethis
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(order([
'**/*.module.js',
'**/*.js'
]))
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
as you can see the order take an array of pattern.
Maybe the pattern are not perfect for your case but it should work.
hope this helps

how to inject factory into another factory in Angular

I have two modules and factories in both, and I need to implement factory from the first module into another module.
angular.module('APIs', [])
.value ("myValue" , "12345")
.factory('apiUrl',['config','url',apiUrl])
function apiUrl(config,url){
}
angular.module('users.service', ['APIs'])
.factory('userService',['myValue',userService])
function userService(apiUrl,myValue){
//login function
function login(){
console.log('myValue',myValue)
console.log('loginUrl',apiUrl)
}
return {
login:login
}
}
notice: no problem when I inject myValue, but the problem in APIs Factory
and my log:
Error: [$injector:unpr] http://errors.angularjs.org/1.5.0/$injector/unpr?p0=urlProvider%20%3C-%20url%20%3C-%20apiUrl%20%3C-%20userService
at Error (native)
and sorry for my English.
I would do it like this:
If you create factory use angular.module('app').factory(). If you create service use angular.module('app').service()
Always try to have same module name. It is easier later, when you have big application, because of dependency injection.
Try to keep files separately, and concatenate it later, for example using gulp gulp-concat
Try to keep all you configuration in app.js file and, when you concatenating file, remember, this file should be on top.
I would keep values and constants in app.js file or would create new file like factory or service and include it, same as I injected it below.
app.js
(function () {
'use strict';
var app = angular.module('app', [
// other module
]);
app.value('myValue', '12345');
app.constant('myConst', 'some_constant');
app.config(['$interpolateProvider', function ($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
}]);
}());
factory.js
(function () {
'use strict';
angular
.module('app')
.factory('apiUrlFactory', apiUrlFactory);
apiUrlFactory.$inject = [];
function apiUrlFactory() {
var self = this;
self.url = 'some_url';
return self;
}
}());
service.js
(function () {
'use strict';
angular
.module('app')
.service('userService', userService);
userService.$inject = ['apiUrlFactory', 'myValue'];
function userService(apiUrlFactory, myValue) {
var self = this;
self.login = function () {
console.log('myValue', myValue);
console.log('loginUrl', apiUrlFactory.url);
};
return self;
}
}());
If you have more questions, do not hesitate to contact with me. Also try angular style guide https://github.com/johnpapa/angular-styleguide It will help you a lot.

Issues wiring up a new Angular controller module

I wonder if someone could assist me in figuring out how to wire up my new angular service module.
I keep getting the same Javascript error :
Error: [$injector] Unknown provider: gridHierarchyServiceProvider" <-- gridHierarchyService
My new services/gridHierarchyService.js file :
(function () {
'use strict';
var controllerId = 'gridHierarchyService';
angular.module('app').controller(controllerId, ['common', gridHierarchyService]);
function gridHierarchyService(common) {
var service = {
getDataSourceSchema: getDataSourceSchema
};
return service;
function getDataSourceSchema() {
var i = 1;
// TESTING...
}
}
});
and in my dashboard.js file, I attempt to inject this new service module:
(function () {
'use strict';
var controllerId = 'dashboard';
angular.module('app').controller(controllerId, ['common', 'datacontext', 'gridHierarchyService', dashboard]);
function dashboard(common, datacontext, gridHierarchyService) {
...
}
and of course I'm loading it up in my index.html:
<!-- app Services -->
<script src="app/services/datacontext.js"></script>
<script src="app/services/gridHierarchyService.js"></script>
<script src="app/services/directives.js"></script>
However, I'm clearing missing something here.
I also tried mimicking my datacontext.js service, using the .factory() call:
angular.module('app').factory(serviceI,d [...]);
but it doesn't work.
========== UPDATE !!! ============
I'm happy to say it was a very easy fix, and thanks to the community here !
WORKING SERVICE :
(function () {
'use strict';
var serviceId = 'gridHierarchyService';
angular.module('app').factory(serviceId, ['common', gridHierarchyService]);
function gridHierarchyService(common) {
var service = {
getDataSourceSchema: getDataSourceSchema
};
return service;
function getDataSourceSchema() {
var i = 1;
i = 2;
}
}
})();
I see that you are defining your controllers inside anonymous functions but that you never execute those anonymous functions.
Your code:
(function(){
//declarations
alert('You will never see me because I do not execute');
});
Fixed code (notice the parens at the end):
(function(){
//declarations
alert('You can see me!');
})();
See the above code run here: http://jsfiddle.net/wilsonjonash/cDBc7/
See more about the module pattern here:
http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
Also, you were right to try factory for a service. To make your gridHierarchyService into a service (rather than a controller), just change controller to factory or service in
var controllerId = 'gridHierarchyService';
angular.module('app').controller(controllerId, ['common', gridHierarchyService]);
Hope that does the trick for you.

Resources