Skip to content

Commit

Permalink
chore(promises): Wait for promises explicitly
Browse files Browse the repository at this point in the history
See angular#68 for details
  • Loading branch information
sjelin committed Nov 23, 2016
1 parent e05cd3c commit c67de4a
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 25 deletions.
1 change: 1 addition & 0 deletions .jshintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./spec/asyncAwaitSpec.js
61 changes: 38 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
* @parm {!string} fnName The name of the function being wrapped (e.g. `'it'`)
* @return {!Function} The new function.
*/
function wrapInControlFlow(flow, globalFn, fnName) {
Expand All @@ -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);
});
);
};
}

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; tsc -t ES2015 spec/asyncAwaitSpec.ts",
"test": "scripts/test.sh"
},
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
21 changes: 21 additions & 0 deletions spec/adapterSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});
20 changes: 20 additions & 0 deletions spec/errorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});

0 comments on commit c67de4a

Please sign in to comment.