Trying to use mocha, ES6 modules, and ts-node with the --experimental-loader option - node.js

I'm trying to get the ts-node option --experimental-loader working along with mocha, and having no luck. Until I started trying to compile ES6 modules, I used to be able to run mocha tests this way:
"test": "nyc --reporter=html mocha --require ts-node/register src/**/*.spec.ts"
That doesn't work anymore when generating ES6 modules.
I'd use the TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' solution for testing, but that won't work for me because of another complication: I'm generating ES6 modules as a first step in my build, but also generating ES5/CommonJS modules using webpack and babel. That last step doesn't work unless I add .js to the end of my local TypeScript import statements.
But adding those .js extensions turns out to break the TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' solution, which will work, however, if I go back and delete all of the .js extensions. I obviously don't want a test and build process where I have to keep going back and forth between adding and removing those extensions.
To simplify for now, I've taken out nyc, and I'm trying to run tests like this:
mocha -r ts-node/register --experimental-loader ./ts-loader.mjs src/**/*.spec.ts
I get no errors this way, but nothing happens either. It's like the src/**/*.spec.ts doesn't exist.
My do-nothing (for now) dummy loader looks like this:
console.log('ts-loader loaded');
export async function resolve(specifier, context, defaultResolve) {
console.log('resolve');
return defaultResolve(specifier, context, defaultResolve);
}
export async function getFormat(url, context, defaultGetFormat) {
console.log('getFormat');
return defaultGetFormat(url, context, defaultGetFormat);
}
export async function getSource(url, context, defaultGetSource) {
console.log('getSource');
return defaultGetSource(url, context, defaultGetSource);
}
export async function transformSource(source, context, defaultTransformSource) {
console.log('transformSource');
return defaultTransformSource(source, context, defaultTransformSource);
}
export function getGlobalPreloadCode() {
console.log('getGlobalPreloadCode');
return '';
}
I can tell it gets loaded because the 'ts-loader loaded' message appears, but none of the functions ever get called.
I've tried other permutations, but just get errors like src/**/*.spec.ts being treated as a literal file name instead of a glob, or errors about modules not being found.
I was hoping to see my loader invoked for every import being handled, and then figuring out how to manipulate the file extensions, but I haven't managed to get that far yet. Any suggestions?
I'm using node v14.15.1. The full code for my project, with a working build, but broken tests, can be found here: https://github.com/kshetline/tubular_math

I finally found a solution, although it wasn't along the lines I was originally looking for. I gave up on trying to make mocha happy with the extra .js extensions, and found a way to make webpack happy without them. So...
import { Angle, Mode, Unit } from './angle.js';
...went back to...
import { Angle, Mode, Unit } from './angle';
My test script looks like this:
"scripts": {
"build": "rimraf dist/ && tsc && webpack && webpack --env target=umd",
"prepublishOnly": "npm run build",
"lint": "eslint 'src/**/*.ts'",
"test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter=html mocha --require ts-node/register src/**/*.spec.ts"
},
And finally, most importantly, I figured out how to make webpack 5.x (4.x didn't have this issue) happy with local JavaScript imports that don't have a .js extension, which webpack now otherwise insists upon if your package.json says "type": "module":
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader', resolve: { fullySpecified: false } }
]
}
...where setting fullySpecified to false is the key to the solution.
UPDATE: The above example was done working on a deliberately simple project, something easy for a starter to generate an npm package with ESM modules. Now that I'm trying something a little more advanced, I've run into a snag again running unit tests. As soon as a *.spec.ts file directly or indirectly imports external code, module loading fails. I can only test code with no external dependencies until I figure out how to fix that problem. Apparently using "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' is only letting me go one level deep into fixing the basic problem with running mocha along with ts-node.

Related

How to map errors in transpiled babel files back to source?

I'm working with an express app and am using babel to transpile my code to be able to use some ES6/7/8 goodies.
THe command I'm running to transpile the files is: npx babel server --out-dir lib --watch. Then to start my server, I run nodemon lib/server.js.
The issue I'm currently running into is that all errors are happening from the transpiled files in /lib, so the trace doesn't quite match with what is actually in the source, making it hard to debug.
So let's say an exception is thrown on line 10 in a transpiled file in /lib, that error doesn't match up to where the error actually is in the source since the trace is with respect to the transpiled file.
Is there a way I can get it to map correctly?
Thanks!
#Brian i suggest you to use "babel-polyfill" and "babel-register" modules.
add these modules in your main entry file for example refer the below code.
in this way you do not need to transpile your code seprately and can debug in the same ES6+ original code.
just add the start command simply as shown in the below code snippet, it will run your node.js code and transpiles all your ES6+ features as well at run time on fly.
Example:
app.js
// Added for regenerator runtime!!
require('babel-polyfill');
// Transpile on the fly
require('babel-register')({
ignore: false,
only: /\/src/,
});
require('dotenv/config');
let server = require('./server');
server.listen(process.env.APP_PORT, () => {
console.info(`application started on port ${process.env.APP_PORT}`);
});
package.json
"scripts": {
"start": "node src/app.js",
Happy Coding :)

