Handle WebPack CSS imports when testing with Mocha and Babel - javascript

When testing .js files that have Webpack CSS imports like import './style.css', Mocha throws a syntax error (because it tries to import and parse the CSS file as JS). There is a solution for this that has already been posted on Stack Overflow, but it only addresses if you aren't already using a compiler with Mocha. I'm using Babel 5. I've tried the following, but it seems that Mocha doesn't support passing multiple compilers:
// npm test script
mocha ./src/**/*Test.js --compilers css:./scripts/mocha-webpack-compiler.js js:babel/register
// scripts/mocha-webpack-compiler.js
function noop() {
return null;
}
require.extensions['.css'] = noop;
Is there a way to have multiple Mocha compilers, or a better way to tell Mocha not to try to parse Webpack CSS imports?
EDIT:
I like the proposed solution by #Giles B below; it was exactly what I needed. However, since I am still on Babel 5 I needed a few tweaks as shown below:
mocha.opts
--require scripts/support/babelhook
--require scripts/support/mocha-webpack-compiler
scripts/babelhook.js
require('babel/register');
scripts/mocha-webpack-compiler.js
function noop() {
return null;
}
require.extensions['.css'] = noop;
mocha script
mocha ./src/**/*Test.js
This is working for me using babel and babel-core, both version 5.8.23.

I came across the same problem and just got it working, so in my mocha.opts file I added the following require calls:
--require test/babelhook
--require test/css-null-compiler
In babelhook.js I have one require statement to load babel:
// This file is required in mocha.opts
// The only purpose of this file is to ensure
// the babel transpiler is activated prior to any
// test code, and using the same babel options
require("babel-register")();
Then from the link you provided I setup css-null-compiler.js as follows:
// Prevent Mocha from compiling class
function noop() {
return null;
}
require.extensions['.css'] = noop;
require.extensions['.scss'] = noop;
Hope that helps.

