Getting started with unit testing JavaScript without a framework - javascript

I am building a JavaScript application (no framework yet, but I may move it to Backbone). I have created various classes, here's an example, chart.js:
var moment = require('moment');
var chart = {
...
getAllMonths: function(firstMonth, lastMonth) {
var startDate = moment(firstMonth);
var endDate = moment(lastMonth);
var monthRange = [];
while (startDate.isBefore(endDate)) {
monthRange.push(startDate.format("YYYY-MM-01"));
startDate.add(1, 'month');
}
return monthRange;
},
setMonths: function() {
// get data via ajax
this.globalOptions.months = this.getAllMonths(data['firstMonth'], data['lastMonth']);
}
};
module.exports = chart;
My file structure is as follows:
index.js
src/
chart.js
form.js
I import the two classes into index.js and use browserify to bundle these scripts up in order to use them in my web app.
Now I want to add tests for chart.js and form.js. I have added a new directory called test/ and empty test files:
index.js
src/
chart.js
form.js
test/
test_chart.js
test_form.js
My question now is what test_chart.js should look like in order to test the getAllMonths function, and what test runner I should use.
I've started experimenting with the following in test_chart.js:
console.log('hello world');
var chart = require('../src/chart');
var months = chart.getAllMonths('2014-02-01', '2015-03-01');
// assert if months.length != 14
But if I run this with node test/test_chart.js, I get errors about failed module imports for moment etc (shouldn't these be imported automatically with the original chart.js module?).
Secondly, what test runner could I use for this kind of simple testing? I'd like something that will automatically run everything in the test directory, and offers asserts etc.

I ended up using Mocha. It's really pretty easy:
npm install --save-dev mocha
mocha
Boom!
It automatically looks for files in the test/ folder.
Still having the problem with imports though.

Related

Application modularity with Vue.js and local NPM packages

I'm trying to build a modular application in Vue via the vue-cli-service. The main app and the modules are separated projects living in different folders, the structure is something like this:
-- app/package.json
/src/**
-- module1/package.json
/src**
-- module2/package.json
/src**
The idea is to have the Vue app completely agnostic about the application modules that can be there at runtime, the modules themself are compiled with vue-cli-service build --target lib in a local moduleX/dist folder, pointed with the package.json "main" and "files" nodes.
My first idea (now just for development speed purposes) was to add the modules as local NPM packages to the app, building them with a watcher and serving the app with a watcher itself, so that any change to the depending modules would (I think) be distributed automatically to the main app.
So the package.json of the app contains dependencies like:
...
"module1": "file:../module1",
"module2": "file:../module2",
...
This dependencies are mean to be removed at any time, or in general be composed as we need, the app sould just be recompiled and everything should work.
I'm trying to understand now how to dynamically load and activate the modules in the application, as I cannot use the dynamic import like this:
import(/* webpackMode: "eager" */ `module1`).then(src => {
src.default.boot();
resolve();
});
Because basically I don't know the 'module1', 'module2', etc...
In an OOP world I would just use dependency injection retrieving classes implementing a specific interface, but in JS/TS I'm not sure it is viable.
There's a way to accomplish this?
Juggling with package.json doesn't sound like a good idea to me - doesn't scale. What I would do:
Keep all available "modules" in package.json
Create separate js file (or own prop inside package.json) with all available configurations (for different clients for example)
module.exports = {
'default': ['module1', 'module2', 'module3'],
'clientA': ['module1', 'module2', 'module4'],
'clientB': ['module2', 'module3', 'module4']
}
tap into VueCLI build process - best example I found is here and create js file which will run before each build (or "serve") and using simple template (for example lodash) generate new js file which will boot configured modules based on the value of some ENV variable. See following (pseudo)code (remember this runs inside node during build):
const fs = require('fs')
const _ = require('lodash')
const modulesConfig = require(`your module config js`)
const configurationName = process.env.MY_APP_CONFIGURATION ?? 'default'
const modules = modulesConfig[configurationName]
const template = fs.loadFileSync('name of template file')
const templateCompiled = _.template(template)
const generatedJS = templateCompiled({ `modules`: modules })
fs.writeFileSync('bootModules.js', generatedJS)
Write your template for bootModules.js. Simplest would be:
<% _.forEach(modules , function(module) { %>import '<%= module %>' as <%= module %><% }); %>;
import bootModules.js into your app
Use MY_APP_CONFIGURATION ENV variable to switch desired module configuration - works not just during development but you can also setup different CI processes targeting same repo with just different MY_APP_CONFIGURATION values
This way you have all configurations at one place, you don't need to change package.json before every build, you have simple mechanism to switch between different module configurations and every build (bundle) contains only the modules needed....
In an OOP world I would just use dependency injection retrieving classes implementing a specific interface, but in JS/TS I'm not sure it is viable.
Why not?
More than this, with JS/TS you are not restricted to use classes implementing a specific interface: you just need to define the interface (i.e. the module.exports) of your modules and respecting it in the libraries entries (vue build lib).
EDIT: reading comments probably I understood the request.
Each module should respect following interface (in the file which is the entry of the vue library)
export function isMyAppModule() {
return true;
}
export function myAppInit() {
return { /* what you need to export */ };
}
Than in your app:
require("./package.json").dependencies.forEach(name => {
const module = require(name);
if(! module.isMyAppModule || module.isMyAppModule() !== true) return;
const { /* the refs you need */ } = module.myAppInit();
// use your refs as you need
});

