Command processing (2/n): testing

Summary:
*Stack summary*: this stack refactors plugin management actions to perform them in a dispatcher rather than in the root reducer (store.tsx) as all of these actions has side effects. To do that, we store requested plugin management actions (install/update/uninstall, star/unstar) in a queue which is then handled by pluginManager dispatcher. This dispatcher then dispatches all required state updates.

*Diff summary*: refactored Flipper mocking helpers to allow testing of plugin commands, and wrote some tests for pluginManager.

Reviewed By: mweststrate

Differential Revision: D26450344

fbshipit-source-id: 0e8414517cc1ad353781dffd7ffb4a5f9a815d38
This commit is contained in:
Anton Nikolaev
2021-02-16 10:46:11 -08:00
committed by Facebook GitHub Bot
parent 8efdde08c4
commit 24aed8fd45
8 changed files with 551 additions and 108 deletions

View File

@@ -8,7 +8,6 @@
*/
import React from 'react';
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import {
render,
@@ -25,18 +24,14 @@ import {
} from '../reducers/connections';
import BaseDevice from '../devices/BaseDevice';
import {rootReducer} from '../store';
import {Store} from '../reducers/index';
import Client, {ClientQuery} from '../Client';
import {buildClientId} from '../utils/clientUtils';
import {Logger} from '../fb-interfaces/Logger';
import {PluginDefinition} from '../plugin';
import {registerPlugins} from '../reducers/plugins';
import PluginContainer from '../PluginContainer';
import {getPluginKey, isDevicePluginDefinition} from '../utils/pluginUtils';
import {getInstance} from '../fb-stubs/Logger';
import {initializeFlipperLibImplementation} from '../utils/flipperLibImplementation';
import MockFlipper from './MockFlipper';
export type MockFlipperResult = {
client: Client;
@@ -64,94 +59,35 @@ type MockOptions = Partial<{
additionalPlugins?: PluginDefinition[];
dontEnableAdditionalPlugins?: true;
asBackgroundPlugin?: true;
supportedPlugins?: string[];
}>;
export async function createMockFlipperWithPlugin(
pluginClazz: PluginDefinition,
options?: MockOptions,
): Promise<MockFlipperResult> {
const store = createStore(rootReducer);
const logger = getInstance();
initializeFlipperLibImplementation(store, logger);
store.dispatch(
registerPlugins([pluginClazz, ...(options?.additionalPlugins ?? [])]),
);
const mockFlipper = new MockFlipper();
await mockFlipper.init({
plugins: [pluginClazz, ...(options?.additionalPlugins ?? [])],
});
const logger = mockFlipper.logger;
const store = mockFlipper.store;
function createDevice(serial: string): BaseDevice {
const device = new BaseDevice(
serial,
'physical',
'MockAndroidDevice',
'Android',
);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: device,
});
device.loadDevicePlugins(store.getState().plugins.devicePlugins);
return device;
}
async function createClient(
const createDevice = (serial: string) => mockFlipper.createDevice({serial});
const createClient = async (
device: BaseDevice,
name: string,
query?: ClientQuery,
skipRegister?: boolean,
): Promise<Client> {
query = query ?? {
app: name,
os: 'Android',
device: device.title,
device_id: device.serial,
sdk_version: 4,
};
const id = buildClientId({
app: query.app,
os: query.os,
device: query.device,
device_id: query.device_id,
});
const client = new Client(
id,
) => {
const client = await mockFlipper.createClient(device, {
name,
query,
null, // create a stub connection to avoid this plugin to be archived?
logger,
store,
[
...(isDevicePluginDefinition(pluginClazz) ? [] : [pluginClazz.id]),
...(options?.dontEnableAdditionalPlugins
? []
: options?.additionalPlugins?.map((p) => p.id) ?? []),
],
device,
);
client.rawCall = async (
method: string,
_fromPlugin: boolean,
params: any,
): Promise<any> => {
const intercepted = options?.onSend?.(method, params);
if (intercepted !== undefined) {
return intercepted;
}
switch (method) {
case 'getPlugins':
// assuming this plugin supports all plugins for now
return {
plugins: [...store.getState().plugins.clientPlugins.keys()],
};
case 'getBackgroundPlugins':
return {plugins: options?.asBackgroundPlugin ? [pluginClazz.id] : []};
default:
throw new Error(
`Test client doesn't support rawCall method '${method}'`,
);
}
};
client.rawSend = jest.fn();
skipRegister,
onSend: options?.onSend,
supportedPlugins: options?.supportedPlugins,
backgroundPlugins: options?.asBackgroundPlugin ? [pluginClazz.id] : [],
});
// enable the plugin
if (
!isDevicePluginDefinition(pluginClazz) &&
@@ -180,17 +116,8 @@ export async function createMockFlipperWithPlugin(
}
});
}
await client.init();
// As convenience, by default we select the new client, star the plugin, and select it
if (!skipRegister) {
store.dispatch({
type: 'NEW_CLIENT',
payload: client,
});
}
return client;
}
};
const device = createDevice('serial');
const client = await createClient(device, 'TestApp');