Files
jasmine/spec/core/integration/GlobalErrorHandlingSpec.js
Steve Gravrock bdf63f2402 Remove code to support browsers that don't have MessageChannel
Jasmine hasn't actually run in any such browsers since 2.x.
2025-11-12 21:59:17 -08:00

1247 lines
39 KiB
JavaScript

describe('Global error handling (integration)', function() {
const isBrowser = typeof window !== 'undefined';
let env;
beforeEach(function() {
specHelpers.registerIntegrationMatchers();
env = new privateUnderTest.Env();
});
afterEach(function() {
env.cleanup_();
});
function mockGlobal() {
return {
...browserEventMethods(),
setTimeout: function(fn, delay) {
return setTimeout(fn, delay);
},
clearTimeout: function(fn, delay) {
clearTimeout(fn, delay);
},
queueMicrotask: function(fn) {
queueMicrotask(fn);
},
onerror: function() {},
// Enough Node globals to make getStackClearer() return the microtask
// implementation, which is the easiest to mock
process: {
versions: {
node: ''
}
}
};
}
it('reports errors that occur during loading', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
const reporter = jasmine.createSpyObj('reporter', [
'jasmineDone',
'suiteDone',
'specDone'
]);
env.addReporter(reporter);
dispatchErrorEvent(global, 'error', {
message: 'Uncaught SyntaxError: Unexpected end of input',
error: undefined,
filename: 'borkenSpec.js',
lineno: 42
});
const error = new Error('ENOCHEESE');
dispatchErrorEvent(global, 'error', { 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',
filename: 'borkenSpec.js',
lineno: 42,
matcherName: undefined,
stack: jasmine.any(String)
},
{
passed: false,
globalErrorType: 'load',
message: 'ENOCHEESE',
matcherName: undefined,
stack: jasmine.any(String)
}
]);
});
describe('If suppressLoadErrors: true was passed', function() {
it('does not install a global error handler during loading', async function() {
const global = mockGlobal();
const originalOnerror = jasmine.createSpy('original onerror');
global.onerror = originalOnerror;
const globalErrors = new privateUnderTest.GlobalErrors(global);
const onerror = jasmine.createSpy('onerror');
globalErrors.pushListener(onerror);
env.cleanup_();
env = new privateUnderTest.Env({
suppressLoadErrors: true,
global,
GlobalErrors: function() {
return globalErrors;
}
});
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('Handling unhandled exceptions', function() {
it('routes unhandled exceptions to the running spec', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
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', { error: 'fail' });
specDone();
});
});
});
await env.execute();
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
'A suite fails',
['fail thrown']
);
});
describe('When the most recently running spec has reported specDone', function() {
it('routes unhandled exceptions to an ancestor suite', async function() {
const global = mockGlobal();
const stackClearer = privateUnderTest.getStackClearer(global);
const realClearStack = stackClearer.clearStack;
const clearStackCallbacks = {};
let clearStackCallCount = 0;
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
clearStackCallCount++;
if (clearStackCallbacks[clearStackCallCount]) {
clearStackCallbacks[clearStackCallCount]();
}
realClearStack(fn);
});
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
stackClearer
);
env.cleanup_();
env = new privateUnderTest.Env({ global });
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', {
error: 'fail at the end of the reporter queue'
});
};
clearStackCallbacks[clearStackCallCount + 2] = function() {
dispatchErrorEvent(global, 'error', {
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 unhandled exceptions to the running suite', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
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', { 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 most recently suite has reported suiteDone', function() {
it('routes unhandled exceptions to an ancestor suite', async function() {
const global = mockGlobal();
const stackClearer = privateUnderTest.getStackClearer(global);
const realClearStack = stackClearer.clearStack;
const clearStackCallbacks = {};
let clearStackCallCount = 0;
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
clearStackCallCount++;
if (clearStackCallbacks[clearStackCallCount]) {
clearStackCallbacks[clearStackCallCount]();
}
realClearStack(fn);
});
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
stackClearer
);
env.cleanup_();
env = new privateUnderTest.Env({ global });
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', {
error: 'fail at the end of the reporter queue'
});
};
clearStackCallbacks[clearStackCallCount + 2] = function() {
dispatchErrorEvent(global, 'error', {
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 = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
spyOn(console, 'error');
env.addReporter({
jasmineDone: function() {
dispatchErrorEvent(global, 'error', { 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 = mockGlobal();
const stackClearer = privateUnderTest.getStackClearer(global);
const realClearStack = stackClearer.clearStack;
let clearStackCallCount = 0;
let jasmineDone = false;
const expectedErrors = [];
const expectedErrorsAfterJasmineDone = [];
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
clearStackCallCount++;
const msg = `Error in clearStack #${clearStackCallCount}`;
if (jasmineDone) {
expectedErrorsAfterJasmineDone.push(`${msg} thrown`);
} else {
expectedErrors.push(`${msg} thrown`);
}
dispatchErrorEvent(global, 'error', { error: msg });
realClearStack(fn);
});
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(stackClearer);
spyOn(console, 'error');
env.cleanup_();
env = new privateUnderTest.Env({ global });
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 })
);
}
});
});
describe('Handling unhandled promise rejections', function() {
it('routes unhandled promise rejections to the running spec', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
const reporter = jasmine.createSpyObj('fakeReporter', [
'specDone',
'suiteDone'
]);
env.addReporter(reporter);
env.describe('A suite', function() {
env.it('fails', function(specDone) {
setTimeout(function() {
dispatchErrorEvent(global, 'unhandledrejection', {
reason: 'fail'
});
specDone();
});
});
});
await env.execute();
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
'A suite fails',
['Unhandled promise rejection: fail thrown']
);
});
describe('When the most recently running spec has reported specDone', function() {
it('routes unhandled promise rejections to an ancestor suite', async function() {
const global = mockGlobal();
const stackClearer = privateUnderTest.getStackClearer(global);
const realClearStack = stackClearer.clearStack;
const clearStackCallbacks = {};
let clearStackCallCount = 0;
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
clearStackCallCount++;
if (clearStackCallbacks[clearStackCallCount]) {
clearStackCallbacks[clearStackCallCount]();
}
realClearStack(fn);
});
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
stackClearer
);
env.cleanup_();
env = new privateUnderTest.Env({ global });
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, 'unhandledrejection', {
reason: 'fail at the end of the reporter queue'
});
};
clearStackCallbacks[clearStackCallCount + 2] = function() {
dispatchErrorEvent(global, 'unhandledrejection', {
reason: '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([
'Unhandled promise rejection: fail at the end of the reporter queue thrown',
'Unhandled promise rejection: fail at the end of the spec queue thrown'
]);
});
});
it('routes unhandled promise rejections to the running suite', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
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, 'unhandledrejection', {
reason: 'fail'
});
});
});
});
});
});
env.describe('Ignored', function() {
env.it('is not run', function() {});
});
await env.execute();
expect(reporter.specDone).not.toHaveFailedExpectationsForRunnable(
'A suite fails',
['Unhandled promise rejection: fail thrown']
);
expect(reporter.suiteDone).toHaveFailedExpectationsForRunnable(
'A suite',
['Unhandled promise rejection: fail thrown']
);
});
describe('When the most recently suite has reported suiteDone', function() {
it('routes unhandled promise rejections to an ancestor suite', async function() {
const global = mockGlobal();
const stackClearer = privateUnderTest.getStackClearer(global);
const realClearStack = stackClearer.clearStack;
const clearStackCallbacks = {};
let clearStackCallCount = 0;
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
clearStackCallCount++;
if (clearStackCallbacks[clearStackCallCount]) {
clearStackCallbacks[clearStackCallCount]();
}
realClearStack(fn);
});
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(
stackClearer
);
env.cleanup_();
env = new privateUnderTest.Env({ global });
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, 'unhandledrejection', {
reason: 'fail at the end of the reporter queue'
});
};
clearStackCallbacks[clearStackCallCount + 2] = function() {
dispatchErrorEvent(global, 'unhandledrejection', {
reason: '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([
'Unhandled promise rejection: fail at the end of the reporter queue thrown',
'Unhandled promise rejection: fail at the end of the suite queue thrown'
]);
});
});
describe('When the env has started reporting jasmineDone', function() {
it('logs the rejection to the console', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
spyOn(console, 'error');
env.addReporter({
jasmineDone: function() {
dispatchErrorEvent(global, 'unhandledrejection', {
reason: '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: 'Unhandled promise rejection: a very late error thrown',
globalErrorType: 'afterAll'
})
);
});
});
it('routes all unhandled promise rejections that occur during stack clearing somewhere', async function() {
const global = mockGlobal();
const stackClearer = privateUnderTest.getStackClearer(global);
const realClearStack = stackClearer.clearStack;
let clearStackCallCount = 0;
let jasmineDone = false;
const expectedErrors = [];
const expectedErrorsAfterJasmineDone = [];
spyOn(stackClearer, 'clearStack').and.callFake(function(fn) {
clearStackCallCount++;
const reason = `Error in clearStack #${clearStackCallCount}`;
const expectedMsg = `Unhandled promise rejection: ${reason} thrown`;
if (jasmineDone) {
expectedErrorsAfterJasmineDone.push(expectedMsg);
} else {
expectedErrors.push(expectedMsg);
}
dispatchErrorEvent(global, 'unhandledrejection', { reason });
realClearStack(fn);
});
spyOn(privateUnderTest, 'getStackClearer').and.returnValue(stackClearer);
spyOn(console, 'error');
env.cleanup_();
env = new privateUnderTest.Env({ global });
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 })
);
}
});
describe('When the detectLateRejectionHandling config option is set', function() {
describe('When the unhandled rejection event has a promise', function() {
function makeEvent(suffix) {
const reason = `rejection ${suffix}`;
const promise = Promise.reject(reason);
promise.catch(() => {});
return { reason, promise };
}
let global, reporter;
beforeEach(function() {
global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
env.configure({ detectLateRejectionHandling: true });
reporter = jasmine.createSpyObj('fakeReporter', [
'specDone',
'suiteDone'
]);
env.addReporter(reporter);
});
describe('During spec execution', function() {
it('reports the rejection unless a corresponding rejection handled event occurs', async function() {
env.describe('A suite', function() {
env.it('fails', function(specDone) {
setTimeout(function() {
const events = ['spec 1', 'spec 2'].map(makeEvent);
for (const e of events) {
dispatchErrorEvent(global, 'unhandledrejection', e);
}
dispatchErrorEvent(global, 'rejectionhandled', events[0]);
specDone();
});
});
});
await env.execute();
expect(reporter.specDone).toHaveBeenCalledWith(
jasmine.objectContaining({
fullName: 'A suite fails',
failedExpectations: [
// Only the second rejection should be reported, since the first
// one was eventually handled.
jasmine.objectContaining({
message:
'Unhandled promise rejection: rejection spec 2 thrown'
})
]
})
);
});
});
describe('During beforeAll execution', function() {
it('reports the rejection unless a corresponding rejection handled event occurs by the end of the beforeAll', async function() {
env.describe('A suite', function() {
let events;
env.beforeAll(function(beforeAllDone) {
setTimeout(function() {
events = ['suite 1', 'suite 2'].map(makeEvent);
for (const e of events) {
dispatchErrorEvent(global, 'unhandledrejection', e);
}
dispatchErrorEvent(global, 'rejectionhandled', events[0]);
beforeAllDone();
});
});
env.it('is a spec', function(specDone) {
setTimeout(function() {
// Should not prevent the second rejection from being reported
dispatchErrorEvent(global, 'rejectionhandled', events[1]);
specDone();
});
});
});
await env.execute();
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
fullName: 'A suite',
failedExpectations: [
// Only the second rejection should be reported, since the first
// one was eventually handled.
jasmine.objectContaining({
message:
'Unhandled promise rejection: rejection suite 2 thrown'
})
]
})
);
});
});
describe('During afterAll execution', function() {
it('reports the rejection unless a corresponding rejection handled event occurs by the end of the afterAll', async function() {
env.describe('A suite', function() {
let events;
env.afterAll(function(beforeAllDone) {
setTimeout(function() {
events = ['suite 1', 'suite 2'].map(makeEvent);
for (const e of events) {
dispatchErrorEvent(global, 'unhandledrejection', e);
}
dispatchErrorEvent(global, 'rejectionhandled', events[0]);
beforeAllDone();
});
});
env.it('is a spec', function() {});
});
await env.execute();
expect(reporter.suiteDone).toHaveBeenCalledWith(
jasmine.objectContaining({
fullName: 'A suite',
failedExpectations: [
// Only the second rejection should be reported, since the first
// one was eventually handled.
jasmine.objectContaining({
message:
'Unhandled promise rejection: rejection suite 2 thrown'
})
]
})
);
});
});
});
describe("When the unhandled rejection event doesn't have a promise", function() {
it('reports the rejection', async function() {
const global = mockGlobal();
env = new privateUnderTest.Env({ global });
env.configure({ detectLateRejectionHandling: true });
const reporter = jasmine.createSpyObj('fakeReporter', [
'specDone',
'suiteDone'
]);
env.addReporter(reporter);
env.describe('A suite', function() {
env.it('fails', function(specDone) {
setTimeout(function() {
dispatchErrorEvent(global, 'unhandledrejection', {
reason: 'fail'
});
specDone();
});
});
});
await env.execute();
expect(reporter.specDone).toHaveFailedExpectationsForRunnable(
'A suite fails',
['Unhandled promise rejection: fail thrown']
);
});
});
});
});
it('works when the suite is run multiple times', async function() {
const global = mockGlobal();
env.cleanup_();
env = new privateUnderTest.Env({ global });
env.configure({ autoCleanClosures: false });
const reporter = jasmine.createSpyObj('fakeReporter', ['specDone']);
env.addReporter(reporter);
env.it('fails', function(specDone) {
setTimeout(function() {
dispatchErrorEvent(global, 'error', { error: 'fail' });
specDone();
});
});
await env.execute();
reporter.specDone.calls.reset();
await env.execute();
expect(reporter.specDone).toHaveFailedExpectationsForRunnable('fails', [
'fail thrown'
]);
});
describe('#spyOnGlobalErrorsAsync', function() {
const leftInstalledMessage =
'Global error spy was not uninstalled. ' +
'(Did you forget to await the return value of spyOnGlobalErrorsAsync?)';
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 passingResult = resultForRunable(
reporter.specDone,
'a passing spec'
);
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() {
env.configure({ random: false });
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() {
env.configure({ random: false });
env.describe('Suite 1', function() {
env.afterAll(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')
);
}
});
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/
);
});
});
function browserEventMethods() {
return {
listeners_: { error: [], unhandledrejection: [], rejectionhandled: [] },
addEventListener(eventName, listener) {
this.listeners_[eventName].push(listener);
},
removeEventListener(eventName, listener) {
this.listeners_[eventName] = this.listeners_[eventName].filter(
l => l !== listener
);
}
};
}
function dispatchErrorEvent(global, eventName, event) {
expect(global.listeners_[eventName].length)
.withContext(`number of ${eventName} listeners`)
.toBeGreaterThan(0);
for (const l of global.listeners_[eventName]) {
l(event);
}
}
});