Fix message truncating failing in production builds

Summary: Message truncated worked in devs build, but failed in production build, as it JSON formatted React elements in prod build (incorreclty), while it didn't do so in Dev builds, as in dev that generates an exception (undesired) meaning the serialisation gets skipped (desired).

Reviewed By: passy, nikoant, priteshrnandgaonkar

Differential Revision: D27467280

fbshipit-source-id: 1f8e0ca4750464c778c33b69a8cf13d05f019143
This commit is contained in:
Michel Weststrate
2021-03-31 09:23:32 -07:00
committed by Facebook GitHub Bot
parent 77abba6459
commit 1060ea6e9e
2 changed files with 187 additions and 5 deletions

View File

@@ -13,7 +13,7 @@ import {
CopyOutlined,
} from '@ant-design/icons';
import {Button, Typography} from 'antd';
import {pad} from 'lodash';
import {isPlainObject, pad} from 'lodash';
import React, {createElement, Fragment, isValidElement, useState} from 'react';
import {tryGetFlipperLibImplementation} from '../plugin/FlipperLib';
import {safeStringify} from '../utils/safeStringify';
@@ -97,6 +97,9 @@ export const DataFormatter = {
},
prettyPrintJson(value: any) {
if (isValidElement(value)) {
return value;
}
if (typeof value === 'string' && value.length >= 2) {
const last = value.length - 1;
// kinda looks like json
@@ -112,7 +115,11 @@ export const DataFormatter = {
}
}
}
if (typeof value === 'object' && value !== null) {
if (
typeof value === 'object' &&
value !== null &&
(Array.isArray(value) || isPlainObject(value))
) {
try {
// Note: we don't need to be inserted <br/>'s in the output, but assume the text container uses
// white-space: pre-wrap (or pre)
@@ -137,7 +144,8 @@ export const DataFormatter = {
},
};
function TruncateHelper({
// exported for testing
export function TruncateHelper({
value,
maxLength,
}: {

View File

@@ -7,7 +7,10 @@
* @format
*/
import {DataFormatter} from '../DataFormatter';
import {render, fireEvent} from '@testing-library/react';
import React from 'react';
import {act} from 'react-dom/test-utils';
import {DataFormatter, TruncateHelper} from '../DataFormatter';
test('default formatter', () => {
expect(DataFormatter.format(true)).toMatchInlineSnapshot(`"true"`);
@@ -126,7 +129,7 @@ test('linkify formatter', () => {
expect(linkify('fb.com')).toMatchInlineSnapshot(`"fb.com"`);
});
test('linkify formatter', () => {
test('jsonify formatter', () => {
const jsonify = (value: any) =>
DataFormatter.format(value, DataFormatter.prettyPrintJson);
@@ -162,3 +165,174 @@ test('linkify formatter', () => {
</React.Fragment>
`);
});
test("jsonify doesn't process react elements", () => {
const jsonify = (value: any) =>
DataFormatter.format(value, DataFormatter.prettyPrintJson);
expect(jsonify('abcde')).toEqual('abcde');
expect(jsonify('{ a: 1 }')).toMatchInlineSnapshot(`"{ a: 1 }"`);
expect(jsonify({a: 1})).toMatchInlineSnapshot(`
"{
\\"a\\": 1
}"
`);
expect(jsonify(<span>hi</span>)).toMatchInlineSnapshot(`
<span>
hi
</span>
`);
});
test('truncate formatter', () => {
const truncate = (value: any) =>
DataFormatter.format(value, DataFormatter.truncate(5));
expect(truncate({test: true})).toMatchInlineSnapshot(`
"{
\\"test\\": true
}"
`);
expect(truncate('abcde')).toEqual('abcde');
expect(truncate('abcdefghi')).toMatchInlineSnapshot(`
<TruncateHelper
maxLength={5}
value="abcdefghi"
/>
`);
});
test('render truncate helper', () => {
const res = render(
<TruncateHelper value="!! COOL CONTENT !!" maxLength={4} />,
);
expect(res.baseElement).toMatchInlineSnapshot(`
<body>
<div>
!! C
<button
class="ant-btn ant-btn-text ant-btn-sm"
style="margin-left: 4px;"
type="button"
>
<span
aria-label="caret-right"
class="anticon anticon-caret-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="caret-right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M715.8 493.5L335 165.1c-14.2-12.2-35-1.2-35 18.5v656.8c0 19.7 20.8 30.7 35 18.5l380.8-328.4c10.9-9.4 10.9-27.6 0-37z"
/>
</svg>
</span>
<span>
and 14 more
</span>
</button>
<button
class="ant-btn ant-btn-text ant-btn-sm"
style="margin-left: 4px;"
type="button"
>
<span
aria-label="copy"
class="anticon anticon-copy"
role="img"
>
<svg
aria-hidden="true"
data-icon="copy"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"
/>
</svg>
</span>
<span>
copy
</span>
</button>
</div>
</body>
`);
act(() => {
fireEvent.click(res.getAllByText(/and \d+ more/)[0]);
});
expect(res.baseElement).toMatchInlineSnapshot(`
<body>
<div>
!! COOL CONTENT !!
<button
class="ant-btn ant-btn-text ant-btn-sm"
style="margin-left: 4px;"
type="button"
>
<span
aria-label="caret-up"
class="anticon anticon-caret-up"
role="img"
>
<svg
aria-hidden="true"
data-icon="caret-up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M858.9 689L530.5 308.2c-9.4-10.9-27.5-10.9-37 0L165.1 689c-12.2 14.2-1.2 35 18.5 35h656.8c19.7 0 30.7-20.8 18.5-35z"
/>
</svg>
</span>
<span>
collapse
</span>
</button>
<button
class="ant-btn ant-btn-text ant-btn-sm"
style="margin-left: 4px;"
type="button"
>
<span
aria-label="copy"
class="anticon anticon-copy"
role="img"
>
<svg
aria-hidden="true"
data-icon="copy"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"
/>
</svg>
</span>
<span>
copy
</span>
</button>
</div>
</body>
`);
});