Hamza Ishak
Hamza Ishak

Reputation: 360

Nightmare: Error: Timeout of 2000ms exceeded. For async tests and hooks

I've started experimenting around with Nightmare and mocha to perform automated tests. I am getting the error below, despite following the instruction in the error itself.

var Nightmare = require('nightmare');
var expect = require('chai').expect;

var url = 'http://www.google.com/'

describe('Page availability', function() {
  it('Should open homepage', function(done) {
    var nightmare = Nightmare();

    nightmare
      .goto(url)
      .wait('body')
      .evaluate(function () {
        return document.querySelector('.gb_P').innerHTML
      })
      .end()
      .then(function(text) {
        expect(text).to.equal('images');
        done();
      })
  });
});

when running the above script with `mocha test.js', this is the output that I get:

Page availability
    1) Should open homepage


  0 passing (2s)
  1 failing

  1) Page availability Should open homepage:
     Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

Confession: I am not (yet) versed with promises, but I don't quite get why Mocha isn't accounting for the done callback that I've provided inside then.

Upvotes: 1

Views: 1322

Answers (2)

Avery Kushner
Avery Kushner

Reputation: 166

I have been struggling with this exact same problem for hours, finally found a solution that works!

First and foremost @Ross is correct here, and while 2 seconds can be enough, it is not always going to be enough time to wait for google, or any other page for that matter. For this reason I bumped my timeout up to 15000ms, a safe bet, maybe over kill, but unless you have a really slow internet connection, you'll be safe.

Secondly, I have found that

querySelector(selector).innerHTML

often returns null. This may be due to the fact that there is a large amount of human error involved in writing a selector path, or some other technical issue happening behind the scenes with either javascript or nightmare's .evaluate() function.

It is worth mentioning that the problem does not seem to originate from the wait() function, as I have found that the framework is perfectly happy using the same query selector, but swapping the innerHTML property for the length property.

Example:

This will result in a timeout error, in addition to .evaluate() returning null:

.wait("body > div.wrap > h1")
   .evaluate(() => {
        return document.querySelectorAll("body > div.wrap > h1").innerHTML;
    })

whereas changing innerHTML to length will pass perfectly fine

.wait("body > div.wrap > h1")
   .evaluate(() => {
        return document.querySelectorAll("body > div.wrap > h1").length;
    })

I am not confident why this is, however, these are my findings.

Thusly, in order to resolve OP's issue I used document.getElementsByClassName() which has proven to me a far easier and/or accurate method for obtaining DOM elements

re-writing OP's code as such:

var Nightmare = require('nightmare');
var expect = require('chai').expect;

var url = 'http://www.google.com/'

describe('Page availability', function() {
    this.timeout(15000);
    it('Should open homepage', function(done) {
        var nightmare = Nightmare();

        nightmare
        .goto(url)
        .wait('body')
        .evaluate(function () {
            var headerDiv = document.getElementsByClassName("gb_P");
            return headerDiv[1].innerHTML
        })
        .end()
        .then(function(text) {
            expect(text).to.equal('Images');
            done();
        })
    });
});

Yields a Passing test.

Further, OP has forgotten to capitalize 'Images' which is the innerHTML of that class.

In conclusion,

I am not querySelector(selector).innerHTML can still work, however, for targeting specific classes and id's getElementById() is a safer choice. They can work in tandem too, for example the following code runs with out errors for me, where as jamming the whole query selector in querySelector(selector).innerHTML crashes the test.

.wait("body > div#wpwrap > div#wpcontent > div#wpbody > div#wpbody-content > div.wrap > h1")
    .evaluate(() => {
        var headerDiv = document.getElementById("wpbody-content");
        h1Html = headerDiv.querySelector(".wrap h1").innerHTML;
        return h1Html;
    })

Upvotes: 0

Ross
Ross

Reputation: 2468

This is because the default time out for Mocha is 2 seconds, and your Nightmare actions are likely taking longer than that to complete. (Google takes notoriously long to finish loading.)

Inside of the describe or it, you could set this.timeout(ms), or use --timeout [ms] when running the mocha command.

Upvotes: 1

Related Questions