Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb777e93e5 | ||
|
|
9d3fb167a2 | ||
|
|
3176eaf1d8 | ||
|
|
d31a431d1f | ||
|
|
84f78c1435 | ||
|
|
ff476b1982 | ||
|
|
d53d2ff3eb | ||
|
|
adfbd00c75 | ||
|
|
495e5fcd50 | ||
|
|
bc2aa7be25 | ||
|
|
af04599bb5 | ||
|
|
21db6ec0e3 | ||
|
|
2d07b3e6d7 | ||
|
|
6891789ed2 | ||
|
|
7a3d3c9360 | ||
|
|
1b2922e008 | ||
|
|
bd8d23f2a7 | ||
|
|
de26763868 | ||
|
|
f4be08b657 | ||
|
|
50ef882a1a | ||
|
|
c1cd5c6291 | ||
|
|
63ed2b3948 | ||
|
|
0183acc682 | ||
|
|
e15819c0dd | ||
|
|
f694194b2b | ||
|
|
94c00886a6 | ||
|
|
f5915d7963 | ||
|
|
15587f3ce3 | ||
|
|
3ecddc2555 | ||
|
|
6a7c0e6368 | ||
|
|
84daa0f5dc | ||
|
|
c6b3e947e9 |
@@ -4,6 +4,10 @@
|
|||||||
version: 2.1
|
version: 2.1
|
||||||
|
|
||||||
executors:
|
executors:
|
||||||
|
node24:
|
||||||
|
docker:
|
||||||
|
- image: cimg/node:24.0.0
|
||||||
|
working_directory: ~/workspace
|
||||||
node22:
|
node22:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:22.0.0
|
- image: cimg/node:22.0.0
|
||||||
@@ -14,7 +18,7 @@ executors:
|
|||||||
working_directory: ~/workspace
|
working_directory: ~/workspace
|
||||||
node18:
|
node18:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.0.0
|
- image: cimg/node:18.20.5
|
||||||
working_directory: ~/workspace
|
working_directory: ~/workspace
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -100,6 +104,9 @@ workflows:
|
|||||||
|
|
||||||
push:
|
push:
|
||||||
jobs:
|
jobs:
|
||||||
|
- build:
|
||||||
|
executor: node24
|
||||||
|
name: build_node_24
|
||||||
- build:
|
- build:
|
||||||
executor: node22
|
executor: node22
|
||||||
name: build_node_22
|
name: build_node_22
|
||||||
@@ -125,10 +132,10 @@ workflows:
|
|||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_18
|
||||||
- test_parallel:
|
- test_parallel:
|
||||||
executor: node18
|
executor: node24
|
||||||
name: test_parallel_node_18
|
name: test_parallel_node_24
|
||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_24
|
||||||
- test_parallel:
|
- test_parallel:
|
||||||
executor: node22
|
executor: node22
|
||||||
name: test_parallel_node_22
|
name: test_parallel_node_22
|
||||||
@@ -139,6 +146,11 @@ workflows:
|
|||||||
name: test_parallel_node_20
|
name: test_parallel_node_20
|
||||||
requires:
|
requires:
|
||||||
- build_node_20
|
- build_node_20
|
||||||
|
- test_parallel:
|
||||||
|
executor: node18
|
||||||
|
name: test_parallel_node_18
|
||||||
|
requires:
|
||||||
|
- build_node_18
|
||||||
- test_browsers:
|
- test_browsers:
|
||||||
requires:
|
requires:
|
||||||
- build_node_18
|
- build_node_18
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Microsoft Edge) as well as Node.
|
|||||||
|
|
||||||
| Environment | Supported versions |
|
| Environment | Supported versions |
|
||||||
|-------------------|----------------------------|
|
|-------------------|----------------------------|
|
||||||
| Node | 18, 20, 22 |
|
| Node | 18.20.5+*, 20, 22, 24 |
|
||||||
| Safari | 15*, 16*, 17* |
|
| Safari | 15*, 16*, 17* |
|
||||||
| Chrome | Evergreen |
|
| Chrome | Evergreen |
|
||||||
| Firefox | Evergreen, 102*, 115*, 128 |
|
| Firefox | Evergreen, 102*, 115*, 128 |
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ export default defineConfig([{
|
|||||||
...globals.node,
|
...globals.node,
|
||||||
},
|
},
|
||||||
|
|
||||||
ecmaVersion: 2018,
|
// 2022 isn't exactly right, but it's the earliest version that allows
|
||||||
|
// private properties.
|
||||||
|
ecmaVersion: 2022,
|
||||||
sourceType: "commonjs",
|
sourceType: "commonjs",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -253,9 +253,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
j$.isObject_ = function(value) {
|
j$.isObject_ = function(value) {
|
||||||
return (
|
return value !== undefined && value !== null && j$.isA_('Object', value);
|
||||||
!j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
j$.isString_ = function(value) {
|
j$.isString_ = function(value) {
|
||||||
@@ -676,10 +674,6 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
getJasmineRequireObj().util = function(j$) {
|
getJasmineRequireObj().util = function(j$) {
|
||||||
const util = {};
|
const util = {};
|
||||||
|
|
||||||
util.isUndefined = function(obj) {
|
|
||||||
return obj === void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
util.clone = function(obj) {
|
util.clone = function(obj) {
|
||||||
if (Object.prototype.toString.apply(obj) === '[object Array]') {
|
if (Object.prototype.toString.apply(obj) === '[object Array]') {
|
||||||
return obj.slice();
|
return obj.slice();
|
||||||
@@ -727,16 +721,9 @@ getJasmineRequireObj().util = function(j$) {
|
|||||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||||
};
|
};
|
||||||
|
|
||||||
util.errorWithStack = function errorWithStack() {
|
|
||||||
// Don't throw and catch. That makes it harder for users to debug their
|
|
||||||
// code with exception breakpoints, and it's unnecessary since all
|
|
||||||
// supported environments populate new Error().stack
|
|
||||||
return new Error();
|
|
||||||
};
|
|
||||||
|
|
||||||
function callerFile() {
|
function callerFile() {
|
||||||
const trace = new j$.StackTrace(util.errorWithStack());
|
const trace = new j$.StackTrace(new Error());
|
||||||
return trace.frames[2].file;
|
return trace.frames[1].file;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.jasmineFile = (function() {
|
util.jasmineFile = (function() {
|
||||||
@@ -918,10 +905,12 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
* @property {String} description - The description passed to the {@link it} that created this spec.
|
* @property {String} description - The description passed to the {@link it} that created this spec.
|
||||||
* @property {String} fullName - The full description including all ancestors of this spec.
|
* @property {String} fullName - The full description including all ancestors of this spec.
|
||||||
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
||||||
* @property {String} filename - The name of the file the spec was defined in.
|
* @property {String} filename - Deprecated. The name of the file the spec was defined in.
|
||||||
* Note: The value may be incorrect if zone.js is installed or
|
* Note: The value may be incorrect if zone.js is installed or
|
||||||
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
|
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
|
||||||
* same call stack height as the originals.
|
* same call stack height as the originals. This property may be removed in
|
||||||
|
* a future version unless there is enough user interest in keeping it.
|
||||||
|
* See {@link https://github.com/jasmine/jasmine/issues/2065}.
|
||||||
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
||||||
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
|
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
|
||||||
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
|
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
|
||||||
@@ -2315,7 +2304,7 @@ getJasmineRequireObj().Anything = function(j$) {
|
|||||||
function Anything() {}
|
function Anything() {}
|
||||||
|
|
||||||
Anything.prototype.asymmetricMatch = function(other) {
|
Anything.prototype.asymmetricMatch = function(other) {
|
||||||
return !j$.util.isUndefined(other) && other !== null;
|
return other !== undefined && other !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
Anything.prototype.jasmineToString = function() {
|
Anything.prototype.jasmineToString = function() {
|
||||||
@@ -2803,7 +2792,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
|
|
||||||
this.track = function(context) {
|
this.track = function(context) {
|
||||||
if (opts.cloneArgs) {
|
if (opts.cloneArgs) {
|
||||||
context.args = j$.util.cloneArgs(context.args);
|
context.args = opts.argsCloner(context.args);
|
||||||
}
|
}
|
||||||
calls.push(context);
|
calls.push(context);
|
||||||
};
|
};
|
||||||
@@ -2911,13 +2900,15 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this spy to do a shallow clone of arguments passed to each invocation.
|
* Set this spy to do a clone of arguments passed to each invocation.
|
||||||
* @name Spy#calls#saveArgumentsByValue
|
* @name Spy#calls#saveArgumentsByValue
|
||||||
* @since 2.5.0
|
* @since 2.5.0
|
||||||
|
* @param {Function} [argsCloner] A function to use to clone the arguments. Defaults to a shallow cloning function.
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.saveArgumentsByValue = function() {
|
this.saveArgumentsByValue = function(argsCloner = j$.util.cloneArgs) {
|
||||||
opts.cloneArgs = true;
|
opts.cloneArgs = true;
|
||||||
|
opts.argsCloner = argsCloner;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.unverifiedCount = function() {
|
this.unverifiedCount = function() {
|
||||||
@@ -2974,7 +2965,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
|
|
||||||
function getUnclampedSetTimeout(global) {
|
function getUnclampedSetTimeout(global) {
|
||||||
const { setTimeout } = global;
|
const { setTimeout } = global;
|
||||||
if (j$.util.isUndefined(global.MessageChannel)) {
|
if (!global.MessageChannel) {
|
||||||
return setTimeout;
|
return setTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3034,10 +3025,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
// so we avoid the overhead.
|
// so we avoid the overhead.
|
||||||
return nodeQueueMicrotaskImpl(global);
|
return nodeQueueMicrotaskImpl(global);
|
||||||
} else if (
|
} else if (SAFARI_OR_WIN_WEBKIT || !global.MessageChannel /* tests */) {
|
||||||
SAFARI_OR_WIN_WEBKIT ||
|
|
||||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
|
||||||
) {
|
|
||||||
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||||
// and other WebKit-based browsers, such as the one distributed by Playwright
|
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||||
// to test Safari-like behavior on Windows.
|
// to test Safari-like behavior on Windows.
|
||||||
@@ -3125,6 +3113,10 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.uninstall = function() {
|
this.uninstall = function() {
|
||||||
|
// Ensure auto ticking loop is aborted when clock is uninstalled
|
||||||
|
if (tickMode.mode === 'auto') {
|
||||||
|
tickMode = { mode: 'manual', counter: tickMode.counter + 1 };
|
||||||
|
}
|
||||||
delayedFunctionScheduler = null;
|
delayedFunctionScheduler = null;
|
||||||
mockDate.uninstall();
|
mockDate.uninstall();
|
||||||
replace(global, realTimingFunctions);
|
replace(global, realTimingFunctions);
|
||||||
@@ -3278,6 +3270,12 @@ callbacks to execute _before_ running the next one.
|
|||||||
//
|
//
|
||||||
// @return {!Promise<undefined>}
|
// @return {!Promise<undefined>}
|
||||||
async function newMacrotask() {
|
async function newMacrotask() {
|
||||||
|
if (NODE_JS) {
|
||||||
|
// setImmediate is generally faster than setTimeout in Node
|
||||||
|
// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
|
||||||
|
return new Promise(resolve => void setImmediate(resolve));
|
||||||
|
}
|
||||||
|
|
||||||
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||||
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||||
@@ -3444,7 +3442,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
this.scheduledLookup_ = [];
|
this.scheduledLookup_ = [];
|
||||||
this.scheduledFunctions_ = {};
|
this.scheduledFunctions_ = {};
|
||||||
this.currentTime_ = 0;
|
this.currentTime_ = 0;
|
||||||
this.delayedFnCount_ = 0;
|
this.delayedFnStartCount_ = 1e12; // arbitrarily large number to avoid collisions with native timer IDs;
|
||||||
this.deletedKeys_ = [];
|
this.deletedKeys_ = [];
|
||||||
|
|
||||||
this.tick = function(millis, tickDate) {
|
this.tick = function(millis, tickDate) {
|
||||||
@@ -3476,7 +3474,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
millis = millis || 0;
|
millis = millis || 0;
|
||||||
timeoutKey = timeoutKey || ++this.delayedFnCount_;
|
timeoutKey = timeoutKey || ++this.delayedFnStartCount_;
|
||||||
runAtMillis = runAtMillis || this.currentTime_ + millis;
|
runAtMillis = runAtMillis || this.currentTime_ + millis;
|
||||||
|
|
||||||
const funcToSchedule = {
|
const funcToSchedule = {
|
||||||
@@ -3721,7 +3719,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
|
|||||||
|
|
||||||
Deprecator.prototype.stackTrace_ = function() {
|
Deprecator.prototype.stackTrace_ = function() {
|
||||||
const formatter = new j$.ExceptionFormatter();
|
const formatter = new j$.ExceptionFormatter();
|
||||||
return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, '');
|
return formatter.stack(new Error()).replace(/^Error\n/m, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
Deprecator.prototype.report_ = function(runnable, deprecation, options) {
|
Deprecator.prototype.report_ = function(runnable, deprecation, options) {
|
||||||
@@ -4031,7 +4029,7 @@ getJasmineRequireObj().Expectation = function(j$) {
|
|||||||
return function() {
|
return function() {
|
||||||
// Capture the call stack here, before we go async, so that it will contain
|
// Capture the call stack here, before we go async, so that it will contain
|
||||||
// frames that are relevant to the user instead of just parts of Jasmine.
|
// frames that are relevant to the user instead of just parts of Jasmine.
|
||||||
const errorForStack = j$.util.errorWithStack();
|
const errorForStack = new Error();
|
||||||
|
|
||||||
return this.expector
|
return this.expector
|
||||||
.compare(name, matcherFactory, arguments)
|
.compare(name, matcherFactory, arguments)
|
||||||
@@ -4306,135 +4304,38 @@ getJasmineRequireObj().formatErrorMsg = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getJasmineRequireObj().GlobalErrors = function(j$) {
|
getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||||
function GlobalErrors(global) {
|
class GlobalErrors {
|
||||||
global = global || j$.getGlobal();
|
#adapter;
|
||||||
|
#handlers;
|
||||||
|
#overrideHandler;
|
||||||
|
#onRemoveOverrideHandler;
|
||||||
|
|
||||||
const handlers = [];
|
constructor(global) {
|
||||||
let overrideHandler = null,
|
global = global || j$.getGlobal();
|
||||||
onRemoveOverrideHandler = null;
|
const dispatchError = this.#dispatchError.bind(this);
|
||||||
|
|
||||||
function onBrowserError(event) {
|
|
||||||
dispatchBrowserError(event.error, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchBrowserError(error, event) {
|
|
||||||
if (overrideHandler) {
|
|
||||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
|
||||||
overrideHandler(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
handler(error, event);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.originalHandlers = {};
|
|
||||||
this.jasmineHandlers = {};
|
|
||||||
this.installOne_ = function installOne_(errorType, jasmineMessage) {
|
|
||||||
function taggedOnError(error) {
|
|
||||||
if (j$.isError_(error)) {
|
|
||||||
error.jasmineMessage = jasmineMessage + ': ' + error;
|
|
||||||
} else {
|
|
||||||
let substituteMsg;
|
|
||||||
|
|
||||||
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(' +
|
|
||||||
(error ? '...' : '') +
|
|
||||||
').)';
|
|
||||||
}
|
|
||||||
|
|
||||||
error = new Error(substituteMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
|
||||||
|
|
||||||
if (overrideHandler) {
|
|
||||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
|
||||||
overrideHandler(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
handler(error);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.originalHandlers[errorType] = global.process.listeners(errorType);
|
|
||||||
this.jasmineHandlers[errorType] = taggedOnError;
|
|
||||||
|
|
||||||
global.process.removeAllListeners(errorType);
|
|
||||||
global.process.on(errorType, taggedOnError);
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
const errorTypes = Object.keys(this.originalHandlers);
|
|
||||||
for (const errorType of errorTypes) {
|
|
||||||
global.process.removeListener(
|
|
||||||
errorType,
|
|
||||||
this.jasmineHandlers[errorType]
|
|
||||||
);
|
|
||||||
|
|
||||||
for (let i = 0; i < this.originalHandlers[errorType].length; i++) {
|
|
||||||
global.process.on(errorType, this.originalHandlers[errorType][i]);
|
|
||||||
}
|
|
||||||
delete this.originalHandlers[errorType];
|
|
||||||
delete this.jasmineHandlers[errorType];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
this.install = function install() {
|
|
||||||
if (
|
if (
|
||||||
global.process &&
|
global.process &&
|
||||||
global.process.listeners &&
|
global.process.listeners &&
|
||||||
j$.isFunction_(global.process.on)
|
j$.isFunction_(global.process.on)
|
||||||
) {
|
) {
|
||||||
this.installOne_('uncaughtException', 'Uncaught exception');
|
this.#adapter = new NodeAdapter(global, dispatchError);
|
||||||
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
|
||||||
} else {
|
} else {
|
||||||
global.addEventListener('error', onBrowserError);
|
this.#adapter = new BrowserAdapter(global, dispatchError);
|
||||||
|
|
||||||
const browserRejectionHandler = function browserRejectionHandler(
|
|
||||||
event
|
|
||||||
) {
|
|
||||||
if (j$.isError_(event.reason)) {
|
|
||||||
event.reason.jasmineMessage =
|
|
||||||
'Unhandled promise rejection: ' + event.reason;
|
|
||||||
dispatchBrowserError(event.reason, event);
|
|
||||||
} else {
|
|
||||||
dispatchBrowserError(
|
|
||||||
'Unhandled promise rejection: ' + event.reason,
|
|
||||||
event
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
global.removeEventListener('error', onBrowserError);
|
|
||||||
global.removeEventListener(
|
|
||||||
'unhandledrejection',
|
|
||||||
browserRejectionHandler
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
this.#handlers = [];
|
||||||
|
this.#overrideHandler = null;
|
||||||
|
this.#onRemoveOverrideHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#adapter.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
this.#adapter.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
// The listener at the top of the stack will be called with two arguments:
|
// The listener at the top of the stack will be called with two arguments:
|
||||||
// the error and the event. Either of them may be falsy.
|
// the error and the event. Either of them may be falsy.
|
||||||
@@ -4443,35 +4344,183 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
// browsers but will be falsy in Node.
|
// browsers but will be falsy in Node.
|
||||||
// Listeners that are pushed after spec files have been loaded should be
|
// Listeners that are pushed after spec files have been loaded should be
|
||||||
// able to just use the error parameter.
|
// able to just use the error parameter.
|
||||||
this.pushListener = function pushListener(listener) {
|
pushListener(listener) {
|
||||||
handlers.push(listener);
|
this.#handlers.push(listener);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.popListener = function popListener(listener) {
|
popListener(listener) {
|
||||||
if (!listener) {
|
if (!listener) {
|
||||||
throw new Error('popListener expects a listener');
|
throw new Error('popListener expects a listener');
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.pop();
|
this.#handlers.pop();
|
||||||
};
|
}
|
||||||
|
|
||||||
this.setOverrideListener = function(listener, onRemove) {
|
setOverrideListener(listener, onRemove) {
|
||||||
if (overrideHandler) {
|
if (this.#overrideHandler) {
|
||||||
throw new Error("Can't set more than one override listener at a time");
|
throw new Error("Can't set more than one override listener at a time");
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideHandler = listener;
|
this.#overrideHandler = listener;
|
||||||
onRemoveOverrideHandler = onRemove;
|
this.#onRemoveOverrideHandler = onRemove;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.removeOverrideListener = function() {
|
removeOverrideListener() {
|
||||||
if (onRemoveOverrideHandler) {
|
if (this.#onRemoveOverrideHandler) {
|
||||||
onRemoveOverrideHandler();
|
this.#onRemoveOverrideHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideHandler = null;
|
this.#overrideHandler = null;
|
||||||
onRemoveOverrideHandler = null;
|
this.#onRemoveOverrideHandler = null;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Either error or event may be undefined
|
||||||
|
#dispatchError(error, event) {
|
||||||
|
if (this.#overrideHandler) {
|
||||||
|
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||||
|
this.#overrideHandler(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = this.#handlers[this.#handlers.length - 1];
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler(error, event);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserAdapter {
|
||||||
|
#global;
|
||||||
|
#dispatchError;
|
||||||
|
#onError;
|
||||||
|
#onUnhandledRejection;
|
||||||
|
|
||||||
|
constructor(global, dispatchError) {
|
||||||
|
this.#global = global;
|
||||||
|
this.#dispatchError = dispatchError;
|
||||||
|
this.#onError = event => this.#dispatchError(event.error, event);
|
||||||
|
this.#onUnhandledRejection = this.#unhandledRejectionHandler.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#global.addEventListener('error', this.#onError);
|
||||||
|
|
||||||
|
this.#global.addEventListener(
|
||||||
|
'unhandledrejection',
|
||||||
|
this.#onUnhandledRejection
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
this.#global.removeEventListener('error', this.#onError);
|
||||||
|
this.#global.removeEventListener(
|
||||||
|
'unhandledrejection',
|
||||||
|
this.#onUnhandledRejection
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#unhandledRejectionHandler(event) {
|
||||||
|
if (j$.isError_(event.reason)) {
|
||||||
|
event.reason.jasmineMessage =
|
||||||
|
'Unhandled promise rejection: ' + event.reason;
|
||||||
|
this.#dispatchError(event.reason, event);
|
||||||
|
} else {
|
||||||
|
this.#dispatchError(
|
||||||
|
'Unhandled promise rejection: ' + event.reason,
|
||||||
|
event
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeAdapter {
|
||||||
|
#global;
|
||||||
|
#dispatchError;
|
||||||
|
#originalHandlers;
|
||||||
|
#jasmineHandlers;
|
||||||
|
#onError;
|
||||||
|
#onUnhandledRejection;
|
||||||
|
|
||||||
|
constructor(global, dispatchError) {
|
||||||
|
this.#global = global;
|
||||||
|
this.#dispatchError = dispatchError;
|
||||||
|
|
||||||
|
this.#jasmineHandlers = {};
|
||||||
|
this.#originalHandlers = {};
|
||||||
|
|
||||||
|
this.#onError = error =>
|
||||||
|
this.#eventHandler(error, 'uncaughtException', 'Uncaught exception');
|
||||||
|
this.#onUnhandledRejection = error =>
|
||||||
|
this.#eventHandler(
|
||||||
|
error,
|
||||||
|
'unhandledRejection',
|
||||||
|
'Unhandled promise rejection'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#installHandler('uncaughtException', this.#onError);
|
||||||
|
this.#installHandler('unhandledRejection', this.#onUnhandledRejection);
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
const errorTypes = Object.keys(this.#originalHandlers);
|
||||||
|
for (const errorType of errorTypes) {
|
||||||
|
this.#global.process.removeListener(
|
||||||
|
errorType,
|
||||||
|
this.#jasmineHandlers[errorType]
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#originalHandlers[errorType].length; i++) {
|
||||||
|
this.#global.process.on(
|
||||||
|
errorType,
|
||||||
|
this.#originalHandlers[errorType][i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
delete this.#originalHandlers[errorType];
|
||||||
|
delete this.#jasmineHandlers[errorType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#installHandler(errorType, handler) {
|
||||||
|
this.#originalHandlers[errorType] = this.#global.process.listeners(
|
||||||
|
errorType
|
||||||
|
);
|
||||||
|
this.#jasmineHandlers[errorType] = handler;
|
||||||
|
|
||||||
|
this.#global.process.removeAllListeners(errorType);
|
||||||
|
this.#global.process.on(errorType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventHandler(error, errorType, jasmineMessage) {
|
||||||
|
if (j$.isError_(error)) {
|
||||||
|
error.jasmineMessage = jasmineMessage + ': ' + error;
|
||||||
|
} else {
|
||||||
|
let substituteMsg;
|
||||||
|
|
||||||
|
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(' +
|
||||||
|
(error ? '...' : '') +
|
||||||
|
').)';
|
||||||
|
}
|
||||||
|
|
||||||
|
error = new Error(substituteMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#dispatchError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GlobalErrors;
|
return GlobalErrors;
|
||||||
@@ -4865,13 +4914,14 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
|
|||||||
|
|
||||||
const actualCustom = this.prettyPrinter_.customFormat_(actual);
|
const actualCustom = this.prettyPrinter_.customFormat_(actual);
|
||||||
const expectedCustom = this.prettyPrinter_.customFormat_(expected);
|
const expectedCustom = this.prettyPrinter_.customFormat_(expected);
|
||||||
const useCustom = !(
|
const useCustom =
|
||||||
j$.util.isUndefined(actualCustom) &&
|
actualCustom !== undefined || expectedCustom !== undefined;
|
||||||
j$.util.isUndefined(expectedCustom)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (useCustom) {
|
if (useCustom) {
|
||||||
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
|
const prettyActual = actualCustom || this.prettyPrinter_(actual);
|
||||||
|
const prettyExpected =
|
||||||
|
expectedCustom || this.prettyPrinter_(expected);
|
||||||
|
messages.push(wrapPrettyPrinted(prettyActual, prettyExpected, path));
|
||||||
return false; // don't recurse further
|
return false; // don't recurse further
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5124,13 +5174,13 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
|||||||
bStack,
|
bStack,
|
||||||
diffBuilder
|
diffBuilder
|
||||||
);
|
);
|
||||||
if (!j$.util.isUndefined(asymmetricResult)) {
|
if (asymmetricResult !== undefined) {
|
||||||
return asymmetricResult;
|
return asymmetricResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tester of this.customTesters_) {
|
for (const tester of this.customTesters_) {
|
||||||
const customTesterResult = tester(a, b);
|
const customTesterResult = tester(a, b);
|
||||||
if (!j$.util.isUndefined(customTesterResult)) {
|
if (customTesterResult !== undefined) {
|
||||||
if (!customTesterResult) {
|
if (!customTesterResult) {
|
||||||
diffBuilder.recordMismatch();
|
diffBuilder.recordMismatch();
|
||||||
}
|
}
|
||||||
@@ -7512,7 +7562,7 @@ getJasmineRequireObj().MockDate = function(j$) {
|
|||||||
if (mockDate instanceof GlobalDate) {
|
if (mockDate instanceof GlobalDate) {
|
||||||
currentTime = mockDate.getTime();
|
currentTime = mockDate.getTime();
|
||||||
} else {
|
} else {
|
||||||
if (!j$.util.isUndefined(mockDate)) {
|
if (mockDate !== undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'The argument to jasmine.clock().mockDate(), if specified, ' +
|
'The argument to jasmine.clock().mockDate(), if specified, ' +
|
||||||
'should be a Date instance.'
|
'should be a Date instance.'
|
||||||
@@ -7729,7 +7779,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
|||||||
|
|
||||||
if (customFormatResult) {
|
if (customFormatResult) {
|
||||||
this.emitScalar(customFormatResult);
|
this.emitScalar(customFormatResult);
|
||||||
} else if (j$.util.isUndefined(value)) {
|
} else if (value === undefined) {
|
||||||
this.emitScalar('undefined');
|
this.emitScalar('undefined');
|
||||||
} else if (value === null) {
|
} else if (value === null) {
|
||||||
this.emitScalar('null');
|
this.emitScalar('null');
|
||||||
@@ -7748,7 +7798,11 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
|||||||
} else if (value instanceof RegExp) {
|
} else if (value instanceof RegExp) {
|
||||||
this.emitScalar(value.toString());
|
this.emitScalar(value.toString());
|
||||||
} else if (typeof value === 'function') {
|
} else if (typeof value === 'function') {
|
||||||
this.emitScalar('Function');
|
if (value.name) {
|
||||||
|
this.emitScalar(`Function '${value.name}'`);
|
||||||
|
} else {
|
||||||
|
this.emitScalar('Function');
|
||||||
|
}
|
||||||
} else if (j$.isDomNode(value)) {
|
} else if (j$.isDomNode(value)) {
|
||||||
if (value.tagName) {
|
if (value.tagName) {
|
||||||
this.emitDomElement(value);
|
this.emitDomElement(value);
|
||||||
@@ -9664,7 +9718,7 @@ getJasmineRequireObj().Spy = function(j$) {
|
|||||||
"Spy '" +
|
"Spy '" +
|
||||||
strategyArgs.name +
|
strategyArgs.name +
|
||||||
"' received a call with arguments " +
|
"' received a call with arguments " +
|
||||||
j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) +
|
matchersUtil.pp(Array.prototype.slice.call(args)) +
|
||||||
' but all configured strategies specify other arguments.'
|
' but all configured strategies specify other arguments.'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -9824,7 +9878,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
this.spyOn = function(obj, methodName) {
|
this.spyOn = function(obj, methodName) {
|
||||||
const getErrorMsg = spyOnMsg;
|
const getErrorMsg = spyOnMsg;
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj) || obj === null) {
|
if (obj === undefined || obj === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
getErrorMsg(
|
getErrorMsg(
|
||||||
'could not find an object to spy upon for ' + methodName + '()'
|
'could not find an object to spy upon for ' + methodName + '()'
|
||||||
@@ -9832,11 +9886,11 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(methodName) || methodName === null) {
|
if (methodName === undefined || methodName === null) {
|
||||||
throw new Error(getErrorMsg('No method name supplied'));
|
throw new Error(getErrorMsg('No method name supplied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj[methodName])) {
|
if (obj[methodName] === undefined) {
|
||||||
throw new Error(getErrorMsg(methodName + '() method does not exist'));
|
throw new Error(getErrorMsg(methodName + '() method does not exist'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9901,7 +9955,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
|
|
||||||
accessType = accessType || 'get';
|
accessType = accessType || 'get';
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj)) {
|
if (!obj) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
getErrorMsg(
|
getErrorMsg(
|
||||||
'spyOn could not find an object to spy upon for ' +
|
'spyOn could not find an object to spy upon for ' +
|
||||||
@@ -9911,7 +9965,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(propertyName)) {
|
if (propertyName === undefined) {
|
||||||
throw new Error(getErrorMsg('No property name supplied'));
|
throw new Error(getErrorMsg('No property name supplied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9976,7 +10030,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
|
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
|
||||||
if (j$.util.isUndefined(obj)) {
|
if (!obj) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'spyOnAllFunctions could not find an object to spy upon'
|
'spyOnAllFunctions could not find an object to spy upon'
|
||||||
);
|
);
|
||||||
@@ -10483,10 +10537,12 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
||||||
* @property {String} fullName - The full description including all ancestors of this suite.
|
* @property {String} fullName - The full description including all ancestors of this suite.
|
||||||
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
|
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
|
||||||
* @property {String} filename - The name of the file the suite was defined in.
|
* @property {String} filename - Deprecated. The name of the file the suite was defined in.
|
||||||
* Note: The value may be incorrect if zone.js is installed or
|
* Note: The value may be incorrect if zone.js is installed or
|
||||||
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
|
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
|
||||||
* don't maintain the same call stack height as the originals.
|
* don't maintain the same call stack height as the originals. This property
|
||||||
|
* may be removed in a future version unless there is enough user interest
|
||||||
|
* in keeping it. See {@link https://github.com/jasmine/jasmine/issues/2065}.
|
||||||
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
||||||
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
|
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
|
||||||
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
||||||
@@ -11391,5 +11447,5 @@ getJasmineRequireObj().UserContext = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getJasmineRequireObj().version = function() {
|
getJasmineRequireObj().version = function() {
|
||||||
return '5.7.0';
|
return '5.9.0';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jasmine-core",
|
"name": "jasmine-core",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "5.7.0",
|
"version": "5.9.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/jasmine/jasmine.git"
|
"url": "https://github.com/jasmine/jasmine.git"
|
||||||
@@ -49,9 +49,7 @@
|
|||||||
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
"jasmine-browser-runner": "github:jasmine/jasmine-browser-runner",
|
||||||
"jsdom": "^26.0.0",
|
"jsdom": "^26.0.0",
|
||||||
"prettier": "1.17.1",
|
"prettier": "1.17.1",
|
||||||
"rimraf": "^5.0.10",
|
"sass": "^1.58.3"
|
||||||
"sass": "^1.58.3",
|
|
||||||
"shelljs": "^0.9.2"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"Safari >= 15",
|
"Safari >= 15",
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
to automatically tick the clock asynchronously
|
to automatically tick the clock asynchronously
|
||||||
* Merges #2042 from @atscott and @stephenfarrar
|
* Merges #2042 from @atscott and @stephenfarrar
|
||||||
* Fixes #1725
|
* Fixes #1725
|
||||||
* Fixes #1932
|
|
||||||
|
|
||||||
* Expose [spec path](https://jasmine.github.io/api/5.7/Spec.html#getPath) as an
|
* Expose [spec path](https://jasmine.github.io/api/5.7/Spec.html#getPath) as an
|
||||||
array of names in addition to the existing concatenated name
|
array of names in addition to the existing concatenated name
|
||||||
@@ -54,10 +53,9 @@ This version has been tested in the following environments.
|
|||||||
|
|
||||||
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||||
version available at release time.<br>
|
version available at release time.<br>
|
||||||
\** Environments that are past end of life are supported on a best-effort basis.
|
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||||
They may be dropped in a future minor release of Jasmine if continued support
|
if it becomes impractical, and bugs affecting only these versions may not be
|
||||||
becomes impractical.
|
treated as release blockers.
|
||||||
|
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
28
release_notes/5.7.1.md
Normal file
28
release_notes/5.7.1.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Jasmine Core 5.7.1 Release Notes
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Ensure that uninstalling the clock also stops auto tick
|
||||||
|
* Merges #2057 from @atscott
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| Node | 18**, 20, 22 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 136* |
|
||||||
|
| Firefox | 102**, 115**, 128, 138* |
|
||||||
|
| Edge | 135* |
|
||||||
|
|
||||||
|
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||||
|
version available at release time.<br>
|
||||||
|
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||||
|
if it becomes impractical, and bugs affecting only these versions may not be
|
||||||
|
treated as release blockers.
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
44
release_notes/5.8.0.md
Normal file
44
release_notes/5.8.0.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Jasmine Core 5.8.0 Release Notes
|
||||||
|
|
||||||
|
## New Features
|
||||||
|
|
||||||
|
* Allow passing a function to `saveArgumentsByValue` to customize how arguments
|
||||||
|
are saved
|
||||||
|
* Merges [#2062](https://github.com/jasmine/jasmine/pull/2062) from @evanwaslh
|
||||||
|
* Fixes [#1886](https://github.com/jasmine/jasmine/issues/1886)
|
||||||
|
* Use custom object formatters in spy strategy mismatch errors
|
||||||
|
* Include function names in pretty printer output
|
||||||
|
* Improve performance of autoTick in Node
|
||||||
|
* Merges [#2058](https://github.com/jasmine/jasmine/pull/2058) from @atscott
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
* Fix diff building when only one side has a custom object formatter
|
||||||
|
* Fixes [#2061](https://github.com/jasmine/jasmine/issues/2061)
|
||||||
|
|
||||||
|
## Documentation improvements
|
||||||
|
|
||||||
|
* Added Node 24 to supported environments
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| Node | 18**, 20, 22, 24 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 137* |
|
||||||
|
| Firefox | 102**, 115**, 128, 139* |
|
||||||
|
| Edge | 137* |
|
||||||
|
|
||||||
|
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||||
|
version available at release time.<br>
|
||||||
|
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||||
|
if it becomes impractical, and bugs affecting only these versions may not be
|
||||||
|
treated as release blockers.
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
56
release_notes/5.9.0.md
Normal file
56
release_notes/5.9.0.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Jasmine Core 5.9.0 Release Notes
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Avoid generating mock clock timer IDs that conflict with native ones
|
||||||
|
* Fixes [#2068](https://github.com/jasmine/jasmine/issues/2068)
|
||||||
|
* Merges [#2069](https://github.com/jasmine/jasmine/pull/2069) from @atscott
|
||||||
|
|
||||||
|
## Deprecations and changes to platform support
|
||||||
|
|
||||||
|
* Node versions before 18.20.5 are no longer supported
|
||||||
|
|
||||||
|
Older 18.x versions might still work, but Jasmine is no longer tested in them
|
||||||
|
prior to release.
|
||||||
|
* Document that the filename properties of suite and spec results are deprecated
|
||||||
|
|
||||||
|
These properties are incorrect in many configurations. They'll be removed in
|
||||||
|
the next major release unless there is enough user interest in fixing them.
|
||||||
|
See <https://github.com/jasmine/jasmine/issues/2065>.
|
||||||
|
|
||||||
|
## Internal improvements
|
||||||
|
|
||||||
|
* Extensive GlobalErrors refactoring
|
||||||
|
* Removed many of the error dispatching differences between browsers and Node
|
||||||
|
* Split into portable and platform-specific parts
|
||||||
|
* Converted to ES6 classes
|
||||||
|
* Removed unnecessary errorWithStack helper
|
||||||
|
|
||||||
|
Jasmine no longer runs on platforms that create errors without stack traces.
|
||||||
|
* Removed protections against user code redefining undefined
|
||||||
|
|
||||||
|
Jasmine no longer runs on platforms that allow redefining undefined.
|
||||||
|
* Removed rimraf and shelljs dev dependencies
|
||||||
|
|
||||||
|
## Supported environments
|
||||||
|
|
||||||
|
This version has been tested in the following environments.
|
||||||
|
|
||||||
|
| Environment | Supported versions |
|
||||||
|
|-------------------|-------------------------|
|
||||||
|
| Node | 18.20.5**, 20, 22, 24 |
|
||||||
|
| Safari | 15**, 16**, 17** |
|
||||||
|
| Chrome | 138* |
|
||||||
|
| Firefox | 102**, 115**, 128, 140* |
|
||||||
|
| Edge | 138* |
|
||||||
|
|
||||||
|
\* Evergreen browser. Each version of Jasmine is tested against the latest
|
||||||
|
version available at release time.<br>
|
||||||
|
\** Supported on a best-effort basis. Support for these versions may be dropped
|
||||||
|
if it becomes impractical, and bugs affecting only these versions may not be
|
||||||
|
treated as release blockers.
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
_Release Notes generated with _[Anchorman](http://github.com/infews/anchorman)_
|
||||||
@@ -1,22 +1,16 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
const ejs = require('ejs');
|
const ejs = require('ejs');
|
||||||
const archiver = require('archiver');
|
const archiver = require('archiver');
|
||||||
const { rimrafSync } = require('rimraf');
|
|
||||||
const buildDistribution = require('./lib/buildDistribution');
|
const buildDistribution = require('./lib/buildDistribution');
|
||||||
|
|
||||||
const tmpDir = 'dist/tmp'
|
const prefix = path.join(os.tmpdir(), 'jasmine-build-standalone');
|
||||||
|
const tmpDir = fs.mkdtempSync(prefix);
|
||||||
if (!fs.existsSync(tmpDir)) {
|
|
||||||
if (!fs.existsSync(path.dirname(tmpDir))) {
|
|
||||||
fs.mkdirSync(path.dirname(tmpDir));
|
|
||||||
}
|
|
||||||
fs.mkdirSync(tmpDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildStandaloneDist().finally(function() {
|
buildStandaloneDist().finally(function() {
|
||||||
rimrafSync(tmpDir);
|
fs.rmSync(tmpDir, { recursive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
async function buildStandaloneDist() {
|
async function buildStandaloneDist() {
|
||||||
@@ -30,7 +24,7 @@ function compileSpecRunner(jasmineVersion) {
|
|||||||
const template = fs.readFileSync('src/SpecRunner.html.ejs',
|
const template = fs.readFileSync('src/SpecRunner.html.ejs',
|
||||||
{encoding: 'utf8'});
|
{encoding: 'utf8'});
|
||||||
const runnerHtml = ejs.render(template, { jasmineVersion });
|
const runnerHtml = ejs.render(template, { jasmineVersion });
|
||||||
fs.writeFileSync('dist/tmp/SpecRunner.html', runnerHtml,
|
fs.writeFileSync(path.join(tmpDir, 'SpecRunner.html'), runnerHtml,
|
||||||
{encoding: 'utf8'});
|
{encoding: 'utf8'});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +33,7 @@ async function zipStandaloneDist(jasmineVersion) {
|
|||||||
{
|
{
|
||||||
src: [
|
src: [
|
||||||
'LICENSE',
|
'LICENSE',
|
||||||
'dist/tmp/SpecRunner.html',
|
path.join(tmpDir, 'SpecRunner.html'),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,15 +25,7 @@ run_browser() {
|
|||||||
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
passfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
||||||
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
failfile=`mktemp -t jasmine-results.XXXXXX` || exit 1
|
||||||
|
|
||||||
# As of 2023-09-30, Sauce Connect doesn't work with the latest Chrome version
|
run_browser chrome latest
|
||||||
# on the default Linux. Run on Mac OS instead. The OS specification may need to
|
|
||||||
# be updated or removed when new Chrome versions stop being available on Mac OS
|
|
||||||
# 12, although historically this has taken several major OS versions.
|
|
||||||
# See <https://saucelabs.com/products/supported-browsers-devices>.
|
|
||||||
# On Saucelabs, the test suite frequently runs ~30s slower on Mac OS than it
|
|
||||||
# does on Linux, so it's probably worth removing the OS specification once Sauce
|
|
||||||
# Connect works with Chrome latest on Linux again.
|
|
||||||
run_browser chrome latest "macOS 12"
|
|
||||||
|
|
||||||
run_browser firefox latest
|
run_browser firefox latest
|
||||||
run_browser firefox 128
|
run_browser firefox 128
|
||||||
|
|||||||
@@ -277,23 +277,18 @@ describe('AsyncExpectation', function() {
|
|||||||
|
|
||||||
it('reports a passing result to the spec when the comparison passes', function() {
|
it('reports a passing result to the spec when the comparison passes', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
matchersUtil = {
|
const matchersUtil = {
|
||||||
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
|
buildFailureMessage: jasmine.createSpy('buildFailureMessage')
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -310,7 +305,7 @@ describe('AsyncExpectation', function() {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
expected: 'hello',
|
expected: 'hello',
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -329,13 +324,8 @@ describe('AsyncExpectation', function() {
|
|||||||
buildFailureMessage: function() {
|
buildFailureMessage: function() {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -352,30 +342,25 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: '',
|
message: '',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
|
it('reports a failing result and a custom fail message to the spec when the comparison fails', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: 'I am a custom message'
|
message: 'I am a custom message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -391,32 +376,27 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
|
it('reports a failing result with a custom fail message function to the spec when the comparison fails', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: function() {
|
message: function() {
|
||||||
return 'I am a custom message';
|
return 'I am a custom message';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -432,28 +412,23 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
|
it('reports a passing result to the spec when the comparison fails for a negative expectation', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: false });
|
return Promise.resolve({ pass: false });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -469,7 +444,7 @@ describe('AsyncExpectation', function() {
|
|||||||
error: undefined,
|
error: undefined,
|
||||||
expected: 'hello',
|
expected: 'hello',
|
||||||
actual: actual,
|
actual: actual,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -488,14 +463,9 @@ describe('AsyncExpectation', function() {
|
|||||||
buildFailureMessage: function() {
|
buildFailureMessage: function() {
|
||||||
return 'default message';
|
return 'default message';
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -512,31 +482,26 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: 'default message',
|
message: 'default message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
|
it('reports a failing result and a custom fail message to the spec when the comparison passes for a negative expectation', function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: true,
|
pass: true,
|
||||||
message: 'I am a custom message'
|
message: 'I am a custom message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -552,31 +517,26 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
|
it("reports a passing result to the spec when the 'not' comparison passes, given a negativeCompare", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
},
|
},
|
||||||
negativeCompare: function() {
|
negativeCompare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -592,34 +552,29 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: '',
|
message: '',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
|
it("reports a failing result and a custom fail message to the spec when the 'not' comparison fails, given a negativeCompare", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({ pass: true });
|
return Promise.resolve({ pass: true });
|
||||||
},
|
},
|
||||||
negativeCompare: function() {
|
negativeCompare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: "I'm a custom message"
|
message: "I'm a custom message"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
actual = 'an actual',
|
const actual = 'an actual';
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
customAsyncMatchers: matchers,
|
customAsyncMatchers: matchers,
|
||||||
@@ -635,7 +590,7 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: actual,
|
actual: actual,
|
||||||
message: "I'm a custom message",
|
message: "I'm a custom message",
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -643,24 +598,19 @@ describe('AsyncExpectation', function() {
|
|||||||
it('reports errorWithStack when a custom error message is returned', function() {
|
it('reports errorWithStack when a custom error message is returned', function() {
|
||||||
const customError = new Error('I am a custom error');
|
const customError = new Error('I am a custom error');
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: false,
|
pass: false,
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: customError
|
error: customError
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -676,30 +626,25 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a custom message to the spec when a 'not' comparison fails", function() {
|
it("reports a custom message to the spec when a 'not' comparison fails", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: true,
|
pass: true,
|
||||||
message: 'I am a custom message'
|
message: 'I am a custom message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
const expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -715,32 +660,27 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
|
it("reports a custom message func to the spec when a 'not' comparison fails", function() {
|
||||||
const matchers = {
|
const matchers = {
|
||||||
toFoo: function() {
|
toFoo: function() {
|
||||||
return {
|
return {
|
||||||
compare: function() {
|
compare: function() {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
pass: true,
|
pass: true,
|
||||||
message: function() {
|
message: function() {
|
||||||
return 'I am a custom message';
|
return 'I am a custom message';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
addExpectationResult = jasmine.createSpy('addExpectationResult'),
|
const addExpectationResult = jasmine.createSpy('addExpectationResult');
|
||||||
errorWithStack = new Error('errorWithStack');
|
|
||||||
|
|
||||||
spyOn(jasmineUnderTest.util, 'errorWithStack').and.returnValue(
|
|
||||||
errorWithStack
|
|
||||||
);
|
|
||||||
|
|
||||||
let expectation = jasmineUnderTest.Expectation.asyncFactory({
|
let expectation = jasmineUnderTest.Expectation.asyncFactory({
|
||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
@@ -756,7 +696,7 @@ describe('AsyncExpectation', function() {
|
|||||||
actual: 'an actual',
|
actual: 'an actual',
|
||||||
message: 'I am a custom message',
|
message: 'I am a custom message',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorForStack: errorWithStack
|
errorForStack: jasmine.any(Error)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,6 +134,42 @@ describe('CallTracker', function() {
|
|||||||
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('allows object arguments to be deep cloned', function() {
|
||||||
|
const callTracker = new jasmineUnderTest.CallTracker();
|
||||||
|
callTracker.saveArgumentsByValue(args => JSON.parse(JSON.stringify(args)));
|
||||||
|
|
||||||
|
const objectArg = { foo: { bar: { baz: ['qux'] } } },
|
||||||
|
arrayArg = ['foo', 'bar'];
|
||||||
|
|
||||||
|
callTracker.track({
|
||||||
|
object: {},
|
||||||
|
args: [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0]
|
||||||
|
});
|
||||||
|
|
||||||
|
objectArg.foo.bar.baz.push('quux');
|
||||||
|
|
||||||
|
expect(callTracker.mostRecent().args[0]).not.toBe(objectArg);
|
||||||
|
expect(callTracker.mostRecent().args[0]).not.toEqual(objectArg);
|
||||||
|
expect(callTracker.mostRecent().args[0]).toEqual({
|
||||||
|
foo: { bar: { baz: ['qux'] } }
|
||||||
|
});
|
||||||
|
expect(callTracker.mostRecent().args[1]).not.toBe(arrayArg);
|
||||||
|
expect(callTracker.mostRecent().args[1]).toEqual(arrayArg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can take any function to transform arguments when saving by value', function() {
|
||||||
|
const callTracker = new jasmineUnderTest.CallTracker();
|
||||||
|
callTracker.saveArgumentsByValue(JSON.stringify);
|
||||||
|
|
||||||
|
const objectArg = { foo: { bar: { baz: ['qux'] } } },
|
||||||
|
arrayArg = ['foo', 'bar'],
|
||||||
|
args = [objectArg, arrayArg, false, undefined, null, NaN, '', 0, 1.0];
|
||||||
|
|
||||||
|
callTracker.track({ object: {}, args });
|
||||||
|
|
||||||
|
expect(callTracker.mostRecent().args).toEqual(JSON.stringify(args));
|
||||||
|
});
|
||||||
|
|
||||||
it('saves primitive arguments by value', function() {
|
it('saves primitive arguments by value', function() {
|
||||||
const callTracker = new jasmineUnderTest.CallTracker(),
|
const callTracker = new jasmineUnderTest.CallTracker(),
|
||||||
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
|
args = [undefined, null, false, '', /\s/, 0, 1.2, NaN];
|
||||||
|
|||||||
@@ -699,22 +699,39 @@ describe('Clock (acceptance)', function() {
|
|||||||
tick: function() {},
|
tick: function() {},
|
||||||
uninstall: function() {}
|
uninstall: function() {}
|
||||||
};
|
};
|
||||||
|
// window setTimeout to window to make firefox happy
|
||||||
|
const _setTimeout =
|
||||||
|
typeof window !== 'undefined' ? setTimeout.bind(window) : setTimeout;
|
||||||
|
// passing a fake global allows us to preserve the real timing functions for use in tests
|
||||||
|
const _global = { setTimeout: _setTimeout, setInterval: setInterval };
|
||||||
clock = new jasmineUnderTest.Clock(
|
clock = new jasmineUnderTest.Clock(
|
||||||
// We use the real window for global or firefox is displeased when we try to call a real setTimeout on an object "that doesn't implement window".
|
_global,
|
||||||
typeof window !== 'undefined' ? window : { setTimeout: setTimeout },
|
|
||||||
function() {
|
function() {
|
||||||
return delayedFunctionScheduler;
|
return delayedFunctionScheduler;
|
||||||
},
|
},
|
||||||
mockDate
|
mockDate
|
||||||
);
|
);
|
||||||
clock.install();
|
clock.install().autoTick();
|
||||||
clock.autoTick();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
clock.uninstall();
|
clock.uninstall();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('flushes microtask queue between macrotasks', async () => {
|
||||||
|
const log = [];
|
||||||
|
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
|
||||||
|
log.push(1);
|
||||||
|
Promise.resolve().then(() => log.push(2));
|
||||||
|
Promise.resolve().then(() => log.push(3));
|
||||||
|
});
|
||||||
|
await new Promise(r => clock.setTimeout(r, 10)).then(() => {
|
||||||
|
log.push(4);
|
||||||
|
Promise.resolve().then(() => log.push(5));
|
||||||
|
});
|
||||||
|
expect(log).toEqual([1, 2, 3, 4, 5]);
|
||||||
|
});
|
||||||
|
|
||||||
it('can run setTimeouts/setIntervals asynchronously', function() {
|
it('can run setTimeouts/setIntervals asynchronously', function() {
|
||||||
const recurring = jasmine.createSpy('recurring'),
|
const recurring = jasmine.createSpy('recurring'),
|
||||||
fn1 = jasmine.createSpy('fn1'),
|
fn1 = jasmine.createSpy('fn1'),
|
||||||
@@ -776,6 +793,26 @@ describe('Clock (acceptance)', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('aborts auto ticking when uninstalled, even if installed again synchonrously', async () => {
|
||||||
|
clock.uninstall();
|
||||||
|
clock.install();
|
||||||
|
|
||||||
|
let resolved = false;
|
||||||
|
const promise = new Promise(resolve => {
|
||||||
|
clock.setTimeout(resolve, 1);
|
||||||
|
}).then(() => {
|
||||||
|
resolved = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait some real time and verify that the clock did not flush the timer above automatically
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2));
|
||||||
|
expect(resolved).toBe(false);
|
||||||
|
|
||||||
|
// enabling auto tick again will flush the timer
|
||||||
|
clock.autoTick();
|
||||||
|
await expectAsync(promise).toBeResolved();
|
||||||
|
});
|
||||||
|
|
||||||
it('speeds up the execution of the timers in all browsers', async () => {
|
it('speeds up the execution of the timers in all browsers', async () => {
|
||||||
const startTimeMs = performance.now() / 1000;
|
const startTimeMs = performance.now() / 1000;
|
||||||
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
await new Promise(resolve => clock.setTimeout(resolve, 5000));
|
||||||
|
|||||||
@@ -86,12 +86,13 @@ describe('DelayedFunctionScheduler', function() {
|
|||||||
it('increments scheduled fns ids unless one is passed', function() {
|
it('increments scheduled fns ids unless one is passed', function() {
|
||||||
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(1);
|
const initial = scheduler.scheduleFunction(function() {}, 0);
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(2);
|
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 1);
|
||||||
|
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 2);
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0, [], false, 123)).toBe(
|
expect(scheduler.scheduleFunction(function() {}, 0, [], false, 123)).toBe(
|
||||||
123
|
123
|
||||||
);
|
);
|
||||||
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(3);
|
expect(scheduler.scheduleFunction(function() {}, 0)).toBe(initial + 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#removeFunctionWithId removes a previously scheduled function with a given id', function() {
|
it('#removeFunctionWithId removes a previously scheduled function with a given id', function() {
|
||||||
@@ -313,6 +314,28 @@ describe('DelayedFunctionScheduler', function() {
|
|||||||
expect(tickDate).toHaveBeenCalledWith(1);
|
expect(tickDate).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not conflict with native timer IDs', function() {
|
||||||
|
const NODE_JS =
|
||||||
|
typeof process !== 'undefined' &&
|
||||||
|
process.versions &&
|
||||||
|
typeof process.versions.node === 'string';
|
||||||
|
if (NODE_JS) {
|
||||||
|
pending('numeric timer ID conflicts only relevant for browsers.');
|
||||||
|
}
|
||||||
|
const nativeTimeoutId = setTimeout(function() {}, 100);
|
||||||
|
|
||||||
|
const scheduler = new jasmineUnderTest.DelayedFunctionScheduler();
|
||||||
|
const fn = jasmine.createSpy('fn');
|
||||||
|
|
||||||
|
for (let i = 0; i < nativeTimeoutId; i++) {
|
||||||
|
scheduler.scheduleFunction(fn, 0, [], false);
|
||||||
|
}
|
||||||
|
scheduler.removeFunctionWithId(nativeTimeoutId);
|
||||||
|
scheduler.tick(1);
|
||||||
|
|
||||||
|
expect(fn).toHaveBeenCalledTimes(nativeTimeoutId);
|
||||||
|
});
|
||||||
|
|
||||||
describe('ticking inside a scheduled function', function() {
|
describe('ticking inside a scheduled function', function() {
|
||||||
let clock;
|
let clock;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
describe('GlobalErrors', function() {
|
describe('GlobalErrors', function() {
|
||||||
it('calls the added handler on error', function() {
|
it('calls the added handler on error', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.is(error),
|
jasmine.is(error),
|
||||||
@@ -17,17 +17,17 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('is not affected by overriding global.onerror', function() {
|
it('is not affected by overriding global.onerror', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
fakeGlobal.onerror = () => {};
|
globals.global.onerror = () => {};
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.is(error),
|
jasmine.is(error),
|
||||||
@@ -36,17 +36,17 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('only calls the most recent handler', function() {
|
it('only calls the most recent handler', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler1 = jasmine.createSpy('errorHandler1');
|
const handler1 = jasmine.createSpy('errorHandler1');
|
||||||
const handler2 = jasmine.createSpy('errorHandler2');
|
const handler2 = jasmine.createSpy('errorHandler2');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
errors.pushListener(handler2);
|
errors.pushListener(handler2);
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
expect(handler2).toHaveBeenCalledWith(
|
expect(handler2).toHaveBeenCalledWith(
|
||||||
@@ -56,10 +56,10 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('calls previous handlers when one is removed', function() {
|
it('calls previous handlers when one is removed', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler1 = jasmine.createSpy('errorHandler1');
|
const handler1 = jasmine.createSpy('errorHandler1');
|
||||||
const handler2 = jasmine.createSpy('errorHandler2');
|
const handler2 = jasmine.createSpy('errorHandler2');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
@@ -68,7 +68,7 @@ describe('GlobalErrors', function() {
|
|||||||
errors.popListener(handler2);
|
errors.popListener(handler2);
|
||||||
|
|
||||||
const error = new Error('nope');
|
const error = new Error('nope');
|
||||||
dispatchErrorEvent(fakeGlobal, { error });
|
dispatchEvent(globals.listeners, 'error', { error });
|
||||||
|
|
||||||
expect(handler1).toHaveBeenCalledWith(
|
expect(handler1).toHaveBeenCalledWith(
|
||||||
jasmine.is(error),
|
jasmine.is(error),
|
||||||
@@ -85,26 +85,26 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uninstalls itself', function() {
|
it('uninstalls itself', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
function unrelatedListener() {}
|
function unrelatedListener() {}
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
fakeGlobal.addEventListener('error', unrelatedListener);
|
globals.global.addEventListener('error', unrelatedListener);
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.listeners_.error).toEqual([unrelatedListener]);
|
expect(globals.listeners.error).toEqual([unrelatedListener]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rethrows the original error when there is no handler', function() {
|
it('rethrows the original error when there is no handler', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
const originalError = new Error('nope');
|
const originalError = new Error('nope');
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dispatchErrorEvent(fakeGlobal, { error: originalError });
|
dispatchEvent(globals.listeners, 'error', { error: originalError });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e).toBe(originalError);
|
expect(e).toBe(originalError);
|
||||||
}
|
}
|
||||||
@@ -113,191 +113,129 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports uncaught exceptions in node.js', function() {
|
it('reports uncaught exceptions in node.js', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
on: jasmine.createSpy('process.on'),
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
removeListener: jasmine.createSpy('process.removeListener'),
|
function originalHandler() {}
|
||||||
listeners: jasmine
|
globals.listeners.uncaughtException = [originalHandler];
|
||||||
.createSpy('process.listeners')
|
|
||||||
.and.returnValue(['foo']),
|
|
||||||
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
expect(globals.listeners.uncaughtException).toEqual([
|
||||||
'uncaughtException',
|
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
|
expect(globals.listeners.uncaughtException).not.toEqual([
|
||||||
'uncaughtException'
|
originalHandler()
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
|
|
||||||
'uncaughtException'
|
|
||||||
);
|
|
||||||
|
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(0)[1];
|
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
|
||||||
addedListener(new Error('bar'));
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(new Error('bar'));
|
expect(handler).toHaveBeenCalledWith(new Error('bar'), undefined);
|
||||||
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||||
'Uncaught exception: Error: bar'
|
'Uncaught exception: Error: bar'
|
||||||
);
|
);
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
|
expect(globals.listeners.uncaughtException).toEqual([originalHandler]);
|
||||||
'uncaughtException',
|
|
||||||
addedListener
|
|
||||||
);
|
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
|
||||||
'uncaughtException',
|
|
||||||
'foo'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reporting unhandled promise rejections in node.js', function() {
|
describe('Reporting unhandled promise rejections in node.js', function() {
|
||||||
it('reports rejections with `Error` reasons', function() {
|
it('reports rejections with `Error` reasons', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
on: jasmine.createSpy('process.on'),
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
removeListener: jasmine.createSpy('process.removeListener'),
|
function originalHandler() {}
|
||||||
listeners: jasmine
|
globals.listeners.unhandledRejection = [originalHandler];
|
||||||
.createSpy('process.listeners')
|
|
||||||
.and.returnValue(['foo']),
|
|
||||||
removeAllListeners: jasmine.createSpy('process.removeAllListeners')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
expect(globals.listeners.unhandledRejection).toEqual([
|
||||||
'unhandledRejection',
|
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.listeners).toHaveBeenCalledWith(
|
expect(globals.listeners.unhandledRejection).not.toEqual([
|
||||||
'unhandledRejection'
|
originalHandler()
|
||||||
);
|
]);
|
||||||
expect(fakeGlobal.process.removeAllListeners).toHaveBeenCalledWith(
|
|
||||||
'unhandledRejection'
|
|
||||||
);
|
|
||||||
|
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('bar'));
|
||||||
addedListener(new Error('bar'));
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(new Error('bar'));
|
expect(handler).toHaveBeenCalledWith(new Error('bar'), undefined);
|
||||||
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
expect(handler.calls.argsFor(0)[0].jasmineMessage).toBe(
|
||||||
'Unhandled promise rejection: Error: bar'
|
'Unhandled promise rejection: Error: bar'
|
||||||
);
|
);
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.process.removeListener).toHaveBeenCalledWith(
|
expect(globals.listeners.unhandledRejection).toEqual([originalHandler]);
|
||||||
'unhandledRejection',
|
|
||||||
addedListener
|
|
||||||
);
|
|
||||||
expect(fakeGlobal.process.on).toHaveBeenCalledWith(
|
|
||||||
'unhandledRejection',
|
|
||||||
'foo'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections with non-`Error` reasons', function() {
|
it('reports rejections with non-`Error` reasons', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
on: jasmine.createSpy('process.on'),
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
removeListener: function() {},
|
|
||||||
listeners: function() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
dispatchEvent(globals.listeners, 'unhandledRejection', 17);
|
||||||
'unhandledRejection'
|
|
||||||
);
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
|
||||||
addedListener(17);
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
new Error(
|
new Error(
|
||||||
'Unhandled promise rejection: 17\n' +
|
'Unhandled promise rejection: 17\n' +
|
||||||
'(Tip: to get a useful stack trace, use ' +
|
'(Tip: to get a useful stack trace, use ' +
|
||||||
'Promise.reject(new Error(...)) instead of Promise.reject(...).)'
|
'Promise.reject(new Error(...)) instead of Promise.reject(...).)'
|
||||||
)
|
),
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections with no reason provided', function() {
|
it('reports rejections with no reason provided', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
on: jasmine.createSpy('process.on'),
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
removeListener: function() {},
|
|
||||||
listeners: function() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
expect(fakeGlobal.process.on.calls.argsFor(1)[0]).toEqual(
|
dispatchEvent(globals.listeners, 'unhandledRejection', undefined);
|
||||||
'unhandledRejection'
|
|
||||||
);
|
|
||||||
const addedListener = fakeGlobal.process.on.calls.argsFor(1)[1];
|
|
||||||
addedListener(undefined);
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
new Error(
|
new Error(
|
||||||
'Unhandled promise rejection with no error or message\n' +
|
'Unhandled promise rejection with no error or message\n' +
|
||||||
'(Tip: to get a useful stack trace, use ' +
|
'(Tip: to get a useful stack trace, use ' +
|
||||||
'Promise.reject(new Error(...)) instead of Promise.reject().)'
|
'Promise.reject(new Error(...)) instead of Promise.reject().)'
|
||||||
)
|
),
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reporting unhandled promise rejections in the browser', function() {
|
describe('Reporting unhandled promise rejections in the browser', function() {
|
||||||
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
it('subscribes and unsubscribes from the unhandledrejection event', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
|
expect(globals.listeners.unhandledrejection).toEqual([
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
|
expect(globals.listeners.unhandledrejection).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is a string', function() {
|
it('reports rejections whose reason is a string', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const event = { reason: 'nope' };
|
const event = { reason: 'nope' };
|
||||||
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
dispatchEvent(globals.listeners, 'unhandledrejection', event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
'Unhandled promise rejection: nope',
|
'Unhandled promise rejection: nope',
|
||||||
@@ -306,16 +244,16 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is an Error', function() {
|
it('reports rejections whose reason is an Error', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const reason = new Error('bar');
|
const reason = new Error('bar');
|
||||||
const event = { reason };
|
const event = { reason };
|
||||||
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
dispatchEvent(globals.listeners, 'unhandledrejection', event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -330,73 +268,51 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
describe('Reporting uncaught exceptions in node.js', function() {
|
describe('Reporting uncaught exceptions in node.js', function() {
|
||||||
it('prepends a descriptive message when the error is not an `Error`', function() {
|
it('prepends a descriptive message when the error is not an `Error`', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
on: jasmine.createSpy('process.on'),
|
|
||||||
removeListener: function() {},
|
|
||||||
listeners: function() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
uncaughtExceptionListener(fakeGlobal)(17);
|
dispatchEvent(globals.listeners, 'uncaughtException', 17);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(new Error('Uncaught exception: 17'));
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
new Error('Uncaught exception: 17'),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('substitutes a descriptive message when the error is falsy', function() {
|
it('substitutes a descriptive message when the error is falsy', function() {
|
||||||
const fakeGlobal = {
|
const globals = nodeGlobals();
|
||||||
process: {
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
on: jasmine.createSpy('process.on'),
|
|
||||||
removeListener: function() {},
|
|
||||||
listeners: function() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
removeAllListeners: function() {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
uncaughtExceptionListener(fakeGlobal)();
|
dispatchEvent(globals.listeners, 'uncaughtException', undefined);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
new Error('Uncaught exception with no error or message')
|
new Error('Uncaught exception with no error or message'),
|
||||||
|
undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function uncaughtExceptionListener(global) {
|
|
||||||
// Grab the right listener
|
|
||||||
expect(global.process.on.calls.argsFor(0)[0]).toEqual(
|
|
||||||
'uncaughtException'
|
|
||||||
);
|
|
||||||
return global.process.on.calls.argsFor(0)[1];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#setOverrideListener', function() {
|
describe('#setOverrideListener', function() {
|
||||||
it('overrides the existing handlers in browsers until removed', function() {
|
it('overrides the existing handlers in browsers until removed', function() {
|
||||||
const fakeGlobal = browserGlobal();
|
const globals = browserGlobals();
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler, () => {});
|
errors.setOverrideListener(overrideHandler, () => {});
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
|
dispatchEvent(globals.listeners, 'error', { error: 'foo' });
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
@@ -405,55 +321,42 @@ describe('GlobalErrors', function() {
|
|||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
|
|
||||||
const event = { error: 'baz' };
|
const event = { error: 'baz' };
|
||||||
dispatchErrorEvent(fakeGlobal, event);
|
dispatchEvent(globals.listeners, 'error', event);
|
||||||
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
||||||
expect(handler1).toHaveBeenCalledWith('baz', event);
|
expect(handler1).toHaveBeenCalledWith('baz', event);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('overrides the existing handlers in Node until removed', function() {
|
it('overrides the existing handlers in Node until removed', function() {
|
||||||
const globalEventListeners = {};
|
const globals = nodeGlobals();
|
||||||
const fakeGlobal = {
|
|
||||||
process: {
|
|
||||||
on: (name, listener) => (globalEventListeners[name] = listener),
|
|
||||||
removeListener: () => {},
|
|
||||||
listeners: name => globalEventListeners[name],
|
|
||||||
removeAllListeners: name => (globalEventListeners[name] = [])
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler);
|
errors.setOverrideListener(overrideHandler);
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
|
|
||||||
globalEventListeners['uncaughtException'](new Error('foo'));
|
dispatchEvent(globals.listeners, 'uncaughtException', new Error('foo'));
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('foo'));
|
expect(overrideHandler).toHaveBeenCalledWith(new Error('foo'));
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
overrideHandler.calls.reset();
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
|
|
||||||
globalEventListeners['uncaughtException'](new Error('bar'));
|
dispatchEvent(globals.listeners, 'uncaughtException', new Error('bar'));
|
||||||
expect(overrideHandler).not.toHaveBeenCalledWith(new Error('bar'));
|
expect(overrideHandler).not.toHaveBeenCalled();
|
||||||
expect(handler1).toHaveBeenCalledWith(new Error('bar'));
|
expect(handler1).toHaveBeenCalledWith(new Error('bar'), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled promise rejections in browsers', function() {
|
it('handles unhandled promise rejections in browsers', function() {
|
||||||
const globalEventListeners = {};
|
const globals = browserGlobals();
|
||||||
const fakeGlobal = {
|
|
||||||
addEventListener(name, listener) {
|
|
||||||
globalEventListeners[name] = listener;
|
|
||||||
},
|
|
||||||
removeEventListener() {}
|
|
||||||
};
|
|
||||||
const handler = jasmine.createSpy('handler');
|
const handler = jasmine.createSpy('handler');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
@@ -461,7 +364,7 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
const reason = new Error('bar');
|
const reason = new Error('bar');
|
||||||
|
|
||||||
globalEventListeners['unhandledrejection']({ reason: reason });
|
dispatchEvent(globals.listeners, 'unhandledrejection', { reason });
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(
|
expect(overrideHandler).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
@@ -474,32 +377,18 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled promise rejections in Node', function() {
|
it('handles unhandled promise rejections in Node', function() {
|
||||||
const globalEventListeners = {};
|
const globals = nodeGlobals();
|
||||||
const fakeGlobal = {
|
|
||||||
process: {
|
|
||||||
on(name, listener) {
|
|
||||||
globalEventListeners[name] = listener;
|
|
||||||
},
|
|
||||||
removeListener() {},
|
|
||||||
listeners(name) {
|
|
||||||
return globalEventListeners[name];
|
|
||||||
},
|
|
||||||
removeAllListeners(name) {
|
|
||||||
globalEventListeners[name] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const handler0 = jasmine.createSpy('handler0');
|
const handler0 = jasmine.createSpy('handler0');
|
||||||
const handler1 = jasmine.createSpy('handler1');
|
const handler1 = jasmine.createSpy('handler1');
|
||||||
const overrideHandler = jasmine.createSpy('overrideHandler');
|
const overrideHandler = jasmine.createSpy('overrideHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(globals.global);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler, () => {});
|
errors.setOverrideListener(overrideHandler, () => {});
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
|
|
||||||
globalEventListeners['unhandledRejection'](new Error('nope'));
|
dispatchEvent(globals.listeners, 'unhandledRejection', new Error('nope'));
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('nope'));
|
expect(overrideHandler).toHaveBeenCalledWith(new Error('nope'));
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
@@ -507,7 +396,7 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws if there is already an override handler', function() {
|
it('throws if there is already an override handler', function() {
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, () => {});
|
errors.setOverrideListener(() => {}, () => {});
|
||||||
expect(function() {
|
expect(function() {
|
||||||
@@ -519,7 +408,7 @@ describe('GlobalErrors', function() {
|
|||||||
describe('#removeOverrideListener', function() {
|
describe('#removeOverrideListener', function() {
|
||||||
it("calls the handler's onRemove callback", function() {
|
it("calls the handler's onRemove callback", function() {
|
||||||
const onRemove = jasmine.createSpy('onRemove');
|
const onRemove = jasmine.createSpy('onRemove');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, onRemove);
|
errors.setOverrideListener(() => {}, onRemove);
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
@@ -528,42 +417,60 @@ describe('GlobalErrors', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw if there is no handler', function() {
|
it('does not throw if there is no handler', function() {
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobals().global);
|
||||||
|
|
||||||
expect(() => errors.removeOverrideListener()).not.toThrow();
|
expect(() => errors.removeOverrideListener()).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function browserGlobal() {
|
function browserGlobals() {
|
||||||
|
const listeners = { error: [], unhandledrejection: [] };
|
||||||
return {
|
return {
|
||||||
listeners_: { error: [], unhandledrejection: [] },
|
listeners,
|
||||||
addEventListener(eventName, listener) {
|
global: {
|
||||||
this.listeners_[eventName].push(listener);
|
addEventListener(eventName, listener) {
|
||||||
},
|
listeners[eventName].push(listener);
|
||||||
removeEventListener(eventName, listener) {
|
},
|
||||||
this.listeners_[eventName] = this.listeners_[eventName].filter(
|
removeEventListener(eventName, listener) {
|
||||||
l => l !== listener
|
listeners[eventName] = listeners[eventName].filter(
|
||||||
);
|
l => l !== listener
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatchErrorEvent(global, event) {
|
function nodeGlobals() {
|
||||||
expect(global.listeners_.error.length)
|
const listeners = { uncaughtException: [], unhandledRejection: [] };
|
||||||
.withContext('number of error listeners')
|
return {
|
||||||
.toBeGreaterThan(0);
|
listeners,
|
||||||
|
global: {
|
||||||
for (const l of global.listeners_.error) {
|
process: {
|
||||||
l(event);
|
on(eventName, listener) {
|
||||||
}
|
listeners[eventName].push(listener);
|
||||||
|
},
|
||||||
|
removeListener(eventName, listener) {
|
||||||
|
listeners[eventName] = listeners[eventName].filter(
|
||||||
|
l => l !== listener
|
||||||
|
);
|
||||||
|
},
|
||||||
|
removeAllListeners(eventName) {
|
||||||
|
listeners[eventName] = [];
|
||||||
|
},
|
||||||
|
listeners(eventName) {
|
||||||
|
return listeners[eventName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatchUnhandledRejectionEvent(global, event) {
|
function dispatchEvent(listeners, eventName, event) {
|
||||||
expect(global.listeners_.unhandledrejection.length)
|
expect(listeners[eventName].length)
|
||||||
.withContext('number of unhandledrejection listeners')
|
.withContext(`number of ${eventName} listeners`)
|
||||||
.toBeGreaterThan(0);
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
for (const l of global.listeners_.unhandledrejection) {
|
for (const l of listeners[eventName]) {
|
||||||
l(event);
|
l(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ describe('PrettyPrinter', function() {
|
|||||||
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
|
"Object({ foo: 'bar', baz: 3, nullValue: null, undefinedValue: undefined })"
|
||||||
);
|
);
|
||||||
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
|
expect(pp({ foo: function() {}, bar: [1, 2, 3] })).toEqual(
|
||||||
'Object({ foo: Function, bar: [ 1, 2, 3 ] })'
|
"Object({ foo: Function 'foo', bar: [ 1, 2, 3 ] })"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -450,7 +450,7 @@ describe('PrettyPrinter', function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(pp(objFromOtherContext)).toEqual(
|
expect(pp(objFromOtherContext)).toEqual(
|
||||||
"Object({ foo: 'bar', toString: Function })"
|
"Object({ foo: 'bar', toString: Function 'toString' })"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -477,6 +477,17 @@ describe('PrettyPrinter', function() {
|
|||||||
expect(pp(a)).toEqual('<anonymous>({ })');
|
expect(pp(a)).toEqual('<anonymous>({ })');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('stringifies functions with names', function() {
|
||||||
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
|
expect(pp(foo)).toEqual("Function 'foo'");
|
||||||
|
function foo() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stringifies functions without names', function() {
|
||||||
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
|
expect(pp(function() {})).toEqual('Function');
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle objects with null prototype', function() {
|
it('should handle objects with null prototype', function() {
|
||||||
const pp = jasmineUnderTest.makePrettyPrinter();
|
const pp = jasmineUnderTest.makePrettyPrinter();
|
||||||
const obj = Object.create(null);
|
const obj = Object.create(null);
|
||||||
|
|||||||
@@ -128,17 +128,6 @@ describe('util', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isUndefined', function() {
|
|
||||||
it('reports if a variable is defined', function() {
|
|
||||||
let a;
|
|
||||||
expect(jasmineUnderTest.util.isUndefined(a)).toBe(true);
|
|
||||||
expect(jasmineUnderTest.util.isUndefined(undefined)).toBe(true);
|
|
||||||
|
|
||||||
const defined = 'diz be undefined yo';
|
|
||||||
expect(jasmineUnderTest.util.isUndefined(defined)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('cloneArgs', function() {
|
describe('cloneArgs', function() {
|
||||||
it('clones primitives as-is', function() {
|
it('clones primitives as-is', function() {
|
||||||
expect(jasmineUnderTest.util.cloneArgs([true, false])).toEqual([
|
expect(jasmineUnderTest.util.cloneArgs([true, false])).toEqual([
|
||||||
|
|||||||
@@ -429,350 +429,6 @@ describe('Env integration', function() {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Handling async errors', function() {
|
|
||||||
it('routes async errors to a running spec', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn, delay) {
|
|
||||||
clearTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
|
||||||
'specDone',
|
|
||||||
'suiteDone'
|
|
||||||
]);
|
|
||||||
|
|
||||||
env.addReporter(reporter);
|
|
||||||
|
|
||||||
env.describe('A suite', function() {
|
|
||||||
env.it('fails', function(specDone) {
|
|
||||||
setTimeout(function() {
|
|
||||||
dispatchErrorEvent(global, { error: 'fail' });
|
|
||||||
specDone();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
|
|
||||||
'A suite fails',
|
|
||||||
['fail thrown']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When the running spec has reported specDone', function() {
|
|
||||||
it('routes async errors to an ancestor suite', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn) {
|
|
||||||
clearTimeout(fn);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
|
|
||||||
const realClearStack = jasmineUnderTest.getClearStack(global);
|
|
||||||
const clearStackCallbacks = {};
|
|
||||||
let clearStackCallCount = 0;
|
|
||||||
spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
|
||||||
clearStackCallCount++;
|
|
||||||
|
|
||||||
if (clearStackCallbacks[clearStackCallCount]) {
|
|
||||||
clearStackCallbacks[clearStackCallCount]();
|
|
||||||
}
|
|
||||||
|
|
||||||
realClearStack(fn);
|
|
||||||
});
|
|
||||||
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
|
|
||||||
let suiteErrors = [];
|
|
||||||
env.addReporter({
|
|
||||||
suiteDone: function(result) {
|
|
||||||
const messages = result.failedExpectations.map(e => e.message);
|
|
||||||
suiteErrors = suiteErrors.concat(messages);
|
|
||||||
},
|
|
||||||
specDone: function() {
|
|
||||||
clearStackCallbacks[clearStackCallCount + 1] = function() {
|
|
||||||
dispatchErrorEvent(global, {
|
|
||||||
error: 'fail at the end of the reporter queue'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
clearStackCallbacks[clearStackCallCount + 2] = function() {
|
|
||||||
dispatchErrorEvent(global, {
|
|
||||||
error: 'fail at the end of the spec queue'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('A suite', function() {
|
|
||||||
env.it('is finishing when the failure occurs', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
expect(suiteErrors).toEqual([
|
|
||||||
'fail at the end of the reporter queue thrown',
|
|
||||||
'fail at the end of the spec queue thrown'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('routes async errors to a running suite', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn, delay) {
|
|
||||||
clearTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
const reporter = jasmine.createSpyObj('fakeReporter', [
|
|
||||||
'specDone',
|
|
||||||
'suiteDone'
|
|
||||||
]);
|
|
||||||
|
|
||||||
env.addReporter(reporter);
|
|
||||||
|
|
||||||
env.fdescribe('A suite', function() {
|
|
||||||
env.it('fails', function(specDone) {
|
|
||||||
setTimeout(function() {
|
|
||||||
specDone();
|
|
||||||
queueMicrotask(function() {
|
|
||||||
queueMicrotask(function() {
|
|
||||||
dispatchErrorEvent(global, { error: 'fail' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('Ignored', function() {
|
|
||||||
env.it('is not run', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
expect(reporter.specDone).not.toHaveFailedExpectationsForRunnable(
|
|
||||||
'A suite fails',
|
|
||||||
['fail thrown']
|
|
||||||
);
|
|
||||||
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
|
|
||||||
'A suite',
|
|
||||||
['fail thrown']
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When the running suite has reported suiteDone', function() {
|
|
||||||
it('routes async errors to an ancestor suite', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn, delay) {
|
|
||||||
clearTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
|
|
||||||
const realClearStack = jasmineUnderTest.getClearStack(global);
|
|
||||||
const clearStackCallbacks = {};
|
|
||||||
let clearStackCallCount = 0;
|
|
||||||
spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
|
||||||
clearStackCallCount++;
|
|
||||||
|
|
||||||
if (clearStackCallbacks[clearStackCallCount]) {
|
|
||||||
clearStackCallbacks[clearStackCallCount]();
|
|
||||||
}
|
|
||||||
|
|
||||||
realClearStack(fn);
|
|
||||||
});
|
|
||||||
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
|
|
||||||
let suiteErrors = [];
|
|
||||||
env.addReporter({
|
|
||||||
suiteDone: function(result) {
|
|
||||||
const messages = result.failedExpectations.map(e => e.message);
|
|
||||||
suiteErrors = suiteErrors.concat(messages);
|
|
||||||
|
|
||||||
if (result.description === 'A nested suite') {
|
|
||||||
clearStackCallbacks[clearStackCallCount + 1] = function() {
|
|
||||||
dispatchErrorEvent(global, {
|
|
||||||
error: 'fail at the end of the reporter queue'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
clearStackCallbacks[clearStackCallCount + 2] = function() {
|
|
||||||
dispatchErrorEvent(global, {
|
|
||||||
error: 'fail at the end of the suite queue'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('A suite', function() {
|
|
||||||
env.describe('A nested suite', function() {
|
|
||||||
env.it('a spec', function() {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
expect(suiteErrors).toEqual([
|
|
||||||
'fail at the end of the reporter queue thrown',
|
|
||||||
'fail at the end of the suite queue thrown'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('When the env has started reporting jasmineDone', function() {
|
|
||||||
it('logs the error to the console', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn, delay) {
|
|
||||||
clearTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
|
|
||||||
spyOn(console, 'error');
|
|
||||||
|
|
||||||
env.addReporter({
|
|
||||||
jasmineDone: function() {
|
|
||||||
dispatchErrorEvent(global, { error: 'a very late error' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('a spec', function() {});
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
/* eslint-disable-next-line no-console */
|
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
|
||||||
'Jasmine received a result after the suite finished:'
|
|
||||||
);
|
|
||||||
/* eslint-disable-next-line no-console */
|
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
message: 'a very late error thrown',
|
|
||||||
globalErrorType: 'afterAll'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('routes all errors that occur during stack clearing somewhere', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn) {
|
|
||||||
clearTimeout(fn);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
|
|
||||||
const realClearStack = jasmineUnderTest.getClearStack(global);
|
|
||||||
let clearStackCallCount = 0;
|
|
||||||
let jasmineDone = false;
|
|
||||||
const expectedErrors = [];
|
|
||||||
const expectedErrorsAfterJasmineDone = [];
|
|
||||||
spyOn(jasmineUnderTest, 'getClearStack').and.returnValue(function(fn) {
|
|
||||||
clearStackCallCount++;
|
|
||||||
const msg = `Error in clearStack #${clearStackCallCount}`;
|
|
||||||
|
|
||||||
if (jasmineDone) {
|
|
||||||
expectedErrorsAfterJasmineDone.push(`${msg} thrown`);
|
|
||||||
} else {
|
|
||||||
expectedErrors.push(`${msg} thrown`);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchErrorEvent(global, { error: msg });
|
|
||||||
realClearStack(fn);
|
|
||||||
});
|
|
||||||
spyOn(console, 'error');
|
|
||||||
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
|
|
||||||
const receivedErrors = [];
|
|
||||||
function logErrors(event) {
|
|
||||||
for (const failure of event.failedExpectations) {
|
|
||||||
receivedErrors.push(failure.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env.addReporter({
|
|
||||||
specDone: logErrors,
|
|
||||||
suiteDone: logErrors,
|
|
||||||
jasmineDone: function(event) {
|
|
||||||
jasmineDone = true;
|
|
||||||
logErrors(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('A suite', function() {
|
|
||||||
env.it('is finishing when the failure occurs', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
expect(receivedErrors.length).toEqual(expectedErrors.length);
|
|
||||||
|
|
||||||
for (const e of expectedErrors) {
|
|
||||||
expect(receivedErrors).toContain(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const message of expectedErrorsAfterJasmineDone) {
|
|
||||||
/* eslint-disable-next-line no-console */
|
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({ message })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('reports multiple calls to done in the top suite as errors', async function() {
|
it('reports multiple calls to done in the top suite as errors', async function() {
|
||||||
const reporter = jasmine.createSpyObj('fakeReporter', ['jasmineDone']);
|
const reporter = jasmine.createSpyObj('fakeReporter', ['jasmineDone']);
|
||||||
const message =
|
const message =
|
||||||
@@ -1524,7 +1180,7 @@ describe('Env integration', function() {
|
|||||||
env = new jasmineUnderTest.Env({
|
env = new jasmineUnderTest.Env({
|
||||||
global: {
|
global: {
|
||||||
setTimeout: function(cb, t) {
|
setTimeout: function(cb, t) {
|
||||||
const stack = jasmine.util.errorWithStack().stack;
|
const stack = new Error().stack;
|
||||||
if (stack.indexOf('ClearStack') >= 0) {
|
if (stack.indexOf('ClearStack') >= 0) {
|
||||||
return realSetTimeout(cb, t);
|
return realSetTimeout(cb, t);
|
||||||
} else {
|
} else {
|
||||||
@@ -2697,7 +2353,7 @@ describe('Env integration', function() {
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
throw new Error('suite');
|
throw new Error('suite');
|
||||||
}, 1);
|
}, 1);
|
||||||
}, 10);
|
}, 50);
|
||||||
|
|
||||||
env.it('spec', function() {});
|
env.it('spec', function() {});
|
||||||
});
|
});
|
||||||
@@ -2710,7 +2366,7 @@ describe('Env integration', function() {
|
|||||||
throw new Error('spec');
|
throw new Error('spec');
|
||||||
}, 1);
|
}, 1);
|
||||||
},
|
},
|
||||||
10
|
50
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2840,104 +2496,6 @@ describe('Env integration', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports errors that occur during loading', async function() {
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn, delay) {
|
|
||||||
clearTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
},
|
|
||||||
onerror: function() {}
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env();
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', [
|
|
||||||
'jasmineDone',
|
|
||||||
'suiteDone',
|
|
||||||
'specDone'
|
|
||||||
]);
|
|
||||||
|
|
||||||
env.addReporter(reporter);
|
|
||||||
dispatchErrorEvent(global, {
|
|
||||||
message: 'Uncaught SyntaxError: Unexpected end of input',
|
|
||||||
error: undefined,
|
|
||||||
filename: 'borkenSpec.js',
|
|
||||||
lineno: 42
|
|
||||||
});
|
|
||||||
const error = new Error('ENOCHEESE');
|
|
||||||
dispatchErrorEvent(global, { error });
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
const e = reporter.jasmineDone.calls.argsFor(0)[0];
|
|
||||||
expect(e.failedExpectations).toEqual([
|
|
||||||
{
|
|
||||||
passed: false,
|
|
||||||
globalErrorType: 'load',
|
|
||||||
message: 'Uncaught SyntaxError: Unexpected end of input',
|
|
||||||
stack: undefined,
|
|
||||||
filename: 'borkenSpec.js',
|
|
||||||
lineno: 42
|
|
||||||
},
|
|
||||||
{
|
|
||||||
passed: false,
|
|
||||||
globalErrorType: 'load',
|
|
||||||
message: 'ENOCHEESE',
|
|
||||||
stack: error.stack,
|
|
||||||
filename: undefined,
|
|
||||||
lineno: undefined
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('If suppressLoadErrors: true was passed', function() {
|
|
||||||
it('does not install a global error handler during loading', async function() {
|
|
||||||
const originalOnerror = jasmine.createSpy('original onerror');
|
|
||||||
const global = {
|
|
||||||
...browserEventMethods(),
|
|
||||||
setTimeout: function(fn, delay) {
|
|
||||||
return setTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
clearTimeout: function(fn, delay) {
|
|
||||||
clearTimeout(fn, delay);
|
|
||||||
},
|
|
||||||
queueMicrotask: function(fn) {
|
|
||||||
queueMicrotask(fn);
|
|
||||||
},
|
|
||||||
onerror: originalOnerror
|
|
||||||
};
|
|
||||||
spyOn(jasmineUnderTest, 'getGlobal').and.returnValue(global);
|
|
||||||
const globalErrors = new jasmineUnderTest.GlobalErrors(global);
|
|
||||||
const onerror = jasmine.createSpy('onerror');
|
|
||||||
globalErrors.pushListener(onerror);
|
|
||||||
spyOn(jasmineUnderTest, 'GlobalErrors').and.returnValue(globalErrors);
|
|
||||||
|
|
||||||
env.cleanup_();
|
|
||||||
env = new jasmineUnderTest.Env({ suppressLoadErrors: true });
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', [
|
|
||||||
'jasmineDone',
|
|
||||||
'suiteDone',
|
|
||||||
'specDone'
|
|
||||||
]);
|
|
||||||
|
|
||||||
env.addReporter(reporter);
|
|
||||||
global.onerror('Uncaught Error: ENOCHEESE');
|
|
||||||
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
const e = reporter.jasmineDone.calls.argsFor(0)[0];
|
|
||||||
expect(e.failedExpectations).toEqual([]);
|
|
||||||
expect(originalOnerror).toHaveBeenCalledWith('Uncaught Error: ENOCHEESE');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Overall status in the jasmineDone event', function() {
|
describe('Overall status in the jasmineDone event', function() {
|
||||||
describe('When everything passes', function() {
|
describe('When everything passes', function() {
|
||||||
it('is "passed"', async function() {
|
it('is "passed"', async function() {
|
||||||
@@ -3855,345 +3413,33 @@ describe('Env integration', function() {
|
|||||||
expect(failedExpectations).toEqual([]);
|
expect(failedExpectations).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#spyOnGlobalErrorsAsync', function() {
|
it('uses custom object formatters in spy strategy argument mismatch errors', async function() {
|
||||||
const leftInstalledMessage =
|
env.it('a spec', function() {
|
||||||
'Global error spy was not uninstalled. ' +
|
env.addCustomObjectFormatter(function(value) {
|
||||||
'(Did you forget to await the return value of spyOnGlobalErrorsAsync?)';
|
if (typeof value === 'string') {
|
||||||
|
return 'custom:' + value;
|
||||||
function resultForRunable(reporterSpy, fullName) {
|
|
||||||
const match = reporterSpy.calls.all().find(function(call) {
|
|
||||||
return call.args[0].fullName === fullName;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
throw new Error(`No result for runable "${fullName}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return match.args[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
it('allows global errors to be suppressed and spied on', async function() {
|
|
||||||
env.it('a passing spec', async function() {
|
|
||||||
await env.spyOnGlobalErrorsAsync(async spy => {
|
|
||||||
setTimeout(() => {
|
|
||||||
throw new Error('nope');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
env.expect(spy).toHaveBeenCalledWith(new Error('nope'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('a failing spec', async function() {
|
|
||||||
await env.spyOnGlobalErrorsAsync(async spy => {
|
|
||||||
setTimeout(() => {
|
|
||||||
throw new Error('yep');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
env.expect(spy).toHaveBeenCalledWith(new Error('nope'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
|
||||||
env.addReporter(reporter);
|
|
||||||
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
if (isBrowser) {
|
|
||||||
// Verify that there were no unexpected errors
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledTimes(2);
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('nope'));
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('yep'));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const spy = env
|
||||||
const passingResult = resultForRunable(
|
.createSpy('foo')
|
||||||
reporter.specDone,
|
.withArgs('x')
|
||||||
'a passing spec'
|
.and.returnValue('');
|
||||||
);
|
spy('y');
|
||||||
expect(passingResult.status).toEqual('passed');
|
|
||||||
expect(passingResult.failedExpectations).toEqual([]);
|
|
||||||
|
|
||||||
const failingResult = resultForRunable(
|
|
||||||
reporter.specDone,
|
|
||||||
'a failing spec'
|
|
||||||
);
|
|
||||||
expect(failingResult.status).toEqual('failed');
|
|
||||||
expect(failingResult.failedExpectations[0].message).toMatch(
|
|
||||||
/Expected \$\[0] = Error: yep to equal Error: nope\./
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cleans up if the global error spy is left installed in a beforeAll', async function() {
|
let failedExpectations;
|
||||||
env.configure({ random: false });
|
env.addReporter({
|
||||||
|
specDone: r => (failedExpectations = r.failedExpectations)
|
||||||
env.describe('Suite 1', function() {
|
|
||||||
env.beforeAll(async function() {
|
|
||||||
env.spyOnGlobalErrorsAsync(function() {
|
|
||||||
// Never resolves
|
|
||||||
return new Promise(() => {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('a spec', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('Suite 2', function() {
|
|
||||||
env.it('a spec', async function() {
|
|
||||||
setTimeout(function() {
|
|
||||||
throw new Error('should fail the spec');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', [
|
|
||||||
'specDone',
|
|
||||||
'suiteDone'
|
|
||||||
]);
|
|
||||||
env.addReporter(reporter);
|
|
||||||
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
if (isBrowser) {
|
|
||||||
// Verify that there were no unexpected errors
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(
|
|
||||||
new Error('should fail the spec')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const suiteResult = resultForRunable(reporter.suiteDone, 'Suite 1');
|
|
||||||
expect(suiteResult.status).toEqual('failed');
|
|
||||||
expect(suiteResult.failedExpectations.length).toEqual(1);
|
|
||||||
expect(suiteResult.failedExpectations[0].message).toEqual(
|
|
||||||
leftInstalledMessage
|
|
||||||
);
|
|
||||||
|
|
||||||
const specResult = resultForRunable(reporter.specDone, 'Suite 2 a spec');
|
|
||||||
expect(specResult.status).toEqual('failed');
|
|
||||||
expect(specResult.failedExpectations.length).toEqual(1);
|
|
||||||
expect(specResult.failedExpectations[0].message).toMatch(
|
|
||||||
/Error: should fail the spec/
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cleans up if the global error spy is left installed in an afterAll', async function() {
|
await env.execute();
|
||||||
env.configure({ random: false });
|
expect(failedExpectations).toEqual([
|
||||||
|
jasmine.objectContaining({
|
||||||
env.describe('Suite 1', function() {
|
message: jasmine.stringContaining(
|
||||||
env.afterAll(async function() {
|
'received a call with arguments [ custom:y ]'
|
||||||
env.spyOnGlobalErrorsAsync(function() {
|
)
|
||||||
// Never resolves
|
})
|
||||||
return new Promise(() => {});
|
]);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('a spec', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('Suite 2', function() {
|
|
||||||
env.it('a spec', async function() {
|
|
||||||
setTimeout(function() {
|
|
||||||
throw new Error('should fail the spec');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', [
|
|
||||||
'specDone',
|
|
||||||
'suiteDone'
|
|
||||||
]);
|
|
||||||
env.addReporter(reporter);
|
|
||||||
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
if (isBrowser) {
|
|
||||||
// Verify that there were no unexpected errors
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(
|
|
||||||
new Error('should fail the spec')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
|
|
||||||
'Suite 1',
|
|
||||||
[leftInstalledMessage]
|
|
||||||
);
|
|
||||||
|
|
||||||
const suiteResult = resultForRunable(reporter.suiteDone, 'Suite 1');
|
|
||||||
expect(suiteResult.status).toEqual('failed');
|
|
||||||
expect(suiteResult.failedExpectations.length).toEqual(1);
|
|
||||||
expect(suiteResult.failedExpectations[0].message).toEqual(
|
|
||||||
leftInstalledMessage
|
|
||||||
);
|
|
||||||
|
|
||||||
const specResult = resultForRunable(reporter.specDone, 'Suite 2 a spec');
|
|
||||||
expect(specResult.status).toEqual('failed');
|
|
||||||
expect(specResult.failedExpectations.length).toEqual(1);
|
|
||||||
expect(specResult.failedExpectations[0].message).toMatch(
|
|
||||||
/Error: should fail the spec/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('cleans up if the global error spy is left installed in a beforeEach', async function() {
|
|
||||||
env.configure({ random: false });
|
|
||||||
|
|
||||||
env.describe('Suite 1', function() {
|
|
||||||
env.beforeEach(async function() {
|
|
||||||
env.spyOnGlobalErrorsAsync(function() {
|
|
||||||
// Never resolves
|
|
||||||
return new Promise(() => {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('a spec', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('Suite 2', function() {
|
|
||||||
env.it('a spec', async function() {
|
|
||||||
setTimeout(function() {
|
|
||||||
throw new Error('should fail the spec');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', [
|
|
||||||
'specDone',
|
|
||||||
'suiteDone'
|
|
||||||
]);
|
|
||||||
env.addReporter(reporter);
|
|
||||||
|
|
||||||
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
if (isBrowser) {
|
|
||||||
// Verify that there were no unexpected errors
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(
|
|
||||||
new Error('should fail the spec')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec');
|
|
||||||
expect(spec1Result.status).toEqual('failed');
|
|
||||||
expect(spec1Result.failedExpectations.length).toEqual(1);
|
|
||||||
expect(spec1Result.failedExpectations[0].message).toEqual(
|
|
||||||
leftInstalledMessage
|
|
||||||
);
|
|
||||||
|
|
||||||
const spec2Result = resultForRunable(reporter.specDone, 'Suite 2 a spec');
|
|
||||||
expect(spec2Result.status).toEqual('failed');
|
|
||||||
expect(spec2Result.failedExpectations.length).toEqual(1);
|
|
||||||
expect(spec2Result.failedExpectations[0].message).toMatch(
|
|
||||||
/Error: should fail the spec/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('cleans up if the global error spy is left installed in an it', async function() {
|
|
||||||
env.configure({ random: false });
|
|
||||||
|
|
||||||
env.it('spec 1', async function() {
|
|
||||||
env.spyOnGlobalErrorsAsync(function() {
|
|
||||||
// Never resolves
|
|
||||||
return new Promise(() => {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('spec 2', async function() {
|
|
||||||
setTimeout(function() {
|
|
||||||
throw new Error('should fail the spec');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
});
|
|
||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
|
||||||
env.addReporter(reporter);
|
|
||||||
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
if (isBrowser) {
|
|
||||||
// Verify that there were no unexpected errors
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(
|
|
||||||
new Error('should fail the spec')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const spec1Result = resultForRunable(reporter.specDone, 'spec 1');
|
|
||||||
expect(spec1Result.status).toEqual('failed');
|
|
||||||
expect(spec1Result.failedExpectations.length).toEqual(1);
|
|
||||||
expect(spec1Result.failedExpectations[0].message).toEqual(
|
|
||||||
leftInstalledMessage
|
|
||||||
);
|
|
||||||
|
|
||||||
const spec2Result = resultForRunable(reporter.specDone, 'spec 2');
|
|
||||||
expect(spec2Result.status).toEqual('failed');
|
|
||||||
expect(spec2Result.failedExpectations.length).toEqual(1);
|
|
||||||
expect(spec2Result.failedExpectations[0].message).toMatch(
|
|
||||||
/Error: should fail the spec/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('cleans up if the global error spy is left installed in an afterEach', async function() {
|
|
||||||
env.configure({ random: false });
|
|
||||||
|
|
||||||
env.describe('Suite 1', function() {
|
|
||||||
env.afterEach(async function() {
|
|
||||||
env.spyOnGlobalErrorsAsync(function() {
|
|
||||||
// Never resolves
|
|
||||||
return new Promise(() => {});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.it('a spec', function() {});
|
|
||||||
});
|
|
||||||
|
|
||||||
env.describe('Suite 2', function() {
|
|
||||||
env.it('a spec', async function() {
|
|
||||||
setTimeout(function() {
|
|
||||||
throw new Error('should fail the spec');
|
|
||||||
});
|
|
||||||
await new Promise(resolve => setTimeout(resolve));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', [
|
|
||||||
'specDone',
|
|
||||||
'suiteDone'
|
|
||||||
]);
|
|
||||||
env.addReporter(reporter);
|
|
||||||
await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) {
|
|
||||||
await env.execute();
|
|
||||||
|
|
||||||
if (isBrowser) {
|
|
||||||
// Verify that there were no unexpected errors
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(globalErrorSpy).toHaveBeenCalledWith(
|
|
||||||
new Error('should fail the spec')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec');
|
|
||||||
expect(spec1Result.status).toEqual('failed');
|
|
||||||
expect(spec1Result.failedExpectations.length).toEqual(1);
|
|
||||||
expect(spec1Result.failedExpectations[0].message).toEqual(
|
|
||||||
leftInstalledMessage
|
|
||||||
);
|
|
||||||
|
|
||||||
const spec2Result = resultForRunable(reporter.specDone, 'Suite 2 a spec');
|
|
||||||
expect(spec2Result.status).toEqual('failed');
|
|
||||||
expect(spec2Result.failedExpectations.length).toEqual(1);
|
|
||||||
expect(spec2Result.failedExpectations[0].message).toMatch(
|
|
||||||
/Error: should fail the spec/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports a suite level error when a describe fn throws', async function() {
|
it('reports a suite level error when a describe fn throws', async function() {
|
||||||
|
|||||||
1171
spec/core/integration/GlobalErrorHandlingSpec.js
Normal file
1171
spec/core/integration/GlobalErrorHandlingSpec.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -161,6 +161,63 @@ describe('DiffBuilder', function() {
|
|||||||
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
|
expect(diffBuilder.getMessage()).toEqual(expectedMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('handles cases where only the expected has a custom object formatter', function() {
|
||||||
|
const formatter = function(x) {
|
||||||
|
if (typeof x === 'number') {
|
||||||
|
return '[number:' + x + ']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||||
|
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
prettyPrinter: prettyPrinter
|
||||||
|
});
|
||||||
|
|
||||||
|
diffBuilder.setRoots('five', 4);
|
||||||
|
diffBuilder.recordMismatch();
|
||||||
|
|
||||||
|
expect(diffBuilder.getMessage()).toEqual(
|
||||||
|
"Expected 'five' to equal [number:4]."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles cases where only the actual has a custom object formatter', function() {
|
||||||
|
const formatter = function(x) {
|
||||||
|
if (typeof x === 'number') {
|
||||||
|
return '[number:' + x + ']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||||
|
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
prettyPrinter: prettyPrinter
|
||||||
|
});
|
||||||
|
|
||||||
|
diffBuilder.setRoots(5, 'four');
|
||||||
|
diffBuilder.recordMismatch();
|
||||||
|
|
||||||
|
expect(diffBuilder.getMessage()).toEqual(
|
||||||
|
"Expected [number:5] to equal 'four'."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles complex cases where only one side has a custom object formatter', function() {
|
||||||
|
const formatter = function(x) {
|
||||||
|
if (typeof x === 'number') {
|
||||||
|
return '[number:' + x + ']';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([formatter]);
|
||||||
|
const diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
prettyPrinter: prettyPrinter
|
||||||
|
});
|
||||||
|
|
||||||
|
diffBuilder.setRoots(5, { foo: 'bar', fnord: { graults: ['wombat'] } });
|
||||||
|
diffBuilder.recordMismatch();
|
||||||
|
|
||||||
|
expect(diffBuilder.getMessage()).toEqual(
|
||||||
|
"Expected [number:5] to equal Object({ foo: 'bar', fnord: Object({ graults: [ 'wombat' ] }) })."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() {
|
it('builds diffs involving asymmetric equality testers that implement valuesForDiff_ at the root', function() {
|
||||||
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]),
|
const prettyPrinter = jasmineUnderTest.makePrettyPrinter([]),
|
||||||
diffBuilder = new jasmineUnderTest.DiffBuilder({
|
diffBuilder = new jasmineUnderTest.DiffBuilder({
|
||||||
|
|||||||
@@ -458,9 +458,9 @@ describe('toEqual', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reports mismatches between Functions', function() {
|
it('reports mismatches between Functions', function() {
|
||||||
const actual = { x: function() {} },
|
const actual = { x: function() {} };
|
||||||
expected = { x: function() {} },
|
const expected = { x: function() {} };
|
||||||
message = 'Expected $.x = Function to equal Function.';
|
const message = "Expected $.x = Function 'x' to equal Function 'x'.";
|
||||||
|
|
||||||
expect(compareEquals(actual, expected).message).toEqual(message);
|
expect(compareEquals(actual, expected).message).toEqual(message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const os = require('node:os');
|
const os = require('node:os');
|
||||||
const { rimrafSync } = require('rimraf');
|
const child_process = require('node:child_process');
|
||||||
|
|
||||||
describe('npm package', function() {
|
describe('npm package', function() {
|
||||||
beforeAll(function() {
|
beforeAll(function() {
|
||||||
const shell = require('shelljs'),
|
const packOutput = child_process.execSync('npm pack', {
|
||||||
pack = shell.exec('npm pack', { silent: true });
|
encoding: 'utf8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe']
|
||||||
this.tarball = pack.stdout.split('\n')[0];
|
});
|
||||||
|
this.tarball = packOutput.split('\n')[0];
|
||||||
const prefix = path.join(os.tmpdir(), 'jasmine-npm-package');
|
const prefix = path.join(os.tmpdir(), 'jasmine-npm-package');
|
||||||
this.tmpDir = fs.mkdtempSync(prefix);
|
this.tmpDir = fs.mkdtempSync(prefix);
|
||||||
|
|
||||||
const untar = shell.exec(
|
child_process.execSync(`tar -xzf ${this.tarball} -C ${this.tmpDir}`, {
|
||||||
'tar -xzf ' + this.tarball + ' -C ' + this.tmpDir,
|
encoding: 'utf8'
|
||||||
{
|
});
|
||||||
silent: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(untar.code).toBe(0);
|
|
||||||
|
|
||||||
this.packagedCore = require(path.join(
|
this.packagedCore = require(path.join(
|
||||||
this.tmpDir,
|
this.tmpDir,
|
||||||
@@ -43,7 +40,7 @@ describe('npm package', function() {
|
|||||||
|
|
||||||
afterAll(function() {
|
afterAll(function() {
|
||||||
fs.unlinkSync(this.tarball);
|
fs.unlinkSync(this.tarball);
|
||||||
rimrafSync(this.tmpDir);
|
fs.rmSync(this.tmpDir, { recursive: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a root path', function() {
|
it('has a root path', function() {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
|
|
||||||
this.track = function(context) {
|
this.track = function(context) {
|
||||||
if (opts.cloneArgs) {
|
if (opts.cloneArgs) {
|
||||||
context.args = j$.util.cloneArgs(context.args);
|
context.args = opts.argsCloner(context.args);
|
||||||
}
|
}
|
||||||
calls.push(context);
|
calls.push(context);
|
||||||
};
|
};
|
||||||
@@ -117,13 +117,15 @@ getJasmineRequireObj().CallTracker = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this spy to do a shallow clone of arguments passed to each invocation.
|
* Set this spy to do a clone of arguments passed to each invocation.
|
||||||
* @name Spy#calls#saveArgumentsByValue
|
* @name Spy#calls#saveArgumentsByValue
|
||||||
* @since 2.5.0
|
* @since 2.5.0
|
||||||
|
* @param {Function} [argsCloner] A function to use to clone the arguments. Defaults to a shallow cloning function.
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.saveArgumentsByValue = function() {
|
this.saveArgumentsByValue = function(argsCloner = j$.util.cloneArgs) {
|
||||||
opts.cloneArgs = true;
|
opts.cloneArgs = true;
|
||||||
|
opts.argsCloner = argsCloner;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.unverifiedCount = function() {
|
this.unverifiedCount = function() {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
|
|
||||||
function getUnclampedSetTimeout(global) {
|
function getUnclampedSetTimeout(global) {
|
||||||
const { setTimeout } = global;
|
const { setTimeout } = global;
|
||||||
if (j$.util.isUndefined(global.MessageChannel)) {
|
if (!global.MessageChannel) {
|
||||||
return setTimeout;
|
return setTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,10 +104,7 @@ getJasmineRequireObj().clearStack = function(j$) {
|
|||||||
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
// Unlike browsers, Node doesn't require us to do a periodic setTimeout
|
||||||
// so we avoid the overhead.
|
// so we avoid the overhead.
|
||||||
return nodeQueueMicrotaskImpl(global);
|
return nodeQueueMicrotaskImpl(global);
|
||||||
} else if (
|
} else if (SAFARI_OR_WIN_WEBKIT || !global.MessageChannel /* tests */) {
|
||||||
SAFARI_OR_WIN_WEBKIT ||
|
|
||||||
j$.util.isUndefined(global.MessageChannel) /* tests */
|
|
||||||
) {
|
|
||||||
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
// queueMicrotask is dramatically faster than MessageChannel in Safari
|
||||||
// and other WebKit-based browsers, such as the one distributed by Playwright
|
// and other WebKit-based browsers, such as the one distributed by Playwright
|
||||||
// to test Safari-like behavior on Windows.
|
// to test Safari-like behavior on Windows.
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ getJasmineRequireObj().Clock = function() {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
this.uninstall = function() {
|
this.uninstall = function() {
|
||||||
|
// Ensure auto ticking loop is aborted when clock is uninstalled
|
||||||
|
if (tickMode.mode === 'auto') {
|
||||||
|
tickMode = { mode: 'manual', counter: tickMode.counter + 1 };
|
||||||
|
}
|
||||||
delayedFunctionScheduler = null;
|
delayedFunctionScheduler = null;
|
||||||
mockDate.uninstall();
|
mockDate.uninstall();
|
||||||
replace(global, realTimingFunctions);
|
replace(global, realTimingFunctions);
|
||||||
@@ -222,6 +226,12 @@ callbacks to execute _before_ running the next one.
|
|||||||
//
|
//
|
||||||
// @return {!Promise<undefined>}
|
// @return {!Promise<undefined>}
|
||||||
async function newMacrotask() {
|
async function newMacrotask() {
|
||||||
|
if (NODE_JS) {
|
||||||
|
// setImmediate is generally faster than setTimeout in Node
|
||||||
|
// https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout
|
||||||
|
return new Promise(resolve => void setImmediate(resolve));
|
||||||
|
}
|
||||||
|
|
||||||
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
// MessageChannel ensures that setTimeout is not throttled to 4ms.
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
|
||||||
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
// https://stackblitz.com/edit/stackblitz-starters-qtlpcc
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
this.scheduledLookup_ = [];
|
this.scheduledLookup_ = [];
|
||||||
this.scheduledFunctions_ = {};
|
this.scheduledFunctions_ = {};
|
||||||
this.currentTime_ = 0;
|
this.currentTime_ = 0;
|
||||||
this.delayedFnCount_ = 0;
|
this.delayedFnStartCount_ = 1e12; // arbitrarily large number to avoid collisions with native timer IDs;
|
||||||
this.deletedKeys_ = [];
|
this.deletedKeys_ = [];
|
||||||
|
|
||||||
this.tick = function(millis, tickDate) {
|
this.tick = function(millis, tickDate) {
|
||||||
@@ -38,7 +38,7 @@ getJasmineRequireObj().DelayedFunctionScheduler = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
millis = millis || 0;
|
millis = millis || 0;
|
||||||
timeoutKey = timeoutKey || ++this.delayedFnCount_;
|
timeoutKey = timeoutKey || ++this.delayedFnStartCount_;
|
||||||
runAtMillis = runAtMillis || this.currentTime_ + millis;
|
runAtMillis = runAtMillis || this.currentTime_ + millis;
|
||||||
|
|
||||||
const funcToSchedule = {
|
const funcToSchedule = {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ getJasmineRequireObj().Deprecator = function(j$) {
|
|||||||
|
|
||||||
Deprecator.prototype.stackTrace_ = function() {
|
Deprecator.prototype.stackTrace_ = function() {
|
||||||
const formatter = new j$.ExceptionFormatter();
|
const formatter = new j$.ExceptionFormatter();
|
||||||
return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, '');
|
return formatter.stack(new Error()).replace(/^Error\n/m, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
Deprecator.prototype.report_ = function(runnable, deprecation, options) {
|
Deprecator.prototype.report_ = function(runnable, deprecation, options) {
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ getJasmineRequireObj().Expectation = function(j$) {
|
|||||||
return function() {
|
return function() {
|
||||||
// Capture the call stack here, before we go async, so that it will contain
|
// Capture the call stack here, before we go async, so that it will contain
|
||||||
// frames that are relevant to the user instead of just parts of Jasmine.
|
// frames that are relevant to the user instead of just parts of Jasmine.
|
||||||
const errorForStack = j$.util.errorWithStack();
|
const errorForStack = new Error();
|
||||||
|
|
||||||
return this.expector
|
return this.expector
|
||||||
.compare(name, matcherFactory, arguments)
|
.compare(name, matcherFactory, arguments)
|
||||||
|
|||||||
@@ -1,133 +1,36 @@
|
|||||||
getJasmineRequireObj().GlobalErrors = function(j$) {
|
getJasmineRequireObj().GlobalErrors = function(j$) {
|
||||||
function GlobalErrors(global) {
|
class GlobalErrors {
|
||||||
global = global || j$.getGlobal();
|
#adapter;
|
||||||
|
#handlers;
|
||||||
|
#overrideHandler;
|
||||||
|
#onRemoveOverrideHandler;
|
||||||
|
|
||||||
const handlers = [];
|
constructor(global) {
|
||||||
let overrideHandler = null,
|
global = global || j$.getGlobal();
|
||||||
onRemoveOverrideHandler = null;
|
const dispatchError = this.#dispatchError.bind(this);
|
||||||
|
|
||||||
function onBrowserError(event) {
|
|
||||||
dispatchBrowserError(event.error, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchBrowserError(error, event) {
|
|
||||||
if (overrideHandler) {
|
|
||||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
|
||||||
overrideHandler(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
handler(error, event);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.originalHandlers = {};
|
|
||||||
this.jasmineHandlers = {};
|
|
||||||
this.installOne_ = function installOne_(errorType, jasmineMessage) {
|
|
||||||
function taggedOnError(error) {
|
|
||||||
if (j$.isError_(error)) {
|
|
||||||
error.jasmineMessage = jasmineMessage + ': ' + error;
|
|
||||||
} else {
|
|
||||||
let substituteMsg;
|
|
||||||
|
|
||||||
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(' +
|
|
||||||
(error ? '...' : '') +
|
|
||||||
').)';
|
|
||||||
}
|
|
||||||
|
|
||||||
error = new Error(substituteMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
|
||||||
|
|
||||||
if (overrideHandler) {
|
|
||||||
// See discussion of spyOnGlobalErrorsAsync in base.js
|
|
||||||
overrideHandler(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
handler(error);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.originalHandlers[errorType] = global.process.listeners(errorType);
|
|
||||||
this.jasmineHandlers[errorType] = taggedOnError;
|
|
||||||
|
|
||||||
global.process.removeAllListeners(errorType);
|
|
||||||
global.process.on(errorType, taggedOnError);
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
const errorTypes = Object.keys(this.originalHandlers);
|
|
||||||
for (const errorType of errorTypes) {
|
|
||||||
global.process.removeListener(
|
|
||||||
errorType,
|
|
||||||
this.jasmineHandlers[errorType]
|
|
||||||
);
|
|
||||||
|
|
||||||
for (let i = 0; i < this.originalHandlers[errorType].length; i++) {
|
|
||||||
global.process.on(errorType, this.originalHandlers[errorType][i]);
|
|
||||||
}
|
|
||||||
delete this.originalHandlers[errorType];
|
|
||||||
delete this.jasmineHandlers[errorType];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
this.install = function install() {
|
|
||||||
if (
|
if (
|
||||||
global.process &&
|
global.process &&
|
||||||
global.process.listeners &&
|
global.process.listeners &&
|
||||||
j$.isFunction_(global.process.on)
|
j$.isFunction_(global.process.on)
|
||||||
) {
|
) {
|
||||||
this.installOne_('uncaughtException', 'Uncaught exception');
|
this.#adapter = new NodeAdapter(global, dispatchError);
|
||||||
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
|
||||||
} else {
|
} else {
|
||||||
global.addEventListener('error', onBrowserError);
|
this.#adapter = new BrowserAdapter(global, dispatchError);
|
||||||
|
|
||||||
const browserRejectionHandler = function browserRejectionHandler(
|
|
||||||
event
|
|
||||||
) {
|
|
||||||
if (j$.isError_(event.reason)) {
|
|
||||||
event.reason.jasmineMessage =
|
|
||||||
'Unhandled promise rejection: ' + event.reason;
|
|
||||||
dispatchBrowserError(event.reason, event);
|
|
||||||
} else {
|
|
||||||
dispatchBrowserError(
|
|
||||||
'Unhandled promise rejection: ' + event.reason,
|
|
||||||
event
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
|
||||||
global.removeEventListener('error', onBrowserError);
|
|
||||||
global.removeEventListener(
|
|
||||||
'unhandledrejection',
|
|
||||||
browserRejectionHandler
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
this.#handlers = [];
|
||||||
|
this.#overrideHandler = null;
|
||||||
|
this.#onRemoveOverrideHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#adapter.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
this.#adapter.uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
// The listener at the top of the stack will be called with two arguments:
|
// The listener at the top of the stack will be called with two arguments:
|
||||||
// the error and the event. Either of them may be falsy.
|
// the error and the event. Either of them may be falsy.
|
||||||
@@ -136,35 +39,183 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
// browsers but will be falsy in Node.
|
// browsers but will be falsy in Node.
|
||||||
// Listeners that are pushed after spec files have been loaded should be
|
// Listeners that are pushed after spec files have been loaded should be
|
||||||
// able to just use the error parameter.
|
// able to just use the error parameter.
|
||||||
this.pushListener = function pushListener(listener) {
|
pushListener(listener) {
|
||||||
handlers.push(listener);
|
this.#handlers.push(listener);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.popListener = function popListener(listener) {
|
popListener(listener) {
|
||||||
if (!listener) {
|
if (!listener) {
|
||||||
throw new Error('popListener expects a listener');
|
throw new Error('popListener expects a listener');
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.pop();
|
this.#handlers.pop();
|
||||||
};
|
}
|
||||||
|
|
||||||
this.setOverrideListener = function(listener, onRemove) {
|
setOverrideListener(listener, onRemove) {
|
||||||
if (overrideHandler) {
|
if (this.#overrideHandler) {
|
||||||
throw new Error("Can't set more than one override listener at a time");
|
throw new Error("Can't set more than one override listener at a time");
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideHandler = listener;
|
this.#overrideHandler = listener;
|
||||||
onRemoveOverrideHandler = onRemove;
|
this.#onRemoveOverrideHandler = onRemove;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.removeOverrideListener = function() {
|
removeOverrideListener() {
|
||||||
if (onRemoveOverrideHandler) {
|
if (this.#onRemoveOverrideHandler) {
|
||||||
onRemoveOverrideHandler();
|
this.#onRemoveOverrideHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideHandler = null;
|
this.#overrideHandler = null;
|
||||||
onRemoveOverrideHandler = null;
|
this.#onRemoveOverrideHandler = null;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Either error or event may be undefined
|
||||||
|
#dispatchError(error, event) {
|
||||||
|
if (this.#overrideHandler) {
|
||||||
|
// See discussion of spyOnGlobalErrorsAsync in base.js
|
||||||
|
this.#overrideHandler(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = this.#handlers[this.#handlers.length - 1];
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
handler(error, event);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserAdapter {
|
||||||
|
#global;
|
||||||
|
#dispatchError;
|
||||||
|
#onError;
|
||||||
|
#onUnhandledRejection;
|
||||||
|
|
||||||
|
constructor(global, dispatchError) {
|
||||||
|
this.#global = global;
|
||||||
|
this.#dispatchError = dispatchError;
|
||||||
|
this.#onError = event => this.#dispatchError(event.error, event);
|
||||||
|
this.#onUnhandledRejection = this.#unhandledRejectionHandler.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#global.addEventListener('error', this.#onError);
|
||||||
|
|
||||||
|
this.#global.addEventListener(
|
||||||
|
'unhandledrejection',
|
||||||
|
this.#onUnhandledRejection
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
this.#global.removeEventListener('error', this.#onError);
|
||||||
|
this.#global.removeEventListener(
|
||||||
|
'unhandledrejection',
|
||||||
|
this.#onUnhandledRejection
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#unhandledRejectionHandler(event) {
|
||||||
|
if (j$.isError_(event.reason)) {
|
||||||
|
event.reason.jasmineMessage =
|
||||||
|
'Unhandled promise rejection: ' + event.reason;
|
||||||
|
this.#dispatchError(event.reason, event);
|
||||||
|
} else {
|
||||||
|
this.#dispatchError(
|
||||||
|
'Unhandled promise rejection: ' + event.reason,
|
||||||
|
event
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NodeAdapter {
|
||||||
|
#global;
|
||||||
|
#dispatchError;
|
||||||
|
#originalHandlers;
|
||||||
|
#jasmineHandlers;
|
||||||
|
#onError;
|
||||||
|
#onUnhandledRejection;
|
||||||
|
|
||||||
|
constructor(global, dispatchError) {
|
||||||
|
this.#global = global;
|
||||||
|
this.#dispatchError = dispatchError;
|
||||||
|
|
||||||
|
this.#jasmineHandlers = {};
|
||||||
|
this.#originalHandlers = {};
|
||||||
|
|
||||||
|
this.#onError = error =>
|
||||||
|
this.#eventHandler(error, 'uncaughtException', 'Uncaught exception');
|
||||||
|
this.#onUnhandledRejection = error =>
|
||||||
|
this.#eventHandler(
|
||||||
|
error,
|
||||||
|
'unhandledRejection',
|
||||||
|
'Unhandled promise rejection'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
this.#installHandler('uncaughtException', this.#onError);
|
||||||
|
this.#installHandler('unhandledRejection', this.#onUnhandledRejection);
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall() {
|
||||||
|
const errorTypes = Object.keys(this.#originalHandlers);
|
||||||
|
for (const errorType of errorTypes) {
|
||||||
|
this.#global.process.removeListener(
|
||||||
|
errorType,
|
||||||
|
this.#jasmineHandlers[errorType]
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#originalHandlers[errorType].length; i++) {
|
||||||
|
this.#global.process.on(
|
||||||
|
errorType,
|
||||||
|
this.#originalHandlers[errorType][i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
delete this.#originalHandlers[errorType];
|
||||||
|
delete this.#jasmineHandlers[errorType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#installHandler(errorType, handler) {
|
||||||
|
this.#originalHandlers[errorType] = this.#global.process.listeners(
|
||||||
|
errorType
|
||||||
|
);
|
||||||
|
this.#jasmineHandlers[errorType] = handler;
|
||||||
|
|
||||||
|
this.#global.process.removeAllListeners(errorType);
|
||||||
|
this.#global.process.on(errorType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#eventHandler(error, errorType, jasmineMessage) {
|
||||||
|
if (j$.isError_(error)) {
|
||||||
|
error.jasmineMessage = jasmineMessage + ': ' + error;
|
||||||
|
} else {
|
||||||
|
let substituteMsg;
|
||||||
|
|
||||||
|
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(' +
|
||||||
|
(error ? '...' : '') +
|
||||||
|
').)';
|
||||||
|
}
|
||||||
|
|
||||||
|
error = new Error(substituteMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#dispatchError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GlobalErrors;
|
return GlobalErrors;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ getJasmineRequireObj().MockDate = function(j$) {
|
|||||||
if (mockDate instanceof GlobalDate) {
|
if (mockDate instanceof GlobalDate) {
|
||||||
currentTime = mockDate.getTime();
|
currentTime = mockDate.getTime();
|
||||||
} else {
|
} else {
|
||||||
if (!j$.util.isUndefined(mockDate)) {
|
if (mockDate !== undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'The argument to jasmine.clock().mockDate(), if specified, ' +
|
'The argument to jasmine.clock().mockDate(), if specified, ' +
|
||||||
'should be a Date instance.'
|
'should be a Date instance.'
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
|||||||
|
|
||||||
if (customFormatResult) {
|
if (customFormatResult) {
|
||||||
this.emitScalar(customFormatResult);
|
this.emitScalar(customFormatResult);
|
||||||
} else if (j$.util.isUndefined(value)) {
|
} else if (value === undefined) {
|
||||||
this.emitScalar('undefined');
|
this.emitScalar('undefined');
|
||||||
} else if (value === null) {
|
} else if (value === null) {
|
||||||
this.emitScalar('null');
|
this.emitScalar('null');
|
||||||
@@ -35,7 +35,11 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) {
|
|||||||
} else if (value instanceof RegExp) {
|
} else if (value instanceof RegExp) {
|
||||||
this.emitScalar(value.toString());
|
this.emitScalar(value.toString());
|
||||||
} else if (typeof value === 'function') {
|
} else if (typeof value === 'function') {
|
||||||
this.emitScalar('Function');
|
if (value.name) {
|
||||||
|
this.emitScalar(`Function '${value.name}'`);
|
||||||
|
} else {
|
||||||
|
this.emitScalar('Function');
|
||||||
|
}
|
||||||
} else if (j$.isDomNode(value)) {
|
} else if (j$.isDomNode(value)) {
|
||||||
if (value.tagName) {
|
if (value.tagName) {
|
||||||
this.emitDomElement(value);
|
this.emitDomElement(value);
|
||||||
|
|||||||
@@ -147,10 +147,12 @@ getJasmineRequireObj().Spec = function(j$) {
|
|||||||
* @property {String} description - The description passed to the {@link it} that created this spec.
|
* @property {String} description - The description passed to the {@link it} that created this spec.
|
||||||
* @property {String} fullName - The full description including all ancestors of this spec.
|
* @property {String} fullName - The full description including all ancestors of this spec.
|
||||||
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
* @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe().
|
||||||
* @property {String} filename - The name of the file the spec was defined in.
|
* @property {String} filename - Deprecated. The name of the file the spec was defined in.
|
||||||
* Note: The value may be incorrect if zone.js is installed or
|
* Note: The value may be incorrect if zone.js is installed or
|
||||||
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
|
* `it`/`fit`/`xit` have been replaced with versions that don't maintain the
|
||||||
* same call stack height as the originals.
|
* same call stack height as the originals. This property may be removed in
|
||||||
|
* a future version unless there is enough user interest in keeping it.
|
||||||
|
* See {@link https://github.com/jasmine/jasmine/issues/2065}.
|
||||||
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed during execution of this spec.
|
||||||
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
|
* @property {ExpectationResult[]} passedExpectations - The list of expectations that passed during execution of this spec.
|
||||||
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
|
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec.
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ getJasmineRequireObj().Spy = function(j$) {
|
|||||||
"Spy '" +
|
"Spy '" +
|
||||||
strategyArgs.name +
|
strategyArgs.name +
|
||||||
"' received a call with arguments " +
|
"' received a call with arguments " +
|
||||||
j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) +
|
matchersUtil.pp(Array.prototype.slice.call(args)) +
|
||||||
' but all configured strategies specify other arguments.'
|
' but all configured strategies specify other arguments.'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
this.spyOn = function(obj, methodName) {
|
this.spyOn = function(obj, methodName) {
|
||||||
const getErrorMsg = spyOnMsg;
|
const getErrorMsg = spyOnMsg;
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj) || obj === null) {
|
if (obj === undefined || obj === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
getErrorMsg(
|
getErrorMsg(
|
||||||
'could not find an object to spy upon for ' + methodName + '()'
|
'could not find an object to spy upon for ' + methodName + '()'
|
||||||
@@ -33,11 +33,11 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(methodName) || methodName === null) {
|
if (methodName === undefined || methodName === null) {
|
||||||
throw new Error(getErrorMsg('No method name supplied'));
|
throw new Error(getErrorMsg('No method name supplied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj[methodName])) {
|
if (obj[methodName] === undefined) {
|
||||||
throw new Error(getErrorMsg(methodName + '() method does not exist'));
|
throw new Error(getErrorMsg(methodName + '() method does not exist'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
|
|
||||||
accessType = accessType || 'get';
|
accessType = accessType || 'get';
|
||||||
|
|
||||||
if (j$.util.isUndefined(obj)) {
|
if (!obj) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
getErrorMsg(
|
getErrorMsg(
|
||||||
'spyOn could not find an object to spy upon for ' +
|
'spyOn could not find an object to spy upon for ' +
|
||||||
@@ -112,7 +112,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j$.util.isUndefined(propertyName)) {
|
if (propertyName === undefined) {
|
||||||
throw new Error(getErrorMsg('No property name supplied'));
|
throw new Error(getErrorMsg('No property name supplied'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
|
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
|
||||||
if (j$.util.isUndefined(obj)) {
|
if (!obj) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'spyOnAllFunctions could not find an object to spy upon'
|
'spyOnAllFunctions could not find an object to spy upon'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -104,10 +104,12 @@ getJasmineRequireObj().Suite = function(j$) {
|
|||||||
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
* @property {String} description - The description text passed to the {@link describe} that made this suite.
|
||||||
* @property {String} fullName - The full description including all ancestors of this suite.
|
* @property {String} fullName - The full description including all ancestors of this suite.
|
||||||
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
|
* @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe().
|
||||||
* @property {String} filename - The name of the file the suite was defined in.
|
* @property {String} filename - Deprecated. The name of the file the suite was defined in.
|
||||||
* Note: The value may be incorrect if zone.js is installed or
|
* Note: The value may be incorrect if zone.js is installed or
|
||||||
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
|
* `describe`/`fdescribe`/`xdescribe` have been replaced with versions that
|
||||||
* don't maintain the same call stack height as the originals.
|
* don't maintain the same call stack height as the originals. This property
|
||||||
|
* may be removed in a future version unless there is enough user interest
|
||||||
|
* in keeping it. See {@link https://github.com/jasmine/jasmine/issues/2065}.
|
||||||
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
* @property {ExpectationResult[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite.
|
||||||
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
|
* @property {ExpectationResult[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
|
||||||
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ getJasmineRequireObj().Anything = function(j$) {
|
|||||||
function Anything() {}
|
function Anything() {}
|
||||||
|
|
||||||
Anything.prototype.asymmetricMatch = function(other) {
|
Anything.prototype.asymmetricMatch = function(other) {
|
||||||
return !j$.util.isUndefined(other) && other !== null;
|
return other !== undefined && other !== null;
|
||||||
};
|
};
|
||||||
|
|
||||||
Anything.prototype.jasmineToString = function() {
|
Anything.prototype.jasmineToString = function() {
|
||||||
|
|||||||
@@ -73,9 +73,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
j$.isObject_ = function(value) {
|
j$.isObject_ = function(value) {
|
||||||
return (
|
return value !== undefined && value !== null && j$.isA_('Object', value);
|
||||||
!j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
j$.isString_ = function(value) {
|
j$.isString_ = function(value) {
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ getJasmineRequireObj().DiffBuilder = function(j$) {
|
|||||||
|
|
||||||
const actualCustom = this.prettyPrinter_.customFormat_(actual);
|
const actualCustom = this.prettyPrinter_.customFormat_(actual);
|
||||||
const expectedCustom = this.prettyPrinter_.customFormat_(expected);
|
const expectedCustom = this.prettyPrinter_.customFormat_(expected);
|
||||||
const useCustom = !(
|
const useCustom =
|
||||||
j$.util.isUndefined(actualCustom) &&
|
actualCustom !== undefined || expectedCustom !== undefined;
|
||||||
j$.util.isUndefined(expectedCustom)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (useCustom) {
|
if (useCustom) {
|
||||||
messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path));
|
const prettyActual = actualCustom || this.prettyPrinter_(actual);
|
||||||
|
const prettyExpected =
|
||||||
|
expectedCustom || this.prettyPrinter_(expected);
|
||||||
|
messages.push(wrapPrettyPrinted(prettyActual, prettyExpected, path));
|
||||||
return false; // don't recurse further
|
return false; // don't recurse further
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,13 +177,13 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
|
|||||||
bStack,
|
bStack,
|
||||||
diffBuilder
|
diffBuilder
|
||||||
);
|
);
|
||||||
if (!j$.util.isUndefined(asymmetricResult)) {
|
if (asymmetricResult !== undefined) {
|
||||||
return asymmetricResult;
|
return asymmetricResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tester of this.customTesters_) {
|
for (const tester of this.customTesters_) {
|
||||||
const customTesterResult = tester(a, b);
|
const customTesterResult = tester(a, b);
|
||||||
if (!j$.util.isUndefined(customTesterResult)) {
|
if (customTesterResult !== undefined) {
|
||||||
if (!customTesterResult) {
|
if (!customTesterResult) {
|
||||||
diffBuilder.recordMismatch();
|
diffBuilder.recordMismatch();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
getJasmineRequireObj().util = function(j$) {
|
getJasmineRequireObj().util = function(j$) {
|
||||||
const util = {};
|
const util = {};
|
||||||
|
|
||||||
util.isUndefined = function(obj) {
|
|
||||||
return obj === void 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
util.clone = function(obj) {
|
util.clone = function(obj) {
|
||||||
if (Object.prototype.toString.apply(obj) === '[object Array]') {
|
if (Object.prototype.toString.apply(obj) === '[object Array]') {
|
||||||
return obj.slice();
|
return obj.slice();
|
||||||
@@ -52,16 +48,9 @@ getJasmineRequireObj().util = function(j$) {
|
|||||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||||
};
|
};
|
||||||
|
|
||||||
util.errorWithStack = function errorWithStack() {
|
|
||||||
// Don't throw and catch. That makes it harder for users to debug their
|
|
||||||
// code with exception breakpoints, and it's unnecessary since all
|
|
||||||
// supported environments populate new Error().stack
|
|
||||||
return new Error();
|
|
||||||
};
|
|
||||||
|
|
||||||
function callerFile() {
|
function callerFile() {
|
||||||
const trace = new j$.StackTrace(util.errorWithStack());
|
const trace = new j$.StackTrace(new Error());
|
||||||
return trace.frames[2].file;
|
return trace.frames[1].file;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.jasmineFile = (function() {
|
util.jasmineFile = (function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user