UI End-to-End Testing with Nightwatch.js

Good morning and Happy Friday! I’m back after a little prompting from an old friend.

Song of the day:

Quick life update: I’m living near New York City now, and I’m working as a Senior Software Engineer for a team that writes the software that supports such excellent blogs as Engadget, Huffington Post Australia (more international editions to follow!), and Autoblog, among many others.

I’ve also gotten way into 3D printing, Internet of Things development, and am still doing minor development on my open-source fitness project, PPL.fitness.

Today we’re going to talk about testing our front-end code. I know, I know, I’ve used the excuses myself: “I don’t have time! My deadlines are too tight.” “I need to test how something LOOKS, not unit test a function in my code.” “Writing tests is weird and unnatural.” “Deuteronomy says UI testing is an abomination.” Yeah.

Enter stage right: Nightwatch.js. Super simple to set up. I get to use Node.js to simulate clicks, typing, and key presses, and check to see if elements are visible. I also get to check properties of the elements. Plus, it runs against the industry-standard Selenium server.

Though the initial setup took about a day to get my code decently covered by tests, I can now rest a little easier knowing that when I release new feature updates to my software, everything will work.

Setting Up

Install guide
Also download: https://sites.google.com/a/chromium.org/chromedriver/

I set up my folder structure like this:

project folder
…source files…
nightwatch.json
– bin
— chromedriver
— selenium-server-standalone-2.53.0.jar
– tests
— pages
—- pageCreateNew.js
— login.js
— createNew.js

I’m going to put my files up, with my comments inline for explanation.

nightwatch.js

