diff --git a/.gitignore b/.gitignore index 77dfc75..709d5d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ *.log node_modules -spec/asyncAwaitSpec.js +spec/asyncAwaitSpec*.js diff --git a/README.md b/README.md index 3d90e22..484aa43 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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. diff --git a/index.js b/index.js index 3c54ca8..239c263 100644 --- a/index.js +++ b/index.js @@ -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'); diff --git a/package.json b/package.json index 4997a3e..96e0fee 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/test.sh b/scripts/test.sh index 20553d8..ffaf0d1 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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" @@ -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" diff --git a/spec/asyncAwaitSpec.ts b/spec/asyncAwaitSpec.ts index f8a308b..e094354 100644 --- a/spec/asyncAwaitSpec.ts +++ b/spec/asyncAwaitSpec.ts @@ -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'); }); }); diff --git a/spec/asyncAwaitSpec_experimental.ts b/spec/asyncAwaitSpec_experimental.ts new file mode 100644 index 0000000..588fcdd --- /dev/null +++ b/spec/asyncAwaitSpec_experimental.ts @@ -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'); + }); +}); diff --git a/spec/support/experimental_specs.json b/spec/support/experimental_specs.json new file mode 100644 index 0000000..bec0768 --- /dev/null +++ b/spec/support/experimental_specs.json @@ -0,0 +1,6 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "asyncAwaitSpec_experimental.js" + ] +}