Please, find below my karma.conf.js:
module.exports = function (config) {
var path = require("path"),
basePath = "../../../../../..",
version = require(path.join(__dirname, basePath, "ThirdPartyJsLibRootMap.json"));
config.set({
basePath: basePath,
frameworks: ['intern'],
files: [
'Tools/Karma/dojo-config.js',
'http://devstatic/libraries/dojo-debug/' + version['dojo-debug'] + '/dojo/dojo.js',
...
],
reporters: ['progress', 'html'],
htmlReporter: {
outputFile: 'Tools/Karma/HRunitTestReport.html'
},
port: 9876,
colors: false,
logLevel: config.LOG_INFO,
autoWatch: false,
browsers: ['Chrome'],
captureTimeout: 60000,
browserNoActivityTimeout: 60000,
singleRun: true
});
};
The files section specifies a few files, but the most important ones are:
Tools/Karma/dojo-config.js
http://devstatic/libraries/dojo-debug/' + version['dojo-debug'] + '/dojo/dojo.js
Notice, that all the 3rd party dependencies are served from a dedicated server - devstatic. It is sort of an internal CDN. The 3rd party content is also versioned and the versions used by the current revision of our application are stored in a dedicated json file - ThirdPartyJsLibRootMap.json, which is part of the application.
The aforementioned karma.conf.js works, but I am not sure if I am doing it correctly. I have a feeling that the files section is not intended to hold http urls. And I am pretty sure it will not work with expressions like **.
But there is more. Tools/Karma/dojo-config.js is responsible for common configuration and it looks like this:
window.__karma__.loaded = function () { };
setImmediate = function (fn) { fn(); };
dojoConfig = {
baseUrl: "http://devstatic/libraries/dojo-debug/1.10.4",
async: true,
useDeferredInstrumentation: true,
has: {
'host-karma': true
},
paths: {
'Globalization': 'http://devstatic/libraries/globalization-debug/0.1.1',
'put-selector': 'http://devstatic/libraries/put-selector/0.3.5',
'dgrid': 'http://devstatic/libraries/dgrid/0.3.16',
'xstyle': 'http://devstatic/libraries/xstyle/0.1.3',
'toastr': 'http://devstatic/libraries/toastr/1.2.2-514cc12',
...
},
packages:
[
{
name: "jquery",
location: "http://devstatic/libraries/jquery/1.8.3",
main: "jquery"
},
{
name: "jquerybase64",
location: "http://devstatic/libraries/jquery/1.8.3",
main: "jquery.base64"
},
{
name: "dojo",
location: "http://devstatic/libraries/dojo-debug/1.10.4/dojo"
},
{
name: "dijit",
location: "http://devstatic/libraries/dojo-debug/1.10.4/dijit"
},
{
name: "dojox",
location: "http://devstatic/libraries/dojo-debug/1.10.4/dojox"
},
{
name: "intern",
location: "/base/Tools/Intern/node_modules/intern"
},
{
name: "karma",
location: "/base/Tools/Karma"
}
],
deps: null,
callback: null
};
And here I am in a major problem. How do I communicate the 3rd party versions loaded from ThirdPartyJsLibRootMap.json over to Tools/Karma/dojo-config.js?
I mean, those versions are known at the time karma.conf.js is processed and if I could inject them into the html page generated by karma then I could reference them from Tools/Karma/dojo-config.js and compose the right third party URLs. Alas, so far found no way to do it.
So, basically I have the following questions:
What is the right way to refer to http URLs in karma.conf.js? The answer could be - don't, but then how do I make it loaded by the generated html page?
If loading http URLs the way I do it in karma.conf.js is fine, then how to specify expressions like **?
How to make the information computed in karma.conf.js available to the javascript loaded in the browser? This information is known at the "node time" (when karma.conf.js is run), but I am puzzled as to how to make it available at the "browser time".
Related
I created an Aurelia app using the Aurelia CLI (au new) and would like to set up code coverage (preferably with karma-coverage, but if that's not possible I'll use whatever).
I first npm install karma-coverage --save-dev then copy the test.js task over to a cover.js (so that I can run au cover).
cover.js
import {Server as Karma} from 'karma';
import {CLIOptions} from 'aurelia-cli';
// import project from "../aurelia.json";
export function cover(done) {
new Karma({
// This is the same as what's in karma.conf.js after running
// Except I added the 'src\\**\\*.js' part
files: [
'scripts\\vendor-bundle.js',
{pattern: 'test\\unit\\**\\*.js', included: false},
'test/aurelia-karma.js',
'scripts\\app-bundle.js',
'scripts\\materialize-bundle.js',
{pattern: 'src\\**\\*.js', included: false}
],
configFile: __dirname + '/../../karma.conf.js',
singleRun: !CLIOptions.hasFlag('watch'),
reporters: ['progress', 'coverage'],
//logLevel: 'debug',
preprocessors: {
// [project.unitTestRunner.source]: [project.transpiler.id], // Is this actually needed? Nothing changes if I add or remove this...
'src/**/*.js': ['babel', 'coverage']
},
coverageReporter: {
includeAllSources: true,
reporters: [
{type: 'html', dir: 'coverage'},
{type: 'text'}
]
}
}, done).start();
}
export default cover;
This... gets me somewhere?
But I don't think the tests are being linked to the individual src files (they're instead being linked to app-bundle.js).
Is there any way to get code coverage at the src file level (i.e. not bundle level) for an Aurelia app?
Other Files of Interest
app.js
export class App {
constructor() {
this.message = 'Hello World!';
}
}
karma.conf.js
"use strict";
const path = require('path');
const project = require('./aurelia_project/aurelia.json');
let testSrc = [
{ pattern: project.unitTestRunner.source, included: false },
'test/aurelia-karma.js'
];
let output = project.platform.output;
let appSrc = project.build.bundles.map(x => path.join(output, x.name));
let entryIndex = appSrc.indexOf(path.join(output, project.build.loader.configTarget));
let entryBundle = appSrc.splice(entryIndex, 1)[0];
let files = [entryBundle].concat(testSrc).concat(appSrc); console.log(files);
module.exports = function(config) {
config.set({
basePath: '',
frameworks: [project.testFramework.id],
files: files,
exclude: [],
preprocessors: {
[project.unitTestRunner.source]: [project.transpiler.id]
},
'babelPreprocessor': { options: project.transpiler.options },
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
// client.args must be a array of string.
// Leave 'aurelia-root', project.paths.root in this order so we can find
// the root of the aurelia project.
client: {
args: ['aurelia-root', project.paths.root]
}
});
};
From your terminal screenshot, it looks like you're getting coverage on your individual files src/app.js (12.5%), src/environment.js (0%) and src/main.js (12.5%) in addition to the bundle file. It has to do with that extra line you added including the files that are in src/.
I suspect that if you go to your coverage directory and view that in the browser, you'll see more detailed results.
I'm not sure about the Aurelia app specifically, but to get individual file coverage, you want to have your tests run against your original files instead of the bundled version and you want that same path to your original files to also be in preprocessors.
files: [
...
'src/**/*.js',
'path/to/tests/*.spec.js'
...
],
preprocessors: {
'src/**/*.js': ['coverage']
}
This pattern (which you've done) is what tells Karma to load the individual files, and include them in the coverage. At this point, I wouldn't bother loading the bundled application code since you have all the raw stuff being loaded.
Also be sure to load them in the order they're probably bundled in so that you can be sure Karma can initialize the app in the first place.
files: [
'src/app.js',
'src/environment.js',
'src/main.js'
]
And if you are putting your specs named myTest.spec.js next to the files they test, you can use src/!(*.spec).js to make sure that you are including application code without the tests.
I finally got it working, but it required a lot of under-the-hood modifications =\
First I had to install the following packages
npm install karma-coverage --save-dev
npm install karma-requirejs --save-dev
npm install babel-plugin-istanbul --save-dev
aurelia_project/tasks/cover.js
This is based on test.js.
Add requirejs to the list of frameworks (from the karma-requirejs package)
Add the individual src/test files (with included: false) as well as test/aurelia-karma-cover.js, which does the actual requireing of test files.
Modified aurelia-karma.js into aurelia-karma-cover.js to not include /test/unit/setup.js (it was giving me trouble with aurelia-browser-pal dependencies)
Remove "coverage" from src file preprocessing (babel-plugin-istanbul will now handle instrumentation of code - see statement at the end for details).
import {Server as Karma} from 'karma';
import {CLIOptions} from 'aurelia-cli';
import project from "../aurelia.json";
export function cover(done) {
new Karma({
configFile: __dirname + '/../../karma.conf.js',
frameworks: [project.testFramework.id, 'requirejs'],
files: [
{pattern: 'src\\**\\*.js', included: false},
{pattern: 'test\\unit\\**\\*.js', included: false},
// This file actually loads the spec files via require - it's based on aurelia-karma.js
// but removes setup.js and its dependencies
'test/aurelia-karma-cover.js'
],
exclude: [
'src/environment.js',
'src/main.js',
'src/resources/index.js'
],
preprocessors: {
'src/**/*.js': ['babel'],
},
reporters: ['progress', 'coverage'],
singleRun: !CLIOptions.hasFlag('watch'),
coverageReporter: {
includeAllSources: true,
reporters: [
{type: 'html', dir: 'coverage'},
{type: 'text'}
]
}
}, done).start();
}
export default cover;
test/unit/aurelia-karma-cover.js
Just change var allTestFiles = ['/base/test/unit/setup.js']; to var allTestFiles = []; to avoid aurelia-pal-browser dependency errors.
aurelia_project/aurelia.json
Add "istanbul" to the transpiler.options.plugins list if using babel-plugin-istanbul.
* Without babel-plugin-istanbul, code coverage works on post-transpiled code, which adds boilerplate that can't really be tested. This allows you to get to 100% code coverage ;)
#lebolo These were the adjustments I had to make to get things working here:
aurelia_project/tasks/cover.js
Included a constant to vendors path in the beginning of cover function:
const VENDORS_PATH = __dirname + '/../../node_modules/';
Used VENDORS_PATH to include libs I depend on, in files:
files: [
{pattern: VENDORS_PATH + 'moment/min/moment.min.js', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'test/unit/**/*.js', included: false},
'test/aurelia-karma-cover.js'
]
Included test files in preprocessors:
preprocessors: {
'src/**/*.js': ['babel'],
'test/unit/**/*.js': ['babel']
}
test/unit/aurelia-karma-cover.js
Change the way url is built in requirejs.load function:
url = '/base/' + url; => url = ['/base', root, url].join('/');
Included one more function to config requirejs paths and calling it after patchRequireJS() call:
function configRequire() {
var VENDORS_PATH = '../node_modules/';
requirejs.config({
paths: {
'moment': VENDORS_PATH + 'moment/min/moment.min'
}
});
}
I am switching my SPA web app from Durandal to Aurelia and am now taking a look at the bundling process.
I use gulp to bundle the app and I followed the instructions on this Aurelia documentation page and other resources on the web. It works but there are some unclear things to me.
This is my gulpfile.js
var gulp = require('gulp'),
bundler = require('aurelia-bundler'),
uglify = require('gulp-uglify'),
htmlmin = require('gulp-htmlmin'),
del = require('del');
var config = {
force: true,
baseURL: '.', // baseURL of the application
configPath: './config.js', // config.js file. Must be within `baseURL`
bundles: {
"Scripts/build/app-build": { // bundle name/path. Must be within `baseURL`. Final path is: `baseURL/dist/app-build.js`.
includes: [
'[Scripts/app/**/*.js]',
'Scripts/app/**/*.html!text',
'Content/*.css!text'
],
options: {
inject: true,
minify: true,
depCache: true,
rev: true
}
},
"Scripts/build/vendor-build": {
includes: [
'jspm_packages/npm/babel-runtime#5.8.38/helpers/class-call-check.js',
'jspm_packages/npm/babel-runtime#5.8.38/helpers/create-class.js',
'jspm_packages/npm/babel-runtime#5.8.38/core-js/object/define-property.js',
'jspm_packages/npm/core-js#1.2.7/library/fn/object/define-property.js',
'jspm_packages/npm/babel-runtime#5.8.38/core-js/object/define-properties.js',
'jspm_packages/npm/core-js#1.2.7/library/fn/object/define-properties.js',
'npm:aurelia-framework#1.0.7',
'npm:aurelia-loader-default#1.0.0',
'npm:aurelia-logging-console#1.0.0',
'npm:aurelia-templating-binding#1.0.0',
'npm:aurelia-templating-resources#1.1.1',
'npm:aurelia-templating-router#1.0.0',
'npm:aurelia-knockout#1.0.2',
'npm:aurelia-history-browser#1.0.0',
'npm:aurelia-bootstrapper#1.0.0',
'npm:aurelia-fetch-client#1.0.1',
'npm:aurelia-router#1.0.6',
'npm:aurelia-animator-css#1.0.1',
'npm:babel-core#5.8.38',
'npm:babel-runtime#5.8.38',
'npm:core-js#1.2.7',
'github:systemjs/plugin-text#0.0.9'
],
options: {
inject: true,
minify: true,
depCache: true,
rev: true
}
}
}
};
gulp.task('build', ['minify'], function () {
return bundler.bundle(config);
});
And this is config.js
System.config({
baseURL: "/",
defaultJSExtensions: true,
transpiler: "babel",
babelOptions: {
"optional": [
"es7.decorators",
"es7.classProperties",
"runtime"
],
"compact": true
},
paths: {
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*"
},
bundles: {
},
map: //some mappings
}
If I run the gulp task to bundle, it works, and I can load my app using the bundled files, but there are some things I don't understand:
After the bundled files are created, the config.js file is updated within the "bundles:" property with the files which have been created for the bundle, and a random versioning number (since I had set 'rev: true' in the options)
bundles: [
"Scripts/build/vendor-build-4c2789cace.js": [
//list of files
]
]
When I run the task again, maybe after some changes, the new bundled file has been added to the config.js file like that:
bundles: [
"Scripts/build/vendor-build-4c2789cace.js": [
//list of files
],
"Scripts/build/vendor-build-t67uj8e5f4.js": [
//list of files
]
]
but as you can see, the old one is still there. How do I tell him to "clear" the bundles property when I create a new bundle?
I am attempting to add Code Coverage for my Karma tests, however although it finds the correct JS files that I'm testing, it does not find the functions inside those files.
From what I have read so far I believe it to be to do with the files not being correctly browserified before being passed to istanbul to do the coverage, but admittedly I am new to this so I'm hoping for some suggestions.
Here is my JS file(common.js):
var applicationSettings = require('./settings');
var common = {
getAjaxBaseUrl: function () {
var strVirtualDirectory = applicationSettings.VirtualDirectory;
if (strVirtualDirectory.length > 1) {
if (!strVirtualDirectory.startsWith("/")) {
strVirtualDirectory = "/" + strVirtualDirectory;
}
}
return strVirtualDirectory;
}
}
module.exports = common;
And here are the tests I have written:
it('Client - Should get correct AjaxBaseUrl with /', function () {
var clientSettings = require('./../client/scripts/settings');
var clientCommon = require('./../client/scripts/common');
clientSettings.VirtualDirectory = '/VD';
expect(clientCommon.getAjaxBaseUrl()).to.equal('/VD');
});
it('Client - Should get correct AjaxBaseUrl without /', function () {
var clientSettings = require('./../client/scripts/settings');
var clientCommon = require('./../client/scripts/common');
clientSettings.VirtualDirectory = 'VD';
expect(clientCommon.getAjaxBaseUrl()).to.equal('/VD');
});
My Karma.conf is below:
// Karma configuration
// Generated on Mon Jan 11 2016 09:43:00 GMT+0000 (GMT 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: ['phantomjs-shim', 'browserify', 'mocha'],
// list of files / patterns to load in the browser
files: [
'https://code.jquery.com/jquery-2.2.0.min.js',
'http://cdn.kendostatic.com/2015.3.1111/js/kendo.all.min.js',
'test_unit/*Spec.js',
'client/scripts/*.js'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test_unit/*Spec.js': ['browserify'],
'client/scripts/*.js': ['browserify', 'coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'coverage', 'junit'],
// Configure jUnit reporter
junitReporter: {
outputDir: '', // results will be saved as $outputDir/$browserName.xml
outputFile: undefined, // if included, results will be saved as $outputDir/$browserName/$outputFile
suite: '', // suite will become the package name attribute in xml testsuite element
useBrowserName: true // add browser name to report and classes names
},
// Configure coverage reporter
coverageReporter: {
type: 'html',
dir: 'test_coverage',
subdir: '.',
file: 'coverage.htm'
},
// 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: false,
browserify: {
configure: function (bundle) {
bundle.transform('reactify', { extensions: ['.jsx'] });
}
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity,
})
}
This does produce a report, but this shows 100% and the only line found in the common.js file is:
require("C:\\Source\\ProjectName\\client\\scripts\\common.js");
I attempted to add Browerify-Istanbul into the mix, by adding a require for it at the top of the Karma.conf an additional transform in the browserify section
bundle.transform(istanbul)
However this just makes my tests fail and throw several errors:
undefined is not an object (evaluating
'__cov_qQLFhXEMt7fatxiMx0_vQQ.b[' 1'][0]')
getAjaxBaseUrl#C:/Users/CHARLE~1.WIC/AppData/Local/Temp/0d61da722d2838c9
600d83d1cbb4c0b8.browserify:43:1498
C:/Users/CHARLE~1.WIC/AppData/Local/Temp/0d61da722d2838c9600d83d1cbb4c0b
8.browserify:51742:1849
16 02 2016 09:14:08.515:ERROR [coverage]: [TypeError: Cannot read
property 'star t' of undefined] TypeError: Cannot read property
'start' of undefined
at C:\Source\ProjectName\node_modules\istanbul\lib\o bject-utils.js:59:44
at Array.forEach (native)
at Object.addDerivedInfoForFile (C:\Source\ProjectName\node_modules\istanbul\lib\object-utils.js:58:37)
at Object.Collector.fileCoverageFor (C:\Source\ProjectName\node_modules\istanbul\lib\collector.js:94:15)
at C:\Source\ProjectName\node_modules\istanbul\lib\r eport\html.js:558:90
at Array.forEach (native)
at HtmlReport.Report.mix.writeReport (C:\Source\ProjectName\node_modules\istanbul\lib\report\html.js:557:27)
at writeReport (C:\Source\ProjectName\node_modules\k arma-coverage\lib\reporter.js:62:16)
at C:\Source\ProjectName\node_modules\karma-coverage \lib\reporter.js:288:11
at C:\Source\ProjectName\node_modules\karma\lib\help er.js:82:7
at FSReqWrap.oncomplete (fs.js:82:15)
Am I missing something, or going about this the wrong way?
I had the exact same issue. What worked for me was removing "coverage" from the preprocessors section AND using browserify-istanbul. Also, you want to configure browserify-istanbul to ignore your test files.
So your preprocessors should look something like (removed 'coverage'):
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test_unit/*Spec.js': ['browserify'],
'client/scripts/*.js': ['browserify']
},
And your browserify config should look something like:
browserify: {
configure: function (bundle) {
bundle.transform('reactify', { extensions: ['.jsx'] });
bundle.transform(require('browserify-istanbul')({
ignore: ['**/test_unit/**']
}));
}
},
Hope that helps
For me the solution was to:
delete 'coverage' in the array of values of *.js files from preprocessors index into karma.conf
Hope that helps
Here's my config file:
module.exports = function(config) {
config.set({
basePath: './',
autoWatch: true,
frameworks: ['jasmine'],
files: [
'../public_html/libs/mylib/lib.js',
'../public_html/libs/mylib/utility.js',
'../public_html/libs/mylib/config/*.js',
'../public_html/libs/mylib/enumerations.js',
'../public_html/libs/mylib/apiComm.js',
'../public_html/libs/mylib/baseObject.js',
'../public_html/libs/mylib/book.js',
'../public_html/libs/mylib/file.js',
'../public_html/libs/mylib/library.js',
'../public_html/libs/mylib/publishing.js',
'../public_html/libs/mylib/topic.js',
'../test/*Spec.js'
],
reporters: ['progress', 'coverage'],
preprocessors: {
'../public_html/libs/mylib/topic.js': ['coverage']
},
port: 9876,
colors: true,
browsers: ['Chrome'],
captureTimeout: 60000,
singleRun: false
});
};
Whenever I run karma start config/karma.config.js it runs the unit tests and creates the coverage folder in the correct place. However, it dumps the topic.js.html file in the same directory as topic.js. Why?
I know this is late reply, but it might help someone else as well.
Move your config file up one folder and change the "../public_html" to "public_html"
I just got done dealing with this.
Yes you need to have the...
coverageReporter: {
type : 'html',
dir : 'coverage/'
}
...as Acosta stated, but your also instructing Istanbul when writing the report files to go up one directory and then into the public_html folder. If the the karma.conf.js file is located in the same folder as the app and test folders then your reports should render in the proper location.
you need to add coverageReporter,
coverageReporter: {
type : 'html',
dir : 'coverage/'
}
O you also may add a collection of reporters
coverageReporter: {
reporters: [
//{ type: 'html', dir: 'TestOutputFolder/' },
{ type: 'text-summary', dir: 'TestOutputFolder/' },
{ type: 'cobertura', dir: 'TestOutputFolder/' }
]
},
Is there a way to generate pages from json/yaml if you provide a layout? I thought this was possible, but can't find in docs.
This is currently being tracked here in GitHub: http://webb.li/QjTX
Since the options.pages feature has been implemented, you can add pages like this:
options: {
pages: {
"about": {
"data": {
"title": "About"
},
"content": "Content for {{title}}"
},
...
}
}
We aren't supporting automatic loading of a json/yml file, but you can do this inside your Gruntfile and add the object to options.pages that way...
module.exports = function(grunt) {
grunt.initConfig({
// package.json
pkg: grunt.file.readJSON('package.json'),
assemble: {
options: {
flatten: true,
layoutdir: 'src/layouts',
assets: 'dest/assets'
},
blog: {
options: {
engine: 'handlebars',
layout: 'post.hbs',
site: {
title: 'This is my Blog'
},
pages: grunt.file.readJSON('pages.json')
},
files: { 'dest/': ['src/index.hbs'] }
}
}
});
// Load npm plugins to provide necessary tasks.
grunt.loadNpmTasks('assemble');
// Default task.
grunt.registerTask('default', ['assemble']);
};
This example uses the post.hbs file as the layout for any pages defined in the pages.json file. It will also build a page for the index.hbs specified in the files src array. Right now, the files dest/src is required so Assemble knows where to write the files, but I think we'll add that to the options, or the page object so it can be run without the files object.