From f7e4eb83694aa007a492ef66c28ffbe6a2dae791 Mon Sep 17 00:00:00 2001 From: Ashcon Partovi Date: Tue, 7 Mar 2023 12:22:34 -0800 Subject: Reorganize tests (#2332) --- test/js/third_party/react-dom/react-dom.test.tsx | 285 +++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 test/js/third_party/react-dom/react-dom.test.tsx (limited to 'test/js/third_party/react-dom/react-dom.test.tsx') diff --git a/test/js/third_party/react-dom/react-dom.test.tsx b/test/js/third_party/react-dom/react-dom.test.tsx new file mode 100644 index 000000000..2f1309fb8 --- /dev/null +++ b/test/js/third_party/react-dom/react-dom.test.tsx @@ -0,0 +1,285 @@ +import { + concatArrayBuffers, + readableStreamToArray, + readableStreamToArrayBuffer, + readableStreamToBlob, + readableStreamToText, + serve, +} from "bun"; +import { heapStats } from "bun:jsc"; +import { describe, expect, it } from "bun:test"; +import { renderToReadableStream as renderToReadableStreamBrowser } from "react-dom/server.browser"; +import { gc } from "harness"; +import { renderToReadableStream as renderToReadableStreamBun } from "./react-dom-server.bun.cjs"; +import React from "react"; + +Object.defineProperty(renderToReadableStreamBrowser, "name", { + value: "server.browser", +}); +Object.defineProperty(renderToReadableStreamBun, "name", { + value: "server.bun", +}); +var port = 8908; + +const fixtures = [ + // Needs at least six variations + // - < 8 chars, latin1 + // - 8+ chars, latin1 + // - 16+ chars, latin1 + // - < 8 chars, utf16 + // - 8+ chars, utf16 + // - 16+ chars, utf16 + ["b", b], + ["Hello World!", Hello World!], + ["", ], + ["πŸ˜‹", πŸ˜‹], + ["πŸ˜‹", πŸ˜‹], + ["Hello World! πŸ˜‹", Hello World! πŸ˜‹], + [ + "Hello World!πŸ˜‹", + <> + Hello World!πŸ˜‹ + , + ], + [ + "πŸ˜‹Hello World!", + <> + πŸ˜‹Hello World! + , + ], + ["πŸ˜‹", <>πŸ˜‹], + ["lπŸ˜‹l", <>lπŸ˜‹l], + ["loπŸ˜‹", <>loπŸ˜‹], + ["πŸ˜‹lo", <>πŸ˜‹lo], + [ + "πŸ˜‹Hello World!", + <> + πŸ˜‹ + Hello World! + , + ], + [ + "πŸ˜‹πŸ˜‹πŸ˜‹πŸ˜‹Hello World!", + <> + πŸ˜‹πŸ˜‹πŸ˜‹πŸ˜‹ + Hello World! + , + ], + ["HelloπŸ˜‹πŸ˜‹πŸ˜‹πŸ˜‹World!", HelloπŸ˜‹πŸ˜‹πŸ˜‹πŸ˜‹World!], + [ + "Hello World!πŸ˜‹πŸ˜‹πŸ˜‹πŸ˜‹", + <> + Hello World! + πŸ˜‹πŸ˜‹πŸ˜‹πŸ˜‹ + , + ], + [ + "πŸ˜‹LπŸ˜‹lπŸ˜‹LπŸ˜‹Alternating latin1 & utf16", + <> + πŸ˜‹LπŸ˜‹lπŸ˜‹LπŸ˜‹Alternating latin1 & utf16 + , + ], + ["HelloπŸ˜‹LπŸ˜‹lπŸ˜‹LπŸ˜‹World!", HelloπŸ˜‹LπŸ˜‹lπŸ˜‹LπŸ˜‹World!], + [ + "Hello World!πŸ˜‹LπŸ˜‹lπŸ˜‹LπŸ˜‹", + <> + Hello World! + πŸ˜‹LπŸ˜‹lπŸ˜‹LπŸ˜‹ + , + ], +]; + +describe("React", () => { + it("React.createContext works", () => { + expect(typeof React.createContext).toBe("function"); + const pleaseDontThrow = React.createContext({ foo: true }); + expect((pleaseDontThrow as any).$$typeof.description).toBe("react.context"); + + const pleaseDontThrow2 = (React as any).default.createContext({ + foo: true, + }); + expect(pleaseDontThrow2.$$typeof.description).toBe("react.context"); + }); +}); + +describe("ReactDOM", () => { + for (let renderToReadableStream of [renderToReadableStreamBun, renderToReadableStreamBrowser]) { + for (let [inputString, reactElement] of fixtures) { + describe(`${renderToReadableStream.name}(${inputString})`, () => { + it("Response.text()", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const response = new Response(stream); + gc(); + try { + const text = await response.text(); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + } catch (e: any) { + console.log(e.stack); + throw e; + } + }); + it("Response.arrayBuffer()", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const response = new Response(stream); + gc(); + const text = new TextDecoder().decode(await response.arrayBuffer()); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + it("Response.blob()", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const response = new Response(stream); + gc(); + const text = await (await response.blob()).text(); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + it("readableStreamToText(stream)", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const text = await readableStreamToText(stream); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + it("readableStreamToBlob(stream)", async () => { + try { + const stream = await renderToReadableStream(reactElement); + gc(); + const blob = await readableStreamToBlob(stream); + const text = await blob.text(); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + } catch (e: any) { + console.error(e.message); + console.error(e.stack); + throw e; + } + }); + it("readableStreamToArray(stream)", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const array = await readableStreamToArray(stream); + const text = + renderToReadableStream === renderToReadableStreamBun + ? array.join("") + : new TextDecoder().decode(concatArrayBuffers(array as any[])); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + it("readableStreamToArrayBuffer(stream)", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const arrayBuffer = await readableStreamToArrayBuffer(stream); + const text = new TextDecoder().decode(arrayBuffer); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + it("for await (chunk of stream)", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const chunks: any = []; + for await (let chunk of stream) { + chunks.push(chunk); + } + const text = await new Response(chunks).text(); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + + it("for await (chunk of stream) (arrayBuffer)", async () => { + const stream = await renderToReadableStream(reactElement); + gc(); + const chunks: any[] = []; + for await (let chunk of stream) { + chunks.push(chunk); + } + const text = new TextDecoder().decode(await new Response(chunks as any).arrayBuffer()); + gc(); + expect(text.replaceAll("", "")).toBe(inputString); + gc(); + }); + }); + } + } + for (let renderToReadableStream of [renderToReadableStreamBun, renderToReadableStreamBrowser]) { + // there is an event loop bug that causes deadlocks + // the bug is with `fetch`, not with the HTTP server + for (let [inputString, reactElement] of fixtures) { + describe(`${renderToReadableStream.name}(${inputString})`, () => { + it("http server, 1 request", async () => { + await (async function () { + var server; + try { + server = serve({ + port: port++, + async fetch(req) { + return new Response(await renderToReadableStream(reactElement)); + }, + }); + const resp = await fetch("http://localhost:" + server.port + "/"); + expect((await resp.text()).replaceAll("", "")).toBe(inputString); + gc(); + } catch (e) { + throw e; + } finally { + server?.stop(); + gc(); + } + })(); + gc(); + expect(heapStats().objectTypeCounts.ReadableHTTPResponseSinkController ?? 0).toBe(1); + }); + const count = 4; + it(`http server, ${count} requests`, async () => { + var remain = count; + await (async function () { + var server; + try { + server = serve({ + port: port++, + async fetch(req) { + return new Response(await renderToReadableStream(reactElement)); + }, + }); + gc(); + while (remain--) { + var attempt = remain + 1; + const response = await fetch("http://localhost:" + server.port + "/"); + gc(); + const result = await response.text(); + try { + expect(result.replaceAll("", "")).toBe(inputString); + } catch (e: any) { + e.message += "\nAttempt: " + attempt; + throw e; + } + + gc(); + } + } catch (e) { + throw e; + } finally { + server.stop(); + } + })(); + + const { ReadableHTTPResponseSinkController = 0 } = heapStats().objectTypeCounts; + expect(ReadableHTTPResponseSinkController).toBe(1); + expect(remain + 1).toBe(0); + }); + }); + } + } +}); -- cgit v1.2.3