Based on #Giles' answer above, this is what I used to get working on Babel 6
package.json
"scripts": {
"test": "mocha --compilers js:babel-core/register
--require ./tools/testHelper.js 'src/**/*-spec.#(js|jsx)'",
tools/testHelper.js
// Prevent mocha from interpreting CSS #import files
function noop() {
return null;
}
require.extensions['.css'] = noop;
This enables you to have your tests inside your src folder alongside your components.

Related

Jest command not recognized

So i just started learning about Test Driven Developement and as an example i was asked to run the command npm test helloWorld.spec.js in the terminal but i got this error :
> javascript-exercises#1.0.0 test
> jest "helloWorld.spec.js"
'jest' n’est pas reconnu en tant que commande interne
ou externe, un programme exécutable ou un fichier de commandes.
// in english jest isn't recognized as an internal command or external
I'm working on windows and the only thing i have installed is node so what do i have to do?
Choose one of the following methods
1) Install globally
You need to install jest globally:
npm install jest -g
Note: You will have to call it as jest something.spec.js in your cli or specify a test command in your package.json.
2) Install locally
Install jest locally with npm install jest -D.
You can use a script in your package.json called test which would be "test": "jest".
If any of the above don't work, try reinstalling jest.
If it still doesn't work, try removing node_modules and npm cache clean --force and npm install
3) Config file
If you already have jest installed but it's not working, you can use a config file to track files based on regex pattern (you can do a lot more if you check out the docs).
The following part is from the docs:
Jest's configuration can be defined in the package.json file of your project, or through a jest.config.js, or jest.config.ts file or through the --config <path/to/file.js|ts|cjs|mjs|json> option. If you'd like to use your package.json to store Jest's config, the "jest" key should be used on the top level so Jest will know how to find your settings:
{
"name": "my-project",
"jest": {
"verbose": true
}
}
Or through JavaScript:
// Sync object
/** #type {import('#jest/types').Config.InitialOptions} */
const config = {
verbose: true,
};
module.exports = config;
// Or async function
module.exports = async () => {
return {
verbose: true,
};
};
Or through TypeScript (if ts-node is installed):
import type {Config} from '#jest/types';
// Sync object
const config: Config.InitialOptions = {
verbose: true,
};
export default config;
// Or async function
export default async (): Promise<Config.InitialOptions> => {
return {
verbose: true,
};
};
When using the --config option, the JSON file must not contain a "jest" key:
{
"bail": 1,
"verbose": true
}
Regex options
testMatch [array]
(default: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ])
The glob patterns Jest uses to detect test files. By default it looks for .js, .jsx, .ts and .tsx files inside of __tests__ folders, as well as any files with a suffix of .test or .spec (e.g. Component.test.js or Component.spec.js). It will also find files called test.js or spec.js.
Note: Each glob pattern is applied in the order they are specified in the config. (For example ["!**/__fixtures__/**", "**/__tests__/**/*.js"] will not exclude __fixtures__ because the negation is overwritten with the second pattern. In order to make the negated glob work in this example it has to come after **/__tests__/**/*.js.)
testRegex [string | array]
Default: (/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$
The pattern or patterns Jest uses to detect test files. By default it looks for .js, .jsx, .ts and .tsx files inside of \_\_tests\_\_ folders, as well as any files with a suffix of .test or .spec (e.g. Component.test.js or Component.spec.js). It will also find files called test.js or spec.js. See also testMatch [array], but note that you cannot specify both options.

Is it possible to use ES6 modules in Mocha tests?

ES6, Windows 10 x64, Node.js 8.6.0, Mocha 3.5.3
Is it possible to use ES6 modules in Mocha tests? I have the problems with export and import keywords.
/* eventEmitter.js
*/
/* Event emitter. */
export default class EventEmitter{
constructor(){
const subscriptions = new Map();
Object.defineProperty(this, 'subscriptions', {
enumerable: false,
configurable: false,
get: function(){
return subscriptions;
}
});
}
/* Add the event listener.
* #eventName - the event name.
* #listener - the listener.
*/
addListener(eventName, listener){
if(!eventName || !listener) return false;
else{
if(this.subscriptions.has(eventName)){
const arr = this.subscriptions.get(eventName);
arr.push(listener);
}
else{
const arr = [listener];
this.subscriptions.set(eventName, arr);
}
return true;
}
}
/* Delete the event listener.
* #eventName - the event name.
* #listener - the listener.
*/
deleteListener(eventName, listener){
if(!eventName || !listener) return false;
else{
if(this.subscriptions.has(eventName)){
const arr = this.subscriptions.get(eventName);
let index = arr.indexOf(listener);
if(index >= 0){
arr.splice(index, 1);
return true;
}
else{
return false;
}
}
else{
return false;
}
}
}
/* Emit the event.
* #eventName - the event name.
* #info - the event argument.
*/
emit(eventName, info){
if(!eventName || !this.subscriptions.has(eventName)) {
return false;
}
else{
for(let fn of this.subscriptions.get(eventName)){
if(fn) fn(info);
}
return true;
}
}
}
Mocha test:
/* test.js
* Mocha tests.
*/
import EventEmitter from '../../src/js/eventEmitter.js';
const assert = require('assert');
describe('EventEmitter', function() {
describe('#constructor()', function() {
it('should work.', function() {
const em = new EventEmitter();
assert.equal(true, Boolean(em));
});
});
});
I launch the mocha directly through the PowerShell console. The result:
Mocha has support for ESM from version 7.1.0 onward (release: Feb. 26, 2020).
This requires Node 12.11.0 or higher, and is subject to the current restrictions/limitations of using modules in Node:
Either you must use .mjs file extensions for source files that use ES modules, or you must have "type": "module" in your package.json
You can't use named imports when importing from CommonJS modules
Local import statements have to explicitly include the .js file extension
And so on.
Updated answer
I had previously recommended using the esm package as an alternative to Mocha's built-in module support, but it is no longer being mantained, can't handle newer syntactical constructs like ?., and seems to possibly not work at all with newer versions of Mocha.
However, #babel/register seems to work well for this:
mocha -r #babel/register -r regenerator-runtime/runtime
I'm using this with this preset (in .babelrc):
{
"presets": [
"#babel/preset-env"
]
}
This setup requires the following packages:
#babel/core
#babel/register
#babel/preset-env
regenerator-runtime
You can also specify these in your .mocharc.js file instead of on the command line:
module.exports = {
require: [
'#babel/register',
'regenerator-runtime/runtime',
],
};
My personal experience as of yet is that trying to take advantage of Mocha's new, inherent ESM support is still a considerable burden, but using this approach is quite seamless.
Previous answer
Another option is to use the esm package, which is not subject to the above limitations:
mocha -r esm
My personal experience as of yet is that trying to take advantage of Mocha's new, inherent ESM support is still a considerable burden, but using the esm package is quite seamless.
In my case run with:
basic comand:
npx mocha --require esm test_path/
package.json
"scripts": {
// ...
"test": "npx mocha --require esm --reporter spec test_path/"
}
Runing
npm test
It's possible with Babel and Browserfy
https://drublic.de/blog/es6-modules-using-browserify-mocha/
Regarding your main question
Is it possible to use ES6 modules in Mocha tests?
Yes, as of Mocha version 9:
Mocha is going ESM-first! This means that it will now use ESM import(test_file) to load the test files, instead of the CommonJS require(test_file). This is not a problem, as import can also load most files that require does. In the rare cases where this fails, it will fallback to require(...). This ESM-first approach is the next step in Mocha's ESM migration, and allows ESM loaders to load and transform the test file.
You also need to use a Node version which supports import, which would be >= 13.2.0
Regarding the Unexpected token import problem - others here wrote good answers, but here's a better answer from another related question:
How does mocha / babel transpile my test code on the fly?

How to tell which files are being transpiled by Babel 6?

I have a project that is using babel-register to dynamically transpile ES6 source to ES5 when requiring that module in a Node 6.6 project. I've read that babel-register hooks into Node's require function in order to transpile a file when you try to load it, but I'm not always clear on which files will be affected by that change.
This question comes up for me a lot when I'm writing tests: is only my production code getting transpiled, or does the test code get transpiled too?This brings me to the more general question, which is the topic of this post:
How can I tell when Babel is actually running, and which files are being transpiled?
Example code
Let's say I have production classes like this that are written in ES6 syntax
//src/greeter.js
export default class Greeter {
sayHello() {
return 'Hello World';
}
}
and Babel is configured to transpile as so (.babelrc)
{
"presets": ["es2015"]
}
and then there's some test code
//features/step_definitions/greeter_steps.js
import Greeter from '../../src/greeter'; //Causes greeter.js to be transpiled
import expect from 'expect';
var stepWrapper = function() {
//Does Babel try to transpile this code too?
this.Given(/^a greeter$/, function() {
this.greeter = new Greeter();
});
this.When(/^I ask it for a general greeting$/, function() {
this.greeting = this.greeter.sayHello();
});
this.Then(/^it should greet the entire world$/, function() {
expect(this.greeting).toEqual('Hello World');
});
};
module.exports = stepWrapper;
and all of that runs on Node like so
cucumberjs --compiler js:babel-core/register
Example code is available here, if that is helpful.
I made a hack to node_modules/babel-register/lib/node.js to do some logging like so
function compile(filename) {
var result = void 0;
var opts = new _babelCore.OptionManager().init((0, _extend2.default)({ sourceRoot: _path2.default.dirname(filename) }, (0, _cloneDeep2.default)(transformOpts), { filename: filename }));
var cacheKey = (0, _stringify2.default)(opts) + ":" + babel.version;
var env = process.env.BABEL_ENV || process.env.NODE_ENV;
console.log('[babel-register::compile] filename=' + filename + '\n'); //Added logging here
if (env) cacheKey += ":" + env;
if (cache) {
var cached = cache[cacheKey];
if (cached && cached.mtime === mtime(filename)) {
result = cached;
}
}
...
}
which then reports that test and production code are at least passing through Babel on some level
$ npm t
> cucumber-js-babel#1.0.0 test /Users/krull/git/sandbox/node/cucumber-js-babel
> cucumberjs --compiler js:babel-core/register
[babel-register::compile] filename=.../node/cucumber-js-babel/features/step_definitions/greeter_steps.js
[babel-register::compile] filename=.../node/cucumber-js-babel/src/greeter.js
...test results...
However, I'm hoping for a better solution that
works by some means of plugins and/or configuration, instead of monkey patching
better distinguishes which files are actually being transpiled, and which ones pass through Babel without modification
Because of this:
cucumberjs --compiler js:babel-core/register
...babel is invoked for both your test and regular source code. Keep in mind that in node, the only way to import JS is through require, so obviously babel-register will always be invoked. Of course, what babel does depends on its configuration, but most likely you have a simple configuration where all files required by require except those under node_modules will be transpiled.

Karma: Running a single test file from command line

So, I've been looking all over for this, found "similar" answers here, but not exactly what I want.
Right now if I want to test a single file with karma, I need to do fit(), fdescribe() on the file in question...
However, what I do want is to be able to just call karma, with the config file, and direct it to a specific file, so I don't need to modify the file at all, ie:
karma run --conf karma.conf.js --file /path/to/specific/test_file.js
is it possible to do this? Or with any helper? (using grunt or gulp?)
First you need to start karma server with
karma start
Then, you can use grep to filter a specific test or describe block:
karma run -- --grep=testDescriptionFilter
Even though --files is no longer supported, you can use an env variable to provide a list of files:
// karma.conf.js
function getSpecs(specList) {
if (specList) {
return specList.split(',')
} else {
return ['**/*_spec.js'] // whatever your default glob is
}
}
module.exports = function(config) {
config.set({
//...
files: ['app.js'].concat(getSpecs(process.env.KARMA_SPECS))
});
});
Then in CLI:
$ env KARMA_SPECS="spec1.js,spec2.js" karma start karma.conf.js --single-run
This option is no longer supported in recent versions of karma:
see https://github.com/karma-runner/karma/issues/1731#issuecomment-174227054
The files array can be redefined using the CLI as such:
karma start --files=Array("test/Spec/services/myServiceSpec.js")
or escaped:
karma start --files=Array\(\"test/Spec/services/myServiceSpec.js\"\)
References
karma-runner source: cli.js
karma-runner source: config.js
I tried #Yuriy Kharchenko's solution but ran into a Expected string or object with "pattern" property error.
Therefore I made the following modifications to his answer and now I'm able to run single files using Karma:
function getSpecs(specList) {
if (specList) {
return specList.toString();
} else {
return ['**/*_spec.js'] // whatever your default glob is
}
}
module.exports = function(config) {
config.set({
//...
files: [
{ pattern: getSpecs(process.env.KARMA_SPECS), type: "module"}
]
});
});
Note: This solution only works with a single file mentioned in the KARMA_SPECS env variable. Ex: export KARMA_SPECS="src/plugins/muc-views/tests/spec1.js"

