Step 1: Hello World

In this step, we are going to build the simplest possible webpage, and set it up with a test framework.

By the end of this step, we will have :

  • An End-To-End Test Framework
  • A Test to prove the existence of the webpage
  • The webpage
  • A development webserver

Specification

Even a simple application needs an specification, so that you know when it is finished.

My ‘Hello World’ application has this specification:

  • It is an HTML file, called index.html.
  • When viewed in a browser, the user will see an H1 header which says “Hello World”

Discussion

Very simple. Should take 10 seconds to create that, right?

Wrong.

I do Test-Driven Development, so I need to write a failing test before I write the code. Which won't take long.

But, since this is the start of the project, I also need to setup the testing infrastructure.

Testing is one of the rabbit holes that it is very easy to go down, when starting on a project. There are many ways to do it, and it can be very confusing whether some tools are complementary or direct replacements for each other.

I will endeavour to explain when you need to make a choice, and why I chose what I did.

I use both unit testing and end-to-end (E2E) testing in this project.

This step uses an E2E test. We do not yet have any code where a unit test would be useful.

Task Breakdown

So this is what we need to do to complete this step.

  1. Create my initial package.json
  2. Add a workflow tool
  3. Setup an E2E test system.
  4. Write a failing E2E test which tests :
    • Is H1 header present?
    • Does it contain "Hello World!"?
  5. Write the index.html file.
  6. Watch the test pass.

1. Create the Package.json

Npm gives you the choice of installing modules locally or globally.

I want all the npm packages to be installed locally, so that I can maintain different projects with different versions of modules if necessary in different projects.

I create a very basic package.json.

{
    "name": "ng2-firebase-howto",
    "version": "0.0.1",
    "author": {
        "name": "Rachel Willmer",
        "email": "[email protected]"
    },
    "homepage": "https://github.com/rwillmer/ng2-firebase-step-by-step",
    "repository": {
        "type": "git",
        "url": "https://github.com/rwillmer/ng2-firebase-step-by-step"
    }
}

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/86105f6861c929979884100624d9eea6ceed8b4e

2. Add A Workflow Tool: gulp

There are 2 common workflow tools used in the AngularJS world: gulp or grunt. Both have their advocates. I prefer gulp.

http://gulpjs.com

I follow the instructions at https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md

$ npm install --save-dev gulp

Create a basic gulpfile.js

var gulp = require('gulp');

gulp.task('default', function() {
  // place code for your default task here
});

Run it to check the installation succeeded.

$ gulp

Gulp should run, but it is not yet configured to do anything.

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/0e3db9e64071d55d72ba25f03a0b525687904efd

Ignore the node_modules directory in git

Running 'npm_install' has created a node_modules directory.

We want git to ignore this directory, so we use the .gitignore file to do that.

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/7b2c9986711ffce5fde056846fa3bc678e3682d4

3. Add an E2E test system: protractor

Protractor is the E2E test framework for AngularJS.

http://www.protractortest.org

$ npm install --save-dev protractor
$ node_modules/protractor/bin/webdriver-manager update

Now I can start the Selenium server to run tests against with

$ node_modules/protractor/bin/webdriver-manager start

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/f9b195150eea36d69e2878fbb08988b66c225f45

Configure Protractor

Now I need to decide on my directory structure and configure protractor.

  • The unit tests will reside in the component directory, along with the code they are testing.
  • The e2e tests live in a top-level directory.

Protractor expects to run against an AngularJS system; but this first test is written before I’ve added AngularJS into the project.

So I add a workaround to enable me to use Protractor to be used to test both Angular and non-Angular sites. (Thanks to ng-learn.org for this tip http://ng-learn.org/2014/02/Protractor_Testing_With_Angular_And_Non_Angular_Sites/)

So here’s the protractor.conf.js file which sets up Protractor to:

  1. Specify that we want to use Jasmine2 (default is Jasmine 1)
  2. Know where its Selenium server can be found
  3. Know where the tests to be run can be found
  4. Allow for the testing of a non-Angular site

    exports.config = {

    framework: 'jasmine2',

    seleniumServerJar: '../node_modules/protractor/selenium/selenium-server-standalone-2.47.1.jar',

    // location of E2E test specs specs: [

     '../e2e-tests/tests/**/*.spec.js'
    

    ],

    // allow for testing of non-Angular sites // // From http://ng-learn.org/2014/02/Protractor_Testing_With_Angular_And_Non_Angular_Sites/ // onPrepare: function(){

     global.isAngularSite = function(flag){
       browser.ignoreSynchronization = !flag;
     };
    

    } };

I can confirm that the configuration is complete by running protractor.

$ node_modules/protractor/bin/protractor e2e-tests/protractor.conf.js

This will fail with an error saying that there are no files in the specified directory. We'll fix that in a minute.

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/9d892469166690740db4c2fe90d4ffe71b4b571d

Run e2e tests from gulp

But I don’t want to do that manually; I want to set up a workflow in gulp to automatically start the server and run any E2E tests found. Then all my workflow tasks are in one place.

$ npm install --save-dev gulp-protractor

https://github.com/mllrsohn/gulp-protractor

Add this to gulpfile.js

var protractor = require("gulp-protractor").protractor;
var protractor_conf = require('./e2e-tests/protractor.conf');

gulp.task('e2e-test', function() {

    gulp.src(protractor_conf.config.specs)
        .pipe(protractor({
            configFile: "e2e-tests/protractor.conf.js",
            args: ['--baseUrl', 'http://127.0.0.1:8000']
        }))
        .on('error', function(e) { throw e });

});

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/1b29c7c236f9d62308dec7b584dfa83809f261dd

Run webserver when e2e-test is running

$ npm install --save-dev browser-sync

Add this to gulpfile.js

var browserSync = require('browser-sync');

Update the e2e-test task to start/stop the webserver:

gulp.task('e2e-test', function() {

    var bs = browserSync.create();
    bs.init({
        server: {
            baseDir: "src/client"
        },
        open: false
    });

    gulp.src(protractor_conf.config.specs)
        .pipe(protractor({
            configFile: "e2e-tests/protractor.conf.js",
            args: ['--baseUrl', 'http://127.0.0.1:3000']
        }))
        .on('error', function(e) {
          throw e
        })
        .on('end', function () {
          // Close browser sync server
          bs.exit();
        })
});

Add a new gulp task to fire up a webserver when needed for development.

gulp.task('serve-dev', function() {

  /**
   * Run Browsersync with server config
   */
  browserSync({
      server: "src/client",
  });
});

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/c781114ef18de01bd96cf8f6cb4c0e5397e973a7

4. Write The Failing Test

So finally we can write a test which checks whether our webpage says 'Hello World!'

See e2e-tests/tests/0001_helloworld_html.spec.js for the example.

Run the test and it will fail.

$ gulp e2e-test

The error looks like this.

Failures:
1) Hello World in HTML should have an h1 which says Hello World
  Message:
    Failed: No element found using locator: By.id("h1")

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/30953070055af017aed46cfc519bdfc678e34705

5. Write the page that makes the test pass.

Create src/client/index.html

<html>
<body>
    <h1 id='h1'>Hello World!</h1>
</body>
</html>

Snapshot: https://github.com/rwillmer/ng2-firebase-step-by-step/commit/171eaf1681ebd629fd26fa44352235872d1803b6

6. Step 1 Complete

And that's it! Step 1 is complete.

We now have a 'Hello World!' HTML file, and an E2E test framework to test it.