Unit testing AngularJS application with karma-browserify - javascript

I'm trying to unit test an AngularJS/Browserify application via karma-browserify. Ultimately, when I run my gulp karma task, I get the error Error: [$injector:nomod] Module 'myApp' is not available! You either misspelled the module name or forgot to load it...
My gulpfile.js has the task
gulp.task('test', function(done) {
new karma.Server({
configFile: __dirname + '/karma.conf.js'
}, done).start();
});
My karma.conf.js includes
{
// ...
frameworks: ['browserify', 'jasmine'],
files: [
'node_modules/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'spec/**/*.js'
],
preprocessors: {
'spec/**/*.js': [ 'browserify' ]
},
browserify: {
debug: true
}
// ...
}
I define my module in a main.js that includes
require('angular').module('myApp', [
//...lots of `require`s for each dependency...
]);
I define my controller in a MainCtrl.js that looks like
function MainCtrl(/*...deps...*/) {
var ctrl = this;
ctrl.foo = 'bar';
}
module.exports = MainCtrl;
then register the controller elsewhere like
var app = require('angular').module('myApp');
app.controller('MainCtrl', [/*...deps...*/, require('./MainCtrl')]);
Finally my test looks like
(function() {
"use strict";
describe('MainCtrl', function() {
var ctrl;
beforeEach( angular.mock.module('myApp') );
beforeEach( inject( function($controller) {
ctrl = $controller('MainCtrl');
}));
it('should be defined', function() {
expect(ctrl).toBeDefined();
});
});
}());
Workaround
The workaround I have is to add my main.js file to karma.conf.js
files: [
'js/main.js', // ADDED: Defines the app and `require`s angular
'node_modules/angular-mocks/angular-mocks.js',
'spec/**/*.js'
],
preprocessors: {
'js/main.js': ['browserify'], // ADDED
'spec/**/*.js': ['browserify']
}
and everything works. But I thought I wasn't supposed to add my source files to karma (at least not with karma-browserify). Is this the right way to set up my project?

Yes, the 'workaround' is the desired way to use karma-browserify.
preprocessors definition specifies which of the included files should be processed by which preprocessor but does not include them:
The keys of the preprocessors config object are used to filter the
files specified in the files configuration.
it is files definition that actually includes the files:
The files array determines which files are included in the browser and
which files are watched and served by Karma.

Files tells Karma which files it should load relative to the base path. These are:
All test related libraries
Our source code to test
The tests themselves

Related

gulp-angular-templatecache Module is not available

I am using gulp-angular-templacache.
I have that task that should create a module named templates and I added it as a dependency to my app module:
Configurations:
templateCache: {
file: 'tempaltes.js',
options: {
module: 'templates',
standalone: true,
root: 'app/'
}
}
App module:
var App = angular.module('app', [
'templates']);
Gulp task:
gulp.task('templatecache', ['clean-code'], function() {
log('Creating AngularJS $templateCache');
return gulp
.src(config.htmltemplates)
.pipe($.minifyHtml({empty: true}))
.pipe($.angularTemplatecache(
config.templateCache.file,
config.templateCache.options
))
.pipe(gulp.dest(config.temp));
});
But, when I try to run this I am always getting that error message:
Uncaught Error: [$injector:nomod] Module 'templates' 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.
I tried to make the templatescache also not standalone and removing the dependency but without success...
What should I do ??
Seems you are loading template.js, after the module app definition. You can load the file before your angular app file
OR, Don't define a standalone module.
Configuration
templateCache: {
file: 'tempaltes.js',
options: {
module: 'templates',
standalone: false,
root: 'app/'
}
}
Angualr
//Define the module
angular.module('templates', []);
var App = angular.module('app', ['templates']);

AngularJS Unit Test: Module 'admin.module' is not available

I'm testing an Angular controller using Karma and Jasmine but I can't seem to load in my module from my main class.
Here's my main class: admin.controller.js
angular.module('admin.module').controller('admin.controller', ['$scope', function ($scope) {
$scope.SaveChanges = function()
{
return true;
}
}]);
Here's my test class: admin.controller.tests.js
describe('admin.controller tests', function () {
beforeEach(module('admin.module'));
var $controller = {};
beforeEach(inject(function (_$controller_) {
$controller = _$controller_;
}));
describe('$scope.SaveChanges', function () {
it('Should return true', function () {
var $scope = {};
var controller = $controller('admin.controller', { $scope: $scope });
expect($scope.SaveChanges()).toBe(true);
});
});
});
My karma.conf.js file points to the following files in my project:
// list of files / patterns to load in the browser
files: [
'../TriAngular/scripts/angular.js',
'../TriAngular/scripts/angular-mocks.js',
'../TriAngular/app/admin/*.js',
'app/admin/*.js'
],
The admin.controller.js file is inside ../TriAngular/app/admin and my admin.controller.test.js is inside 'app/admin'.
I have tried to directly reference the files in my karma config file which has not worked. The full error is:
Module 'admin.module' 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.
The issue turned out to not be obviously from the exception being shown. I was missing a angular-route.js file which needed to be included as it looked like my admin module was dependent on it.
List of includes in my karma.conf.js file:
// list of files / patterns to load in the browser
files: [
'../TriAngular/scripts/angular.js',
'../TriAngular/scripts/angular-mocks.js',
'../TriAngular/scripts/angular-route.js',
'../TriAngular/app/admin/*.js',
'app/admin/*.js'
],
Try;
beforeEach(function(){
module('admin.module');
}
});
I'm not familiar with Karma, but you also need to register your module as well, I've only ever used Jasmine with resharper so what I do to register files is;
/// <reference path="../scripts/app.js" />
At the top of the file.
Also don't forget to reference your module's dependencies as well.