{
  "src_folders" : ["tests"],   
  "output_folder" : "reports",
  "custom_commands_path" : "",
  "custom_assertions_path" : "",
  "page_objects_path" : "tests/pages",
  "globals_path" : "",

  "selenium" : {
    "start_process" : true,
    "server_path" : "bin/selenium-server-standalone-2.53.0.jar",
    "log_path" : "",
    "host" : "127.0.0.1",
    "port" : 4444,
    "cli_args" : {
      "webdriver.chrome.driver" : "bin/chromedriver", //THIS IS A BIG DEAL SO WE CAN TEST IN CHROME
      "webdriver.ie.driver" : ""
    }
  },

  "test_settings" : {
    "default" : { //runs when we don't pass in any options
      "launch_url" : "http://localhost:3000/management/splash/", //point this to whatever URL you want to test
      "selenium_port"  : 4444,
      "selenium_host"  : "localhost",
      "silent": true,
      "screenshots" : {
        "enabled" : false,
        "path" : ""
      },
      "desiredCapabilities": {
        "browserName": "firefox",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    },

    "chrome" : { //runs when user runs `nightwatch --env chrome`
      "desiredCapabilities": {
        "browserName": "chrome",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    },

    "production" : { //runs when user runs `nightwatch --env production`
      "launch_url" : "http://production.com/management/splash/",//point this to whatever URL you want to test
      "selenium_port"  : 4444,
      "selenium_host"  : "localhost",
      "silent": true,
      "screenshots" : {
        "enabled" : false,
        "path" : ""
      },
      "desiredCapabilities": {
        "browserName": "firefox",
        "javascriptEnabled": true,
        "acceptSslCerts": true
      }
    }
  }
}

Next, I wrote a tiny login function that I can call to get past authentication screens:

login.js

module.exports = function(client){
    return client
        .url('https://cms.aol.com')
        .waitForElementVisible('body', 5000)
        .waitForElementVisible('#signinemail', 3000) //change the selector to whatever the username input is on the site you're testing
        .setValue('#signinemail', email_goes_here) //change the selector accordingly
        .click('#continue_button')
        .waitForElementVisible('#signinpassword', 3000) //change the selector to whatever the password input is on the site you're testing
        .setValue('#signinpassword', password_goes_here) //change the selector accordingly
        .click('#signin_button')  //again, change this to the ID of the login button
};

One thing that’s really cool about Nightwatch is the ability to define pages. You get to define elements here and re-use them later in your testing code:

pageCreateNew.js

module.exports = {
  elements: {
    splashesListContainer: { 
      selector: '.splashes-list-container' 
    },
    startFreshButton: { 
      selector: '.new-button'
    }
  }
};

 

Quick break.

Alright, and now we get to the meat of our testing code!

var login = require('./login.js'); //this is our login function from before

module.exports = {
  '@tags': ['create', 'splash'], //tags are used to run certain groups of tests; I'll talk more about this in a minute

  'can edit the first headline': function (client) { //name your functions like this so that the person running the test knows what's broken or working
  	var createNew = client.page.pageCreateNew(); 

  	login(client); //this is how easy it is to call our login.js script!

  	createNew.navigate(client.launchUrl)
      .waitForElementVisible('@startFreshButton', 15000) //this is how we use the element selectors we defined in pageCreateNew.js
      .click('@startFreshButton')
      .waitForElementVisible('#template-container .headline-1 .splash__header', 1000) 
      .click('#template-container .headline-1 .splash__header')
      .setValue('#template-container textarea', 'Automated Testing Is the Best!') 
      //custom color tests
      .waitForElementVisible('.showCustomColors', 2000)
      .click('.showCustomColors')
      .click('.customColorOption:nth-of-type(2)')
      .click('.showCustomColors') //hide it
      //font size
      .setValue('#font-size-number-input', '80')
      //hyperlink
      .click('.link-button')
      .clearValue('.anchorLink')
      .setValue('.anchorLink', 'http://test.com')
      .click('.accept-item-button')
      .waitForElementVisible('#template-container .headline-1 .splash__header', 1000)
  
   //this is where we make our actual comparisons to see if everything is working!
    client.expect.element('#template-container .headline-1 .splash__header').text.to.equal('Automated Testing Is the Best!');
    client.expect.element('#template-container .headline-1 .splash__header').to.have.css('font-size', '80px');
    client.expect.element('#template-container .headline-1 .splash__header').to.have.css('color', '#2D7061');
    
    client.end();
  }
};

As you can see, the syntax is really simple!


expect(elementSelector).to.have.css(style, value)

expect(elementSelector).text.to.equal(value)

 

I am even able to test the type of my remote data store’s JSON schema with it by running AngularJS commands using client.api.execute(command)!


'is the schema set up correctly?':function (client) {
 var pageData= client.page.pageData();
 var code;

login(client);
 pageSplashLive.navigate(DATA_STORE_URL)
 .waitForElementVisible('body', 10000)
 .api.execute("return angular.element($('.data-editor-form')).scope()['ctrl']['data']['schema']['properties'];", [], function(response) {
 code = response.value;
 var dataType = typeof code;

client.assert.equal(dataType, 'object');
 client.assert.equal(code['Data']['type'], 'array');
 });

client.end();

},

 

I’ve just barely scratched the surface of this fantastic automation framework. I envision being able to automate a ton of online stuff with this tool – it doesn’t just have to be used to test my code.

 

Discussion of the day: what would you automate to make your life easier? I’m currently working on an automatic window blinds project (I’ll write that up soon!)

 

Cheers!

Sam

NodeJS – Wrapping the Request() Module to Point Fingers at Services

Lately, I’ve been looking into tracing the route of service calls through chained services. Since many companies are moving toward a Service-Oriented-Architecture model, tracing when services are called is a crucial step in debugging. This way, when there’s an issue with a service, we know which one is breaking and causing everyone downstream to fail.

As a quick example of what I’m talking about, I’ve made a simple model:

Service A: returns “Hello”

Service B: returns “world”

Service C: calls A and B, then returns “Hello world”

 

Now, imagine service B is taking a long time to respond. Service C will be stuck, and we couldn’t be sure whether it was A or B who is causing the problem!

My goal is to get in the middle of those calls to services A and B and attach headers that track when those calls are made. First, I tried digging into my services’ Bunyan() logging modules and adding my interceptors there. Didn’t work.

Instead, after asking around work and on StackOverflow, I’ve decided to make my own NodeJS module that wraps around Request() and handles all of this for developers with them doing a minimal amount of work. Here’s what I came up with:


'use strict';
var request = require('request');

function spRequest( options, callback ) {

options.incoming_req.headers['id'];

// check for our specific headers
if (options.incoming_req.headers['id'] == undefined) {
   console.log('No id passed to this service');
}
else{
   console.log('I was given id ' + options.incoming_req.headers['id']);
}

// pass those headers boldly forward where no service call has gone before
var forwardHeaders = {
   id: options.incoming_req.headers['id']
   //add other headers here as needed
};

var forwardOptions = { //these are the options we'll use to call request()
   url: options.url,
   headers: forwardHeaders
};

// get start time for the call
var hrTime = process.hrtime()

// we use hrTime because it's more accurate than Date.now()
var start = hrTime[0] * 1000000 + hrTime[1] / 1000;

// called when request() comes back with our data
function _onResponse( start, cb ) {
   var hrTime = process.hrtime()
   var end = hrTime[0] * 1000000 + hrTime[1] / 1000; //since hrTime is an array{milliseconds, nanoseconds} we have to do a bit of conversion
   var responseTime = end - start;
   console.log('request took ' + responseTime + ' milliseconds');

   return cb;
}

// and...GO!
   return request(forwardOptions, _onResponse(start, callback));
}

module.exports = spRequest

What’s really cool about this is that developers really only have to change one thing in their code. When making a HTTP request, they just have to include the request object in the call, like so:

Old way:

request('http://localhost:3000/hello', function (error, response, body) {...});

New way:

request({incoming_req: req, url: 'http://localhost:3000/hello'}, function (error, response, body) {...});

I learned a lot about functional programming today.

Do you like this? Is this something you’d like to read more about? Do you hate this and want me to write more about cooking or building physical things?

Let me know in the comments!

Happy Thursday, everybody!

AngularJS – Form Validation with Angular-UI-Validate

I was recently tasked with validating an input field – comparing whatever someone typed with a list and making sure there aren’t any duplicates. Here’s what I did:

Install Angular-UI-Validate. I chose to use this library because of its ability to skip the hassle of writing custom formatters and easy integration with Angular. Make sure to add it as a module in app.js!

 


angular.module('app', [ui.validate'])

 

In the controller, I had to add a function that checked the value passed to it against a list of names.


$scope.nameHasNotBeenUsed = function( $value ) {
 console.log($value);
 if ($scope.listOfNames) { // check if the names have been loaded yet
 var names = []; // for holding the names of the stacks
 angular.forEach($scope.listOfNames , function(name) { 
 names.push(name.name); // put the names in the array
 });
 return names.indexOf($value) === -1; // returns true if the name doesn't exist in the array; false otherwise
 }
 }

In the view, I just had to attach a couple properties to the input: ui-validate and ng-class.

<form name="formName"> 
   <div class="form-group"> 
      <div class="input-group"> 
         <input name="inputName" ui-validate=" {taken : 'nameHasNotBeenUsed($value)' } " ng-pattern="/^[a-zA-Z0-9-_]*$/" ng-class="{ 'permission-input-wrong': formName.inputName.$error.taken }" required ng-model="inputModel" type="text" class="form-control"> 
      </div> 
      <span ng-show='formName.inputName.$error.taken'>This name is already in use.</span> 
   </div> 
</form>

In the ui-validate property, the input is labeled as taken if the $scope.nameHasNotBeenUsed function returns false. Then, ng-class takes over: it checks for formName.inputName.$error.taken, and if it’s present, it applies the class permission-input-wrong (this just outlines the field in red).

Another cool thing we are doing is selectively hiding and showing error messages based on that formName.inputName.$error.taken.

 

Thanks for reading! If this helps you – if this is a dumb way of doing this, and I need to be corrected – tell me in the comments!

 

Cheers,

Sam

 

Get a site like mine!

My friend and co-worker Ashpak Shaikh asked me to put together a how-to on a personal website like mine, so I figured I’d write it up as a blog post, just in case anyone else wants to do something similar. It’s super simple, cheap, and easy to maintain.

1) Buy a domain name.

I’ve been using nameCheap.com for a few years now with no problems. I’ve been advised to avoid GoDaddy for numerous reasons, so I can’t really speak to the pros and cons of each service. I can say that NameCheap has everything I need at a cheap price. It’s just a domain name, don’t go crazy with it.

Also – if you find a domain name you like that’s open, snatch it up as soon as you can. People have written scripts that buy up domain names after they’ve been searched, then they sell the name for a profit. They’re assholes.

2) Find some hosting.

I use Amazon EC2, which was free for my first year, and has been averaging around $13-15 per month since. It gives you full terminal access for any imaginable kind of server – Ubuntu, Red Hat, CentOS, even Windows. It also scales automatically if more people visit the site, with the ability to limit the scaling to a dollar amount.
There are cheaper services, namely DigitalOcean. I haven’t used them. Again, whatever floats your boat is fine here.

3) Point your domain name toward your new host.

AWS Tutorial
DigitalOcean Tutorial

4) Set up your server. I use a LAMP setup.