Using Webpack to add JavaScript module to ASP.NET MVC app

I'm trying to use Webpack to create a couple of simple modules in an ASP.NET MVC 5 Visual Studio 2015 project. Following instructions on the Webpack site, I downloaded the latest version of Node.js. Then using the Node command prompt, changed to my project's folder. There, I ran this command to install Webpack locally:
npm install webpack --save-dev
It created a package.json file in the root of my project:
{
"devDependencies": {
"webpack": "^2.4.1"
}
}
Note that the project already has jQuery and Bootstrap as bundles via the BundleConfig.cs, which are then referenced on _Layout.cshtml; hence they're available on all pages of the app.
Now I'd like to create a very simple test to see how to create and require modules using Webpack; once I understand it better, I can add more complex modules. I've been reading about code-splitting: https://webpack.js.org/guides/code-splitting-async/ but it's still not clear how you do this.
The function test requires function isEmpty. I'd like to define isEmpty as a module and then use it with test.
var test = function(value){
return isEmpty(value);
};
var isEmpty = function(value) {
return $.trim(value).length === 0 ? true : false;
};
This article has been helping: http://developer.telerik.com/featured/webpack-for-visual-studio-developers/
The Webpack documentation mentions import() and also require.ensure(). How do I use Webpack to modularize the isEmpty code and then use it?
Webpack allows you to use the commonJS approach for dependency management which Node.js uses, so if you have experience with Node.js it's very similar.
If not have a look at this article on the module system or the spec for a description of the module system.
For this problem I will assume all files are in the same directory. I think you will need to first move the isEmpty code into a separate file maybe isEmpty.js and change it's structure a bit so that it looks like this:
module.exports = function(value) {
return $.trim(value).length === 0 ? true : false;
};
then your test function can be moved into a separate test.js file and you can require the isEmpty module and use it like this:
var isEmpty = require('./isEmpty');
var test = function(value){
return isEmpty(value);
};
You will probably have to do something about the dependency on $ (I'm guessing jquery?) but I think that can be handled with shimming
If you have a number of functions you can do something like:
someFunctions.js
var self = {};
self.Double = function(value){
return value*2;
}
self.Triple = function(value){
return value*3;
}
module.exports = self;
useFunctions.js
var useFunctions = require('./someFunctions');
var num = 5;
console.log(useFunctions.Double(num));
console.log(useFunctions.Triple(num));

Getting started with browserify: import local files?

I have been prototyping a JavaScript application and now I want to move to a more robust setup using browserify and managing dependencies with require.
Currently I have the following files in my application:
chart.js
form.js
highcharts-options.js
vendor/
highcharts.js
jquery.js
highcharts-options.js is basically a list of constants, while chart.js looks like this...
var myChart = {
setup: function(data) { ... this.render(data); },
render: function(data) { ... }
},
and form.js looks like this:
var myForm = {
setup: function() { button.onclick(_this.getData(); },
getData: function() { // on ajax complete, callChart },
callChart: function() { myChart.setup(data); }
};
myForm.setup();
And then I have an index.html page that imports everything as follows:
<script src="/js/vendor/highcharts.js"></script>
<script src="/js/vendor/jquery.js"></script>
<script src="/js/highcharts-options.js"></script>
<script src="/js/chart.js"></script>
<script src="/js/form.js"></script>
So now I want to move this to a more modern setup with browserify.
I have deleted the vendor directory and instead created an index.js file and a package.json file, so now my directory structure looks like this:
index.js
package.json
chart.js
form.js
highcharts-options.js
node_modules/
I have run npm i --save highcharts-browserify and npm i --save jquery and that has saved these modules to package.json and installed them in node_modules. I've also added a build task in package.json: browserify index.js -o bundle.js. And in my front-end template I know just have:
<script src="/js/bundle.js"></script>
So far so good.
My question is what to put into my index.js file, because I'm not sure how to import the files that I already have. So far I've got this:
var $ = require('jquery');
var HighCharts = require('highcharts-browserify');
var options = require('highcharts-options');
var myChart = require('chart');
var myForm = require('form');
myForm.setup();
But when I try to build this, I get:
Error: Cannot find module 'chart' from '/mypath/static/js/app'
It looks like require doesn't know how to find this file, or how to import it, which is not surprising given that this is all total guesswork on my part.
How should I adapt these files to work in a more modular way? Am I on the right lines, or is this completely the wrong approach? I'm not even sure what I should be Googling for.
(NB: Eventually I want to refactor chart.js and form.js to use Backbone, but I need to work one step at a time.)
You are very close!
First, the way to reference a module in the same directory is to say:
var myChart = require('./chart');
Without the leading path component, require will look in your npm package directory.
Second, you need to export the variables in the modules so that they can be used elsewhere. So your form module needs to look something like this:
var myForm = {
setup: function() { button.onclick(_this.getData(); },
getData: function() { // on ajax complete, callChart },
callChart: function() { myChart.setup(data); }
};
myForm.setup();
module.exports = myForm;
I just finished struggling with this error for a while, I'll post my solution in case anyone else runs into the same issue I did. It seems that Browserify sometimes can't find local modules depending on where the require goes. This code didn't work:
window.Namespace = {
foo: new require('./foo.js')()
};
but this worked fine:
var Foo = require('./foo.js');
window.Namespace = {
foo: new Foo()
};

How to add a broccoli plugin to ember-cli app with live-reload?

When I run ember build, the broccoli plugin runs correctly,
and outputs the sprite CSS file and sprite PNG file into the assets directory.
When I run ember serve, the same thing happens initially too.
However, when I save any file, causing Broccoli to rebuild its tree,
the sprite CSS and PNG files are no longer merged into the main app tree,
and when the page refreshes from live-reload the page no longer displays the sprited images.
Why does this happen?
How do I ensure that the outputs from my plugin get merged every time?
Details:
After asking this question,
and getting no responses despite putting a bounty on it,
I decided to write my own broccoli plugin for CSS image sprite generation:
broccoli-sprite
What I have tried so far:
#1
I am merging the output from my plugin with that of the main app using this in Brocfile.js
var app = new EmberApp(/* ... */);
/* other ember-cli init for app */
var broccoliSprite = require('broccoli-sprite');
var spritesTree = broccoliSprite(/* ... */);
var appTree = app.toTree();
var broccoliMergeTrees = require('broccoli-merge-trees');
module.exports = broccoliMergeTrees([spritesTree, appTree]);
I understand that this might not be the way to go, and I am fairly new to both ember-cli and broccoli, so pardon the newbie error, if this is one.
#2
In Brocfile.js, extend EmberApp to include a new tree for sprites:
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var broccoliSprite = require('broccoli-sprite');
EmberApp.prototype.sprites = function() {
console.log('EmberApp.prototype.sprites');
var spritesTree = broccoliSprite('public', this.options.sprite);
return spritesTree;
};
var originalEmberAppToArray = EmberApp.prototype.toArray;
EmberApp.prototype.toArray = function() {
var sourceTrees = originalEmberAppToArray.apply(this, arguments);
sourceTrees.push(this.sprites());
return sourceTrees;
};
The next release of Ember CLI will have first class support for add-ons. Thanks to Robert Jackson.
Take a look at https://github.com/rjackson/ember-cli-esnext to get an idea of how to package up broccoli-sprite for Ember CLI.
I look forward to using it in future apps :)

gulp-filter not filtering out excluded files correctly

I'm experimenting with using gulpjs instead of grunt for a project. I'm attempting to use gulp filter to ignore vendor libraries when running jsHint on my code. I've based my code off of the code from the readme's example, but the files have not been filtered.
I'm running node 0.10.26, gulp 3.8.0,and gulp filter 0.4.1
I'm trying to run jshint on a directory wcui/app/js that contains many other directories of JS files, with about 120 js files total. I want to exclude the vendor directory only.
My code looks like this:
var gulp = require('gulp');
var gulpFilter = require('gulp-filter');
var jshint = require('gulp-jshint');
var srcs = {
scripts: ['wcui/app/js/**/*.js'],
styles: ['wcui/app/css/**/*.less','wcui/app/css/**/*.css']
};
var dests = {
scripts: 'wcui/static/js/',
styles: 'wcui/static/css/'
};
gulp.task('scripts', function() {
var filter = gulpFilter('!wcui/app/js/vendor');
return gulp.src(srcs.scripts)
.pipe(filter)
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'))
.pipe(filter.restore)
.pipe(gulp.dest(dests.scripts));
});
gulp.task('styles', function() {
return gulp.src(srcs.styles)
.pipe(gulp.dest(dests.styles));
});
gulp.task('dev',['scripts','styles']);
Right now running gulp dev does the same thing it did before I added the filter, linting every js file. How can I change this to make it filter correctly? The gulp example had the src in the format 'wcui/app/js/*.js' but when I admit the ** glob, I don't get subdirectories at all. Other than that I think I'm following the readme to the letter (with changes for my particular task).
For readers that have a more up-to-date version of gulp-filter (release at the time of writing is 1.0.0)
The release of version 0.5.0 of gulp-filter introduced multimatch 0.3.0 which come with a breaking change.
Breaking change
Using a negate ! as the first pattern no longer matches anything.
Workaround: ['*', '!cake']
Basically, what it means is you need to replace
var filter = gulpFilter('!wcui/app/js/vendor');
with
var filter = gulpFilter(['*', '!wcui/app/js/vendor']);
and you are good to go.
Also, as noted in the comment by MildlySerious, you should have .pipe(filter.restore()) instead of .pipe(filter.restore)
Use filter like this gulpFilter(['*', '!app/vendor'])

Categories

Resources