diff --git a/packages/react/src/__tests__/ReactFetchEdge-test.js b/packages/react/src/__tests__/ReactFetchEdge-test.js new file mode 100644 index 0000000000000..f71d7d9b008f0 --- /dev/null +++ b/packages/react/src/__tests__/ReactFetchEdge-test.js @@ -0,0 +1,82 @@ +/** + * 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. + * + * @emails react-core + */ + +'use strict'; + +// Polyfills for test environment +global.ReadableStream = + require('web-streams-polyfill/ponyfill/es6').ReadableStream; +global.TextEncoder = require('util').TextEncoder; +global.TextDecoder = require('util').TextDecoder; +global.Headers = require('node-fetch').Headers; +global.Request = require('node-fetch').Request; +global.Response = require('node-fetch').Response; +// Patch for Edge environments for global scope +global.AsyncLocalStorage = require('async_hooks').AsyncLocalStorage; + +let fetchCount = 0; +async function fetchMock(resource, options) { + fetchCount++; + const request = new Request(resource, options); + return new Response( + request.method + + ' ' + + request.url + + ' ' + + JSON.stringify(Array.from(request.headers.entries())), + ); +} + +let React; +let ReactServerDOMServer; +let ReactServerDOMClient; +let use; +let cache; + +describe('ReactFetch', () => { + beforeEach(() => { + jest.resetModules(); + fetchCount = 0; + global.fetch = fetchMock; + + if (gate(flags => !flags.www)) { + jest.mock('react', () => require('react/react.shared-subset')); + } + + React = require('react'); + ReactServerDOMServer = require('react-server-dom-webpack/server.edge'); + ReactServerDOMClient = require('react-server-dom-webpack/client'); + use = React.use; + cache = React.cache; + }); + + async function render(Component) { + const stream = ReactServerDOMServer.renderToReadableStream(); + return ReactServerDOMClient.createFromReadableStream(stream); + } + + // @gate enableFetchInstrumentation && enableCache + it('can dedupe fetches separately in interleaved renders', async () => { + async function getData() { + const r1 = await fetch('hi'); + const t1 = await r1.text(); + const r2 = await fetch('hi'); + const t2 = await r2.text(); + return t1 + ' ' + t2; + } + function Component() { + return use(getData()); + } + const render1 = render(Component); + const render2 = render(Component); + expect(await render1).toMatchInlineSnapshot(`"GET hi [] GET hi []"`); + expect(await render2).toMatchInlineSnapshot(`"GET hi [] GET hi []"`); + expect(fetchCount).toBe(2); + }); +});