webdriverio: Errors not reporting useful line number in call stack

The problem

When an error is thrown, I am seeing a call stack which points to a line within webdriverio code instead of to the line of the test which failed. This makes it difficult to know, for example, which click within a test was the click which didn’t register correctly.

e.g.

1) My Example Test:
unknown error: Element <label for="val_1">...</label> is not clickable at point (108, 20). Other element would receive the click: <a href="/" class="header">...</a>
running Chrome on OS X 10.10
Check out job at https://saucelabs.com/tests/09as8d09a8sd098asd098as098asd098as
Error: An unknown server-side error occurred while processing the command.
    at elementIdClick("0.8803705949365321-14") - click.js:20:22

I wish this error pointed to the test file and the line of the click.

Environment

  • WebdriverIO version: 4.10.2
  • Node.js version: 8.9.4
  • Standalone mode or wdio testrunner: wdio
  • if wdio testrunner, running synchronous or asynchronous tests: synchronous
  • Additional wdio packages used (if applicable):

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 9
  • Comments: 28 (8 by maintainers)

Most upvoted comments

I’ve been able to get some useful stacktrace information by adding the following to the before hook:

    Object.keys(Object.getPrototypeOf(global.browser)).forEach((commandName) => {
      let origFn = global.browser[commandName];
      global.browser[commandName] = function () {
          try {
              return origFn.apply(this, arguments);
          }catch( e) {
              Error.captureStackTrace(e);
              throw e;
          }
      }.bind(global.browser); 
    });

I think the main issue is due to the monkey patching performed by wdio-sync… Maybe webdriver-io should monkey patch on top to ensure stacktraces are preserved…

I also encountered this issue, thanks @mherodev for the workaround! Only thing, the workaround works great for functions like browser.waitForVisible('some_element'), but not for browser.element('some_element').waitForVisible(). @marcelas Maybe it’s your case too.

This is my slightly more complicated solution for fix stack trace of element functions too


function wrapMethods (element, commands, options = {}) {
  const { makeCopy } = options

  const functions = makeCopy ? {} : element

  commands.forEach(commandName => {
    const origFn = element[commandName]
    functions[commandName] = function (...args) {
      try {
        if (commandName === 'element') {
          // so browser.element('.some').element('.elem').click()
          // will also have better stack trace
          // You also can add waitForVisible and other functions here
          return wrapMethods(origFn.apply(this, args), ['click', 'element'], {makeCopy: true})
        } else {
          return origFn.apply(this, args)
        }
      } catch (e) {
        Error.captureStackTrace(e)
        throw e
      }
    }.bind(element)
  })

  // Functions .click in elements is read-only. So by this way we make copy-like of the element
  return new Proxy({}, {
    get (target, prop) {
      if (functions[prop]) {
        return functions[prop]
      } else {
        return element[prop]
      }
    },

    set (target, prop, value) {
      return element[prop] = value
    }
  })
}

function improveStackTrace () {
  const commands = Object.keys(Object.getPrototypeOf(global.browser))
  wrapMethods(global.browser, commands)
}

module.exports = improveStackTrace

In our environment it works great