Files
react-native/packages/dev-middleware/src/__tests__/InspectorDebuggerUtils.js
T
Moti Zilberman 7a69fe424d Migrate to private member syntax (#42299)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/42299

Changelog: [Internal]

TSIA

Reviewed By: hoxyq

Differential Revision: D52798108

fbshipit-source-id: 9ba980b9fb439b1aeee3eb1b08535b95556d72bb
2024-01-17 08:03:34 -08:00

123 lines
3.0 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {JSONSerializable} from '../inspector-proxy/types';
import type {
CdpMessageToTarget,
CdpResponseFromTarget,
} from './InspectorProtocolUtils';
import nullthrows from 'nullthrows';
import until from 'wait-for-expect';
import WebSocket from 'ws';
export class DebuggerAgent {
#ws: ?WebSocket;
#readyPromise: Promise<void>;
constructor(url: string, signal?: AbortSignal) {
const ws = new WebSocket(url, {
// The mock server uses a self-signed certificate.
rejectUnauthorized: false,
});
this.#ws = ws;
ws.on('message', data => {
this.__handle(JSON.parse(data.toString()));
});
if (signal != null) {
signal.addEventListener('abort', () => {
this.close();
});
}
this.#readyPromise = new Promise<void>((resolve, reject) => {
ws.once('open', () => {
resolve();
});
ws.once('error', error => {
reject(error);
});
});
}
__handle(message: JSONSerializable): void {}
send(message: JSONSerializable) {
if (!this.#ws) {
return;
}
this.#ws.send(JSON.stringify(message));
}
ready(): Promise<void> {
return this.#readyPromise;
}
close() {
if (!this.#ws) {
return;
}
try {
this.#ws.terminate();
} catch {}
this.#ws = null;
}
// $FlowIgnore[unsafe-getters-setters]
get socket(): WebSocket {
return nullthrows(this.#ws);
}
}
export class DebuggerMock extends DebuggerAgent {
// Empty handlers
+handle: JestMockFn<[message: JSONSerializable], void> = jest.fn();
__handle(message: JSONSerializable): void {
this.handle(message);
}
async sendAndGetResponse(
message: CdpMessageToTarget,
): Promise<CdpResponseFromTarget> {
const originalHandleCallsArray = this.handle.mock.calls;
const originalHandleCallCount = originalHandleCallsArray.length;
this.send(message);
await until(() =>
expect(this.handle).toHaveBeenCalledWith(
expect.objectContaining({
id: message.id,
}),
),
);
// Find the first matching handle call that wasn't already in the mock calls
// array before we sent the message.
const newHandleCalls =
originalHandleCallsArray === this.handle.mock.calls
? this.handle.mock.calls.slice(originalHandleCallCount)
: this.handle.mock.calls;
// $FlowIgnore[incompatible-use]
// $FlowIgnore[prop-missing]
const [response] = newHandleCalls.find(args => args[0].id === message.id);
// $FlowIgnore[incompatible-return]
// $FlowIgnore[incompatible-indexer]
return response;
}
}
export async function createDebuggerMock(
url: string,
signal: AbortSignal,
): Promise<DebuggerMock> {
const debuggerMock = new DebuggerMock(url, signal);
await debuggerMock.ready();
return debuggerMock;
}