I am attempting to incorporate the humane.js notification library into my AngularJS app. I have wrapped the humane.js use into an Angular service, and the app is working correctly. But when I attempt to write unit tests for the Angular service, I get the following error when attempting to execute the first test of this service:
ReferenceError: humane is not defined
My karma.conf file contains the reference to the humane.js file:
files: [
'bower_components/angular/angular.js',
'bower_components/angular-route/angular-route.js',
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/messageformat/messageformat.js',
'bower_components/angular-translate/angular-translate.js',
'bower_components/angular-translate-interpolation-messageformat/angular-translate-interpolation-messageformat.js',
'bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js',
'bower_components/momentjs/moment.js',
'other_components/highcharts-ng/src/directives/highcharts-ng.js',
'other_components/keylines/keylines.js',
'other_components/logging/log4javascript.js',
'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
'bower_components/humane-js/humane.js',
'scripts/*.js',
'scripts/*/*.js',
'resources/defaultTranslations.js',
'test/unit/*/*.js',
'views/*.html'
],
I can't figure out what's going wrong. If I replace Humane.js with an otherwise similar notification library - alertify.js - and do the exact same thing, I do not get the error.
What am I missing? Like I said, the application works, including the notification portion. Only the unit tests are affected. The Humane.js module uses some unusual (to me) self-executing module syntax:
;!function (name, context, definition) {
if (typeof module !== 'undefined') module.exports = definition(name, context)
else if (typeof define === 'function' && typeof define.amd === 'object') define(definition)
else context[name] = definition(name, context)
}('humane', this, function (name, context) {
...module definitions here...
return new Humane()
})
I'm wondering if this is confusing the karma loader somehow. The other library (alertify.js) does not have this structure.
Got here while trying to figure out a different Karma error. It might not be related but I see your karma file paths look different that to me. I usually use the default generated via the angular-generator and it products something like this in my karma kong:
files : [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-cookies/angular-cookies.js',
'app/bower_components/angular-sanitize/angular-sanitize.js',
'app/bower_components/angular-route/angular-route.js'
...
],
Related
I'm using yeoman generator for scaffolding angular web application with requirejs. Its working fine but when I tried to concat and minifying all the js file into a single file through grunt task runner its started giving me above mentioned error. I've researched online about the issue and common solution is I may be mis-spelled any service injecting in the module or service does not exists, I've cross checked again all the spelling, quotation marks etc everything seems fine but still I'm unable to resolve this issue.
Here is my app.js file where my main module with dependencies is listed.
return angular
.module('arteciateYeomanApp', [
'arteciateYeomanApp.controllers.MainCtrl',
'arteciateYeomanApp.controllers.AboutCtrl',
'arteciateYeomanApp.services.Xhr',
'arteciateYeomanApp.services.Common',
'arteciateYeomanApp.controllers.ArtworkCtrl',
'arteciateYeomanApp.controllers.AddAccountCtrl',
'arteciateYeomanApp.controllers.AddArtgroupCtrl',
'arteciateYeomanApp.controllers.AddArtistCtrl',
'arteciateYeomanApp.controllers.AddArtworkCtrl',
'arteciateYeomanApp.controllers.AddCampaignsCtrl',
'arteciateYeomanApp.controllers.AddGenreCtrl',
'arteciateYeomanApp.controllers.AddInstitutionCtrl',
'arteciateYeomanApp.controllers.AdminSignupCtrl',
'arteciateYeomanApp.controllers.ArtistInfoCtrl',
'arteciateYeomanApp.controllers.DirectUserSignupCtrl',
'arteciateYeomanApp.controllers.ErrorCtrl',
'arteciateYeomanApp.controllers.ForgotPasswordCtrl',
'arteciateYeomanApp.controllers.GroupBuyingCtrl',
'arteciateYeomanApp.controllers.LoginCtrl',
'arteciateYeomanApp.controllers.AdminLoginCtrl',
'arteciateYeomanApp.controllers.ResetPasswordCtrl',
'arteciateYeomanApp.controllers.SignupCtrl',
'arteciateYeomanApp.controllers.UnblockUserCtrl',
'arteciateYeomanApp.controllers.UpdatePasswordCtrl',
'arteciateYeomanApp.controllers.DashboardCtrl',
'ngRoute','ngResource']).config(.....);
here is grunt task which I'm running for minifying the js files.
registering task
grunt.registerTask('dev', ['requirejs' ]);
Here is task running script
requirejs : {
compile : {
options : {
baseUrl : "<%= yeoman.app %>/scripts",
mainConfigFile : "<%= yeoman.app %>/scripts/main.js",
name : "main",
out : "requireArterciate.js"
}
}
}
Please let me know if I'm doing something wrong here.
If you need to minify the angularjs code, then use the following standard format syntax to define the controller and to inject the dependencies. Refer Dependency Injection
angular.module('test').controller('testController', testController);
testController.$inject = ['$scope', '$rootScope'];
function testController($scope, $rootScope) {};
I have a very simple jasmine unit test that targets testing a simple operation on an AngularJS controller as follows:
/// <reference path="../src/jasmine.js"/>
/// <reference path="../../Scripts/angular.js"/>
/// <reference path="../../Scripts/angular-mocks.js"/>
/// <reference path="../../Scripts/angular-ui-router.js"/>
/// <reference path="../../Scripts/angular-route.js"/>
/// <reference path="../../Scripts/angular-resource.js"/>
/// <reference path="../../Scripts/angular-ui/ui-bootstrap.js"/>
/// <reference path="../../app/app.js"/>
/// <reference path="../../app/controllers/myController.js"/>
describe("my Controller", function () {
var scope, ctrl, vm;
beforeEach(module("app"));
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
ctrl = $controller(MyController, { $scope: scope });
vm = ctrl;
}));
it("sets the title", function() {
expect(vm.title).toBe("This is the title");
});
});
To begin the above test passes when using either of the following:
Open SpecRunner.html which contains the same references and displays a passed unit test in the jasmine default test runner
Test without modification passes if running via ReSharper test runner (this tool is not important other than to show the test passes using another test runner; the test isn't the culprit)
However no matter whenever I do any of the following using Chutzpah the test fails:
Run from the command line using chutzpah.console.exe
Use the right-click menu open in VS.NET to Run JS Tests
Each time the following error is given:
Error: [$injector:modulerr] Failed to instantiate module
app due to: [$injector:nomod] Module
'app' 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.
So this is a very typical error if the AngularJS references are not included. However as you can see above they are for sure included and the other test runners see them and the test passes. The error is identical regardless if I run the unit test from the command line or if I view the output in VS.NET.
I even turned on the /trace parameter from the command line and viewed the chutzpah.log file. It sure enough is finding the Angular reference files so I'm not sure why the test is failing.
I've seen a ton of posts surrounding this and the solution is always that the references are missing. That's not the case here and I'm stuck. What am I missing to make this test pass using Chutzpah?
I recommend using a chutzpah.json settings file for angular projects. I placed mine at the root of my project, next to the web.config. You can specify the dependencies in one place this way, which is very helpful as the angular project grows rapidly. I can't know for sure, but I think yours would look something like the following:
{
"Framework": "jasmine",
"FrameworkVersion": "1",
"References": [
{ "Path": "./Scripts/angular/angular.js" },
{
"Path": "./Scripts/angular",
"Includes": [ "*.js" ]
},
{ "Path": "./Scripts/angular-ui/ui-bootstrap.js" },
{ "Path": "./app/app.js" },
{
"Path": "./app",
"Includes": [ "*.js" ]
}
],
"Tests": [
{ "Includes": [ "*.spec.js" ] }
]
}
This will run on jasmine version 1. I'm not sure how you It will include everything that matches "./Scripts/angular*.js" and everything that matches "./app/*.js" (including sub-directories). As you add new angular references and app modules, you should not need to modify this configuration (once you get it working).
I believe through thorough testing I have determined the cause. The issue is most likely that I'm using ES6 classes in my Angular .js files being used in my tests.
The issue is PhantomJS which Chutzpah uses does not support ES6 and the class keyword and thus it fails (see this for validation). I was able to reproduce in a non Angular sample. I used an old fashioned ES5 style IIFE and the simple test now passed. This is why it worked in Chrome and in ReSharper which used Chrome as the default test browser and Chrome supports the ES6 class keyword. If I make ReSharper use PhantomJS instead of Chrome, then my simple test also fails.
The root problem appears to be PhantomJS and its lack of support for ES6 syntax.
Here is a blog post I wrote on my findings after pulling my hair out on this for a couple of weeks. It explains more in detail about the issues with writing non-ES5 complaint code and using that with Chutzpah and PhantomJS:
Chutzpah and non-ES5 JavaScript for Unit Testing is Problematic
I am setting up my Karma configuration file, but I do not fully understand some of options that exist as I am not having success testing templates that have ran through the ngHtml2JsPreprocessor and have been
$templateCached
Inside of the ngHtml2JsPreprocessor I can add a few key value properties involving paths.
ngHtml2JsPreprocessor: {
stripPrefix: ".*/Went all the way back to the root of my application/",
// moduleName: 'templatesCached'//
},
I commented out the templates for now to make sure that I am getting access to each file as module. I am loading the modules with no error. I can find the templateCached version in my dev tools.
beforeEach(module('template'));
My Templates folder sits outside the basepath I created.
basePath: 'Scripts/',
I have it referenced inside the preprocessors object
preprocessors: {
'../Templates/**/*.html' : ['ng-html2js']
},
Again all of my templates are now js files and cached.
I inside of my package.json I saved the files as
save-dev
"karma-chrome-launcher": "^0.2.2",
"karma-jasmine": "^0.2.2",
"karma-ng-html2js-preprocessor": "^0.2.1",
I referenced my installs in the plugins.
plugins: [
'karma-chrome-launcher',
'karma-jasmine',
'karma-sinon',
'karma-ng-html2js-preprocessor'
],
I have all of my files loaded
files: [
//jquery libaries
// angular libraries
// Scripts files
// source app.js
// tests folder and files
]
My tests are running off of Karma start
However, my directive is just an empty string
element.html()
returns ""
I have bard inject set up
bard.inject(
"$compile",
"$controller",
"$rootScope",
'$templateCache',
"haConfig",
"$q"
);
Here is the inside of my beforeEach
bard.mockService(haConfig, {
getTemplateUrl: '/tst!'
});
//bard.mockService(haConfig, {});
console.log('ha config2', haConfig.getTemplateUrl());
var html = angular.element("<div explore-hero></div>");
console.log('htl',haConfig.getTemplateUrl());
scope = $rootScope.$new();
//templateCache
element = $compile(html)(scope);
//console.log(haConfig.getTemplateUrl(html));
scope.$digest(element);
console.log('missing text',haConfig.getTemplateUrl(html));
controller = element.scope();
console.log("element", element);
I have no idea why I am getting an empty string back. I am creating the html file but, nothing is inside of it.
All I can wonder if I should have the the templatesCached files showing up in a folder on my dev tools? Also whether or not the files should be referenced inside of the files array inside karma.conf.js
Right now I have the html files referenced? I have tried the js files but that did not seem to do anything
The problem was actually quite simple fix. I was tempted to delete it but, in case someone has a similar issue I want this to be available.
Inside the karma.conf.js I have a
stripPrefix: 'rootDirectory' // was already in place
stripSuffix: '.js.html' // I had to make a strip on the templatesCached
prependSuffix: '.html' // this is what I was searching for
When the preprocessor ran it templateCached all of my files. However, they did not end the way that I was expecting them and I could not read them. I had the module and other parts set up correctly.
I'm testing an Angular app with Karma. I've got everything working, but it seems like I'm doing something wrong.
https://gist.github.com/guyjacks/7bca850844deb612e681
Karma will throw the following error if I comment out 'app/notes/notes.main.js' :
Uncaught Error: [$injector:nomod] Module 'notes.main' 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.
http://errors.angularjs.org/1.4.3/$injector/nomod?p0=notes.main
at /Users/guyjacks/projects/adr-demo/node_modules/angular/angular.js:1958
I don't want to have to manually list each application file to control the order in which each file loads. Am I don't something wrong or do I just have to list each file in the order I want?
---- Solution based on the accepted answer ----
My app is organized into modules as recommended by the Angular Style Guide: https://github.com/johnpapa/angular-styleguide.
'app/app.module.js',
'app/**/*.module.js',
'app/**/*.service.js',
'app/**/*.controller.js',
'app/**/*.directive.js',
'app/**/*.js'
I don't think the following lines are necessary above
'app/**/*.service.js',
'app/**/*.controller.js',
'app/**/*.directive.js'
when each module has an angular module declared in the *.module.js file like my app does.
That said, if you did need to explicitly load services before controllers & controllers before directives then this would be the way to do it.
Update : I could not see your karma file, now Gist link is fixed.
The point in notes[.]main.js is causing the problem,
So, 'app/**/*.js' is not matching notes.main.js.
Try now like this : app/**/*. *.js
=============================================================
Before update :
You have to load the modules that you app depends on, in karma config. file :
module.exports = function(config) {
config.set({
.......
// list of files / patterns to load in the browser
files: [
'./client/app/vendors/angular/angular.js',
// =====> load Your modules here ...
'./client/app/app.js',
'./client/app/controllers/*.js',
'./client/app/directives/*.js',
'./client/app/services/*.js',
'./test/client/unit/**/*.js'
],
.....
}) }
I run my angular tests with karma, my application is running fine in browser, but tests fails and I am suspecting wrong settings.
Here are the controllers and tests :
// app/scripts/controllers/main.js
'use strict';
angular.module('GloubiBoulgaClientApp')
.controller('MainCtrl', function ($scope) {
});
Here is the test file :
'use strict';
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('GloubiBoulgaClientApp'));
var MainCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('MainCtrl', {
$scope: scope
});
}));
it('should attach a list of awesomeThings to the scope', function () {
expect(true).toBe(true);
});
});
The karma conf
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: [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-cookies/angular-cookies.js',
'app/bower_components/angular-sanitize/angular-sanitize.js',
'app/scripts/*.js',
'app/scripts/**/*.js',
'test/mock/**/*.js',
'test/spec/**/*.js'
],
// list of files / patterns to exclude
exclude: [],
// web server port
port: 8080,
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || 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
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};
The error ouput
PhantomJS 1.9.2 (Linux) Controller: MainCtrl should attach a list of awesomeThings to the scope FAILED
Error: [ng:areq] Argument 'MainCtrl' is not a function, got undefined
http://errors.angularjs.org/1.2.8-build.2094+sha.b6c42d5/ng/areq?p0=MainCtrl&p1=not%20a%20function%2C%20got% 2
0undefined
at assertArg (--obfuscated-path--GloubiBoulga/GloubiBoulgaClient/app/bower_components/angular/angula
r.js:1362)
at assertArgFn (--obfuscated-path--GloubiBoulga/GloubiBoulgaClient/app/bower_components/angular/angu
lar.js:1373)
at --obfuscated-path--GloubiBoulga/GloubiBoulgaClient/app/bower_components/angular/angular.js:6763
at --obfuscated-path--GloubiBoulga/GloubiBoulgaClient/test/spec/controllers/main.js:15
at invoke (--obfuscated-path--GloubiBoulga/GloubiBoulgaClient/app/bower_components/angular/angular.j
s:3704)
at workFn (--obfuscated-path--GloubiBoulga/GloubiBoulgaClient/app/bower_components/angular-mocks/angular -mocks.js:2120)
I am wondering why this happen, I tried to find some documentation about the karma initialization with angularjs. But the most documentation I found is only dummy tutorial that are repeating the same pattern ( like the dummy todo list, but with phones ... )
It seem that $controllerProvide.register fails to resolve my controllers name.
But Directives tests are working correctly ...
Thanks for your attention.
Edit Notes : I replaced the controller PersonCtrl by MainCtrl in this thread because It was confusing people about where to look. Now MainCtrl is the simpliest failing example I found.
This issue is only affecting my controllers, ( all of them ), but tests for Services and Directives are working as expected
I solved my problem, I've spent nearly a week to figure why this was not working.
I'd like to warn you, that Karma Stacktrace and error reports, even in debug mode, were not showing clues and were mainly missleading.
I've spent time in javascript debugger jumping frame to frame, to understand why my controllers where not loaded. ( Inspecting Angular's controllers register, shown it was empty)
While digging in my directories I've found a *.js that were not loaded in the index in production but by the globbing pattern in tests.
It was my old http_interceptor service that I moved but did not trashed the file.
Removing this buggy file fixed the weird Karma/Jasmine/Angular behaviour.
Lesson learned :
Do not trust tests output ( but what should I trust then ? ).
Remove files you are not using/testing.
Thanks to everyone who tryied to solve this issue.
i think the main problem is coming from the karma conf :
files: [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-cookies/angular-cookies.js',
'app/bower_components/angular-sanitize/angular-sanitize.js',
'app/scripts/*.js',
'app/scripts/**/*.js',
'app/scripts/**/**/*.js',
'test/mock/**/*.js',
'test/spec/**/*.js'
],
removing the * and specifying files one by one in the correct order, because if one is loaded before another it can break.
Edit : Add your files in the same order as your index.html
looking at this I'm wondering if the error message is confusing things.
in the stack trace i can see
TypeError: 'undefined' is not an object (evaluating 'scope.awesomeThings.length')
and from the example it does seem as though you have not defined any properties on the scope in your controller.
do you still have the problem if you add
$scope.awesomeThings = [];
to your controller?
If you're using the latest angular and also using the angular-route module, you should include the angular-route script in the karma conf file too:
files: [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-route/angular-route.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-cookies/angular-cookies.js',
'app/bower_components/angular-sanitize/angular-sanitize.js',
'app/scripts/*.js',
'app/scripts/**/*.js',
'app/scripts/**/**/*.js',
'test/mock/**/*.js',
'test/spec/**/*.js'
],
I had this problem and adding it to the karma file did the trick.