I am trying to implement front-end unit-tests on an Angular project which uses a server-side templating engine to render an index.ejs or index.dust. This file also includes a global variable var cdn = "http://s3.amazon.com"; which is used throughout the angular app.
When I runstart karma karma.config.js, I receive ReferenceError: cdn is not defined: app.js L20.
The cdn var is available when I run my app with Node, but when I try to run unit-tests on my Angular Ctrls, I receive the error above.
I tried to use the karma-ejs-preprocessor module (here: https://www.npmjs.com/package/karma-ejs-preprocessor) to instantiatecdn in my tests, but I get a new error: No provider for "framework: jasmine".
index.ejs
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="utf-8">
<title>Test</title>
<script src="./bower_components/angular/angular.js"></script>
<script src="./bower_components/angular-ui-router/release/angular-ui-router.js"></script>
<script src="./bower_components/angular-mocks/angular-mocks.js"></script>
<script src="./bower_components/angular-resource/angular-resource.js"></script>
<script src="./app.js"></script>
</head>
<body>
<h1>Angular Unit Testing</h1>
<script>
var cdn = "./";
</script>
<main>
<div ui-view></div>
</main>
</body>
</html>
karma.conf.js
// Karma configuration
// Generated on Sat Mar 12 2016 02:53:19 GMT-0800 (Pacific Standard Time)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'client/src/bower_components/angular/angular.js',
'client/src/bower_components/angular-ui-router/release/angular-ui-router.js',
'client/src/bower_components/angular-mocks/angular-mocks.js',
'client/src/bower_components/angular-resource/angular-resource.js',
'client/src/app.js',
'tests/test.js'
],
// list of files to exclude
exclude: [
],
plugins: [
'karma-qunit',
'karma-ejs-preprocessor',
'karma-jasmine',
'karma-chrome-launcher'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'server/_views/index.ejs': ['ejs']
},
ejsOptions: {
parentPath: './server/_views/index.ejs'
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
test.js
describe('State1Ctrl', function () {
var $rootScope,
$scope,
controller;
beforeEach(function () {
module('app', 'ui.router');
inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
controller = $injector.get('$controller')('State1Ctrl', { $scope: $scope } );
});
});
describe('Init', function () {
it('should be init', function () {
expect($scope.test).toBeTruthy();
});
});
});
app.js
(function() {
'use strict';
angular.module('app', [
'ui.router'
])
.controller('State1Ctrl', ['$scope', function($scope) {
console.log('cdn2', cdn);
$scope.test = 'hi';
console.log($scope.test);
}])
.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {
// For any unmatched url, redirect to /state1
$urlRouterProvider.otherwise("/state1");
// cdn is available here
console.log("cdn: ", cdn);
// Now set up the states
$stateProvider
.state('state1', {
url: "/state1",
templateUrl: cdn + "views/state1.html",
controller: 'State1Ctrl'
})
.state('state1.list', {
url: "/list",
templateUrl: "views/state1.list.html",
controller: function($scope) {
$scope.items = ["A", "List", "Of", "Items"];
}
})
.state('state2', {
url: "/state2",
templateUrl: "views/state2.html"
})
.state('state2.list', {
url: "/list",
templateUrl: "views/state2.list.html",
controller: function($scope) {
$scope.things = ["A", "Set", "Of", "Things"];
}
});
}]);
})();
Repo here demonstrating problem: https://github.com/superveetz/Loopback-Tests.git
npm install from base.
bower install from /client/src
karma start karma.conf.js from base.
node . from base to see cdn is avail normally.
Please update your plugin part in karma.config.
plugins: [
'karma-qunit',
'karma-ejs-preprocessor',
'karma-jasmine',
'karma-chrome-launcher'
],
and run these command.
npm install karma-jasmine --save-dev
npm install karma-chrome-launcher --save-dev
for further details please check here
EDIT 1:
Please do thihs changes.
inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
controller = $injector.get('$controller')('State1Ctrl', { $scope: $scope } );
});
Instead of $rootScope.new() use $rootScope.$new()
Define cdn console.log('cdn2', cdn) your cdn so it will not stop your test script
Related
I'm doing a web page and everything works great. No errors at all.
But now I decided to make a unit test case using Karma and Jasmine.
Somehow I get this error when I run my test
Error: [ng:areq] Argument 'moduleApp.SearchPageController' is not a function, got undefined
I also tried just SearchPageController but I get the same error.
What is missing here??
Route
angular.module('moduleApp').config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/cmt/complaint/search', {
templateUrl: 'src/callCenter/page1/searchPage.html',
controller: 'moduleApp.SearchPageController',
controllerAs: 'vm'
})
.otherwise({ redirectTo: '/' });
}]);
Unit test
describe('Controller: SearchPageController', function () {
var vm;
beforeEach(module('moduleApp'));
beforeEach(inject(function ($controller) {
vm = $controller('moduleApp.SearchPageController', {}, {});
}));
it('should bla bla', function () {
expect(vm).toBeDefined(); // vm is undefined, probably because of the erro above
});
});
Controller
angular.module('moduleApp.controller').controller('moduleApp.SearchPageController', SearchPageController);
SearchPageController.$inject = ['$log', '$scope', 'moduleApp.SearchPageService', '$http'];
function SearchPageController($log, $scope, searchPageService, $http) {
'use strict';
var vm = this;
vm.filterOption = 'Account ID';
}
Karma Config:
// Karma configuration
m
odule.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'angular.js' // yes this is on the same folder, it works
'../app/app.js',
'../components/angular-mocks/angular-mocks.js',
'unit/*.js',
'..app/src/'
],
// list of files / patterns to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_LOG,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
You definitely don't want to reference the controller with the module name:
$routeProvider
.when('/cmt/complaint/search', {
templateUrl: 'src/callCenter/page1/searchPage.html',
controller: 'moduleApp.SearchPageController', //<--- this is never correct
controllerAs: 'vm'
})
You always access a controller, service, or factory by the name it was defined:
$routeProvider
.when('/cmt/complaint/search', {
templateUrl: 'src/callCenter/page1/searchPage.html',
controller: 'SearchPageController',
controllerAs: 'vm'
})
It looks like you are including your tests BEFORE you include your app source:
// list of files / patterns to load in the browser
files: [
'angular.js' // yes this is on the same folder, it works
'../app/app.js',
'../components/angular-mocks/angular-mocks.js',
'unit/*.js',
'..app/src/' //<-- if this is your app source, it should be before tests!
],
I would just re-arrange the last two entries, but it also looks like you aren't actually including any files with the '..app/src/' statement. You should use the wildcarad to include all .js files:
// list of files / patterns to load in the browser
files: [
'angular.js' // yes this is on the same folder, it works
'../app/app.js',
'../components/angular-mocks/angular-mocks.js',
'..app/src/**/*.js' //<--- all .js files in folders and subfolders
'unit/*.js',
],
Typically, you want your tests to be the very last entry in the files section of your config.
I ran a jasmine test over angular js, and i get the next error:
PhantomJS 2.1.1 (Linux 0.0.0) test_servicioCalculadora should provide
a version FAILED
/home/ivan/workspace/mobile.trackphone/www/lib/ionic/js/ionic.bundle.js:13218:53
forEach#/home/ivan/workspace/mobile.trackphone/www/lib/ionic/js/ionic.bundle.js:9168:24
loadModules#/home/ivan/workspace/mobile.trackphone/www/lib/ionic/js/ionic.bundle.js:13178:12
createInjector#/home/ivan/workspace/mobile.trackphone/www/lib/ionic/js/ionic.bundle.js:13104:22
workFn#/home/ivan/workspace/mobile.trackphone/www/lib/angular-mocks/angular-mocks.js:3074:60
loaded#http://localhost:9876/context.js:151:17
I tried to change the routes, inject other service, and it's not working too
I ran other case like
it("compara un valor con otro", function () {
var pi = 3.1415926,
e = 2.78;
expect(e).toBeLessThan(pi);
expect(pi).not.toBeLessThan(e);
});
and this's working fine.
This is my test file
describe("test_servicioCalculadora", function () {
var calcu;
beforeEach(module('starter'));
it('should provide a version', inject(function(version) {
expect(version).toEqual('v1');
}));
});
my index.js is
var app = angular.module('starter', ['ionic', 'LocalStorageModule', 'btford.socket-io', 'angularMoment', 'ngCordova', 'ngAudio']);
app.value('version', 'v1');
my karma.config.js
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'../www/lib/ionic/js/ionic.bundle.js',
'../www/lib/angular-mocks/angular-mocks.js',
'../www/js/index.js',
'../tests/**/*-test.js'
],
exclude: [
],
preprocessors: {
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_ERROR,
autoWatch: true,
browsers: ['PhantomJS'],
singleRun: false,
concurrency: Infinity
})
};
You are trying to inject a value. Should be a service or a factory.
e.g.
beforeEach(module('some.service', function($provide){
var log = {
info: function(data){
console.log(data);
}
}
$provide.value('$log', log);
});
beforeEach(inject(function(_serviceName_){
serviceName = _serviceName_;
}));
it("Actual test", function(){
serviceName.performAction();
});
See the example, first is the module where you can set some values. Then is the service or factory at the end the test.
This mean that the value of log will be injected on serviceName when is used on the actual test.
Hope this help to clarify.
This is my first time using Jasmine, and I have tested my first Factory without problems.
But now, I want to test this Service:
angular.module('Questions', [])
.service('QuestionsService', function($uibModal, $log, _) {
...
}
$uibModal is from UI Bootstrap (see here) and _ is Lodash.
My Jasmine test so far is:
describe('Service: QuestionsService', function() {
var QuestionsService;
beforeEach(inject(function(_QuestionsService_) {
QuestionsService = _QuestionsService_;
}));
...
}
And when I try it (grunt test), I get the following error:
Error: [$injector:unpr] Unknown provider: $uibModalProvider <- $uibModal <- QuestionsService
And at some point I also had:
Error: [$injector:unpr] Unknown provider: _Provider <- _ <- QuestionsService
If it can help, my Karma conf is:
module.exports = function(config) {
'use strict';
config.set({
autoWatch: true,
basePath: '../',
frameworks: [
"jasmine"
],
// list of files / patterns to load in the browser
files: [
// bower:js
'bower_components/jquery/dist/jquery.js',
'bower_components/lodash/lodash.js',
'bower_components/angular/angular.js',
'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap.js',
'bower_components/angular-animate/angular-animate.js',
'bower_components/angular-cookies/angular-cookies.js',
'bower_components/angular-resource/angular-resource.js',
'bower_components/angular-route/angular-route.js',
'bower_components/angular-sanitize/angular-sanitize.js',
'bower_components/angular-touch/angular-touch.js',
'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
'bower_components/angular-mocks/angular-mocks.js',
// endbower
"app/scripts/**/*.js",
"test/mock/**/*.js",
"test/spec/**/*.js",
],
exclude: [
],
port: 8080,
browsers: [
"PhantomJS"
],
plugins: [
"karma-phantomjs-launcher",
"karma-jasmine"
],
singleRun: false,
colors: true,
logLevel: config.LOG_INFO,
});
};
Just in case others find this. To solve the error when testing a directive's controller, I mocked the $uibModal service, conceptually like this:
describe('Service: QuestionsService', function() {
var controller;
beforeEach(inject(function($controller) {
controller = $controller('controllerName', {
$uibModal : {}
});
}));
...
}
$uibModal may need to be more than just an empty object if you are writing tests against controller functions that interact with it.
The app's module was not included in the test. The refactored test for the QuestionService would be:
describe('Service: QuestionsService', function() {
var QuestionsService;
// The module needs to be included in the test.
beforeEach(module('boardgameApp'));
beforeEach(inject(function(_QuestionsService_) {
QuestionsService = _QuestionsService_;
}));
...
}
Despite some people having the same issues (like [here][1] or [there][2]), I do not succeed to test my directive in my Angular (1.2.25) application.
Here is my project structure:
myapp
+- src/main/java/resources/META-INF/resources/workflow/directives
| +- directives.js
| +- *.html (all templates)
+- src/test/javascript
+- karma.conf.js
+- spec/directives
+- text-input.spec.js
(yes, not a good structure, but my Angular application is stuck in a Java project)
My karma configuration:
// Karma configuration
module.exports = function (config) {
config.set({
...
// base path, that will be used to resolve files and exclude
basePath: '',
// testing framework to use (jasmine/mocha/qunit/...)
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
// Third parties dependencies: jQuery, Angular, Angular modules, Angular mocks
'../../main/resources/META-INF/resources/workflow/bower_components/...',
// My directives
'../../main/resources/META-INF/resources/workflow/directives/*.html',
'../../main/resources/META-INF/resources/workflow/directives/*.js',
// My application
'../../main/resources/META-INF/resources/workflow/scripts/*.js',
'../../main/resources/META-INF/resources/workflow/app/**/*.js',
// My Test files
'spec/directives/*.js'
],
// list of files / patterns to exclude
exclude: [],
// web server port
port: 8888,
browsers: [ 'Chrome' ],
// Which plugins to enable
plugins: [
'karma-ng-html2js-preprocessor',
'karma-chrome-launcher',
'karma-jasmine'
],
preprocessors: {
'../../main/resources/META-INF/resources/workflow/directives/*.html': [ 'ng-html2js' ]
},
ngHtml2JsPreprocessor: {
// Not sure what to put here...
},
...
});
};
My test:
describe('directive: text-input', function() {
var element, scope;
beforeEach(module('myApp'));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
element = '<div my-input-text data-label="Foo" data-model="bar"></div>';
element = $compile(element)(scope);
scope.$digest();
}));
describe('basics tests', function() {
it('should be editable', function () {
expect(element.text()).toBe('Foo');
});
});
});
And the directive itself:
var myDirs = angular.module('my-directives', []);
// Text input
myDirs.directive('myInputText', function () {
return {
replace: true,
templateUrl: 'directives/text-input.html',
scope: {
label: '=',
readOnly: '=',
code: '=',
model: '='
}
};
});
When running the tests (grunt karma), I get that error:
Chrome 31.0.1650 (Windows 7) directive: text-input basics tests should be editable FAILED
Error: Unexpected request: GET directives/text-input.html
No more request expected
I still don't get what I do wrong in my preprocessor. I've tried a lot of different configuration in the ngHtml2JsPreprocessor, but the error is always the same.
I saw in the DEBUG logs that the pre processor is working on my template HTML files:
DEBUG [preprocessor.html2js]: Processing "d:/dev/my-app/src/main/resources/META-INF/resources/workflow/directives/text-input.html".
Thanks.
I finally found a solution.
In my karma.conf.js, I set a module-name, like that:
ngHtml2JsPreprocessor: {
moduleName: 'my-directives'
},
then, in my Jasmine test, I add it:
beforeEach(module('myApp'));
beforeEach(module('my-directives'));
Another solution is to directly set the HTML file as a module without changing the karma.conf.js:
beforeEach(module('directives/text-input.html'));
But not a good solution as I have dozen of directives/*.html...
I am trying to test a Directive which uses external template. I tried all the following solutions with no luck:
ng-directive-testing
How to test directives that use templateUrl and controllers?
AngularJS + Karma + Ng-html2js => Failed to instantiate module ...html
I created a test directive (a simple div) and tested it using an inline 'template' and external 'templateUrl'. The inline solution works while the external doesn't:
angular.module('AdUnit').directive('actionButton',function($location){
return{
scope:{
actionName: '#'
},
restrict: 'E',
//template: "<div ng-click='click()'>action button</div>",
templateUrl: '/staticfiles/adunit/html/directives/actionButtonTemplate.html',
controller: ['$scope', function($scope){
$scope.click = function(){
$scope.$emit('ACTION_CLICK', $scope.actionName);
}
}]
}
});
describe("Unit: Testing action button directive", function() {
var elm, scope, linkFn;
beforeEach(
module('AdUnit')
);
beforeEach(module('/staticfiles/adunit/html/directives/actionButtonTemplate.html'));
beforeEach(inject(function($rootScope, $compile) {
elm = angular.element('<action-button action-name="post-action-0"></action-button>');
scope = $rootScope;
linkFn = $compile(elm);
linkFn(scope);
scope.$digest(); // have to digest to bring html from templateCache
console.log('post compile',elm.html());// <== the html here still have {{}}
}));
it('should show a thumb',function() {
console.log('post link',elm.html());// <== the html is bound
expect(elm.text()).toBe("action button");
});
});
My Karma config file:
module.exports = function(config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
// frameworks to use
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js',
'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js',
'http://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular-route.js',
'http://code.angularjs.org/1.0.6/angular-mocks.js',
'../html/*.html',
'../html/directives/*.html',
'../js/adUnit.js',
'../js/controllers/*.js',
'../js/directives/*.js',
'../js/services/*.js',
'../*.js',
'../**.*.js',
'**/*.tests.js'
],
preprocessors : {
'../html/**/*.html': ['ng-html2js']
},
/* ngHtml2JsPreprocessor: {
'AdUnit': '/staticfiles/adunit/html/directives/actionButtonTemplate.html'
*//*moduleName: '/staticfiles/adunit/html/directives/internalPlayerTemplate.html'*//*
},*/
// list of files to exclude
exclude: [
],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera (has to be installed with `npm install karma-opera-launcher`)
// - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
// - PhantomJS
// - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
browsers: ['Chrome'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};
I keep getting the following error:
Failed to instantiate module /staticfiles/adunit/html/directives/actionButtonTemplate.html due to:
Error: [$injector:nomod]
Any help will be appreciated!
EDIT: #MK Safi's answer solved my problem. I was missing the following:
ngHtml2JsPreprocessor: {
'moduleName': 'Templates',
// Function that transforms the path to look exactly like
// you have it in templateUrl in your Angular code
//
// Mine looks like this
cacheIdFromPath: function(filepath) {
return filepath.match(/\/staticfiles\/adunit\/html\/directives\/.*\.html/);
}
},
and before each test:
beforeEach(module('Templates'));
it is important for the regular expression to point to the same path as the directive's "templateUrl", since html2js will cache those templates using this path (see html2js for more details about that)
I have this setup correctly in my tests and your setup looks right, except for a few things.
Make the following changes to your Karma config file:
ngHtml2JsPreprocessor = {
'moduleName': 'Templates',
// Function that transforms the path to look exactly like
// you have it in templateUrl in your Angular code
//
// Mine looks like this
cacheIdFromPath: function(filepath) {
return filepath.match(/views\/.*/)[0]
}
}
Then in your test's beforeEach include the Templates module that you specified in your Karma config above: module('Templates')
beforeEach(function() {
module('Templates')
})