question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

(feature) Allow returning promise objects in executeAsync

See original GitHub issue

I’m currently doing some acceptance tests using nightwatch, parts of which run on the clientside, and I’d like to use Promises, as they can roll up errors and report them back quite easily.

However my problem is that I have to wrap all my client functions in the following:

(mostly pseudocode)

.executeAsync(function iAmExecutedOnTheClient(done) {
  Promise.resolve()  // to propagate any sync error in doSomeSetUp()
  .then(() => {
    return window.doSomeSetUp()
  }).then(() => {
    // custom stuff goes here. Exceptions may occur.
    // Testing that a third party is being called correctly using a sinon spy, if you're curious.
  }).then(() => { done({ noIssuesOccurred: true }) }).catch((e) => { done({error:e}) })
}, [], function checkForClientErrors (value) {
  if (value.noIssuesOccurred) // Ok, cool
  else if (value.error) // something was thrown/rejected, fail the test
  else // something else happened, like hitting ctrl+C during the test
})

This is not very DRY. I’ve factored out checkForClientErrors, but still the part where the promise is being made (just to catch stuff like accidental ReferenceErrors), and then the error being recreated and sent back to nightwatch, is quite problematic.

What I would like to write would be:

.executeAsync(() => {
  return Promise.resolve()
  .then(() => {
    return window.doSomeSetUp()
  }).then(() => {
    // my custom stuff goes here. If any errors are thrown by doSomeSetUp,
    // they are sent back to the test
  })
})

Which is free of custom error handling, similar to the mocha interface (returning a promise means the test ends when it resolves, and fails when it rejects), and catches all errors thrown synchronously (meaning, if doSomeSetUp were undefined, the test would fail and report that back to me).

I quite like my current workaround (macros OMG!), but since it involves Function#toString and eval() I’d like to avoid it altogether:

function createBrowserWrapper(fn) {
  return eval('(' + String(function wrapper(done__) {
    return Promise.resolve()
    .then(() => {
      return (MAIN_PART)()
    })
    .then(() => { done__({ noIssuesOccurred: true }) }).catch((e) => { done__({error:e}) })
  }).replace('MAIN_PART', String(fn)) + ')')
}

...

browser
.executeAsync(createBrowserWrapper(() => {
  return doSomeSetUp()
  .then(() => {
    // my test goes here currently
  })
}), [], verifyClientOutput)

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:2
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
marrcommented, Sep 24, 2016

Thanks @senocular! I remember your Flash tutorials from the AS3/Flex days.

1reaction
senocularcommented, Sep 22, 2016

@marr That SO response is convoluted. The purpose of the last callback function in executeAsync is to have a function that is executed in the nightwatch test after the execute code (first function argument) has completed and called done. That example is setting something in a synchronous execute using a timeout then later calling an executeAsync to wait for that result when it could have used a single executeAsync from the start:

    client.executeAsync(function(data, done) {
        window._asyncResult = undefined;
        setTimeout(function(){
          window._asyncResult = "abcde";
          done(window._asyncResult);
        }, 2000);
      }, ["1234"],
      function(result) {
        // evaluate the result
        client.assert.equal(result.value, "abcde");
      });

I guess you could use 2 if you felt it important to perform the assignment and validation in separate call stacks, but then you’d still go with the first async and then follow it up with a sync, avoiding any of that polling in the second bit.

As for this issue, I’m fairly sure its not getting in any time soon if at all. Use of promises have been proposed before (#638), and its not anywhere on the roadmap. This is also cramming promises into an existing api at the content level which involves additional string parsing to handle the promise wrapping (the implementation already shared above).

This would work better as a custom command, something like executeAsyncP which would use the createBrowserWrapper to do all the stuff needed to make promises work as expected in the execute code.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Async function returning promise, instead of value
Every async function returns a promise so that inside of it you can await other promises, that's the whole point.
Read more >
How to use promises - Learn web development | MDN
A promise is an object returned by an asynchronous function, which represents the current state of the operation. At the time the promise...
Read more >
Async and Await in JavaScript, the extension to a promise.
An async function simply implies that a promise will be returned and if a promise is not returned, JavaScript will automatically wrap it...
Read more >
'return await promise' vs 'return promise' in JavaScript
When returning from a promise from an asynchronous function, ... Now let's try to use the second expression without the await keyword, ...
Read more >
Javascript: How to access the return value of a Promise object
Let's see how we can access returned data. ... function load (url) { return new Promise(async function (resolve, reject) { // do async...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found