unable to specify path to javascript file in Karma - karma-runner

My JS file (which I need to test) is /JasmineTest/src/mySource.js. It has myObj object
myObj={
setA:function(value){
a=value;
},
getA:function(){
return a;
},
};
My Jasmine spec file is /JasmineTest/spec/mySpec.js. It tests myObj
describe("Jasmine sample suite",function(){
it("tracks that spy was called",function(){
expect(myObj.getA).toHaveBeenCalled();
});
In karma, I have specified the spec file location as
files: [
'spec/*.js'
],
when I start Karma in /JasmineTest, the test gives error
Chrome 60.0.3112 (Windows 10 0.0.0) Jasmine sample suite tracks that spy was called FAILED
ReferenceError: myObj is not defined
at UserContext.<anonymous> (spec/mySpec.js:4:9)
I tried exporting myObj module.exports = myObj; and importing it in spec file using require('../src/mySource.js') but I got error require is not defined
How do I make myObj visible in the specs file?

Karma doesn't know how to do module requiring unless you configure it specially to do bundling. In general I would expect to use the same sort of bundling in Karma as you do for your web app, so Webpack or Browserify or similar.
Another way is to list mySource.js in under the "files" field in karma.conf.js, which will just execute it and put myObj in as a global, but that doesn't scale very well.

Related

How can I use my webpack's html-loader imports in Jest tests?

I am just getting started with the Jest test framework and while straight up unit tests work fine, I am having massive issues testing any component that in its module (ES module via babel+webpack) requires a HTML file.
Here is an example:
import './errorHandler.scss';
import template from './errorHandler.tmpl';
class ErrorHandler {
...
I am loading the component specific SCSS file which I have set in Jest's package.json config to return an empty object but when Jest tries to run the import template from './errorHandler.tmpl'; line it breaks saying:
/Users/jannis/Sites/my-app/src/scripts/errorHandler/errorHandler.tmpl.html:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<div class="overlay--top">
^
SyntaxError: Unexpected token <
at transformAndBuildScript (node_modules/jest-runtime/build/transform.js:284:10)
My Jest config from package.json is as follows:
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>/test/setupFile.js",
"moduleDirectories": ["node_modules"],
"moduleFileExtensions": ["js", "json", "html", "scss"],
"moduleNameMapper": {
"^.+\\.scss$": "<rootDir>/test/styleMock.js"
}
}
It seems that the webpack html-loader is not working correctly with Jest but I can't find any solution on how to fix this.
Does anyone know how I can make these html-loader imports work in my tests? They load my lodash template markup and i'd rather not have these at times massive HTML chunks in my .js file so i can omit the import template from x part.
PS: This is not a react project, just plain webpack, babel, es6.
I encountered this specific problem recently and creating your own transform preprocesser will solve it. This was my set up:
package.json
"jest": {
"moduleFileExtensions": [
"js",
"html"
],
"transform": {
"^.+\\.js$": "babel-jest",
"^.+\\.html$": "<rootDir>/test/utils/htmlLoader.js"
}
}
NOTE: babel-jest is normally included by default, but if you specify a custom transform preprocessor, you seem to have to include it manually.
test/utils/htmlLoader.js:
const htmlLoader = require('html-loader');
module.exports = {
process(src, filename, config, options) {
return htmlLoader(src);
}
}
A bit late to the party, but wanted to add that there is also this html-loader-jest npm package out there to do this if you wanted to go that route.
Once you npm install it you will add it to your jest configuration with
"transform": {
"^.+\\.js$": "babel-jest",
"^.+\\.html?$": "html-loader-jest"
}
For Jest > 28.x.x with html-loader:
Create a custom transformer as documented here.
jest/html-loader.js
const htmlLoader = require("html-loader");
module.exports = {
process(sourceText) {
return {
code: `module.exports = ${htmlLoader(sourceText)};`,
};
},
};
Add it to your jest config.
jest.config.js
...
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.html$": "<rootDir>/jest/html-loader.js",
},
...
It will fix the error : Invalid return value: process() or/and processAsync() method of code transformer found at "<PATH>" should return an object or a Promise resolving to an object.
Maybe your own preprocessor file will be the solution:
ScriptPreprocessor
Custom-preprocessors
scriptpreprocessor: The path to a module that provides a synchronous function from pre-processing source files. For example, if you wanted to be able to use a new language feature in your modules or tests that isn't yet supported by node (like, for example, ES6 classes), you might plug in one of many transpilers that compile ES6 to ES5 here.
I created my own preprocessor when I had a problems with my tests after added transform-decorators-legacy to my webpack module loaders.
html-loader-jest doesn't work for me. My workaround for this:
"transform": {
'\\.(html)$': '<rootDir>/htmlTemplateMock.html'
}
htmlTemplateMock.html is empty file
For Jest 28+ you can use jest-html-loader to make Jest work with code that requires HTML files.
npm install --save-dev jest-html-loader
In your jest config, add it as a transformer for .HTML files:
"transform": {
"^.+\\.html?$": "jest-html-loader"
},

Error : You need to include some adapter that implements __karma__.start method

I'm trying to run unit tests using karma and i'm getting the error You need to include some adapter that implements __karma__.start method!. I tried running with grunt and karma start commands. I did googling and all the solutions didn't work out. Not sure what i'm doing wrong. I included the right adapter which comes with karma-jasmine, which has the __karma__.start method, under plugins in karma.conf.js file. Here's my configuration file :-
module.exports = function(config){
config.set({
// root path location that will be used to resolve all relative paths in files and exclude sections
basePath : '../',
files : [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'node_modules/requirejs/require.js',
'node_modules/karma-jasmine/lib/adapter.js',
'app.js',
'mainapp/mainapp.js',
'mainapp/notes/notes.js',
'mainapp/notes/partial/create/create.js',
'mainapp/notes/partial/create/create-spec.js'
],
// files to exclude
exclude: [
'bower_components/angular/angular/*.min.js'
],
// karma has its own autoWatch feature but Grunt watch can also do this
autoWatch : false,
// testing framework, be sure to install the correct karma plugin
frameworks: ['jasmine', 'browserify', 'requirejs'],
// browsers to test against, be sure to install the correct browser launcher plugins
browsers : ['PhantomJS'],
// map of preprocessors that is used mostly for plugins
preprocessors: {
'mainapp/notes/partial/create/create-spec.js' : ['browserify']
},
reporters: ['progress'],
// list of karma plugins
plugins : [
'karma-teamcity-reporter',
'karma-chrome-launcher',
'karma-phantomjs-launcher',
'karma-babel-preprocessor',
'karma-requirejs',
'karma-jasmine',
'karma-browserify'
],
singleRun: true
})}
Using the requirejs framework turns off the automatic calling of __karma__.start. You need to create a file that a) configures RequireJS and b) calls __karma__.start to kick of the tests. Here's an example. It scans through the files that Karma is serving to find the files that contains tests. This is based on a naming convention that any file that ends with spec.js or test.js is a test file. It converts the file names to module names. It then configures RequireJS. One thing it does is pass all the test modules as deps so that all test modules are loaded right away. It sets __karma__.start as callback so that when all modules passed in deps are loaded, the tests start.
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
// Normalize paths to RequireJS module names.
allTestFiles.push(file);
}
});
require.config({
baseUrl: '/base',
paths: {
'chai': 'node_modules/chai/chai',
'chai-jquery': 'node_modules/chai-jquery/chai-jquery',
'jquery': 'node_modules/jquery/dist/jquery.min',
'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min',
'sn/sn-underscore': 'static/scripts/sn/sn-underscore',
'vendor/jquery-ui': '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min'
},
deps: allTestFiles,
callback: window.__karma__.start
});
You need to include this file in your files parameter in your karma.conf.js file. Since you use the requirejs framework, you just need to put it first in the list. For instance, if you call the file test-main.js (as suggested in Karma's documentation):
files: [
'test-main.js',
...
]
If you load RequireJS yourself by listing it in files, you need to put test-main.js after RequireJS.

Karma check build environment

In the karma.conf.js you can set what browsers to use e.g. :
browsers: [
"Chrome",
"Firefox",
"IE"
],
Is there anyway in this configuration file to see what the build environment is e.g. Windows or Linux and then only run the tests in the appropriate browsers.
I basically don't want to have to keep changing the config file for karma every time I switch a OS.
After looking into Node.js I found out that you can get the operating system with the following call:
var os = require("os");
With the os variable I was then able to do simple if/else statements with os.type:
if(os.type()==='Linux')
I would then assign the appropriate browser options to the browser array in the config object.
browsers = ["Chrome","Firefox"];
All this logic was done inside the Karam.conf.js file but before the module.exports = function() call.

How do I run only certain tests in karma?

I have karma config set up correctly, config file, running in the background, just great. As soon as I change and save a file, it reruns the tests.... all 750 of the unit tests. I want to be able to run just a few. Short of manually hacking the config file or commenting out hundreds of tests across many files, is there any easy way to do it?
E.g. when running command line server tests using say mocha, I just use regexp: mocha -g 'only tests that I want'. Makes it much easier to debug and quickly check.
So now I feel foolish. mocha supports a very narrow version of regexp matching.
This runs all tests
describe('all tests',function(){
describe('first tests',function(){
});
describe('second tests',function(){
});
});
This runs just 'first tests'
describe('all tests',function(){
describe.only('first tests',function(){
});
describe('second tests',function(){
});
});
You can also do it.only()
I should have noticed that. Sigh.
You can do that at karma startup time unfortunately, not at runtime.
If you want to change it dynamically you have to put some more effort.
Say you want to focus on a specific set/suite of tests from the beginning, on the karma-mocha plugin page there's this snippet of code to do what you want:
module.exports = function(config) {
config.set({
// karma configuration here
...
// this is a mocha configuration object
client: {
// The pattern string will be passed to mocha
args: ['--grep', '<pattern>'],
...
}
});
};
In order to make the <pattern> parametric you have to wrap the configuration file in a Configurator that will listen CLI and customize the karma configuration for you.
Have a look to this SO answer to know how to setup a very simple Configurator.
I have same question and this is my workround by a little change on karma.conf.js.
In fact, take an argument from command line and modify the pattern in "files".
I use minimist to parse the argument list.
In config file:
/* Begin */
var minimist = require('minimist');
var argv = minimist(process.argv);
var testBase="test/unit";
var testExt=".spec.js";
var unitTestPattern = testBase+'/**/*'+testExt;
if ("test" in argv){
unitTestPattern = testBase+"/"+argv["test"]+testExt;
}
/* End */
module.exports = function(config){
config.set({
//....
files : [
//....
unitTestPattern, //place here
// 'test/unit/**/*.spec.js', //replace this
//....
],
//....
});
};
run in command prompt:
karma start test/karma.conf.js --single-run --test #TEST_CASE_FILE#
a nice extension that can help here is karma-jasmine-html-reporter-livereload
https://www.npmjs.com/package/karma-jasmine-html-reporter-livereload
or karma-jasmine-html-reporter https://www.npmjs.com/package/karma-jasmine-html-reporter?__hstc=72727564.86845f057bb4d741f59d578059e30644.1443860954685.1453095135802.1453138187458.37&__hssc=72727564.1.1453138187458&__hsfp=2285154675
It creates a debug page in which you can run each test individually. very useful for large projects!
1) In your karma.conf.js get the params from the terminal:
var files = (process.env.npm_config_single_file) ? process.env.npm_config_single_file : 'test/test_index.js';
2) In order to run a single test you will need to set an option object with all your configuration (Without files and preprocessors):
var option = {
webpack: {
// webpack configuration
},
// more configuration......
};
3) Set your files path and preprocessors:
option.files = [
{pattern: files, watch: false}
];
option.preprocessors = {};
option.preprocessors[files] = [ 'webpack', 'sourcemap' ];
// call config.set function
config.set(option);
4) Run in the terminal:
npm test --single_file=**/my-specific-file-spec.js
For more information check this PR:
https://github.com/webpack/karma-webpack/pull/178
There are different ways to do it.
Use --grep option. The disadvantage of this is that all the tests are preprocessed before running the specific test suite.
Use .only method. Disadvantage same as no. 1. Using both 1 and 2 method my node process used to crash often saying out of memory.
Limit the files options for processing. This is super fast.
Limit preprocessing to certain folder like Unit or Integration folder.
For this I have used custom cli option --only and in the karma config
const modules = config.only;
and in the the files pattern
files: typeof modules === 'string ? '[`tests/**/${module}/**/*.(test|spec).js`]: 'tests/**/*.(test|spec).js'
Advantage: Developers can run only certain tests when they make a small change way faster by limiting in the preprocessing phase.
You can also use combination of no.3 and no.1 or 2.

