Make sure Sandy plugins are selectable during export
Summary: This diff makes sure Sandy plugins show up as well in the plugin selector when making exports (and in support form as well). Also verified that this works with the Sandy updated section plugin. Note that persisted state now didn't need any changes in the plugin code to work :) Commented / simplified the calculation of available plugins a little bit and fixed a confusing issue where two different redux stores where created in one unit test, which caused an issue in the new implementation. Reviewed By: jknoxville Differential Revision: D22434301 fbshipit-source-id: c911196bc5b105309e82204188f124f40aab487a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
44f99eb304
commit
6fe477f19b
@@ -40,31 +40,22 @@ function getStore(selectedPlugins: Array<string>) {
|
||||
debug: () => {},
|
||||
trackTimeSince: () => {},
|
||||
};
|
||||
let mockStore = configureStore([])();
|
||||
const selectedDevice = new BaseDevice(
|
||||
'serial',
|
||||
'emulator',
|
||||
'TestiPhone',
|
||||
'iOS',
|
||||
);
|
||||
const client = new Client(
|
||||
generateClientIdentifier(selectedDevice, 'app'),
|
||||
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial'},
|
||||
null,
|
||||
logger,
|
||||
// @ts-ignore
|
||||
mockStore,
|
||||
['TestPlugin', 'TestDevicePlugin'],
|
||||
);
|
||||
|
||||
const clientId = generateClientIdentifier(selectedDevice, 'app');
|
||||
const pluginStates: {[key: string]: any} = {};
|
||||
pluginStates[`${client.id}#TestDevicePlugin`] = {
|
||||
pluginStates[`${clientId}#TestDevicePlugin`] = {
|
||||
msg: 'Test Device plugin',
|
||||
};
|
||||
pluginStates[`${client.id}#TestPlugin`] = {
|
||||
pluginStates[`${clientId}#TestPlugin`] = {
|
||||
msg: 'Test plugin',
|
||||
};
|
||||
mockStore = configureStore([])({
|
||||
const mockStore = configureStore([])({
|
||||
application: {share: {closeOnFinish: false, type: 'link'}},
|
||||
plugins: {
|
||||
clientPlugins: new Map([['TestPlugin', TestPlugin]]),
|
||||
@@ -76,8 +67,23 @@ function getStore(selectedPlugins: Array<string>) {
|
||||
},
|
||||
pluginStates,
|
||||
pluginMessageQueue: [],
|
||||
connections: {selectedApp: client.id, clients: [client]},
|
||||
connections: {selectedApp: clientId, clients: []},
|
||||
});
|
||||
|
||||
const client = new Client(
|
||||
clientId,
|
||||
{app: 'app', os: 'iOS', device: 'TestiPhone', device_id: 'serial'},
|
||||
null,
|
||||
logger,
|
||||
// @ts-ignore
|
||||
mockStore,
|
||||
['TestPlugin', 'TestDevicePlugin'],
|
||||
);
|
||||
mockStore.dispatch({
|
||||
type: 'NEW_CLIENT',
|
||||
payload: client,
|
||||
});
|
||||
|
||||
return mockStore;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ function mockPluginState(
|
||||
function mockPluginDefinition(name: string): PluginDetails {
|
||||
return {
|
||||
name,
|
||||
id: name,
|
||||
out: 'out',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
import {State as PluginStatesState} from '../reducers/pluginStates';
|
||||
import {State as PluginsState} from '../reducers/plugins';
|
||||
import {State as PluginMessageQueueState} from '../reducers/pluginMessageQueue';
|
||||
import {PluginDetails} from 'flipper-plugin-lib';
|
||||
import {deconstructPluginKey, deconstructClientId} from './clientUtils';
|
||||
|
||||
type Client = import('../Client').default;
|
||||
@@ -27,14 +26,10 @@ export const defaultEnabledBackgroundPlugins = ['Navigation']; // The navigation
|
||||
export function pluginsClassMap(
|
||||
plugins: PluginsState,
|
||||
): Map<string, PluginDefinition> {
|
||||
const pluginsMap: Map<string, PluginDefinition> = new Map([]);
|
||||
plugins.clientPlugins.forEach((val, key) => {
|
||||
pluginsMap.set(key, val);
|
||||
});
|
||||
plugins.devicePlugins.forEach((val, key) => {
|
||||
pluginsMap.set(key, val);
|
||||
});
|
||||
return pluginsMap;
|
||||
return new Map<string, PluginDefinition>([
|
||||
...plugins.clientPlugins.entries(),
|
||||
...plugins.devicePlugins.entries(),
|
||||
]);
|
||||
}
|
||||
|
||||
export function getPluginKey(
|
||||
@@ -141,65 +136,70 @@ export function getActivePersistentPlugins(
|
||||
plugins: PluginsState,
|
||||
selectedClient?: Client,
|
||||
): {id: string; label: string}[] {
|
||||
const pluginsMap: Map<string, PluginDefinition> = pluginsClassMap(plugins);
|
||||
const pluginsMap = pluginsClassMap(plugins);
|
||||
return getPersistentPlugins(plugins)
|
||||
.map((pluginName) => pluginsMap.get(pluginName)!)
|
||||
.sort(sortPluginsByName)
|
||||
.map((plugin) => {
|
||||
const keys = [
|
||||
...new Set([
|
||||
...Object.keys(pluginsState),
|
||||
...Object.keys(pluginsMessageQueue),
|
||||
]),
|
||||
]
|
||||
.filter((k) => !selectedClient || k.includes(selectedClient.id))
|
||||
.map((key) => deconstructPluginKey(key).pluginName);
|
||||
let result = plugin.id == 'DeviceLogs';
|
||||
const pluginsWithExportPersistedState =
|
||||
plugin && plugin.exportPersistedState != undefined;
|
||||
const pluginsWithReduxData = keys.includes(plugin.id);
|
||||
if (!result && selectedClient) {
|
||||
// If there is a selected client, active persistent plugin is the plugin which is active for selectedClient and also persistent.
|
||||
result =
|
||||
selectedClient.plugins.includes(plugin.id) &&
|
||||
(pluginsWithExportPersistedState || pluginsWithReduxData);
|
||||
} else if (!result && !selectedClient) {
|
||||
// If there is no selected client, active persistent plugin is the plugin which is just persistent.
|
||||
result =
|
||||
(plugin && plugin.exportPersistedState != undefined) ||
|
||||
keys.includes(plugin.id);
|
||||
.filter((plugin) => {
|
||||
if (plugin.id == 'DeviceLogs') {
|
||||
return true;
|
||||
}
|
||||
if (selectedClient) {
|
||||
const pluginKey = getPluginKey(
|
||||
selectedClient.id,
|
||||
{serial: selectedClient.query.device_id},
|
||||
plugin.id,
|
||||
);
|
||||
// If there is a selected client, active persistent plugins are those that (can) have persisted state
|
||||
return (
|
||||
selectedClient.isEnabledPlugin(plugin.id) &&
|
||||
// this plugin can fetch and export state
|
||||
(plugin.exportPersistedState ||
|
||||
// this plugin has some persisted state already
|
||||
pluginsState[pluginKey] ||
|
||||
pluginsMessageQueue[pluginKey] ||
|
||||
// this plugin has some persistable sandy state
|
||||
selectedClient.sandyPluginStates.get(plugin.id)?.isPersistable())
|
||||
);
|
||||
}
|
||||
{
|
||||
// If there is no selected client, active persistent plugin is the plugin which is just persistent.
|
||||
const pluginsWithReduxData = [
|
||||
...new Set([
|
||||
...Object.keys(pluginsState),
|
||||
...Object.keys(pluginsMessageQueue),
|
||||
]),
|
||||
].map((key) => deconstructPluginKey(key).pluginName);
|
||||
return (
|
||||
(plugin && plugin.exportPersistedState != undefined) ||
|
||||
isSandyPlugin(plugin) ||
|
||||
pluginsWithReduxData.includes(plugin.id)
|
||||
);
|
||||
}
|
||||
return (result
|
||||
? {
|
||||
id: plugin.id,
|
||||
label: getPluginTitle(plugin),
|
||||
}
|
||||
: undefined)!;
|
||||
})
|
||||
.filter(Boolean);
|
||||
.map((plugin) => ({
|
||||
id: plugin.id,
|
||||
label: getPluginTitle(plugin),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all enabled plugins that are potentially exportable
|
||||
* @param plugins
|
||||
*/
|
||||
export function getPersistentPlugins(plugins: PluginsState): Array<string> {
|
||||
const pluginsMap: Map<string, PluginDefinition> = pluginsClassMap(plugins);
|
||||
const pluginsMap = pluginsClassMap(plugins);
|
||||
|
||||
const arr: Array<PluginDetails> = plugins.disabledPlugins.concat(
|
||||
plugins.gatekeepedPlugins,
|
||||
);
|
||||
arr.forEach((plugin: PluginDetails) => {
|
||||
if (pluginsMap.has(plugin.name)) {
|
||||
[...plugins.disabledPlugins, ...plugins.gatekeepedPlugins].forEach(
|
||||
(plugin) => {
|
||||
pluginsMap.delete(plugin.name);
|
||||
}
|
||||
},
|
||||
);
|
||||
plugins.failedPlugins.forEach(([details]) => {
|
||||
pluginsMap.delete(details.id);
|
||||
});
|
||||
|
||||
plugins.failedPlugins.forEach((plugin: [PluginDetails, string]) => {
|
||||
if (plugin[0] && plugin[0].name && pluginsMap.has(plugin[0].name)) {
|
||||
pluginsMap.delete(plugin[0].name);
|
||||
}
|
||||
});
|
||||
|
||||
const activePlugins = [...pluginsMap.keys()];
|
||||
|
||||
return activePlugins.filter((plugin) => {
|
||||
return Array.from(pluginsMap.keys()).filter((plugin) => {
|
||||
const pluginClass = pluginsMap.get(plugin);
|
||||
return (
|
||||
plugin == 'DeviceLogs' ||
|
||||
|
||||
@@ -35,9 +35,6 @@ export function plugin(client: FlipperClient<Events, Methods>) {
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: add tests for sending and receiving data T68683442
|
||||
// including typescript assertions
|
||||
|
||||
client.onConnect(connectStub);
|
||||
client.onDisconnect(disconnectStub);
|
||||
client.onDestroy(destroyStub);
|
||||
|
||||
@@ -217,6 +217,10 @@ export class SandyPluginInstance {
|
||||
);
|
||||
}
|
||||
|
||||
isPersistable(): boolean {
|
||||
return Object.keys(this.rootStates).length > 0;
|
||||
}
|
||||
|
||||
private assertNotDestroyed() {
|
||||
if (this.destroyed) {
|
||||
throw new Error('Plugin has been destroyed already');
|
||||
|
||||
@@ -119,4 +119,23 @@ test('It can have selection and render details', async () => {
|
||||
|
||||
// Sidebar should be visible now
|
||||
expect(await renderer.findByText('Extras')).not.toBeNull();
|
||||
|
||||
// Verify export
|
||||
expect(plugin.exportState()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"rows": Object {
|
||||
"1": Object {
|
||||
"id": 1,
|
||||
"title": "Dolphin",
|
||||
"url": "http://dolphin.png",
|
||||
},
|
||||
"2": Object {
|
||||
"id": 2,
|
||||
"title": "Turtle",
|
||||
"url": "http://turtle.png",
|
||||
},
|
||||
},
|
||||
"selection": "2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -50,8 +50,8 @@ type PersistedState = {
|
||||
};
|
||||
|
||||
export function plugin(client: FlipperClient<Events, {}>) {
|
||||
const rows = createState<PersistedState>({});
|
||||
const selectedID = createState<string | null>(null);
|
||||
const rows = createState<PersistedState>({}, {persist: 'rows'});
|
||||
const selectedID = createState<string | null>(null, {persist: 'selection'});
|
||||
|
||||
client.onMessage('newRow', (row) => {
|
||||
rows.update((draft) => {
|
||||
|
||||
Reference in New Issue
Block a user