Skip to content

feat(async/await): Support overwriting native Promise with WebDriver's version #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
*.log
node_modules
spec/asyncAwaitSpec.js
spec/asyncAwaitSpec*.js
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Features

- If a `done` function is passed to the test, waits for both the control flow and until done is called.

- If a test returns a promise, waits for both the the control flow and the promise to resolve.

- Enhances `expect` so that it automatically unwraps promises before performing the assertion.

Installation
Expand Down Expand Up @@ -58,3 +60,13 @@ describe('tests with webdriver', function() {
});
})
```

`async` functions / `await`
---------------------------

`async` functions and the `await` keyword are likely coming in ES7, and
available via several compilers. At the moment, they often break the WebDriver
control flow.
([GitHub issue](https://github.com/SeleniumHQ/selenium/issues/3037)). You can
still use them, but if you do then you will have to use `await`/Promises for
almost all your synchronization. See `spec/asyncAwaitSpec.ts` for details.
10 changes: 9 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,21 @@ function wrapInControlFlow(flow, globalFn, fnName) {
* pass webdriver here instead of using require() in order to ensure Protractor
* and Jasminews are using the same webdriver instance.
* @param {Object} flow. The ControlFlow to wrap tests in.
* @param {function=} WDPromise The class for promises managed by the
* ControlFlow. This is an optional parameter. If you use it, jasminewd
* will overwrite the global `Promise` class with this so that tools like
* async/await work. This might cause other unexpected problems however.
*/
function initJasmineWd(flow) {
function initJasmineWd(flow, WDPromise) {
if (jasmine.JasmineWdInitialized) {
throw Error('JasmineWd already initialized when init() was called');
}
jasmine.JasmineWdInitialized = true;

if (WDPromise) {
WDPromise.prototype = global.Promise || WDPromise.prototype;
global.Promise = WDPromise;
}
global.it = wrapInControlFlow(flow, global.it, 'it');
global.fit = wrapInControlFlow(flow, global.fit, 'fit');
global.beforeEach = wrapInControlFlow(flow, global.beforeEach, 'beforeEach');
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"main": "index.js",
"scripts": {
"pretest": "node_modules/.bin/jshint index.js spec --exclude spec/asyncAwaitSpec.js; tsc -t ES2015 spec/asyncAwaitSpec.ts",
"pretest": "node_modules/.bin/jshint index.js spec --exclude spec/asyncAwaitSpec*.js; tsc -t ES2015 spec/asyncAwaitSpec*.ts",
"test": "scripts/test.sh"
},
"license": "MIT",
Expand Down
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
PASSING_SPECS="spec/support/passing_specs.json"
FAILING_SPECS="spec/support/failing_specs.json"
EXPERIMENTAL_SPECS="spec/support/experimental_specs.json"
CMD_BASE="node node_modules/.bin/jasmine JASMINE_CONFIG_PATH="

echo "### running passing specs"
Expand All @@ -18,4 +19,12 @@ results_line=`echo "$res" | tail -2 | head -1`
echo "result: $results_line"
[ "$results_line" = "$EXPECTED_RESULTS" ] || exit 1

echo "### running experimental specs"
CMD=$CMD_BASE$EXPERIMENTAL_SPECS
echo "### $CMD"
$CMD
# We don't care if the experimental specs pass.
# [ "$?" -eq 0 ] || exit 1
echo

echo "all pass"
28 changes: 23 additions & 5 deletions spec/asyncAwaitSpec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
/* Here we have an example of using async/await with jasminewd. We are using
* typescript to gain access to async/await, but mostly this should look like
* normal javascript to you.
*
* The key thing to note here is that once you use async/await, the webdriver
* control flow is no longer reliable. This means you have to use async/await
* or promises for all your asynchronous behavior. In Protractor this would
* mean putting `await` before every line interacting with the browser. In this
* example, we have to put `await` before `driver.sleep()`.
*/
"use strict";

// Declare globals
declare var describe;
declare var it;
declare var expect;
declare var require;

async function asyncHelper() {
return 7;
}
let driver = require('./common.js').getFakeDriver();

describe('async function', function() {
let sharedVal: any;
it('should wait on async functions', async function() {
let helperVal = await asyncHelper();
expect(helperVal).toBe(7);
sharedVal = await driver.getValueA(); // Async unwraps this to 'a'
expect(sharedVal).toBe('a');
await driver.sleep(1000); // Normally you wouldn't need to `await` this, but
// the control flow is broken for async functions.
sharedVal = await driver.getValueB();
});

it('should have waited until the end of the last it() block', function() {
expect(sharedVal).toBe('b');
});
});
28 changes: 28 additions & 0 deletions spec/asyncAwaitSpec_experimental.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* This is a test for an experimental feature to make async/await work with the
* webdriver control flow. You should not use this.
*/
"use strict";

// Declare globals
declare var describe;
declare var it;
declare var expect;
declare var require;

let webdriver = require('selenium-webdriver'),
flow = webdriver.promise.controlFlow();
require('../index.js').init(flow, webdriver.promise.Promise);

describe('async function', function() {
let sharedVal: any;
it('should wait on async functions', async function() {
sharedVal = await webdriver.promise.fulfilled('a');
expect(sharedVal).toBe('a');
flow.timeout(1000); // The control flow needs to work for this.
sharedVal = await webdriver.promise.fulfilled('b');
});

it('should have waited until the end of the last it() block', function() {
expect(sharedVal).toBe('b');
});
});
6 changes: 6 additions & 0 deletions spec/support/experimental_specs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"spec_dir": "spec",
"spec_files": [
"asyncAwaitSpec_experimental.js"
]
}