mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
46b3c3e4ae
* Use `this` inside invokeGuardedCallback It's slightly odd but that's exactly how our www fork works. Might as well do it in the open source version to make it clear we rely on context here. * Move invokeGuardedCallback into a separate file This lets us introduce forks for it. * Add a www fork for invokeGuardedCallback * Fix Flow
213 lines
5.4 KiB
JavaScript
213 lines
5.4 KiB
JavaScript
/**
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @emails react-core
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var ReactErrorUtils;
|
|
|
|
describe('ReactErrorUtils', () => {
|
|
beforeEach(() => {
|
|
// TODO: can we express this test with only public API?
|
|
ReactErrorUtils = require('shared/ReactErrorUtils').default;
|
|
});
|
|
|
|
it(`it should rethrow caught errors`, () => {
|
|
var err = new Error('foo');
|
|
var callback = function() {
|
|
throw err;
|
|
};
|
|
ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(
|
|
'foo',
|
|
callback,
|
|
null,
|
|
);
|
|
expect(ReactErrorUtils.hasCaughtError()).toBe(false);
|
|
expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);
|
|
});
|
|
|
|
it(`should call the callback the passed arguments`, () => {
|
|
var callback = jest.fn();
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
callback,
|
|
null,
|
|
'arg1',
|
|
'arg2',
|
|
);
|
|
expect(callback).toBeCalledWith('arg1', 'arg2');
|
|
});
|
|
|
|
it(`should call the callback with the provided context`, () => {
|
|
var context = {didCall: false};
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
function() {
|
|
this.didCall = true;
|
|
},
|
|
context,
|
|
);
|
|
expect(context.didCall).toBe(true);
|
|
});
|
|
|
|
it(`should catch errors`, () => {
|
|
const error = new Error();
|
|
const returnValue = ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
function() {
|
|
throw error;
|
|
},
|
|
null,
|
|
'arg1',
|
|
'arg2',
|
|
);
|
|
expect(returnValue).toBe(undefined);
|
|
expect(ReactErrorUtils.hasCaughtError()).toBe(true);
|
|
expect(ReactErrorUtils.clearCaughtError()).toBe(error);
|
|
});
|
|
|
|
it(`should return false from clearCaughtError if no error was thrown`, () => {
|
|
var callback = jest.fn();
|
|
ReactErrorUtils.invokeGuardedCallback('foo', callback, null);
|
|
expect(ReactErrorUtils.hasCaughtError()).toBe(false);
|
|
expect(ReactErrorUtils.clearCaughtError).toThrow('no error was captured');
|
|
});
|
|
|
|
it(`can nest with same debug name`, () => {
|
|
const err1 = new Error();
|
|
let err2;
|
|
const err3 = new Error();
|
|
let err4;
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
function() {
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
function() {
|
|
throw err1;
|
|
},
|
|
null,
|
|
);
|
|
err2 = ReactErrorUtils.clearCaughtError();
|
|
throw err3;
|
|
},
|
|
null,
|
|
);
|
|
err4 = ReactErrorUtils.clearCaughtError();
|
|
|
|
expect(err2).toBe(err1);
|
|
expect(err4).toBe(err3);
|
|
});
|
|
|
|
it(`handles nested errors`, () => {
|
|
const err1 = new Error();
|
|
let err2;
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
function() {
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
'foo',
|
|
function() {
|
|
throw err1;
|
|
},
|
|
null,
|
|
);
|
|
err2 = ReactErrorUtils.clearCaughtError();
|
|
},
|
|
null,
|
|
);
|
|
// Returns null because inner error was already captured
|
|
expect(ReactErrorUtils.hasCaughtError()).toBe(false);
|
|
|
|
expect(err2).toBe(err1);
|
|
});
|
|
|
|
it('handles nested errors in separate renderers', () => {
|
|
const ReactErrorUtils1 = require('shared/ReactErrorUtils').default;
|
|
jest.resetModules();
|
|
const ReactErrorUtils2 = require('shared/ReactErrorUtils').default;
|
|
expect(ReactErrorUtils1).not.toEqual(ReactErrorUtils2);
|
|
|
|
let ops = [];
|
|
|
|
ReactErrorUtils1.invokeGuardedCallback(
|
|
null,
|
|
() => {
|
|
ReactErrorUtils2.invokeGuardedCallback(
|
|
null,
|
|
() => {
|
|
throw new Error('nested error');
|
|
},
|
|
null,
|
|
);
|
|
// ReactErrorUtils2 should catch the error
|
|
ops.push(ReactErrorUtils2.hasCaughtError());
|
|
ops.push(ReactErrorUtils2.clearCaughtError().message);
|
|
},
|
|
null,
|
|
);
|
|
|
|
// ReactErrorUtils1 should not catch the error
|
|
ops.push(ReactErrorUtils1.hasCaughtError());
|
|
|
|
expect(ops).toEqual([true, 'nested error', false]);
|
|
});
|
|
|
|
if (!__DEV__) {
|
|
// jsdom doesn't handle this properly, but Chrome and Firefox should. Test
|
|
// this with a fixture.
|
|
it('catches null values', () => {
|
|
ReactErrorUtils.invokeGuardedCallback(
|
|
null,
|
|
function() {
|
|
throw null; // eslint-disable-line no-throw-literal
|
|
},
|
|
null,
|
|
);
|
|
expect(ReactErrorUtils.hasCaughtError()).toBe(true);
|
|
expect(ReactErrorUtils.clearCaughtError()).toBe(null);
|
|
});
|
|
}
|
|
|
|
it(`can be shimmed`, () => {
|
|
const ops = [];
|
|
jest.resetModules();
|
|
jest.mock(
|
|
'shared/invokeGuardedCallback',
|
|
() =>
|
|
function invokeGuardedCallback(name, func, context, a) {
|
|
ops.push(a);
|
|
try {
|
|
func.call(context, a);
|
|
} catch (error) {
|
|
this._hasCaughtError = true;
|
|
this._caughtError = error;
|
|
}
|
|
},
|
|
);
|
|
ReactErrorUtils = require('shared/ReactErrorUtils').default;
|
|
|
|
try {
|
|
var err = new Error('foo');
|
|
var callback = function() {
|
|
throw err;
|
|
};
|
|
ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(
|
|
'foo',
|
|
callback,
|
|
null,
|
|
'somearg',
|
|
);
|
|
expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err);
|
|
expect(ops).toEqual(['somearg']);
|
|
} finally {
|
|
jest.unmock('shared/invokeGuardedCallback');
|
|
}
|
|
});
|
|
});
|