Bradley Braithwaite
1 import {
2 Learning,
3 JavaScript,
4 AngularJS
5 } from 'Brad on Code.com'
6 |
Check out my online course: AngularJS Unit Testing in-depth with ngMock.

Creating an AJAX Autocomplete

Creating an AJAX Autocomplete with no dependencies using TDD approach.

on
on tutorials, javascript

I needed a very small AJAX autocomplete library with no 3rd party dependencies (jQuery!) for a side-project I was working on. I couldn’t find what I needed so for a little fun I decided to write my own.

It has a very limited feature set and only supports AJAX GET requests. It’s 1.7kb in file size. It works with the mouse and the keyboard, and you can see it in action below:

autocomplete in action

Before you run off and ditch your existing autocomplete controls please keep in mind that this isn’t production ready (yet!).

I’m sharing this for educational purposes, as you may be interested to see how I:

  • Created a workflow to test across multiple browsers
  • Reported on test coverage with Karma
  • Used a TDD approach using Karma (in particular working with keyboard events)

The source code is available at: https://github.com/bbraithwaite/boc-autocomplete

Creating a workflow to test across multiple browsers

I’ve talked about testing with Karma before. When developing I used the Chrome launcher with autoWatch set to true to re-run the tests when I saved a change to a file. Once I had worked through a lot of changes, I would run the test with a different (slower) profile that would target Chrome & Safari (and others if I needed) to verify the code changes, cross platform.

The config files are in the karma folder of the project.

I configured the package.json file to add the following npm commands:

npm test

Runs the tests with the dev profile (Chrome & auto watch).

npm run test-all

Runs the tests targeting all configured browsers (Chrome & Safari in this case, but could be more).

npm run build

Runs the Grunt task for minifying the JavaScript files, and then runs the same tests for Chrome & Safari against the minified code.

Here is the package.json file.

For reference, here are just some of the launchers available for the main browsers:

Reporting on test coverage with Karma

I used a package called karma-coverage. I configured my application so that I could generate the coverage report via the command:

npm run coverage

This runs the coverage report (and nicely open the coverage folder for me).

Once run, the tool will create an index.html page in the coverage folder that will look something similar to:

coverage example

Using TDD with Karma and Keyboard Events

In the past, when wanting tests for user input controls such as an autocomplete I’ve used the automated testing approach (typically with Selenium). These tools are great for checking correctness but the feedback loop is slower because you have to wait for the browser to launch etc. But, the reason for choosing to test via an actual browser is that the tests are easier to write given that it’s not required to mock input events etc.

This library takes a simple html input element (below) and enhances its functionality by listening to the input and keydown events:

<input type="search" id="autocomplete" placeholder="Search...">

This raises the question: how do I raise keyboard events for unit tests?

Firstly, I checked that the events were being registered when the code to initialise the search input element is called:

describe('control event handler registration', function() {
  
  var searchInput;

  // add html fragments to page
  beforeEach(function() {
    document.body.insertAdjacentHTML(
      'afterbegin', 
      '<div id="fixture"><input type="search" id="autocomplete"></div>');
    searchInput = document.getElementById('autocomplete');
  });

  // remove the html fixture from the DOM
  afterEach(function() {
    document.body.removeChild(document.getElementById('fixture'));
  });

  beforeEach(function() {
    testUtils.createMock(Autocomplete.prototype);
  });

  afterEach(function() {
    Autocomplete.prototype.restore();
  });

  it('should listen to keydown event', function (done) {
    // mock of the autocomplete keyboard event handler
    Autocomplete.prototype.keyEvt = function() {
      // test won't pass unless done() is called
      done();
    };
    // init the control
    new window.Autocomplete(searchInput, {});
    // trigger a keydown event
    searchInput.dispatchEvent(testUtils.getKeyboardEvent('keydown'));
  });

});

The key points of this test are:

  • The dispatchEvent allows an event to be triggered for an element, simulating a real user action such as a keypress within an input field.
  • The getKeyboardEvent function in this example is a utility method I wrote. The code for that is below:
util.getKeyboardEvent = function(type) {
  var evt = document.createEvent('KeyboardEvent');
  evt.initEvent(type, true, false);
  return evt;
};

Testing Key Press Sequences

How to write tests for specific key press sequences e.g. arrow keys up/down? Here is a snap shot from the tests:

describe('arrow key behaviour', function() {
  var keydown;
  var thisArg;

  beforeEach(function() {
    thisArg = {
      ul: ulFixture(3) // utility function to create a <ul> with 3 <li> items.
    };

    keydown = Autocomplete.prototype.keyEvt.bind(thisArg);
  });

  it('should not run off the end of the list', function() { 
    keydown({ keyCode: keys.DOWN });
    assertOptionSelected(thisArg.ul , 0);
    keydown({ keyCode: keys.DOWN });
    assertOptionSelected(thisArg.ul , 1);
    keydown({ keyCode: keys.DOWN });
    assertOptionSelected(thisArg.ul , 2);
    keydown({ keyCode: keys.DOWN });
    assertOptionSelected(thisArg.ul , 2);
  });
});

The key point with this code snippet, is to try and design your code so that you can call the function directly, using a mock event object rather than trying to dispatch events from within the tests. Given that the earlier test verified that the event itself is registered for the control, it’s not necessary to go via the full event cycle for the remaining tests.

The tests can be found at: https://github.com/bbraithwaite/boc-autocomplete/tree/master/test

Get the Source Code

You can find the source code below. Pull requests/code reviews are welcome or feel free to raise an Issue with any problems/feature requests.

https://github.com/bbraithwaite/boc-autocomplete

SHARE
Don't miss out on the free technical content:

Subscribe to Updates

CONNECT WITH BRADLEY

Bradley Braithwaite Software Blog Bradley Braithwaite is a software engineer who works for search engine start-ups. He is a published author at pluralsight.com. He writes about software development practices, JavaScript, AngularJS and Node.js via his website . Find out more about Brad. Find him via:
You might also like:
mean stack tutorial AngularJS Testing - Unit Testing Tutorials