Skip to content

Commit

Permalink
Merge branch 'promises' of https://github.com/sgravrock/jasmine into …
Browse files Browse the repository at this point in the history
…sgravrock-promises

- Merges #1356 from @sgravrock
- Fixes #1336
- Fixes #1270
- Fixes #1350
- Fixes #1320
  • Loading branch information
Gregg Van Hove committed May 19, 2017
2 parents 99fd7dd + 9672689 commit c848a66
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 46 deletions.
62 changes: 39 additions & 23 deletions lib/jasmine-core/jasmine.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return j$.isA_('Function', value);
};

j$.isAsyncFunction_ = function(value) {
return j$.isA_('AsyncFunction', value);
};

j$.isA_ = function(typeName, value) {
return j$.getType_(value) === '[object ' + typeName + ']';
};
Expand Down Expand Up @@ -935,6 +939,12 @@ getJasmineRequireObj().Env = function(j$) {
}
};

var ensureIsFunctionOrAsync = function(fn, caller) {
if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) {
throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
}
};

var suiteFactory = function(description) {
var suite = new j$.Suite({
env: self,
Expand Down Expand Up @@ -1073,7 +1083,7 @@ getJasmineRequireObj().Env = function(j$) {
// it() sometimes doesn't have a fn argument, so only check the type if
// it's given.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunction(fn, 'it');
ensureIsFunctionOrAsync(fn, 'it');
}
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
if (currentDeclarationSuite.markedPending) {
Expand All @@ -1087,15 +1097,15 @@ getJasmineRequireObj().Env = function(j$) {
// xit(), like it(), doesn't always have a fn argument, so only check the
// type when needed.
if (arguments.length > 1 && typeof fn !== 'undefined') {
ensureIsFunction(fn, 'xit');
ensureIsFunctionOrAsync(fn, 'xit');
}
var spec = this.it.apply(this, arguments);
spec.pend('Temporarily disabled with xit');
return spec;
};

this.fit = function(description, fn, timeout){
ensureIsFunction(fn, 'fit');
ensureIsFunctionOrAsync(fn, 'fit');
var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
currentDeclarationSuite.addChild(spec);
focusedRunnables.push(spec.id);
Expand All @@ -1112,31 +1122,31 @@ getJasmineRequireObj().Env = function(j$) {
};

this.beforeEach = function(beforeEachFunction, timeout) {
ensureIsFunction(beforeEachFunction, 'beforeEach');
ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach');
currentDeclarationSuite.beforeEach({
fn: beforeEachFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
});
};

this.beforeAll = function(beforeAllFunction, timeout) {
ensureIsFunction(beforeAllFunction, 'beforeAll');
ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll');
currentDeclarationSuite.beforeAll({
fn: beforeAllFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
});
};

this.afterEach = function(afterEachFunction, timeout) {
ensureIsFunction(afterEachFunction, 'afterEach');
ensureIsFunctionOrAsync(afterEachFunction, 'afterEach');
currentDeclarationSuite.afterEach({
fn: afterEachFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
});
};

this.afterAll = function(afterAllFunction, timeout) {
ensureIsFunction(afterAllFunction, 'afterAll');
ensureIsFunctionOrAsync(afterAllFunction, 'afterAll');
currentDeclarationSuite.afterAll({
fn: afterAllFunction,
timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
Expand Down Expand Up @@ -3859,11 +3869,10 @@ getJasmineRequireObj().QueueRunner = function(j$) {

for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
var queueableFn = queueableFns[iterativeIndex];
if (queueableFn.fn.length > 0) {
attemptAsync(queueableFn);
var completedSynchronously = attempt(queueableFn);

if (!completedSynchronously) {
return;
} else {
attemptSync(queueableFn);
}
}

Expand All @@ -3872,25 +3881,20 @@ getJasmineRequireObj().QueueRunner = function(j$) {
self.onComplete();
});

function attemptSync(queueableFn) {
try {
queueableFn.fn.call(self.userContext);
} catch (e) {
handleException(e, queueableFn);
}
}

function attemptAsync(queueableFn) {
function attempt(queueableFn) {
var clearTimeout = function () {
Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
},
handleError = function(error) {
onException(error);
next();
},
next = once(function () {
cleanup = once(function() {
clearTimeout(timeoutId);
self.globalErrors.popListener(handleError);
}),
next = once(function () {
cleanup();
self.run(queueableFns, iterativeIndex + 1);
}),
timeoutId;
Expand All @@ -3911,11 +3915,23 @@ getJasmineRequireObj().QueueRunner = function(j$) {
}

try {
queueableFn.fn.call(self.userContext, next);
if (queueableFn.fn.length === 0) {
var maybeThenable = queueableFn.fn.call(self.userContext);

if (maybeThenable && j$.isFunction_(maybeThenable.then)) {
maybeThenable.then(next, next.fail);
return false;
}
} else {
queueableFn.fn.call(self.userContext, next);
return false;
}
} catch (e) {
handleException(e, queueableFn);
next();
}

cleanup();
return true;
}

function onException(e) {
Expand Down
42 changes: 42 additions & 0 deletions spec/core/EnvSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ describe("Env", function() {
env.it('pending spec');
}).not.toThrow();
});

it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.it('async', jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});

describe('#xit', function() {
Expand All @@ -114,6 +121,13 @@ describe("Env", function() {
env.xit('pending spec');
}).not.toThrow();
});

it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.xit('async', jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});

describe('#fit', function () {
Expand All @@ -130,6 +144,13 @@ describe("Env", function() {
env.beforeEach(undefined);
}).toThrowError(/beforeEach expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});

it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.beforeEach(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});

describe('#beforeAll', function () {
Expand All @@ -138,6 +159,13 @@ describe("Env", function() {
env.beforeAll(undefined);
}).toThrowError(/beforeAll expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});

it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.beforeAll(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});

describe('#afterEach', function () {
Expand All @@ -146,6 +174,13 @@ describe("Env", function() {
env.afterEach(undefined);
}).toThrowError(/afterEach expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});

it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.afterEach(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});

describe('#afterAll', function () {
Expand All @@ -154,5 +189,12 @@ describe("Env", function() {
env.afterAll(undefined);
}).toThrowError(/afterAll expects a function argument; received \[object (Undefined|DOMWindow|Object)\]/);
});

it('accepts an async function', function() {
jasmine.getEnv().requireAsyncAwait();
expect(function() {
env.afterAll(jasmine.getEnv().makeAsyncAwaitFunction());
}).not.toThrow();
});
});
});
78 changes: 78 additions & 0 deletions spec/core/QueueRunnerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,84 @@ describe("QueueRunner", function() {
});
});

describe("with a function that returns a promise", function() {
function StubPromise() {}

StubPromise.prototype.then = function(resolve, reject) {
this.resolveHandler = resolve;
this.rejectHandler = reject;
};

beforeEach(function() {
jasmine.clock().install();
});

afterEach(function() {
jasmine.clock().uninstall();
});

it("runs the function asynchronously, advancing once the promise is settled", function() {
var onComplete = jasmine.createSpy('onComplete'),
fnCallback = jasmine.createSpy('fnCallback'),
p1 = new StubPromise(),
p2 = new StubPromise(),
queueableFn1 = { fn: function() {
setTimeout(function() {
p1.resolveHandler();
}, 100);
return p1;
} };
queueableFn2 = { fn: function() {
fnCallback();
setTimeout(function() {
p2.resolveHandler();
}, 100);
return p2;
} },
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
onComplete: onComplete
});

queueRunner.execute();
expect(fnCallback).not.toHaveBeenCalled();
expect(onComplete).not.toHaveBeenCalled();

jasmine.clock().tick(100);

expect(fnCallback).toHaveBeenCalled();
expect(onComplete).not.toHaveBeenCalled();

jasmine.clock().tick(100);

expect(onComplete).toHaveBeenCalled();
});

it("fails the function when the promise is rejected", function() {
var promise = new StubPromise(),
queueableFn1 = { fn: function() {
setTimeout(function() { promise.rejectHandler('foo'); }, 100);
return promise;
} },
queueableFn2 = { fn: jasmine.createSpy('fn2') },
failFn = jasmine.createSpy('fail'),
queueRunner = new jasmineUnderTest.QueueRunner({
queueableFns: [queueableFn1, queueableFn2],
fail: failFn
});

queueRunner.execute();

expect(failFn).not.toHaveBeenCalled();
expect(queueableFn2.fn).not.toHaveBeenCalled();

jasmine.clock().tick(100);

expect(failFn).toHaveBeenCalledWith('foo');
expect(queueableFn2.fn).toHaveBeenCalled();
});
});

