From e645f8c94a1ca1e16d31429f86657fd8cbd88ea5 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Mon, 18 Nov 2024 14:01:09 -0800 Subject: [PATCH] dev-middleware: refactor tests to use undici.request Summary: Use `request` over `fetch` in `dev-middleware`'s tests. This is required by the next diff in the stack to spoof the `Host` header for testing purposes, which isn't permitted by the `fetch` spec. The return type is a bit different (eg `statusCode` vs `status`, no `ok` prop), but the modifications needed are pretty straightforward. Changelog: [Internal] Reviewed By: huntie Differential Revision: D66005427 --- flow-typed/npm/undici_v5.x.x.js | 20 ++++++++++ .../src/__tests__/FetchUtils.js | 40 +++++++++++++------ .../__tests__/InspectorProxyHttpApi-test.js | 10 +++-- .../src/__tests__/embedderScriptStub-test.js | 7 ++-- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/flow-typed/npm/undici_v5.x.x.js b/flow-typed/npm/undici_v5.x.x.js index 48360acd233..59bdbdb9b83 100644 --- a/flow-typed/npm/undici_v5.x.x.js +++ b/flow-typed/npm/undici_v5.x.x.js @@ -16,6 +16,13 @@ declare interface undici$Agent$Options { } declare module 'undici' { + declare export type RequestOptions = $ReadOnly<{ + dispatcher?: Dispatcher, + method?: string, + headers?: HeadersInit, + ... + }>; + declare export class Dispatcher extends events$EventEmitter { constructor(): void; } @@ -23,4 +30,17 @@ declare module 'undici' { declare export class Agent extends Dispatcher { constructor(opts?: undici$Agent$Options): void; } + + declare export function request( + url: string | URL, + options: RequestOptions, + ): Promise<{ + statusCode: number, + headers: Headers, + body: { + read(): Promise, + ... + }, + ... + }>; } diff --git a/packages/dev-middleware/src/__tests__/FetchUtils.js b/packages/dev-middleware/src/__tests__/FetchUtils.js index 43195383e80..650ce613943 100644 --- a/packages/dev-middleware/src/__tests__/FetchUtils.js +++ b/packages/dev-middleware/src/__tests__/FetchUtils.js @@ -10,8 +10,9 @@ */ import type {JSONSerializable} from '../inspector-proxy/types'; +import type {RequestOptions} from 'undici'; -import {Agent} from 'undici'; +import {Agent, request} from 'undici'; declare var globalThis: $FlowFixMe; @@ -19,15 +20,22 @@ declare var globalThis: $FlowFixMe; * A version of `fetch` that is usable with the HTTPS server created in * ServerUtils (which uses a self-signed certificate). */ -export async function fetchLocal( +export async function requestLocal( url: string, - options?: Partial[1] & {dispatcher?: mixed}>, -): ReturnType { - return await fetch(url, { + options?: RequestOptions, +): Promise<{ + statusCode: number, + headers: Headers, + bodyBuffer: Buffer, +}> { + const { + statusCode, + headers: rawHeaders, + body, + } = await request(url, { ...options, - // Node's native `fetch` comes from undici and supports the same options, - // including `dispatcher` which we use to make it accept self-signed - // certificates. + + // Use undici's `dispatcher` to make it accept self-signed certificates. dispatcher: options?.dispatcher ?? new Agent({ @@ -36,14 +44,22 @@ export async function fetchLocal( }, }), }); + return { + statusCode, + bodyBuffer: await body.read(), + headers: new Headers(rawHeaders), + }; } export async function fetchJson(url: string): Promise { - const response = await fetchLocal(url); - if (!response.ok) { - throw new Error(`HTTP ${response.status} ${response.statusText}`); + const response = await requestLocal(url); + if (response.statusCode !== 200) { + throw new Error(`HTTP ${response.statusCode}`); } - return response.json(); + if (!response.headers.get('Content-Type')?.startsWith('application/json')) { + throw new Error('Expected Content-Type: application/json'); + } + return JSON.parse(response.bodyBuffer.toString()); } /** diff --git a/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js b/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js index d87c3a526f4..d55fe867a72 100644 --- a/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js +++ b/packages/dev-middleware/src/__tests__/InspectorProxyHttpApi-test.js @@ -15,7 +15,7 @@ import type { } from '../inspector-proxy/types'; import DefaultBrowserLauncher from '../utils/DefaultBrowserLauncher'; -import {fetchJson, fetchLocal} from './FetchUtils'; +import {fetchJson, requestLocal} from './FetchUtils'; import {createDeviceMock} from './InspectorDeviceUtils'; import {withAbortSignalForEachTest} from './ResourceUtils'; import {withServerForEachTest} from './ServerUtils'; @@ -362,7 +362,7 @@ describe('inspector proxy HTTP API', () => { jest.advanceTimersByTime(PAGES_POLLING_DELAY); - const response = await fetchLocal( + const response = await requestLocal( `${serverRef.serverBaseUrl}${endpoint}`, ); expect(response.headers.get('Content-Length')).not.toBeNull(); @@ -415,10 +415,12 @@ describe('inspector proxy HTTP API', () => { ); openUrl.searchParams.set('target', firstPage.id); // Request to open the debugger for the first device - const response = await fetchLocal(openUrl.toString(), {method: 'POST'}); + const response = await requestLocal(openUrl.toString(), { + method: 'POST', + }); // Ensure the request was handled properly - expect(response.status).toBe(200); + expect(response.statusCode).toBe(200); // Ensure the debugger was launched expect(launchDebuggerSpy).toHaveBeenCalledWith(expect.any(String)); } finally { diff --git a/packages/dev-middleware/src/__tests__/embedderScriptStub-test.js b/packages/dev-middleware/src/__tests__/embedderScriptStub-test.js index 2885727f37c..b84c7cedef9 100644 --- a/packages/dev-middleware/src/__tests__/embedderScriptStub-test.js +++ b/packages/dev-middleware/src/__tests__/embedderScriptStub-test.js @@ -9,7 +9,7 @@ * @oncall react_native */ -import {fetchLocal} from './FetchUtils'; +import {requestLocal} from './FetchUtils'; import {withServerForEachTest} from './ServerUtils'; jest.useRealTimers(); @@ -22,11 +22,10 @@ describe('embedder script', () => { }); test('is always served', async () => { - const resp = await fetchLocal( + const resp = await requestLocal( serverRef.serverBaseUrl + '/debugger-frontend/embedder-static/embedderScript.js', ); - expect(resp.ok).toBeTruthy(); - expect(resp.status).toBe(200); + expect(resp.statusCode).toBe(200); }); });