100% Code Coverage or Die!

I have been working on my next tutorial. It will be a hapi.js api (maybe even with a React/Redux client) as close to production quality as I can get without a different (aka better) hardware infrastructure. Quality error messages, logging, input validation, fully documented, authentication with scopes and most importantly testing with 100% code coverage.

This is what I am currently working on and it’s been interesting learning how Hapi.js gets tested and what differences configuration over coding makes in my approach. So far I like it even though I am struggling with some specific routes. It is always fun to learn something new and feel that struggle again. I certainly prefer to have to work at something for a bit over working on the same thing over and over again.

I should have something ready in the next few days, maybe a week! I swear I just love coding so much!

 

Publishing an NPM Module Part 7

These are my notes based on the phenomenal tutorial by Kent C. Dodds hosted by egghead.io both of which are my go to sources when I want to learn something new. I highly suggest you sign up for the pro subscription.

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6 | >Part 7

Part 7: Making a Browser Build

Now what if we wanted to make it so a website could consume our nifty library? We could force them to create a node api to use our wonderful features. Or we could be nice and just let them load up via cdn! I think we should go that route. So first we need to install webpack the module bundler! In a nutshell webpack will take all of your various bits and bobs and package them up. So head to your project folder in your console and type:

$ npm i -D webpack

Now we need to create a basic config file for it.

import {join} from 'path'

const include = join(__dirname, 'src');

export default {
    entry:  './src/index',
    output: {
        path: join(__dirname, 'dist'),
        libraryTarget: 'umd',
        library: 'randomCharacterName',
    },
    devtool: 'source-map',
    module: {
        loaders: [
            {test: /\.js$/, loader: 'babel', include},
            {test: /\.json$/, 'loader': 'json', include}
        ]
    }
}

Entry is pretty simple, where do we look for the app? Output is also fairly self-explanatory, where do we put the distributed files and what do we do with them. Path is the place, libraryTarget is what format we will be exporting, in our case we are using umd and finally library is just the name of the library. The devtool section specifies a tool to help with debugging in our case we are using source-map to help us track down where errors are actually occurring.  The modules section allows us to use certain loaders. In our case we will be using babel load and json loader.  In fact we should probably install them!

$ npm i -D babel-loader json-loader

Now that those are in place we need to modify a few things in our package.json file. We are going to rename build to build:main, add build:umd and build:umd.min.

glyphicons-424-git-create
"build:main": "babel --copy-files --out-dir dist --ignore *.test.js src",
"build:umd": "webpack --output-filename index.umd.js",
"build:umd.min": "webpack --output-filename index.umd.min.js -p",

Go ahead and run:

$ npm run build:umd

Neat! Only problem now is how to we run all of these when we need to build again… There is a nifty package just for that called npm-run-all.

$ npm i -D npm-run-all

Now jump back to package.json and add in the new supercharged build script:

"build": "npm-run-all --parallel build:*",

Now after you have this all committed and pushed take a look at npmcdn.com/name-of-your-library. See mine for an example. You can go to a specific version by adding an @ to the end of the url and specifying the semver i.e. 1.6.0. You can also specify which one you want by adding a pointer to a specific file in your dist directory. 

https://npmcdn.com/random-character-name@1.6.0/dist/index.umd.min.js

All cool stuff! This is the conclusion of this tutorial. My next one will be building out an API, stay tuned.

glyphicons-55-clock  = Time Saving Idea

glyphicons-499-sunglasses = Pro Tip

glyphicons-31-pencil = Note

glyphicons-197-exclamation-sign = Alert

glyphicons-424-git-create = Code Update

glyphicons-28-search  = A Closer Look

Publishing an NPM Module Part 6

These are my notes based on the phenomenal tutorial by Kent C. Dodds hosted by egghead.io both of which are my go to sources when I want to learn something new. I highly suggest you sign up for the pro subscription.

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | >Part 6 | Part 7

Part 6: Adding ES6 Support

We need to add babel-cli to our dev dependencies:

$ npm i -D babel-cli
glyphicons-28-search  The Docs 
Common uses:
babel -w -o  //watch for file changes and output
babel src -o  // compile entire src dir and concat
babel src -d lib //compile entire src dir and output to lib
babel --ignore  //add this to ignore files i.e. tests

Next we will add a build script for babel. Looking in our node_modules/bin we can see babel has been added making it available to npm for scripts. Let’s edit our package.json to include:

"build": "babel --out-dir dist src”

Now let’s test it to see what gets generated when we run our script. In your terminal run:

