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: () => {},
|
debug: () => {},
|
||||||
trackTimeSince: () => {},
|
trackTimeSince: () => {},
|
||||||
};
|
};
|
||||||
let mockStore = configureStore([])();
|
|
||||||
const selectedDevice = new BaseDevice(
|
const selectedDevice = new BaseDevice(
|
||||||
'serial',
|
'serial',
|
||||||
'emulator',
|
'emulator',
|
||||||
'TestiPhone',
|
'TestiPhone',
|
||||||
'iOS',
|
'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} = {};
|
const pluginStates: {[key: string]: any} = {};
|
||||||
pluginStates[`${client.id}#TestDevicePlugin`] = {
|
pluginStates[`${clientId}#TestDevicePlugin`] = {
|
||||||
msg: 'Test Device plugin',
|
msg: 'Test Device plugin',
|
||||||
};
|
};
|
||||||
pluginStates[`${client.id}#TestPlugin`] = {
|
pluginStates[`${clientId}#TestPlugin`] = {
|
||||||
msg: 'Test plugin',
|
msg: 'Test plugin',
|
||||||
};
|
};
|
||||||
mockStore = configureStore([])({
|
const mockStore = configureStore([])({
|
||||||
application: {share: {closeOnFinish: false, type: 'link'}},
|
application: {share: {closeOnFinish: false, type: 'link'}},
|
||||||
plugins: {
|
plugins: {
|
||||||
clientPlugins: new Map([['TestPlugin', TestPlugin]]),
|
clientPlugins: new Map([['TestPlugin', TestPlugin]]),
|
||||||
@@ -76,8 +67,23 @@ function getStore(selectedPlugins: Array<string>) {
|
|||||||
},
|
},
|
||||||
pluginStates,
|
pluginStates,
|
||||||
pluginMessageQueue: [],
|
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;
|
return mockStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ function mockPluginState(
|
|||||||
function mockPluginDefinition(name: string): PluginDetails {
|
function mockPluginDefinition(name: string): PluginDetails {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
|
id: name,
|
||||||
out: 'out',
|
out: 'out',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
import {State as PluginStatesState} from '../reducers/pluginStates';
|
import {State as PluginStatesState} from '../reducers/pluginStates';
|
||||||
import {State as PluginsState} from '../reducers/plugins';
|
import {State as PluginsState} from '../reducers/plugins';
|
||||||
import {State as PluginMessageQueueState} from '../reducers/pluginMessageQueue';
|
import {State as PluginMessageQueueState} from '../reducers/pluginMessageQueue';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
|
||||||
import {deconstructPluginKey, deconstructClientId} from './clientUtils';
|
import {deconstructPluginKey, deconstructClientId} from './clientUtils';
|
||||||
|
|
||||||
type Client = import('../Client').default;
|
type Client = import('../Client').default;
|
||||||
@@ -27,14 +26,10 @@ export const defaultEnabledBackgroundPlugins = ['Navigation']; // The navigation
|
|||||||
export function pluginsClassMap(
|
export function pluginsClassMap(
|
||||||
plugins: PluginsState,
|
plugins: PluginsState,
|
||||||
): Map<string, PluginDefinition> {
|
): Map<string, PluginDefinition> {
|
||||||
const pluginsMap: Map<string, PluginDefinition> = new Map([]);
|
return new Map<string, PluginDefinition>([
|
||||||
plugins.clientPlugins.forEach((val, key) => {
|
...plugins.clientPlugins.entries(),
|
||||||
pluginsMap.set(key, val);
|
...plugins.devicePlugins.entries(),
|
||||||
});
|
]);
|
||||||
plugins.devicePlugins.forEach((val, key) => {
|
|
||||||
pluginsMap.set(key, val);
|
|
||||||
});
|
|
||||||
return pluginsMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPluginKey(
|
export function getPluginKey(
|
||||||
@@ -141,65 +136,70 @@ export function getActivePersistentPlugins(
|
|||||||
plugins: PluginsState,
|
plugins: PluginsState,
|
||||||
selectedClient?: Client,
|
selectedClient?: Client,
|
||||||
): {id: string; label: string}[] {
|
): {id: string; label: string}[] {
|
||||||
const pluginsMap: Map<string, PluginDefinition> = pluginsClassMap(plugins);
|
const pluginsMap = pluginsClassMap(plugins);
|
||||||
return getPersistentPlugins(plugins)
|
return getPersistentPlugins(plugins)
|
||||||
.map((pluginName) => pluginsMap.get(pluginName)!)
|
.map((pluginName) => pluginsMap.get(pluginName)!)
|
||||||
.sort(sortPluginsByName)
|
.sort(sortPluginsByName)
|
||||||
.map((plugin) => {
|
.filter((plugin) => {
|
||||||
const keys = [
|
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([
|
...new Set([
|
||||||
...Object.keys(pluginsState),
|
...Object.keys(pluginsState),
|
||||||
...Object.keys(pluginsMessageQueue),
|
...Object.keys(pluginsMessageQueue),
|
||||||
]),
|
]),
|
||||||
]
|
].map((key) => deconstructPluginKey(key).pluginName);
|
||||||
.filter((k) => !selectedClient || k.includes(selectedClient.id))
|
return (
|
||||||
.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) ||
|
(plugin && plugin.exportPersistedState != undefined) ||
|
||||||
keys.includes(plugin.id);
|
isSandyPlugin(plugin) ||
|
||||||
|
pluginsWithReduxData.includes(plugin.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (result
|
})
|
||||||
? {
|
.map((plugin) => ({
|
||||||
id: plugin.id,
|
id: plugin.id,
|
||||||
label: getPluginTitle(plugin),
|
label: getPluginTitle(plugin),
|
||||||
}
|
}));
|
||||||
: undefined)!;
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all enabled plugins that are potentially exportable
|
||||||
|
* @param plugins
|
||||||
|
*/
|
||||||
export function getPersistentPlugins(plugins: PluginsState): Array<string> {
|
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.disabledPlugins, ...plugins.gatekeepedPlugins].forEach(
|
||||||
plugins.gatekeepedPlugins,
|
(plugin) => {
|
||||||
);
|
|
||||||
arr.forEach((plugin: PluginDetails) => {
|
|
||||||
if (pluginsMap.has(plugin.name)) {
|
|
||||||
pluginsMap.delete(plugin.name);
|
pluginsMap.delete(plugin.name);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
plugins.failedPlugins.forEach(([details]) => {
|
||||||
|
pluginsMap.delete(details.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
plugins.failedPlugins.forEach((plugin: [PluginDetails, string]) => {
|
return Array.from(pluginsMap.keys()).filter((plugin) => {
|
||||||
if (plugin[0] && plugin[0].name && pluginsMap.has(plugin[0].name)) {
|
|
||||||
pluginsMap.delete(plugin[0].name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const activePlugins = [...pluginsMap.keys()];
|
|
||||||
|
|
||||||
return activePlugins.filter((plugin) => {
|
|
||||||
const pluginClass = pluginsMap.get(plugin);
|
const pluginClass = pluginsMap.get(plugin);
|
||||||
return (
|
return (
|
||||||
plugin == 'DeviceLogs' ||
|
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.onConnect(connectStub);
|
||||||
client.onDisconnect(disconnectStub);
|
client.onDisconnect(disconnectStub);
|
||||||
client.onDestroy(destroyStub);
|
client.onDestroy(destroyStub);
|
||||||
|
|||||||
@@ -217,6 +217,10 @@ export class SandyPluginInstance {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPersistable(): boolean {
|
||||||
|
return Object.keys(this.rootStates).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
private assertNotDestroyed() {
|
private assertNotDestroyed() {
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
throw new Error('Plugin has been destroyed already');
|
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
|
// Sidebar should be visible now
|
||||||
expect(await renderer.findByText('Extras')).not.toBeNull();
|
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, {}>) {
|
export function plugin(client: FlipperClient<Events, {}>) {
|
||||||
const rows = createState<PersistedState>({});
|
const rows = createState<PersistedState>({}, {persist: 'rows'});
|
||||||
const selectedID = createState<string | null>(null);
|
const selectedID = createState<string | null>(null, {persist: 'selection'});
|
||||||
|
|
||||||
client.onMessage('newRow', (row) => {
|
client.onMessage('newRow', (row) => {
|
||||||
rows.update((draft) => {
|
rows.update((draft) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user