mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
fix(dev-middleware): create custom message handler for synthetic page (#43559)
Summary: This is a follow-up bugfix for expo/expo#27425, related to: - https://github.com/facebook/react-native/issues/43291 - https://github.com/facebook/react-native/issues/43307 - https://github.com/facebook/react-native/issues/43310 - https://github.com/facebook/react-native/issues/43364 The middleware API works as intended and can run our extended CDP events. Unfortunately, this only applies to an actual `Page` from the device, not for the `React Native Experimental (Improved Chrome Reloads)` synthetic / virtual page. That's because the middleware instantiation gets aborted when the page can't be found in `this.#pages.get(pageId)`, which always returns `null` for this synthetic page. ## Changelog: [GENERAL] [FIXED] Create custom message handler for synthetic page Pull Request resolved: https://github.com/facebook/react-native/pull/43559 Test Plan: See added test case. Reviewed By: motiz88 Differential Revision: D55129412 Pulled By: huntie fbshipit-source-id: 9679d8fe68f3cb4104f4a042f93612b995baddc9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
d03b5dca74
commit
652c74104a
@@ -9,9 +9,13 @@
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import {fetchJson} from './FetchUtils';
|
||||
import {createDebuggerMock} from './InspectorDebuggerUtils';
|
||||
import {createDeviceMock} from './InspectorDeviceUtils';
|
||||
import {createAndConnectTarget} from './InspectorProtocolUtils';
|
||||
import {withAbortSignalForEachTest} from './ResourceUtils';
|
||||
import {baseUrlForServer, createServer} from './ServerUtils';
|
||||
import invariant from 'invariant';
|
||||
import until from 'wait-for-expect';
|
||||
|
||||
// WebSocket is unreliable when using fake timers.
|
||||
@@ -76,6 +80,86 @@ describe('inspector proxy device message middleware', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('middleware is created with device, debugger, and page information for synthetic reloadable page', async () => {
|
||||
const createCustomMessageHandler = jest.fn().mockImplementation(() => null);
|
||||
const {server} = await createServer({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
unstable_customInspectorMessageHandler: createCustomMessageHandler,
|
||||
});
|
||||
const {serverBaseUrl, serverBaseWsUrl} = serverRefUrls(server);
|
||||
|
||||
let device, debugger_;
|
||||
try {
|
||||
device = await createDeviceMock(
|
||||
`${serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
// Mock the device to return a normal React (Native) page
|
||||
device.getPages.mockImplementation(() => [
|
||||
{
|
||||
...page,
|
||||
// NOTE: 'React' is a magic string used to detect React Native pages.
|
||||
title: 'React Native (mock)',
|
||||
},
|
||||
]);
|
||||
|
||||
// Retrieve the full page list from device
|
||||
let pageList;
|
||||
await until(async () => {
|
||||
pageList = (await fetchJson(
|
||||
`${serverBaseUrl}/json`,
|
||||
// $FlowIgnore[unclear-type]
|
||||
): any);
|
||||
expect(pageList.length).toBeGreaterThan(0);
|
||||
});
|
||||
invariant(pageList != null, '');
|
||||
|
||||
// Find the synthetic page
|
||||
const syntheticPage = pageList.find(
|
||||
({title}) =>
|
||||
// NOTE: Magic string used for the synthetic page that has a stable ID
|
||||
title === 'React Native Experimental (Improved Chrome Reloads)',
|
||||
);
|
||||
expect(syntheticPage).not.toBeUndefined();
|
||||
|
||||
// Connect the debugger to this synthetic page
|
||||
debugger_ = await createDebuggerMock(
|
||||
syntheticPage.webSocketDebuggerUrl,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
|
||||
// Ensure the middleware was created with the device information
|
||||
await until(() =>
|
||||
expect(createCustomMessageHandler).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
page: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
title: syntheticPage.title,
|
||||
vm: syntheticPage.vm,
|
||||
app: expect.any(String),
|
||||
capabilities: expect.any(Object),
|
||||
}),
|
||||
device: expect.objectContaining({
|
||||
appId: expect.any(String),
|
||||
id: expect.any(String),
|
||||
name: expect.any(String),
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
debugger: expect.objectContaining({
|
||||
userAgent: null,
|
||||
sendMessage: expect.any(Function),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
device?.close();
|
||||
debugger_?.close();
|
||||
await closeServer(server);
|
||||
}
|
||||
});
|
||||
|
||||
test('send message functions are passing messages to sockets', async () => {
|
||||
const handleDebuggerMessage = jest.fn();
|
||||
const handleDeviceMessage = jest.fn();
|
||||
|
||||
@@ -176,14 +176,7 @@ export default class Device {
|
||||
|
||||
getPagesList(): $ReadOnlyArray<Page> {
|
||||
if (this.#lastConnectedLegacyReactNativePage) {
|
||||
const reactNativeReloadablePage = {
|
||||
id: REACT_NATIVE_RELOADABLE_PAGE_ID,
|
||||
title: 'React Native Experimental (Improved Chrome Reloads)',
|
||||
vm: "don't use",
|
||||
app: this.#app,
|
||||
capabilities: {},
|
||||
};
|
||||
return [...this.#pages.values(), reactNativeReloadablePage];
|
||||
return [...this.#pages.values(), this.#createSyntheticPage()];
|
||||
} else {
|
||||
return [...this.#pages.values()];
|
||||
}
|
||||
@@ -224,7 +217,10 @@ export default class Device {
|
||||
|
||||
// TODO(moti): Handle null case explicitly, e.g. refuse to connect to
|
||||
// unknown pages.
|
||||
const page: ?Page = this.#pages.get(pageId);
|
||||
const page: ?Page =
|
||||
pageId === REACT_NATIVE_RELOADABLE_PAGE_ID
|
||||
? this.#createSyntheticPage()
|
||||
: this.#pages.get(pageId);
|
||||
|
||||
this.#debuggerConnection = debuggerInfo;
|
||||
|
||||
@@ -379,6 +375,19 @@ export default class Device {
|
||||
return page.capabilities[flag] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the synthetic "React Native Experimental (Improved Chrome Reloads)" page.
|
||||
*/
|
||||
#createSyntheticPage(): Page {
|
||||
return {
|
||||
id: REACT_NATIVE_RELOADABLE_PAGE_ID,
|
||||
title: 'React Native Experimental (Improved Chrome Reloads)',
|
||||
vm: "don't use",
|
||||
app: this.#app,
|
||||
capabilities: {},
|
||||
};
|
||||
}
|
||||
|
||||
// Handles messages received from device:
|
||||
// 1. For getPages responses updates local #pages list.
|
||||
// 2. All other messages are forwarded to debugger as wrappedEvent.
|
||||
|
||||
Reference in New Issue
Block a user