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.
- Create my initial package.json
- Add a workflow tool
- Setup an E2E test system.
- Write a failing E2E test which tests :
- Is H1 header present?
- Does it contain "Hello World!"?
- Write the index.html file.
- 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"
}
}
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.
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.
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.
3. Add an E2E test system: protractor
Protractor is the E2E test framework for AngularJS.
$ 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
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:
- Specify that we want to use Jasmine2 (default is Jasmine 1)
- Know where its Selenium server can be found
- Know where the tests to be run can be found
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.
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 });
});
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",
});
});
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")
5. Write the page that makes the test pass.
Create src/client/index.html
<html>
<body>
<h1 id='h1'>Hello World!</h1>
</body>
</html>
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.