diff --git a/test/vitest-ui-e2e-preflight.test.ts b/test/vitest-ui-e2e-preflight.test.ts index 7a8d72a3df48..cf7007e1c1fc 100644 --- a/test/vitest-ui-e2e-preflight.test.ts +++ b/test/vitest-ui-e2e-preflight.test.ts @@ -2,7 +2,11 @@ import { createServer } from "node:http"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { assertUiE2ePreflight } from "./vitest/vitest.ui-e2e-preflight.ts"; -vi.mock("node:http", { spy: true }); +vi.mock("node:http", async (importOriginal) => { + const actual = await importOriginal(); + // Native HTTP and socket prototypes outlive a test file in shared workers. + return { ...actual, createServer: vi.fn(actual.createServer) }; +}); const listeners: Array<{ server: ReturnType; @@ -22,14 +26,19 @@ beforeEach(async () => { }); afterEach(async () => { - // Keep a failed cleanup assertion from leaking its real listener into later tests. - for (const { server } of listeners) { - server.closeAllConnections(); - if (server.listening) { - await new Promise((resolve, reject) => { - server.close((error) => (error ? reject(error) : resolve())); - }); + try { + // Keep a failed cleanup assertion from leaking its real listener into later tests. + for (const { server } of listeners) { + server.closeAllConnections(); + if (server.listening) { + await new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); + } } + } finally { + // restoreAllMocks does not reset module spies' custom implementations. + vi.mocked(createServer).mockRestore(); } }); @@ -43,6 +52,35 @@ function expectListenerClosed() { } describe("UI E2E environment preflight", () => { + it("preserves native HTTP and socket implementations for later shared-worker consumers", () => { + const nativeHttp = process.getBuiltinModule("node:http"); + const nativeNet = process.getBuiltinModule("node:net"); + for (const implementation of [ + nativeHttp.Server, + nativeHttp.Agent, + nativeHttp.IncomingMessage, + nativeHttp.OutgoingMessage, + nativeHttp.ServerResponse, + nativeHttp.ClientRequest, + nativeHttp.createServer, + nativeNet.Socket, + ]) { + expect(vi.isMockFunction(implementation)).toBe(false); + } + for (const [prototype, method] of [ + [nativeHttp.IncomingMessage.prototype, "read"], + [nativeHttp.OutgoingMessage.prototype, "write"], + [nativeHttp.OutgoingMessage.prototype, "end"], + [nativeHttp.globalAgent, "createConnection"], + [nativeNet.Socket.prototype, "write"], + ] as const) { + expect(prototype).toHaveProperty( + method, + expect.toSatisfy((implementation) => !vi.isMockFunction(implementation)), + ); + } + }); + it("proves the owned HTTP response and releases its listener", async () => { const fetchImpl = vi.fn(fetch); await assertUiE2ePreflight({ fetchImpl });