Skip to content

Commit 9cca4c1

Browse files
motiz88facebook-github-bot
authored andcommitted
Add test for connecting multiple debuggers to a single page, reproing "zombie" state (facebook#45034)
Summary: Pull Request resolved: facebook#45034 Changelog: [Internal] TSIA Reviewed By: hoxyq Differential Revision: D58728261 fbshipit-source-id: 32d4b05d69f88e68b5327dc5c8e837291e3d7726
1 parent 6695b6e commit 9cca4c1

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

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

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
*/
1111

1212
import type {
13+
ConnectRequest,
14+
DisconnectRequest,
1315
JsonPagesListResponse,
1416
PageDescription,
1517
} from '../inspector-proxy/types';
1618

1719
import {fetchJson} from './FetchUtils';
1820
import {createDebuggerMock} from './InspectorDebuggerUtils';
1921
import {createDeviceMock} from './InspectorDeviceUtils';
22+
import {sendFromDebuggerToTarget} from './InspectorProtocolUtils';
2023
import {withAbortSignalForEachTest} from './ResourceUtils';
2124
import {withServerForEachTest} from './ServerUtils';
2225
import until from 'wait-for-expect';
@@ -151,5 +154,122 @@ describe.each(['HTTP', 'HTTPS'])(
151154
device1.close();
152155
}
153156
});
157+
158+
test('multiple debuggers to the same page on the same device', async () => {
159+
let device, debugger1, debugger2;
160+
try {
161+
// Connect a device to the proxy.
162+
device = await createDeviceMock(
163+
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
164+
autoCleanup.signal,
165+
);
166+
// Capture a log of events so we can assert on their order later.
167+
const events: Array<
168+
| ConnectRequest
169+
| DisconnectRequest
170+
| {event: 'create-debugger-mock', name: string},
171+
> = [];
172+
device.disconnect.mockImplementation(message => {
173+
events.push(message);
174+
});
175+
device.connect.mockImplementation(message => {
176+
events.push(message);
177+
});
178+
// Set up the page.
179+
device.getPages.mockImplementation(() => [
180+
{
181+
app: 'bar-app',
182+
id: 'page1',
183+
title: 'bar-title',
184+
vm: 'bar-vm',
185+
},
186+
]);
187+
let pageList: Array<PageDescription> = [];
188+
await until(async () => {
189+
pageList = (await fetchJson(
190+
`${serverRef.serverBaseUrl}/json`,
191+
// $FlowIgnore[unclear-type]
192+
): any);
193+
expect(pageList).toHaveLength(1);
194+
});
195+
const [{webSocketDebuggerUrl}] = pageList;
196+
197+
// Connect the first debugger and send a message.
198+
events.push({event: 'create-debugger-mock', name: 'debugger1'});
199+
debugger1 = await createDebuggerMock(
200+
webSocketDebuggerUrl,
201+
autoCleanup.signal,
202+
);
203+
await sendFromDebuggerToTarget(debugger1, device, 'page1', {
204+
method: 'Runtime.enable',
205+
id: 0,
206+
});
207+
208+
// Connect the second debugger.
209+
events.push({event: 'create-debugger-mock', name: 'debugger2'});
210+
debugger2 = await createDebuggerMock(
211+
webSocketDebuggerUrl,
212+
autoCleanup.signal,
213+
);
214+
215+
// The first debugger gets disconnected. TODO: In the future, we should
216+
// amend the protocol to allow for multiple debuggers to connect at the
217+
// same time.
218+
await until(async () => {
219+
expect([
220+
// CLOSING
221+
3,
222+
// CLOSED
223+
4,
224+
]).toContain(debugger1.socket.readyState);
225+
});
226+
227+
// Send a message from the second debugger.
228+
await sendFromDebuggerToTarget(debugger2, device, 'page1', {
229+
method: 'Debugger.enable',
230+
id: 1,
231+
});
232+
233+
// Check the order of `connect` and `disconnect` events received by the
234+
// device. `create-debugger-mock` events are included for convenience.
235+
expect(events).toEqual([
236+
{
237+
event: 'create-debugger-mock',
238+
name: 'debugger1',
239+
},
240+
{
241+
event: 'connect',
242+
payload: {
243+
pageId: 'page1',
244+
},
245+
},
246+
{
247+
event: 'create-debugger-mock',
248+
name: 'debugger2',
249+
},
250+
// FIXME: We currently send `connect` (for debugger2) before
251+
// `disconnect` (for debugger1), which is wrong - it leaves the
252+
// device thinking the connection is gone while the proxy keeps the
253+
// debugger connection open. The user of debugger2 sees the frontend
254+
// in a "zombie" state (not disconnected, but unresponsive).
255+
{
256+
event: 'connect',
257+
payload: {
258+
pageId: 'page1',
259+
},
260+
},
261+
{
262+
event: 'disconnect',
263+
payload: {
264+
pageId: 'page1',
265+
},
266+
},
267+
]);
268+
} finally {
269+
device?.close();
270+
debugger1?.close();
271+
debugger2?.close();
272+
}
273+
});
154274
},
155275
);

0 commit comments

Comments
 (0)