Form auto-fill bookmarklet

May 16th, 2006. Tagged: bookmarklets, JavaScript

Intro

So here's the thing, we all know we hate forms, the only thing we hate more than forms themselves is actually filling out forms. But the forms are the interface to our web apps, so we cannot do without them. What we would love to do without, but can't, is skip the application testing part where you fill out forms like there's no tomorrow, just to make sure your app is rock solid.

And filling out forms is a pain. So for some time I wanted to get my hands on a little something that can fill out a form, any form, with a click of a button. JavaScript is ideal for such a task and the best sort of a "little something" is probably a bookmarklet. That is how this bookmark was born.

What is it, what it does?

This is a bookmarklet. You go to page that has one or more forms and you click the bookmarklet. It completes the form for you with some random data. The whole thinking was to have a form ready to be submitted and generating as less validation errors as possible. Here are some details:

  • All defaults are kept as they are
  • All passwords fields are completed with the same password, in case there is a password/password confirmation combo. The default password is "secret"
  • If a text field has the string "email" in its name, the auto-generated value would be a random string @ example.org
  • If a text field has the string "name" in its name, a name-looking value will be generated.
  • All checkboxes will be checked (who knows which one of them might be "Accept terms" or anything else that is required)
  • Multi-selects will have a random number of random options selected

Install

Right-click and bookmark or drag to your personal bookmarks toolbar.

Form auto-fill

Demo

Here's the demo.

The code

The demo and the code below are "normal" code, with proper indentation and all. The actual bookmark though has to be on one line and as small as possible, so it's pretty much unreadable. Ah, and while the demo will work in IE, the bookmarklet won't, because it's too big for IE. IE allows up to about 500 characters in the URL (or a bookmarklet) while mine is about 2000 "compressed" or 3000 cleaner. So unless I do something heroic in compressing the script, it won't work on IE. No biggie I'd say, since you'll be testing your application and most likely you use Firefox anyway.

The big picture

Using JSON, the class/object is called auto and it has the following interface:

var auto ={
 
    // a list of names that will be used to generate 
    // normal looking names
    names: "Steve Buscemi Catherine Keener ...",
 
    // this is where all the random words will come from
    blurb: "phpBB is a free...",
 
    // default password to be used in all password fields
    password: "secret",
 
    // the main function that does all
    fillerup: function() {},
 
    // helper function, returns randomly selected words
    // coming from this.blurb
    getRandomString: function (how_many_words) {},
 
    // helper function, returns randomly selected names
    // coming from this.names
    getRandomName: function () {},
    
    // returns this.password
    getPassword: function () {},
    
    // returns a random int from 0 to count
    getRand: function (count) {}
}

The actual form fill-out is initiated by calling auto.fillerup()

As you can probably guess, the only interesting function is fillerup(), so let me show you what it does.

fillerup()

In case you're wondering, the name of the function comes from a Sting song:
Fill'er up, son, unleaded.
I need a full tank of gas where I'm headed ...

The function starts by identifying all the elements candidate to be completed:

var all_inputs    = document.getElementsByTagName('input');
var all_selects   = document.getElementsByTagName('select');
var all_textareas = document.getElementsByTagName('textarea');

OK, we have our work cut out for us, let's start by looping through the selects:

// selects
for (var i = 0, max = all_selects.length; i < max; i++) {
    var sel = all_selects[i]; // current element
    if (sel.selectedIndex != -1 
        && sel.options[sel.selectedIndex].value) {
        continue; // has a default value, skip it
    }
    var howmany = 1; // how many options we'll select
    if (sel.type == 'select-multiple') { // multiple selects
        // random number of options will be selected
        var howmany = 1 + this.getRand(sel.options.length - 1);
    }
    for (var j = 0; j < howmany; j++) {
        var index = this.getRand(sel.options.length - 1);
        sel.options[index].selected = 'selected';
        // @todo - Check if the selected index actually 
        //         has a value otherwise try again
    }
}

Then - textareas, they cannot be simpler. We only check if there isn't already a value and if there's none, we get two "paragraphs" of 10 words each.

// textareas
for (var i = 0, max = all_textareas.length; i < max; i++) {
    var ta = all_textareas[i];
    if (!ta.value) {
        ta.value = this.getRandomString(10) 
                   + '\\n\\n' 
                   + this.getRandomString(10);
    }
}

Next (and last), come the inputs. They are a bit more complicated as there are too many of them. Here's the overall code with the skipped details for each input type.

// inputs
for (var i = 0, max = all_inputs.length; i < max; i++) {
    var inp = all_inputs[i];
    var type = inp.getAttribute('type');
    if (!type) {
        type = 'text'; // default is 'text''
    }
    if (type == 'checkbox') {...}
    if (type == 'radio') {...}
    if (type == 'password') {...}
    if (type == 'text') {...}
}

We're absolutely unforgiving when it comes to checkboxes - just check them all, no questions asked, take no prisoners.

if (type == 'checkbox') {
    // check'em all
    // who knows which ones are required
    inp.setAttribute('checked', 'checked'); 
    /* ... ooor random check-off
    if (!inp.getAttribute('checked')) {
        if (Math.round(Math.random())) { // 0 or 1
            inp.setAttribute('checked', 'checked');
        }
    }
    */
}

Next, do the radios. They are a bit more complicated, because once we have an element, before checking it, we need to verify that there are no other radios with the same name (and in the same form) are already selected and checked.

if (type == 'radio') {
 
    var to_update = true;
    // we assume this radio needs to be checked
    // but in any event we'll check first
 
    var name = inp.name;
    var input_array = inp.form.elements[inp.name];
    for (var j = 0; j < input_array.length; j++) {
        if (input_array[j].checked) {
            // match! already has a value
            to_update = false;
            continue;
        }
    }
 
    if (to_update) {
        // ok, ok, checking the radio
        // only ... randomly
        var index = this.getRand(input_array.length - 1);
        input_array[index].setAttribute('checked', 'checked');
    }
}

Passwords - trivial, just make sure you always set the same password.

if (type == 'password') {
    if (!inp.value) {
        inp.value = this.getPassword();
    }
}

And finally - the text inputs. We try to guess the nature of the text field by its name. Here there's plenty of room for improvement and more guesses.

if (type == 'text') {
    if (!inp.value) {
        // try to be smart about some stuff
        // like email and name
        if (inp.name.indexOf('name') != -1) { // name
            inp.value = this.getRandomName() + ' ' + this.getRandomName();
        } else if (inp.name.indexOf('email') != -1) { // email address
            inp.value = this.getRandomString(1) + '@example.org';
        } else {
            inp.value = this.getRandomString(1);
        }
    }
}

C'est tout

That's it, hope you liked it and start using it ;) Any comment or suggestions - let me know.

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov