NOTE: This is especially going to help someone that runs karma.conf.js through WebStorm.
The Question:
I was trying to run some tests on a directive, but since it kept coming back, "TypeError: 'undefined' is not a function" I started to narrow down where the problem might be. THE RESULT: It seems to be reading the template, but it's like the div's on the template are empty.
My Test
describe("Main Component Directive", function() {
var $scope, $element, directiveTemplate;
beforeEach(angular.mock.module('app'))
beforeEach(inject(function ($rootScope, $compile) {
$scope = $rootScope.$new();
directiveTemplate = '<gk-directive></gk-directive>';
$element = $compile(directiveTemplate)($scope);
$scope.$digest();
}));
it("should show reset button when current time is <5s under session/breakLength", function() {
var button = $element.find(".button")
expect(button).toBeDefined();
console.log(button.text())
});
The test passes, but it won't console.log the text inside this template: container.html
<div>
<div class="button">Button Here</div>
</div>
Could there be something wrong with my directive..?
module.exports = angular.module('app')
.directive('gkTimerDirective', function () {
return {
template: require('./container.html') //gets turned into a string by webpack's raw-loader
}
});
I've been looking through stackOverflow and there are plenty of examples of testing different kinds of directives and their behaviors, but it seems like I'm missing something basic, here. I do have jQuery added before AngularJs or Angular-Mocks.
I feel like I'm missing something fundamental. If I console.log as I am, shouldn't I get, "Button Here", instead of two single quotes: " '' "?
UPDATE
To clarify, the key problem here is that AngularJs is not traversing the DOM with $element.find() when it looks for a class, despite the fact that jQuery really should be loaded.
Explanation of What Happened
When I tried running the tests with an npm run command, I noticed that all of the tests were passing, but when I ran the tests through WebStorm's "Run karma.conf.js" it acted like jQuery hadn't been loaded before Angular and, therefore, it couldn't find the div by its class, and the final test, where I used angular.element with jQuery pre-loaded, continued to fail.
I can only think that this is because jQuery isn't easily assimilated by the build system. In fact, you can see how Webpack had to do some extra work after it got to jQuery.
Hash: d234b2da503b21a23772
Version: webpack 1.12.9
Time: 395ms
Asset Size Chunks Chunk Names
main 2.77 MB 0, 1 [emitted] main
src/app.module.spec.js 2.77 MB 1, 0 [emitted] src/app.module.spec.js
chunk {0} main (main) 2.56 MB [rendered]
[0] ./src/app.module.spec.js 273 bytes {0} {1}
[1] ./src/jquery.js 1.38 MB {0} {1}
[2] (webpack)/buildin/module.js 251 bytes {0} {1}
[3] (webpack)/buildin/amd-options.js 43 bytes {0} {1}
[4] ./src/app.module.js 2.42 kB {0} {1}
[5] ./~/angular/index.js 48 bytes {0} {1}
[6] ./~/angular/angular.js 1.07 MB {0} {1}
[7] ./src/factories/index.js 1.03 kB {0} {1}
[8] ./src/factories/timer.service.js 9.16 kB {0} {1}
[9] ./src/timer/index.js 1.26 kB {0} {1}
[10] ./src/timer/gkTimer.directive.js 1.82 kB {0} {1}
[11] ./src/timer/gk-timer-container.html 144 bytes {0} {1}
[12] ./src/timer/timerContainerCtrl.controller.js 4.12 kB {0} {1}
[13] ./~/angular-mocks/angular-mocks.js 82.8 kB {0} {1}
[14] ./src/tests/timer.service.spec.js 3.27 kB {0} {1}
[15] ./src/tests/timer.spec.js 1.15 kB {0} {1} [built]
I'm still uncertain why this was a problem, but it did inspire a solution.
The Solution
Since I'm only using jQuery for testing directives, I just added it to my files list on Karma:
var path = require('path');
var _ = require('lodash');
var here = require('path-here');
// SET-UP SETTINGS
process.env.NODE_ENV = process.env.NODE_ENV || 'test';
// coverage always true when testing (for me)
var coverage = true // = process.env.COVERAGE === 'true';
// ci determines if autowatch and single run will be true or false
var ci = process.env.NODE_ENV === 'test:ci';
if (coverage) {
console.log('-- recording coverage --');
}
// all runs after the NODE_ENV has been set to 'test'
var webpackConfig = require('./webpack.config');
var entry = path.join(webpackConfig.context, webpackConfig.entry);
var preprocessors = {};
preprocessors[entry] = ['webpack'];
// SETTINGS INPUT
module.exports = function(config) {
config.set({
basePath: './',
frameworks: ['jasmine'],
files: [
'./node_modules/jquery/dist/jquery.js', // <-- Added it right here
entry
],
exclude: [],
preprocessors: preprocessors,
reporters: getReporters(),
webpack: webpackConfig,
webpackMiddleware: {noInfo: true},
coverageReporter: {
reporters: [
{type: 'lcov', dir: 'coverage/', subdir: '.'},
{type: 'json', dir: 'coverage/', subdir: '.'},
{type: 'text-summary'}
]
},
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: !ci,
browsers: ['PhantomJS'],
singleRun: ci,
browserNoActivityTimeout: 180000,
plugins: [
'karma-webpack',
'karma-jasmine',
'karma-coverage',
'karma-phantomjs-launcher'
]
});
};
function getReporters() {
var reps = ['progress'];
if (coverage) {
reps.push('coverage');
}
return reps;
}
Now, for testing, jQuery is loaded first and Angular can search for DOM elements by class, id, or whatever selectors jQuery allows.
But what about for production?
I have no reason to think it might not be working just fine for a production build in my case, but if that's your problem try this: https://github.com/webpack/webpack/issues/582