Add infra to test a plugin

Summary:
There were several reports that it is hard to test an entire plugin, and send updates.

This should address that and help with avoiding plugin regressions in the future.

Added a `renderMockFlipperWithPlugin` utility and demo test.

Reviewed By: passy

Differential Revision: D22114217

fbshipit-source-id: ceefd954abc4ea47c336eab495fb50f161993df9
This commit is contained in:
Michel Weststrate
2020-06-22 06:04:14 -07:00
committed by Facebook GitHub Bot
parent 5dd6edc533
commit f373872b5c
3 changed files with 148 additions and 9 deletions

View File

@@ -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(),
);
}

View File

@@ -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<any, any, any> {
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 (
<h1>
Hello:{' '}
<span data-testid="counter">{this.props.persistedState.count}</span>
</h1>
);
}
}
test('Plugin container can render plugin and receive updates', async () => {
await renderMockFlipperWithPlugin(
TestPlugin,
async ({renderer, sendMessage, act}) => {
expect(renderer.baseElement).toMatchInlineSnapshot(`
<body>
<div>
<div
class="css-1orvm1g-View-FlexBox-FlexColumn"
>
<h1>
Hello:
<span
data-testid="counter"
>
0
</span>
</h1>
</div>
<div
class="css-bxcvv9-View-FlexBox-FlexRow"
id="detailsSidebar"
/>
</div>
</body>
`);
act(() => {
sendMessage('inc', {delta: 2});
});
expect((await renderer.findByTestId('counter')).textContent).toBe('2');
},
);
});

View File

@@ -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 {
};
}
export async function createMockFlipperWithPlugin(
pluginClazz: typeof FlipperPlugin,
callback: (args: {
type MockFlipperCallbackArgs = {
client: Client;
device: BaseDevice;
store: Store;
sendMessage(method: string, params: any): void;
createDevice(serial: string): BaseDevice;
createClient(device: BaseDevice, name: string): Client;
}) => Promise<void>,
logger: Logger;
};
export async function createMockFlipperWithPlugin(
pluginClazz: typeof FlipperPlugin,
callback: (args: MockFlipperCallbackArgs) => Promise<void>,
) {
const store = createStore(reducers);
const logger = createStubLogger();
@@ -141,5 +152,50 @@ export async function createMockFlipperWithPlugin(
},
createDevice,
createClient,
logger,
});
}
type Renderer = RenderResult<typeof import('testing-library__dom/queries')>;
export async function renderMockFlipperWithPlugin(
pluginClazz: typeof FlipperPlugin,
callback: (
args: MockFlipperCallbackArgs & {
renderer: Renderer;
act: (cb: () => void) => void;
},
) => Promise<void>,
) {
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(
<Provider store={args.store}>
<PluginContainer logger={args.logger} />
</Provider>,
);
await callback({
...args,
renderer: renderer as any,
act(cb) {
testingLibAct(cb);
args.client.flushMessageBuffer();
},
});
renderer.unmount();
});
}