Skip to content

Commit 75af0e4

Browse files
motiz88facebook-github-bot
authored andcommitted
Add multi-session support to Device.js (facebook#55237)
Summary: Adds multi-session support to the Node side of the inspector-proxy protocol implementation. The protocol now has an explicit `sessionId` property in all relevant messages. Multi-session support is fully backwards compatible: * An app that does not report itself as multi-session capable will get the old proxy behaviour. * If the `enableStandaloneFuseboxShell` experiment flag is disabled by the integrator/framework, we automatically disable multi-session support, too. This is to guarantee that we continue to have at most one active RNDT window/tab open per app. (The standalone shell guarantees this independently of the proxy, while the old browser-based flow requires the proxy to keep enforcing the single-session UX.) Changelog: [General][Added] Support multiple CDP connections to one React Native Host (diff 2 of 2) Reviewed By: robhogan Differential Revision: D90174643
1 parent f0ce6aa commit 75af0e4

12 files changed

+824
-165
lines changed

packages/dev-middleware/src/__tests__/InspectorDeviceUtils.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type {
1616
JSONSerializable,
1717
MessageFromDevice,
1818
MessageToDevice,
19-
WrappedEvent,
19+
WrappedEventToDevice,
2020
} from '../inspector-proxy/types';
2121

2222
import nullthrows from 'nullthrows';
@@ -106,9 +106,14 @@ export class DeviceMock extends DeviceAgent {
106106
| Promise<GetPagesResponse['payload'] | void>
107107
| void,
108108
> = jest.fn();
109-
+wrappedEvent: JestMockFn<[message: WrappedEvent], void> = jest.fn();
109+
+wrappedEvent: JestMockFn<[message: WrappedEventToDevice], void> = jest.fn();
110110
+wrappedEventParsed: JestMockFn<
111-
[payload: {...WrappedEvent['payload'], wrappedEvent: JSONSerializable}],
111+
[
112+
payload: {
113+
...WrappedEventToDevice['payload'],
114+
wrappedEvent: JSONSerializable,
115+
},
116+
],
112117
void,
113118
> = jest.fn();
114119

packages/dev-middleware/src/__tests__/InspectorProtocolUtils.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,15 @@ export async function sendFromDebuggerToTarget<Message: CdpMessageToTarget>(
8181
device: DeviceMock,
8282
pageId: string,
8383
message: Message,
84+
{sessionId}: {sessionId?: string} = {},
8485
): Promise<Message> {
8586
const originalEventCallsArray = device.wrappedEventParsed.mock.calls;
8687
const originalEventCallCount = originalEventCallsArray.length;
8788
debugger_.send(message);
8889
await until(() =>
8990
expect(device.wrappedEventParsed).toBeCalledWith({
9091
pageId,
92+
sessionId: sessionId ?? expect.any(String),
9193
wrappedEvent: expect.objectContaining({id: message.id}),
9294
}),
9395
);
@@ -125,9 +127,10 @@ export async function createAndConnectTarget(
125127
deviceId?: ?string,
126128
deviceHostHeader?: ?string,
127129
}> = {},
128-
): Promise<{device: DeviceMock, debugger_: DebuggerMock}> {
130+
): Promise<{device: DeviceMock, debugger_: DebuggerMock, sessionId: string}> {
129131
let device;
130132
let debugger_;
133+
let sessionId;
131134
try {
132135
device = await createDeviceMock(
133136
`${serverRef.serverBaseWsUrl}/inspector/device?device=${
@@ -149,16 +152,26 @@ export async function createAndConnectTarget(
149152
const [{webSocketDebuggerUrl}] = pageList;
150153
expect(webSocketDebuggerUrl).toBeDefined();
151154

155+
const originalConnectCallsArray = device.connect.mock.calls;
156+
const originalConnectCallCount = originalConnectCallsArray.length;
152157
debugger_ = await createDebuggerMock(
153158
webSocketDebuggerUrl,
154159
signal,
155160
debuggerHeaders,
156161
);
157162
await until(() => expect(device.connect).toBeCalled());
163+
// Find the first connect call that wasn't already in the mock calls array
164+
// before we connected the debugger.
165+
const newConnectCalls =
166+
originalConnectCallsArray === device.connect.mock.calls
167+
? device.connect.mock.calls.slice(originalConnectCallCount)
168+
: device.connect.mock.calls;
169+
const [[{payload}]] = newConnectCalls;
170+
sessionId = payload.sessionId;
158171
} catch (e) {
159172
device?.close();
160173
debugger_?.close();
161174
throw e;
162175
}
163-
return {device, debugger_};
176+
return {device, debugger_, sessionId};
164177
}

packages/dev-middleware/src/__tests__/InspectorProxyCdpRewritingHacks-test.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ describe.each(['HTTP', 'HTTPS'])(
566566

567567
describe('Debugger.getScriptSource', () => {
568568
test('should forward request directly to device (does not read source from disk in proxy)', async () => {
569-
const {device, debugger_} = await createAndConnectTarget(
569+
const {device, debugger_, sessionId} = await createAndConnectTarget(
570570
serverRef,
571571
autoCleanup.signal,
572572
pageDescription,
@@ -579,10 +579,17 @@ describe.each(['HTTP', 'HTTPS'])(
579579
scriptId: 'script1',
580580
},
581581
};
582-
await sendFromDebuggerToTarget(debugger_, device, 'page1', message);
582+
await sendFromDebuggerToTarget(
583+
debugger_,
584+
device,
585+
'page1',
586+
message,
587+
{sessionId},
588+
);
583589

584590
expect(device.wrappedEventParsed).toBeCalledWith({
585591
pageId: 'page1',
592+
sessionId,
586593
wrappedEvent: message,
587594
});
588595
} finally {
@@ -594,7 +601,7 @@ describe.each(['HTTP', 'HTTPS'])(
594601

595602
describe('Network.loadNetworkResource', () => {
596603
test('should forward event directly to client (does not rewrite url host)', async () => {
597-
const {device, debugger_} = await createAndConnectTarget(
604+
const {device, debugger_, sessionId} = await createAndConnectTarget(
598605
serverRef,
599606
autoCleanup.signal,
600607
pageDescription,
@@ -607,11 +614,19 @@ describe.each(['HTTP', 'HTTPS'])(
607614
url: `${protocol.toLowerCase()}://10.0.2.2:${serverRef.port}`,
608615
},
609616
};
610-
await sendFromDebuggerToTarget(debugger_, device, 'page1', message);
611-
expect(device.wrappedEventParsed).toBeCalledWith({
612-
pageId: 'page1',
613-
wrappedEvent: message,
614-
});
617+
await sendFromDebuggerToTarget(
618+
debugger_,
619+
device,
620+
'page1',
621+
message,
622+
{sessionId},
623+
);
624+
expect(device.wrappedEventParsed).toBeCalledWith(
625+
expect.objectContaining({
626+
pageId: 'page1',
627+
wrappedEvent: message,
628+
}),
629+
);
615630
} finally {
616631
device.close();
617632
debugger_.close();

0 commit comments

Comments
 (0)