Saturday, May 7, 2016

Testing TypeScript Modules with Jasmine

In my previous post I talked about using TypeScript/ES6 modules. I have been thinking about making the transition from namespaces to modules, but there were a few things I needed to work out first. First of all how to load them (native ES6 support isn't widely available at the time of this writing). In the previous post I talked about using a library called Require1k (http://requirejs.org/) as the module loader. As the name suggests it is only 1kb in size yet provides the basics you need for CommonJS module loading. So far it does everything I need.

The next thing I needed to work out was how to package up my application when it was ready for release. In that post I also talked about using WebPack in Node.js to combine and minify the application modules.

The final thing I needed to work out was how to get my Jasmine (http://jasmine.github.io/) unit tests to run against modules. This was giving me a lot of trouble; it's not as easy as you might think. Jasmine was built in such a way that it expects all of your files to be loaded using script tags, then it runs when the page onload event fires. Unfortunately when using a module loader things don't happen that way. The module loader doesn't load your modules until after the page is loaded.

So for the remainder of this post I'll show how I got my Jasmine tests to run using the Require1k module loader. Although I'm using a specific module loader there shouldn't be any problem using the same principles for any other CommonJS module loader library as they most all work the same way.

Creating the Unit Tests


The first thing we need to do is create unit tests. I won't go into how to write Jasmine tests. There are plenty of good examples on their web site. The only difference is that you will need to use import statements to reference the modules you want to test.

For example, say you want to test module1.ts. Then your unit tests might look something like this.

/// <reference path="../scripts/typings/jasmine/jasmine.d.ts" />
import * as Module1 from "./module1";
describe("When testing module1", () =>
{
    it("should be true when something happens", () =>
        expect(Module1.something()).toBeTruthy());
});

First we have a reference to the Jasmine type definition file (because as TS programmers we like to have better tooling and autocompletion). Then we import everything from module1. Then we define the Jasmine specs for the unit tests. It's that simple ;-).

One important thing to point out here: your unit tests are modules too. Once you decide to use modules everything needs to be a module. Our unit tests need to load the modules they test, therefore they have to be modules for the module loader to pick up the imports and load them.

The Bootstrap File


Now we need a bootstrap file. This is the main file that gets things going. We will call this file unit-tests.ts.

declare function require(s: string): void;
 
require("./module1.spec");
require("./module2.spec");
 
jasmine.getEnv().execute();

The first thing we did was to declare the require function. This is the function defined by the Require1k library (or any other library for loading CommonJS modules) to load a module file. Without this we would get errors from the compiler because it doesn't know what "require" is.

Next we use require to load all of the unit test modules. In this case we have unit tests for two different modules.

I like to name my unit test files with the name of the module it's testing and a ".spec.ts" extension. This way you know exactly which module it's for and you know it's a Jasmine test file (Note: unit tests are known as specs in Jasmine).

The last line is necessary to tell Jasmine to start running the tests. As I stated above, Jasmine will look for any unit tests and run them on page load. But since this is all happening after page load we have to explicitly tell it to run again.

You might be asking yourself: why not use import statements to load all of the unit test files? That's a valid question. The unit tests are modules, I said so previously. The answer is: because nothing is being exported from the unit test modules. All they do is define Jasmine specs. Therefore if you tried to import them the TypeScript compiler would see there's nothing being exported and just skip it. Then your unit tests wouldn't load and you would be wondering what's going on (not that I'm speaking from experience or anything like that).

The Host File


The final thing we need to do is set up the host HTML file to run the unit tests. It will have all of the files for jasmine, of course. Then it will need the script tag for Require1k.

<link href="../Content/jasmine/jasmine.css" rel="stylesheet" />
<script src="../Scripts/jasmine/jasmine.js"></script>
<script src="../Scripts/jasmine/jasmine-html.js"></script>
<script src="../Scripts/jasmine/boot.js"></script>
 
<script src="require1k.js" data-main="./unit-tests"></script>

The "data-main" attribute tells Require1k what the main or start file is. In this case unit-tests.js which we created above. This will define the starting point of the module loader. From there it will load the unit test modules which will in turn load the modules they are testing.

Conclusion


The trick to testing external modules with Jasmine is to have a single bootstrap file that loads all of the unit test files and to tell the module loader where to find it. Then the key to making it work is to tell Jasmine to run again after all of the tests and modules have been loaded.

Switching from internal namespaces to external modules in TypeScript requires a whole new way of looking at things and some new ways of doing things that you're not used to. You just have to learn a few new tricks to get it to work across the board from your source code to your unit tests.

I think in the end using modules is the way of the future for TypeScript, and JavaScript. Once ES6 is fully supported by all browsers and able to provide native module loading things will be a lot easier and modular JS will become more popular.

<jmg/>

Wednesday, April 6, 2016

Packaging Web Apps with Node.js

There comes a time when you're at the end of developing your awesome TypeScript/JavaScript web app that you need to think about releasing it into the wild. If you've been using good programming practices you should have split your app into multiple files to make it easier to manage and maintain. Only problem is, it's not a good practice to serve up a ton of files when the user runs your app. The best practice in this scenario is to package up your JavaScript files into one file and only serve that one file.

In this post I will show you how to use TypeScript/ES6's module loading system and Node.js to make it easy to package your web app's files into one file for release while allowing you to develop without packaging. We will use TypeScript's module system to set up file dependencies. Then we will use Require.js to load those modules in the browser. Finally we will use Webpack to package up all of the files into a single JS file and compress it for release.

Prerequisites: I'm assuming you know TypeScript and Node.js basics, but even if you don't you should be able to understand the concepts and use them as a jumping off point to learn more.

The Modules


First of all lets look at how external modules work in TypeScript/ES6. For this example we are going to create three separate TS files, each of which depends on the next one. Two of the files will be modules and the other will be the main application file.

The first file module2.ts exports a class named MyClass2 with a public method getText().

export class MyClass2
{
    public getText(): string
    {
        return "Hello from module 2";
    }
}

Nothing special yet. We just have a class with a method.

The next file is module1.ts. It is a bit more interesting.

import * as module2 from "./module2";
 
export class MyClass1
{
    public getText(): string
    {
        let c = new module2.MyClass2();
        return "Hello from module 1 " + c.getText();
    }
}

At the top of the file we import our module2 module that was defined in module2.ts. Now this file depends on module2.ts. This allows us to use the exported members of module2.ts in module1.ts. This syntax follows the ES6 standard for importing modules.

Next we define the contents of module1. We export a class called MyClass1. In the getText() of this class it creates and instance of MyClass2 from module2 then calls its getText() method and concatenates the messages together.

That's how we do external modules in TypeScript and ES6. Pretty simple, right?

Now we need a main app file for our starting point, which in this case is app.ts.

import {MyClass1} from "./module1";
 
namespace WebpackModules
{
    export class MyApp
    {
        constructor()
        {
            let c1 = new MyClass1();
            var el = document.getElementById('content');
            el.innerText = "MyApp started " + c1.getText();
 
        }
    }
}
 
var app = new WebpackModules.MyApp();

We begin with an import of module1.ts. Now this file depends on module1.ts and by association module2.ts. Notice that the import is a little different from the previous one. Instead of importing everything from module1.ts and assigning a name for the module, we are only importing the MyClass1 class.

Next we wrap the MyApp class in a namespace just because this is what I would do in a real app to keep the global scope clean. If you're not familiar with it, namespace is the new keyword to create internal modules in TypeScript and is equivalent to the old keyword of module.

Inside the namespace we export the main application class, MyApp, so we can use it outside of the namespace. Inside the constructor is where we use the MyClass1 that we imported. We create a new instance of MyClass1, then call getText() on that object and output it to the web page.

The Script Tag


Now we need to set up our web page and get a module loader that can load the modules for us. At this time most browsers don't support the loading of modules using the ES6 specification. Therefore we are going to have to use something else. A well known module loading framework is RequireJS (http://requirejs.org/). So let's use that.

In order to use RequireJS we need to tell the TypeScript compiler which kind of package manager we are using. RequireJS implements the AMD standard so we must tell the TS compiler to use AMD. If you're in Visual Studio you can change it in the project properties. Otherwise change it in the tsconfig.json file ("module": "amd"). The TypeScript compiler will output the correct code that works with the package manager of your choice.

In the html file we only need one script tag that references the require.js file.

<script src="../Scripts/require.js" data-main="app"></script>


Notice that it has a data attribute called data-main. This is where you tell it what file is your entry point. In this case it's app.js, or just app (you don't need to specify the file extension). When the web page is loaded Require will load app.js first, then fulfill all of the imports specified inside the application's modules (module1.js and module2.js).

At this point the application is running and it is loading all the files separately. This great for our development environment. If all our files were packaged up in development it would make it a lot harder to debug.

Packaging the App


When it's time to release the app we need to package it up. For this we will be using a Node package called Webpack. You can easily install it using "npm install webpack -g". Then you can execute it from the command line telling it where your entry point file is and where to save the packaged file.

$ webpack ./src/app.js ./app.package.js


Here we tell it to start with app.js and save the result to app.package.js. Webpack will go through all of the imports just like Require did and load all the dependencies then save all of the code in the correct order in the output file along with any code it needs to bootstrap the app.

At this point you can change the require.js script tag to set data-main="app.package.js" and the application should run. Congrats, you now have a packaged up web application.

One thing to note about using Require, it's a very comprehensive library and therefore not a small file. The minified version is about 85kb. So you're making the user download 85kb just to run your app. If you don't need all of that functionality there is another, much smaller option, Require1k.

Require1k is a minimal library for loading CommonJS modules. If all you want to do is use modules in a web application it's perfect. And, as you can tell from the name, the file is only 1kb. That is a BIG difference. If you can use it instead you probably should.

To use Require1k we will need to change the TS compiler module type option to CommonJS ("module": "commonjs"). Other than that it works exactly the same. The script tag would look like this now:

<script src="require1k.js" data-main="./app"></script>


The only thing that changed was the name of the source file. From here we can use webpack just as we did before and everything should run, but with only 1k of bootstrap code.

Bonus Points: Minification


OK, all of our JS files are packaged up into one file. That's great but it could be better. We could minify the file to make it as small as possible. To do that we can use a Node package called Uglify. Install it using "npm install uglify-js -g".

It's very simple to use. Just point it at the file you want to minify.

$ uglifyjs ./app.package.js -o ./release/app.js -c -m


Use the -o option to specify where to save the minified file to. The -c option tells it to compress. The -m option tells it to mangle names which will rename everything it can to single letter names for more space savings.

Conclusion


Here we have seen how to use external modules in TypeScript/ES6 and how to use Require to load modules in the browser. Then we learned how to use the Node webpack tool to help us package up our TypeScript app for release while keeping them unpackaged for development. Then we seen how to use uglify to compress our application's code.

Knockout in Chrome Web Apps (Yes!)

In a previous post I lamented on the fact that you can't use Knockout when creating a Chrome web app. The problem is that Knockout doesn't play well with Chrome's content security policy (CSP) because its binding engine uses "new Function" to parse bindings. This could allow someone to inject malicious code into the Chrome app which has access to low level resources that normal HTML5 apps don't.

After looking around though I came upon a solution to the CSP problem in Knockout. It seems I wasn't the only one with the problem. I found that Brian Hunt created a binding provider for Knockout that is CSP compliant. The project, called Knockout Secure Binding (KSB) can be found on GitHub. It is a replacement for Knockout's built in binding provider.

KSB is not a complete replacement though. It is limited in the kind of bindings you can create. Since it can't use "new Function" to parse bindings you can't create more complex bindings such as passing parameters into functions, and only a subset of JS expressions are available. But these limitations can easily be resolved by being more mindful of your bindings.

For example, say you have a binding for a "like" button that sets a certain thing as being liked. It calls a function in your view model called "like" and passes in the thing to like and true or false.

<button data-bind="click: like("knockoutJS", true)">Like</button>
<button data-bind="click: like("knockoutJS", false)">Dislike</button>

This won't work when using KSB because it doesn't support passing parameters to functions. You will need to rewrite the binding so it doesn't have to pass parameters.

<button data-bind="click: likeKnockoutJS">Like</button>
<button data-bind="click: dislikeKnockoutJS">Dislike</button>

Now the expression is moved back into the view model into functions called likeKnockoutJS and dislikeKnockoutJS. The downside is that this is going to require your view model to have a lot more functions in it, but your bindings will probably be a lot cleaner in the end.

KSB supports most any logical expression including comparison and mathematical operators, so you can perform some basic binding expressions.

<button data-bind="visible: !knockoutJSLiked())">Like</button>
<button data-bind="visible: knockoutJSLikeCount() > 0">Dislike</button>

Here the like button is visible only when the knockoutJSLiked() function does not return true. The dislike button is only visible when the knockoutJSLikeCount() function returns a value greater than zero.

To use KSB you just need to tell Knockout to use it as your binding provider before applying your bindings. You create an instance of a ko.secureBindingsProvider passing in the options you need and set that as the ko.bindingProvider.

let options = {
    attribute: "data-bind",        // default "data-sbind"
    globals: window,               // default {}
    bindings: ko.bindingHandlers,  // default ko.bindingHandlers
    noVirtualElements: false       // default true
};
ko.bindingProvider.instance = new ko.secureBindingsProvider(options);
ko.applyBindings(this.viewModel);

For more info on using KSB see the GitHub page.

In my app (PowerChord) I had to do some reworking to get my bindings to work with KSB. I also had to rewrite some components to make them work, which was probably a good thing because they were a bit convoluted. KSB forced me to write my components the way they should have been written instead of trying to use shortcuts with complicated bindings in my view.

Overall I'm very pleased with KSB and highly recommend it if you are writing a Chrome web app or need to implement the CSP and want to use Knockout as your binding framework (which I highly recommend).