Concatenating and minifying RequireJS with Grunt

I have a project written in CoffeeScript that uses AngularJS. My vendor dependancies are installed using Bower and my file structure is like this:
- assets
- js
- app
- model
- *.coffee
- factory
- *.coffee
...
- app.coffee
- config.coffee
- routes.cofeee
- vendor
- angular
- lodash
...
- dist
What I'm trying to do is the following:
I'm trying to work out how I can use RequireJS's r.js to optimise my app files so that I essentially get a concatenated file all ordered nice (so vendor dependancies, my config and routes, and they my app files).
Integrate this into my Grunt file.
I've tried using the r.js optimiser but maybe I've being too silly as all it seems to do is copy my app files (minus the vendor dependancies) into the dist folder; it does, however, manage to optimise the coffee generated js files.
Has anyone got any experience with this?
I figured it out: r.js works by reading your mainConfigFile and any modules you name within your configuration, the important note here is that r.js only looks at the first require/define within your named modules and goes off to seek them; so, for example, I had one named module called app:
require ['config'], (cfg) ->
require ['angular'], (A) ->
A.module cfg.ngApp, []
require ['routes'], () ->
require [
'factory/a-factory',
'service/a-service',
'controller/a-controller'
], () ->
A.bootstrap document, [cfg.ngApp]
The problem here was that r.js never got past the first require statement and thus the concatenation wasn't working. When I changed this to, say (my app.coffee):
require ['config'], (cfg) ->
require ['angular'], (A) ->
A.module cfg.ngApp, []
require ['bootstrap'], (bootstrap) ->
bootstrap()
And my bootstrap.coffee:
define [
'config',
'angular',
'routes',
'factory/a-factory',
'service/a-service',
'controller/a-controller'
], (cfg, A, routes) ->
class Bootstrap
constructor: () ->
routes()
A.bootstrap document, [cfg.ngApp]
This meant that I only needed to define angular and bootstrap in my r.js configuration as includes and then r.js would do the rest, like so:
baseUrl: 'assets/js/app',
mainConfigFile: 'assets/js/app/config.js',
name: 'app',
include: [
'../vendor/requirejs/require',
'bootstrap'
],
out: 'assets/js/dist/app.js'
And now it all works fine! ~~It's a shame that I have to tell r.js to include requirejs though, maybe I've done something silly there?~~
Blimey, I'm such a dingus!
So in my HTML I was loading my concatenated script as:
<script src="assets/js/dist/app.js"></script>
When really it should be loaded like this:
<script src="assets/js/vendor/requirejs/require.js" data-main="assets/js/dist/app"></script>
D'oh!
From r.js doc
https://github.com/jrburke/r.js/blob/master/build/example.build.js#L322
Nested dependencies can be bundled in requireJS > v1.0.3
//Finds require() dependencies inside a require() or define call. By default
//this value is false, because those resources should be considered dynamic/runtime
//calls. However, for some optimization scenarios, it is desirable to
//include them in the build.
//Introduced in 1.0.3. Previous versions incorrectly found the nested calls
//by default.
findNestedDependencies: false,