LAMP tutorial for Amazon
LAMP for Digital Ocean

5) Install WordPress.

This tutorial is fucking thorough. It actually covers a lot of what I’ve said here already.

6) Add plugins.

Plugins are a fun and useful part of WordPress. My favorites are Jetpack for monitoring site statistics and social media broadcasting, as well as SyntaxHighlighter for showing my pretty code. There are so many out there. You can even write your own!

That’s all for now! Thanks for reading.

Working with the TP-Link TL-WA5210G

I recently picked up two TP-Link point-to-point WiFi extenders for my dad’s shop behind his house. This will allow us to view his Uniden Guardian cameras from anywhere we have access to the internet!

Now that I’ve connected to the router and am logged in to the interface, I have 3 options: AP Client Router, AP Router, and AP. However, the guide doesn’t define these well, so I’m looking up which I should use. We’re going to try AP first.

After a bit of haggling, I found this guide which helped a bit.

After 2 hours, we’re still having issues connecting to the house’s router, though, and the lack of manufacturer diagrams for network setup isn’t helping.

We finally got it to work by simply restarting the equipment and running through the instructions we found again. The feed from the cameras is steady, high-speed, and can handle all four cameras with ease. All in all, this wasn’t the easiest setup, but the gear is weatherproof, holds a signal, and should serve for many years to come.

