Fix asynctree from getting cleared in between test case. #4325
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
Right now, if a test case is written using the
async/await
syntax, theasynctree
we use inside our queueing system gets reset after everyawait
ed Nightwatch command/assertion, after which thedone()
method of the queue itself is called.But, in between the tree getting reset and the queue's
done()
method being called, the control goes back inside the test case itself (because theawait
ed command/assertion is resolved now) and it reads the following NW commands and adds them into a new queue asynctree. So, when thedone()
method of the queue is called after that, because of this condition which checks if the tree is empty or not, because the tree is non-empty now, nothing happens and the test case continues its normal execution.But, when the pointer goes back to the test case between the tree getting reset and the queue
done()
call, instead of executing a NW command (which results in the queue tree getting filled again), if we execute a normal JS code andawait
it (like thesetTimeout
code below):the tree will stay empty after the execution of this code and the control will pass to the
done()
method of the queue.The above situation will cause the current queue to be resolved (through the call to
this.deferred.resolve()
), which in turn will cause the current runnable'sthis.deferred.resolveFn(result)
to be called. But because it is called with theresult
passed as argument, which represents the promise returned by the currently running test case, the current runnable does not actually get resolved but remains stuck waiting for the test case to complete and theresult
to be resolved.This means that because the resolution of the current runnable is now tied to the resolution of the promise returned by the test case, the current runnable will never be resolved until the test case itself is completed and resolved.
Now, when the awaited JS code inside the test case (due to which all of the above happened) finally gets resolved and returns its result, the following NW commands/assertions will be executed and added to the same queue (but with a different
this.deferred
no longer tied to the current runnable) and everything will continue running normally.So, if nothing eventful happens and the rest of the test case executes successfully, the
result
to which the resolution of the current runnable is tied to will resolve normally, leading to the resolution of the current runnable and everything works fine.BUT, if there is some error or an assertion failure someplace in the remaining test case, the tree will get cleared here [, followed by the entry inside the test case which is covered below and not important here], followed by a call to the
done()
method of the queue and hence the call to queue'sthis.deferred.resolve()
. But since thethis.deferred
of the queue is no longer tied to the current runnable, this path of the resolution/rejection of current runnable is closed now.Now, the resolution/rejection of the current runnable depends entirely on the resolution/rejection of the test case itself.
So, if this error or failure in the currently running NW command/assertion leads to:
await
is directly used with the command and the command promise gets rejected)result
to which the resolution of the current runnable is tied will get rejected and the current runnable will resolve with an error.--> TEST WILL CONTINUE NORMALLY.
await
is directly used with the command and the command promise gets resolved)failingCommand
and run the.sendKeys
command.--> TEST WILL CONTINUE BUT ABNORMALLY.
failingCommand
becasue the tree is emptied after the failure and so the lastsendKeys
command (to which theawait
is tied) will never run or get resolved.--> TEST WILL GET STUCK OR THE PROCESS WILL EXIT ABRUPTLY.
SOLUTION
The root cause of this issue is that if a test case is written with the async/await syntax, the tree is reset after every awaited NW command/assertion, which is then followed by a call to the queue
done()
method. Instead of this, the tree should also be reset at the end of the current test case or hook (or in case there is an error or a failure in some command, which is covered here). On doing this, although the queuedone()
method will still be called after every awaited NW command/assertion, it will never be executed completely because the tree will be non-empty at that point.This also sovles an issue when using the "Step over and pause again" feature of the
.pause()
command where the feature wasn't working for test cases written with async/await syntax.