Merge branch '3.99' into 4.0
This commit is contained in:
@@ -8,7 +8,18 @@ Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.authors = ["Gregg Van Hove"]
|
||||
s.summary = %q{JavaScript BDD framework}
|
||||
s.description = %q{Test your JavaScript without any framework dependencies, in any environment, and with a nice descriptive syntax.}
|
||||
s.description = <<~DESC
|
||||
Test your JavaScript without any framework dependencies, in any environment,
|
||||
and with a nice descriptive syntax.
|
||||
|
||||
Jasmine for Ruby is deprecated. The direct replacment for the jasmine-core
|
||||
gem is the jasmine-core NPM package. If you are also using the jasmine gem,
|
||||
we recommend using the jasmine-browser-runner NPM package instead. It
|
||||
supports all the same scenarios as the jasmine gem gem plus Webpacker. See
|
||||
https://jasmine.github.io/setup/browser.html for setup instructions, and
|
||||
https://github.com/jasmine/jasmine-gem/blob/main/release_notes/3.9.0.md
|
||||
for other options.
|
||||
DESC
|
||||
s.email = %q{jasmine-js@googlegroups.com}
|
||||
s.homepage = "http://jasmine.github.io"
|
||||
s.license = "MIT"
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
if ENV["SUPPRESS_JASMINE_DEPRECATION"].nil?
|
||||
puts <<~END_DEPRECATION_MSG
|
||||
The Jasmine Ruby gems are deprecated. There will be no further releases after
|
||||
the end of the Jasmine 3.x series. We recommend that most users migrate to the
|
||||
jasmine-browser-runner npm package, which is the direct replacement for the
|
||||
jasmine gem. See <https://jasmine.github.io/setup/browser.html> for setup
|
||||
instructions, including for Rails applications that use either Sprockets or
|
||||
Webpacker.
|
||||
|
||||
If jasmine-browser-runner doesn't meet your needs, one of these might:
|
||||
|
||||
* The jasmine npm package to run specs in Node.js:
|
||||
<https://github.com/jasmine/jasmine-npm>
|
||||
* The standalone distribution to run specs in browsers with no additional
|
||||
tools: <https://github.com/jasmine/jasmine#installation>
|
||||
* The jasmine-core npm package if all you need is the Jasmine assets:
|
||||
<https://github.com/jasmine/jasmine>. This is the direct equivalent of the
|
||||
jasmine-core Ruby gem.
|
||||
|
||||
To prevent this message from appearing, set the SUPPRESS_JASMINE_DEPRECATION
|
||||
environment variable.
|
||||
|
||||
END_DEPRECATION_MSG
|
||||
end
|
||||
|
||||
module Jasmine
|
||||
module Core
|
||||
class << self
|
||||
|
||||
@@ -1096,6 +1096,8 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
* @since 3.5.0
|
||||
* @type function
|
||||
* @default undefined
|
||||
* @deprecated In a future version, Jasmine will ignore the Promise config
|
||||
* property and always create native promises instead.
|
||||
*/
|
||||
Promise: undefined,
|
||||
/**
|
||||
@@ -1190,6 +1192,11 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
typeof configuration.Promise.reject === 'function'
|
||||
) {
|
||||
customPromise = configuration.Promise;
|
||||
self.deprecated(
|
||||
'The `Promise` config property is deprecated. Future versions ' +
|
||||
'of Jasmine will create native promises even if the `Promise` ' +
|
||||
'config property is set. Please remove it.'
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Custom promise library missing `resolve`/`reject` functions'
|
||||
@@ -1619,11 +1626,17 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
*
|
||||
* 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
|
||||
* @since 2.0.0
|
||||
* @function
|
||||
* @param {(string[])=} runnablesToRun IDs of suites and/or specs to run
|
||||
* @param {Function=} onComplete Function that will be called after all specs have run
|
||||
* @return {Promise<undefined>}
|
||||
*/
|
||||
this.execute = function(runnablesToRun, onComplete) {
|
||||
installGlobalErrors();
|
||||
@@ -1683,65 +1696,86 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
var jasmineTimer = new j$.Timer();
|
||||
jasmineTimer.start();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
var Promise = customPromise || global.Promise;
|
||||
|
||||
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';
|
||||
if (Promise) {
|
||||
return new Promise(function(resolve) {
|
||||
runAll(function() {
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
} 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
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -4081,16 +4115,22 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||
function taggedOnError(error) {
|
||||
var substituteMsg;
|
||||
|
||||
if (error) {
|
||||
if (j$.isError_(error)) {
|
||||
error.jasmineMessage = jasmineMessage + ': ' + error;
|
||||
} else {
|
||||
substituteMsg = jasmineMessage + ' with no error or message';
|
||||
if (error) {
|
||||
substituteMsg = jasmineMessage + ': ' + error;
|
||||
} else {
|
||||
substituteMsg = jasmineMessage + ' with no error or message';
|
||||
}
|
||||
|
||||
if (errorType === 'unhandledRejection') {
|
||||
substituteMsg +=
|
||||
'\n' +
|
||||
'(Tip: to get a useful stack trace, use ' +
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject().)';
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject(' +
|
||||
(error ? '...' : '') +
|
||||
').)';
|
||||
}
|
||||
|
||||
error = new Error(substituteMsg);
|
||||
@@ -9606,5 +9646,5 @@ getJasmineRequireObj().UserContext = function(j$) {
|
||||
};
|
||||
|
||||
getJasmineRequireObj().version = function() {
|
||||
return '3.8.0';
|
||||
return '3.9.0';
|
||||
};
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
#
|
||||
module Jasmine
|
||||
module Core
|
||||
VERSION = "3.8.0"
|
||||
VERSION = "3.9.0"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jasmine-core",
|
||||
"license": "MIT",
|
||||
"version": "3.8.0",
|
||||
"version": "3.9.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jasmine/jasmine.git"
|
||||
|
||||
65
release_notes/3.9.0.md
Normal file
65
release_notes/3.9.0.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Jasmine Core 3.9 Release Notes
|
||||
|
||||
## New features and bug fixes
|
||||
|
||||
* Fixed Trusted Types error in `j$.isError_` in Chromium-based browsers
|
||||
* Merges [#1921](https://github.com/jasmine/jasmine/pull/1921) from @bjarkler
|
||||
* Fixes [#1910](https://github.com/jasmine/jasmine/issues/1910)
|
||||
* Fixes [#1653](https://github.com/jasmine/jasmine/issues/1653)
|
||||
|
||||
* Better reporting of unhandled promise rejections with truthy but non-`Error`
|
||||
reasons on Node
|
||||
* `Env#execute` returns a promise in environments that support promises
|
||||
* Renamed `failFast` and `oneFailurePerSpec` config props to `stopOnSpecFailure`
|
||||
and `stopSpecOnExpectationFailure`
|
||||
|
||||
The new names are more self-explanatory and consistent with jasmine-npm. The
|
||||
old names are deprecated but will still work until the next major release.
|
||||
|
||||
* Split `boot.js` into two files to allow the env to be configured in between
|
||||
|
||||
This is mainly intended to support jasmine-browser-runner, which will load
|
||||
a script that configures the env in between the two boot files (`boot0.js` and
|
||||
`boot1.js`). The single-file `boot.js` will still be included until the next
|
||||
major release.
|
||||
|
||||
## Ruby deprecation
|
||||
|
||||
The Jasmine Ruby gems are deprecated. There will be no further releases after
|
||||
the end of the Jasmine 3.x series. We recommend that most users migrate to the
|
||||
[jasmine-browser-runner](https://github.com/jasmine/jasmine-browser)
|
||||
npm package, which is the direct replacement for the `jasmine` gem.
|
||||
|
||||
If `jasmine-browser-runner` doesn't meet your needs, one of these might:
|
||||
|
||||
* The [jasmine](https://github.com/jasmine/jasmine-npm) npm package to run
|
||||
specs in Node.js.
|
||||
* The [standalone distribution](https://github.com/jasmine/jasmine#installation)
|
||||
to run specs in browsers with no additional tools.
|
||||
* The [jasmine-core](https://github.com/jasmine/jasmine) npm package if all
|
||||
you need is the Jasmine assets. This is the direct equivalent of the
|
||||
`jasmine-core` Ruby gem.
|
||||
|
||||
## Documentation updates
|
||||
|
||||
* Added API docs for `Suite#id` and `Spec#id`
|
||||
* Marked `Env#hideDisabled` deprecated in jsdocs
|
||||
|
||||
|
||||
------
|
||||
|
||||
## Supported environments
|
||||
|
||||
jasmine-core 3.9.0 has been tested in the following environments.
|
||||
|
||||
| Environment | Supported versions |
|
||||
|-------------------|--------------------|
|
||||
| Node | 10, 12, 14, 16 |
|
||||
| Safari | 8-14 |
|
||||
| Chrome | 92 |
|
||||
| Firefox | 91, 78, 68 |
|
||||
| Edge | 92 |
|
||||
| Internet Explorer | 10, 11 |
|
||||
|
||||
|
||||
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||
@@ -144,11 +144,17 @@ describe('Env', function() {
|
||||
});
|
||||
|
||||
it('can be configured with a custom library', function() {
|
||||
spyOn(env, 'deprecated');
|
||||
var myLibrary = {
|
||||
resolve: jasmine.createSpy(),
|
||||
reject: jasmine.createSpy()
|
||||
};
|
||||
env.configure({ Promise: myLibrary });
|
||||
expect(env.deprecated).toHaveBeenCalledWith(
|
||||
'The `Promise` config property is deprecated. Future versions of ' +
|
||||
'Jasmine will create native promises even if the `Promise` config ' +
|
||||
'property is set. Please remove it.'
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot be configured with an invalid promise library', function() {
|
||||
@@ -540,4 +546,10 @@ describe('Env', function() {
|
||||
|
||||
expect(suiteThis).not.toBeInstanceOf(jasmineUnderTest.Suite);
|
||||
});
|
||||
|
||||
describe('#execute', function() {
|
||||
it('returns a promise', function() {
|
||||
expect(env.execute()).toBeInstanceOf(Promise);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -170,84 +170,118 @@ describe('GlobalErrors', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('reports unhandled promise rejections in node.js', function() {
|
||||
var fakeGlobal = {
|
||||
process: {
|
||||
on: jasmine.createSpy('process.on'),
|
||||
removeListener: jasmine.createSpy('process.removeListener'),
|
||||
listeners: jasmine
|
||||
.createSpy('process.listeners')
|
||||
.and.returnValue(['foo']),
|
||||
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
|
||||
}
|
||||
},
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
describe('Reporting unhandled promise rejections in node.js', function() {
|
||||
it('reports rejections with `Error` reasons', function() {
|
||||
var fakeGlobal = {
|
||||
process: {
|
||||
on: jasmine.createSpy('process.on'),
|
||||
removeListener: jasmine.createSpy('process.removeListener'),
|
||||
listeners: jasmine
|
||||
.createSpy('process.listeners')
|
||||
.and.returnValue(['foo']),
|
||||
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
|
||||
}
|
||||
},
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
||||
'unhandledRejection',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
|
||||
'unhandledRejection'
|
||||
);
|
||||
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
|
||||
'unhandledRejection'
|
||||
);
|
||||
errors.install();
|
||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
||||
'unhandledRejection',
|
||||
jasmine.any(Function)
|
||||
);
|
||||
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
|
||||
'unhandledRejection'
|
||||
);
|
||||
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
|
||||
'unhandledRejection'
|
||||
);
|
||||
|
||||
errors.pushListener(handler);
|
||||
errors.pushListener(handler);
|
||||
|
||||
var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
||||
addedListener(new Error('bar'));
|
||||
var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
||||
addedListener(new Error('bar'));
|
||||
|
||||
expect(handler).toHaveBeenCalledWith(new Error('bar'));
|
||||
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||
'Unhandled promise rejection: Error: bar'
|
||||
);
|
||||
expect(handler).toHaveBeenCalledWith(new Error('bar'));
|
||||
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||
'Unhandled promise rejection: Error: bar'
|
||||
);
|
||||
|
||||
errors.uninstall();
|
||||
errors.uninstall();
|
||||
|
||||
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
|
||||
'unhandledRejection',
|
||||
addedListener
|
||||
);
|
||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
||||
'unhandledRejection',
|
||||
'foo'
|
||||
);
|
||||
});
|
||||
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
|
||||
'unhandledRejection',
|
||||
addedListener
|
||||
);
|
||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
||||
'unhandledRejection',
|
||||
'foo'
|
||||
);
|
||||
});
|
||||
|
||||
it('reports unhandled promise rejections in node.js when no error is provided', function() {
|
||||
var fakeGlobal = {
|
||||
process: {
|
||||
on: jasmine.createSpy('process.on'),
|
||||
removeListener: function() {},
|
||||
listeners: function() {
|
||||
return [];
|
||||
},
|
||||
removeAllListeners: function() {}
|
||||
}
|
||||
},
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
it('reports rejections with non-`Error` reasons', function() {
|
||||
var fakeGlobal = {
|
||||
process: {
|
||||
on: jasmine.createSpy('process.on'),
|
||||
removeListener: function() {},
|
||||
listeners: function() {
|
||||
return [];
|
||||
},
|
||||
removeAllListeners: function() {}
|
||||
}
|
||||
},
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler);
|
||||
errors.install();
|
||||
errors.pushListener(handler);
|
||||
|
||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
||||
'unhandledRejection'
|
||||
);
|
||||
var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
||||
addedListener(undefined);
|
||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
||||
'unhandledRejection'
|
||||
);
|
||||
var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
||||
addedListener(17);
|
||||
|
||||
expect(handler).toHaveBeenCalledWith(
|
||||
new Error(
|
||||
'Unhandled promise rejection with no error or message\n' +
|
||||
'(Tip: to get a useful stack trace, use ' +
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject().)'
|
||||
)
|
||||
);
|
||||
expect(handler).toHaveBeenCalledWith(
|
||||
new Error(
|
||||
'Unhandled promise rejection: 17\n' +
|
||||
'(Tip: to get a useful stack trace, use ' +
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject(...).)'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('reports rejections with no reason provided', function() {
|
||||
var fakeGlobal = {
|
||||
process: {
|
||||
on: jasmine.createSpy('process.on'),
|
||||
removeListener: function() {},
|
||||
listeners: function() {
|
||||
return [];
|
||||
},
|
||||
removeAllListeners: function() {}
|
||||
}
|
||||
},
|
||||
handler = jasmine.createSpy('errorHandler'),
|
||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||
|
||||
errors.install();
|
||||
errors.pushListener(handler);
|
||||
|
||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
||||
'unhandledRejection'
|
||||
);
|
||||
var addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
||||
addedListener(undefined);
|
||||
|
||||
expect(handler).toHaveBeenCalledWith(
|
||||
new Error(
|
||||
'Unhandled promise rejection with no error or message\n' +
|
||||
'(Tip: to get a useful stack trace, use ' +
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject().)'
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reporting unhandled promise rejections in the browser', function() {
|
||||
|
||||
@@ -264,6 +264,7 @@ describe('Spies', function() {
|
||||
reject: jasmine.createSpy()
|
||||
};
|
||||
customPromise.resolve.and.returnValue('resolved');
|
||||
spyOn(env, 'deprecated');
|
||||
env.configure({ Promise: customPromise });
|
||||
|
||||
var spy = env.createSpy('foo').and.resolveTo(42);
|
||||
|
||||
@@ -2961,6 +2961,108 @@ describe('Env integration', function() {
|
||||
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() {
|
||||
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) {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
beforeEach(function() {
|
||||
this.savedInterval = jasmineUnderTest.DEFAULT_TIMEOUT_INTERVAL;
|
||||
|
||||
144
src/core/Env.js
144
src/core/Env.js
@@ -119,6 +119,8 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
* @since 3.5.0
|
||||
* @type function
|
||||
* @default undefined
|
||||
* @deprecated In a future version, Jasmine will ignore the Promise config
|
||||
* property and always create native promises instead.
|
||||
*/
|
||||
Promise: undefined,
|
||||
/**
|
||||
@@ -213,6 +215,11 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
typeof configuration.Promise.reject === 'function'
|
||||
) {
|
||||
customPromise = configuration.Promise;
|
||||
self.deprecated(
|
||||
'The `Promise` config property is deprecated. Future versions ' +
|
||||
'of Jasmine will create native promises even if the `Promise` ' +
|
||||
'config property is set. Please remove it.'
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Custom promise library missing `resolve`/`reject` functions'
|
||||
@@ -642,11 +649,17 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
*
|
||||
* 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
|
||||
* @since 2.0.0
|
||||
* @function
|
||||
* @param {(string[])=} runnablesToRun IDs of suites and/or specs to run
|
||||
* @param {Function=} onComplete Function that will be called after all specs have run
|
||||
* @return {Promise<undefined>}
|
||||
*/
|
||||
this.execute = function(runnablesToRun, onComplete) {
|
||||
installGlobalErrors();
|
||||
@@ -706,65 +719,86 @@ getJasmineRequireObj().Env = function(j$) {
|
||||
var jasmineTimer = new j$.Timer();
|
||||
jasmineTimer.start();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
var Promise = customPromise || global.Promise;
|
||||
|
||||
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';
|
||||
if (Promise) {
|
||||
return new Promise(function(resolve) {
|
||||
runAll(function() {
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
} 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
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,16 +19,22 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||
function taggedOnError(error) {
|
||||
var substituteMsg;
|
||||
|
||||
if (error) {
|
||||
if (j$.isError_(error)) {
|
||||
error.jasmineMessage = jasmineMessage + ': ' + error;
|
||||
} else {
|
||||
substituteMsg = jasmineMessage + ' with no error or message';
|
||||
if (error) {
|
||||
substituteMsg = jasmineMessage + ': ' + error;
|
||||
} else {
|
||||
substituteMsg = jasmineMessage + ' with no error or message';
|
||||
}
|
||||
|
||||
if (errorType === 'unhandledRejection') {
|
||||
substituteMsg +=
|
||||
'\n' +
|
||||
'(Tip: to get a useful stack trace, use ' +
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject().)';
|
||||
'Promise.reject(new Error(...)) instead of Promise.reject(' +
|
||||
(error ? '...' : '') +
|
||||
').)';
|
||||
}
|
||||
|
||||
error = new Error(substituteMsg);
|
||||
|
||||
Reference in New Issue
Block a user