Run Jasmine tests written in TypeScript - javascript

I have a Typescript+Node+Angular2+Electron app and currently trying to run tests for node classes, written also in Typescript.
For building the application and running it within electron I use following tsconfig:
"compilerOptions": {
"module": "system",
"target": "es6",
...
}
So as you can see, it's using systemjs and compiling TS into JS-es6. It works fine, application itself is working.
Now I need Jasmine to come on board. I installed this npm package, updated my gulp tasks to run gulp-jasmine for just 1 file:
gulp.task('jasmine', function() {
gulp.src('./test/test.js')
.pipe(jasmine())
});
This is how my test.js looks like:
System.register(["./models-src/app/models/pathWatch/pathWatch"], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var pathWatch_1;
return {
setters:[
function (pathWatch_1_1) {
pathWatch_1 = pathWatch_1_1;
}],
execute: function() {
describe("Run Application:", () => {
it("starts", () => {
var pw1 = new pathWatch_1.PathWatch();
expect(true).toEqual(true);
});
});
}
}
});
So, nothing special, 1 import-1test-1assert, wrapped with SystemJs stuff.
When I try to run this test, I have an error: "System is not defined".
My questions are:
1) Is it possible to run jasmine tests, using systemjs loader inside?
2) If it's possible, do I need to install/configure some additional stuff?
3) I tried to compile TS using Module="commonjs" and it's working. But I don't want to compile my source differently for tests and build. Why it's working fine with commonjs without any additional manipulations?
4) Also I tried to compile TS using Module="es6". It's not working, I have an error "Unexpected reserved word". Is it possible to run jasmine tests written in js es6 without transpiling them into es5?
Thanks a lot!

1) Is it possible to run jasmine tests, using systemjs loader inside?
2) If it's possible, do I need to install/configure some additional
stuff?
You mean, run jasmine tests in node using systemjs as a loader? I don't think jasmine supports using systemjs instead of require for loading modules. So your tests need to be in commonjs, but test code can use SystemJS to load and test application code. Something like this in test.js could work, provided that systemjs is configured properly and can find pathWatch module:
describe("Run Application:", () => {
it("starts", (done) => {
var system = require('systemjs');
system.config({
// systemjs config here
//
});
system.import('path-to-path-watch-module').then(pathWatch => {
var pw = new pathWatch.PathWatch();
expect(true).toEqual(true);
done();
});
});
});
system.import is asynchronous, so all jasmine tests need to be async too.
3) I tried to compile TS using Module="commonjs" and it's working. But
I don't want to compile my source differently for tests and build. Why
it's working fine with commonjs without any additional manipulations?
Because then there is no reference to System in the compiled code - it uses module.exports like any other node module and can be loaded as is by jasmine.
4) Also I tried to compile TS using Module="es6". It's not working, I
have an error "Unexpected reserved word". Is it possible to run
jasmine tests written in js es6 without transpiling them into es5?
Module="es6" requires a runtime that supports es6 import and export, so it needs a transpiler and module loader before it can run on current version of node.

Related

After upgrading Gulp, javascript is throwing an syntax-error "import declarations may only appear at top level of a module"

I have a gulpfile.js that watch file changes in a folder. Everything was working fine until I upgraded from Gulp 3.9.1 to 4.0.2. When upgrading the Gulp, I also upgraded my node.js to the latest v12.17.0
Here is my gulp task that bundles the js files
var gulp = require("gulp"),
concat = require("gulp-concat"),
filter = require('gulp-filter'),
merge = require("merge-stream");
gulp.task("min:js", function () {
var tasks = [];
var task = gulp.src(['path_1/**/*.js', 'path_2/**/*.js'], { base: "." })
.pipe(filter('**/*.js'))
.pipe(concat('output.js'))
.pipe(gulp.dest("."));
tasks.push(task);
return merge(tasks);
});
The task generates the following code
// More Js code...
import { setTimeout } from "timers";
'use strict';
// more js code.....
But the line import { setTimeout } from "timers"; is throwing the following error
SyntaxError: import declarations may only appear at the level of a
module
How can I fix this error?
The above problem might be caused due to the attempt to import setTimeout as a module.
setTimeout is a standard javascript function and it need not be imported explicitly. Therefore, removing the import statement may solve the issue.
If the above step does not fix the issue, then the following may be tried:
Add type="module" attribute to the HTML script tag that includes the module.
<script type="module" src="my-module.js"></script>

