Introduce isPluginAvailable and selectPlugin
Summary: Introduced API to replace the deprecated `selectPlugin` in Sandy. The API can be used to navigate from `device plugin -> device plugin`, or` client plugin -> device / client plugin` Introduced `isPluginAvailable` as well, so that the user interaction an be fine tuned in case the plugin is not disabled. Reviewed By: jknoxville Differential Revision: D25422370 fbshipit-source-id: c6c603f1c68e6291280b3d0883e474448754ded1
This commit is contained in:
committed by
Facebook GitHub Bot
parent
02a56da3f5
commit
52862f6083
@@ -876,3 +876,128 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
|
||||
});
|
||||
expect(linksSeen).toEqual([theUniverse, 'london!', 'london!']);
|
||||
});
|
||||
|
||||
test('Sandy plugins support isPluginSupported + selectPlugin', async () => {
|
||||
let renders = 0;
|
||||
const linksSeen: any[] = [];
|
||||
|
||||
function MySandyPlugin() {
|
||||
renders++;
|
||||
return <h1>Plugin1</h1>;
|
||||
}
|
||||
|
||||
const plugin = (client: PluginClient) => {
|
||||
const activatedStub = jest.fn();
|
||||
const deactivatedStub = jest.fn();
|
||||
client.onDeepLink((link) => {
|
||||
linksSeen.push(link);
|
||||
});
|
||||
client.onActivate(activatedStub);
|
||||
client.onDeactivate(deactivatedStub);
|
||||
return {
|
||||
activatedStub,
|
||||
deactivatedStub,
|
||||
isPluginAvailable: client.isPluginAvailable,
|
||||
selectPlugin: client.selectPlugin,
|
||||
};
|
||||
};
|
||||
|
||||
const definition = new _SandyPluginDefinition(
|
||||
TestUtils.createMockPluginDetails({id: 'base'}),
|
||||
{
|
||||
plugin,
|
||||
Component: MySandyPlugin,
|
||||
},
|
||||
);
|
||||
const definition2 = new _SandyPluginDefinition(
|
||||
TestUtils.createMockPluginDetails({id: 'other'}),
|
||||
{
|
||||
plugin() {
|
||||
return {};
|
||||
},
|
||||
Component() {
|
||||
return <h1>Plugin2</h1>;
|
||||
},
|
||||
},
|
||||
);
|
||||
const definition3 = new _SandyPluginDefinition(
|
||||
TestUtils.createMockPluginDetails({id: 'device'}),
|
||||
{
|
||||
supportsDevice() {
|
||||
return true;
|
||||
},
|
||||
devicePlugin() {
|
||||
return {};
|
||||
},
|
||||
Component() {
|
||||
return <h1>Plugin3</h1>;
|
||||
},
|
||||
},
|
||||
);
|
||||
const {renderer, client, store} = await renderMockFlipperWithPlugin(
|
||||
definition,
|
||||
{
|
||||
additionalPlugins: [definition2, definition3],
|
||||
},
|
||||
);
|
||||
|
||||
expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(`
|
||||
<h1>
|
||||
Plugin1
|
||||
</h1>
|
||||
`);
|
||||
expect(renders).toBe(1);
|
||||
|
||||
const pluginInstance: ReturnType<typeof plugin> = client.sandyPluginStates.get(
|
||||
definition.id,
|
||||
)!.instanceApi;
|
||||
expect(pluginInstance.isPluginAvailable(definition.id)).toBeTruthy();
|
||||
expect(pluginInstance.isPluginAvailable('nonsense')).toBeFalsy();
|
||||
expect(pluginInstance.isPluginAvailable(definition2.id)).toBeFalsy(); // not enabled yet
|
||||
expect(pluginInstance.isPluginAvailable(definition3.id)).toBeTruthy();
|
||||
expect(pluginInstance.activatedStub).toBeCalledTimes(1);
|
||||
expect(pluginInstance.deactivatedStub).toBeCalledTimes(0);
|
||||
expect(linksSeen).toEqual([]);
|
||||
|
||||
// open a device plugin
|
||||
pluginInstance.selectPlugin(definition3.id);
|
||||
expect(store.getState().connections.selectedPlugin).toBe(definition3.id);
|
||||
expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(`
|
||||
<h1>
|
||||
Plugin3
|
||||
</h1>
|
||||
`);
|
||||
expect(pluginInstance.deactivatedStub).toBeCalledTimes(1);
|
||||
|
||||
// go back by opening own plugin again (funny, but why not)
|
||||
pluginInstance.selectPlugin(definition.id, 'data');
|
||||
expect(store.getState().connections.selectedPlugin).toBe(definition.id);
|
||||
expect(pluginInstance.activatedStub).toBeCalledTimes(2);
|
||||
expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(`
|
||||
<h1>
|
||||
Plugin1
|
||||
</h1>
|
||||
`);
|
||||
expect(linksSeen).toEqual(['data']);
|
||||
|
||||
// try to go to plugin 2, fails (not starred, so no-op)
|
||||
pluginInstance.selectPlugin(definition2.id);
|
||||
expect(store.getState().connections.selectedPlugin).toBe(definition.id);
|
||||
|
||||
// star plugin 2 and navigate to plugin 2
|
||||
store.dispatch(
|
||||
starPlugin({
|
||||
plugin: definition2,
|
||||
selectedApp: client.query.app,
|
||||
}),
|
||||
);
|
||||
pluginInstance.selectPlugin(definition2.id);
|
||||
expect(store.getState().connections.selectedPlugin).toBe(definition2.id);
|
||||
expect(pluginInstance.deactivatedStub).toBeCalledTimes(2);
|
||||
expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(`
|
||||
<h1>
|
||||
Plugin2
|
||||
</h1>
|
||||
`);
|
||||
expect(renders).toBe(2);
|
||||
});
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
import {produce} from 'immer';
|
||||
|
||||
import BaseDevice from '../devices/BaseDevice';
|
||||
import type BaseDevice from '../devices/BaseDevice';
|
||||
import MacDevice from '../devices/MacDevice';
|
||||
import Client from '../Client';
|
||||
import type Client from '../Client';
|
||||
import {UninitializedClient} from '../UninitializedClient';
|
||||
import {isEqual} from 'lodash';
|
||||
import {performance} from 'perf_hooks';
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
act as testingLibAct,
|
||||
} from '@testing-library/react';
|
||||
import {queries} from '@testing-library/dom';
|
||||
import {TestUtils} from 'flipper-plugin';
|
||||
|
||||
import {
|
||||
selectPlugin,
|
||||
@@ -37,7 +36,7 @@ import {registerPlugins} from '../reducers/plugins';
|
||||
import PluginContainer from '../PluginContainer';
|
||||
import {getPluginKey, isDevicePluginDefinition} from '../utils/pluginUtils';
|
||||
import {getInstance} from '../fb-stubs/Logger';
|
||||
import {setFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
||||
import {initializeFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
||||
|
||||
export type MockFlipperResult = {
|
||||
client: Client;
|
||||
@@ -55,7 +54,8 @@ type MockOptions = Partial<{
|
||||
* can be used to intercept outgoing calls. If it returns undefined
|
||||
* the base implementation will be used
|
||||
*/
|
||||
onSend(pluginId: string, method: string, params?: object): any;
|
||||
onSend?: (pluginId: string, method: string, params?: object) => any;
|
||||
additionalPlugins?: PluginDefinition[];
|
||||
}>;
|
||||
|
||||
export async function createMockFlipperWithPlugin(
|
||||
@@ -64,9 +64,10 @@ export async function createMockFlipperWithPlugin(
|
||||
): Promise<MockFlipperResult> {
|
||||
const store = createStore(rootReducer);
|
||||
const logger = getInstance();
|
||||
setFlipperLibImplementation(TestUtils.createMockFlipperLib());
|
||||
|
||||
store.dispatch(registerPlugins([pluginClazz]));
|
||||
initializeFlipperLibImplementation(store, logger);
|
||||
store.dispatch(
|
||||
registerPlugins([pluginClazz, ...(options?.additionalPlugins ?? [])]),
|
||||
);
|
||||
|
||||
function createDevice(serial: string): BaseDevice {
|
||||
const device = new BaseDevice(
|
||||
|
||||
@@ -12,18 +12,18 @@ import type {Logger} from '../fb-interfaces/Logger';
|
||||
import type {Store} from '../reducers';
|
||||
import createPaste from '../fb-stubs/createPaste';
|
||||
import GK from '../fb-stubs/GK';
|
||||
import {getInstance} from '../fb-stubs/Logger';
|
||||
import type BaseDevice from '../devices/BaseDevice';
|
||||
|
||||
let flipperLibInstance: FlipperLib | undefined;
|
||||
|
||||
export function initializeFlipperLibImplementation(
|
||||
_store: Store,
|
||||
_logger: Logger,
|
||||
store: Store,
|
||||
logger: Logger,
|
||||
) {
|
||||
// late require to avoid cyclic dependency
|
||||
const {addSandyPluginEntries} = require('../MenuBar');
|
||||
flipperLibInstance = {
|
||||
logger: getInstance(),
|
||||
logger,
|
||||
enableMenuEntries(entries) {
|
||||
addSandyPluginEntries(entries);
|
||||
},
|
||||
@@ -31,6 +31,44 @@ export function initializeFlipperLibImplementation(
|
||||
GK(gatekeeper: string) {
|
||||
return GK.get(gatekeeper);
|
||||
},
|
||||
isPluginAvailable(device, client, pluginId) {
|
||||
// supported device pluin
|
||||
if (device.devicePlugins.includes(pluginId)) {
|
||||
return true;
|
||||
}
|
||||
if (client) {
|
||||
// plugin supported?
|
||||
if (client.plugins.includes(pluginId)) {
|
||||
// part of an archived device?
|
||||
if (device.isArchived) {
|
||||
return true;
|
||||
}
|
||||
// plugin enabled?
|
||||
if (
|
||||
store
|
||||
.getState()
|
||||
.connections.userStarredPlugins[client.query.app]?.includes(
|
||||
pluginId,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
selectPlugin(device, client, pluginId, deeplink) {
|
||||
store.dispatch({
|
||||
type: 'SELECT_PLUGIN',
|
||||
payload: {
|
||||
selectedPlugin: pluginId,
|
||||
selectedDevice: device as BaseDevice,
|
||||
selectedApp: client ? client.id : null,
|
||||
deepLinkPayload: deeplink,
|
||||
time: Date.now(),
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user