how does the ignore parameter works in gulp and nodemon?

I'm trying to create a gulpfile that lint my personal javascript files (.js) but ignore any vendor/third party libaries.
My gulp file is listed below:
var gulp = require("gulp"),
uglify = require("gulp-uglify"),
jshint = require("gulp-jshint"),
jasmine = require("gulp-jasmine"),
nodemon = require("gulp-nodemon");
// lint JS files for bad habbits
gulp.task("lint", function () {
gulp.src(["**/*.js", "node_modules/*"])
.pipe(jshint())
.pipe(jshint.reporter("default"));
});
gulp.task("nodemon", function () {
nodemon({
script: "server.js",
ext: "html css jade js",
ignore: ["node_modules/*"]
})
.on("change", ["lint"])
.on("restart", function () {
console.log("Change detected, restarting server ...");
});
});
gulp.task("default",["nodemon"]);
When I run the "gulp default" command in my terminal it still lint the javascript files in the node_modules. I've tried variations of the glob syntax but I can't seem to achieve the desired behaviour.
Any idea where I've gone wrong?
Thanks.
The glob pattern you've set, node_modules/* only matches the files in the root of the node_modules directory, but not all the children with an highter depth.
You need to set is as node_modules/**/*, this will match everything. I advice you however to only match js files, with node_modules/**/*.js.
You also need to negate the pattern with !, gulp does not know that you want to do that, so it will look like:
gulp.src(['**/*.js', '!node_modules/**/*.js'])
For nodemon, following the same thing you can use the node_modules/**/* pattern.
The library used by gulp for globbing is minimatch.
use this ignore: ["node_modules/"] without '*' and it will work

Categories

Resources