Typescript ambient module declarations

With ES2015 JavaScript got the concept of modules using the export and import keywords. Typescript supports this and and it all works well as long as you are writing your modules in TypeScript. But to use external libraries or code that is not written in TypeScript you need to have a type declaration. For all major libraries these already exists in the definitelytyped repository which can be queried via TypeSearch. But sometimes you’ll have to write one yourself.

I had some problems understanding how these declaration should be consumed and found the documentation a little bit tricky to understand which made me to write this.

Ambient modules

From TypeScript documentation:

We call declarations that don’t define an implementation “ambient”. Typically, these are defined in .d.ts files. If you’re familiar with C/C++, you can think of these as .h files

So to write type declarations for code that is already written in JavaScript we have to write an ambient module. As stated above these are almost always defined in a file ending with a .d.ts extension.

How can we define an ambient module ? There are two ways.

Global declarations

When using declare module to create an ambient module the naming of the .d.ts file doesn’t matter what’s important is that the file is included in the compilation.

declare module "my-module" {
    export const a: number;
    export function b(paramA: number): void;
}

When the file above is included in the compilation TypeScript will register that there’s a module named my-module which then can be imported

import { a, b } from "my-module"

There are different ways to include declaration files in the compilation

  1. Specify path in the typeRoots compilerOptions in tsconfig.All global declarations files under typeRoots will be automatically included. This can be controlled with the types property compilerOption where you can explicitly control which definitions should be automatically included
  2. Specify the files property in tsconfig so that the declaration file is included
  3. Use the tripple slash directive /// <reference path="..." />
  4. With the help of paths compilerOptions in tsconfig

From TypeScript documentation

Keep in mind that automatic inclusion is only important if you’re using files with global declarations (as opposed to files declared as modules). If you use an import “foo” statement, for instance, TypeScript may still look through node_modules & node_modules/@types folders to find the foo package.

Files declared as modules

Using top-level export and import module declarations the .d.ts does not need to be included in the compilation. The important thing here is that the file is named index.d.ts and resides in a folder named after the module which in this case is my-module

// index.d.ts
export const a: number;
export function b(paramA: number): void;

// In a file importing the library
import { a, b } from "my-module"

What TypeScript will do is by default to try and lookup my-module. It will try with a number of different steps looking for both code(ts files) and declarations(.d.ts files). One of the steps is to look for declaration files in node_modules/@types. It will look for a folder named like the imported module with an index.d.ts file looking like the one above.

Sometimes you don’t wan’t to publish declaration files to definitelytyped and have folder with custom type declarations and therefore inform TypeScript to look for declarations in other folder than node_modules/@types. This can be done with the help of compilerOption paths.

{
"baseUrl": ".",
"paths": {
"*": [
"custom-typings/*"
]
}
}

With this configuration in tsconfig.json TypeScript will look for code and declaration files in the custom-typings folder.

To verify where TypeScript is trying to resolve things you can run the compiler with the traceresolution flag

tsc --traceresolution

Unit testing with jspm, karma, mocha and react

Javascript biggest downside is all the dependencies you need to bring in and configuration needed to get something similar as unit testing going. Imagine your psyched for this idea of a code project you just had and just wan’t to get started coding. But after a few nights of your precious spare time you almost give up because you even haven’t started coding yet. All this packages and configuration is killing your motivation. Javascript fatigue… Enough ranting…

I’ve been struggling for some days now to get unit testing with react in a project using jspm. I’m documenting this to hopefully save future me and someone else the hours and frustration.

Jspm

Jspm is a package manager for Javascript that aims to solve the problem of different module formats in JavaScript(ES6, AMD, CommonJS, and globals). Jspm uses the SystemJS module loader which handles all this cases and also can handle registries other than npm like github.

When developing your Javascript application jspm uses a transpiler of your choice to transpile your next generation Javascript code, TypeScript or whatever needs transpiling directly in the browser. When going to production you can produce a bundle.

Karma

Karma is a test runner which runs your tests in a browser or even headless browsers like PhantomJS.  It spawns a web server that executes the test against each browser which then displays the results in the command line. It’s primary usage is for unit testing and not end to end testing.

Karma can watch specified files and run the tests whenever something changes. It also has the functionality to load preprocessors which means that your javascript files can be processed before getting served to the browser. One example could be running the files through babel or webpack.

It’s testing framework agnostic and there’s support for every popular testing framework out there like Mocha, QUnit and Jasmine and so on. If it doesn’t you can build your own adapter.

Mocha

My previous post was about unit testing with mocha. Read it here.

React

React is Javascript library for building user interfaces. Enough of that. Let’s get started.

Installation

I’ve already created jspm-react-boilerplace. I will use that as a starting point. So start by cloning the github repo and install the packages.

$ git clone https://github.com/Jontem/jspm-react-boilerplate.git
$ npm install
$ jspm install

After the 1000 dependencies have finished installing we need to install karma and mocha along with some required karma plugins.

$ npm install --save-dev karma karma-chrome-launcher karma-jspm karma-mocha mocha

After another 1000 packages is downloaded we can run.

$ ./node_modules/.bin/karma init

Just choose mocha as the testing framework and Chrome for browser then choose the defaults. We will edit the config file directly. If you don’t want to specify the path to karma you can install it globally. But after we add an npm script we don’t need to worry about that.

Open karma.conf.js and find the frameworks property and add jspm.

    frameworks: [
      'jspm',
      'mocha'
    ]

