Skip to content

Commit

Permalink
Add React integration tests for LogBox
Browse files Browse the repository at this point in the history
Summary:
This diff adds some integration tests that throw read React errors and asserts on the LogBox data.

Changelog: [Internal]

Reviewed By: motiz88

Differential Revision: D22752147

fbshipit-source-id: eb2e1542524d67558f2450130015164acd4d6093
  • Loading branch information
rickhanlonii authored and facebook-github-bot committed Jul 28, 2020
1 parent 4720ee9 commit 7af3f6e
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 0 deletions.
128 changes: 128 additions & 0 deletions Libraries/LogBox/__tests__/LogBox-integration-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* 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
*/

'use strict';

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

import * as React from 'react';

import {
DoesNotUseKey,
FragmentWithProp,
} from './__fixtures__/ReactWarningFixtures';

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

LogBox.install();
};

const uninstallLogBox = () => {
const LogBox = require('../LogBox');
LogBox.uninstall();
};

const BEFORE_SLASH_RE = /(?:\/[a-zA-Z]+\/)(.+?)(?:\/.+)\//;

const cleanPath = message => {
return message.replace(BEFORE_SLASH_RE, '/path/to/');
};

const cleanLog = logs => {
return logs.map(log => {
return {
...log,
componentStack: log.componentStack.map(stack => ({
...stack,
fileName: cleanPath(stack.fileName),
})),
};
});
};

describe('LogBox', () => {
const {error, warn} = console;
const mockError = jest.fn();
const mockWarn = jest.fn();

beforeEach(() => {
jest.resetModules();
jest.restoreAllMocks();

mockError.mockClear();
mockWarn.mockClear();

(console: any).error = mockError;
(console: any).warn = mockWarn;
});

afterEach(() => {
uninstallLogBox();
(console: any).error = error;
(console: any).warn = warn;
});

it('integrates with React and handles a key error in LogBox', () => {
const spy = jest.spyOn(LogBoxData, 'addLog');
installLogBox();

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

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

// The key error should always be the highest severity.
// In LogBox, we expect these errors to:
// - Be added to LogBox, because all errors and warnings are.
// - Not call through to console.warn, because they are errors.
// - Pass to console.error, with a "Warning" prefix so it does not pop a RedBox.
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log sent from React',
);
expect(cleanLog(spy.mock.calls[0])).toMatchSnapshot('Log added to LogBox');
expect(mockError.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log passed to console error',
);
expect(mockError.mock.calls[0][0].startsWith('Warning: ')).toBe(true);
});

it('integrates with React and handles a fragment warning in LogBox', () => {
const spy = jest.spyOn(LogBoxData, 'addLog');
installLogBox();

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

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

// The fragment warning is not as severe. For this warning we don't want to
// pop open a dialog, so we show a collapsed error UI.
// That means we expect these warnings to:
// - Be added to LogBox and displayed collapsed as an error.
// - Not call console.warn, because they are errors in the console.
// - Pass to console.error, with a "Warning" prefix so it does not pop a RedBox.
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log sent from React',
);
expect(cleanLog(spy.mock.calls[0])).toMatchSnapshot('Log added to LogBox');
expect(mockError.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log passed to console error',
);
expect(mockError.mock.calls[0][0].startsWith('Warning: ')).toBe(true);
});
});
34 changes: 34 additions & 0 deletions Libraries/LogBox/__tests__/__fixtures__/ReactWarningFixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 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
*/

'use strict';

import {Text} from 'react-native';
import * as React from 'react';

export const DoesNotUseKey = () => {
return (
<>
{['foo', 'bar'].map(item => (
<Text>{item}</Text>
))}
</>
);
};

export const FragmentWithProp = () => {
return (
<React.Fragment invalid="prop">
{['foo', 'bar'].map(item => (
<Text key={item}>{item}</Text>
))}
</React.Fragment>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LogBox integrates with React and handles a fragment warning in LogBox: Log added to LogBox 1`] = `
Array [
Object {
"category": "Warning: Invalid prop \`%s\` supplied to \`React.Fragment\`. React.Fragment can only have \`key\` and \`children\` props.",
"componentStack": Array [
Object {
"content": "Fragment",
"fileName": "ReactWarningFixtures.js",
"location": Object {
"column": -1,
"row": 28,
},
},
],
"level": "warn",
"message": Object {
"content": "Warning: Invalid prop \`invalid\` supplied to \`React.Fragment\`. React.Fragment can only have \`key\` and \`children\` props.",
"substitutions": Array [
Object {
"length": 7,
"offset": 23,
},
],
},
},
]
`;

exports[`LogBox integrates with React and handles a fragment warning in LogBox: Log passed to console error 1`] = `
Array [
"Warning: Invalid prop \`invalid\` supplied to \`React.Fragment\`. React.Fragment can only have \`key\` and \`children\` props.
in Fragment (at ReactWarningFixtures.js:28)",
]
`;

exports[`LogBox integrates with React and handles a fragment warning in LogBox: Log sent from React 1`] = `
Array [
"Warning: Invalid prop \`%s\` supplied to \`React.Fragment\`. React.Fragment can only have \`key\` and \`children\` props.%s",
"invalid",
"
in Fragment (at ReactWarningFixtures.js:28)",
]
`;

exports[`LogBox integrates with React and handles a key error in LogBox: Log added to LogBox 1`] = `
Array [
Object {
"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.",
"componentStack": Array [
Object {
"content": "Text",
"fileName": "ReactWarningFixtures.js",
"location": Object {
"column": -1,
"row": 20,
},
},
],
"level": "warn",
"message": Object {
"content": "Warning: Each child in a list should have a unique \\"key\\" prop. See https://fb.me/react-warning-keys for more information.",
"substitutions": Array [
Object {
"length": 0,
"offset": 62,
},
Object {
"length": 0,
"offset": 62,
},
],
},
},
]
`;

exports[`LogBox integrates with React and handles a key error in LogBox: Log passed to console error 1`] = `
Array [
"Warning: Each child in a list should have a unique \\"key\\" prop. See https://fb.me/react-warning-keys for more information.
in Text (at ReactWarningFixtures.js:20)",
]
`;

exports[`LogBox integrates with React and handles a key error in LogBox: Log sent from React 1`] = `
Array [
"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",
"",
"",
"
in Text (at ReactWarningFixtures.js:20)",
]
`;

0 comments on commit 7af3f6e

Please sign in to comment.