Mocha - Running test ReferenceError: regeneratorRuntime is not defined

I am trying to run tests with async/await using mocha. The project architecture was setup before I started working on it and I have been trying to update it's node version to 8.9.4. The project is an isomorphic application and uses babel, gulp and webpack to run.
To run the tests we run a gulp task. There are two .bablerc files in the project. One in the root folder of the project and another in the test fodler.
Both have the same configuration:
{
"presets": [
["env", {"exclude": ["transform-regenerator"]}],
"react",
"stage-1"
],
"plugins": [
"babel-plugin-root-import"
]
}
When I run the app locally there is no error returned anymore. However when I run the tests with gulp test:api I constantly get the error: ReferenceError: regeneratorRuntime is not defined
This is my gulp file in the test folder:
var gulp = require('gulp')
var gutil = require('gulp-util')
var gulpLoadPlugins = require('gulp-load-plugins')
var plugins = gulpLoadPlugins()
var babel = require('gulp-babel')
require('babel-register')({
presets:["es2015", "react", "stage-1"]
});
// This is a cheap way of getting 'test:browser' to run fully before 'test:api' kicks in.
gulp.task('test', ['test:browser'], function(){
return gulp.start('test:api')
});
gulp.task('test:api', function () {
global.env = 'test'
gulp.src(['test/unit-tests/server/**/*.spec.js'], {read: false})
.pipe(plugins.mocha({reporter: 'spec'}))
.once('error', function (error) {
console.log(error)
process.exit(1);
})
.once('end', function () {
process.exit(0);
})
});
gulp.task('default', ['test']);
Any help on why this is happening wouldd be much appreciated.
Node version 8 already has support for async/await so you do not need Babel to transform it; indeed, your root .babelrc includes this preset to exclude the regenerator that would transform async/await (and introduce a dependency on regeneratorRuntime):
["env", {"exclude": ["transform-regenerator"]}]
However, in your test file, the configuration does not specify this preset. Instead, it specifies the preset "es2015", which does include the unwanted transform-regenerator (as you can see at https://babeljs.io/docs/plugins/preset-es2015/). If you change this to match the presets in the root .babelrc, you'll get more consistent results.
Strangely i ran into this issue after i upgraded to Node v8.10.0 from v8.6.x . I had used babel-require like so in my test-setup.js
require('babel-register')();
and the testing tools are Mocha,chai,enzyme + JSDOM . I was getting the same issue when i was making a async call to a API, also while using generator functions via sagas. Adding babel-polyfill seemed to have solved the issue.
require('babel-register')();
require('babel-polyfill');
i guess even babel docs themselves advocate using polyfill for generators and such
Polyfill not included
You must include a polyfill separately when using features that require it, like generators.
Ran into the same issue when running mocha tests from within Visual Studio Code.
The solution was to add the necessary babel plugins in the Visual Studio Code settings.json :
"mocha.requires": [
"babel-register",
"babel-polyfill"
],
I've run into this error before myself when using async/await, mocha, nyc, and when attempting to run coverage. There's never an issue when leveraging mocha for running tests, just with mocha tests while leveraging nyc for running coverage.
11) Filesystem:removeDirectory
Filesystem.removeDirectory()
Should delete the directory "./tmp".:
ReferenceError: regeneratorRuntime is not defined
at Context.<anonymous> (build/tests/filesystem.js:153:67)
at processImmediate (internal/timers.js:461:21)
You can fix the issue a couple of different ways.
Method 1 - NPM's package.json:
...
"nyc": {
"require": [
"#babel/register",
"#babel/polyfill"
],
...
},
...
It really depends which polyfill package you're using. It's recommended to use the scoped (#babel) variant: #babel/pollyfill. However, if you're using babel-polyfill then ensure that's what you reference.
Method 2 - Direct Import
your-test-file.js (es6/7):
...
import '#babel/polyfill';
...
OR
your-test-file.js (CommonJS):
...
require("#babel/polyfill");
...
Don't assign it to a variable, just import or require the package. Again, using the package name for the variant you've sourced. It includes the polyfill and resolves the error.
HTH

Run "node test" as part of Visual Studio Team Services build task with results in "tests" tab

I have a project that contains tests that I am running with Mocha from the command line. I have set up a test script in my packages.json, which looks as follows:
"test": "mocha ./**/*.spec.js --reporter dot --require jsdom-global/register"
I have currently got a simple task set up in Visual Studio Team Services, which just runs the npm test command, this runs Mocha within a console and continues/fails the build depending on whether the tests pass.
What I'd like to be able to do is have the results of my tests populate the "tests" tab in the build definition after it's run. In the same way that I can get this tab populated if I'm running tests on C# code.
I've tried using Chutzpah for this, but it's overly complicated and seems to require that I jump through all sorts of hoops that mean changing my tests and writing long config files. I already have loads of tests written, so really don't want to have to do that. When it did finally discover any of my tests, it complained about require and other things related to Node modules.
Is what I'm asking for actually possible? Is there a simple way of achieving this that's compatible with running my tests in Node?
I've found a good way of doing it that requires no third-party adapter (eg. Chutzpah). It involves getting Mocha to output its report in an XML format, and setting up Visual Studio Team Services to publish the results in an extra step of the build definition.
I installed mocha-junit-reporter (https://www.npmjs.com/package/mocha-junit-reporter) and altered my test script to the following:
"test": "mocha ./temp/**/*.spec.js --reporter mocha-junit-reporter --require jsdom-global/register"
I then created a new step in my build definition using the "Publish Test Results" task. I set the result format to "JUnit" and added the correct path for the outputted test-results.xml file created by the reporter.
It is worth noting that although Mocha comes with an "XUnit" reporter, this format appears to not work correctly with VSTS even though it's listed as an option.
The results of npm test now show up in the "tests" tab alongside any other tests from MSTest etc.
I'm using karma and got this to work in the same way as #dylan-parry suggested. Some excepts below in case it helps others:
package.json
"scripts": {
"test": "cross-env NODE_ENV=test karma start"
}
karma.conf.js
const webpackCfg = require('./webpack.config')('test');
module.exports = function karmaConfig(config) {
config.set({
reporters: ['mocha', 'coverage', 'junit'],
junitReporter: {
outputDir: 'coverage',
outputFile: 'junit-result.xml',
useBrowserName: false
}
})
...
TFS
It may also be worth adding I'm using branch policies on my git branch to prevent PR's being merged if the tests fail, info via this link:
https://www.visualstudio.com/en-us/docs/git/branch-policies
Here's the output in TFS:
Next step is to get the coverage working too!

npm + Mocha + RequireJS

I'm having quite the time trying to set up node/npm with Mocha and RequireJS. Here's what I've done.
I've created a testing/ directory, with this structure:
testing/
|
+-- package.json
|
+-- README.md
|
+-- test/
|
+-- mocha.opts
|
+-- widgets/
|
+--mywidget/
|
+-- test.js
Here is what each relevant file contains:
package.json:
{
"name":"testing-project",
"version":"2.5.0",
"description":"Testing Project",
"keywords":["test"],
"engines": { "node": ">= 0.7.0 < 0.11.0" },
"scripts" : {
"test": "./node_modules/.bin/mocha"
},
"devDependencies": {
"mocha": ">= 1.18.2",
"requirejs": ">= 2.1.11",
"should": ">= 3.2.0",
"expect.js": ">= 0.3.1"
}
}
test/mocha.opts:
--require expect.js
--require should
--require requirejs
--require mocha
--reporter spec
--ui tdd
--recursive
--growl
test/widgets/mywidget/test.js
'use strict';
var requirejs = require('requirejs');
// Also tried with: require('../../../r.js') which I downloaded from RequireJS' site
requirejs.config({
baseUrl: '../../../../',
nodeRequire: require
});
console.log('before require');
requirejs(['mywidget/trunk/src/mywidgetfile.js'], function(myObj){
console.log('after require');
var expect = require('expect.js');
// Instead of the above "requirejs['mywidget..." line, I've also tried:
// var myObj = requirejs('mywidget/trunk/src/mywidgetfile.js'); AND:
// var myObj = requirejs('../../../../mywidget/trunk/src/mywidgetfile.js');
describe('My Widget', function(){
describe('my-widget#getInfo', function(){
it('should pass this test', function(done){
expect( myObj.returnString('test') ).to.equal( 'test' );
done();
})
})
});
});
console.log('done');
It will output the console lines "before require" and "done", but as long as I have the requirejs(['mywidget... line in, it will not hit the after require. If I remove the requirejs line (and the corresponding closing brace/paren line), and instead use the direct "var myObj =" line, I get "cannot find module", and if I use the second "var myObj" line, I get "Reference Error: define is not defined".
I'm trying to package this all, for convenience for other developers, with npm, such that I'm running the command "npm test" from within the top "testing/" directory.
I've been scouring for answers and trying so many things, but I can't seem to require a file using RequireJS and have "define()" defined. I can execute tests, that's not a problem... it's just trying to insert RequireJS into the mix that is when I start having the issues.
Any help would be tremendous!
Thank you!
There are multiple problems going on in what you are showing us. You are incorrectly using both RequireJS and Mocha.
RequireJS
I am pretty sure your baseUrl is incorrect. You seem to think that Mocha's current working directory will be set to test/widgets/mywidget/ when it executes the tests in test/widgets/mywidget/test.js. That's not the case. The working directory is wherever you happen to be when you run npm test. According to your description you are in testing/ when you run it. It is not clear to me what value your baseUrl should be because you do not provide enough information in your question but I trust that from the explanation I just gave you can figure it out.
Now you may think "surely, my baseUrl is correct because when I execute requirejs(['mywidget/trunk/src/mywidgetfile.js'], function(myObj){ I don't get an error". This would be an incorrect inference. While this requirejs invocation schedules the loading of your module, RequireJS does not get the opportunity to try loading it because Mocha exits before RequireJS tries to load it. You can check this by replacing your module path with complete garbage and you won't get an error.
Once you fix this baseUrl issue, using var myObj = requirejs('mywidget/trunk/src/mywidgetfile.js') will work as you expect. So you'll be able to avoid using the asynchronous form of require (this is the form that uses an array of dependencies as the first argument). (The requirejs function you use is just an alias for the function normally called require in RequireJS' documentation.)
Mocha
Your tests are not running because Mocha does not see them. The way Mocha works is by reading all of the test files it finds and then executing them. The callbacks to each describe calls are executed right away, and the callbacks to each it calls are recorded as tests to be run. Once Mocha is done figuring out what tests exist, it runs them.
What happens with your test file is that Mocha executes it, as usual. However, there is no call to describe or it in the top scope of your test file. There are calls in your callback to requirejs but remember what I said above: RequireJS does not get the opportunity to load the module so it does not get the opportunity to run the callback. (And even if it did run it, it would be too late.) So Mocha does not see any tests and exits right away.
The Way Forward
Figure out the baseUrl you need, and then this should work:
'use strict';
var requirejs = require('requirejs');
requirejs.config({
baseUrl: <whatever value is needed here>,
nodeRequire: require
});
var myObj = requirejs('mywidget/trunk/src/mywidgetfile.js');
describe('My Widget', function() {
// etc...
You might also consider dropping RequireJS entirely from your test suite. I've written a library that works just as well in Node as in the browser. It is composed of AMD modules and is loaded by RequireJS in the browser, but I don't use RequireJS in the test suite. This is the loader I use to load my modules in Node. There's also amdefine which I've not used but should give similar capabilities.

How to run jasmine-node tests with RequireJS

How can I properly run jasmine tests using jasmine-node and RequireJS?
I already tried something like this, but doesnt work (CoffeeScript):
requirejs = require 'requirejs'
requirejs.config { baseUrl: __dirname + '/../' }
requirejs ['MyClasses', 'FooClass'], (MyClasses, FooClass) ->
describe "someProp", ->
it "should be true", ->
expect(MyClasses.FooClass.someProp).toEqual true
Finished in 0 seconds 0 tests, 0 assertions, 0 failures
My goal is to write modular classes using RequireJS, CoffeeScript and classes must be testable with jasmine-node (CI server).
How can I do that please?
Thank you!
EDIT:
I executing tests with command (at directory with tests):
jasmine-node ./
Jonathan Tran is right, it's the spec in the file name for me.
I have this:
"scripts": {
"install": "cake install",
"test": "node_modules/jasmine-node/bin/jasmine-node --verbose --coffee --runWithRequireJs --captureExceptions spec"
},
in my package.json and I installed jasmine-node from inside the project npm install jasmine-node
Minimal test file called RingBuffer.spec.coffee
require ["disrasher"], (mod) ->
describe "A test", ->
it "should fail", ->
expect(1).toEqual 0
It doesn't actually work at the moment because I haven't got the project hooked up with require properly I don't think. I'll post back here when it does.
If anyone is running into this, much has changed since this question was asked. The first thing to check is still that you're naming your files like thing.spec.coffee.
But if you're running the tests and still seeing the output "0 tests", you need to make a JavaScript file with your requirejs config. This must be JavaScript, not CoffeeScript.
// requirejs-setup.js
requirejs = require('requirejs');
requirejs.config({ baseUrl: __dirname + '/../' });
Then tell jasmine to use this setup file:
jasmine-node --coffee --requireJsSetup requirejs-setup.js ./
One nice thing about this is that you don't need to include the requirejs config in every spec file.
I've tested this on node v12.16, jasmine-node v3.0.0, and requirejs v2.3.6.
It seems that jasmine-node and require.js are completely incompatible. That said, it is possible to run jasmine tests on require.js modules in node using a bit of extra code. Take a look at https://github.com/geddski/amd-testing to see how.

Resources