Skip to content

Commit

Permalink
TestRenderer toJSON should not expose the Array wrapper Suspense uses…
Browse files Browse the repository at this point in the history
… for hidden trees (facebook#14392)
  • Loading branch information
bvaughn authored and jetoneza committed Jan 23, 2019
1 parent e8f3b32 commit d9601b6
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
describe('when suspending during mount', () => {
it('properly accounts for base durations when a suspended times out in a sync tree', () => {
const root = ReactTestRenderer.create(<App shouldSuspend={true} />);
expect(root.toJSON()).toEqual(['Loading...']);
expect(root.toJSON()).toEqual('Loading...');
expect(onRender).toHaveBeenCalledTimes(1);

// Initial mount only shows the "Loading..." Fallback.
Expand Down Expand Up @@ -343,7 +343,7 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
expect(onRender.mock.calls[0][3]).toBe(5);

root.update(<App shouldSuspend={true} textRenderDuration={5} />);
expect(root.toJSON()).toEqual(['Loading...']);
expect(root.toJSON()).toEqual('Loading...');
expect(onRender).toHaveBeenCalledTimes(2);

// The suspense update should only show the "Loading..." Fallback.
Expand All @@ -355,7 +355,7 @@ function runPlaceholderTests(suiteLabel, loadReactNoop) {
root.update(
<App shouldSuspend={true} text="New" textRenderDuration={6} />,
);
expect(root.toJSON()).toEqual(['Loading...']);
expect(root.toJSON()).toEqual('Loading...');
expect(onRender).toHaveBeenCalledTimes(3);

// If we force another update while still timed out,
Expand Down
10 changes: 9 additions & 1 deletion packages/react-test-renderer/src/ReactTestRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,15 @@ const ReactTestRendererFiber = {
if (container.children.length === 1) {
return toJSON(container.children[0]);
}

if (
container.children.length === 2 &&
container.children[0].isHidden === true &&
container.children[1].isHidden === false
) {
// Omit timed out children from output entirely, including the fact that we
// temporarily wrap fallback and timed out children in an array.
return toJSON(container.children[1]);
}
let renderedChildren = null;
if (container.children && container.children.length) {
for (let i = 0; i < container.children.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const ReactDOM = require('react-dom');

// Isolate test renderer.
jest.resetModules();
const React = require('react');
const ReactCache = require('react-cache');
const ReactTestRenderer = require('react-test-renderer');

describe('ReactTestRenderer', () => {
Expand All @@ -26,4 +28,76 @@ describe('ReactTestRenderer', () => {
withoutStack: true,
});
});

describe('timed out Suspense hidden subtrees should not be observable via toJSON', () => {
let AsyncText;
let PendingResources;
let TextResource;

beforeEach(() => {
PendingResources = {};
TextResource = ReactCache.unstable_createResource(
text =>
new Promise(resolve => {
PendingResources[text] = resolve;
}),
text => text,
);

AsyncText = ({text}) => {
const value = TextResource.read(text);
return value;
};
});

it('for root Suspense components', async done => {
const App = ({text}) => {
return (
<React.Suspense fallback="fallback">
<AsyncText text={text} />
</React.Suspense>
);
};

const root = ReactTestRenderer.create(<App text="initial" />);
PendingResources.initial('initial');
await Promise.resolve();
expect(root.toJSON()).toEqual('initial');

root.update(<App text="dynamic" />);
expect(root.toJSON()).toEqual('fallback');

PendingResources.dynamic('dynamic');
await Promise.resolve();
expect(root.toJSON()).toEqual('dynamic');

done();
});

it('for nested Suspense components', async done => {
const App = ({text}) => {
return (
<div>
<React.Suspense fallback="fallback">
<AsyncText text={text} />
</React.Suspense>
</div>
);
};

const root = ReactTestRenderer.create(<App text="initial" />);
PendingResources.initial('initial');
await Promise.resolve();
expect(root.toJSON().children).toEqual(['initial']);

root.update(<App text="dynamic" />);
expect(root.toJSON().children).toEqual(['fallback']);

PendingResources.dynamic('dynamic');
await Promise.resolve();
expect(root.toJSON().children).toEqual(['dynamic']);

done();
});
});
});

0 comments on commit d9601b6

Please sign in to comment.