Big news and a great comic

San Diego skyline
I’ve accepted a co-op at Intuit starting January 26th! I’ll be moving to San Diego, California for 6 months for this great opportunity.

Since I still have a few classes to take before I graduate, I have a lot of work to do so I can still graduate on time. I have to test out of three classes and take one class online.

Here’s a great comic about Java vs. Javascript! I love it.

My mobile device testing array

Mobile device testing array
Imgur Album
With some scrap wood, I made a mobile device testing array for increasing my web development productivity! Using Adobe Edge Inspect CC, all of the devices update as soon as I make a change to my code. It works great, and I like how it looks.

I made a WordPress plugin.

FormToServer v1
This week, I’ve been working on a WordPress plugin for my employer, Catertrax. It hooks into any GravityForms form and forwards it to a server for backup. I took cues from Google’s Material Design guides, and I love how it’s turning out. Unfortunately, I can’t release the source code, but working with WordPress was a great learning experience, and I hope to continue to work with this great platform again soon.

I made a Chrome Extension!

Today, I made my first Chrome Extension, called Niceties. Go check it out!

It was incredibly simple to make, thanks to Yeoman and its numerous generators. I was able to create the entire extension in less than 3 hours. I’d like to thank:

Download Niceties

GitHub Project

Yeoman! It’s awesome.

Todo List

My manager David Ross pointed me to a scaffolding framework called Yeoman. It’s cool as hell! It lets us developers automate the boring parts of site and dependency setup and get straight into the code. Plus, their tutorial is ultra helpful. I learned how to setup and use:

  • Node
  • NPM
  • Angular.js
  • Grunt
  • Bower
  • JavaScript unit testing

Github Repository