$ npm run build

Look in the dist directory and you will see that it was created and files were moved over. It also added our test files! This is something we don’t want to push to the dist since if we test when we publish, not when we consume. We can solve this by adding an ignore flag:

“build”: ''babel --out-dir dist --ignore *.test.js src"

Since this creates a dist directory with files in it, we want to make sure that if we make a change in a filename that the old one is not present in the next build in order to delete the directory cleanly we will add a prebuild script to package.json

"prebuild": "rm -rf dist”

However this is only *nix supported in order to allow windows devs to use this we must add a new dev package called rimraf  a cross-platform rm -rf built for node.

$ npm i -D rimraf

replace rm-rf with rimraf:

"prebuild": "rimraf dist”

now convert to es6 i.e.:

glyphicons-424-git-create
 
'use strict'
//require external dependencies
import uniqueRandomArray from 'unique-random-array';
import _ from 'lodash';

//define available data
import firstNames from '../data/first-names.json';
import middleNames from '../data/middle-names.json';
import lastNames from '../data/last-names.json';

//random generators
let randomFirstName = uniqueRandomArray(firstNames);
let randomMiddleName = uniqueRandomArray(middleNames);
let randomLastName = uniqueRandomArray(lastNames);

//filter functions
const filteredNames = function (nameList, initial) {
 return nameList.filter(function (name) {
 return name[0] === initial;
 })
};

//methods
const list = ()=>{
 let allNames = ["FirstName MiddleName LastName"];
 for (var i = 0; i < firstNames.length; i++) {
 //Math.floor((Math.random() * 10) + 1);
 var tmpName = randomFirstName() + ' ' + randomMiddleName() + ' ' + randomLastName();
 allNames.push(tmpName);
 }
 return allNames;
};


const single = ()=>{
 return randomFirstName() + ' ' + randomMiddleName() + ' ' + randomLastName();
};

const startsWithLetter = (f, m, l)=>{
 var firstName = _.sample(filteredNames(firstNames, f));
 var middleName = _.sample(filteredNames(middleNames, m));
 var lastName = _.sample(filteredNames(lastNames, l));
 return firstName + ' ' + middleName + ' ' + lastName;
}

const numberOfNames = (number=1, allNames=[])=>{
 for (var i = 0; i < number; i++) {
 var tmpName = randomFirstName() + ' ' + randomMiddleName() + ' ' + randomLastName();
 allNames.push(tmpName);
 }
 return allNames;
}

//available methods
module.exports = {
 list: list,
 single: single,
 startsWithLetter: startsWithLetter,
 numberOfNames: numberOfNames
}

Now lets run our build script again:

$ npm run build

Now let’s take a look at index.js in dist and notice nothing happened! That is because really all we did was have it copy it over. We did not tell it to do anything! So we need to install some presets to tell babel how to do the transpiling:

npm i -D babel-preset-es2015 babel-preset-stage-2
glyphicons-28-search Learn more about presets

In order for babel to know about these presets we have added we need to change our package.json to reflect this:

"babel": {
  "presets": [
    "es2015",
    "stage-2"
  ]
}

 

 

We need to change our package.json file to point to dist now:

"main": "dist/index.js",

While we are here we should update the build script by adding –copy-files:

"build:main": "babel --copy-files --out-dir dist --ignore *.test.js src",

 

Now we need to make sure the build script runs before the publish script. We do this by modifying the .travis.yml file scripts section, after check-coverage add:

- npm run build

Now we need to do a little verification on our changes. Let’s run a command to see what will actually be published:

$ npm pack   

A file was generated ending in tgz. Open that up and take a peek. It looks alright, but there are just some extra files in there that we might not need or want. So let’s learn how to limit what gets packed up. We do this by adding a files section to our package.json file:

"files": [
  "dist",
  "data",
  "README.md"
],

  Now that we are writing in ES6 we want to make sure that all of the other pieces are able to work with the new syntax as well. Let’s start with istanbul. It turns out we need a replacement package for that called nyc.

$ npm i -D nyc

Now we need to update package.json to use this new method:

glyphicons-424-git-create
"check-coverage": "nyc check-coverage --statements 100 --branches 100 --functions 100 --lines 100",

Next in the test with the watch command (-w) we want to add in something that will transpile our code. 

"test:watch": "mocha src/index.test.js --compilers js:babel-register -w",

Since we are using it we might as well install it!

npm i -D babel-register

The tests are getting a little clunky, with a lot of repeated code so we are going to clean things up a bit. by letting the watch test just fire off the regular test adding in a -w flag.