After frameworks add a jspm configuration section

jspm: {
      loadFiles: [
        'test/**/*.js',
        'src/**/*.js'
      ]
    }

This will make jspm run a transpiler on the files specified before handing them over to mocha.

The last thing we need to add to the karma cofig is the proxies property and this is because karma loads our specified files under the base url /base/. We can tell the karma webserver that som paths should automatically be proxied to /base/

proxies: {
      '/test/': '/base/test/',
      '/src/': '/base/src/',
      '/jspm_packages/': '/base/jspm_packages/'
    }

Let’s install two more packages… Chai which is an assertion library and react-addons-test-utils

$ jspm install --dev npm:chai npm:react-addons-test-utils

Now edit package.json so we can run npm test. The script property should have these entries.

"scripts": {
    "start": "npm run webserver & npm run watch",
    "webserver": "node server.js",
    "watch": "chokidar-socket-emitter",
    "test": "karma start"
  }

Running npm start launches a browser that will execute the tests. But it will fail because it won’t find any tests. So lets add one. We start by creating the test/ and src/ directories.

Create a file hello_world_component.test.js under the test/ directory.

import {assert} from "chai";
import TestUtils from "react-addons-test-utils";
import * as React from "react";
import DOM from "react-dom";
import {HelloWorld} from "../src/hello_world_component";

describe("Hello world component", function () {

    it("should should display the text hello world", function () {
        var renderer = TestUtils.createRenderer();
        const result = renderer.render(<HelloWorld />);

        assert.equal(result.type, "div");
        assert.deepEqual(result.props.children, [
            <h1>Hello world</h1>,
            <span>test</span>
        ]);
    });

});

Next we create the file which defines the component hello_world_component.js under src/

import * as React from "react";

export const HelloWorld = React.createClass({
    render() {
        return (
            <div>
                <h1>Hello world</h1>
                <span>test</span>
            </div>
        );
    }
});

Now run npm test and you should have one green test!

The benefit of using karma is that we’re running our tests in a browser environment which means we have access to DOM which some of React test utils needs for simulating DOM events or rendering deeper hierarchies of react components. In our tests above we make use of shallow rendering which only renders react components one level deep and doesn’t need access to a DOM. Read more about React test utils on reacts website.

If you think this post was long you don’t have any idea of the struggle to get this working :). Oh one last thing… If you want to use the renderIntoDocument method be aware that stateless components cannot be tested. It will only return null, I don’t know why… But it was hidden somewhere in the documentation.

Getting started with Mocha and unit testing

Mocha

Mocha is a JavaScript test framework running on NodeJS and in the browser. It’s feature rich and has great support for asynchronous testing. It also gives you the possibility to use the assertion library of choice.

A selection of features

  • Async support
  • Test coverage reporting
  • Javascript API for running tests
  • file watcher support
  • Highlights slow tests
  • Transpiler support

Run our first test

Getting started with mocha is easy.

npm install --global mocha
npm install --save-dev mocha
mkdir test

Lets add our first test
test/first_test.js

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

Now we just run mocha

mocha

You should be getting an output like this.
mocha test results

Well that wasn’t too complicated. So what happened.

First we used a built in node module called assert. Basically it just evaluates an expression and throws and exception if the result is false. Most times you would use a more advanced assert library like ChaiJS.

Then we used the describe function which takes a string and a callback. It’s used for grouping tests and as seen above you can nest describe functions.

The It function takes a string and callback which will make one test case. We then assert the result which mocha displays in it’s test reporter.

Specifying tests to run

When we run mocha it looks for test with this glob pattern test/*.js.

If we want to look for tests recursively

mocha --recursive

Or if you want to specify another directory for your tests

mocha -- path/to/tests/*.js

And if you don’t wan’t to re run your tests you can tell mocha to watch your test files for changes.

mocha --watch

To see all parameters just run mocha --help. In my osx terminal i had this strange issue that the help output was cut off so i only got a third of the output. Upgrading node from 6.2.0 to 6.5.0 solved that problem.

Using the “fat arrow”(arrow functions)

You can use arrow functions with Mocha  to specify callbacks to describe and It. But because of the lexical binding of this it’s discourage because you wont be able to access Mocha context. With the mocha context you can specify how the tests should behave for example this.skip() and this.slow(1000). The skip function explains itself and the slow function defines a threshold for mocha to mark this test as slow which the test reporter will notify you about.

Testing async code

Lets add a new test.
test/async_test.js

var assert = require('assert');
describe('Async', function () {

  describe('callbacks', function () {
    it('should return 123 after 3 seconds', function (done) {
      this.timeout(4000);
      asyncWithCallback(function (data) {
        assert.equal(data, 123);
        done();
      })
    });
  });

  describe('promises', function () {
    it('should return 567 after 1 second', function () {
      return asyncWithPromise().then((data) =&gt; {
        assert.equal(data, 567);
      });
    });
  });

});

function asyncWithCallback(callback) {
  setTimeout(() =&gt; {
    callback(123);
  }, 3000);
}

function asyncWithPromise() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      resolve(567);
    }, 1000);
  });
}

With mocha we can make use of the done callback to specify when asynchronous tests are done or we can return a promise which mocha will wait for being resolved. Mocha has a default timeout of 2000ms so if a test is taking longer you need to use the mocha context to set a higher timeout.

Conclusion

It’s really simple and fast to get mocha up and running and write simple unit tests. Head over to the Mocha website fore more detailed information.