Using the RequireJS text plugin

I am using Karma to run tests against some code.
Both the tests and the code are transpiled (ES6 => ES5 using babel) before being run by Karma.
This works great and the tests run fine.
But if I try and use the text! plugin from any of the files being tested...
import template from 'text!./template.html';
...I get:
There is no timestamp for /base/src/text.js!
Uncaught Error: Script error for "text", needed by: text!app/template.html_unnormalized2
http://requirejs.org/docs/errors.html#scripterror
Uncaught Error: Load timeout for modules: text!app/template.html_unnormalized2
Does anyone have any ideas why this might be?
The built artifact in the dist folder (i.e. the item under test) contains the successfully encoded text RequireJS items eg:
define('text!app/template.html',[],function () { return '<div>foo</div>';});
Additional Info
test-main.js
var TEST_REGEXP = /(spec|test)\.js$/i;
var allTestFiles = [];
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(normalizedTestModule);
}
});
require.config({
baseUrl: '/base/src',
paths: {},
shim: {},
deps: allTestFiles,
callback: window.__karma__.start
});
karma.conf.js
module.exports = function(config) {
'use strict';
var path = require('path');
var cdn = 'http://localhost:55635/modules/';
var basePath = path.dirname(__filename);
config.set({
basePath: '../../..',
frameworks: [
'requirejs',
'jasmine'
],
files: [
{
pattern: path.join(basePath, 'test-transpiled', '*-spec.js'),
included: false
},
path.join(basePath, 'dist', 'artifacts', 'app.js'),
path.join(basePath, 'test', 'unit', 'test-main.js')
],
proxies: {
'/cdn/': cdn
},
exclude: [],
preprocessors: {},
reporters: ['dots'],
colors: true,
autoWatch: false,
singleRun: false,
browsers: ['Chrome'],
});
};
Edit:
I have added the following to karma.conf.js:
files: [
{
pattern: path.join(basePath, 'node_modules/require-plugins/text/text.js'),
included: false
},
// ...
],
I continue to get an error when running the tests:
There is no timestamp for /base/src/text.js!
Presumably because I need to add "text" to the paths section of test-main.js?
require.config({
baseUrl: '/base/src',
paths: {
'text': '../node_modules/require-plugins/text/text'
},
// ...
});
But I have tried various combinations of baseUrl and the path in the text path and I cannot get it to stop 404-ing.
Your files option in karma.conf.js does not include the text plugin, which is why you get the error that there's no timestamp for it.
Add an item to your files list that hits the text plugin on your file system, and make sure that you have included: false for it. RequireJS plugins are like other modules: RequireJS must be able to load them to use them.
You may need to also set paths in test-main.js depending on where you put your plugin. RequireJS already is looking for it at /base/src/text.js. If you locate it so that the plugin is served at this URL, then there's no need to set paths. If you put it somewhere else, then you do need to set paths. Something like:
paths: {
text: 'path/to/text',
}
Remember that the paths in paths are interpreted relative to your baseUrl setting.
I tried using the require.js text! plugin, and also found that it conflicts with the baseUrl defined for the rest of the project.
The requirejs.config sets the baseUrl to the parent directory of the JS files, while my templates are defined in a sibling directory to the js.
There was no way to tell requirejs to load templates from /base/templates and js from base/js.
My solution was to change the text.js plugin, and add a hook to change the url just before the ajax call is made to fetch the HTML file. You can take my version of text.js from here.

Include file with module from AngularJS app into RequireJS config file in Karma

