Files
react-native/Libraries/LogBox/Data/__tests__/parseLogBoxLog-test.js
T
Sam Zhou a0ee6fae5e Annotate empty arrays in xplat
Summary: Changelog: [Internal]

Differential Revision: D40410427

fbshipit-source-id: f819fcb00673e19b21aecb383541850436805a0e
2022-10-17 12:40:57 -07:00

1235 lines
41 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
'use strict';
import type {StackFrame} from '../../../Core/NativeExceptionsManager';
const {parseLogBoxException, parseLogBoxLog} = require('../parseLogBoxLog');
describe('parseLogBoxLog', () => {
it('parses strings', () => {
expect(parseLogBoxLog(['A'])).toEqual({
componentStack: [],
category: 'A',
message: {
content: 'A',
substitutions: [],
},
});
});
it('parses strings with arguments', () => {
expect(parseLogBoxLog(['A', 'B', 'C'])).toEqual({
componentStack: [],
category: 'A B C',
message: {
content: 'A B C',
substitutions: [],
},
});
});
it('parses formatted strings', () => {
expect(parseLogBoxLog(['%s', 'A'])).toEqual({
componentStack: [],
category: '\ufeff%s',
message: {
content: 'A',
substitutions: [
{
length: 1,
offset: 0,
},
],
},
});
});
it('parses formatted strings with insufficient arguments', () => {
expect(parseLogBoxLog(['%s %s', 'A'])).toEqual({
componentStack: [],
category: '\ufeff%s %s',
message: {
content: 'A %s',
substitutions: [
{
length: 1,
offset: 0,
},
{
length: 2,
offset: 2,
},
],
},
});
});
it('parses formatted strings with excess arguments', () => {
expect(parseLogBoxLog(['%s', 'A', 'B'])).toEqual({
componentStack: [],
category: '\ufeff%s B',
message: {
content: 'A B',
substitutions: [
{
length: 1,
offset: 0,
},
],
},
});
});
it('treats "%s" in arguments as literals', () => {
expect(parseLogBoxLog(['%s', '%s', 'A'])).toEqual({
componentStack: [],
category: '\ufeff%s A',
message: {
content: '%s A',
substitutions: [
{
length: 2,
offset: 0,
},
],
},
});
});
it('detects a component stack in an interpolated warning', () => {
expect(
parseLogBoxLog([
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s%s',
'\n\nCheck the render method of `Container(Component)`.',
'\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
]),
).toEqual({
componentStack: [
{
content: 'MyComponent',
fileName: 'filename.js',
location: {column: -1, row: 1},
},
{
content: 'MyOtherComponent',
fileName: 'filename2.js',
location: {column: -1, row: 1},
},
],
category:
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s',
message: {
content:
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?\n\nCheck the render method of `Container(Component)`.',
substitutions: [
{
length: 52,
offset: 129,
},
],
},
});
});
it('detects a component stack in the first argument', () => {
expect(
parseLogBoxLog([
'Some kind of message\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
]),
).toEqual({
componentStack: [
{
content: 'MyComponent',
fileName: 'filename.js',
location: {column: -1, row: 1},
},
{
content: 'MyOtherComponent',
fileName: 'filename2.js',
location: {column: -1, row: 1},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the second argument', () => {
expect(
parseLogBoxLog([
'Some kind of message',
'\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
]),
).toEqual({
componentStack: [
{
content: 'MyComponent',
fileName: 'filename.js',
location: {column: -1, row: 1},
},
{
content: 'MyOtherComponent',
fileName: 'filename2.js',
location: {column: -1, row: 1},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the nth argument', () => {
expect(
parseLogBoxLog([
'Some kind of message',
'Some other kind of message',
'\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
'Some third kind of message',
]),
).toEqual({
componentStack: [
{
content: 'MyComponent',
fileName: 'filename.js',
location: {column: -1, row: 1},
},
{
content: 'MyOtherComponent',
fileName: 'filename2.js',
location: {column: -1, row: 1},
},
],
category:
'Some kind of message Some other kind of message Some third kind of message',
message: {
content:
'Some kind of message Some other kind of message Some third kind of message',
substitutions: [],
},
});
});
it('parses a transform error as a fatal', () => {
const error = {
message: 'TransformError failed to transform file.',
originalMessage: 'TransformError failed to transform file.',
name: '',
isComponentError: false,
componentStack: '',
stack: ([]: Array<StackFrame>),
id: 0,
isFatal: true,
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
isComponentError: false,
message: {
content: 'TransformError failed to transform file.',
substitutions: [],
},
stack: [],
componentStack: [],
category: 'TransformError failed to transform file.',
});
});
it('parses a babel transform syntax error', () => {
const error = {
message: `
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
originalMessage: `TransformError SyntaxError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
name: '',
isComponentError: false,
componentStack: '',
stack: ([]: Array<StackFrame>),
id: 0,
isFatal: true,
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
isComponentError: false,
codeFrame: {
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
location: {row: 199, column: 0},
content: ` 197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
},
message: {
content: "'import' and 'export' may only appear at the top level",
substitutions: [],
},
stack: [],
componentStack: [],
category: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js-199-0',
});
});
it('parses an invalid require syntax error', () => {
const error = {
message: `Unable to resolve module \`ListCellx\` from /path/to/file.js: ListCellx could not be found within the project.
If you are sure the module exists, try these steps:
1. Clear watchman watches: watchman watch-del-all
2. Delete node_modules and run yarn install
3. Reset Metro's cache: yarn start --reset-cache
4. Remove the cache: rm -rf /tmp/metro-*
\u001b[0m \u001b[90m 10 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mColor\u001b[39m from \u001b[32m'Color'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 11 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mList\u001b[39m from \u001b[32m'List'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 12 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mListCell\u001b[39m from \u001b[32m'ListCellx'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 13 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mNullState\u001b[39m from \u001b[32m'NullState'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 14 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mUnitHeader\u001b[39m from \u001b[32m'UnitHeader'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 15 | \u001b[39m\u001b[36mimport\u001b[39m fbRemoteAsset from \u001b[32m'fbRemoteAsset'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m`,
originalMessage: `Unable to resolve module \`ListCellx\` from /path/to/file.js: ListCellx could not be found within the project.
If you are sure the module exists, try these steps:
1. Clear watchman watches: watchman watch-del-all
2. Delete node_modules and run yarn install
3. Reset Metro's cache: yarn start --reset-cache
4. Remove the cache: rm -rf /tmp/metro-*
\u001b[0m \u001b[90m 10 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mColor\u001b[39m from \u001b[32m'Color'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 11 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mList\u001b[39m from \u001b[32m'List'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 12 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mListCell\u001b[39m from \u001b[32m'ListCellx'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 13 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mNullState\u001b[39m from \u001b[32m'NullState'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 14 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mUnitHeader\u001b[39m from \u001b[32m'UnitHeader'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 15 | \u001b[39m\u001b[36mimport\u001b[39m fbRemoteAsset from \u001b[32m'fbRemoteAsset'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m`,
name: '',
isComponentError: false,
componentStack: '',
stack: ([]: Array<StackFrame>),
id: 0,
isFatal: true,
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
isComponentError: false,
codeFrame: {
fileName: '/path/to/file.js',
location: null,
content: `\u001b[0m \u001b[90m 10 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mColor\u001b[39m from \u001b[32m'Color'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 11 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mList\u001b[39m from \u001b[32m'List'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 12 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mListCell\u001b[39m from \u001b[32m'ListCellx'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 13 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mNullState\u001b[39m from \u001b[32m'NullState'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 14 | \u001b[39m\u001b[36mimport\u001b[39m \u001b[33mUnitHeader\u001b[39m from \u001b[32m'UnitHeader'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m
\u001b[0m \u001b[90m 15 | \u001b[39m\u001b[36mimport\u001b[39m fbRemoteAsset from \u001b[32m'fbRemoteAsset'\u001b[39m\u001b[33m;\u001b[39m\u001b[0m`,
},
message: {
content: `ListCellx could not be found within the project.
If you are sure the module exists, try these steps:
1. Clear watchman watches: watchman watch-del-all
2. Delete node_modules and run yarn install
3. Reset Metro's cache: yarn start --reset-cache
4. Remove the cache: rm -rf /tmp/metro-*`,
substitutions: [],
},
stack: [],
componentStack: [],
category: '/path/to/file.js-1-1',
});
});
it('parses a reference error', () => {
const error = {
message: `
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
originalMessage: `TransformError ReferenceError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
name: '',
isComponentError: false,
componentStack: '',
stack: ([]: Array<StackFrame>),
id: 0,
isFatal: true,
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
isComponentError: false,
codeFrame: {
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
location: {row: 199, column: 0},
content: ` 197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
},
message: {
content: "'import' and 'export' may only appear at the top level",
substitutions: [],
},
stack: [],
componentStack: [],
category: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js-199-0',
});
});
it('parses a babel codeframe error', () => {
const error = {
message: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
Please follow the instructions at: fburl.com/rn-remote-assets
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
originalMessage: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
Please follow the instructions at: fburl.com/rn-remote-assets
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
name: '',
isComponentError: false,
componentStack: '',
stack: ([]: Array<StackFrame>),
id: 0,
isFatal: true,
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
isComponentError: false,
codeFrame: {
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
location: null,
content: ` 197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
},
message: {
content: `The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
Please follow the instructions at: fburl.com/rn-remote-assets`,
substitutions: [],
},
stack: [],
componentStack: [],
category: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js-1-1',
});
});
it('parses a babel codeframe error with ansi', () => {
const error = {
message: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
Please follow the instructions at: fburl.com/rn-remote-assets
197 | });
198 |
> 199 | export default CrashReactApp;
| ^
200 |`,
originalMessage: `TransformError RKJSModules/Apps/CrashReact/CrashReactApp.js: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
Please follow the instructions at: fburl.com/rn-remote-assets
\u001b[0m \u001b[90m 46 | \u001b[39m headline\u001b[33m=\u001b[39m\u001b[32m"CrashReact Error Boundary"\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 47 | \u001b[39m body\u001b[33m=\u001b[39m{\u001b[32m\`\${this.state.errorMessage}\`\u001b[39m}\u001b[0m\n\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 48 | \u001b[39m icon\u001b[33m=\u001b[39m{fbRemoteAsset(\u001b[32m'null_state_glyphs'\u001b[39m\u001b[33m,\u001b[39m {\u001b[0m\n\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 49 | \u001b[39m name\u001b[33m:\u001b[39m \u001b[32m'codexxx'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 50 | \u001b[39m size\u001b[33m:\u001b[39m \u001b[32m'112'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 51 | \u001b[39m })}\u001b[0m`,
name: '',
isComponentError: false,
componentStack: '',
stack: ([]: Array<StackFrame>),
id: 0,
isFatal: true,
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
isComponentError: false,
codeFrame: {
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
location: null,
content:
"\u001b[0m \u001b[90m 46 | \u001b[39m headline\u001b[33m=\u001b[39m\u001b[32m\"CrashReact Error Boundary\"\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 47 | \u001b[39m body\u001b[33m=\u001b[39m{\u001b[32m`${this.state.errorMessage}`\u001b[39m}\u001b[0m\n\u001b[0m\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 48 | \u001b[39m icon\u001b[33m=\u001b[39m{fbRemoteAsset(\u001b[32m'null_state_glyphs'\u001b[39m\u001b[33m,\u001b[39m {\u001b[0m\n\u001b[0m \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 49 | \u001b[39m name\u001b[33m:\u001b[39m \u001b[32m'codexxx'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 50 | \u001b[39m size\u001b[33m:\u001b[39m \u001b[32m'112'\u001b[39m\u001b[33m,\u001b[39m\u001b[0m\n\u001b[0m \u001b[90m 51 | \u001b[39m })}\u001b[0m",
},
message: {
content: `The first argument to \`fbRemoteAsset\` is "null_state_glyphs", but the requested asset is missing from the local metadata. Either the asset does not exist or the metadata is not up-to-date.
Please follow the instructions at: fburl.com/rn-remote-assets`,
substitutions: [],
},
stack: [],
componentStack: [],
category: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js-1-1',
});
});
it('parses an error log with `error.componentStack`', () => {
const error = {
id: 0,
isFatal: false,
isComponentError: false,
message: '### Error',
originalMessage: '### Error',
name: '',
componentStack:
'\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'error',
category: '### Error',
isComponentError: false,
message: {
content: '### Error',
substitutions: [],
},
componentStack: [
{
content: 'MyComponent',
fileName: 'filename.js',
location: {column: -1, row: 1},
},
{
content: 'MyOtherComponent',
fileName: 'filename2.js',
location: {column: -1, row: 1},
},
],
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
});
});
it('parses an error log with a component stack in the message', () => {
const error = {
id: 0,
isFatal: false,
isComponentError: false,
message:
'Some kind of message\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
originalMessage:
'Some kind of message\n in MyComponent (at filename.js:1)\n in MyOtherComponent (at filename2.js:1)',
name: '',
componentStack: null,
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'error',
isComponentError: false,
stack: [
{
collapse: false,
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
},
],
componentStack: [
{
content: 'MyComponent',
fileName: 'filename.js',
location: {column: -1, row: 1},
},
{
content: 'MyOtherComponent',
fileName: 'filename2.js',
location: {column: -1, row: 1},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('parses a fatal exception', () => {
const error = {
id: 0,
isFatal: true,
isComponentError: false,
message: '### Fatal',
originalMessage: '### Fatal',
componentStack: null,
name: '',
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'fatal',
category: '### Fatal',
isComponentError: false,
message: {
content: '### Fatal',
substitutions: [],
},
componentStack: [],
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
});
});
it('parses a render error', () => {
const error = {
id: 0,
isComponentError: true,
isFatal: true,
message: '### Fatal',
originalMessage: '### Fatal',
componentStack: null,
name: '',
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'fatal',
category: '### Fatal',
isComponentError: true,
message: {
content: '### Fatal',
substitutions: [],
},
componentStack: [],
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
});
});
it('a malformed syntax error falls back to a syntax error', () => {
const error = {
id: 0,
isFatal: true,
isComponentError: false,
// Note no code frame.
message:
"TransformError SyntaxError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)",
originalMessage:
"TransformError SyntaxError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)",
componentStack: null,
name: '',
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'syntax',
category:
"TransformError SyntaxError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)",
message: {
content:
"TransformError SyntaxError: /path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js: 'import' and 'export' may only appear at the top level (199:0)",
substitutions: [],
},
isComponentError: false,
componentStack: [],
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
});
});
describe('Handles component stack frames formatted as call stacks', () => {
// In new versions of React, the component stack frame format changed to match call stacks.
it('detects a component stack in an interpolated warning', () => {
expect(
parseLogBoxLog([
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s%s',
'\n\nCheck the render method of `MyComponent`.',
'\n at MyComponent (/path/to/filename.js:1:2)\n at MyOtherComponent\n at MyAppComponent (/path/to/app.js:100:20)',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because React isn't sending back a properly formatted stackframe.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category:
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s',
message: {
content:
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?\n\nCheck the render method of `MyComponent`.',
substitutions: [
{
length: 43,
offset: 129,
},
],
},
});
});
it('detects a component stack in the first argument', () => {
expect(
parseLogBoxLog([
'Some kind of message\n at MyComponent (/path/to/filename.js:1:2)\n at MyOtherComponent\n at MyAppComponent (/path/to/app.js:100:20)',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because React isn't sending back a properly formatted stackframe.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the second argument', () => {
expect(
parseLogBoxLog([
'Some kind of message',
'\n at MyComponent (/path/to/filename.js:1:2)\n at MyOtherComponent\n at MyAppComponent (/path/to/app.js:100:20)',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because React isn't sending back a properly formatted stackframe.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the nth argument', () => {
expect(
parseLogBoxLog([
'Warning: Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.%s',
'\n\nCheck the render method of `MyOtherComponent`.',
'',
'\n at MyComponent (/path/to/filename.js:1:2)\n at MyOtherComponent\n at MyAppComponent (/path/to/app.js:100:20)',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because React isn't sending back a properly formatted stackframe.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category:
'Warning: Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.',
message: {
content:
'Warning: Each child in a list should have a unique "key" prop.\n\nCheck the render method of `MyOtherComponent`. See https://fb.me/react-warning-keys for more information.',
substitutions: [
{
length: 48,
offset: 62,
},
{
length: 0,
offset: 110,
},
],
},
});
});
it('parses an error log with `error.componentStack`', () => {
const error = {
id: 0,
isFatal: false,
isComponentError: false,
message: '### Error',
originalMessage: '### Error',
name: '',
componentStack:
'\n at MyComponent (/path/to/filename.js:1:2)\n at MyOtherComponent\n at MyAppComponent (/path/to/app.js:100:20)',
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'error',
category: '### Error',
isComponentError: false,
message: {
content: '### Error',
substitutions: [],
},
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because React isn't sending back a properly formatted stackframe.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
});
});
});
describe('Handles component stack frames formatted as call stacks in JSC', () => {
// In new versions of React, the component stack frame format changed to match call stacks.
it('detects a component stack in an interpolated warning', () => {
expect(
parseLogBoxLog([
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s%s',
'\n\nCheck the render method of `MyComponent`.',
'\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category:
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s',
message: {
content:
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?\n\nCheck the render method of `MyComponent`.',
substitutions: [
{
length: 43,
offset: 129,
},
],
},
});
});
it('detects a component stack in the first argument', () => {
expect(
parseLogBoxLog([
'Some kind of message\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the first argument (JSC)', () => {
expect(
parseLogBoxLog([
'Some kind of message\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the second argument', () => {
expect(
parseLogBoxLog([
'Some kind of message',
'\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
it('detects a component stack in the nth argument', () => {
expect(
parseLogBoxLog([
'Warning: Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.%s',
'\n\nCheck the render method of `MyOtherComponent`.',
'',
'\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
]),
).toEqual({
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category:
'Warning: Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.',
message: {
content:
'Warning: Each child in a list should have a unique "key" prop.\n\nCheck the render method of `MyOtherComponent`. See https://fb.me/react-warning-keys for more information.',
substitutions: [
{
length: 48,
offset: 62,
},
{
length: 0,
offset: 110,
},
],
},
});
});
it('parses an error log with `error.componentStack`', () => {
const error = {
id: 0,
isFatal: false,
isComponentError: false,
message: '### Error',
originalMessage: '### Error',
name: '',
componentStack:
'\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'error',
category: '### Error',
isComponentError: false,
message: {
content: '### Error',
substitutions: [],
},
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
});
});
it('parses an error log with a component stack in the message', () => {
const error = {
id: 0,
isFatal: false,
isComponentError: false,
message:
'Some kind of message\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
originalMessage:
'Some kind of message\nMyComponent@/path/to/filename.js:1:2\nforEach@[native code]\nMyAppComponent@/path/to/app.js:100:20',
name: '',
componentStack: null,
stack: [
{
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
collapse: false,
},
],
};
expect(parseLogBoxException(error)).toEqual({
level: 'error',
isComponentError: false,
stack: [
{
collapse: false,
column: 1,
file: 'foo.js',
lineNumber: 1,
methodName: 'bar',
},
],
componentStack: [
{
collapse: false,
content: 'MyComponent',
fileName: '/path/to/filename.js',
location: {column: 1, row: 1},
},
// TODO: we're missing the second component,
// because stackframe-parser does not recognize it.
{
collapse: false,
content: 'MyAppComponent',
fileName: '/path/to/app.js',
location: {column: 19, row: 100},
},
],
category: 'Some kind of message',
message: {
content: 'Some kind of message',
substitutions: [],
},
});
});
});
});