InspectedElement Test
InspectedElement Test
describe('InspectedElement', () => {
let React;
let ReactDOM;
let ReactDOMClient;
let PropTypes;
let TestRenderer: ReactTestRenderer;
let bridge: FrontendBridge;
let store: Store;
let utils;
let BridgeContext;
let InspectedElementContext;
let InspectedElementContextController;
let SettingsContextController;
let StoreContext;
let TreeContextController;
let TestUtilsAct;
let TestRendererAct;
let legacyRender;
let testRendererInstance;
let ErrorBoundary;
let errorBoundaryInstance;
global.IS_REACT_ACT_ENVIRONMENT = true;
beforeEach(() => {
utils = require('./utils');
utils.beforeEachProfiling();
legacyRender = utils.legacyRender;
bridge = global.bridge;
store = global.store;
store.collapseNodesByDefault = false;
React = require('react');
ReactDOM = require('react-dom');
ReactDOMClient = require('react-dom/client');
PropTypes = require('prop-types');
TestUtilsAct = require('internal-test-utils').act;
TestRenderer = utils.requireTestRenderer();
TestRendererAct = require('internal-test-utils').act;
BridgeContext =
require('react-devtools-shared/src/devtools/views/context').BridgeContext;
InspectedElementContext =
require('react-devtools-shared/src/devtools/views/Components/InspectedElementContex
t').InspectedElementContext;
InspectedElementContextController =
require('react-devtools-shared/src/devtools/views/Components/InspectedElementContex
t').InspectedElementContextController;
SettingsContextController =
require('react-devtools-shared/src/devtools/views/Settings/SettingsContext').Settin
gsContextController;
StoreContext =
require('react-devtools-shared/src/devtools/views/context').StoreContext;
TreeContextController =
require('react-devtools-shared/src/devtools/views/Components/TreeContext').TreeCont
extController;
errorBoundaryInstance = null;
if (this.state.error) {
return null;
}
return this.props.children;
}
};
});
afterEach(() => {
jest.restoreAllMocks();
});
const Contexts = ({
children,
defaultSelectedElementID = null,
defaultSelectedElementIndex = null,
}) => (
<BridgeContext.Provider value={bridge}>
<StoreContext.Provider value={store}>
<SettingsContextController>
<TreeContextController
defaultSelectedElementID={defaultSelectedElementID}
defaultSelectedElementIndex={defaultSelectedElementIndex}>
<React.Suspense fallback="Loading...">
<InspectedElementContextController>
{children}
</InspectedElementContextController>
</React.Suspense>
</TreeContextController>
</SettingsContextController>
</StoreContext.Provider>
</BridgeContext.Provider>
);
function useInspectedElement() {
const {inspectedElement} = React.useContext(InspectedElementContext);
return inspectedElement;
}
function useInspectElementPath() {
const {inspectPaths} = React.useContext(InspectedElementContext);
return inspectPaths;
}
function noop() {}
function Suspender() {
useCustomHook();
inspectedElement = useInspectedElement();
didFinish = true;
return null;
}
return inspectedElement;
}
const cases = [
{
// <LegacyContextConsumer />
index: 1,
shouldHaveLegacyContext: true,
},
{
// <BoolContext.Consumer>
index: 2,
shouldHaveLegacyContext: false,
},
{
// <ModernContextType />
index: 3,
shouldHaveLegacyContext: false,
},
{
// <ModernContext.Consumer>
index: 5,
shouldHaveLegacyContext: false,
},
];
expect(inspectedElement.context).not.toBe(null);
expect(inspectedElement.hasLegacyContext).toBe(shouldHaveLegacyContext);
}
});
it('should poll for updates for the currently selected element', async () => {
const Example = () => null;
await utils.actAsync(
() => legacyRender(<Example a={2} b="def" />, container),
false,
);
// TODO (cache)
// This test only passes if both the check-for-updates poll AND the test
renderer.update() call are included below.
// It seems like either one of the two should be sufficient but:
// 1. Running only check-for-updates schedules a transition that React never
renders.
// 2. Running only renderer.update() loads stale data (first props)
it('should not re-render a function with hooks if it did not update since it was
last inspected', async () => {
let targetRenderCount = 0;
targetRenderCount = 0;
targetRenderCount = 0;
await utils.actAsync(
() =>
legacyRender(
<Wrapper>
<Target a={2} b="def" />
</Wrapper>,
container,
),
false,
);
// Target should have been rendered once (by ReactDOM) and once by DevTools for
inspection.
inspectedElement = await inspectElementAtIndex(1);
expect(targetRenderCount).toBe(2);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"a": 2,
"b": "def",
}
`);
});
// See github.com/facebook/react/issues/22241#issuecomment-931299972
it('should properly recover from a cache miss on the frontend', async () => {
let targetRenderCount = 0;
targetRenderCount = 0;
// This test causes an intermediate error to be logged but we can ignore it.
jest.spyOn(console, 'error').mockImplementation(() => {});
// Clear the frontend cache to simulate DevTools being closed and re-opened.
// The backend still thinks the most recently-inspected element is still
cached,
// so the frontend needs to tell it to resend a full value.
// We can verify this by asserting that the component is re-rendered again.
utils.withErrorsOrWarningsIgnored(
['An update to %s inside a test was not wrapped in act'],
() => {
testRendererInstance = TestRenderer.create(null, {
unstable_isConcurrent: true,
});
},
);
const {
clearCacheForTests,
} = require('react-devtools-shared/src/inspectedElementMutableSource');
clearCacheForTests();
targetRenderCount = 0;
inspectedElement = await inspectElementAtIndex(1);
expect(targetRenderCount).toBe(1);
expect(inspectedElement).toEqual(prevInspectedElement);
});
expect(inspectedElement).not.toBe(null);
expect(targetRenderCount).toBe(2);
expect(console.error).toHaveBeenCalledTimes(1);
expect(console.info).toHaveBeenCalledTimes(1);
expect(console.log).toHaveBeenCalledTimes(1);
expect(console.warn).toHaveBeenCalledTimes(1);
});
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"boolean_false": false,
"boolean_true": true,
"float": 1.23,
"infinity": Infinity,
"integer_one": 1,
"integer_zero": 0,
"nan": NaN,
"string": "abc",
"string_empty": "",
"value_null": null,
"value_undefined": undefined,
}
`);
});
class Class {
anonymousFunction = () => {};
}
const instance = new Class();
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"anonymous_fn": Dehydrated {
"preview_short": ƒ () {},
"preview_long": ƒ () {},
},
"array_buffer": Dehydrated {
"preview_short": ArrayBuffer(3),
"preview_long": ArrayBuffer(3),
},
"array_of_arrays": [
Dehydrated {
"preview_short": Array(2),
"preview_long": [Array(3), Array(0)],
},
],
"big_int": Dehydrated {
"preview_short": 123n,
"preview_long": 123n,
},
"bound_fn": Dehydrated {
"preview_short": ƒ bound exampleFunction() {},
"preview_long": ƒ bound exampleFunction() {},
},
"data_view": Dehydrated {
"preview_short": DataView(3),
"preview_long": DataView(3),
},
"date": Dehydrated {
"preview_short": Tue Dec 31 2019 23:42:42 GMT+0000 (Coordinated Universal
Time),
"preview_long": Tue Dec 31 2019 23:42:42 GMT+0000 (Coordinated Universal
Time),
},
"fn": Dehydrated {
"preview_short": ƒ exampleFunction() {},
"preview_long": ƒ exampleFunction() {},
},
"html_element": Dehydrated {
"preview_short": <div />,
"preview_long": <div />,
},
"immutable": {
"0": Dehydrated {
"preview_short": Array(2),
"preview_long": ["a", List(3)],
},
"1": Dehydrated {
"preview_short": Array(2),
"preview_long": ["b", 123],
},
"2": Dehydrated {
"preview_short": Array(2),
"preview_long": ["c", Map(2)],
},
},
"map": {
"0": Dehydrated {
"preview_short": Array(2),
"preview_long": ["name", "Brian"],
},
"1": Dehydrated {
"preview_short": Array(2),
"preview_long": ["food", "sushi"],
},
},
"map_of_maps": {
"0": Dehydrated {
"preview_short": Array(2),
"preview_long": ["first", Map(2)],
},
"1": Dehydrated {
"preview_short": Array(2),
"preview_long": ["second", Map(2)],
},
},
"object_of_objects": {
"inner": Dehydrated {
"preview_short": {…},
"preview_long": {boolean: true, number: 123, string: "abc"},
},
},
"object_with_symbol": {
"Symbol(name)": "hello",
},
"proxy": Dehydrated {
"preview_short": ƒ () {},
"preview_long": ƒ () {},
},
"react_element": Dehydrated {
"preview_short": <span />,
"preview_long": <span />,
},
"regexp": Dehydrated {
"preview_short": /abc/giu,
"preview_long": /abc/giu,
},
"set": {
"0": "abc",
"1": 123,
},
"set_of_sets": {
"0": Dehydrated {
"preview_short": Set(3),
"preview_long": Set(3) {"a", "b", "c"},
},
"1": Dehydrated {
"preview_short": Set(3),
"preview_long": Set(3) {1, 2, 3},
},
},
"symbol": Dehydrated {
"preview_short": Symbol(symbol),
"preview_long": Symbol(symbol),
},
"typed_array": {
"0": 100,
"1": -100,
"2": 0,
},
}
`);
});
function* generator() {
throw Error('Should not be consumed!');
}
const object = {
name: 'blah',
hasOwnProperty: true,
};
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"object": {
"hasOwnProperty": true,
"name": "blah",
},
}
`);
});
it('should support custom objects with enumerable properties and getters', async
() => {
class CustomData {
_number = 42;
get number() {
return this._number;
}
set number(value) {
this._number = value;
}
}
it('should allow component prop value and value`s prototype has same name
params.', async () => {
const testData = Object.create(
{
a: undefined,
b: Infinity,
c: NaN,
d: 'normal',
},
{
a: {
value: undefined,
writable: true,
enumerable: true,
configurable: true,
},
b: {
value: Infinity,
writable: true,
enumerable: true,
configurable: true,
},
c: {
value: NaN,
writable: true,
enumerable: true,
configurable: true,
},
d: {
value: 'normal',
writable: true,
enumerable: true,
configurable: true,
},
},
);
const Example = ({data}) => null;
const container = document.createElement('div');
await utils.actAsync(() =>
legacyRender(<Example data={testData} />, container),
);
it('should not dehydrate nested values until explicitly requested', async () => {
const Example = () => {
const [state] = React.useState({
foo: {
bar: {
baz: 'hi',
},
},
});
return state.foo.bar.baz;
};
const container = document.createElement('div');
await utils.actAsync(() =>
legacyRender(
<Example
nestedObject={{
a: {
b: {
c: [
{
d: {
e: {},
},
},
],
},
},
}}
/>,
container,
),
);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": Dehydrated {
"preview_short": {…},
"preview_long": {b: {…}},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"c": Dehydrated {
"preview_short": Array(1),
"preview_long": [{…}],
},
},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"c": [
{
"d": Dehydrated {
"preview_short": {…},
"preview_long": {e: {…}},
},
},
],
},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"c": [
{
"d": {
"e": {},
},
},
],
},
},
},
}
`);
expect(inspectedElement.hooks).toMatchInlineSnapshot(`
[
{
"hookSource": {
"columnNumber": "removed by Jest serializer",
"fileName": "react-devtools-shared/src/__tests__/inspectedElement-
test.js",
"functionName": "Example",
"lineNumber": "removed by Jest serializer",
},
"id": 0,
"isStateEditable": true,
"name": "State",
"subHooks": [],
"value": {
"foo": {
"bar": Dehydrated {
"preview_short": {…},
"preview_long": {baz: "hi"},
},
},
},
},
]
`);
expect(inspectedElement.hooks).toMatchInlineSnapshot(`
[
{
"hookSource": {
"columnNumber": "removed by Jest serializer",
"fileName": "react-devtools-shared/src/__tests__/inspectedElement-
test.js",
"functionName": "Example",
"lineNumber": "removed by Jest serializer",
},
"id": 0,
"isStateEditable": true,
"name": "State",
"subHooks": [],
"value": {
"foo": {
"bar": {
"baz": "hi",
},
},
},
},
]
`);
});
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"set_of_sets": {
"0": Dehydrated {
"preview_short": Set(3),
"preview_long": Set(3) {1, 2, 3},
},
"1": Dehydrated {
"preview_short": Set(3),
"preview_long": Set(3) {"a", "b", "c"},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"set_of_sets": {
"0": {
"0": 1,
"1": 2,
"2": 3,
},
"1": Dehydrated {
"preview_short": Set(3),
"preview_long": Set(3) {"a", "b", "c"},
},
},
}
`);
});
it('should include updates for nested values that were previously hydrated',
async () => {
const Example = () => null;
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": Dehydrated {
"preview_short": {…},
"preview_long": {b: {…}, value: 1},
},
"c": Dehydrated {
"preview_short": {…},
"preview_long": {d: {…}, value: 1},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"value": 1,
},
"value": 1,
},
"c": {
"d": {
"e": Dehydrated {
"preview_short": {…},
"preview_long": {value: 1},
},
"value": 1,
},
"value": 1,
},
},
}
`);
// Wait for pending poll-for-update and then update inspected element data.
jest.runOnlyPendingTimers();
await Promise.resolve();
inspectedElement = await inspectElementAtIndex(0);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"value": 2,
},
"value": 2,
},
"c": {
"d": {
"e": Dehydrated {
"preview_short": {…},
"preview_long": {value: 2},
},
"value": 2,
},
"value": 2,
},
},
}
`);
});
it('should return a full update if a path is inspected for an object that has
other pending changes', async () => {
const Example = () => null;
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": Dehydrated {
"preview_short": {…},
"preview_long": {b: {…}, value: 1},
},
"c": Dehydrated {
"preview_short": {…},
"preview_long": {d: {…}, value: 1},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"value": 1,
},
"value": 1,
},
"c": Dehydrated {
"preview_short": {…},
"preview_long": {d: {…}, value: 1},
},
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"value": 2,
},
"value": 2,
},
"c": {
"d": {
"e": Dehydrated {
"preview_short": {…},
"preview_long": {value: 2},
},
"value": 2,
},
"value": 2,
},
},
}
`);
});
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": Dehydrated {
"preview_short": {…},
"preview_long": {b: {…}, value: 1},
},
"value": 1,
},
}
`);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"nestedObject": {
"a": {
"b": {
"value": 2,
},
"value": 2,
},
"value": 2,
},
}
`);
});
it('should inspect hooks for components that only use context', async () => {
const Context = React.createContext(true);
const Example = () => {
const value = React.useContext(Context);
return value;
};
const nestedObject = {
a: {
value: 1,
b: {
value: 1,
c: {
value: 1,
},
},
},
};
// Should store the whole value (not just the hydrated parts)
storeAsGlobal(['props', 'nestedObject']);
jest.runOnlyPendingTimers();
expect(console.log).toHaveBeenCalledWith('$reactTemp0');
expect(global.$reactTemp0).toBe(nestedObject);
console.log.mockReset();
// Should store the nested property specified (not just the outer value)
storeAsGlobal(['props', 'nestedObject', 'a', 'b']);
jest.runOnlyPendingTimers();
expect(console.log).toHaveBeenCalledWith('$reactTemp1');
expect(global.$reactTemp1).toBe(nestedObject.a.b);
});
const nestedObject = {
a: {
value: 1,
b: {
value: 1,
c: {
value: 1,
},
},
},
};
// Should copy the whole value (not just the hydrated parts)
copyPath(['props', 'nestedObject']);
jest.runOnlyPendingTimers();
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
JSON.stringify(nestedObject, undefined, 2),
);
global.mockClipboardCopy.mockReset();
// Should copy the nested property specified (not just the outer value)
copyPath(['props', 'nestedObject', 'a', 'b']);
jest.runOnlyPendingTimers();
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
JSON.stringify(nestedObject.a.b, undefined, 2),
);
});
// Should copy the whole value (not just the hydrated parts)
copyPath(['props']);
jest.runOnlyPendingTimers();
// Should not error despite lots of unserialized values.
global.mockClipboardCopy.mockReset();
// Should copy the nested property specified (not just the outer value)
copyPath(['props', 'bigInt']);
jest.runOnlyPendingTimers();
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
JSON.stringify('123n', undefined, 2),
);
global.mockClipboardCopy.mockReset();
// Should copy the nested property specified (not just the outer value)
copyPath(['props', 'typedArray']);
jest.runOnlyPendingTimers();
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
JSON.stringify({0: 100, 1: -100, 2: 0}, undefined, 2),
);
});
function useDebuggableHook() {
React.useDebugValue({foo: 2});
React.useState(1);
return 1;
}
function DisplayedComplexValue() {
useDebuggableHook();
return null;
}
await utils.actAsync(() =>
legacyRender(<DisplayedComplexValue />, container),
);
// See github.com/facebook/react/issues/21654
it('should support Proxies that dont return an iterator', async () => {
const Example = () => null;
const proxy = new Proxy(
{},
{
get: (target, prop, receiver) => {
target[prop] = value => {};
return target[prop];
},
},
);
expect(inspectedElement.props).toMatchInlineSnapshot(`
{
"proxy": {
"$$typeof": Dehydrated {
"preview_short": ƒ () {},
"preview_long": ƒ () {},
},
"Symbol(Symbol.iterator)": Dehydrated {
"preview_short": ƒ () {},
"preview_long": ƒ () {},
},
"constructor": Dehydrated {
"preview_short": ƒ () {},
"preview_long": ƒ () {},
},
},
}
`);
});
// Select/inspect element
let inspectedElement = await inspectElementAtIndex(0);
expect(inspectedElement).toMatchInlineSnapshot(`
{
"context": null,
"events": undefined,
"hooks": null,
"id": 2,
"owners": null,
"props": {},
"rootType": "render()",
"state": null,
}
`);
it('should gracefully surface backend errors on the frontend rather than timing
out', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
if (shouldThrow) {
throw Error('Expected');
} else {
return count;
}
};
shouldThrow = true;
expect(value).toBe(null);
describe('$r', () => {
it('should support function components', async () => {
const Example = () => {
const [count] = React.useState(1);
return count;
};
await inspectElementAtIndex(0);
expect(global.$r).toMatchInlineSnapshot(`
{
"hooks": [
{
"hookSource": {
"columnNumber": "removed by Jest serializer",
"fileName": "react-devtools-shared/src/__tests__/inspectedElement-
test.js",
"functionName": "Example",
"lineNumber": "removed by Jest serializer",
},
"id": 0,
"isStateEditable": true,
"name": "State",
"subHooks": [],
"value": 1,
},
],
"props": {
"a": 1,
"b": "abc",
},
"type": [Function],
}
`);
});
await inspectElementAtIndex(0);
expect(global.$r).toMatchInlineSnapshot(`
{
"hooks": [
{
"hookSource": {
"columnNumber": "removed by Jest serializer",
"fileName": "react-devtools-shared/src/__tests__/inspectedElement-
test.js",
"functionName": "Example",
"lineNumber": "removed by Jest serializer",
},
"id": 0,
"isStateEditable": true,
"name": "State",
"subHooks": [],
"value": 1,
},
],
"props": {
"a": 1,
"b": "abc",
},
"type": [Function],
}
`);
});
await inspectElementAtIndex(0);
expect(global.$r).toMatchInlineSnapshot(`
{
"hooks": [
{
"hookSource": {
"columnNumber": "removed by Jest serializer",
"fileName": "react-devtools-shared/src/__tests__/inspectedElement-
test.js",
"functionName": "Example",
"lineNumber": "removed by Jest serializer",
},
"id": 0,
"isStateEditable": true,
"name": "State",
"subHooks": [],
"value": 1,
},
],
"props": {
"a": 1,
"b": "abc",
},
"type": [Function],
}
`);
});
it('should support class components', async () => {
class Example extends React.Component {
state = {
count: 0,
};
render() {
return null;
}
}
await inspectElementAtIndex(0);
expect(global.$r.props).toMatchInlineSnapshot(`
{
"a": 1,
"b": "abc",
}
`);
expect(global.$r.state).toMatchInlineSnapshot(`
{
"count": 0,
}
`);
});
});
function Suspender({target}) {
const inspectedElement = useInspectedElement();
errors = inspectedElement.errors;
warnings = inspectedElement.warnings;
return null;
}
let root;
await utils.actAsync(() => {
root = TestRenderer.create(
<Contexts
defaultSelectedElementID={id}
defaultSelectedElementIndex={index}>
<React.Suspense fallback={null}>
<Suspender target={id} />
</React.Suspense>
</Contexts>,
{unstable_isConcurrent: true},
);
}, false);
await utils.actAsync(() => {
root.unmount();
}, false);
const {
clearErrorsAndWarnings,
} = require('react-devtools-shared/src/backendAPI');
clearErrorsAndWarnings({bridge, store});
let data = [
await getErrorsAndWarningsForElementAtIndex(0),
await getErrorsAndWarningsForElementAtIndex(1),
];
expect(data).toMatchInlineSnapshot(`
[
{
"errors": [
[
"test-only: render error #1",
1,
],
],
"warnings": [
[
"test-only: render warning #1",
1,
],
],
},
{
"errors": [
[
"test-only: render error #2",
1,
],
],
"warnings": [],
},
]
`);
data = [
await getErrorsAndWarningsForElementAtIndex(0),
await getErrorsAndWarningsForElementAtIndex(1),
];
expect(data).toMatchInlineSnapshot(`
[
{
"errors": [
[
"test-only: render error #1",
1,
],
],
"warnings": [],
},
{
"errors": [
[
"test-only: render error #2",
1,
],
],
"warnings": [],
},
]
`);
});
const {
clearErrorsForElement,
} = require('react-devtools-shared/src/backendAPI');
clearErrorsForElement({bridge, id, rendererID});
let data = [
await getErrorsAndWarningsForElementAtIndex(0),
await getErrorsAndWarningsForElementAtIndex(1),
];
expect(data).toMatchInlineSnapshot(`
[
{
"errors": [
[
"test-only: render error #1",
1,
],
],
"warnings": [
[
"test-only: render warning #1",
1,
],
],
},
{
"errors": [],
"warnings": [
[
"test-only: render warning #2",
1,
],
],
},
]
`);
data = [
await getErrorsAndWarningsForElementAtIndex(0),
await getErrorsAndWarningsForElementAtIndex(1),
];
expect(data).toMatchInlineSnapshot(`
[
{
"errors": [],
"warnings": [
[
"test-only: render warning #1",
1,
],
],
},
{
"errors": [],
"warnings": [
[
"test-only: render warning #2",
1,
],
],
},
]
`);
});
});
ARTCurrentMode.setCurrent(ArtSVGMode);
const {Surface, Group} = ReactArt;
function Child() {
return (
<Surface width={1} height={1}>
<Group />
</Surface>
);
}
function App() {
return <Child />;
}
// Inspect <ErrorBoundary /> and see that we cannot toggle error state
// on error boundary itself
let inspectedElement = await inspect(0);
expect(inspectedElement.canToggleError).toBe(false);
expect(inspectedElement.targetErrorBoundaryID).toBe(null);
consoleErrorMock.mockRestore();
consoleWarnMock.mockRestore();
await toggleError(false);