Files
react-native/Libraries/LogBox/UI/__tests__/LogBoxContainer-test.js
T
Rick Hanlon 0825c2b2e7 LogBox - Add syntax error handling
Summary:
This diff adds handling for syntax errors.

## Strategy
To do this we introduce a new log level type syntax, giving us these levels with semantics:
- `warn` - console warns, show collapsed, dismissible
- `error` - console errors, show collapsed, dismissible
- `fatal` - thrown exceptions, show expanded, not dismissible
- `syntax` - thrown exceptions for invalid syntax, show expanded, not dismissible

Syntax errors shows expanded, covers all other errors, and are only dismissible when the syntax error is fixed and updated with Fast Refresh. Once the syntax error is fixed, it reveals any previously covered fatals, errors, or warnings behind it

In many ways, this makes syntax errors the highest level error.

## Visuals
Syntax errors also have their own display formatting. Stack traces for syntax errors don't make sense, so we don't show them. Instead, we show the syntax error message and a code frame for the error.

The code frame is also updated so that is doesn't wrap and is horizontally scrollable, making it easier to read.

## Detecting syntax errors

To detect syntax errors we've updated `LogBoxData.addException` to call the parse function `parseLogBoxException`. This method will perform a regex on the error message to detect:

- file name
- location
- error message
- codeframe

If this regex fails for any reason to find all four parts, we'll fall back to a fatal. Over time we'll update this regex to be more robust and handle more cases we've missed.

Changelog: [Internal]

Reviewed By: motiz88

Differential Revision: D18278862

fbshipit-source-id: 59069aba38a27c44787e5248b2973c3a345c4a0a
2019-11-01 16:08:49 -07:00

279 lines
6.6 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @emails oncall+react_native
* @flow
*/
'use strict';
const React = require('react');
const LogBoxContainer = require('../LogBoxContainer').default;
const LogBoxLog = require('../../Data/LogBoxLog').default;
const render = require('../../../../jest/renderer');
describe('LogBoxContainer', () => {
it('should render null with no logs', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={new Set()}
/>,
);
expect(output).toMatchSnapshot();
});
it('should render the latest warning', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={
new Set([
new LogBoxLog(
'warn',
{
content: 'Some kind of message',
substitutions: [],
},
[],
'Some kind of message',
[],
),
new LogBoxLog(
'warn',
{
content: 'Some kind of message (latest)',
substitutions: [],
},
[],
'Some kind of message (latest)',
[],
),
])
}
/>,
);
expect(output).toMatchSnapshot();
});
it('should render the latest error', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={
new Set([
new LogBoxLog(
'error',
{
content: 'Some kind of message',
substitutions: [],
},
[],
'Some kind of message',
[],
),
new LogBoxLog(
'error',
{
content: 'Some kind of message (latest)',
substitutions: [],
},
[],
'Some kind of message (latest)',
[],
),
])
}
/>,
);
expect(output).toMatchSnapshot();
});
it('should render both an error and warning', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={
new Set([
new LogBoxLog(
'warn',
{
content: 'Some kind of message',
substitutions: [],
},
[],
'Some kind of message',
[],
),
new LogBoxLog(
'error',
{
content: 'Some kind of message (latest)',
substitutions: [],
},
[],
'Some kind of message (latest)',
[],
),
])
}
/>,
);
expect(output).toMatchSnapshot();
});
it('should render fatal before warn and error', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={
new Set([
new LogBoxLog(
'fatal',
{
content: 'Some kind of fatal message',
substitutions: [],
},
[],
'Some kind of fatal message',
[],
),
new LogBoxLog(
'warn',
{
content: 'Some kind of message',
substitutions: [],
},
[],
'Some kind of message',
[],
),
new LogBoxLog(
'error',
{
content: 'Some kind of message (latest)',
substitutions: [],
},
[],
'Some kind of message (latest)',
[],
),
])
}
/>,
);
expect(output).toMatchSnapshot();
});
it('should render most recent fatal', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={
new Set([
new LogBoxLog(
'fatal',
{
content: 'Should not be selected',
substitutions: [],
},
[],
'Some kind of fatal message',
[],
),
new LogBoxLog(
'fatal',
{
content: 'Should be selected',
substitutions: [],
},
[],
'Some kind of message',
[],
),
])
}
/>,
);
expect(output).toMatchSnapshot();
});
it('should render most recent syntax error', () => {
const output = render.shallowRender(
<LogBoxContainer
onDismiss={() => {}}
onDismissWarns={() => {}}
onDismissErrors={() => {}}
logs={
new Set([
new LogBoxLog(
'syntax',
{
content: 'Should not be selected',
substitutions: [],
},
[],
'Some kind of syntax error message',
[],
{
fileName:
'/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
location: '(199:0)',
content: ` 197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
},
),
new LogBoxLog(
'syntax',
{
content: 'Should be selected',
substitutions: [],
},
[],
'Some kind of syntax error message',
[],
{
fileName:
'/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
location: '(199:0)',
content: ` 197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
},
),
])
}
/>,
);
expect(output).toMatchSnapshot();
});
});