Import existing AMD module into ES6 module

I have an existing application where I have AMD modules defined using RequireJS. I use "text" and "i18n" plugins for requirejs extensively in my project.
I have been experimenting with ES6 modules lately and would like to use them while creating new modules in my application. However, I want to reuse the existing AMD modules and import them while defining my ES6 modules.
Is this even possible? I know Traceur and Babel can create AMD modules from ES6 modules, but that only works for new modules with no dependency on existing AMD modules, but I could not find an example of reusing the existing AMD modules.
Any help will be appreciated. This is a blocker for me right now to start using all ES6 goodies.
Thanks
Yes, it can be done. Create a new application with the following structure:
gulpfile.js
index.html
js/foo.js
js/main.es6
node_modules
Install gulp and gulp-babel. (I prefer to install gulp locally but you may want it globally: that's up to you.)
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Something</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.20/require.js"></script>
<script>
require.config({
baseUrl: "js",
deps: ["main"]
});
</script>
</head>
<body>
</body>
</html>
gulpfile.js:
"use strict";
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task("copy", function () {
return gulp.src(["./js/**/*.js", "./index.html"], { base: '.' })
.pipe(gulp.dest("build"));
});
gulp.task("compile-es6", function () {
return gulp.src("js/**/*.es6")
.pipe(babel({"modules": "amd"}))
.pipe(gulp.dest("build/js"));
});
gulp.task("default", ["copy", "compile-es6"]);
js/foo.js:
define(function () {
return {
"foo": "the value of the foo field on module foo."
};
});
js/main.es6:
import foo from "foo";
console.log("in main: ", foo.foo);
After you've run gulp to build the application, open the file build/index.html in your browser. You'll see on the console:
in main: the value of the foo field on module foo.
The ES6 module main was able to load the AMD module foo and use the exported value. It would also be possible to have a native-AMD module load an ES6 module that has been converted to AMD. Once Babel has done its work, they are all AMD modules as far as an AMD loader is concerned.
In addition to #Louis's answer, assuming you already have a bunch of third party libraries specified in require.js configuration, in your new ES6 modules, whenever you are importing a module, be it amd or es6, you'll be fine as long as you keep the imported module name consistent. For example:
Here is the gulpfile:
gulp.task("es6", function () {
return gulp.src("modules/newFolder//es6/*.js")
.pipe(babel({
"presets": ["es2015"],
"plugins": ["transform-es2015-modules-amd"]
// don't forget to install this plugin
}))
.pipe(gulp.dest("modules/newFolder/build"));
});
Here is the es6 file:
import d3 from 'd3';
import myFunc from 'modules/newFolder/es6module'
// ...
This will be compiled to sth like this:
define(['d3', 'modules/newFolder/es6module'], function (_d, _myFunc) {
'use strict';
// ...
});
as long as the module in define(['d3', 'modules/newFolder/es6module'], ... of the compiled file is fine in a original AMD file, it should work with under existing require.js setup, such as compress files etc.
In terms of #coderC's question about require.js loaders, I was using i18n!nls/lang in AMD modules, at first I thought it would be a really tricky thing to find an alternative of AMD plugin loaders in ES6 modules, and I switched to other localization tools such as i18next. But it turned out that it's okay to do this:
import lang from 'i18n!nls/lang';
// import other modules..
because it will be compiled by gulp task to sth like:
define(['d3', 'i18n!nls/lang'], function (_d, _lang) {
// ....
This way, we don't have to worry about the require.js loader.
In a nutshell, in ES6 modules, if you want to use existing AMD plugin/modules, you just need to ensure the compiled file is conformed with the existing setup. Additionally, you can also try the ES6 module bundler Rollup to bundle all the new ES6 files.
Hope this can be helpful for those who are trying to integrate ES6 syntax in project.
A few changes for the latest version of babel:
First, babel({"modules": "amd"}) doesn't work with the latest version of babel. Instead, use babel({"plugins": ["#babel/plugin-transform-modules-amd"]}). (You'll need to install that plugin as a separate module in npm, i.e. with npm install --save-dev #babel/plugin-transform-modules-amd.)
Second, the syntax for gulp.task no longer accepts arrays as its second argument. Instead, use gulp.parallel or gulp.series to create a compound task.
Your gulpfile will end up looking like this:
"use strict";
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task("copy", function () {
return gulp.src(["./js/**/*.js", "./index.html"], { base: '.' })
.pipe(gulp.dest("build"));
});
gulp.task("compile-es6", function () {
return gulp.src("js/**/*.es6")
.pipe(babel({"plugins": ["#babel/plugin-transform-modules-amd"]}))
.pipe(gulp.dest("build/js"));
});
gulp.task("default", gulp.parallel("copy", "compile-es6"));

How to properly require modules from mocha.opts file

I'm using the expect.js library with my mocha unit tests. Currently, I'm requiring the library on the first line of each file, like this:
var expect = require('expect.js');
describe('something', function () {
it('should pass', function () {
expect(true).to.be(true); // works
});
});
If possible, I'd like to remove the boilerplate require code from the first line of each file, and have my unit tests magically know about expect. I thought I might be able to do this using the mocha.opts file:
--require ./node_modules/expect.js/index.js
But now I get the following error when running my test:
ReferenceError: expect is not defined
This seems to make sense - how can it know that the reference to expect in my tests refers to what is exported by the expect.js library?
The expect library is definitely getting loaded, as if I change the path to something non-existent then mocha says:
"Error: Cannot find module './does-not-exist.js'"
Is there any way to accomplish what I want? I'm running my tests from a gulp task if perhaps that could help.
You are requiring the module properly but as you figured out, the symbols that the module export won't automatically find themselves into the global space. You can remedy this with your own helper module.
Create test/helper.js:
var expect = require("expect.js")
global.expect = expect;
and set your test/mocha.opts to:
--require test/helper
While Louis's answer is spot on, in the end I solved this with a different approach by using karma and the karma-chai plugin:
Install:
npm install karma-chai --save-dev
Configure:
karma.set({
frameworks: ['mocha', 'chai']
// ...
});
Use:
describe('something', function () {
it('should pass', function () {
expect(true).to.be(true); // works
});
});
Thanks to Louis answer and a bit of fiddling around I sorted out my test environment references using mocha.opts. Here is the complete setup.
My project is a legacy JavaScript application with a lot of "plain" js files which I wish to reference both in an html file using script tags and using require for unit testing with mocha.
I am not certain that this is good practice but I am used to Mocha for unit testing in node project and was eager to use the same tool with minimal adaptation.
I found that exporting is easy:
class Foo{...}
class Bar{...}
if (typeof module !== 'undefined') module.exports = { Foo, Bar };
or
class Buzz{...}
if (typeof module !== 'undefined') module.exports = Buzz;
However, trying to use require in all the files was an issue as the browser would complain about variables being already declared even when enclosed in an if block such as:
if (typeof require !== 'undefined') {
var {Foo,Bar} = require('./foobar.js');
}
So I got rid of the require part in the files and set up a mocha.opts file in my test folder with this content. The paths are relative to the root folder:
--require test/mocha.opts.js
mocha.opts.js content. The paths are relative to the location of the file:
global.assert = require('assert');
global.Foo = require("../foobar.js").Foo;
global.Bar = require("../foobar.js").Bar;
global.Buzz = require("../buzz.js");

Testing Angular with Gulp-mocha: "Window is not Defined"

I am setting up a project with Gulp to run unit tests with Mocha, including Angular tests. I have the basic set up working (indexOf, etc.), however when I include angular-mocks I get this error or a node-module error:
ReferenceError in 'gulp-mocha': "Window is not defined"
I've tried including angular-module-mocks, using gulp-mocha-phantomjs... but the result is the same. (With mocha-phantomjs my error was 'Init timeout'.) I've seen many examples of configurations with Mocha and Angular or Gulp and Karma but have not yet found a solution for Gulp, Mocha and Angular alone.
I'm thinking of something similar to this Karma solution to correctly load angular-mocks by specifying it in a config file and forcing Gulp to load it (Angular testing with Karma: "module is not defined"). However, even if this would work, it seems like gulp-mocha does not support loading a configuration file (mocha.opts - https://github.com/sindresorhus/gulp-mocha/issues/26). I would be happy to hear a more straightforward solution.
I am using angular-mocks 1.2.22 and gulp-mocha 1.1.0.
Code snippets:
var mocha = require('gulp-mocha');
gulp.task('test', function () {
return gulp.src('test/*.js', {read: false})
.pipe(mocha({reporter: 'nyan', timeout: 400}));
});
test/test.js
var assert = require('assert');
var angular_mocks = require('angular-mocks'); //Fails only when this line is present
//tests
What finally worked for me with Gulp/Browserify/Mocha was using Karma and Mocha combined.
Specifically, I used gulp-karma, and defined the configuration at karma.config.js and used a dummy file for gulp.src as others have done:
gulp.task('test', function () {
return gulp.src('./foobar.js').pipe(karma({
configFile:'karma.config.js',
action: 'run'
}))
.on('error', handleErrors);
});
Then I used this karma.config.js file. I needed the npm modules karma-mocha, karma-chai, and karma-bro. (With only the first two, I was getting 'require is not defined'. Then of course I tried including karma-requirejs, but that does not work with Browserify. Then I tried karma-commonjs, which still didn't work. Then I tried karma-browserify, and got a strange error involving bundle() that no one seems to have solved (https://github.com/xdissent/karma-browserify/issues/46). Karma-bro did the trick.)
I also needed to preprocess each file referenced in the tests as well as the tests themselves. (For using phantomJS also include karma-phantomjs-launcher. And I am using the bower version of angular-mocks simply because it is more recent: v1.2.25 compared to 1.2.22 for npm - but the npm version might work.)
module.exports = function(config) {
config.set({
basePath: '',
// frameworks to use
frameworks: ['browserify', 'mocha', 'chai'],
// list of files / patterns to load in the browser
files: [
'node_modules/angular/lib/angular.min.js',
'bower_components/angular-mocks/angular-mocks.js',
'source/javascript/controllers/*.js',
'source/javascript/*.js',
'test/*.js'
],
reporters: ['progress'],
port: 9876,
colors: true,
autoWatch: true,
browsers: ['PhantomJS'],
preprocessors: {
'source/javascript/controllers/*.js': ['browserify'],
'source/javascript/*.js': ['browserify'],
'test/*.js': ['browserify']
}
});
};
And finally this test passes. At the end I needed to make sure the names of my modules and controllers were consistent (capitals etc.) to resolve 'Module not defined' errors. For debugging I replaced node_modules/angular/lib/angular.min.js with node_modules/angular/lib/angular.js in the files.
describe('Angular', function() {
describe('App Controllers', function() {
beforeEach(angular.mock.module('App'));
describe('MessageCtrl', function() {
it('should retrieve the correct amount of messsages', angular.mock.inject(function($controller) {
var scope = {},
ctrl = $controller('MessageCtrl', {$scope:scope});
assert.equal(scope.messages.length, 2);
}));
});
});
});
I do get this: 'WARNING: Tried to load angular more than once.' I can live with it.

Testing CommonJS modules that use browserify aliases and shims

Browserify allows creating aliases and shimming modules that are not directly CommonJS compatible. Since I'd like to run my tests in node CLI, can I somehow handle those aliases and shimmed modules in node?
For example, let's say I'm aliasing ./my-super-module to supermodule and shimming and aliasing some jquery plugin ./vendor/jquery.plugin.js -> ./shims/jquery.plugin.shim.js to jquery.plugin.
As a result, I can do this in my module:
var supermodule = require('supermodule');
require('jquery.plugin');
// do something useful...
module.exports = function(input) {
supermodule.process(output)
}
Are there any practices how I could test this module in node.js/cli so that the dependencies are resolved?
You might want to use proxyquire if you plan to test this module directly in node using any cli runner.
using mocha will be something like this
describe('test', function () {
var proxyquire = require('proxyquire').noCallThru();
it('should execute some test', function () {
var myModule = proxyquire('./my-module', {
// define your mocks to be used inside the modules
'supermodule' : require('./mock-supermodule'),
'jquery.plugin': require('./jquery-plugin-mock.js')
});
});
});
If you want to test this is a real browser, you might not need to mock your aliases modules, you can use browserify to run your tests in karma directly.
If you need to mock modules in that scenario you can use proxyquireify, which will allow you to do the same but with browserify.
there is also browsyquire which is a fork of proxyquireify that I made with some extra features and a bug fix.

Categories