Search code examples
javascriptunit-testingdomcode-injectionfunctional-testing

Unit test a local JavaScript file against a remote URL


How can I unit test a local JavaScript file against a remote URL? I've been working on this on and off for a few days now but can't quite figure it out yet.

I have a local file, with DOM manipulation, such as one called app.js that contains:

document.querySelector('p').textContent = 'This is a test.';

How can I then test this against a remote URL, such as http://example.com/, to ensure that the code successfully runs? The URL is not one that I control. Perhaps a solution that includes Selenium would make the most sense, but I'm not sure how I would also run my local JS file against the remote URL.

Such as with this pseudo code:

openUrl('http://example.com/', function() {
  inject('./app.js');

  element = document.querySelector('p');

  assertEquals('This is a test.', element.textContent);
});

I tried using Casper JS (based off of Phantom JS) and that was the most promising, but it doesn't wait for the DOM to finish loading before running assertions, so anything that's inside a jQuery $(document).ready() block will not have executed yet.

Here's what I got in Casper JS so far; the second assertion fails because it runs in a ready() block.

// app.js
$ = require('jquery');

$('p').first().text('test');

$(document).ready(function() {
  $('h1').text('one');
});

And:

// test.js
var url = 'http://example.com/';
var script = 'Example/app.js';

casper.test.begin('Test', function suite(test) {
  casper.start(url, function() {
    casper.page.injectJs(script);

    // 'test' was added immediately, in app.js. This succeeds.
    test.assertTextExists('test');

    // 'one' runs in a $(document).ready() block. This assertion fails.
    test.assertTextExists('one');
  });

  casper.run(function() {
    test.done();
  });
});

Solution

  • I ended up using Casper, and I solved the problem of waiting for the DOM by simply using casper.wait(100).

    Some sample code (I haven't tested this but it should work; my actual code is much more complex). Run with casperjs test casper.js

    casper.js

    // Begin test suite.
    casper.test.begin('Example', function(test) {
      // Start Casper.
      casper.start();
    
      // Open URL.
      casper.thenOpen('http://example.com/', function() {
        casper.page.injectJs('inject.js');
      });
    
      // Call the test. Wait for code to finish first.
      casper.wait(100, function() {
        test.assertExists('.element-added-by-injection');
      });
    
      // A second URL can even be opened.
      casper.thenOpen('http://google.com/', function() {
        casper.page.injectJs('google.js');
      });
    
      casper.wait(100, function() {
        test.assertExists('.another-element-we-added');
      });
    
      casper.run(function() {
        test.done();
      });
    });
    

    inject.js

    var $ = require('jquery');
    
    $(function() {
      $('p').after('<div class="element-added-by-injection">Test</div>');
    });