"test:watch": "npm t -- -w",
"test": "mocha src/index.test.js --compilers js:babel-register",

Note the double dash in the watch script. This allows you to send in the watch flag to the previous call as if it were in the original.  Since we are cleaning things up let’s go a little farther, but adding a cover script.

"cover": "nyc --reporter=lcov npm t",

We now need to adjust our githook. 

"config": {
  "ghooks": {
    "pre-commit": "npm run cover && npm run check-coverage"
  }

One more spot in .travis.yml we need to make sure we run our cover script so it ends up looking like:

script:
  - npm run cover
  - npm run check-coverage
  - npm run build

 

There we go! We now have an es6 friendly set up!

Next… UMD Build!

glyphicons-55-clock  = Time Saving Idea

glyphicons-499-sunglasses = Pro Tip

glyphicons-31-pencil = Note

glyphicons-197-exclamation-sign = Alert

glyphicons-424-git-create = Code Update

glyphicons-28-search  = A Closer Look

Publishing an NPM Module Part 3

These are my notes based on the phenomenal tutorial by Kent C. Dodds hosted by egghead.io both of which are my go to sources when I want to learn something new. I highly suggest you sign up for the pro subscription.

Part 1 | Part 2 | >Part 3 | Part 4 | Part 5 | Part 6 | Part 7

Part 3: Setting up Unit Testing

There are tons of testing libraries out there. I am going to stick with the more common ones mocha and chai for now. In later parts I will introduce a few more. In order to use them we should install them!

Head to your trusty console:

$ npm i -D mocha chai

glyphicons-499-sunglasses  the above is the same as ‘npm install mocha chai –save-dev’ only faster!

Create index-test.js in the same directory.

$ touch src/index.test.js

Now looking at our index.js we see we have a few things to test. At least 4 since we do have 4 methods.

module.exports = {
    list: list,
    single: single,
    startsWithLetter: startsWithLetter,
    numberOfNames: numberOfNames
}

Let’s start building our index.test.js. We will first wrap our initial section in a describe block. This is just a container to describe what we should expect to see tested inside.

var expect = require('chai').expect;
var rng = require('./index');

describe('Our first tests', function () {
   
});

Our list method should return an array containing strings. So let’s test for that!

describe('tests should be running', function () {
    describe('list()', function () {
        it('Expect it to return an array of strings', function () {
            expect(rng.list()).to.satisfy(isArrayOfStrings);

            function isArrayOfStrings(array) {
                return array.every(function(item){
                    return typeof item === 'string';
                })
            }
        });
    });
});

glyphicons-28-search Let’s take a closer look at each part in the above test. We have another describe block, this time focused on the all method. This is a nice way of grouping our tests to keep them focused. The first argument in describe is a string denoting the topic, the second is a function that will contain our actual tests and also could be used to set up mock data if we needed it.

The it is similar to describe in that you first give it a string but this time it will be much more specific. This is what you are actually testing. In this example we want to make sure that the all function returns an array of strings. The second argument is again a function block where we will do the specific test.

One of the benefits of this style of testing is to create very readable tests. Even if you don’t know the nitty gritty you could read the test and it will explain itself. Using expect or should gives you a clear idea of what we are looking for. They are pretty similar, however there are some syntactical differences to be aware of. (read more)

These allow you to chain together natural language assertions. Expect takes the value you want to test and then you chain together what assertions you need to use in order to test. The language used to chain the expect and the test generally do not  provide testing functionality.

List of Chains:

    .to
    .be
    .been
    .is
    .that
    .which
    .and
    .has
    .have
    .with
    .at
    .of
    .same

These get chained together finally ending in an actual test assertion. For example in my test we see:

expect(rng.list()).to.satisfy(isArrayOfStrings);

This means that we expect that when we execute rng.list() that it will satisfy the given truth test. Here our truth test is another function that checks to see if every item in the array has a type of string.  This is very flexible and will help you easily generate tests. The full api can be read here. Here are some of the ones I use most:

.match(regexp)

  • @param { RegExp } RegularExpression
  • @param { String } message _optional_

Asserts that the target matches a regular expression.

expect('foobar').to.match(/^foo/);

.instanceof(constructor)

  • @param { Constructor } constructor
  • @param { String } message _optional_

Asserts that the target is an instance of constructor.

var Tea = function (name) { this.name = name; }
  , Chai = new Tea('chai');

expect(Chai).to.be.an.instanceof(Tea);
expect([ 1, 2, 3 ]).to.be.instanceof(Array);

.equal(value)

  • @param { Mixed } value
  • @param { String } message _optional_

Asserts that the target is strictly equal (===) to value. Alternately, if the deep flag is set, asserts that the target is deeply equal to value.

expect('hello').to.equal('hello');
expect(42).to.equal(42);
expect(1).to.not.equal(true);
expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' });
expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' });
Now that you are familiar with the basics of testing write a few more tests for your functions. Here is an example of basic testing covereage:
var expect = require('chai').expect;
var randomNameGenerator = require('./index');


describe('tests should be running', function () {
    describe('list()', function () {
        it('should be an array of strings', function () {
            expect(randomNameGenerator.list()).to.satisfy(isArrayOfStrings);

            function isArrayOfStrings(array) {
                return array.every(function(item){
                    return typeof item === 'string';
                })
            }
        });

        it('should contain `FirstName MiddleName LastName`', function () {
            expect(randomNameGenerator.list()).to.include('FirstName MiddleName LastName');
        });
    });

    describe('single()', function () {
        it('should be a string', function () {
            expect(randomNameGenerator.single()).to.be.a('string');
        });

        it('should contain three names', function () {
            var arrayOfName = randomNameGenerator.single().split(' ');
            expect(arrayOfName).to.have.lengthOf(3);
        });
    });

    describe('startsWithLetter()', function () {
        it('should be a string', function () {
            expect(randomNameGenerator.startsWithLetter('A', 'C', 'E')).to.be.a('string');
        });

        it('should contain three names', function () {
            var arrayOfName = randomNameGenerator.startsWithLetter('A', 'C', 'E').split(' ');
            expect(arrayOfName).to.have.lengthOf(3);
        });

        it('should start with the passed values for f,m,l of A, C, E', function () {
            var arrayOfName = randomNameGenerator.startsWithLetter('A', 'C', 'E').split(' ');
            expect(arrayOfName[0][0]).to.equal('A');
            expect(arrayOfName[1][0]).to.equal('C');
            expect(arrayOfName[2][0]).to.equal('E');
        })
    });

    describe('numberOfNames()', function () {
        it('numberOfNames(3) should have a length of three', function () {
            var arrayOfNames =  randomNameGenerator.numberOfNames(3);
            expect(arrayOfNames).to.have.lengthOf(3);

        });

        it('numberOfNames() should default to one', function () {
            var arrayOfOneName = randomNameGenerator.numberOfNames();
            expect(arrayOfOneName).to.have.lengthOf(1);
        });
    });

});

Now that we have some basic tests we will create a script to run them. Open package.json and in the scripts section we will change the test to:

"test": "mocha src/index.test.js -w"

The -w watches the file system for changes and re-runs the test. There are many more options head to mochajs.org and search for usage:

There we go, we have set up a basic testing framework!

Next… Automating releases

Key:

glyphicons-55-clock  = Time Saving Idea

glyphicons-499-sunglasses = Pro Tip

glyphicons-31-pencil = Note

glyphicons-197-exclamation-sign = Alert

glyphicons-424-git-create = Code Update

glyphicons-28-search  = A Closer Look

Just in case…

Just in case you pushed up a change with something that should remain hidden! Not that any of us have ever done this, but we all know someone who has. A nice simple way to purge that data. Go here!

5 Myths About Javascript

1. JavaScript is ECMAScript.

ActionScript and JScript are other languages that implement the ECMAScript.  JavaScript was submitted to ECMA for standardization but due to trademark issues with the name Javascript the standard became called ECMAScript. Every browser has a JavaScript interpreter.

2. JavaScript just for the front end.

With the advent of nodejs and io.js (and others) this is no longer true. JavaScript can now be used to build full fledged RESTful and socket based API’s. Take a look at loopback.io and hapi.js for two (of many) API libraries.

3. JavaScript is simple.

There was a period of time that JavaScript was for front end DOM manipulation and that was about it. Over time the language has matured into a wonderful and powerful functional programming language that can emulate multiple types of inheritance paradigms.

4. JavaScript is not scalable

If this were the case we would not see companies like Google and Facebook spending so much time using it. These guys know scalable and almost everything you use on both of those sites rely on the fact that it is scalable, modular and extensible.

5. JavaScript is boring

No way! You can do so much with it that boring should never enter your mind. Even if you think you have mastered the language, if you explore a bit more you will find the weird stuff left over to maintain backwards compatibility. Check out wtfjs.com for more.