As we know, when use RequireJS in the configuration file we must define 'paths' and 'shim'. When writing tests (Karma, Jasmine), we need to create an additional configuration file for RequireJS and re-define the same data.
I try to extract common parts and load them dynamically. In Angular JS application, everything works without problems, but in test i have always error '404'. But let's get to the beginning. Simple example structure:
.
|-- newApp
| |-- app
| | `-- app.require.js
| |-- newApp.require.js
| `-- index.html
`-- test
|-- test.karma.js
`-- test.require.js
index.html load RequireJS config file
<script data-main="newApp.require.js" src="bower_components/requirejs/require.js"></script>
newApp.require.js init RequireJS
require([
'app/app.require'
], function (appRequire) {
"use strict";
require.config({
baseUrl: 'app',
paths: appRequire.paths,
shim: appRequire.shim,
deps: [
'NewAppBootstrap'
]
});
});
app.require.js Module / object with paths and shim
define([
'some/others/components.require'
], function (componentsRequire) {
"use strict";
var appRequire = {
'NewApp': 'app.module',
'NewAppBootstrap': 'app.bootstrap',
'NewAppRoute': 'app.routes'
};
var vendorRequire = {
'jQuery': {
exports: '$'
},
'angular': {
exports: 'angular'
}
};
return {
paths: Object.assign(
appRequire,
componentsRequire
),
shim: vendorRequire
};
});
Up to this point everything works fine. Now I would like to file app.require.js load into test.require.js. And the problems begin...
test.require.js
var TEST_REGEXP = /(spec|test)\.js$/i;
var allTestFiles = [];
Object.keys(window.__karma__.files).forEach(function (file) {
'use strict';
if (TEST_REGEXP.test(file)) {
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(normalizedTestModule);
}
});
require([
'app/app.require'
], function (appRequire) {
"use strict";
require.config({
baseUrl: '/base/newApp',
waitSeconds: 200,
paths: appRequire.paths,
shim: appRequire.shim,
deps: allTestFiles,
callback: window.__karma__.start
});
});
Unfortunately, I still have error:
WARN [web-server]: 404: /app/app.require.js
PhantomJS 2.1.1 (Linux 0.0.0) ERROR: 'There is no timestamp for app/app.require.js!'
PhantomJS 2.1.1 (Linux 0.0.0) ERROR
Error: Script error for "app/app.require"
http://requirejs.org/docs/errors.html#scripterror at node_modules/requirejs/require.js:143
I tried different paths, but still nothing. Does anyone know how to load this file? Whether it is at all possible load file in this place? I would be grateful for any tips.
By the time your code hits this require call:
require([
'app/app.require'
],
There is no RequireJS configuration in effect, and data-main is not used, so by default RequireJS takes the directory that contains the HTML page that loads RequireJS as the baseUrl (See the documentation.) Since index.html sits at the root, then RequireJS resolves your module name to /app/app.require.js and does not find it.
You can work around it by using a full path:
require([
'base/newApp/app/app.require'
],
Or if it so happens that other code later is going to try to access this same module as app/app.require, then you should have a minimal configuration before your first require call:
require.config({
baseUrl: '/base/newApp',
});
It is perfectly fine to call require.config multiple times. Subsequent calls will override values that are atomic (like baseUrl) and marge values that can be merged. (An example of the latter would be paths. If the first call sets a paths for the module foo and a 2nd call sets a paths for the module bar, then the resulting configuration will have paths for both foo and bar.)

Why my AngularJS + RequireJS application is not building via grunt-contrib-requirejs?

I have src of my application. I use AngularJS. I use RequireJS as module loader. I use Grunt as task runner. When I run application using src: everything is good. When I build application with Grunt, application is not working. I got no errors in console.
Main thing I noticed: my code (code of my application: app.js and files under js/) does not appear in output file which is set in grunt task settings. Also, I don't think there is something about AngularJS.
Main config file:
require.config({
paths: {
'angular' : '../components/angular/angular',
/* etc..... */
'jquery': '../components/jquery/dist/jquery',
'application': './app'
},
shim: {
/* etc */
application: {
deps: ['angular']
},
angular: {
exports : 'angular'
}
},
baseUrl: '/js'
});
require(['application', 'angular', 'ngRoute', 'bootstrap' /* ngRoute and bootstrap from etc :) */], function (app) {
app.init();
});
My app in app.js is:
define([
'require', 'angular', 'main/main', 'common/common'
], function (require) {
'use strict';
var angular = require('angular');
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
I add main RequireJS config file at the end of body tag:
<script type="text/javascript" src="components/requirejs/require.js" data-main="js/bootstrap.js"></script>
Now I have problem. I have Grunt as build system. I have this task:
grunt.initConfig({
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/bootstrap.js",
name: 'bootstrap',
out: "build/js/bootstrap.js",
optimize: 'none'
}
}
},
// etc
I have no optimisation, so I get ~11k lines of code in output file.
As I said. Main problem is: no AngularJS code and no application code in output file.
Why? I set up mainConfigFile correctly. Problem is in RequireJS config file? But everything is okay, when I am running my app on src.
It would be better if you can provide the exactly error output you get. And where you got it (from browser's console or from terminal during build process)
For now I will suggest some adjustments what could possibly help with your case.
angular: {
exports : 'angular'
}
Here you have already export angular.js into global local variable (inside every require and define block).
And by doing var angular = require('angular'); you are possibly asynchronously override angular variable inside your app.js module.
For 'require' being added into define block, as r.js always reading what module got to be loaded in very first step, and then merged into single file. And this may confuse r.js to merging requireJS into itself.
Suggest this adjustment for your app.js:
define([ // Removed 'require' because no needed , it is already global and usable anywhere
'angular', 'main/main', 'common/common'
], function () {
'use strict';
// var angular = require('angular'); // This is a very common mistake. You are not going to call angular this way, requireJS difference with commonJS.
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
And last but not least data-main="js/bootstrap.js" I think it should be js/main.js or a typo.
EDIT added explanations for 'require' in define block, and angular local variable.

Categories

Resources