dev-middleware: refactor tests to use undici.request (#47675)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47675

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

fbshipit-source-id: f311b0188d6d0ec220a037774fca78df5373163a
This commit is contained in:
Rob Hogan 2024-11-18 15:14:02 -08:00 committed by Facebook GitHub Bot
parent acf384a72e
commit da62721061
4 changed files with 57 additions and 20 deletions

View File

@ -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<Buffer>,
...
},
...
}>;
}

View File

@ -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<Parameters<typeof fetch>[1] & {dispatcher?: mixed}>,
): ReturnType<typeof fetch> {
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<T: JSONSerializable>(url: string): Promise<T> {
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());
}
/**

View File

@ -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 {

View File

@ -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);
});
});