Breaking change: use addEventListener rather than setting window.onerror
* Generally simplifies error handling in browsers * Makes Jasmine's own integration tests easier to debug * Stack traces will be provided for more global errors * ... but less error information will be provided in some browsers if the error comes from a file:// URL (use `npx serve` or similar instead) * Jasmine will no longer override existing onerror handlers in browsers * Setting window.onerror will no longer override Jasmine's global error handling (use jasmine.spyOnGlobalErrors instead)
This commit is contained in:
@@ -1291,20 +1291,14 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
|
|
||||||
if (!options.suppressLoadErrors) {
|
if (!options.suppressLoadErrors) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
globalErrors.pushListener(function loadtimeErrorHandler(
|
globalErrors.pushListener(function loadtimeErrorHandler(error, event) {
|
||||||
message,
|
|
||||||
filename,
|
|
||||||
lineno,
|
|
||||||
colNo,
|
|
||||||
err
|
|
||||||
) {
|
|
||||||
topSuite.result.failedExpectations.push({
|
topSuite.result.failedExpectations.push({
|
||||||
passed: false,
|
passed: false,
|
||||||
globalErrorType: 'load',
|
globalErrorType: 'load',
|
||||||
message: message,
|
message: error ? error.message : event.message,
|
||||||
stack: err && err.stack,
|
stack: error && error.stack,
|
||||||
filename: filename,
|
filename: event && event.filename,
|
||||||
lineno: lineno
|
lineno: event && event.lineno
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3978,18 +3972,22 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
let overrideHandler = null,
|
let overrideHandler = null,
|
||||||
onRemoveOverrideHandler = null;
|
onRemoveOverrideHandler = null;
|
||||||
|
|
||||||
function onerror(message, source, lineno, colno, error) {
|
function onBrowserError(event) {
|
||||||
|
dispatchBrowserError(event.error, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchBrowserError(error, event) {
|
||||||
if (overrideHandler) {
|
if (overrideHandler) {
|
||||||
overrideHandler(error || message);
|
overrideHandler(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
const handler = handlers[handlers.length - 1];
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
handler(error, event);
|
||||||
} else {
|
} else {
|
||||||
throw arguments[0];
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4066,8 +4064,7 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
this.installOne_('uncaughtException', 'Uncaught exception');
|
this.installOne_('uncaughtException', 'Uncaught exception');
|
||||||
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
||||||
} else {
|
} else {
|
||||||
const originalHandler = global.onerror;
|
global.addEventListener('error', onBrowserError);
|
||||||
global.onerror = onerror;
|
|
||||||
|
|
||||||
const browserRejectionHandler = function browserRejectionHandler(
|
const browserRejectionHandler = function browserRejectionHandler(
|
||||||
event
|
event
|
||||||
@@ -4075,16 +4072,19 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
if (j$.isError_(event.reason)) {
|
if (j$.isError_(event.reason)) {
|
||||||
event.reason.jasmineMessage =
|
event.reason.jasmineMessage =
|
||||||
'Unhandled promise rejection: ' + event.reason;
|
'Unhandled promise rejection: ' + event.reason;
|
||||||
global.onerror(event.reason);
|
dispatchBrowserError(event.reason, event);
|
||||||
} else {
|
} else {
|
||||||
global.onerror('Unhandled promise rejection: ' + event.reason);
|
dispatchBrowserError(
|
||||||
|
'Unhandled promise rejection: ' + event.reason,
|
||||||
|
event
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
this.uninstall = function uninstall() {
|
||||||
global.onerror = originalHandler;
|
global.removeEventListener('error', onBrowserError);
|
||||||
global.removeEventListener(
|
global.removeEventListener(
|
||||||
'unhandledrejection',
|
'unhandledrejection',
|
||||||
browserRejectionHandler
|
browserRejectionHandler
|
||||||
@@ -4093,6 +4093,13 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 will normally be provided, but will be falsy in the case of
|
||||||
|
// some browser load-time errors. The event will normally be provided in
|
||||||
|
// browsers but will be falsy in Node.
|
||||||
|
// Listeners that are pushed after spec files have been loaded should be
|
||||||
|
// able to just use the error parameter.
|
||||||
this.pushListener = function pushListener(listener) {
|
this.pushListener = function pushListener(listener) {
|
||||||
handlers.push(listener);
|
handlers.push(listener);
|
||||||
};
|
};
|
||||||
@@ -7490,11 +7497,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QueueRunner.prototype.execute = function() {
|
QueueRunner.prototype.execute = function() {
|
||||||
this.handleFinalError = (message, source, lineno, colno, error) => {
|
this.handleFinalError = error => {
|
||||||
// Older browsers would send the error as the first parameter. HTML5
|
this.onException(error);
|
||||||
// specifies the the five parameters above. The error instance should
|
|
||||||
// be preffered, otherwise the call stack would get lost.
|
|
||||||
this.onException(error || message);
|
|
||||||
};
|
};
|
||||||
this.globalErrors.pushListener(this.handleFinalError);
|
this.globalErrors.pushListener(this.handleFinalError);
|
||||||
this.run(0);
|
this.run(0);
|
||||||
@@ -10448,5 +10452,5 @@ getJasmineRequireObj().UserContext = function(j$) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getJasmineRequireObj().version = function() {
|
getJasmineRequireObj().version = function() {
|
||||||
return '4.3.0';
|
return '5.0.0-dev.0';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jasmine-core",
|
"name": "jasmine-core",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "4.3.0",
|
"version": "5.0.0-dev.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/jasmine/jasmine.git"
|
"url": "https://github.com/jasmine/jasmine.git"
|
||||||
|
|||||||
@@ -1,56 +1,42 @@
|
|||||||
describe('GlobalErrors', function() {
|
describe('GlobalErrors', function() {
|
||||||
it('calls the added handler on error', function() {
|
it('calls the added handler on error', function() {
|
||||||
const fakeGlobal = minimalBrowserGlobal();
|
const fakeGlobal = browserGlobal();
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
expect(handler).toHaveBeenCalledWith('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('enables external interception of error by overriding global.onerror', function() {
|
|
||||||
const fakeGlobal = minimalBrowserGlobal();
|
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
|
||||||
const hijackHandler = jasmine.createSpy('hijackErrorHandler');
|
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror = hijackHandler;
|
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
|
||||||
|
|
||||||
expect(hijackHandler).toHaveBeenCalledWith('foo');
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls the global error handler with all parameters', function() {
|
|
||||||
const fakeGlobal = minimalBrowserGlobal();
|
|
||||||
const handler = jasmine.createSpy('errorHandler');
|
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
const fooError = new Error('foo');
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror(fooError.message, 'foo.js', 1, 1, fooError);
|
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
fooError.message,
|
jasmine.is(error),
|
||||||
'foo.js',
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
1,
|
);
|
||||||
1,
|
});
|
||||||
fooError
|
|
||||||
|
it('is not affected by overriding global.onerror', function() {
|
||||||
|
const fakeGlobal = browserGlobal();
|
||||||
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
|
||||||
|
errors.install();
|
||||||
|
errors.pushListener(handler);
|
||||||
|
|
||||||
|
fakeGlobal.onerror = () => {};
|
||||||
|
|
||||||
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
|
|
||||||
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
jasmine.is(error),
|
||||||
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('only calls the most recent handler', function() {
|
it('only calls the most recent handler', function() {
|
||||||
const fakeGlobal = minimalBrowserGlobal();
|
const fakeGlobal = browserGlobal();
|
||||||
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(fakeGlobal);
|
||||||
@@ -59,14 +45,18 @@ describe('GlobalErrors', function() {
|
|||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
errors.pushListener(handler2);
|
errors.pushListener(handler2);
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
|
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
expect(handler2).toHaveBeenCalledWith('foo');
|
expect(handler2).toHaveBeenCalledWith(
|
||||||
|
jasmine.is(error),
|
||||||
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls previous handlers when one is removed', function() {
|
it('calls previous handlers when one is removed', function() {
|
||||||
const fakeGlobal = minimalBrowserGlobal();
|
const fakeGlobal = browserGlobal();
|
||||||
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(fakeGlobal);
|
||||||
@@ -77,9 +67,13 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
errors.popListener(handler2);
|
errors.popListener(handler2);
|
||||||
|
|
||||||
fakeGlobal.onerror('foo');
|
const error = new Error('nope');
|
||||||
|
dispatchErrorEvent(fakeGlobal, { error });
|
||||||
|
|
||||||
expect(handler1).toHaveBeenCalledWith('foo');
|
expect(handler1).toHaveBeenCalledWith(
|
||||||
|
jasmine.is(error),
|
||||||
|
jasmine.objectContaining({ error: jasmine.is(error) })
|
||||||
|
);
|
||||||
expect(handler2).not.toHaveBeenCalled();
|
expect(handler2).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,34 +84,27 @@ describe('GlobalErrors', function() {
|
|||||||
}).toThrowError('popListener expects a listener');
|
}).toThrowError('popListener expects a listener');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uninstalls itself, putting back a previous callback', function() {
|
it('uninstalls itself', function() {
|
||||||
const originalCallback = jasmine.createSpy('error');
|
const fakeGlobal = browserGlobal();
|
||||||
const fakeGlobal = {
|
|
||||||
...minimalBrowserGlobal(),
|
|
||||||
onerror: originalCallback
|
|
||||||
};
|
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
|
function unrelatedListener() {}
|
||||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
|
fakeGlobal.addEventListener('error', unrelatedListener);
|
||||||
expect(fakeGlobal.onerror).not.toBe(originalCallback);
|
|
||||||
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
|
||||||
expect(fakeGlobal.onerror).toBe(originalCallback);
|
expect(fakeGlobal.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 = minimalBrowserGlobal();
|
const fakeGlobal = browserGlobal();
|
||||||
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
const originalError = new Error('nope');
|
const originalError = new Error('nope');
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fakeGlobal.onerror(originalError);
|
dispatchErrorEvent(fakeGlobal, { error: originalError });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e).toBe(originalError);
|
expect(e).toBe(originalError);
|
||||||
}
|
}
|
||||||
@@ -289,128 +276,61 @@ describe('GlobalErrors', function() {
|
|||||||
|
|
||||||
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 = jasmine.createSpyObj('globalErrors', [
|
const fakeGlobal = browserGlobal();
|
||||||
'addEventListener',
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
'removeEventListener',
|
|
||||||
'onerror'
|
|
||||||
]),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
expect(fakeGlobal.addEventListener).toHaveBeenCalledWith(
|
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([
|
||||||
'unhandledrejection',
|
|
||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
]);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
errors.uninstall();
|
errors.uninstall();
|
||||||
|
expect(fakeGlobal.listeners_.unhandledrejection).toEqual([]);
|
||||||
expect(fakeGlobal.removeEventListener).toHaveBeenCalledWith(
|
|
||||||
'unhandledrejection',
|
|
||||||
addedListener
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is a string', function() {
|
it('reports rejections whose reason is a string', function() {
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
const fakeGlobal = browserGlobal();
|
||||||
'addEventListener',
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
'removeEventListener',
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
'onerror'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
const event = { reason: 'nope' };
|
||||||
addedListener({ reason: 'nope' });
|
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith('Unhandled promise rejection: nope');
|
expect(handler).toHaveBeenCalledWith(
|
||||||
|
'Unhandled promise rejection: nope',
|
||||||
|
event
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports rejections whose reason is an Error', function() {
|
it('reports rejections whose reason is an Error', function() {
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
const fakeGlobal = browserGlobal();
|
||||||
'addEventListener',
|
const handler = jasmine.createSpy('errorHandler');
|
||||||
'removeEventListener',
|
const errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
||||||
'onerror'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
errors.install();
|
||||||
errors.pushListener(handler);
|
errors.pushListener(handler);
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
const reason = new Error('bar');
|
const reason = new Error('bar');
|
||||||
|
const event = { reason };
|
||||||
addedListener({ reason: reason });
|
dispatchUnhandledRejectionEvent(fakeGlobal, event);
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalledWith(
|
expect(handler).toHaveBeenCalledWith(
|
||||||
jasmine.objectContaining({
|
jasmine.objectContaining({
|
||||||
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
||||||
message: reason.message,
|
message: reason.message,
|
||||||
stack: reason.stack
|
stack: reason.stack
|
||||||
})
|
}),
|
||||||
|
event
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Enabling external interception of reported rejections by overriding global.onerror', function() {
|
|
||||||
it('overriding global.onerror intercepts rejections whose reason is a string', function() {
|
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
|
||||||
'addEventListener'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror = hijackHandler;
|
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
addedListener({ reason: 'nope' });
|
|
||||||
|
|
||||||
expect(hijackHandler).toHaveBeenCalledWith(
|
|
||||||
'Unhandled promise rejection: nope'
|
|
||||||
);
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('overriding global.onerror intercepts rejections whose reason is an Error', function() {
|
|
||||||
const fakeGlobal = jasmine.createSpyObj('globalErrors', [
|
|
||||||
'addEventListener'
|
|
||||||
]),
|
|
||||||
handler = jasmine.createSpy('errorHandler'),
|
|
||||||
hijackHandler = jasmine.createSpy('hijackErrorHandler'),
|
|
||||||
errors = new jasmineUnderTest.GlobalErrors(fakeGlobal);
|
|
||||||
|
|
||||||
errors.install();
|
|
||||||
errors.pushListener(handler);
|
|
||||||
|
|
||||||
fakeGlobal.onerror = hijackHandler;
|
|
||||||
|
|
||||||
const addedListener = fakeGlobal.addEventListener.calls.argsFor(0)[1];
|
|
||||||
const reason = new Error('bar');
|
|
||||||
|
|
||||||
addedListener({ reason: reason });
|
|
||||||
|
|
||||||
expect(hijackHandler).toHaveBeenCalledWith(
|
|
||||||
jasmine.objectContaining({
|
|
||||||
jasmineMessage: 'Unhandled promise rejection: Error: bar',
|
|
||||||
message: reason.message,
|
|
||||||
stack: reason.stack
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(handler).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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 = minimalBrowserGlobal();
|
const fakeGlobal = browserGlobal();
|
||||||
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');
|
||||||
@@ -420,19 +340,18 @@ describe('GlobalErrors', function() {
|
|||||||
errors.pushListener(handler0);
|
errors.pushListener(handler0);
|
||||||
errors.setOverrideListener(overrideHandler, () => {});
|
errors.setOverrideListener(overrideHandler, () => {});
|
||||||
errors.pushListener(handler1);
|
errors.pushListener(handler1);
|
||||||
fakeGlobal.onerror('foo');
|
dispatchErrorEvent(fakeGlobal, { error: 'foo' });
|
||||||
fakeGlobal.onerror(null, null, null, null, new Error('bar'));
|
|
||||||
|
|
||||||
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
expect(overrideHandler).toHaveBeenCalledWith('foo');
|
||||||
expect(overrideHandler).toHaveBeenCalledWith(new Error('bar'));
|
|
||||||
expect(handler0).not.toHaveBeenCalled();
|
expect(handler0).not.toHaveBeenCalled();
|
||||||
expect(handler1).not.toHaveBeenCalled();
|
expect(handler1).not.toHaveBeenCalled();
|
||||||
|
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
|
|
||||||
fakeGlobal.onerror('baz');
|
const event = { error: 'baz' };
|
||||||
|
dispatchErrorEvent(fakeGlobal, event);
|
||||||
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
expect(overrideHandler).not.toHaveBeenCalledWith('baz');
|
||||||
expect(handler1).toHaveBeenCalledWith('baz');
|
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() {
|
||||||
@@ -532,7 +451,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(minimalBrowserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, () => {});
|
errors.setOverrideListener(() => {}, () => {});
|
||||||
expect(function() {
|
expect(function() {
|
||||||
@@ -544,7 +463,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(minimalBrowserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||||
|
|
||||||
errors.setOverrideListener(() => {}, onRemove);
|
errors.setOverrideListener(() => {}, onRemove);
|
||||||
errors.removeOverrideListener();
|
errors.removeOverrideListener();
|
||||||
@@ -553,17 +472,43 @@ 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(minimalBrowserGlobal());
|
const errors = new jasmineUnderTest.GlobalErrors(browserGlobal());
|
||||||
|
|
||||||
expect(() => errors.removeOverrideListener()).not.toThrow();
|
expect(() => errors.removeOverrideListener()).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function minimalBrowserGlobal() {
|
function browserGlobal() {
|
||||||
return {
|
return {
|
||||||
addEventListener() {},
|
listeners_: { error: [], unhandledrejection: [] },
|
||||||
removeEventListener() {},
|
addEventListener(eventName, listener) {
|
||||||
onerror: null
|
this.listeners_[eventName].push(listener);
|
||||||
|
},
|
||||||
|
removeEventListener(eventName, listener) {
|
||||||
|
this.listeners_[eventName] = this.listeners_[eventName].filter(
|
||||||
|
l => l !== listener
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dispatchErrorEvent(global, event) {
|
||||||
|
expect(global.listeners_.error.length)
|
||||||
|
.withContext('number of error listeners')
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
|
for (const l of global.listeners_.error) {
|
||||||
|
l(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchUnhandledRejectionEvent(global, event) {
|
||||||
|
expect(global.listeners_.unhandledrejection.length)
|
||||||
|
.withContext('number of unhandledrejection listeners')
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
|
for (const l of global.listeners_.unhandledrejection) {
|
||||||
|
l(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ describe('QueueRunner', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the error instance to exception handlers in HTML browsers', function() {
|
it('passes final errors to exception handlers', function() {
|
||||||
const error = new Error('fake error'),
|
const error = new Error('fake error'),
|
||||||
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
queueRunner = new jasmineUnderTest.QueueRunner({
|
||||||
@@ -659,24 +659,11 @@ describe('QueueRunner', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
queueRunner.execute();
|
queueRunner.execute();
|
||||||
queueRunner.handleFinalError(error.message, 'fake.js', 1, 1, error);
|
queueRunner.handleFinalError(error);
|
||||||
|
|
||||||
expect(onExceptionCallback).toHaveBeenCalledWith(error);
|
expect(onExceptionCallback).toHaveBeenCalledWith(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes the first argument to exception handlers for compatibility', function() {
|
|
||||||
const error = new Error('fake error'),
|
|
||||||
onExceptionCallback = jasmine.createSpy('on exception callback'),
|
|
||||||
queueRunner = new jasmineUnderTest.QueueRunner({
|
|
||||||
onException: onExceptionCallback
|
|
||||||
});
|
|
||||||
|
|
||||||
queueRunner.execute();
|
|
||||||
queueRunner.handleFinalError(error.message);
|
|
||||||
|
|
||||||
expect(onExceptionCallback).toHaveBeenCalledWith(error.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls exception handlers when an exception is thrown in a fn', function() {
|
it('calls exception handlers when an exception is thrown in a fn', function() {
|
||||||
const queueableFn = {
|
const queueableFn = {
|
||||||
type: 'queueable',
|
type: 'queueable',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
describe('Env integration', function() {
|
describe('Env integration', function() {
|
||||||
let env;
|
let env;
|
||||||
|
const isBrowser = typeof window !== 'undefined';
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
jasmine.getEnv().registerIntegrationMatchers();
|
jasmine.getEnv().registerIntegrationMatchers();
|
||||||
@@ -455,7 +456,7 @@ describe('Env integration', function() {
|
|||||||
env.describe('A suite', function() {
|
env.describe('A suite', function() {
|
||||||
env.it('fails', function(specDone) {
|
env.it('fails', function(specDone) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
global.onerror('fail');
|
dispatchErrorEvent(global, { error: 'fail' });
|
||||||
specDone();
|
specDone();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -509,10 +510,14 @@ describe('Env integration', function() {
|
|||||||
},
|
},
|
||||||
specDone: function() {
|
specDone: function() {
|
||||||
clearStackCallbacks[clearStackCallCount + 1] = function() {
|
clearStackCallbacks[clearStackCallCount + 1] = function() {
|
||||||
global.onerror('fail at the end of the reporter queue');
|
dispatchErrorEvent(global, {
|
||||||
|
error: 'fail at the end of the reporter queue'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
clearStackCallbacks[clearStackCallCount + 2] = function() {
|
clearStackCallbacks[clearStackCallCount + 2] = function() {
|
||||||
global.onerror('fail at the end of the spec queue');
|
dispatchErrorEvent(global, {
|
||||||
|
error: 'fail at the end of the spec queue'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -559,7 +564,7 @@ describe('Env integration', function() {
|
|||||||
specDone();
|
specDone();
|
||||||
queueMicrotask(function() {
|
queueMicrotask(function() {
|
||||||
queueMicrotask(function() {
|
queueMicrotask(function() {
|
||||||
global.onerror('fail');
|
dispatchErrorEvent(global, { error: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -622,10 +627,14 @@ describe('Env integration', function() {
|
|||||||
|
|
||||||
if (result.description === 'A nested suite') {
|
if (result.description === 'A nested suite') {
|
||||||
clearStackCallbacks[clearStackCallCount + 1] = function() {
|
clearStackCallbacks[clearStackCallCount + 1] = function() {
|
||||||
global.onerror('fail at the end of the reporter queue');
|
dispatchErrorEvent(global, {
|
||||||
|
error: 'fail at the end of the reporter queue'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
clearStackCallbacks[clearStackCallCount + 2] = function() {
|
clearStackCallbacks[clearStackCallCount + 2] = function() {
|
||||||
global.onerror('fail at the end of the suite queue');
|
dispatchErrorEvent(global, {
|
||||||
|
error: 'fail at the end of the suite queue'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -668,7 +677,7 @@ describe('Env integration', function() {
|
|||||||
|
|
||||||
env.addReporter({
|
env.addReporter({
|
||||||
jasmineDone: function() {
|
jasmineDone: function() {
|
||||||
global.onerror('a very late error');
|
dispatchErrorEvent(global, { error: 'a very late error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -720,7 +729,7 @@ describe('Env integration', function() {
|
|||||||
expectedErrors.push(`${msg} thrown`);
|
expectedErrors.push(`${msg} thrown`);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.onerror(msg);
|
dispatchErrorEvent(global, { error: msg });
|
||||||
realClearStack(fn);
|
realClearStack(fn);
|
||||||
});
|
});
|
||||||
spyOn(console, 'error');
|
spyOn(console, 'error');
|
||||||
@@ -2524,15 +2533,24 @@ describe('Env integration', function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await env.execute();
|
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('suite'));
|
||||||
|
expect(globalErrorSpy).toHaveBeenCalledWith(new Error('spec'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
|
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
|
||||||
'async suite',
|
'async suite',
|
||||||
[/^(((Uncaught )?(exception: )?Error: suite( thrown)?)|(suite thrown))$/]
|
[/Error: suite/]
|
||||||
);
|
);
|
||||||
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
|
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
|
||||||
'suite async spec',
|
'suite async spec',
|
||||||
[/^(((Uncaught )?(exception: )?Error: spec( thrown)?)|(spec thrown))$/]
|
[/Error: spec/]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2666,14 +2684,14 @@ describe('Env integration', function() {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
global.onerror(
|
dispatchErrorEvent(global, {
|
||||||
'Uncaught SyntaxError: Unexpected end of input',
|
message: 'Uncaught SyntaxError: Unexpected end of input',
|
||||||
'borkenSpec.js',
|
error: undefined,
|
||||||
42,
|
filename: 'borkenSpec.js',
|
||||||
undefined,
|
lineno: 42
|
||||||
{ stack: 'a stack' }
|
});
|
||||||
);
|
const error = new Error('ENOCHEESE');
|
||||||
global.onerror('Uncaught Error: ENOCHEESE');
|
dispatchErrorEvent(global, { error });
|
||||||
|
|
||||||
await env.execute();
|
await env.execute();
|
||||||
|
|
||||||
@@ -2683,15 +2701,15 @@ describe('Env integration', function() {
|
|||||||
passed: false,
|
passed: false,
|
||||||
globalErrorType: 'load',
|
globalErrorType: 'load',
|
||||||
message: 'Uncaught SyntaxError: Unexpected end of input',
|
message: 'Uncaught SyntaxError: Unexpected end of input',
|
||||||
stack: 'a stack',
|
stack: undefined,
|
||||||
filename: 'borkenSpec.js',
|
filename: 'borkenSpec.js',
|
||||||
lineno: 42
|
lineno: 42
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
passed: false,
|
passed: false,
|
||||||
globalErrorType: 'load',
|
globalErrorType: 'load',
|
||||||
message: 'Uncaught Error: ENOCHEESE',
|
message: 'ENOCHEESE',
|
||||||
stack: undefined,
|
stack: error.stack,
|
||||||
filename: undefined,
|
filename: undefined,
|
||||||
lineno: undefined
|
lineno: undefined
|
||||||
}
|
}
|
||||||
@@ -2923,7 +2941,7 @@ describe('Env integration', function() {
|
|||||||
|
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
env.it('passes', function() {});
|
env.it('passes', function() {});
|
||||||
global.onerror('Uncaught Error: ENOCHEESE');
|
dispatchErrorEvent(global, { error: 'ENOCHEESE' });
|
||||||
await env.execute();
|
await env.execute();
|
||||||
|
|
||||||
expect(reporter.jasmineDone).toHaveBeenCalled();
|
expect(reporter.jasmineDone).toHaveBeenCalled();
|
||||||
@@ -3702,7 +3720,16 @@ describe('Env integration', function() {
|
|||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
await env.execute();
|
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 passingResult = resultForRunable(
|
const passingResult = resultForRunable(
|
||||||
reporter.specDone,
|
reporter.specDone,
|
||||||
@@ -3749,7 +3776,17 @@ describe('Env integration', function() {
|
|||||||
'suiteDone'
|
'suiteDone'
|
||||||
]);
|
]);
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
await env.execute();
|
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');
|
const suiteResult = resultForRunable(reporter.suiteDone, 'Suite 1');
|
||||||
expect(suiteResult.status).toEqual('failed');
|
expect(suiteResult.status).toEqual('failed');
|
||||||
@@ -3794,7 +3831,17 @@ describe('Env integration', function() {
|
|||||||
'suiteDone'
|
'suiteDone'
|
||||||
]);
|
]);
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
await env.execute();
|
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(
|
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
|
||||||
'Suite 1',
|
'Suite 1',
|
||||||
@@ -3844,7 +3891,18 @@ describe('Env integration', function() {
|
|||||||
'suiteDone'
|
'suiteDone'
|
||||||
]);
|
]);
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
await env.execute();
|
|
||||||
|
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');
|
const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec');
|
||||||
expect(spec1Result.status).toEqual('failed');
|
expect(spec1Result.status).toEqual('failed');
|
||||||
@@ -3880,7 +3938,17 @@ describe('Env integration', function() {
|
|||||||
|
|
||||||
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
const reporter = jasmine.createSpyObj('reporter', ['specDone']);
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
await env.execute();
|
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');
|
const spec1Result = resultForRunable(reporter.specDone, 'spec 1');
|
||||||
expect(spec1Result.status).toEqual('failed');
|
expect(spec1Result.status).toEqual('failed');
|
||||||
@@ -3925,7 +3993,17 @@ describe('Env integration', function() {
|
|||||||
'suiteDone'
|
'suiteDone'
|
||||||
]);
|
]);
|
||||||
env.addReporter(reporter);
|
env.addReporter(reporter);
|
||||||
await env.execute();
|
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');
|
const spec1Result = resultForRunable(reporter.specDone, 'Suite 1 a spec');
|
||||||
expect(spec1Result.status).toEqual('failed');
|
expect(spec1Result.status).toEqual('failed');
|
||||||
@@ -3945,8 +4023,25 @@ describe('Env integration', function() {
|
|||||||
|
|
||||||
function browserEventMethods() {
|
function browserEventMethods() {
|
||||||
return {
|
return {
|
||||||
addEventListener() {},
|
listeners_: { error: [], unhandledrejection: [] },
|
||||||
removeEventListener() {}
|
addEventListener(eventName, listener) {
|
||||||
|
this.listeners_[eventName].push(listener);
|
||||||
|
},
|
||||||
|
removeEventListener(eventName, listener) {
|
||||||
|
this.listeners_[eventName] = this.listeners_[eventName].filter(
|
||||||
|
l => l !== listener
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dispatchErrorEvent(global, event) {
|
||||||
|
expect(global.listeners_.error.length)
|
||||||
|
.withContext('number of error listeners')
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
|
for (const l of global.listeners_.error) {
|
||||||
|
l(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -149,20 +149,14 @@ getJasmineRequireObj().Env = function(j$) {
|
|||||||
|
|
||||||
if (!options.suppressLoadErrors) {
|
if (!options.suppressLoadErrors) {
|
||||||
installGlobalErrors();
|
installGlobalErrors();
|
||||||
globalErrors.pushListener(function loadtimeErrorHandler(
|
globalErrors.pushListener(function loadtimeErrorHandler(error, event) {
|
||||||
message,
|
|
||||||
filename,
|
|
||||||
lineno,
|
|
||||||
colNo,
|
|
||||||
err
|
|
||||||
) {
|
|
||||||
topSuite.result.failedExpectations.push({
|
topSuite.result.failedExpectations.push({
|
||||||
passed: false,
|
passed: false,
|
||||||
globalErrorType: 'load',
|
globalErrorType: 'load',
|
||||||
message: message,
|
message: error ? error.message : event.message,
|
||||||
stack: err && err.stack,
|
stack: error && error.stack,
|
||||||
filename: filename,
|
filename: event && event.filename,
|
||||||
lineno: lineno
|
lineno: event && event.lineno
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,22 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
let overrideHandler = null,
|
let overrideHandler = null,
|
||||||
onRemoveOverrideHandler = null;
|
onRemoveOverrideHandler = null;
|
||||||
|
|
||||||
function onerror(message, source, lineno, colno, error) {
|
function onBrowserError(event) {
|
||||||
|
dispatchBrowserError(event.error, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchBrowserError(error, event) {
|
||||||
if (overrideHandler) {
|
if (overrideHandler) {
|
||||||
overrideHandler(error || message);
|
overrideHandler(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = handlers[handlers.length - 1];
|
const handler = handlers[handlers.length - 1];
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler.apply(null, Array.prototype.slice.call(arguments, 0));
|
handler(error, event);
|
||||||
} else {
|
} else {
|
||||||
throw arguments[0];
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +98,7 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
this.installOne_('uncaughtException', 'Uncaught exception');
|
this.installOne_('uncaughtException', 'Uncaught exception');
|
||||||
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
this.installOne_('unhandledRejection', 'Unhandled promise rejection');
|
||||||
} else {
|
} else {
|
||||||
const originalHandler = global.onerror;
|
global.addEventListener('error', onBrowserError);
|
||||||
global.onerror = onerror;
|
|
||||||
|
|
||||||
const browserRejectionHandler = function browserRejectionHandler(
|
const browserRejectionHandler = function browserRejectionHandler(
|
||||||
event
|
event
|
||||||
@@ -103,16 +106,19 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
if (j$.isError_(event.reason)) {
|
if (j$.isError_(event.reason)) {
|
||||||
event.reason.jasmineMessage =
|
event.reason.jasmineMessage =
|
||||||
'Unhandled promise rejection: ' + event.reason;
|
'Unhandled promise rejection: ' + event.reason;
|
||||||
global.onerror(event.reason);
|
dispatchBrowserError(event.reason, event);
|
||||||
} else {
|
} else {
|
||||||
global.onerror('Unhandled promise rejection: ' + event.reason);
|
dispatchBrowserError(
|
||||||
|
'Unhandled promise rejection: ' + event.reason,
|
||||||
|
event
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
global.addEventListener('unhandledrejection', browserRejectionHandler);
|
||||||
|
|
||||||
this.uninstall = function uninstall() {
|
this.uninstall = function uninstall() {
|
||||||
global.onerror = originalHandler;
|
global.removeEventListener('error', onBrowserError);
|
||||||
global.removeEventListener(
|
global.removeEventListener(
|
||||||
'unhandledrejection',
|
'unhandledrejection',
|
||||||
browserRejectionHandler
|
browserRejectionHandler
|
||||||
@@ -121,6 +127,13 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 will normally be provided, but will be falsy in the case of
|
||||||
|
// some browser load-time errors. The event will normally be provided in
|
||||||
|
// browsers but will be falsy in Node.
|
||||||
|
// Listeners that are pushed after spec files have been loaded should be
|
||||||
|
// able to just use the error parameter.
|
||||||
this.pushListener = function pushListener(listener) {
|
this.pushListener = function pushListener(listener) {
|
||||||
handlers.push(listener);
|
handlers.push(listener);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -66,11 +66,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QueueRunner.prototype.execute = function() {
|
QueueRunner.prototype.execute = function() {
|
||||||
this.handleFinalError = (message, source, lineno, colno, error) => {
|
this.handleFinalError = error => {
|
||||||
// Older browsers would send the error as the first parameter. HTML5
|
this.onException(error);
|
||||||
// specifies the the five parameters above. The error instance should
|
|
||||||
// be preffered, otherwise the call stack would get lost.
|
|
||||||
this.onException(error || message);
|
|
||||||
};
|
};
|
||||||
this.globalErrors.pushListener(this.handleFinalError);
|
this.globalErrors.pushListener(this.handleFinalError);
|
||||||
this.run(0);
|
this.run(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user