Make sure Sandy plugis can be initialized
Summary:
This diff makes sure sandy plugins are initialized.
Sandy plugins are stored directly in the client for two reasons
1. we want to decouple any plugin state updates from our central redux store, as redux is particularly bad in handling high frequency updates.
2. The lifecycle of a plugin is now bound to the lifecycle of a client. This means that we don't need 'persistedStore' to make sure state is preserved; that is now the default. Switching plugins will no longer reinitialize them (but only run specific hooks, see later diffs).
3. PersistedState will be introduced for sandy plugins as well later, but primarily for import / export / debug reasons.
A significant difference with the current persistent state, is that if a client crashes and reconnects, plugins will loose their state. We can prevent this (again, since state persisting will be reintroduced), but I'm not sure we need that for the specific reconnect scenario. Because
1. we should fix reconnecting clients anyway, and from stats it looks to happen less already
2. reconnects are usually caused by plugins that aggregate a lot of data and get slower over time. Restoring old state also restores those unstabilites.
For the overview bringing back the archi picture of earlier diff:
{F241508042}
Also fixed a bug where enabling background plugins didn't enable them on all devices with that app.
Reviewed By: jknoxville
Differential Revision: D22186276
fbshipit-source-id: 3fd42b577f86920e5280aa8cce1a0bc4d5564ed9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
bf79c9472e
commit
1dc9e899b8
141
desktop/app/src/reducers/__tests__/sandyplugins.node.tsx
Normal file
141
desktop/app/src/reducers/__tests__/sandyplugins.node.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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 {Store, Client} from '../../';
|
||||
import {selectPlugin, starPlugin} from '../../reducers/connections';
|
||||
import {registerPlugins} from '../../reducers/plugins';
|
||||
import {SandyPluginDefinition, SandyPluginInstance} from 'flipper-plugin';
|
||||
|
||||
interface PersistedState {
|
||||
count: 1;
|
||||
}
|
||||
|
||||
const pluginDetails = {
|
||||
id: 'TestPlugin',
|
||||
dir: '',
|
||||
name: 'TestPlugin',
|
||||
specVersion: 0,
|
||||
entry: '',
|
||||
isDefault: false,
|
||||
main: '',
|
||||
source: '',
|
||||
title: 'Testing Plugin',
|
||||
version: '',
|
||||
} as const;
|
||||
|
||||
let TestPlugin: SandyPluginDefinition;
|
||||
|
||||
beforeEach(() => {
|
||||
TestPlugin = new SandyPluginDefinition(pluginDetails, {
|
||||
plugin: jest.fn().mockImplementation(() => ({})),
|
||||
Component: jest.fn().mockImplementation(() => null),
|
||||
});
|
||||
});
|
||||
|
||||
function starTestPlugin(store: Store, client: Client) {
|
||||
store.dispatch(
|
||||
starPlugin({
|
||||
plugin: TestPlugin,
|
||||
selectedApp: client.query.app,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function selectTestPlugin(store: Store, client: Client) {
|
||||
store.dispatch(
|
||||
selectPlugin({
|
||||
selectedPlugin: TestPlugin.id,
|
||||
selectedApp: client.query.app,
|
||||
deepLinkPayload: null,
|
||||
selectedDevice: store.getState().connections.selectedDevice!,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
test('it should initialize starred sandy plugins', async () => {
|
||||
const {client, store} = await createMockFlipperWithPlugin(TestPlugin);
|
||||
|
||||
// already started, so initialized immediately
|
||||
expect(TestPlugin.module.plugin).toBeCalledTimes(1);
|
||||
expect(client.sandyPluginStates.get(TestPlugin.id)).toBeInstanceOf(
|
||||
SandyPluginInstance,
|
||||
);
|
||||
|
||||
selectTestPlugin(store, client);
|
||||
// TODO: make sure lifecycle 'activated' or something is triggered T68683507
|
||||
});
|
||||
|
||||
test('it should cleanup a plugin if disabled', async () => {
|
||||
const {client, store} = await createMockFlipperWithPlugin(TestPlugin);
|
||||
|
||||
expect(TestPlugin.module.plugin).toBeCalledTimes(1);
|
||||
expect(client.sandyPluginStates.get(TestPlugin.id)).toBeInstanceOf(
|
||||
SandyPluginInstance,
|
||||
);
|
||||
|
||||
// unstar
|
||||
starTestPlugin(store, client);
|
||||
expect(client.sandyPluginStates.get(TestPlugin.id)).toBeUndefined();
|
||||
// TODO: make sure onDestroy is called T68683507
|
||||
});
|
||||
|
||||
test('it should not initialize a sandy plugin if not enabled', async () => {
|
||||
const {client, store} = await createMockFlipperWithPlugin(TestPlugin);
|
||||
|
||||
const Plugin2 = new SandyPluginDefinition(
|
||||
{
|
||||
...pluginDetails,
|
||||
name: 'Plugin2',
|
||||
id: 'Plugin2',
|
||||
},
|
||||
{
|
||||
plugin: jest.fn().mockImplementation(() => ({})),
|
||||
Component() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const pluginState1 = client.sandyPluginStates.get(TestPlugin.id);
|
||||
expect(pluginState1).toBeInstanceOf(SandyPluginInstance);
|
||||
store.dispatch(registerPlugins([Plugin2]));
|
||||
await client.refreshPlugins();
|
||||
// not yet enabled, so not yet started
|
||||
expect(client.sandyPluginStates.get(Plugin2.id)).toBeUndefined();
|
||||
expect(Plugin2.module.plugin).toBeCalledTimes(0);
|
||||
|
||||
store.dispatch(
|
||||
starPlugin({
|
||||
plugin: Plugin2,
|
||||
selectedApp: client.query.app,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(client.sandyPluginStates.get(Plugin2.id)).toBeInstanceOf(
|
||||
SandyPluginInstance,
|
||||
);
|
||||
expect(client.sandyPluginStates.get(TestPlugin.id)).toBe(pluginState1); // not reinitialized
|
||||
|
||||
expect(TestPlugin.module.plugin).toBeCalledTimes(1);
|
||||
expect(Plugin2.module.plugin).toBeCalledTimes(1);
|
||||
|
||||
// disable plugin again
|
||||
store.dispatch(
|
||||
starPlugin({
|
||||
plugin: Plugin2,
|
||||
selectedApp: client.query.app,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(client.sandyPluginStates.get(Plugin2.id)).toBeUndefined();
|
||||
// TODO: test destroy hook is called T68683507
|
||||
});
|
||||
|
||||
// TODO: T68683449 state is persisted if a plugin connects and reconnects
|
||||
Reference in New Issue
Block a user