Skip to content

Commit

Permalink
Add integration tests for console errors + ExceptionManager
Browse files Browse the repository at this point in the history
Summary:
Adds more integration tests for LogBox (currently incorrect, but fixed in a later diff).

Changelog: [Internal]

Differential Revision: D63349614
  • Loading branch information
rickhanlonii authored and facebook-github-bot committed Sep 24, 2024
1 parent b096a97 commit f6b6520
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@
import {
DoesNotUseKey,
FragmentWithProp,
ManualConsoleError,
ManualConsoleErrorWithStack,
} from './__fixtures__/ReactWarningFixtures';
import * as React from 'react';

const LogBoxData = require('../Data/LogBoxData');
const TestRenderer = require('react-test-renderer');

const ExceptionsManager = require('react-native/Libraries/Core/ExceptionsManager.js');

Check failure on line 22 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Use relative paths when importing within 'react-native'

Check failure on line 22 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Use relative paths when importing within 'react-native'

const installLogBox = () => {
const LogBox = require('../LogBox').default;

LogBox.install();
};

Expand All @@ -46,6 +49,23 @@ const cleanLog = logs => {
});
};

const cleanError = logs => {
return logs.map(log => {
return {
...log,
extraData: {redacted: true},
stack: [],
componentStack:
log.componentStack == null
? null
: log.componentStack.map(stack => ({
...stack,
fileName: cleanPath(stack.fileName),
})),
};
});
};

// TODO(T71117418): Re-enable skipped LogBox integration tests once React component
// stack frames are the same internally and in open source.
// eslint-disable-next-line jest/no-disabled-tests

Check warning on line 71 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (20)

'jest/no-disabled-tests' rule is disabled but never reported

Check warning on line 71 in packages/react-native/Libraries/LogBox/__tests__/LogBox-integration-test.js

View workflow job for this annotation

GitHub Actions / test_js (18)

'jest/no-disabled-tests' rule is disabled but never reported
Expand All @@ -60,6 +80,10 @@ describe('LogBox', () => {

mockError.mockClear();
mockWarn.mockClear();
// Reset ExceptionManager patching.
if (console._errorOriginal) {
console._errorOriginal = null;
}
(console: any).error = mockError;
(console: any).warn = mockWarn;
});
Expand Down Expand Up @@ -128,4 +152,79 @@ describe('LogBox', () => {
// The Warning: prefix is added due to a hack in LogBox to prevent double logging.
expect(mockError.mock.calls[0][0].startsWith('Warning: ')).toBe(true);
});

it('handles a manual console.error without a component stack in LogBox', () => {
const LogBox = require('../LogBox').default;
const spy = jest.spyOn(LogBox, 'addException');
installLogBox();

// console.error handling depends on installing the ExceptionsManager error reporter.
ExceptionsManager.installConsoleErrorReporter();

// Spy console.error after LogBox is installed
// so we can assert on what React logs.
jest.spyOn(console, 'error');

const output = TestRenderer.create(<ManualConsoleError />);

// Manual console errors should show a collapsed error dialog.
// When there is no component stack, we expect these errors to:
// - Go to the LogBox patch and fall through to console.error.
// - Get picked up by the ExceptionsManager console.error override.
// - Get passed back to LogBox via addException (non-fatal).
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error).toBeCalledTimes(1);
expect(console.error.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log sent from React',
);
expect(spy).toBeCalledTimes(1);
expect(cleanError(spy.mock.calls[0])).toMatchSnapshot(
'Log added to LogBox',
);
expect(mockError.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log passed to console error',
);

// Doesn't call console.error again
expect(console.error).toBeCalledTimes(1);
});

it('handles a manual console.error with a component stack in LogBox', () => {
const spy = jest.spyOn(LogBoxData, 'addLog');
installLogBox();

// console.error handling depends on installing the ExceptionsManager error reporter.
ExceptionsManager.installConsoleErrorReporter();

// Spy console.error after LogBox is installed
// so we can assert on what React logs.
jest.spyOn(console, 'error');

const output = TestRenderer.create(<ManualConsoleErrorWithStack />);

// Manual console errors should show a collapsed error dialog.
// When there is a component stack, we expect these errors to:
// - Go to the LogBox patch and be detected as a React error.
// - Check the warning filter to see if there is a fiter setting.
// - Call console.error with the parsed error.
// - Get picked up by ExceptionsManager console.error override.
// - Log to console.error.
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error).toBeCalledTimes(1);
expect(console.error.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log sent from React',
);
expect(spy).toBeCalledTimes(1);
expect(cleanError(spy.mock.calls[0])).toMatchSnapshot(
'Log added to LogBox',
);
expect(mockError.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log passed to console error',
);

// Doesn't call console.error again
expect(console.error).toBeCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,27 @@ export const FragmentWithProp = () => {
</React.Fragment>
);
};

export const ManualConsoleError = () => {
console.error('Manual console error');
return (
<React.Fragment>
{['foo', 'bar'].map(item => (
<Text key={item}>{item}</Text>
))}
</React.Fragment>
);
};

export const ManualConsoleErrorWithStack = () => {
console.error(
'Manual console error\n at ManualConsoleErrorWithStack (/path/to/ManualConsoleErrorWithStack:30:175)\n at TestApp',
);
return (
<React.Fragment>
{['foo', 'bar'].map(item => (
<Text key={item}>{item}</Text>
))}
</React.Fragment>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,80 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LogBox handles a manual console.error with a component stack in LogBox: Log added to LogBox 1`] = `
Array [
Object {
"category": "Warning: Manual console error",
"componentStack": Array [
Object {
"collapse": false,
"content": "ManualConsoleErrorWithStack",
"fileName": "/path/to/ManualConsoleErrorWithStack",
"location": Object {
"column": 174,
"row": 30,
},
},
],
"componentStackType": "stack",
"extraData": Object {
"redacted": true,
},
"level": "warn",
"message": Object {
"content": "Warning: Manual console error",
"substitutions": Array [],
},
"stack": Array [],
},
]
`;

exports[`LogBox handles a manual console.error with a component stack in LogBox: Log passed to console error 1`] = `
Array [
"Warning: Manual console error
at ManualConsoleErrorWithStack (/path/to/ManualConsoleErrorWithStack:30:175)
at TestApp",
]
`;

exports[`LogBox handles a manual console.error with a component stack in LogBox: Log sent from React 1`] = `
Array [
"Manual console error
at ManualConsoleErrorWithStack (/path/to/ManualConsoleErrorWithStack:30:175)
at TestApp",
]
`;

exports[`LogBox handles a manual console.error without a component stack in LogBox: Log added to LogBox 1`] = `
Array [
Object {
"componentStack": null,
"extraData": Object {
"redacted": true,
},
"id": 1,
"isComponentError": false,
"isFatal": false,
"message": "console.error: Manual console error",
"name": "console.error",
"originalMessage": "Manual console error",
"stack": Array [],
},
]
`;

exports[`LogBox handles a manual console.error without a component stack in LogBox: Log passed to console error 1`] = `
Array [
"Manual console error",
]
`;

exports[`LogBox handles a manual console.error without a component stack in LogBox: Log sent from React 1`] = `
Array [
"Manual console error",
]
`;

exports[`LogBox integrates with React and handles a fragment warning in LogBox: Log added to LogBox 1`] = `
Array [
Object {
Expand Down

0 comments on commit f6b6520

Please sign in to comment.