Skip to content

Commit

Permalink
Add a test for issue facebook#28595
Browse files Browse the repository at this point in the history
The added test, intended to fail and reproduce the [reported
issue](facebook#28595), unexpectedly
passes in its current state. I see three possible reasons:

1. The bug report could be invalid.
2. How I've structured the test might be insufficient to replicate what
   `ai/rsc` is doing.
3. Something in the test setup could be masking the actual error.
   (Maybe related to fake timers?)

If the problem lies in reason 2 or 3, this test could possibly serve as
a foundation for further investigation.
  • Loading branch information
unstubbable committed Mar 23, 2024
1 parent f09e159 commit 4baed31
Showing 1 changed file with 99 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,105 @@ describe('ReactFlightDOM', () => {
expect(reportedErrors).toEqual([]);
});

it('should handle streaming async server components', async () => {
const reportedErrors = [];

const Row = async ({current, next}) => {
const chunk = await next;

if (chunk.done) {
return chunk.value;
}

return (
<Suspense fallback={chunk.value}>
<Row current={chunk.value} next={chunk.next} />
</Suspense>
);
};

function createResolvablePromise() {
let _resolve, _reject;

const promise = new Promise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});

return {promise, resolve: _resolve, reject: _reject};
}

function createSuspendedChunk(initialValue) {
const {promise, resolve, reject} = createResolvablePromise();

return {
row: (
<Suspense fallback={initialValue}>
<Row current={initialValue} next={promise} />
</Suspense>
),
resolve,
reject,
};
}

function makeDelayedText() {
const {promise, resolve, reject} = createResolvablePromise();
async function DelayedText() {
const data = await promise;
return <div>{data}</div>;
}
return [DelayedText, resolve, reject];
}

const [Posts, resolvePostsData] = makeDelayedText();
const suspendedChunk = createSuspendedChunk(<p>loading</p>);
const {writable, readable} = getTestStream();
const {pipe} = ReactServerDOMServer.renderToPipeableStream(
suspendedChunk.row,
webpackMap,
{
onError(error) {
reportedErrors.push(error);
},
},
);
pipe(writable);
const response = ReactServerDOMClient.createFromReadableStream(readable);
const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);

function ClientRoot() {
return use(response);
}

await act(() => {
root.render(<ClientRoot />);
});

expect(container.innerHTML).toBe('<p>loading</p>');

const donePromise = createResolvablePromise();
const value = <Posts />;

await act(async () => {
suspendedChunk.resolve({value, done: false, next: donePromise.promise});
await Promise.resolve();
donePromise.resolve({value, done: true});
});

expect(container.innerHTML).toBe('<p>loading</p>');

await act(async () => {
jest.advanceTimersByTime(500);
await resolvePostsData('posts');
await 'the inner async function';
});

expect(container.innerHTML).toBe('<div>posts</div>');
expect(reportedErrors).toEqual([]);
});

it('should preserve state of client components on refetch', async () => {
// Client

Expand Down

0 comments on commit 4baed31

Please sign in to comment.