diff --git a/desktop/app/src/MenuBar.tsx b/desktop/app/src/MenuBar.tsx index 44486b720..e028dc6c7 100644 --- a/desktop/app/src/MenuBar.tsx +++ b/desktop/app/src/MenuBar.tsx @@ -178,7 +178,7 @@ export function activateMenuItems( } // set the application menu again to make sure it updates - electron.remote.Menu.setApplicationMenu( + electron.remote.Menu?.setApplicationMenu( electron.remote.Menu.getApplicationMenu(), ); } diff --git a/desktop/app/src/__tests__/PluginContainer.node.tsx b/desktop/app/src/__tests__/PluginContainer.node.tsx new file mode 100644 index 000000000..fdda244bb --- /dev/null +++ b/desktop/app/src/__tests__/PluginContainer.node.tsx @@ -0,0 +1,83 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import produce from 'immer'; +import {FlipperPlugin} from '../plugin'; +import {renderMockFlipperWithPlugin} from '../test-utils/createMockFlipperWithPlugin'; + +interface PersistedState { + count: 1; +} + +class TestPlugin extends FlipperPlugin { + static id = 'TestPlugin'; + + static defaultPersistedState = { + count: 0, + }; + + static persistedStateReducer( + persistedState: PersistedState, + method: string, + payload: {delta?: number}, + ) { + return produce(persistedState, (draft) => { + if (method === 'inc') { + draft.count += payload?.delta || 1; + } + }); + } + + render() { + return ( +

+ Hello:{' '} + {this.props.persistedState.count} +

+ ); + } +} + +test('Plugin container can render plugin and receive updates', async () => { + await renderMockFlipperWithPlugin( + TestPlugin, + async ({renderer, sendMessage, act}) => { + expect(renderer.baseElement).toMatchInlineSnapshot(` + +
+
+

+ Hello: + + + 0 + +

+
+
+
+ + `); + + act(() => { + sendMessage('inc', {delta: 2}); + }); + + expect((await renderer.findByTestId('counter')).textContent).toBe('2'); + }, + ); +}); diff --git a/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx b/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx index f46631d84..aa6a3703f 100644 --- a/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx +++ b/desktop/app/src/test-utils/createMockFlipperWithPlugin.tsx @@ -7,7 +7,14 @@ * @format */ +import React from 'react'; import {createStore} from 'redux'; +import {Provider} from 'react-redux'; +import { + render, + RenderResult, + act as testingLibAct, +} from '@testing-library/react'; import { selectPlugin, @@ -24,6 +31,7 @@ import {buildClientId} from '../utils/clientUtils'; import {Logger} from '../fb-interfaces/Logger'; import {FlipperPlugin} from '../plugin'; import {registerPlugins} from '../reducers/plugins'; +import PluginContainer from '../PluginContainer'; export function createStubLogger(): Logger { return { @@ -33,16 +41,19 @@ export function createStubLogger(): Logger { }; } +type MockFlipperCallbackArgs = { + client: Client; + device: BaseDevice; + store: Store; + sendMessage(method: string, params: any): void; + createDevice(serial: string): BaseDevice; + createClient(device: BaseDevice, name: string): Client; + logger: Logger; +}; + export async function createMockFlipperWithPlugin( pluginClazz: typeof FlipperPlugin, - callback: (args: { - client: Client; - device: BaseDevice; - store: Store; - sendMessage(method: string, params: any): void; - createDevice(serial: string): BaseDevice; - createClient(device: BaseDevice, name: string): Client; - }) => Promise, + callback: (args: MockFlipperCallbackArgs) => Promise, ) { const store = createStore(reducers); const logger = createStubLogger(); @@ -141,5 +152,50 @@ export async function createMockFlipperWithPlugin( }, createDevice, createClient, + logger, + }); +} + +type Renderer = RenderResult; + +export async function renderMockFlipperWithPlugin( + pluginClazz: typeof FlipperPlugin, + callback: ( + args: MockFlipperCallbackArgs & { + renderer: Renderer; + act: (cb: () => void) => void; + }, + ) => Promise, +) { + return createMockFlipperWithPlugin(pluginClazz, async (args) => { + function selectTestPlugin(store: Store, client: Client) { + store.dispatch( + selectPlugin({ + selectedPlugin: pluginClazz.id, + selectedApp: client.query.app, + deepLinkPayload: null, + selectedDevice: store.getState().connections.selectedDevice!, + }), + ); + } + + selectTestPlugin(args.store, args.client); + + const renderer = render( + + + , + ); + + await callback({ + ...args, + renderer: renderer as any, + act(cb) { + testingLibAct(cb); + args.client.flushMessageBuffer(); + }, + }); + + renderer.unmount(); }); }