it("calls exception handlers when an exception is thrown in a fn", function() {
var queueableFn = { type: 'queueable',
fn: function() {
Expand Down
27 changes: 27 additions & 0 deletions spec/helpers/asyncAwait.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
(function(env) {
function getAsyncCtor() {
try {
eval("var func = async function(){};");
} catch (e) {
return null;
}

return Object.getPrototypeOf(func).constructor;
}

function hasAsyncAwaitSupport() {
return getAsyncCtor() !== null;
}

env.makeAsyncAwaitFunction = function() {
var AsyncFunction = getAsyncCtor();
return new AsyncFunction("");
};

env.requireAsyncAwait = function() {
if (!hasAsyncAwaitSupport()) {
env.pending("Environment does not support async/await functions");
}
};
})(jasmine.getEnv());

1 change: 1 addition & 0 deletions spec/support/jasmine.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"npmPackage/**/*.js"
],
"helpers": [
"helpers/asyncAwait.js",
"helpers/checkForSet.js",
"helpers/nodeDefineJasmineUnderTest.js"
],
Expand Down
1 change: 1 addition & 0 deletions spec/support/jasmine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ src_files:
- '**/*.js'
stylesheets:
helpers:
- 'helpers/asyncAwait.js'
- 'helpers/BrowserFlags.js'
- 'helpers/checkForSet.js'
- 'helpers/defineJasmineUnderTest.js'
Expand Down

0 comments on commit c848a66

Please sign in to comment.