Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

protractor wait timeout catch and continue

Tags:

protractor

I have a piece of code in protractor to do a wait:

public waitForElement() {
    return browser.wait(
        ExpectedConditions.visibilityOf(element(by.id('#someEl'))),
        10000,
        'Unable to find the element'
        );
}

My issue is that I cannot seem to catch this exception if it times out. I tried adding a catch() clause, which does not work, e.g.:

this.waitForElement()
    .then(() => { /* do something */ })
    .catch(() => { /* handle the error -- this code never happens if there is a timeout!!! */ });

I tried putting the code in a try-catch block, but that doesn't help either:

try { this.waitForElement().then(() => { }); }
catch (ex) { /* this exception is never caught, the test just fails!! */ }

I'm stumped: how can I catch the wait timeout and continue with the test without the test failing?

like image 266
riqitang Avatar asked Jul 27 '17 15:07

riqitang


2 Answers

I've created a simple testcase for this, see below

// conf
exports.config = {
  capabilities: {
    'browserName': 'chrome'
  },

  // Framework to use. Jasmine is recommended.
  framework: 'jasmine',

  // Spec patterns are relative to the current working directory when
  // protractor is called.
  specs: ['example_spec.js'],

  // Options to be passed to Jasmine.
  jasmineNodeOpts: {
    defaultTimeoutInterval: 30000
  },
  allScriptsTimeout: 30000,
};

// Spec
describe('Wait for element', () => {
  it('element will be found', () => {
    browser.get('http://www.angularjs.org');

    waitForElement(element(by.css('.hero')))
      .then(() => {
        console.log('element is found');
      })
      .catch((error) => {
        console.log('error = ', error);
      });
  });
  it('element will NOT be found', () => {
    browser.get('http://www.angularjs.org');

    waitForElement(element(by.css('.heroic')))
      .then(() => {
        console.log('element is found');
      })
      .catch((error) => {
        console.log('element is not found, do something different');
      });
  });
});

function waitForElement(element) {
  return browser.wait(
    ExpectedConditions.visibilityOf(element),
    3000,
    'Unable to find the element'
  );
}

This gives me the following output

 ~/wswebcreation/contributions/protractor  npm run test.example

> [email protected] test.example /Users/wswebcreation/contributions/protractor
> node ./bin/protractor example/conf.js

[10:32:17] I/launcher - Running 1 instances of WebDriver
[10:32:17] I/hosted - Using the selenium server at http://localhost:4444/wd/hub/
Started
element is found .
element is not found, do something different.


2 specs, 0 failures
Finished in 6.917 seconds

[10:32:24] I/launcher - 0 instance(s) of WebDriver still running
[10:32:24] I/launcher - chrome #01 passed

So it looks like it works

You can also do something like this with your method

function waitForElement(element, maxWaitTime, failOnError) {
  maxWaitTime = maxWaitTime || 10000;
  failOnError = failOnError || false;

  return browser.wait(ExpectedConditions.visibilityOf(element), maxWaitTime)
    .then((found) => Promise.resolve(found))
    .catch((waitError) => {
      if (failOnError) {
        return Promise.reject(`waitForElement: ${waitError} for expected condition ${expectation} for the element: ${element}`);
      } else {
        return Promise.resolve(false);
      }
    });
}

This makes it more "flexibel", you can catch the error in the method and if needed let it fail there, else pass a boolean that you can use to go further.

like image 156
wswebcreation Avatar answered Oct 21 '22 14:10

wswebcreation


Un-picking the particularly important part from @wswebcreation's useful answer: in order to make this work, I've found you must specify an explicit timeout.

The following works:

return browser.wait(ExpectedConditions.visibilityOf(...), 5000)
  .catch(() => { throw new Error('my error'); });

But this does not:

return browser.wait(ExpectedConditions.visibilityOf(...))
  .catch(() => { throw new Error('my error'); });

giving:

 Error: function timed out after 11000 milliseconds
     at Timeout._onTimeout (C:\project\node_modules\cucumber\lib\user_code_runner.js:91:22)
     at ontimeout (timers.js:498:11)
     at tryOnTimeout (timers.js:323:5)
     at Timer.listOnTimeout (timers.js:290:5)

Where 11000 is just the default Protractor timeout we have configured.

Note also that although the documentation for browser.wait implies that the promise form is only required if you want to pause all Javascript execution, not just WebDriver:

This function blocks WebDriver's control flow, not the javascript runtime. It will only delay future webdriver commands from being executed (e.g. it will cause Protractor to wait before sending future commands to the selenium server), and only when the webdriver control flow is enabled.

This function returnes a promise, which can be used if you need to block javascript execution and not just the control flow.

...it is apparently also not possible to catch the result of a timeout-less, promise-less browser.wait in a try-catch, as the condition runs in a setTimeout.

The above all apparently true with protractor 5.3.2 when used with protractor-cucumber-framework 4.2.0.

like image 23
ryanp Avatar answered Oct 21 '22 13:10

ryanp