Code Quality despite of JavaScript


Niko Köbler (@dasniko)
Heiko Spindler (@brainbrix)
Qualitects Group

WTFJS

http://wtfjs.com/

It's all(most)
about scope!

var that = this;

'use strict'

ECMA standards

code conventions

&

styleguides

Douglas Crockford
Google

and many more...

modules

RequireJS

CommonJS

static code analysis

JSLint
JSHint 

SidekickJS

Sonar Qube


ESLint

The pluggable linting utility for JavaScript


installation


on node.js

npm i -g eslint

run it

eslint [options] [file|dir]*

configuration

eslint -c myconfig.json model.js

sample

{
    "env": {
        "browser": true
    },
    "rules": {
        // Override our default settings just for this directory
        "eqeqeq": 1,
        "strict": 1,
		"quotes": 0,
		"no-extra-semi": 1
    }
}

output

 38:13 error   'require' is not defined                           no-undef
 41:18 error   'Java' is not defined                              no-undef
 43:11 error   'require' is not defined                           no-undef
515:69 error   'serviceContainer' is not defined                  no-undef
710:0  error   'exports' is not defined                           no-undef
 36:0  error   Use the function form of "use strict"              no-global-strict
 36:0  warning Strings must use doublequote                       quotes
152:36 error   It's not necessary to initialize 'cb' to undefined no-undef-init
173:63 warning Strings must use doublequote                       quotes
193:36 error   It's not necessary to initialize 'cb' to undefined no-undef-init
389:43 warning Strings must use doublequote                       quotes
491:15 warning Expected '!==' and instead saw '!='                eqeqeq
568:13 warning Unnecessary semicolon                              no-extra-semi
203:93 error   args is defined but never used                     no-unused-vars

define your own rules

module.exports = function(context) {
  return {
    "BinaryExpression": function(node) {
       var operator = node.operator;
       ...
       if (context.options[0] === "smart" && (isTypeOf(node) ||
           bothAreSameTypeLiterals(node)) || isNullCheck(node)) 
       {  return; }

       if (operator === "==") {
           context.report(node, "Expected '===' and instead saw '=='.");
       } else if (operator === "!=") {
           context.report(node, "Expected '!==' and instead saw '!='.");
       }
     }
}; }; 

Sonar Qube (tm)

static typing

???

TypeScript = ES6

types, interfaces, classes,
inheritence, modules, generics

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

var greeter = new Greeter("world");
alert(greeter.greet());

transpiles to native JavaScript/ES5

DefinitelyTyped

test driven development

e.g. Jasmine

Mocha

Jasmine

Behaviour-driven development

describe("A spec", function() {
  it("is just a function, so it can contain any code", function() {
    var foo = 0;
    foo += 1;
    expect(foo).toEqual(1);
  });

  it("can have more than one expectation", function() {
    var foo = 0;
    foo += 1;
    expect(foo).toEqual(1);
    expect(true).toEqual(true);
    expect(true).not.toBe(false);
  });
});

Mocha

describe('Array', function () {
  describe('#indexOf()', function () {
    it('should return -1 when the value is not present', function () {
      assert.equal(-1, [1, 2, 3].indexOf(5));
      assert.equal(-1, [1, 2, 3].indexOf(0));

      // same as above, other assertion notation
      [1,2,3].indexOf(5).should.equal(-1);
      [1,2,3].indexOf(0).should.equal(-1);
    });
  });
});

PhantomJS


Headless WebKit Browser
Scriptable with JS
Supports:  CSS Selectors, XPath, Canvas, SVG, ...

Some drawbacks: e.g. Alert dialogs don't work

CasperJS

Navigation scripting &
testing utility for PhantomJS.

casper.start('http://www.someurl.com', function() {
    casper.assertHttpStatus(200, 'Testlab is up');
	
    casper.capture('LoginScreen.png', {
        top: 0,  left: 0,   width: 1000,   height: 900
    });	
    casper.waitForSelector('#logonForm', function() {
    casper.fill('form#logonForm', {
        'user':    'heiko.spindler@hirnsport.de',
	'password':   'xxx'  }, true);
    });
    casper.wait(1000, function() { ... });
    casper.test.assertExists('Filterform(click to hide)', "Form found");
});
casper.run(); 

resurrectio

CasperJS test recorder Chrome extension

casperbox (alpha)

Run CasperJS scripts in the cloud
with simple REST API 

  • Scripts are limited to 10 KB and 30 seconds
  • One request per second per IP address

casperbox sample

$ curl --data-binary @hello-world.js http://api.casperbox.com/scripts -H 'Content-Type:text/plain'  
 
{
  "id": "430638dd-046a-4d1c-95cd-f510b752de32",
  "status": "QUEUED"
}
$ curl http://api.casperbox.com/scripts/ 430638dd-046a-4d1c-95cd-f510b752de32
{
  "id": "430638dd-046a-4d1c-95cd-f510b752de32",
  "source": "var casper = require('casper').create();\ncasper.start('http://casperjs.org', function() {\n\tcasper.echo('Hello, world!');\n});\ncasper.run();\n",
  "status": "RUN",
  "output": "Hello, world!" 
}

IDE usage

e.g. WebStorm

documentation

JSDoc

/**
 * Creates an instance of Circle.
 *
 * @constructor
 * @param {number} r The desired radius of the circle.
 */
function Circle(r) {
    /** @private */ this.radius = r;
    /** @private */ this.circumference = 2 * Math.PI * r;
}
 
/**
 * Creates a new Circle from a diameter.
 *
 * @param {number} d The desired diameter of the circle.
 * @return {Circle} The new Circle object.
 */
Circle.fromDiameter = function (d) {
    return new Circle(d / 2);
};

Thank you!

Copy of Code Quality despite of JavaScript

By Heiko Spindler

Copy of Code Quality despite of JavaScript

  • 2,509