Skip to content

Instantly share code, notes, and snippets.

@elliotchance
Created December 18, 2015 05:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elliotchance/63f2ebebe8d457307339 to your computer and use it in GitHub Desktop.
Save elliotchance/63f2ebebe8d457307339 to your computer and use it in GitHub Desktop.
Nightwatch: Wait for a selector (CSS) that launches a pseudo-debugger.
/**
* Wait for a selector (CSS) that launches a pseudo-debugger.
*
* This builds on top of `waitForElementPresent()` that comes with Nightwatch
* and works as a drop in replacement. The major difference is that if the
* selector does not appear in the specified time it will act like a breakpoint
* where the state is paused and a Continue button appears to proceed manually.
*
* @param selector The CSS entity to wait for - e.g. '#something'
* @param [timeout] The maximum time we should wait. If this isn't provided 10
* seconds will be used.
*/
exports.command = function(selector, timeout) {
var _this = this;
// 10 seconds should be a reasonable amount of time if a specific wait time is
// not provided.
timeout = timeout || 10000;
// Start waiting for the element.
_this.waitForElementPresent(selector, timeout, false, function() {
_this.elements('css selector', selector, function(result) {
// We arrive here in all scenarios (timeout and success) so we need to
// check again if the selector exists; if it does we can jump out here so
// the test can continue as normal.
if (result.value.length) {
return;
}
// The error message from Nightwatch will print to the console saying it
// couldn't find the element in the given amount of time but we add an
// extra very visible magenta message so that they don't think it has just
// stalled for no reason.
printDebugMessage('Execution is stopped for debugging.');
// Add a big ugly button to the top left that will allow the tester to
// continue after they have done the nessesary debugging and hopefully
// solved the issue. Or maybe they just want to continue anyway.
_this.execute(function() {
// We will abuse a global here becuase the browser cannot send
// information back to Nightwatch. So we have to poll for this variable
// until it changes.
window.canContinue = false;
// Draw the ugly button.
$('body').append('<div id="debugContinue">Continue</div>');
var $debugContinue = $('#debugContinue');
$debugContinue.css({
width: '100px',
height: '50px',
position: 'absolute',
top: 0,
left: 0,
'background-color': 'magenta',
'z-index': 1000000,
color: 'white'
});
// Clicking the button will allow the test to keep running.
$debugContinue.click(function() {
window.canContinue = true;
});
});
// Keep waiting until the user eventually clicks Continue.
waitForContinue();
function waitForContinue() {
// This is where we have to poll the browser to see if canContinue has
// changed.
_this.execute(function() {
return window.canContinue;
}, [], function(result) {
if (!result.value) {
// Try this again in 250ms - they are not ready yet.
_this.pause(250, waitForContinue);
}
});
}
function printDebugMessage(message) {
console.log('\x1b[35m' + message + '\x1b[0m');
}
});
});
return this;
};
@inrix-vincent-jo
Copy link

Hi Elliot, I was wondering where to put this function and how one might use it?

@john-coffey
Copy link

Version without jQuery -

//
//  Inspired by https://gist.github.com/elliotchance/63f2ebebe8d457307339
//  via http://elliot.land/post/nightwatch-just-got-a-debugger
//
/**
 * waitForSelector
 *
 * Wait for a selector (CSS) that launches a pseudo-debugger.
 *
 * This builds on top of `waitForElementPresent()` that comes with Nightwatch
 * and works as a drop in replacement. The major difference is that if the
 * selector does not appear in the specified time it will act like a breakpoint
 * where the state is paused and a Continue button appears to proceed manually.
 *
 * @param selector The CSS entity to wait for - e.g. '#something'
 * @param [timeout] The maximum time we should wait. If this isn't provided 10
 *     seconds will be used.
 */
exports.command = function(selector, timeout) {
  var _this = this;

  // 10 seconds should be a reasonable amount of time if a specific wait time is
  // not provided.
  timeout = timeout || 10000;

  // Start waiting for the element.
  _this.waitForElementPresent(selector, timeout, false, function() {
    _this.elements('css selector', selector, function(result) {

      // We arrive here in all scenarios (timeout and success) so we need to
      // check again if the selector exists; if it does we can jump out here so
      // the test can continue as normal.
      if (result.value.length) {
        return;
      }

      // The error message from Nightwatch will print to the console saying it
      // couldn't find the element in the given amount of time but we add an
      // extra very visible magenta message so that they don't think it has just
      // stalled for no reason.
      printDebugMessage('Execution is stopped for debugging.');

      // Add a big ugly button to the top left that will allow the tester to
      // continue after they have done the nessesary debugging and hopefully
      // solved the issue. Or maybe they just want to continue anyway.
      _this.execute(function() {
        // We will abuse a global here becuase the browser cannot send
        // information back to Nightwatch. So we have to poll for this variable
        // until it changes.
        window.canContinue = false;

        // Draw the ugly button.
        var body = document.querySelector('body');
        var button = document.createElement('div');
        button.appendChild(document.createTextNode("Continue"));
        button.setAttribute(
          'style',
          'width: 150px; height: 50px; line-height: 50px; text-align: center; cursor: pointer; position: absolute; top: 0; left: 0; background-color: magenta; z-index: 1000000; color: white'
        );
        button.addEventListener('click', function() {
          window.canContinue = true;
        });
        body.appendChild(button);

      });

      // Keep waiting until the user eventually clicks Continue.
      waitForContinue();

      function waitForContinue() {
        // This is where we have to poll the browser to see if canContinue has
        // changed.
        _this.execute(function() {
          return window.canContinue;
        }, [], function(result) {

          if (!result.value) {
            // Try this again in 250ms - they are not ready yet.
            _this.pause(250, waitForContinue);
          }
        });
      }

      function printDebugMessage(message) {
        console.log('\x1b[35m' + message + '\x1b[0m');
      }
    });
  });

  return this;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment