RequireJs requests timeout with PhantomJs from time to time

1,185 views
Skip to first unread message

Andreas Köberle

unread,
Mar 18, 2013, 11:23:57 AM3/18/13
to bust...@googlegroups.com
I have a bunch of test with the following pattern.

buster.spec.expose();

describe('a view', function(run) {

  require(['views/View'], function(View) {

    run(function() {
      var view;

      before(function() {
        view = new View();
        view.render();
      });

      it('does something', function() {

      });
    });
  });
});

This tests runs fine in the browser. But when I run the tests with phantomJs I've got the following error from time to time:

caught exception: ./lib/js/requirejs/require.js:1765 Error: Load timeout for modules: views/View

Andreas Köberle

unread,
Mar 18, 2013, 11:47:27 AM3/18/13
to bust...@googlegroups.com
Some more information on it. First it seems that it only happenms in test where I mock out dependancies by creating a new requirejs context like this:

function createContext(stubs) {

  var map = {};

  _.each(stubs, function(value, key) {
    var stubname = 'stub' + key;

    map[key] = stubname;
  });


  var context = require.config({
    context: Math.floor(Math.random() * 1000000),
    map: {
      "*": map
    },
    baseUrl: 'js/cfe/app/'
  });

  _.each(stubs, function(value, key) {
    var stubname = 'stub' + key;

    define(stubname, function() {
      return value;
    });

  });

  return context;
}


buster.spec.expose();

var context= createContext({'models/model: sinon.stub()})

describe('a view', function(run) {

  context(['views/View'], function(View) {

    run(function() {
      var view;

      before(function() {
        view = new View();
        view.render();
      });

      it('does something', function() {

      });
    });
  });
});

When I run only one test it runs throw as long as I wait some seconds before starting the test again.

Andreas Köberle

unread,
May 2, 2013, 3:38:40 AM5/2/13
to bust...@googlegroups.com
So I could figured out after all. First of all the error only happens when using loading the test module with an own context for an test, which I needed to mock out the dependencies for the tested modul.
I"ve to change the buster-amd plugin so it does not call bsuter.run() direct after all modules are loaded but with a setTimeout:

function quote(t) {
    return "'" + t + "'";
}

function amdWrapper(paths, mapper) {
    var tests = paths.map(mapper).map(quote);
    var identifiers = paths.map(function (t, i) { return "t" + i; });

    tests.unshift("'buster'");
    identifiers.unshift('buster');
    return "define('buster', function(){ return buster; });"+
        "require([" + tests.join(", ") + "], function(" + identifiers.join(", ") +
        ") {\n  setTimeout(buster.run, 1000);\n});"; //using setTimeout here instead of calling buster.run() directly
}

module.exports =  {
    name: "buster-amd",

    create: function (options) {
        var ext = Object.create(this);
        var mapper = options && options.pathMapper;
        ext.pathMapper = mapper || function (path) {
            return path.replace(/\.js$/, "").replace(/^\//, "");
        };
        ext.preloadSources = !!options.preloadSources;
        ext.preloadTests = !!options.preloadTests;
        return ext;
    },

    configure: function (conf) {
        conf.options.autoRun = false;

        conf.on("load:tests", function (rs) {
            var paths = rs.loadPath.paths();
            rs.addResource({
                path: "/buster/load-all.js",
                content: amdWrapper(rs.loadPath.paths(), this.pathMapper)
            });
            if (!this.preloadTests) {
                rs.loadPath.clear();
            }
            rs.loadPath.append("/buster/load-all.js");
        }.bind(this));

        if (!this.preloadSources) {
            conf.on("load:sources", function (rs) { rs.loadPath.clear(); });
        }
    }
};


On Monday, 18 March 2013 16:23:57 UTC+1, Andreas Köberle wrote:

Christian Johansen

unread,
May 2, 2013, 6:19:44 AM5/2/13
to bust...@googlegroups.com
I didn't quite follow what the problen was? setTimeout is rarely a good
solution, sounds like we need another hook of some sort?

Andreas Köberle

unread,
May 2, 2013, 8:32:35 AM5/2/13
to bust...@googlegroups.com


On Thursday, 2 May 2013 12:19:44 UTC+2, Christian Johansen wrote:
I didn't quite follow what the problen was?

The problem is that all test cant load there dependencies. So in my case a simple test would look like this:

specHelper.js: 

var cnt = 0;
function createContext(stubs) {
  cnt++;
  var map = {};

  var i18n = stubs.i18n;
  stubs.i18n = {
    load: sinon.spy(function(name, req, onLoad) {
      onLoad(i18n);
    })
  };

  _.each(stubs, function(value, key) {
    var stubName = 'stub' + key + cnt;

    map[key] = stubName;

    define(stubName, function() {
      return value;
    });
  });

  return require.config({
    context: "context_" + cnt,
    map: {
      "*": map
    },
    baseUrl: 'js/cfe/app/'
  });
}

module to test: 

define(['a'], function(a){
  return {
    start: function(){a.foo('bar')}
  }
})


test:

describe(function(run){

  var stubs = {
    a: {foo: sinon.spy()}
  }
  createContext(stubs)('module', function(module){
    run(function(){
describe('my module', function() {

  it('call a with bar', function() {
    module.start();
              expect(stubs.a).toHaveBeenCalledWith('foo') 
  });
 });
    })
  })
})

This all works well within a browser, but in headless phantomJs every test fails with requirejs timeout for "a" in this example. It also works if I would use require directly without creating a new context for every test, which is necessary to mock dependencies  for the module I wanna test.

Martin Aspeli

unread,
Jul 27, 2013, 5:33:55 PM7/27/13
to bust...@googlegroups.com

On Thursday, 2 May 2013 13:32:35 UTC+1, Andreas Köberle wrote:


On Thursday, 2 May 2013 12:19:44 UTC+2, Christian Johansen wrote:
I didn't quite follow what the problen was?

The problem is that all test cant load there dependencies.

For what it's worth, I'm seeing the same problem. Tests fail every time in PhantomJS with "Error: Load timeout for modules: backbone,backbone.marionette", but works flawlessly in a captured Chrome.

I'm doing anything as clever as what Andreas describes below. However, I've noticed something else: the problem happens when I run all tests, but if I run each test file individually using the -t option, every one of them runs fine.

Martin

Martin Aspeli

unread,
Jul 27, 2013, 5:36:12 PM7/27/13
to bust...@googlegroups.com
Oh - and the setTimeout() hack Andreas outlines works for me as well. I assume that means there's a race condition somewhere. :(

Martin 

Garrick Cheung

unread,
Aug 15, 2013, 12:40:20 AM8/15/13
to bust...@googlegroups.com
@Martin what version of Phantom.js are you using?

Martin Aspeli

unread,
Aug 15, 2013, 5:37:46 PM8/15/13
to bust...@googlegroups.com
On 15 August 2013 05:40, Garrick Cheung <gche...@gmail.com> wrote:
@Martin what version of Phantom.js are you using?

↪ phantomjs --version
1.9.1

Martin 
Reply all
Reply to author
Forward
0 new messages