make it easier to test plugin logic

Summary:
This diff will make it easier to test a plugin in a "fully loaded" Flipper state. This makes it possible to test all the wiring of flipper, rather than the individual reducers.

This is in preparation of processing the message queue async.

Reviewed By: passy

Differential Revision: D19140879

fbshipit-source-id: 5a333abe9c7a4d0c33d1d06a105cd094cb8fc19f
This commit is contained in:
Michel Weststrate
2019-12-17 06:01:20 -08:00
committed by Facebook Github Bot
parent 5d17eede85
commit 91d5b95341
4 changed files with 234 additions and 1 deletions

View File

@@ -207,7 +207,7 @@ export default class Client extends EventEmitter {
const client = this; const client = this;
// node.js doesn't support requestIdleCallback // node.js doesn't support requestIdleCallback
this.rIC = this.rIC =
typeof window === 'undefined' typeof window === 'undefined' || !window.requestIdleCallback
? (cb: Function, _: any) => { ? (cb: Function, _: any) => {
cb(); cb();
} }

View File

@@ -0,0 +1,57 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`can create a Fake flipper 1`] = `
Object {
"androidEmulators": Array [],
"clients": Array [
Object {
"id": "TestApp#Android#unit_test#serial",
"query": Object {
"app": "TestApp",
"device": "unit_test",
"device_id": "serial",
"os": "Android",
},
},
],
"deepLinkPayload": null,
"devices": Array [
Object {
"deviceType": "physical",
"logs": Array [],
"os": "Android",
"serial": "serial",
"title": "title",
},
],
"errors": Array [],
"selectedApp": "TestApp#Android#unit_test#serial",
"selectedDevice": Object {
"deviceType": "physical",
"logs": Array [],
"os": "Android",
"serial": "serial",
"title": "title",
},
"selectedPlugin": "TestPlugin",
"staticView": null,
"uninitializedClients": Array [],
"userPreferredApp": null,
"userPreferredDevice": null,
"userPreferredPlugin": "TestPlugin",
"userStarredPlugins": Object {},
}
`;
exports[`can create a Fake flipper 2`] = `
Object {
"clientPlugins": Map {
"TestPlugin" => [Function],
},
"devicePlugins": Map {},
"disabledPlugins": Array [],
"failedPlugins": Array [],
"gatekeepedPlugins": Array [],
"selectedPlugins": Array [],
}
`;

View File

@@ -0,0 +1,63 @@
/**
* 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 {createMockFlipperWithPlugin} from '../test-utils/createMockFlipperWithPlugin';
import {FlipperPlugin} from '../plugin';
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: {},
) {
if (method === 'inc') {
return Object.assign({}, persistedState, {
count: persistedState.count + 1,
});
}
return persistedState;
}
render() {
return null;
}
}
test('can create a Fake flipper', async () => {
await createMockFlipperWithPlugin(
TestPlugin,
async ({client, device, store, sendMessage}) => {
expect(client).toBeTruthy();
expect(device).toBeTruthy();
expect(store).toBeTruthy();
expect(sendMessage).toBeTruthy();
expect(client.plugins.includes(TestPlugin.id)).toBe(true);
expect(store.getState().connections).toMatchSnapshot();
expect(store.getState().plugins).toMatchSnapshot();
sendMessage('inc', {});
expect(store.getState().pluginStates).toMatchInlineSnapshot(`
Object {
"TestApp#Android#unit_test#serial#TestPlugin": Object {
"count": 1,
},
}
`);
},
);
});

View File

@@ -0,0 +1,113 @@
/**
* 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 {createStore} from 'redux';
import {selectPlugin} from '../reducers/connections';
import BaseDevice from '../devices/BaseDevice';
import reducers, {Store} from '../reducers/index';
import Client, {ClientQuery} from '../Client';
import {buildClientId} from '../utils/clientUtils';
import {Logger} from '../fb-interfaces/Logger';
import {FlipperPlugin} from '../plugin';
import {registerPlugins} from '../reducers/plugins';
export function createStubLogger(): Logger {
return {
...console,
track: console.info,
trackTimeSince: console.info,
};
}
export async function createMockFlipperWithPlugin(
pluginClazz: typeof FlipperPlugin,
callback: (args: {
client: Client;
device: BaseDevice;
store: Store;
sendMessage(method: string, params: any): void;
}) => Promise<void>,
) {
const store = createStore(reducers);
const device = new BaseDevice('serial', 'physical', 'title', 'Android');
const logger = createStubLogger();
store.dispatch(registerPlugins([pluginClazz]));
store.dispatch({
type: 'REGISTER_DEVICE',
payload: device,
});
const query: ClientQuery = {
app: 'TestApp',
os: 'Android',
device: 'unit_test',
device_id: device.serial,
};
const id = buildClientId({
app: query.app,
os: query.os,
device: query.device,
device_id: query.device_id,
});
const client = new Client(
id,
query,
null, // create a stub connection to avoid this plugin to be archived?
logger,
store,
[pluginClazz.name],
device,
);
// yikes
client._deviceSet = true;
// client.getDeviceSync = () => device;
client.device = {
then() {
return device;
},
} as any;
store.dispatch({
type: 'NEW_CLIENT',
payload: client,
});
store.dispatch(
selectPlugin({
selectedPlugin: pluginClazz.name,
selectedApp: client.query.app,
deepLinkPayload: null,
}),
);
await callback({
client,
device: device as any,
store,
sendMessage(method, params) {
client.onMessage(
JSON.stringify({
method: 'execute',
params: {
api: pluginClazz.id,
method,
params,
},
}),
);
},
});
}