Promisified error handling

171 views
Skip to first unread message

Dave Haeffner

unread,
Oct 26, 2020, 8:56:27 PM10/26/20
to NightwatchJs
What's the recommended approach for error handling in Nightwatch when working with promises?

I ask because it's not as simple as just wrapping the calls to Nightwatch in a try/catch - the catch block is never reached.

Here's an example that forces a stale element error to throw:
      driver.url(url)
      const element = await driver.element('css selector', '#overflowing-div')
      const elementId = Object.values(element.value)[0]
      await driver.refresh()
      try {
        await driver.elementIdClick(elementId) // breaks the promise chain when it throws
      } catch (err) {
        console.log('HEY') // <---- never reaches here
      }

Does Nightwatch use error-first callbacks? Can I just use util.promisify? Or is there something else at play here that I need to be aware of?

Thanks in advance!


Cheers,
Dave H

Andrei Rusu

unread,
Oct 27, 2020, 6:00:13 AM10/27/20
to NightwatchJs
Nightwatch commands by default do not throw the error but you can use write a custom commands and use protocol actions directly (since v1.4). See https://nightwatchjs.org/guide/extending-nightwatch/custom-commands.html

Is this a viable option? Here's the entire list of protocol actions which you can use: https://github.com/nightwatchjs/nightwatch/blob/master/lib/transport/jsonwire/actions.js

Dave Haeffner

unread,
Oct 27, 2020, 6:35:07 AM10/27/20
to NightwatchJs
So if I understand you correctly, I can't simply wrap Nightwatch commands in a function and add error handling like this...

async function findElement(driver, selector) {
  try {
    const element = await driver.element(...transformSelector(selector))
    return element.value
  } catch (error) {
    // etc.
  }
}

Instead I would need to use protocol actions directly - and if they throw, then I could catch them?

Andrei Rusu

unread,
Oct 27, 2020, 6:35:07 AM10/27/20
to NightwatchJs
The error handling that you were expecting is not that straightforward to implement in nightwatch without major architectural changes. If the solution doesn't work for you then you can also look at using the Nightwatch element locating mechanism, which will give you the element ID and also it has built in support for retrying and error handling. I would have suggested this initially, but it's not properly exported in the API, which is not a big problem but it requires a new release, which might take a bit longer.

Basically you should be able to extend or use this class: https://github.com/nightwatchjs/nightwatch/blob/master/lib/element/command.js and then use the findElement method.

Andrei Rusu

unread,
Oct 27, 2020, 6:40:29 AM10/27/20
to NightwatchJs
Yes, if the protocol actions will throw, you will be able to catch them. So, you can write this function as a custom command.

But actually what I think we should be doing is to add this functionality in Nightwatch itself – a way to retrieve the element ID using the existing retry mechanism.

Dave Haeffner

unread,
Oct 27, 2020, 6:42:10 AM10/27/20
to NightwatchJs
Okay -- appreciate the insight. I'll take a look.

Dave Haeffner

unread,
Oct 27, 2020, 8:49:06 AM10/27/20
to NightwatchJs
A custom command is not a viable option since I'm wrapping the API - not extending it (not yet at least).

Something like this will work, no?

      const error = await new Promise(resolve => {
        driver.elementIdClick(elementId, (err, _result) => {
          resolve(err)
        })
      })

Then I can inspect the error and throw as needed from within my function.

It seems to work for the use case I shared, but there are others (I'm wrapping a lot of Nightwatch commands). Is this a viable approach for all Nightwatch commands?

Andrei Rusu

unread,
Oct 27, 2020, 11:51:06 AM10/27/20
to NightwatchJs
Almost, I think you'd need something like this (not tested):

await new Promise((resolve, reject) => {
  driver.elementIdClick(elementId, (result) => {
     if (result.status !== 0) {
       const err = new Error('An error occurred...');
       // create a proper error object here, this is just an example...
       err.result = result;

       return reject(err);
    } 

    resolve(result);
   });
});
Reply all
Reply to author
Forward
0 new messages