diff --git a/.jshintignore b/.jshintignore index 8b7e536..4979ff3 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1 +1 @@ -spec/asyncAwaitSpec.js +./spec/asyncAwaitSpec.js diff --git a/index.js b/index.js index 4d10791..94684e7 100644 --- a/index.js +++ b/index.js @@ -6,17 +6,6 @@ var webdriver = require('selenium-webdriver'); -/** - * Wraps a function so that all passed arguments are ignored. - * @param {!Function} fn The function to wrap. - * @return {!Function} The wrapped function. - */ -function seal(fn) { - return function() { - fn(); - }; -} - /** * Validates that the parameter is a function. * @param {Object} functionToValidate The function to validate. @@ -59,10 +48,28 @@ function validateString(stringtoValidate) { } } +/** + * Calls a function once the control flow is idle + * @param {webdriver.promise.ControlFlow} flow The Web Driver control flow + * @param {!Function} fn The function to call + */ +function callWhenIdle(flow, fn) { + if (flow.isIdle()) { + fn(); + } else { + flow.once(webdriver.promise.ControlFlow.EventType.IDLE, function() { + fn(); + }); + } +} + + /** * Wraps a function so it runs inside a webdriver.promise.ControlFlow and * waits for the flow to complete before continuing. + * @param {!webdriver.promise.ControlFlow} flow The WebDriver control flow. * @param {!Function} globalFn The function to wrap. + * @param {!string} fnName The name of the function being wrapped (e.g. `'it'`). * @return {!Function} The new function. */ function wrapInControlFlow(flow, globalFn, fnName) { @@ -78,30 +85,38 @@ function wrapInControlFlow(flow, globalFn, fnName) { flow.execute(function controlFlowExecute() { return new webdriver.promise.Promise(function(fulfill, reject) { + function wrappedReject(err) { + var wrappedErr = new Error(err); + reject(wrappedErr); + } if (async) { // If testFn is async (it expects a done callback), resolve the promise of this // test whenever that callback says to. Any promises returned from testFn are // ignored. var proxyDone = fulfill; - proxyDone.fail = function(err) { - var wrappedErr = new Error(err); - reject(wrappedErr); - }; + proxyDone.fail = wrappedReject; testFn(proxyDone); } else { // Without a callback, testFn can return a promise, or it will // be assumed to have completed synchronously. - fulfill(testFn()); + var ret = testFn(); + if (webdriver.promise.isPromise(ret)) { + ret.then(fulfill, wrappedReject); + } else { + fulfill(ret); + } } }, flow); - }, 'Run ' + fnName + description + ' in control flow').then(seal(done), function(err) { - if (!err) { - err = new Error('Unknown Error'); - err.stack = ''; + }, 'Run ' + fnName + description + ' in control flow').then( + callWhenIdle.bind(null, flow, done), function(err) { + if (!err) { + err = new Error('Unknown Error'); + err.stack = ''; + } + err.stack = err.stack + '\nFrom asynchronous test: \n' + driverError.stack; + callWhenIdle(flow, done.fail.bind(done, err)); } - err.stack = err.stack + '\nFrom asynchronous test: \n' + driverError.stack; - done.fail(err); - }); + ); }; } diff --git a/scripts/test.sh b/scripts/test.sh index 20553d8..0194052 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -9,7 +9,7 @@ $CMD [ "$?" -eq 0 ] || exit 1 echo -EXPECTED_RESULTS="16 specs, 15 failures" +EXPECTED_RESULTS="18 specs, 16 failures" echo "### running failing specs (expecting $EXPECTED_RESULTS)" CMD=$CMD_BASE$FAILING_SPECS echo "### $CMD" diff --git a/spec/adapterSpec.js b/spec/adapterSpec.js index 6d4722f..bb09093 100644 --- a/spec/adapterSpec.js +++ b/spec/adapterSpec.js @@ -242,4 +242,25 @@ describe('webdriverJS Jasmine adapter', function() { expect(spec3.description).toBe('test3'); }); }); + + describe('native promises', function() { + var currentTest = null; + + it('should wait for webdriver events sent from native promise', function() { + currentTest = 'A'; + return new Promise(function(resolve) { + setTimeout(function() { + fakeDriver.sleep(100).then(function() { + expect(currentTest).toBe('A'); + }); + resolve(); + }, 100); + }); + }); + + it('should not start a test before another finishes', function(done) { + currentTest = 'B'; + setTimeout(done, 200); + }); + }); }); diff --git a/spec/errorSpec.js b/spec/errorSpec.js index e508891..8f3ad88 100644 --- a/spec/errorSpec.js +++ b/spec/errorSpec.js @@ -102,4 +102,24 @@ describe('things that should fail', function() { expect(fakeDriver.getDecimalNumber()).toBeCloseTo(3.1); expect(fakeDriver.getDecimalNumber()).not.toBeCloseTo(3.14); }); + + describe('native promises', function() { + var testADone = false; + + it('should handle rejection from native promise', function() { + return new Promise(function(resolve, reject) { + setTimeout(function() { + fakeDriver.sleep(100).then(function() { + testADone = true; + }); + reject('Rejected promise'); + }, 100); + }); + }); + + it('should not start a test before another finishes', function(done) { + expect(testADone).toBe(true); // this test actually passes + setTimeout(done, 200); + }); + }); });