Return a promise from Env#execute in environments that support promises
[#178373231]
This commit is contained in:
@@ -1817,11 +1817,17 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
*
|
*
|
||||||
* execute should not be called more than once.
|
* execute should not be called more than once.
|
||||||
*
|
*
|
||||||
|
* If the environment supports promises, execute will return a promise that
|
||||||
|
* is resolved after the suite finishes executing. The promise will be
|
||||||
|
* resolved (not rejected) as long as the suite runs to completion. Use a
|
||||||
|
* {@link Reporter} to determine whether or not the suite passed.
|
||||||
|
*
|
||||||
* @name Env#execute
|
* @name Env#execute
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @function
|
* @function
|
||||||
* @param {(string[])=} runnablesToRun IDs of suites and/or specs to run
|
* @param {(string[])=} runnablesToRun IDs of suites and/or specs to run
|
||||||
* @param {Function=} onComplete Function that will be called after all specs have run
|
* @param {Function=} onComplete Function that will be called after all specs have run
|
||||||
|
* @return {Promise<undefined>}
|
||||||
*/
|
*/
|
||||||
this.execute = function(runnablesToRun, onComplete) {
|
this.execute = function(runnablesToRun, onComplete) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
@@ -1881,65 +1887,86 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
var jasmineTimer = new j$.Timer();
|
var jasmineTimer = new j$.Timer();
|
||||||
jasmineTimer.start();
|
jasmineTimer.start();
|
||||||
|
|
||||||
/**
|
var Promise = customPromise || global.Promise;
|
||||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
|
||||||
* @typedef JasmineStartedInfo
|
|
||||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
|
||||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
|
||||||
*/
|
|
||||||
reporter.jasmineStarted(
|
|
||||||
{
|
|
||||||
totalSpecsDefined: totalSpecsDefined,
|
|
||||||
order: order
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
currentlyExecutingSuites.push(topSuite);
|
|
||||||
|
|
||||||
processor.execute(function() {
|
if (Promise) {
|
||||||
clearResourcesForRunnable(topSuite.id);
|
return new Promise(function(resolve) {
|
||||||
currentlyExecutingSuites.pop();
|
runAll(function() {
|
||||||
var overallStatus, incompleteReason;
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
if (hasFailures || topSuite.result.failedExpectations.length > 0) {
|
|
||||||
overallStatus = 'failed';
|
|
||||||
} else if (focusedRunnables.length > 0) {
|
|
||||||
overallStatus = 'incomplete';
|
|
||||||
incompleteReason = 'fit() or fdescribe() was found';
|
|
||||||
} else if (totalSpecsDefined === 0) {
|
|
||||||
overallStatus = 'incomplete';
|
|
||||||
incompleteReason = 'No specs found';
|
|
||||||
} else {
|
|
||||||
overallStatus = 'passed';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
resolve();
|
||||||
* Information passed to the {@link Reporter#jasmineDone} event.
|
|
||||||
* @typedef JasmineDoneInfo
|
|
||||||
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
|
||||||
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
|
||||||
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
|
||||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
|
||||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
|
||||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
|
||||||
*/
|
|
||||||
reporter.jasmineDone(
|
|
||||||
{
|
|
||||||
overallStatus: overallStatus,
|
|
||||||
totalTime: jasmineTimer.elapsed(),
|
|
||||||
incompleteReason: incompleteReason,
|
|
||||||
order: order,
|
|
||||||
failedExpectations: topSuite.result.failedExpectations,
|
|
||||||
deprecationWarnings: topSuite.result.deprecationWarnings
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
if (onComplete) {
|
|
||||||
onComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
} else {
|
||||||
|
runAll(function() {
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runAll(done) {
|
||||||
|
/**
|
||||||
|
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||||
|
* @typedef JasmineStartedInfo
|
||||||
|
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
||||||
|
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||||
|
*/
|
||||||
|
reporter.jasmineStarted(
|
||||||
|
{
|
||||||
|
totalSpecsDefined: totalSpecsDefined,
|
||||||
|
order: order
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
currentlyExecutingSuites.push(topSuite);
|
||||||
|
|
||||||
|
processor.execute(function() {
|
||||||
|
clearResourcesForRunnable(topSuite.id);
|
||||||
|
currentlyExecutingSuites.pop();
|
||||||
|
var overallStatus, incompleteReason;
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasFailures ||
|
||||||
|
topSuite.result.failedExpectations.length > 0
|
||||||
|
) {
|
||||||
|
overallStatus = 'failed';
|
||||||
|
} else if (focusedRunnables.length > 0) {
|
||||||
|
overallStatus = 'incomplete';
|
||||||
|
incompleteReason = 'fit() or fdescribe() was found';
|
||||||
|
} else if (totalSpecsDefined === 0) {
|
||||||
|
overallStatus = 'incomplete';
|
||||||
|
incompleteReason = 'No specs found';
|
||||||
|
} else {
|
||||||
|
overallStatus = 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information passed to the {@link Reporter#jasmineDone} event.
|
||||||
|
* @typedef JasmineDoneInfo
|
||||||
|
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
||||||
|
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
||||||
|
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
||||||
|
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||||
|
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||||
|
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||||
|
*/
|
||||||
|
reporter.jasmineDone(
|
||||||
|
{
|
||||||
|
overallStatus: overallStatus,
|
||||||
|
totalTime: jasmineTimer.elapsed(),
|
||||||
|
incompleteReason: incompleteReason,
|
||||||
|
order: order,
|
||||||
|
failedExpectations: topSuite.result.failedExpectations,
|
||||||
|
deprecationWarnings: topSuite.result.deprecationWarnings
|
||||||
|
},
|
||||||
|
done
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -465,4 +465,25 @@ describe('Env', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#execute', function() {
|
||||||
|
it('returns a promise when the environment supports promises', function() {
|
||||||
|
jasmine.getEnv().requirePromises();
|
||||||
|
expect(env.execute()).toBeInstanceOf(Promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a promise when a custom promise constructor is provided', function() {
|
||||||
|
function CustomPromise() {}
|
||||||
|
CustomPromise.resolve = function() {};
|
||||||
|
CustomPromise.reject = function() {};
|
||||||
|
|
||||||
|
env.configure({ Promise: CustomPromise });
|
||||||
|
expect(env.execute()).toBeInstanceOf(CustomPromise);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when promises are unavailable', function() {
|
||||||
|
jasmine.getEnv().requireNoPromises();
|
||||||
|
expect(env.execute()).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3038,6 +3038,112 @@ describe('Env integration', function() {
|
|||||||
env.execute(null, done);
|
env.execute(null, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('The promise returned by #execute', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = this.savedInterval;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is resolved after reporter events are dispatched', function() {
|
||||||
|
jasmine.getEnv().requirePromises();
|
||||||
|
var reporter = jasmine.createSpyObj('reporter', [
|
||||||
|
'specDone',
|
||||||
|
'suiteDone',
|
||||||
|
'jasmineDone'
|
||||||
|
]);
|
||||||
|
|
||||||
|
env.addReporter(reporter);
|
||||||
|
env.describe('suite', function() {
|
||||||
|
env.it('spec', function() {});
|
||||||
|
});
|
||||||
|
|
||||||
|
return env.execute(null).then(function() {
|
||||||
|
expect(reporter.specDone).toHaveBeenCalled();
|
||||||
|
expect(reporter.suiteDone).toHaveBeenCalled();
|
||||||
|
expect(reporter.jasmineDone).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is resolved after the stack is cleared', function(done) {
|
||||||
|
jasmine.getEnv().requirePromises();
|
||||||
|
var realClearStack = jasmineUnderTest.getClearStack(
|
||||||
|
jasmineUnderTest.getGlobal()
|
||||||
|
),
|
||||||
|
clearStackSpy = jasmine
|
||||||
|
.createSpy('clearStack')
|
||||||
|
.and.callFake(realClearStack);
|
||||||
|
spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(clearStackSpy);
|
||||||
|
|
||||||
|
// Create a new env that has the clearStack defined above
|
||||||
|
env.cleanup_();
|
||||||
|
env = new jasmineUnderTest.Env();
|
||||||
|
|
||||||
|
env.describe('suite', function() {
|
||||||
|
env.it('spec', function() {});
|
||||||
|
});
|
||||||
|
|
||||||
|
env.execute(null).then(function() {
|
||||||
|
expect(clearStackSpy).toHaveBeenCalled(); // (many times)
|
||||||
|
clearStackSpy.calls.reset();
|
||||||
|
setTimeout(function() {
|
||||||
|
expect(clearStackSpy).not.toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is resolved after QueueRunner timeouts are cleared', function() {
|
||||||
|
jasmine.getEnv().requirePromises();
|
||||||
|
var setTimeoutSpy = spyOn(
|
||||||
|
jasmineUnderTest.getGlobal(),
|
||||||
|
'setTimeout'
|
||||||
|
).and.callThrough();
|
||||||
|
var clearTimeoutSpy = spyOn(
|
||||||
|
jasmineUnderTest.getGlobal(),
|
||||||
|
'clearTimeout'
|
||||||
|
).and.callThrough();
|
||||||
|
|
||||||
|
jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL = 123456; // a distinctive value
|
||||||
|
|
||||||
|
env = new jasmineUnderTest.Env();
|
||||||
|
|
||||||
|
env.describe('suite', function() {
|
||||||
|
env.it('spec', function() {});
|
||||||
|
});
|
||||||
|
|
||||||
|
return env.execute(null).then(function() {
|
||||||
|
var timeoutIds = setTimeoutSpy.calls
|
||||||
|
.all()
|
||||||
|
.filter(function(call) {
|
||||||
|
return call.args[1] === 123456;
|
||||||
|
})
|
||||||
|
.map(function(call) {
|
||||||
|
return call.returnValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(timeoutIds.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
timeoutIds.forEach(function(timeoutId) {
|
||||||
|
expect(clearTimeoutSpy).toHaveBeenCalledWith(timeoutId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is resolved even if specs fail', function() {
|
||||||
|
jasmine.getEnv().requirePromises();
|
||||||
|
env.describe('suite', function() {
|
||||||
|
env.it('spec', function() {
|
||||||
|
env.expect(true).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return expectAsync(env.execute(null)).toBeResolved();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('The optional callback argument to #execute', function() {
|
describe('The optional callback argument to #execute', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL;
|
this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL;
|
||||||
|
|||||||
@@ -4,4 +4,10 @@
|
|||||||
env.pending('Environment does not support promises');
|
env.pending('Environment does not support promises');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
env.requireNoPromises = function() {
|
||||||
|
if (typeof Promise === 'function') {
|
||||||
|
env.pending('Environment supports promises');
|
||||||
|
}
|
||||||
|
};
|
||||||
})(jasmine.getEnv());
|
})(jasmine.getEnv());
|
||||||
|
|||||||
137
src/core/Env.js
137
src/core/Env.js
@@ -807,11 +807,17 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
*
|
*
|
||||||
* execute should not be called more than once.
|
* execute should not be called more than once.
|
||||||
*
|
*
|
||||||
|
* If the environment supports promises, execute will return a promise that
|
||||||
|
* is resolved after the suite finishes executing. The promise will be
|
||||||
|
* resolved (not rejected) as long as the suite runs to completion. Use a
|
||||||
|
* {@link Reporter} to determine whether or not the suite passed.
|
||||||
|
*
|
||||||
* @name Env#execute
|
* @name Env#execute
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
* @function
|
* @function
|
||||||
* @param {(string[])=} runnablesToRun IDs of suites and/or specs to run
|
* @param {(string[])=} runnablesToRun IDs of suites and/or specs to run
|
||||||
* @param {Function=} onComplete Function that will be called after all specs have run
|
* @param {Function=} onComplete Function that will be called after all specs have run
|
||||||
|
* @return {Promise<undefined>}
|
||||||
*/
|
*/
|
||||||
this.execute = function(runnablesToRun, onComplete) {
|
this.execute = function(runnablesToRun, onComplete) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
@@ -871,65 +877,86 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
var jasmineTimer = new j$.Timer();
|
var jasmineTimer = new j$.Timer();
|
||||||
jasmineTimer.start();
|
jasmineTimer.start();
|
||||||
|
|
||||||
/**
|
var Promise = customPromise || global.Promise;
|
||||||
* Information passed to the {@link Reporter#jasmineStarted} event.
|
|
||||||
* @typedef JasmineStartedInfo
|
|
||||||
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
|
||||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
|
||||||
*/
|
|
||||||
reporter.jasmineStarted(
|
|
||||||
{
|
|
||||||
totalSpecsDefined: totalSpecsDefined,
|
|
||||||
order: order
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
currentlyExecutingSuites.push(topSuite);
|
|
||||||
|
|
||||||
processor.execute(function() {
|
if (Promise) {
|
||||||
clearResourcesForRunnable(topSuite.id);
|
return new Promise(function(resolve) {
|
||||||
currentlyExecutingSuites.pop();
|
runAll(function() {
|
||||||
var overallStatus, incompleteReason;
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
if (hasFailures || topSuite.result.failedExpectations.length > 0) {
|
|
||||||
overallStatus = 'failed';
|
|
||||||
} else if (focusedRunnables.length > 0) {
|
|
||||||
overallStatus = 'incomplete';
|
|
||||||
incompleteReason = 'fit() or fdescribe() was found';
|
|
||||||
} else if (totalSpecsDefined === 0) {
|
|
||||||
overallStatus = 'incomplete';
|
|
||||||
incompleteReason = 'No specs found';
|
|
||||||
} else {
|
|
||||||
overallStatus = 'passed';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
resolve();
|
||||||
* Information passed to the {@link Reporter#jasmineDone} event.
|
|
||||||
* @typedef JasmineDoneInfo
|
|
||||||
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
|
||||||
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
|
||||||
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
|
||||||
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
|
||||||
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
|
||||||
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
|
||||||
*/
|
|
||||||
reporter.jasmineDone(
|
|
||||||
{
|
|
||||||
overallStatus: overallStatus,
|
|
||||||
totalTime: jasmineTimer.elapsed(),
|
|
||||||
incompleteReason: incompleteReason,
|
|
||||||
order: order,
|
|
||||||
failedExpectations: topSuite.result.failedExpectations,
|
|
||||||
deprecationWarnings: topSuite.result.deprecationWarnings
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
if (onComplete) {
|
|
||||||
onComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
} else {
|
||||||
|
runAll(function() {
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runAll(done) {
|
||||||
|
/**
|
||||||
|
* Information passed to the {@link Reporter#jasmineStarted} event.
|
||||||
|
* @typedef JasmineStartedInfo
|
||||||
|
* @property {Int} totalSpecsDefined - The total number of specs defined in this suite.
|
||||||
|
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||||
|
*/
|
||||||
|
reporter.jasmineStarted(
|
||||||
|
{
|
||||||
|
totalSpecsDefined: totalSpecsDefined,
|
||||||
|
order: order
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
currentlyExecutingSuites.push(topSuite);
|
||||||
|
|
||||||
|
processor.execute(function() {
|
||||||
|
clearResourcesForRunnable(topSuite.id);
|
||||||
|
currentlyExecutingSuites.pop();
|
||||||
|
var overallStatus, incompleteReason;
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasFailures ||
|
||||||
|
topSuite.result.failedExpectations.length > 0
|
||||||
|
) {
|
||||||
|
overallStatus = 'failed';
|
||||||
|
} else if (focusedRunnables.length > 0) {
|
||||||
|
overallStatus = 'incomplete';
|
||||||
|
incompleteReason = 'fit() or fdescribe() was found';
|
||||||
|
} else if (totalSpecsDefined === 0) {
|
||||||
|
overallStatus = 'incomplete';
|
||||||
|
incompleteReason = 'No specs found';
|
||||||
|
} else {
|
||||||
|
overallStatus = 'passed';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information passed to the {@link Reporter#jasmineDone} event.
|
||||||
|
* @typedef JasmineDoneInfo
|
||||||
|
* @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'.
|
||||||
|
* @property {Int} totalTime - The total time (in ms) that it took to execute the suite
|
||||||
|
* @property {IncompleteReason} incompleteReason - Explanation of why the suite was incomplete.
|
||||||
|
* @property {Order} order - Information about the ordering (random or not) of this execution of the suite.
|
||||||
|
* @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level.
|
||||||
|
* @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level.
|
||||||
|
*/
|
||||||
|
reporter.jasmineDone(
|
||||||
|
{
|
||||||
|
overallStatus: overallStatus,
|
||||||
|
totalTime: jasmineTimer.elapsed(),
|
||||||
|
incompleteReason: incompleteReason,
|
||||||
|
order: order,
|
||||||
|
failedExpectations: topSuite.result.failedExpectations,
|
||||||
|
deprecationWarnings: topSuite.result.deprecationWarnings
|
||||||
|
},
|
||||||
|
done
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user