Remove classic plugin infra
Summary: This removes all code duplication / old plugin infra that isn't needed anymore when all plugin run on the Sandy plugin infra structure. The diff is quite large, but the minimal one that passes tests and compiles. Existing tests are preserved by wrapping all remaining tests in `wrapSandy` for classic plugins where needed Reviewed By: passy Differential Revision: D29394738 fbshipit-source-id: 1315fabd9f048576aed15ed5f1cb6414d5fdbd40
This commit is contained in:
committed by
Facebook GitHub Bot
parent
9d6abd62c6
commit
16154e1343
@@ -7,7 +7,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {PluginDefinition, FlipperPlugin, FlipperDevicePlugin} from './plugin';
|
||||
import {PluginDefinition} from './plugin';
|
||||
import BaseDevice, {OS} from './devices/BaseDevice';
|
||||
import {Logger} from './fb-interfaces/Logger';
|
||||
import {Store} from './reducers/index';
|
||||
@@ -21,7 +21,6 @@ import invariant from 'invariant';
|
||||
import {
|
||||
getPluginKey,
|
||||
defaultEnabledBackgroundPlugins,
|
||||
isSandyPlugin,
|
||||
} from './utils/pluginUtils';
|
||||
import {processMessagesLater} from './utils/messageQueue';
|
||||
import {emitBytesReceived} from './dispatcher/tracking';
|
||||
@@ -118,10 +117,7 @@ export default class Client extends EventEmitter {
|
||||
messageBuffer: Record<
|
||||
string /*pluginKey*/,
|
||||
{
|
||||
plugin:
|
||||
| typeof FlipperPlugin
|
||||
| typeof FlipperDevicePlugin
|
||||
| _SandyPluginInstance;
|
||||
plugin: _SandyPluginInstance;
|
||||
messages: Params[];
|
||||
}
|
||||
> = {};
|
||||
@@ -220,7 +216,7 @@ export default class Client extends EventEmitter {
|
||||
initFromImport(initialStates: Record<string, Record<string, any>>): this {
|
||||
this.plugins.forEach((pluginId) => {
|
||||
const plugin = this.getPlugin(pluginId);
|
||||
if (isSandyPlugin(plugin)) {
|
||||
if (plugin) {
|
||||
// TODO: needs to be wrapped in error tracking T68955280
|
||||
this.sandyPluginStates.set(
|
||||
plugin.id,
|
||||
@@ -253,7 +249,7 @@ export default class Client extends EventEmitter {
|
||||
) {
|
||||
// start a plugin on start if it is a SandyPlugin, which is enabled, and doesn't have persisted state yet
|
||||
if (
|
||||
isSandyPlugin(plugin) &&
|
||||
plugin &&
|
||||
(isEnabled || defaultEnabledBackgroundPlugins.includes(plugin.id)) &&
|
||||
!this.sandyPluginStates.has(plugin.id)
|
||||
) {
|
||||
|
||||
@@ -7,14 +7,10 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
FlipperPlugin,
|
||||
FlipperDevicePlugin,
|
||||
Props as PluginProps,
|
||||
} from './plugin';
|
||||
import {FlipperPlugin, FlipperDevicePlugin} from './plugin';
|
||||
import {Logger} from './fb-interfaces/Logger';
|
||||
import BaseDevice from './devices/BaseDevice';
|
||||
import {pluginKey as getPluginKey} from './reducers/pluginStates';
|
||||
import {pluginKey as getPluginKey} from './utils/pluginUtils';
|
||||
import Client from './Client';
|
||||
import {
|
||||
ErrorBoundary,
|
||||
@@ -31,8 +27,6 @@ import {StaticView, setStaticView} from './reducers/connections';
|
||||
import {switchPlugin} from './reducers/pluginManager';
|
||||
import React, {PureComponent} from 'react';
|
||||
import {connect, ReactReduxContext} from 'react-redux';
|
||||
import {setPluginState} from './reducers/pluginStates';
|
||||
import {Settings} from './reducers/settings';
|
||||
import {selectPlugin} from './reducers/connections';
|
||||
import {State as Store, MiddlewareAPI} from './reducers/index';
|
||||
import {activateMenuItems} from './MenuBar';
|
||||
@@ -40,12 +34,11 @@ import {Message} from './reducers/pluginMessageQueue';
|
||||
import {IdlerImpl} from './utils/Idler';
|
||||
import {processMessageQueue} from './utils/messageQueue';
|
||||
import {Layout} from './ui';
|
||||
import {theme, TrackingScope, _SandyPluginRenderer} from 'flipper-plugin';
|
||||
import {theme, _SandyPluginRenderer} from 'flipper-plugin';
|
||||
import {
|
||||
ActivePluginListItem,
|
||||
isDevicePlugin,
|
||||
isDevicePluginDefinition,
|
||||
isSandyPlugin,
|
||||
} from './utils/pluginUtils';
|
||||
import {ContentContainer} from './sandy-chrome/ContentContainer';
|
||||
import {Alert, Typography} from 'antd';
|
||||
@@ -59,13 +52,6 @@ import {getActiveClient, getActivePlugin} from './selectors/connections';
|
||||
|
||||
const {Text, Link} = Typography;
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
width: 0,
|
||||
flexGrow: 1,
|
||||
flexShrink: 1,
|
||||
backgroundColor: colors.white,
|
||||
});
|
||||
|
||||
export const SidebarContainer = styled(FlexRow)({
|
||||
backgroundColor: theme.backgroundWash,
|
||||
height: '100%',
|
||||
@@ -103,19 +89,14 @@ const ProgressBarBar = styled.div<{progress: number}>(({progress}) => ({
|
||||
|
||||
type OwnProps = {
|
||||
logger: Logger;
|
||||
isSandy?: boolean;
|
||||
};
|
||||
|
||||
type StateFromProps = {
|
||||
pluginState: Object;
|
||||
activePlugin: ActivePluginListItem | null;
|
||||
target: Client | BaseDevice | null;
|
||||
pluginKey: string | null;
|
||||
deepLinkPayload: unknown;
|
||||
selectedApp: string | null;
|
||||
isArchivedDevice: boolean;
|
||||
pendingMessages: Message[] | undefined;
|
||||
settingsState: Settings;
|
||||
latestInstalledVersion: InstalledPluginDetails | undefined;
|
||||
};
|
||||
|
||||
@@ -125,7 +106,6 @@ type DispatchFromProps = {
|
||||
selectedApp?: string | null;
|
||||
deepLinkPayload: unknown;
|
||||
}) => any;
|
||||
setPluginState: (payload: {pluginKey: string; state: any}) => void;
|
||||
setStaticView: (payload: StaticView) => void;
|
||||
enablePlugin: typeof switchPlugin;
|
||||
loadPlugin: typeof loadPlugin;
|
||||
@@ -228,17 +208,13 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
if (
|
||||
target instanceof Client &&
|
||||
activePlugin &&
|
||||
(isSandyPlugin(activePlugin.definition) ||
|
||||
activePlugin.definition.persistedStateReducer) &&
|
||||
pluginKey &&
|
||||
pendingMessages?.length
|
||||
) {
|
||||
const start = Date.now();
|
||||
this.idler = new IdlerImpl();
|
||||
processMessageQueue(
|
||||
isSandyPlugin(activePlugin.definition)
|
||||
? target.sandyPluginStates.get(activePlugin.definition.id)!
|
||||
: activePlugin.definition,
|
||||
target.sandyPluginStates.get(activePlugin.definition.id)!,
|
||||
pluginKey,
|
||||
this.store,
|
||||
(progress) => {
|
||||
@@ -360,18 +336,8 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
renderPlugin() {
|
||||
const {
|
||||
pluginState,
|
||||
setPluginState,
|
||||
activePlugin,
|
||||
pluginKey,
|
||||
target,
|
||||
isArchivedDevice,
|
||||
selectedApp,
|
||||
settingsState,
|
||||
isSandy,
|
||||
latestInstalledVersion,
|
||||
} = this.props;
|
||||
const {activePlugin, pluginKey, target, latestInstalledVersion} =
|
||||
this.props;
|
||||
if (
|
||||
!activePlugin ||
|
||||
!target ||
|
||||
@@ -381,7 +347,6 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
console.warn(`No selected plugin. Rendering empty!`);
|
||||
return this.renderNoPluginActive();
|
||||
}
|
||||
let pluginElement: null | React.ReactElement<any>;
|
||||
const showUpdateAlert =
|
||||
latestInstalledVersion &&
|
||||
activePlugin &&
|
||||
@@ -392,71 +357,14 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
latestInstalledVersion.version,
|
||||
activePlugin.definition.version,
|
||||
);
|
||||
if (isSandyPlugin(activePlugin.definition)) {
|
||||
// Make sure we throw away the container for different pluginKey!
|
||||
const instance = target.sandyPluginStates.get(activePlugin.definition.id);
|
||||
if (!instance) {
|
||||
// happens if we selected a plugin that is not enabled on a specific app or not supported on a specific device.
|
||||
return this.renderNoPluginActive();
|
||||
}
|
||||
pluginElement = (
|
||||
<_SandyPluginRenderer key={pluginKey} plugin={instance} />
|
||||
);
|
||||
} else {
|
||||
const props: PluginProps<Object> & {
|
||||
key: string;
|
||||
ref: (
|
||||
ref:
|
||||
| FlipperPlugin<any, any, any>
|
||||
| FlipperDevicePlugin<any, any, any>
|
||||
| null
|
||||
| undefined,
|
||||
) => void;
|
||||
} = {
|
||||
key: pluginKey,
|
||||
logger: this.props.logger,
|
||||
selectedApp,
|
||||
persistedState: activePlugin.definition.defaultPersistedState
|
||||
? {
|
||||
...activePlugin.definition.defaultPersistedState,
|
||||
...pluginState,
|
||||
}
|
||||
: pluginState,
|
||||
setStaticView: (payload: StaticView) =>
|
||||
this.props.setStaticView(payload),
|
||||
setPersistedState: (state) => setPluginState({pluginKey, state}),
|
||||
target,
|
||||
deepLinkPayload: this.props.deepLinkPayload,
|
||||
selectPlugin: (pluginID: string, deepLinkPayload: unknown) => {
|
||||
const {target} = this.props;
|
||||
// check if plugin will be available
|
||||
if (target instanceof Client && target.plugins.has(pluginID)) {
|
||||
this.props.selectPlugin({
|
||||
selectedPlugin: pluginID,
|
||||
deepLinkPayload,
|
||||
});
|
||||
return true;
|
||||
} else if (target instanceof BaseDevice) {
|
||||
this.props.selectPlugin({
|
||||
selectedPlugin: pluginID,
|
||||
deepLinkPayload,
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
ref: this.refChanged,
|
||||
isArchivedDevice,
|
||||
settingsState,
|
||||
};
|
||||
pluginElement = (
|
||||
<TrackingScope scope={'plugin:' + activePlugin.definition.id}>
|
||||
{React.createElement(activePlugin.definition, props)}
|
||||
</TrackingScope>
|
||||
);
|
||||
}
|
||||
return isSandy ? (
|
||||
|
||||
return (
|
||||
<Layout.Top>
|
||||
<div>
|
||||
{showUpdateAlert && (
|
||||
@@ -490,23 +398,13 @@ class PluginContainer extends PureComponent<Props, State> {
|
||||
heading={`Plugin "${
|
||||
activePlugin.definition.title || 'Unknown'
|
||||
}" encountered an error during render`}>
|
||||
<ContentContainer>{pluginElement}</ContentContainer>
|
||||
<ContentContainer>
|
||||
<_SandyPluginRenderer key={pluginKey} plugin={instance} />
|
||||
</ContentContainer>
|
||||
</ErrorBoundary>
|
||||
<SidebarContainer id="detailsSidebar" />
|
||||
</Layout.Right>
|
||||
</Layout.Top>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Container key="plugin">
|
||||
<ErrorBoundary
|
||||
heading={`Plugin "${
|
||||
activePlugin.definition.title || 'Unknown'
|
||||
}" encountered an error during render`}>
|
||||
{pluginElement}
|
||||
</ErrorBoundary>
|
||||
</Container>
|
||||
<SidebarContainer id="detailsSidebar" />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -516,11 +414,9 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
let pluginKey: string | null = null;
|
||||
let target: BaseDevice | Client | null = null;
|
||||
const {
|
||||
connections: {selectedDevice, selectedApp, deepLinkPayload},
|
||||
pluginStates,
|
||||
connections: {selectedDevice, deepLinkPayload},
|
||||
plugins: {installedPlugins},
|
||||
pluginMessageQueue,
|
||||
settingsState,
|
||||
} = state;
|
||||
const selectedClient = getActiveClient(state);
|
||||
const activePlugin = getActivePlugin(state);
|
||||
@@ -536,24 +432,17 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
pluginKey = getPluginKey(selectedClient.id, activePlugin.details.id);
|
||||
}
|
||||
}
|
||||
const isArchivedDevice = !selectedDevice
|
||||
? false
|
||||
: selectedDevice.isArchived;
|
||||
|
||||
const pendingMessages = pluginKey
|
||||
? pluginMessageQueue[pluginKey]
|
||||
: undefined;
|
||||
|
||||
const s: StateFromProps = {
|
||||
pluginState: pluginStates[pluginKey as string],
|
||||
activePlugin,
|
||||
target,
|
||||
deepLinkPayload,
|
||||
pluginKey,
|
||||
isArchivedDevice,
|
||||
selectedApp: selectedApp || null,
|
||||
pendingMessages,
|
||||
settingsState,
|
||||
latestInstalledVersion: installedPlugins.get(
|
||||
activePlugin?.details?.name ?? '',
|
||||
),
|
||||
@@ -561,7 +450,6 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
||||
return s;
|
||||
},
|
||||
{
|
||||
setPluginState,
|
||||
selectPlugin,
|
||||
setStaticView,
|
||||
enablePlugin: switchPlugin,
|
||||
|
||||
@@ -72,7 +72,14 @@ test('Plugin container can render plugin and receive updates', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<h1>
|
||||
Hello:
|
||||
@@ -89,6 +96,8 @@ test('Plugin container can render plugin and receive updates', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -164,7 +173,14 @@ test('PluginContainer can render Sandy plugins', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy
|
||||
@@ -176,6 +192,8 @@ test('PluginContainer can render Sandy plugins', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
expect(renders).toBe(1);
|
||||
@@ -195,7 +213,14 @@ test('PluginContainer can render Sandy plugins', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy
|
||||
@@ -207,6 +232,8 @@ test('PluginContainer can render Sandy plugins', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -262,7 +289,14 @@ test('PluginContainer can render Sandy plugins', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy
|
||||
@@ -274,6 +308,8 @@ test('PluginContainer can render Sandy plugins', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -501,7 +537,14 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<h1>
|
||||
hello
|
||||
@@ -513,6 +556,8 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -532,7 +577,14 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<h1>
|
||||
hello
|
||||
@@ -544,6 +596,8 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -655,7 +709,14 @@ test('PluginContainer can render Sandy device plugins', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy:
|
||||
@@ -666,6 +727,8 @@ test('PluginContainer can render Sandy device plugins', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
expect(renders).toBe(1);
|
||||
@@ -686,7 +749,14 @@ test('PluginContainer can render Sandy device plugins', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy:
|
||||
@@ -698,6 +768,8 @@ test('PluginContainer can render Sandy device plugins', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -778,7 +850,14 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<h1>
|
||||
hello
|
||||
@@ -790,6 +869,8 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -809,7 +890,14 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<h1>
|
||||
hello
|
||||
@@ -821,6 +909,8 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
@@ -1076,7 +1166,14 @@ test('PluginContainer can render Sandy plugins for archived devices', async () =
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy
|
||||
@@ -1088,6 +1185,8 @@ test('PluginContainer can render Sandy plugins for archived devices', async () =
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
expect(renders).toBe(1);
|
||||
@@ -1136,7 +1235,14 @@ test('PluginContainer can render Sandy plugins for archived devices', async () =
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="css-w6yhx2-View-FlexBox-FlexColumn"
|
||||
class="css-1x2cmzz-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div />
|
||||
<div
|
||||
class="css-1knrt0j-SandySplitContainer e1hsqii10"
|
||||
>
|
||||
<div
|
||||
class="css-1woty6b-Container"
|
||||
>
|
||||
<div>
|
||||
Hello from Sandy
|
||||
@@ -1148,6 +1254,8 @@ test('PluginContainer can render Sandy plugins for archived devices', async () =
|
||||
id="detailsSidebar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
|
||||
@@ -1,77 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`can create a Fake flipper 1`] = `
|
||||
Object {
|
||||
"androidEmulators": Array [],
|
||||
"clients": Array [
|
||||
Object {
|
||||
"id": "TestApp#Android#MockAndroidDevice#serial",
|
||||
"query": Object {
|
||||
"app": "TestApp",
|
||||
"device": "MockAndroidDevice",
|
||||
"device_id": "serial",
|
||||
"os": "Android",
|
||||
"sdk_version": 4,
|
||||
},
|
||||
},
|
||||
],
|
||||
"deepLinkPayload": null,
|
||||
"devices": Array [
|
||||
Object {
|
||||
"deviceType": "physical",
|
||||
"os": "Android",
|
||||
"serial": "serial",
|
||||
"title": "MockAndroidDevice",
|
||||
},
|
||||
],
|
||||
"enabledDevicePlugins": Set {
|
||||
"DeviceLogs",
|
||||
"CrashReporter",
|
||||
"MobileBuilds",
|
||||
"Hermesdebuggerrn",
|
||||
"React",
|
||||
},
|
||||
"enabledPlugins": Object {
|
||||
"TestApp": Array [
|
||||
"TestPlugin",
|
||||
],
|
||||
},
|
||||
"selectedApp": "TestApp#Android#MockAndroidDevice#serial",
|
||||
"selectedAppPluginListRevision": 0,
|
||||
"selectedDevice": Object {
|
||||
"deviceType": "physical",
|
||||
"os": "Android",
|
||||
"serial": "serial",
|
||||
"title": "MockAndroidDevice",
|
||||
},
|
||||
"selectedPlugin": "TestPlugin",
|
||||
"staticView": null,
|
||||
"uninitializedClients": Array [],
|
||||
"userPreferredApp": "TestApp#Android#MockAndroidDevice#serial",
|
||||
"userPreferredDevice": "MockAndroidDevice",
|
||||
"userPreferredPlugin": "TestPlugin",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`can create a Fake flipper 2`] = `
|
||||
Object {
|
||||
"bundledPlugins": Map {},
|
||||
"clientPlugins": Map {
|
||||
"TestPlugin" => [Function],
|
||||
},
|
||||
"devicePlugins": Map {},
|
||||
"disabledPlugins": Array [],
|
||||
"failedPlugins": Array [],
|
||||
"gatekeepedPlugins": Array [],
|
||||
"initialised": false,
|
||||
"installedPlugins": Map {},
|
||||
"loadedPlugins": Map {},
|
||||
"marketplacePlugins": Array [],
|
||||
"selectedPlugins": Array [],
|
||||
"uninstalledPluginNames": Set {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`can create a Fake flipper with legacy wrapper 1`] = `
|
||||
Object {
|
||||
"androidEmulators": Array [],
|
||||
|
||||
@@ -40,26 +40,6 @@ class TestPlugin extends FlipperPlugin<any, any, any> {
|
||||
}
|
||||
}
|
||||
|
||||
test('can create a Fake flipper', async () => {
|
||||
const {client, device, store, sendMessage} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {disableLegacyWrapper: true});
|
||||
expect(client).toBeTruthy();
|
||||
expect(device).toBeTruthy();
|
||||
expect(store).toBeTruthy();
|
||||
expect(sendMessage).toBeTruthy();
|
||||
expect(client.plugins.has(TestPlugin.id)).toBe(true);
|
||||
expect(store.getState().connections).toMatchSnapshot();
|
||||
expect(store.getState().plugins).toMatchSnapshot();
|
||||
sendMessage('inc', {});
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
|
||||
"count": 1,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
const testIdler = new TestIdler();
|
||||
|
||||
function testOnStatusMessage() {
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* 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 React from 'react';
|
||||
import {create, act, ReactTestRenderer} from 'react-test-renderer';
|
||||
import {Provider} from 'react-redux';
|
||||
import ExportDataPluginSheet from '../ExportDataPluginSheet';
|
||||
import {FlipperPlugin, FlipperDevicePlugin} from '../../plugin';
|
||||
import {getPluginKey} from '../../utils/pluginUtils';
|
||||
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {setPluginState} from '../../reducers/pluginStates';
|
||||
import {getExportablePlugins} from '../../selectors/connections';
|
||||
|
||||
class TestPlugin extends FlipperPlugin<any, any, any> {
|
||||
static details = {
|
||||
title: 'TestPlugin',
|
||||
id: 'TestPlugin',
|
||||
} as any;
|
||||
}
|
||||
TestPlugin.title = 'TestPlugin';
|
||||
TestPlugin.id = 'TestPlugin';
|
||||
TestPlugin.defaultPersistedState = {msg: 'Test plugin'};
|
||||
|
||||
class TestDevicePlugin extends FlipperDevicePlugin<any, any, any> {
|
||||
static details = {
|
||||
title: 'TestDevicePlugin',
|
||||
id: 'TestDevicePlugin',
|
||||
} as any;
|
||||
|
||||
static supportsDevice() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TestDevicePlugin.title = 'TestDevicePlugin';
|
||||
TestDevicePlugin.id = 'TestDevicePlugin';
|
||||
TestDevicePlugin.defaultPersistedState = {msg: 'TestDevicePlugin'};
|
||||
|
||||
test('SettingsSheet snapshot with nothing enabled', async () => {
|
||||
let root: ReactTestRenderer;
|
||||
const {store, togglePlugin, client, device, pluginKey} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
additionalPlugins: [TestDevicePlugin],
|
||||
});
|
||||
|
||||
togglePlugin();
|
||||
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey,
|
||||
state: {test: '1'},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(getExportablePlugins(store.getState())).toEqual([]);
|
||||
|
||||
// makes device plugin visible
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey: getPluginKey(undefined, device, 'TestDevicePlugin'),
|
||||
state: {test: '1'},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(getExportablePlugins(store.getState())).toEqual([
|
||||
{
|
||||
id: 'TestDevicePlugin',
|
||||
label: 'TestDevicePlugin',
|
||||
},
|
||||
]);
|
||||
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<Provider store={store}>
|
||||
<ExportDataPluginSheet onHide={() => {}} />
|
||||
</Provider>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root!.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('SettingsSheet snapshot with one plugin enabled', async () => {
|
||||
let root: ReactTestRenderer;
|
||||
const {store, device, pluginKey} = await createMockFlipperWithPlugin(
|
||||
TestPlugin,
|
||||
{
|
||||
additionalPlugins: [TestDevicePlugin],
|
||||
},
|
||||
);
|
||||
|
||||
// enabled
|
||||
// in Sandy wrapper, a plugin is either persistable or not, but it doesn't depend on the current state.
|
||||
// So this plugin will show up, even though its state is still the default
|
||||
expect(getExportablePlugins(store.getState())).toEqual([
|
||||
{
|
||||
id: 'TestPlugin',
|
||||
label: 'TestPlugin',
|
||||
},
|
||||
]);
|
||||
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey,
|
||||
state: {test: '1'},
|
||||
}),
|
||||
);
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey: getPluginKey(undefined, device, 'TestDevicePlugin'),
|
||||
state: {test: '1'},
|
||||
}),
|
||||
);
|
||||
expect(getExportablePlugins(store.getState())).toEqual([
|
||||
{
|
||||
id: 'TestDevicePlugin',
|
||||
label: 'TestDevicePlugin',
|
||||
},
|
||||
{
|
||||
id: 'TestPlugin',
|
||||
label: 'TestPlugin',
|
||||
},
|
||||
]);
|
||||
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<Provider store={store}>
|
||||
<ExportDataPluginSheet onHide={() => {}} />
|
||||
</Provider>,
|
||||
);
|
||||
});
|
||||
|
||||
expect(root!.toJSON()).toMatchSnapshot();
|
||||
});
|
||||
@@ -1,252 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SettingsSheet snapshot with nothing enabled 1`] = `
|
||||
<div
|
||||
className="css-p0wmbe-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
className="css-1edwc8r-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
className="css-18abd42-View-FlexBox-FlexColumn ecr18to0"
|
||||
>
|
||||
<span
|
||||
className="css-meh2qi-Text"
|
||||
>
|
||||
Select the plugins for which you want to export the data
|
||||
</span>
|
||||
<div
|
||||
className="css-1houjzq-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
className="css-18abd42-View-FlexBox-FlexColumn ecr18to0"
|
||||
>
|
||||
<div
|
||||
className="css-auhar3-TooltipContainer e1abcqbd0"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<div
|
||||
className="css-1jrm6r3"
|
||||
>
|
||||
<div
|
||||
className="css-wospjg-View-FlexBox-FlexRow epz0qe20"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="css-xsnw23-Text e19o3fcp0"
|
||||
>
|
||||
TestDevicePlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-t4wmtk-View-FlexBox-Spacer e13mj6h80"
|
||||
/>
|
||||
<input
|
||||
checked={false}
|
||||
className="css-1pxrk7-CheckboxContainer e28aqfo0"
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="css-1p0wwd3-View"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="css-1yqvjo0"
|
||||
>
|
||||
<div
|
||||
className="css-wospjg-View-FlexBox-FlexRow epz0qe20"
|
||||
>
|
||||
<div
|
||||
className="css-t4wmtk-View-FlexBox-Spacer e13mj6h80"
|
||||
/>
|
||||
<div
|
||||
className="css-12n892b"
|
||||
>
|
||||
<button
|
||||
className="ant-btn ant-btn-default"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="css-auhar3-TooltipContainer e1abcqbd0"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<button
|
||||
className="ant-btn ant-btn-primary"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`SettingsSheet snapshot with one plugin enabled 1`] = `
|
||||
<div
|
||||
className="css-p0wmbe-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
className="css-1edwc8r-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
className="css-18abd42-View-FlexBox-FlexColumn ecr18to0"
|
||||
>
|
||||
<span
|
||||
className="css-meh2qi-Text"
|
||||
>
|
||||
Select the plugins for which you want to export the data
|
||||
</span>
|
||||
<div
|
||||
className="css-1houjzq-View-FlexBox-FlexColumn"
|
||||
>
|
||||
<div
|
||||
className="css-18abd42-View-FlexBox-FlexColumn ecr18to0"
|
||||
>
|
||||
<div
|
||||
className="css-auhar3-TooltipContainer e1abcqbd0"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<div
|
||||
className="css-1jrm6r3"
|
||||
>
|
||||
<div
|
||||
className="css-wospjg-View-FlexBox-FlexRow epz0qe20"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="css-xsnw23-Text e19o3fcp0"
|
||||
>
|
||||
TestDevicePlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-t4wmtk-View-FlexBox-Spacer e13mj6h80"
|
||||
/>
|
||||
<input
|
||||
checked={false}
|
||||
className="css-1pxrk7-CheckboxContainer e28aqfo0"
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="css-1p0wwd3-View"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="css-18abd42-View-FlexBox-FlexColumn ecr18to0"
|
||||
>
|
||||
<div
|
||||
className="css-auhar3-TooltipContainer e1abcqbd0"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<div
|
||||
className="css-1jrm6r3"
|
||||
>
|
||||
<div
|
||||
className="css-wospjg-View-FlexBox-FlexRow epz0qe20"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="css-xsnw23-Text e19o3fcp0"
|
||||
>
|
||||
TestPlugin
|
||||
</span>
|
||||
<div
|
||||
className="css-t4wmtk-View-FlexBox-Spacer e13mj6h80"
|
||||
/>
|
||||
<input
|
||||
checked={false}
|
||||
className="css-1pxrk7-CheckboxContainer e28aqfo0"
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="css-1p0wwd3-View"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="css-1yqvjo0"
|
||||
>
|
||||
<div
|
||||
className="css-wospjg-View-FlexBox-FlexRow epz0qe20"
|
||||
>
|
||||
<div
|
||||
className="css-t4wmtk-View-FlexBox-Spacer e13mj6h80"
|
||||
/>
|
||||
<div
|
||||
className="css-12n892b"
|
||||
>
|
||||
<button
|
||||
className="ant-btn ant-btn-default"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
className="css-auhar3-TooltipContainer e1abcqbd0"
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
>
|
||||
<button
|
||||
className="ant-btn ant-btn-primary"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -15,7 +15,7 @@ import {connect} from 'react-redux';
|
||||
import {Text, ManagedTable, styled, colors, Link, Bordered} from '../../ui';
|
||||
import StatusIndicator from '../../ui/components/StatusIndicator';
|
||||
import {State as Store} from '../../reducers';
|
||||
import {DevicePluginDefinition, ClientPluginDefinition} from '../../plugin';
|
||||
import {PluginDefinition} from '../../plugin';
|
||||
|
||||
const InfoText = styled(Text)({
|
||||
lineHeight: '130%',
|
||||
@@ -43,8 +43,8 @@ type StateFromProps = {
|
||||
failedPlugins: Array<[PluginDetails, string]>;
|
||||
clients: Array<Client>;
|
||||
selectedDevice: string | null | undefined;
|
||||
devicePlugins: DevicePluginDefinition[];
|
||||
clientPlugins: ClientPluginDefinition[];
|
||||
devicePlugins: PluginDefinition[];
|
||||
clientPlugins: PluginDefinition[];
|
||||
};
|
||||
|
||||
type DispatchFromProps = {};
|
||||
|
||||
@@ -18,11 +18,7 @@ import {
|
||||
createState,
|
||||
_getFlipperLibImplementation,
|
||||
} from 'flipper-plugin';
|
||||
import {
|
||||
DevicePluginDefinition,
|
||||
DevicePluginMap,
|
||||
FlipperDevicePlugin,
|
||||
} from '../plugin';
|
||||
import {PluginDefinition, DevicePluginMap} from '../plugin';
|
||||
import {DeviceSpec, OS as PluginOS, PluginDetails} from 'flipper-plugin-lib';
|
||||
|
||||
export type DeviceShell = {
|
||||
@@ -191,9 +187,9 @@ export default class BaseDevice {
|
||||
return null;
|
||||
}
|
||||
|
||||
supportsPlugin(plugin: DevicePluginDefinition | PluginDetails) {
|
||||
supportsPlugin(plugin: PluginDefinition | PluginDetails) {
|
||||
let pluginDetails: PluginDetails;
|
||||
if (isDevicePluginDefinition(plugin)) {
|
||||
if (plugin instanceof _SandyPluginDefinition) {
|
||||
pluginDetails = plugin.details;
|
||||
if (!pluginDetails.pluginType && !pluginDetails.supportedDevices) {
|
||||
// TODO T84453692: this branch is to support plugins defined with the legacy approach. Need to remove this branch after some transition period when
|
||||
@@ -205,7 +201,7 @@ export default class BaseDevice {
|
||||
false)
|
||||
);
|
||||
} else {
|
||||
return plugin.supportsDevice(this);
|
||||
return (plugin as any).supportsDevice(this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -240,7 +236,7 @@ export default class BaseDevice {
|
||||
}
|
||||
}
|
||||
|
||||
loadDevicePlugin(plugin: DevicePluginDefinition, initialState?: any) {
|
||||
loadDevicePlugin(plugin: PluginDefinition, initialState?: any) {
|
||||
if (!this.supportsPlugin(plugin)) {
|
||||
return;
|
||||
}
|
||||
@@ -286,12 +282,3 @@ export default class BaseDevice {
|
||||
this.sandyPluginStates.clear();
|
||||
}
|
||||
}
|
||||
|
||||
function isDevicePluginDefinition(
|
||||
definition: any,
|
||||
): definition is DevicePluginDefinition {
|
||||
return (
|
||||
(definition as any).prototype instanceof FlipperDevicePlugin ||
|
||||
(definition instanceof _SandyPluginDefinition && definition.isDevicePlugin)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,16 +10,13 @@
|
||||
import {Store} from '../reducers/index';
|
||||
import {Logger} from '../fb-interfaces/Logger';
|
||||
import {PluginNotification} from '../reducers/notifications';
|
||||
import {PluginDefinition} from '../plugin';
|
||||
import {ipcRenderer, IpcRendererEvent} from 'electron';
|
||||
import {
|
||||
setActiveNotifications,
|
||||
updatePluginBlocklist,
|
||||
updateCategoryBlocklist,
|
||||
} from '../reducers/notifications';
|
||||
import {textContent} from '../utils/index';
|
||||
import {deconstructPluginKey} from '../utils/clientUtils';
|
||||
import {getPluginTitle, isSandyPlugin} from '../utils/pluginUtils';
|
||||
import {getPluginTitle} from '../utils/pluginUtils';
|
||||
import {sideEffect} from '../utils/sideEffect';
|
||||
import {openNotification} from '../sandy-chrome/notification/Notification';
|
||||
|
||||
@@ -28,7 +25,6 @@ const NOTIFICATION_THROTTLE = 5 * 1000; // in milliseconds
|
||||
|
||||
export default (store: Store, logger: Logger) => {
|
||||
const knownNotifications: Set<string> = new Set();
|
||||
const knownPluginStates: Map<string, Object> = new Map();
|
||||
const lastNotificationTime: Map<string, number> = new Map();
|
||||
|
||||
ipcRenderer.on(
|
||||
@@ -78,56 +74,16 @@ export default (store: Store, logger: Logger) => {
|
||||
sideEffect(
|
||||
store,
|
||||
{name: 'notifications', throttleMs: 500},
|
||||
({notifications, pluginStates, plugins}) => ({
|
||||
({notifications, plugins}) => ({
|
||||
notifications,
|
||||
pluginStates,
|
||||
devicePlugins: plugins.devicePlugins,
|
||||
clientPlugins: plugins.clientPlugins,
|
||||
}),
|
||||
({notifications, pluginStates, devicePlugins, clientPlugins}, store) => {
|
||||
({notifications, devicePlugins, clientPlugins}, store) => {
|
||||
function getPlugin(name: string) {
|
||||
return devicePlugins.get(name) ?? clientPlugins.get(name);
|
||||
}
|
||||
|
||||
Object.keys(pluginStates).forEach((key) => {
|
||||
if (knownPluginStates.get(key) !== pluginStates[key]) {
|
||||
knownPluginStates.set(key, pluginStates[key]);
|
||||
const plugin = deconstructPluginKey(key);
|
||||
const pluginName = plugin.pluginName;
|
||||
const client = plugin.client;
|
||||
|
||||
if (!pluginName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const persistingPlugin: undefined | PluginDefinition =
|
||||
getPlugin(pluginName);
|
||||
if (
|
||||
persistingPlugin &&
|
||||
!isSandyPlugin(persistingPlugin) &&
|
||||
persistingPlugin.getActiveNotifications
|
||||
) {
|
||||
try {
|
||||
const notifications = persistingPlugin.getActiveNotifications(
|
||||
pluginStates[key],
|
||||
);
|
||||
store.dispatch(
|
||||
setActiveNotifications({
|
||||
notifications,
|
||||
client,
|
||||
pluginId: pluginName,
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
'Failed to compute notifications for plugin ' + pluginName,
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const {activeNotifications, blocklistedPlugins, blocklistedCategories} =
|
||||
notifications;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
import type {Store} from '../reducers/index';
|
||||
import {clearPluginState} from '../reducers/pluginStates';
|
||||
import type {Logger} from '../fb-interfaces/Logger';
|
||||
import {
|
||||
LoadPluginActionPayload,
|
||||
@@ -27,12 +26,7 @@ import {
|
||||
import {sideEffect} from '../utils/sideEffect';
|
||||
import {requirePlugin} from './plugins';
|
||||
import {showErrorNotification} from '../utils/notifications';
|
||||
import {
|
||||
ClientPluginDefinition,
|
||||
DevicePluginDefinition,
|
||||
FlipperPlugin,
|
||||
PluginDefinition,
|
||||
} from '../plugin';
|
||||
import {PluginDefinition} from '../plugin';
|
||||
import type Client from '../Client';
|
||||
import {unloadModule} from '../utils/electronModuleCache';
|
||||
import {
|
||||
@@ -148,7 +142,6 @@ function uninstallPlugin(store: Store, {plugin}: UninstallPluginActionPayload) {
|
||||
clients.forEach((client) => {
|
||||
stopPlugin(client, plugin.id);
|
||||
});
|
||||
store.dispatch(clearPluginState({pluginId: plugin.id}));
|
||||
if (!plugin.details.isBundled) {
|
||||
unloadPluginModule(plugin.details);
|
||||
}
|
||||
@@ -194,7 +187,7 @@ function switchPlugin(
|
||||
|
||||
function switchClientPlugin(
|
||||
store: Store,
|
||||
plugin: ClientPluginDefinition,
|
||||
plugin: PluginDefinition,
|
||||
selectedApp: string | undefined,
|
||||
) {
|
||||
selectedApp = selectedApp ?? getSelectedAppId(store);
|
||||
@@ -224,7 +217,7 @@ function switchClientPlugin(
|
||||
}
|
||||
}
|
||||
|
||||
function switchDevicePlugin(store: Store, plugin: DevicePluginDefinition) {
|
||||
function switchDevicePlugin(store: Store, plugin: PluginDefinition) {
|
||||
const {connections} = store.getState();
|
||||
const devicesWithPlugin = connections.devices.filter((d) =>
|
||||
d.supportsPlugin(plugin.details),
|
||||
@@ -244,7 +237,7 @@ function switchDevicePlugin(store: Store, plugin: DevicePluginDefinition) {
|
||||
|
||||
function updateClientPlugin(
|
||||
store: Store,
|
||||
plugin: typeof FlipperPlugin,
|
||||
plugin: PluginDefinition,
|
||||
enable: boolean,
|
||||
) {
|
||||
const clients = store.getState().connections.clients;
|
||||
@@ -266,7 +259,6 @@ function updateClientPlugin(
|
||||
clientsWithEnabledPlugin.forEach((client) => {
|
||||
stopPlugin(client, plugin.id);
|
||||
});
|
||||
store.dispatch(clearPluginState({pluginId: plugin.id}));
|
||||
clientsWithEnabledPlugin.forEach((client) => {
|
||||
startPlugin(client, plugin, true);
|
||||
});
|
||||
@@ -279,7 +271,7 @@ function updateClientPlugin(
|
||||
|
||||
function updateDevicePlugin(
|
||||
store: Store,
|
||||
plugin: DevicePluginDefinition,
|
||||
plugin: PluginDefinition,
|
||||
enable: boolean,
|
||||
) {
|
||||
if (enable) {
|
||||
@@ -292,7 +284,6 @@ function updateDevicePlugin(
|
||||
devicesWithEnabledPlugin.forEach((d) => {
|
||||
d.unloadDevicePlugin(plugin.id);
|
||||
});
|
||||
store.dispatch(clearPluginState({pluginId: plugin.id}));
|
||||
const previousVersion = store.getState().plugins.devicePlugins.get(plugin.id);
|
||||
if (previousVersion) {
|
||||
// unload previous version from Electron cache
|
||||
|
||||
@@ -39,8 +39,9 @@ export {default as constants} from './fb-stubs/constants';
|
||||
export {connect} from 'react-redux';
|
||||
export {selectPlugin, StaticView} from './reducers/connections';
|
||||
export {writeBufferToFile, bufferToBlob} from './utils/screenshot';
|
||||
export {getPluginKey, getPersistedState} from './utils/pluginUtils';
|
||||
export {Idler, Notification} from 'flipper-plugin';
|
||||
export {getPluginKey} from './utils/pluginUtils';
|
||||
export {Notification, Idler} from 'flipper-plugin';
|
||||
export {IdlerImpl} from './utils/Idler';
|
||||
export {Store, MiddlewareAPI, State as ReduxState} from './reducers/index';
|
||||
export {default as BaseDevice} from './devices/BaseDevice';
|
||||
export {DeviceLogEntry, LogLevel, DeviceLogListener} from 'flipper-plugin';
|
||||
|
||||
@@ -27,18 +27,10 @@ import {
|
||||
|
||||
type Parameters = {[key: string]: any};
|
||||
|
||||
export type PluginDefinition = ClientPluginDefinition | DevicePluginDefinition;
|
||||
export type PluginDefinition = _SandyPluginDefinition;
|
||||
|
||||
export type DevicePluginDefinition =
|
||||
| typeof FlipperDevicePlugin
|
||||
| _SandyPluginDefinition;
|
||||
|
||||
export type ClientPluginDefinition =
|
||||
| typeof FlipperPlugin
|
||||
| _SandyPluginDefinition;
|
||||
|
||||
export type ClientPluginMap = Map<string, ClientPluginDefinition>;
|
||||
export type DevicePluginMap = Map<string, DevicePluginDefinition>;
|
||||
export type ClientPluginMap = Map<string, PluginDefinition>;
|
||||
export type DevicePluginMap = Map<string, PluginDefinition>;
|
||||
|
||||
// This function is intended to be called from outside of the plugin.
|
||||
// If you want to `call` from the plugin use, this.client.call
|
||||
@@ -211,6 +203,10 @@ export abstract class FlipperBasePlugin<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use the newer "Sandy" plugin APIs!
|
||||
* https://fbflipper.com/docs/extending/sandy-migration
|
||||
*/
|
||||
export class FlipperDevicePlugin<
|
||||
S,
|
||||
A extends BaseAction,
|
||||
@@ -240,6 +236,10 @@ export class FlipperDevicePlugin<
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use the newer "Sandy" plugin APIs!
|
||||
* https://fbflipper.com/docs/extending/sandy-migration
|
||||
*/
|
||||
export class FlipperPlugin<
|
||||
S,
|
||||
A extends BaseAction,
|
||||
|
||||
@@ -13,7 +13,17 @@ import BaseDevice from '../../devices/BaseDevice';
|
||||
import MacDevice from '../../devices/MacDevice';
|
||||
import {FlipperDevicePlugin} from '../../plugin';
|
||||
import MetroDevice from '../../devices/MetroDevice';
|
||||
import {TestUtils} from 'flipper-plugin';
|
||||
import {TestUtils, _setFlipperLibImplementation} from 'flipper-plugin';
|
||||
import {wrapSandy} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {createMockFlipperLib} from 'flipper-plugin/src/test-utils/test-utils';
|
||||
|
||||
beforeEach(() => {
|
||||
_setFlipperLibImplementation(createMockFlipperLib());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
_setFlipperLibImplementation(undefined);
|
||||
});
|
||||
|
||||
test('doing a double REGISTER_DEVICE keeps the last', () => {
|
||||
const device1 = new BaseDevice('serial', 'physical', 'title', 'Android');
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* 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 {default as reducer, setPluginState, Action} from '../pluginStates';
|
||||
|
||||
test('reduce setPluginState', () => {
|
||||
const result = reducer(
|
||||
{},
|
||||
setPluginState({pluginKey: 'myPlugin', state: {a: 1}}),
|
||||
);
|
||||
expect(result).toEqual({myPlugin: {a: 1}});
|
||||
});
|
||||
|
||||
test('CLEAR_CLIENT_PLUGINS_STATE removes plugin state', () => {
|
||||
const clientId = 'app1#device1';
|
||||
const pluginKey = 'app1#device1#plugin1';
|
||||
|
||||
const action: Action = {
|
||||
type: 'CLEAR_CLIENT_PLUGINS_STATE',
|
||||
payload: {clientId: clientId, devicePlugins: new Set()},
|
||||
};
|
||||
const result = reducer(
|
||||
{[pluginKey]: {a: 1}, 'anotherPlugin#key': {b: 2}},
|
||||
action,
|
||||
);
|
||||
expect(result).toEqual({'anotherPlugin#key': {b: 2}});
|
||||
});
|
||||
@@ -15,18 +15,21 @@ import {
|
||||
} from '../plugins';
|
||||
import {FlipperPlugin, FlipperDevicePlugin, BaseAction} from '../../plugin';
|
||||
import {InstalledPluginDetails} from 'flipper-plugin-lib';
|
||||
import {wrapSandy} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
|
||||
const testPlugin = class extends FlipperPlugin<any, BaseAction, any> {
|
||||
const testPluginOrig = class extends FlipperPlugin<any, BaseAction, any> {
|
||||
static id = 'TestPlugin';
|
||||
};
|
||||
const testPlugin = wrapSandy(testPluginOrig);
|
||||
|
||||
const testDevicePlugin = class extends FlipperDevicePlugin<
|
||||
const testDevicePluginOrig = class extends FlipperDevicePlugin<
|
||||
any,
|
||||
BaseAction,
|
||||
any
|
||||
> {
|
||||
static id = 'TestDevicePlugin';
|
||||
};
|
||||
const testDevicePlugin = wrapSandy(testDevicePluginOrig);
|
||||
|
||||
test('add clientPlugin', () => {
|
||||
const res = reducer(
|
||||
|
||||
@@ -18,10 +18,6 @@ import connections, {
|
||||
persistMigrations as devicesPersistMigrations,
|
||||
persistVersion as devicesPersistVersion,
|
||||
} from './connections';
|
||||
import pluginStates, {
|
||||
State as PluginStatesState,
|
||||
Action as PluginStatesAction,
|
||||
} from './pluginStates';
|
||||
import pluginMessageQueue, {
|
||||
State as PluginMessageQueueState,
|
||||
Action as PluginMessageQueueAction,
|
||||
@@ -81,7 +77,6 @@ import {TransformConfig} from 'redux-persist/es/createTransform';
|
||||
export type Actions =
|
||||
| ApplicationAction
|
||||
| DevicesAction
|
||||
| PluginStatesAction
|
||||
| PluginMessageQueueAction
|
||||
| NotificationsAction
|
||||
| PluginsAction
|
||||
@@ -98,7 +93,6 @@ export type Actions =
|
||||
export type State = {
|
||||
application: ApplicationState;
|
||||
connections: DevicesState & PersistPartial;
|
||||
pluginStates: PluginStatesState;
|
||||
pluginMessageQueue: PluginMessageQueueState;
|
||||
notifications: NotificationsState & PersistPartial;
|
||||
plugins: PluginsState & PersistPartial;
|
||||
@@ -158,7 +152,6 @@ export function createRootReducer() {
|
||||
},
|
||||
connections,
|
||||
),
|
||||
pluginStates,
|
||||
pluginMessageQueue: pluginMessageQueue as any,
|
||||
notifications: persistReducer(
|
||||
{
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* 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 {produce} from 'immer';
|
||||
import {Actions} from '.';
|
||||
import {deconstructPluginKey} from '../utils/clientUtils';
|
||||
|
||||
export type State = {
|
||||
[pluginKey: string]: any;
|
||||
};
|
||||
|
||||
export const pluginKey = (serial: string, pluginName: string): string => {
|
||||
return `${serial}#${pluginName}`;
|
||||
};
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: 'SET_PLUGIN_STATE';
|
||||
payload: {
|
||||
pluginKey: string;
|
||||
state: Object;
|
||||
};
|
||||
}
|
||||
| {
|
||||
type: 'CLEAR_CLIENT_PLUGINS_STATE';
|
||||
payload: {clientId: string; devicePlugins: Set<string>};
|
||||
}
|
||||
| {
|
||||
type: 'CLEAR_PLUGIN_STATE';
|
||||
payload: {pluginId: string};
|
||||
};
|
||||
|
||||
export default function reducer(
|
||||
state: State | undefined = {},
|
||||
action: Actions,
|
||||
): State {
|
||||
if (action.type === 'SET_PLUGIN_STATE') {
|
||||
const newPluginState = action.payload.state;
|
||||
if (newPluginState && newPluginState !== state[action.payload.pluginKey]) {
|
||||
return {
|
||||
...state,
|
||||
[action.payload.pluginKey]: {
|
||||
...state[action.payload.pluginKey],
|
||||
...newPluginState,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {...state};
|
||||
} else if (action.type === 'CLEAR_CLIENT_PLUGINS_STATE') {
|
||||
const {payload} = action;
|
||||
return Object.keys(state).reduce((newState: State, pluginKey) => {
|
||||
// Only add the pluginState, if its from a plugin other than the one that
|
||||
// was removed. pluginKeys are in the form of ${clientID}#${pluginID}.
|
||||
const plugin = deconstructPluginKey(pluginKey);
|
||||
const clientId = plugin.client;
|
||||
const pluginId = plugin.pluginName;
|
||||
if (
|
||||
clientId !== payload.clientId ||
|
||||
(pluginId && payload.devicePlugins.has(pluginId))
|
||||
) {
|
||||
newState[pluginKey] = state[pluginKey];
|
||||
}
|
||||
return newState;
|
||||
}, {});
|
||||
} else if (action.type === 'CLEAR_PLUGIN_STATE') {
|
||||
const {pluginId} = action.payload;
|
||||
return produce(state, (draft) => {
|
||||
Object.keys(draft).forEach((pluginKey) => {
|
||||
const pluginKeyParts = deconstructPluginKey(pluginKey);
|
||||
if (pluginKeyParts.pluginName === pluginId) {
|
||||
delete draft[pluginKey];
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export const setPluginState = (payload: {
|
||||
pluginKey: string;
|
||||
state: Object;
|
||||
}): Action => ({
|
||||
type: 'SET_PLUGIN_STATE',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const clearPluginState = (payload: {pluginId: string}): Action => ({
|
||||
type: 'CLEAR_PLUGIN_STATE',
|
||||
payload,
|
||||
});
|
||||
@@ -169,7 +169,7 @@ export function SandyApp() {
|
||||
)}
|
||||
</TrackingScope>
|
||||
) : (
|
||||
<PluginContainer logger={logger} isSandy />
|
||||
<PluginContainer logger={logger} />
|
||||
)}
|
||||
{outOfContentsContainer}
|
||||
</MainContainer>
|
||||
|
||||
@@ -108,10 +108,9 @@ export const getPluginLists = createSelector(
|
||||
);
|
||||
|
||||
export const getExportablePlugins = createSelector(
|
||||
({plugins, connections, pluginStates, pluginMessageQueue}: State) => ({
|
||||
({plugins, connections, pluginMessageQueue}: State) => ({
|
||||
plugins,
|
||||
connections,
|
||||
pluginStates,
|
||||
pluginMessageQueue,
|
||||
}),
|
||||
getActiveDevice,
|
||||
|
||||
@@ -27,7 +27,7 @@ import {Store} from '../reducers/index';
|
||||
import Client, {ClientQuery} from '../Client';
|
||||
|
||||
import {Logger} from '../fb-interfaces/Logger';
|
||||
import {FlipperDevicePlugin, PluginDefinition} from '../plugin';
|
||||
import {FlipperDevicePlugin, FlipperPlugin, PluginDefinition} from '../plugin';
|
||||
import PluginContainer from '../PluginContainer';
|
||||
import {getPluginKey, isDevicePluginDefinition} from '../utils/pluginUtils';
|
||||
import MockFlipper from './MockFlipper';
|
||||
@@ -66,13 +66,12 @@ type MockOptions = Partial<{
|
||||
* the base implementation will be used
|
||||
*/
|
||||
onSend?: (pluginId: string, method: string, params?: object) => any;
|
||||
additionalPlugins?: PluginDefinition[];
|
||||
additionalPlugins?: (PluginDefinition | LegacyPluginDefinition)[];
|
||||
dontEnableAdditionalPlugins?: true;
|
||||
asBackgroundPlugin?: true;
|
||||
supportedPlugins?: string[];
|
||||
device?: BaseDevice;
|
||||
archivedDevice?: boolean;
|
||||
disableLegacyWrapper?: boolean;
|
||||
}>;
|
||||
|
||||
function isPluginEnabled(
|
||||
@@ -90,13 +89,14 @@ function isPluginEnabled(
|
||||
);
|
||||
}
|
||||
|
||||
export async function createMockFlipperWithPlugin(
|
||||
pluginClazz: PluginDefinition,
|
||||
options?: MockOptions,
|
||||
): Promise<MockFlipperResult> {
|
||||
function wrapSandy(clazz: PluginDefinition) {
|
||||
return clazz instanceof _SandyPluginDefinition ||
|
||||
options?.disableLegacyWrapper
|
||||
export type LegacyPluginDefinition =
|
||||
| typeof FlipperDevicePlugin
|
||||
| typeof FlipperPlugin;
|
||||
|
||||
export function wrapSandy(
|
||||
clazz: PluginDefinition | LegacyPluginDefinition,
|
||||
): PluginDefinition {
|
||||
return clazz instanceof _SandyPluginDefinition
|
||||
? clazz
|
||||
: createSandyPluginFromClassicPlugin(
|
||||
createMockActivatablePluginDetails({
|
||||
@@ -111,13 +111,16 @@ export async function createMockFlipperWithPlugin(
|
||||
);
|
||||
}
|
||||
|
||||
pluginClazz = wrapSandy(pluginClazz);
|
||||
export async function createMockFlipperWithPlugin(
|
||||
pluginClazzOrig: PluginDefinition | LegacyPluginDefinition,
|
||||
options?: MockOptions,
|
||||
): Promise<MockFlipperResult> {
|
||||
const pluginClazz = wrapSandy(pluginClazzOrig);
|
||||
const additionalPlugins = options?.additionalPlugins?.map(wrapSandy) ?? [];
|
||||
|
||||
const mockFlipper = new MockFlipper();
|
||||
await mockFlipper.init({
|
||||
plugins: [
|
||||
pluginClazz,
|
||||
...(options?.additionalPlugins?.map(wrapSandy) ?? []),
|
||||
],
|
||||
plugins: [pluginClazz, ...additionalPlugins],
|
||||
});
|
||||
const logger = mockFlipper.logger;
|
||||
const store = mockFlipper.store;
|
||||
@@ -150,7 +153,7 @@ export async function createMockFlipperWithPlugin(
|
||||
}
|
||||
|
||||
if (!options?.dontEnableAdditionalPlugins) {
|
||||
options?.additionalPlugins?.forEach((plugin) => {
|
||||
additionalPlugins.forEach((plugin) => {
|
||||
if (!isPluginEnabled(store, plugin, name)) {
|
||||
store.dispatch(
|
||||
switchPlugin({
|
||||
@@ -246,7 +249,7 @@ export async function createMockFlipperWithPlugin(
|
||||
type Renderer = RenderResult<typeof queries>;
|
||||
|
||||
export async function renderMockFlipperWithPlugin(
|
||||
pluginClazz: PluginDefinition,
|
||||
pluginClazzOrig: PluginDefinition | LegacyPluginDefinition,
|
||||
options?: MockOptions,
|
||||
): Promise<
|
||||
MockFlipperResult & {
|
||||
@@ -254,6 +257,7 @@ export async function renderMockFlipperWithPlugin(
|
||||
act: (cb: () => void) => void;
|
||||
}
|
||||
> {
|
||||
const pluginClazz = wrapSandy(pluginClazzOrig);
|
||||
const args = await createMockFlipperWithPlugin(pluginClazz, options);
|
||||
|
||||
function selectTestPlugin(store: Store, client: Client) {
|
||||
|
||||
@@ -20,7 +20,10 @@ import {
|
||||
import {FlipperPlugin, FlipperDevicePlugin} from '../../plugin';
|
||||
import {default as Client, ClientExport} from '../../Client';
|
||||
import {selectedPlugins, State as PluginsState} from '../../reducers/plugins';
|
||||
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {
|
||||
createMockFlipperWithPlugin,
|
||||
wrapSandy,
|
||||
} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {
|
||||
Notification,
|
||||
TestUtils,
|
||||
@@ -39,12 +42,16 @@ function testOnStatusMessage() {
|
||||
// emtpy stub
|
||||
}
|
||||
|
||||
class TestPlugin extends FlipperPlugin<any, any, any> {}
|
||||
TestPlugin.title = 'TestPlugin';
|
||||
TestPlugin.id = 'TestPlugin';
|
||||
class TestDevicePlugin extends FlipperDevicePlugin<any, any, any> {}
|
||||
TestDevicePlugin.title = 'TestDevicePlugin';
|
||||
TestDevicePlugin.id = 'TestDevicePlugin';
|
||||
class TestPluginOrig extends FlipperPlugin<any, any, any> {}
|
||||
TestPluginOrig.title = 'TestPlugin';
|
||||
TestPluginOrig.id = 'TestPlugin';
|
||||
const TestPlugin = wrapSandy(TestPluginOrig);
|
||||
|
||||
class TestDevicePluginOrig extends FlipperDevicePlugin<any, any, any> {}
|
||||
TestDevicePluginOrig.title = 'TestDevicePlugin';
|
||||
TestDevicePluginOrig.id = 'TestDevicePlugin';
|
||||
const TestDevicePlugin = wrapSandy(TestDevicePluginOrig);
|
||||
|
||||
const logger = {
|
||||
track: () => {},
|
||||
info: () => {},
|
||||
@@ -193,7 +200,6 @@ test('test processStore function for empty state', async () => {
|
||||
processStore({
|
||||
activeNotifications: [],
|
||||
device: null,
|
||||
pluginStates: {},
|
||||
clients: [],
|
||||
devicePlugins: new Map(),
|
||||
clientPlugins: new Map(),
|
||||
@@ -216,7 +222,6 @@ test('test processStore function for an iOS device connected', async () => {
|
||||
os: 'iOS',
|
||||
screenshotHandle: null,
|
||||
}),
|
||||
pluginStates: {},
|
||||
pluginStates2: {},
|
||||
clients: [],
|
||||
devicePlugins: new Map(),
|
||||
@@ -238,8 +243,8 @@ test('test processStore function for an iOS device connected', async () => {
|
||||
expect(deviceType).toEqual('emulator');
|
||||
expect(title).toEqual('TestiPhone');
|
||||
expect(os).toEqual('iOS');
|
||||
const {pluginStates, activeNotifications} = json.store;
|
||||
expect(pluginStates).toEqual({});
|
||||
const {activeNotifications} = json.store;
|
||||
expect(json.pluginStates2).toEqual({});
|
||||
expect(activeNotifications).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -256,11 +261,11 @@ test('test processStore function for an iOS device connected with client plugin
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device,
|
||||
pluginStates: {
|
||||
[`${clientIdentifier}#TestPlugin`]: {msg: 'Test plugin'},
|
||||
},
|
||||
pluginStates2: {
|
||||
[`${clientIdentifier}`]: {TestPlugin2: [{msg: 'Test plugin2'}]},
|
||||
[clientIdentifier]: {
|
||||
TestPlugin2: [{msg: 'Test plugin2'}],
|
||||
TestPlugin: {msg: 'Test plugin'},
|
||||
},
|
||||
},
|
||||
clients: [client],
|
||||
devicePlugins: new Map(),
|
||||
@@ -271,25 +276,16 @@ test('test processStore function for an iOS device connected with client plugin
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const expectedPluginState = {
|
||||
[`${generateClientIdentifierWithSalt(
|
||||
clientIdentifier,
|
||||
'salt',
|
||||
)}#TestPlugin`]: JSON.stringify({
|
||||
msg: 'Test plugin',
|
||||
}),
|
||||
};
|
||||
const expectedPluginState2 = {
|
||||
[`${generateClientIdentifierWithSalt(clientIdentifier, 'salt')}`]: {
|
||||
[generateClientIdentifierWithSalt(clientIdentifier, 'salt')]: {
|
||||
TestPlugin2: [
|
||||
{
|
||||
msg: 'Test plugin2',
|
||||
},
|
||||
],
|
||||
TestPlugin: {msg: 'Test plugin'},
|
||||
},
|
||||
};
|
||||
expect(pluginStates).toEqual(expectedPluginState);
|
||||
expect(json.pluginStates2).toEqual(expectedPluginState2);
|
||||
});
|
||||
|
||||
@@ -324,15 +320,18 @@ test('test processStore function to have only the client for the selected device
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device: selectedDevice,
|
||||
pluginStates: {
|
||||
[unselectedDeviceClientIdentifier + '#TestDevicePlugin']: {
|
||||
pluginStates2: {
|
||||
[unselectedDeviceClientIdentifier]: {
|
||||
TestDevicePlugin: {
|
||||
msg: 'Test plugin unselected device',
|
||||
},
|
||||
[selectedDeviceClientIdentifier + '#TestDevicePlugin']: {
|
||||
},
|
||||
[selectedDeviceClientIdentifier]: {
|
||||
TestDevicePlugin: {
|
||||
msg: 'Test plugin selected device',
|
||||
},
|
||||
},
|
||||
pluginStates2: {},
|
||||
},
|
||||
clients: [
|
||||
selectedDeviceClient,
|
||||
generateClientFromDevice(unselectedDevice, 'testapp'),
|
||||
@@ -347,17 +346,18 @@ test('test processStore function to have only the client for the selected device
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {clients} = json;
|
||||
const {pluginStates} = json.store;
|
||||
const expectedPluginState = {
|
||||
[generateClientIdentifierWithSalt(selectedDeviceClientIdentifier, 'salt') +
|
||||
'#TestDevicePlugin']: JSON.stringify({
|
||||
[generateClientIdentifierWithSalt(selectedDeviceClientIdentifier, 'salt')]:
|
||||
{
|
||||
TestDevicePlugin: {
|
||||
msg: 'Test plugin selected device',
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(clients).toEqual([
|
||||
generateClientFromClientWithSalt(selectedDeviceClient, 'salt'),
|
||||
]);
|
||||
expect(pluginStates).toEqual(expectedPluginState);
|
||||
expect(json.pluginStates2).toEqual(expectedPluginState);
|
||||
});
|
||||
|
||||
test('test processStore function to have multiple clients for the selected device', async () => {
|
||||
@@ -384,15 +384,18 @@ test('test processStore function to have multiple clients for the selected devic
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device: selectedDevice,
|
||||
pluginStates: {
|
||||
[clientIdentifierApp1 + '#TestPlugin']: {
|
||||
pluginStates2: {
|
||||
[clientIdentifierApp1]: {
|
||||
TestPlugin: {
|
||||
msg: 'Test plugin App1',
|
||||
},
|
||||
[clientIdentifierApp2 + '#TestPlugin']: {
|
||||
},
|
||||
[clientIdentifierApp2]: {
|
||||
TestPlugin: {
|
||||
msg: 'Test plugin App2',
|
||||
},
|
||||
},
|
||||
pluginStates2: {},
|
||||
},
|
||||
clients: [
|
||||
generateClientFromDevice(selectedDevice, 'testapp1'),
|
||||
generateClientFromDevice(selectedDevice, 'testapp2'),
|
||||
@@ -407,22 +410,23 @@ test('test processStore function to have multiple clients for the selected devic
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {clients} = json;
|
||||
const {pluginStates} = json.store;
|
||||
const expectedPluginState = {
|
||||
[generateClientIdentifierWithSalt(clientIdentifierApp1, 'salt') +
|
||||
'#TestPlugin']: JSON.stringify({
|
||||
[generateClientIdentifierWithSalt(clientIdentifierApp1, 'salt')]: {
|
||||
TestPlugin: {
|
||||
msg: 'Test plugin App1',
|
||||
}),
|
||||
[generateClientIdentifierWithSalt(clientIdentifierApp2, 'salt') +
|
||||
'#TestPlugin']: JSON.stringify({
|
||||
},
|
||||
},
|
||||
[generateClientIdentifierWithSalt(clientIdentifierApp2, 'salt')]: {
|
||||
TestPlugin: {
|
||||
msg: 'Test plugin App2',
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(clients).toEqual([
|
||||
generateClientFromClientWithSalt(client1, 'salt'),
|
||||
generateClientFromClientWithSalt(client2, 'salt'),
|
||||
]);
|
||||
expect(pluginStates).toEqual(expectedPluginState);
|
||||
expect(json.pluginStates2).toEqual(expectedPluginState);
|
||||
});
|
||||
|
||||
test('test processStore function for device plugin state and no clients', async () => {
|
||||
@@ -437,12 +441,13 @@ test('test processStore function for device plugin state and no clients', async
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device: selectedDevice,
|
||||
pluginStates: {
|
||||
'serial#TestDevicePlugin': {
|
||||
pluginStates2: {
|
||||
serial: {
|
||||
TestDevicePlugin: {
|
||||
msg: 'Test Device plugin',
|
||||
},
|
||||
},
|
||||
pluginStates2: {},
|
||||
},
|
||||
clients: [],
|
||||
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
|
||||
clientPlugins: new Map(),
|
||||
@@ -453,12 +458,11 @@ test('test processStore function for device plugin state and no clients', async
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const {clients} = json;
|
||||
const expectedPluginState = {
|
||||
'salt-serial#TestDevicePlugin': JSON.stringify({msg: 'Test Device plugin'}),
|
||||
'salt-serial': {TestDevicePlugin: {msg: 'Test Device plugin'}},
|
||||
};
|
||||
expect(pluginStates).toEqual(expectedPluginState);
|
||||
expect(json.pluginStates2).toEqual(expectedPluginState);
|
||||
expect(clients).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -474,12 +478,13 @@ test('test processStore function for unselected device plugin state and no clien
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device: selectedDevice,
|
||||
pluginStates: {
|
||||
'unselectedDeviceIdentifier#TestDevicePlugin': {
|
||||
pluginStates2: {
|
||||
unselectedDeviceIdentifier: {
|
||||
TestDevicePlugin: {
|
||||
msg: 'Test Device plugin',
|
||||
},
|
||||
},
|
||||
pluginStates2: {},
|
||||
},
|
||||
clients: [],
|
||||
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
|
||||
clientPlugins: new Map(),
|
||||
@@ -489,9 +494,8 @@ test('test processStore function for unselected device plugin state and no clien
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const {clients} = json;
|
||||
expect(pluginStates).toEqual({});
|
||||
expect(json.pluginStates2).toEqual({});
|
||||
expect(clients).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -519,7 +523,6 @@ test('test processStore function for notifications for selected device', async (
|
||||
const json = await processStore({
|
||||
activeNotifications: [activeNotification],
|
||||
device: selectedDevice,
|
||||
pluginStates: {},
|
||||
pluginStates2: {},
|
||||
clients: [client],
|
||||
devicePlugins: new Map([['TestDevicePlugin', TestDevicePlugin]]),
|
||||
@@ -531,9 +534,8 @@ test('test processStore function for notifications for selected device', async (
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const {clients} = json;
|
||||
expect(pluginStates).toEqual({});
|
||||
expect(json.pluginStates2).toEqual({});
|
||||
expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]);
|
||||
const {activeNotifications} = json.store;
|
||||
const expectedActiveNotification = {
|
||||
@@ -580,7 +582,6 @@ test('test processStore function for notifications for unselected device', async
|
||||
const json = await processStore({
|
||||
activeNotifications: [activeNotification],
|
||||
device: selectedDevice,
|
||||
pluginStates: {},
|
||||
pluginStates2: {},
|
||||
clients: [client, unselectedclient],
|
||||
devicePlugins: new Map(),
|
||||
@@ -591,9 +592,8 @@ test('test processStore function for notifications for unselected device', async
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const {clients} = json;
|
||||
expect(pluginStates).toEqual({});
|
||||
expect(json.pluginStates2).toEqual({});
|
||||
expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]);
|
||||
const {activeNotifications} = json.store;
|
||||
expect(activeNotifications).toEqual([]);
|
||||
@@ -610,18 +610,21 @@ test('test processStore function for selected plugins', async () => {
|
||||
|
||||
const client = generateClientFromDevice(selectedDevice, 'app');
|
||||
const pluginstates = {
|
||||
[generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin1']: {
|
||||
[generateClientIdentifier(selectedDevice, 'app')]: {
|
||||
TestDevicePlugin1: {
|
||||
msg: 'Test plugin1',
|
||||
},
|
||||
[generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin2']: {
|
||||
},
|
||||
[generateClientIdentifier(selectedDevice, 'app')]: {
|
||||
TestDevicePlugin2: {
|
||||
msg: 'Test plugin2',
|
||||
},
|
||||
},
|
||||
};
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device: selectedDevice,
|
||||
pluginStates: pluginstates,
|
||||
pluginStates2: {},
|
||||
pluginStates2: pluginstates as any,
|
||||
clients: [client],
|
||||
devicePlugins: new Map([
|
||||
['TestDevicePlugin1', TestDevicePlugin],
|
||||
@@ -634,15 +637,16 @@ test('test processStore function for selected plugins', async () => {
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const {clients} = json;
|
||||
expect(pluginStates).toEqual({
|
||||
expect(json.pluginStates2).toEqual({
|
||||
[generateClientIdentifierWithSalt(
|
||||
generateClientIdentifier(selectedDevice, 'app'),
|
||||
'salt',
|
||||
) + '#TestDevicePlugin2']: JSON.stringify({
|
||||
)]: {
|
||||
TestDevicePlugin2: {
|
||||
msg: 'Test plugin2',
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]);
|
||||
const {activeNotifications} = json.store;
|
||||
@@ -659,18 +663,19 @@ test('test processStore function for no selected plugins', async () => {
|
||||
});
|
||||
const client = generateClientFromDevice(selectedDevice, 'app');
|
||||
const pluginstates = {
|
||||
[generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin1']: {
|
||||
[generateClientIdentifier(selectedDevice, 'app')]: {
|
||||
TestDevicePlugin1: {
|
||||
msg: 'Test plugin1',
|
||||
},
|
||||
[generateClientIdentifier(selectedDevice, 'app') + '#TestDevicePlugin2']: {
|
||||
TestDevicePlugin2: {
|
||||
msg: 'Test plugin2',
|
||||
},
|
||||
},
|
||||
};
|
||||
const json = await processStore({
|
||||
activeNotifications: [],
|
||||
device: selectedDevice,
|
||||
pluginStates: pluginstates,
|
||||
pluginStates2: {},
|
||||
pluginStates2: pluginstates as any,
|
||||
clients: [client],
|
||||
devicePlugins: new Map([
|
||||
['TestDevicePlugin1', TestDevicePlugin],
|
||||
@@ -684,21 +689,20 @@ test('test processStore function for no selected plugins', async () => {
|
||||
if (!json) {
|
||||
fail('json is undefined');
|
||||
}
|
||||
const {pluginStates} = json.store;
|
||||
const {clients} = json;
|
||||
expect(pluginStates).toEqual({
|
||||
expect(json.pluginStates2).toEqual({
|
||||
[generateClientIdentifierWithSalt(
|
||||
generateClientIdentifier(selectedDevice, 'app'),
|
||||
'salt',
|
||||
) + '#TestDevicePlugin2']: JSON.stringify({
|
||||
)]: {
|
||||
TestDevicePlugin2: {
|
||||
msg: 'Test plugin2',
|
||||
}),
|
||||
[generateClientIdentifierWithSalt(
|
||||
generateClientIdentifier(selectedDevice, 'app'),
|
||||
'salt',
|
||||
) + '#TestDevicePlugin1']: JSON.stringify({
|
||||
},
|
||||
|
||||
TestDevicePlugin1: {
|
||||
msg: 'Test plugin1',
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(clients).toEqual([generateClientFromClientWithSalt(client, 'salt')]);
|
||||
const {activeNotifications} = json.store;
|
||||
@@ -781,14 +785,12 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
|
||||
pluginKey: `${client1.id}#TestPlugin`,
|
||||
pluginId: 'TestPlugin',
|
||||
pluginName: 'TestPlugin',
|
||||
pluginClass: TestPlugin,
|
||||
client: client1,
|
||||
},
|
||||
{
|
||||
pluginKey: `${client3.id}#TestPlugin`,
|
||||
pluginId: 'TestPlugin',
|
||||
pluginName: 'TestPlugin',
|
||||
pluginClass: TestPlugin,
|
||||
client: client3,
|
||||
},
|
||||
]);
|
||||
@@ -905,7 +907,6 @@ test('test determinePluginsToProcess for multiple clients on same device', async
|
||||
pluginKey: `${client1.id}#TestPlugin`,
|
||||
pluginId: 'TestPlugin',
|
||||
pluginName: 'TestPlugin',
|
||||
pluginClass: TestPlugin,
|
||||
client: client1,
|
||||
},
|
||||
]);
|
||||
@@ -999,7 +1000,6 @@ test('test determinePluginsToProcess for multiple clients on different device',
|
||||
pluginKey: `${client1Device2.id}#TestPlugin`,
|
||||
pluginId: 'TestPlugin',
|
||||
pluginName: 'TestPlugin',
|
||||
pluginClass: TestPlugin,
|
||||
client: client1Device2,
|
||||
},
|
||||
]);
|
||||
@@ -1085,7 +1085,6 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
|
||||
pluginKey: `${client.id}#TestPlugin`,
|
||||
pluginId: 'TestPlugin',
|
||||
pluginName: 'TestPlugin',
|
||||
pluginClass: TestPlugin,
|
||||
client: client,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -1,684 +0,0 @@
|
||||
/**
|
||||
* 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 {FlipperPlugin, FlipperDevicePlugin} from '../../plugin';
|
||||
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {Store, Client, sleep} from '../../';
|
||||
import {
|
||||
selectPlugin,
|
||||
selectClient,
|
||||
selectDevice,
|
||||
} from '../../reducers/connections';
|
||||
import {processMessageQueue} from '../messageQueue';
|
||||
import {getPluginKey} from '../pluginUtils';
|
||||
import {TestIdler} from '../Idler';
|
||||
import pluginMessageQueue, {
|
||||
State,
|
||||
queueMessages,
|
||||
} from '../../reducers/pluginMessageQueue';
|
||||
import {registerPlugins} from '../../reducers/plugins';
|
||||
import {switchPlugin} from '../../reducers/pluginManager';
|
||||
|
||||
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: {delta?: number},
|
||||
) {
|
||||
if (method === 'inc') {
|
||||
return Object.assign({}, persistedState, {
|
||||
count: persistedState.count + ((payload && payload?.delta) || 1),
|
||||
});
|
||||
}
|
||||
return persistedState;
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function switchTestPlugin(store: Store, client: Client) {
|
||||
store.dispatch(
|
||||
switchPlugin({
|
||||
plugin: TestPlugin,
|
||||
selectedApp: client.query.app,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function selectDeviceLogs(store: Store) {
|
||||
store.dispatch(
|
||||
selectPlugin({
|
||||
selectedPlugin: 'DeviceLogs',
|
||||
selectedApp: null,
|
||||
deepLinkPayload: null,
|
||||
selectedDevice: store.getState().connections.selectedDevice!,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function selectTestPlugin(store: Store, client: Client) {
|
||||
store.dispatch(
|
||||
selectPlugin({
|
||||
selectedPlugin: TestPlugin.id,
|
||||
selectedApp: client.query.app,
|
||||
deepLinkPayload: null,
|
||||
selectedDevice: store.getState().connections.selectedDevice!,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
test('queue - events are processed immediately if plugin is selected', async () => {
|
||||
const {store, client, sendMessage} = await createMockFlipperWithPlugin(
|
||||
TestPlugin,
|
||||
{
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
},
|
||||
);
|
||||
expect(store.getState().connections.selectedPlugin).toBe('TestPlugin');
|
||||
sendMessage('noop', {});
|
||||
sendMessage('noop', {});
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 4});
|
||||
sendMessage('noop', {});
|
||||
client.flushMessageBuffer();
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
|
||||
"count": 5,
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(
|
||||
`Object {}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('queue - events are NOT processed immediately if plugin is NOT selected (but enabled)', async () => {
|
||||
const {store, client, sendMessage, device} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
expect(store.getState().connections.selectedPlugin).not.toBe('TestPlugin');
|
||||
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3});
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
// the first message is already visible cause of the leading debounce
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
client.flushMessageBuffer();
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
||||
// process the message
|
||||
const pluginKey = getPluginKey(client.id, device, TestPlugin.id);
|
||||
await processMessageQueue(TestPlugin, pluginKey, store);
|
||||
expect(store.getState().pluginStates).toEqual({
|
||||
[pluginKey]: {
|
||||
count: 6,
|
||||
},
|
||||
});
|
||||
|
||||
expect(store.getState().pluginMessageQueue).toEqual({
|
||||
[pluginKey]: [],
|
||||
});
|
||||
|
||||
// disable, but, messages still arrives because selected
|
||||
switchTestPlugin(store, client);
|
||||
selectTestPlugin(store, client);
|
||||
sendMessage('inc', {delta: 3});
|
||||
client.flushMessageBuffer();
|
||||
// active, immediately processed
|
||||
expect(store.getState().pluginStates).toEqual({
|
||||
[pluginKey]: {
|
||||
count: 9,
|
||||
},
|
||||
});
|
||||
|
||||
// different plugin, and not enabled, message will never arrive
|
||||
selectDeviceLogs(store);
|
||||
sendMessage('inc', {delta: 4});
|
||||
client.flushMessageBuffer();
|
||||
expect(store.getState().pluginMessageQueue).toEqual({});
|
||||
|
||||
// star again, plugin still not selected, message is queued
|
||||
switchTestPlugin(store, client);
|
||||
sendMessage('inc', {delta: 5});
|
||||
client.flushMessageBuffer();
|
||||
|
||||
expect(store.getState().pluginMessageQueue).toEqual({
|
||||
[pluginKey]: [{api: 'TestPlugin', method: 'inc', params: {delta: 5}}],
|
||||
});
|
||||
});
|
||||
|
||||
test('queue - events are queued for plugins that are favorite when app is not selected', async () => {
|
||||
const {device, store, sendMessage, createClient} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
expect(store.getState().connections.selectedPlugin).not.toBe('TestPlugin');
|
||||
|
||||
const client2 = await createClient(device, 'TestApp2');
|
||||
store.dispatch(selectClient(client2.id));
|
||||
|
||||
// Now we send a message to the second client, it should arrive,
|
||||
// as the plugin was enabled already on the first client as well
|
||||
sendMessage('inc', {delta: 2});
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('queue - events are queued for plugins that are favorite when app is selected on different device', async () => {
|
||||
const {client, store, sendMessage, createDevice, createClient} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
expect(store.getState().connections.selectedPlugin).not.toBe('TestPlugin');
|
||||
|
||||
const device2 = createDevice('serial2');
|
||||
const client2 = await createClient(device2, client.query.app); // same app id
|
||||
store.dispatch(selectDevice(device2));
|
||||
store.dispatch(selectClient(client2.id));
|
||||
|
||||
// Now we send a message to the first and second client, it should arrive,
|
||||
// as the plugin was enabled already on the first client as well
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3}, client2);
|
||||
client.flushMessageBuffer();
|
||||
client2.flushMessageBuffer();
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
"TestApp#Android#MockAndroidDevice#serial2#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('queue - events processing will be paused', async () => {
|
||||
const {client, device, store, sendMessage} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 3});
|
||||
sendMessage('inc', {delta: 5});
|
||||
client.flushMessageBuffer();
|
||||
|
||||
// process the message
|
||||
const pluginKey = getPluginKey(client.id, device, TestPlugin.id);
|
||||
|
||||
// controlled idler will signal and and off that idling is needed
|
||||
const idler = new TestIdler();
|
||||
|
||||
const p = processMessageQueue(TestPlugin, pluginKey, store, undefined, idler);
|
||||
|
||||
expect(store.getState().pluginStates).toEqual({
|
||||
[pluginKey]: {
|
||||
count: 4,
|
||||
},
|
||||
});
|
||||
|
||||
expect(store.getState().pluginMessageQueue).toEqual({
|
||||
[pluginKey]: [{api: 'TestPlugin', method: 'inc', params: {delta: 5}}],
|
||||
});
|
||||
|
||||
await idler.next();
|
||||
expect(store.getState().pluginStates).toEqual({
|
||||
[pluginKey]: {
|
||||
count: 9,
|
||||
},
|
||||
});
|
||||
|
||||
expect(store.getState().pluginMessageQueue).toEqual({
|
||||
[pluginKey]: [],
|
||||
});
|
||||
|
||||
// don't idle anymore
|
||||
idler.run();
|
||||
await p;
|
||||
});
|
||||
|
||||
test('queue - messages that arrive during processing will be queued', async () => {
|
||||
const {client, device, store, sendMessage} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3});
|
||||
client.flushMessageBuffer();
|
||||
|
||||
// process the message
|
||||
const pluginKey = getPluginKey(client.id, device, TestPlugin.id);
|
||||
|
||||
const idler = new TestIdler();
|
||||
|
||||
const p = processMessageQueue(TestPlugin, pluginKey, store, undefined, idler);
|
||||
|
||||
// first message is consumed
|
||||
expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(1);
|
||||
expect(store.getState().pluginStates[pluginKey].count).toBe(3);
|
||||
|
||||
// Select the current plugin as active, still, messages should end up in the queue
|
||||
store.dispatch(
|
||||
selectPlugin({
|
||||
selectedPlugin: TestPlugin.id,
|
||||
selectedApp: client.id,
|
||||
deepLinkPayload: null,
|
||||
selectedDevice: device,
|
||||
}),
|
||||
);
|
||||
expect(store.getState().connections.selectedPlugin).toBe('TestPlugin');
|
||||
|
||||
sendMessage('inc', {delta: 4});
|
||||
client.flushMessageBuffer();
|
||||
// should not be processed yet
|
||||
expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(2);
|
||||
expect(store.getState().pluginStates[pluginKey].count).toBe(3);
|
||||
|
||||
await idler.next();
|
||||
expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(0);
|
||||
expect(store.getState().pluginStates[pluginKey].count).toBe(10);
|
||||
|
||||
idler.run();
|
||||
await p;
|
||||
});
|
||||
|
||||
test('queue - processing can be cancelled', async () => {
|
||||
const {client, device, store, sendMessage} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3});
|
||||
sendMessage('inc', {delta: 4});
|
||||
sendMessage('inc', {delta: 5});
|
||||
client.flushMessageBuffer();
|
||||
|
||||
// process the message
|
||||
const pluginKey = getPluginKey(client.id, device, TestPlugin.id);
|
||||
|
||||
const idler = new TestIdler();
|
||||
|
||||
const p = processMessageQueue(TestPlugin, pluginKey, store, undefined, idler);
|
||||
|
||||
// first message is consumed
|
||||
await idler.next();
|
||||
expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(1);
|
||||
expect(store.getState().pluginStates[pluginKey].count).toBe(10);
|
||||
|
||||
idler.cancel();
|
||||
|
||||
expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(1);
|
||||
expect(store.getState().pluginStates[pluginKey].count).toBe(10);
|
||||
await p;
|
||||
});
|
||||
|
||||
test('queue - make sure resetting plugin state clears the message queue', async () => {
|
||||
const {client, device, store, sendMessage} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
client.flushMessageBuffer();
|
||||
|
||||
const pluginKey = getPluginKey(client.id, device, TestPlugin.id);
|
||||
|
||||
expect(store.getState().pluginMessageQueue[pluginKey].length).toBe(2);
|
||||
|
||||
store.dispatch({
|
||||
type: 'CLEAR_CLIENT_PLUGINS_STATE',
|
||||
payload: {clientId: client.id, devicePlugins: new Set()},
|
||||
});
|
||||
|
||||
expect(store.getState().pluginMessageQueue[pluginKey]).toBe(undefined);
|
||||
});
|
||||
|
||||
test('queue will be cleaned up when it exceeds maximum size', () => {
|
||||
let state: State = {};
|
||||
const pluginKey = 'test';
|
||||
const queueSize = 5000;
|
||||
let i = 0;
|
||||
for (i = 0; i < queueSize; i++) {
|
||||
state = pluginMessageQueue(
|
||||
state,
|
||||
queueMessages(pluginKey, [{method: 'test', params: {i}}], queueSize),
|
||||
);
|
||||
}
|
||||
// almost full
|
||||
expect(state[pluginKey][0]).toEqual({method: 'test', params: {i: 0}});
|
||||
expect(state[pluginKey].length).toBe(queueSize); // ~5000
|
||||
expect(state[pluginKey][queueSize - 1]).toEqual({
|
||||
method: 'test',
|
||||
params: {i: queueSize - 1}, // ~4999
|
||||
});
|
||||
|
||||
state = pluginMessageQueue(
|
||||
state,
|
||||
queueMessages(pluginKey, [{method: 'test', params: {i: ++i}}], queueSize),
|
||||
);
|
||||
|
||||
const newLength = Math.ceil(0.9 * queueSize) + 1; // ~4500
|
||||
expect(state[pluginKey].length).toBe(newLength);
|
||||
expect(state[pluginKey][0]).toEqual({
|
||||
method: 'test',
|
||||
params: {i: queueSize - newLength + 1}, // ~500
|
||||
});
|
||||
expect(state[pluginKey][newLength - 1]).toEqual({
|
||||
method: 'test',
|
||||
params: {i: i}, // ~50001
|
||||
});
|
||||
});
|
||||
|
||||
test('client - incoming messages are buffered and flushed together', async () => {
|
||||
class StubDeviceLogs extends FlipperDevicePlugin<any, any, any> {
|
||||
static id = 'DevicePlugin';
|
||||
|
||||
static supportsDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static persistedStateReducer = jest.fn();
|
||||
}
|
||||
|
||||
const {client, store, device, sendMessage} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
store.dispatch(registerPlugins([StubDeviceLogs]));
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3});
|
||||
|
||||
// send a message to device logs
|
||||
client.onMessage(
|
||||
JSON.stringify({
|
||||
method: 'execute',
|
||||
params: {
|
||||
api: 'DevicePlugin',
|
||||
method: 'log',
|
||||
params: {line: 'suff'},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
// the first message is already visible cause of the leading debounce
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(client.messageBuffer).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Object {
|
||||
"messages": Array [
|
||||
Object {
|
||||
"api": "DevicePlugin",
|
||||
"method": "log",
|
||||
"params": Object {
|
||||
"line": "suff",
|
||||
},
|
||||
},
|
||||
],
|
||||
"plugin": [Function],
|
||||
},
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
|
||||
"messages": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
"plugin": [Function],
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
await sleep(500);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [
|
||||
Object {
|
||||
"api": "DevicePlugin",
|
||||
"method": "log",
|
||||
"params": Object {
|
||||
"line": "suff",
|
||||
},
|
||||
},
|
||||
],
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(StubDeviceLogs.persistedStateReducer.mock.calls).toMatchInlineSnapshot(
|
||||
`Array []`,
|
||||
);
|
||||
|
||||
// tigger processing the queue
|
||||
const pluginKey = getPluginKey(client.id, device, StubDeviceLogs.id);
|
||||
await processMessageQueue(StubDeviceLogs, pluginKey, store);
|
||||
|
||||
expect(StubDeviceLogs.persistedStateReducer.mock.calls)
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {},
|
||||
"log",
|
||||
Object {
|
||||
"line": "suff",
|
||||
},
|
||||
],
|
||||
]
|
||||
`);
|
||||
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [],
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('queue - messages that have not yet flushed be lost when disabling the plugin', async () => {
|
||||
const {client, store, sendMessage, pluginKey} =
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
disableLegacyWrapper: true, // Sandy is already tested in messageQueueSandy.node.tsx
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
|
||||
expect(client.messageBuffer).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
|
||||
"messages": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {
|
||||
"delta": 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
"plugin": [Function],
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
"method": "inc",
|
||||
"params": Object {},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
||||
// disable
|
||||
switchTestPlugin(store, client);
|
||||
expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(
|
||||
`Object {}`,
|
||||
);
|
||||
|
||||
// re-enable, no messages arrive
|
||||
switchTestPlugin(store, client);
|
||||
client.flushMessageBuffer();
|
||||
processMessageQueue(TestPlugin, pluginKey, store);
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
});
|
||||
@@ -7,8 +7,11 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {FlipperDevicePlugin} from '../../plugin';
|
||||
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {FlipperPlugin} from '../../plugin';
|
||||
import {
|
||||
createMockFlipperWithPlugin,
|
||||
wrapSandy,
|
||||
} from '../../test-utils/createMockFlipperWithPlugin';
|
||||
import {Store, Client, sleep} from '../../';
|
||||
import {
|
||||
selectPlugin,
|
||||
@@ -26,6 +29,10 @@ import {
|
||||
_SandyPluginInstance,
|
||||
} from 'flipper-plugin';
|
||||
import {switchPlugin} from '../../reducers/pluginManager';
|
||||
import pluginMessageQueue, {
|
||||
State,
|
||||
queueMessages,
|
||||
} from '../../reducers/pluginMessageQueue';
|
||||
|
||||
type Events = {
|
||||
inc: {
|
||||
@@ -124,7 +131,6 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3});
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(getTestPluginState(client).count).toBe(0);
|
||||
// the first message is already visible cause of the leading debounce
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
@@ -482,21 +488,21 @@ test('queue - make sure resetting plugin state clears the message queue', async
|
||||
});
|
||||
|
||||
test('client - incoming messages are buffered and flushed together', async () => {
|
||||
class StubDeviceLogs extends FlipperDevicePlugin<any, any, any> {
|
||||
static id = 'DevicePlugin';
|
||||
|
||||
static supportsDevice() {
|
||||
return true;
|
||||
}
|
||||
class StubPlugin extends FlipperPlugin<any, any, any> {
|
||||
static id = 'StubPlugin';
|
||||
|
||||
static persistedStateReducer = jest.fn();
|
||||
}
|
||||
|
||||
const StubPluginWrapped = wrapSandy(StubPlugin);
|
||||
|
||||
const {client, store, device, sendMessage, pluginKey} =
|
||||
await createMockFlipperWithPlugin(TestPlugin);
|
||||
await createMockFlipperWithPlugin(TestPlugin, {
|
||||
additionalPlugins: [StubPluginWrapped],
|
||||
});
|
||||
selectDeviceLogs(store);
|
||||
|
||||
store.dispatch(registerPlugins([StubDeviceLogs]));
|
||||
store.dispatch(registerPlugins([StubPluginWrapped]));
|
||||
sendMessage('inc', {});
|
||||
sendMessage('inc', {delta: 2});
|
||||
sendMessage('inc', {delta: 3});
|
||||
@@ -506,14 +512,13 @@ test('client - incoming messages are buffered and flushed together', async () =>
|
||||
JSON.stringify({
|
||||
method: 'execute',
|
||||
params: {
|
||||
api: 'DevicePlugin',
|
||||
api: 'StubPlugin',
|
||||
method: 'log',
|
||||
params: {line: 'suff'},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(store.getState().pluginStates).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(getTestPluginState(client).count).toBe(0);
|
||||
// the first message is already visible cause of the leading debounce
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
@@ -529,17 +534,17 @@ test('client - incoming messages are buffered and flushed together', async () =>
|
||||
`);
|
||||
expect(client.messageBuffer).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#StubPlugin": Object {
|
||||
"messages": Array [
|
||||
Object {
|
||||
"api": "DevicePlugin",
|
||||
"api": "StubPlugin",
|
||||
"method": "log",
|
||||
"params": Object {
|
||||
"line": "suff",
|
||||
},
|
||||
},
|
||||
],
|
||||
"plugin": [Function],
|
||||
"plugin": "[SandyPluginInstance]",
|
||||
},
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
|
||||
"messages": Array [
|
||||
@@ -569,9 +574,9 @@ test('client - incoming messages are buffered and flushed together', async () =>
|
||||
await sleep(500);
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [
|
||||
"TestApp#Android#MockAndroidDevice#serial#StubPlugin": Array [
|
||||
Object {
|
||||
"api": "DevicePlugin",
|
||||
"api": "StubPlugin",
|
||||
"method": "log",
|
||||
"params": Object {
|
||||
"line": "suff",
|
||||
@@ -602,19 +607,22 @@ test('client - incoming messages are buffered and flushed together', async () =>
|
||||
}
|
||||
`);
|
||||
expect(client.messageBuffer).toMatchInlineSnapshot(`Object {}`);
|
||||
expect(StubDeviceLogs.persistedStateReducer.mock.calls).toMatchInlineSnapshot(
|
||||
expect(StubPlugin.persistedStateReducer.mock.calls).toMatchInlineSnapshot(
|
||||
`Array []`,
|
||||
);
|
||||
|
||||
// tigger processing the queue
|
||||
const pluginKeyDevice = getPluginKey(client.id, device, StubDeviceLogs.id);
|
||||
await processMessageQueue(StubDeviceLogs, pluginKeyDevice, store);
|
||||
const pluginKeyDevice = getPluginKey(client.id, device, StubPlugin.id);
|
||||
await processMessageQueue(
|
||||
client.sandyPluginStates.get(StubPlugin.id)!,
|
||||
pluginKeyDevice,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(StubDeviceLogs.persistedStateReducer.mock.calls)
|
||||
.toMatchInlineSnapshot(`
|
||||
expect(StubPlugin.persistedStateReducer.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
Object {},
|
||||
undefined,
|
||||
"log",
|
||||
Object {
|
||||
"line": "suff",
|
||||
@@ -625,7 +633,7 @@ test('client - incoming messages are buffered and flushed together', async () =>
|
||||
|
||||
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"TestApp#Android#MockAndroidDevice#serial#DevicePlugin": Array [],
|
||||
"TestApp#Android#MockAndroidDevice#serial#StubPlugin": Array [],
|
||||
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
|
||||
Object {
|
||||
"api": "TestPlugin",
|
||||
@@ -704,3 +712,39 @@ test('queue - messages that have not yet flushed be lost when disabling the plug
|
||||
);
|
||||
expect(getTestPluginState(client)).toEqual({count: 0});
|
||||
});
|
||||
|
||||
test('queue will be cleaned up when it exceeds maximum size', () => {
|
||||
let state: State = {};
|
||||
const pluginKey = 'test';
|
||||
const queueSize = 5000;
|
||||
let i = 0;
|
||||
for (i = 0; i < queueSize; i++) {
|
||||
state = pluginMessageQueue(
|
||||
state,
|
||||
queueMessages(pluginKey, [{method: 'test', params: {i}}], queueSize),
|
||||
);
|
||||
}
|
||||
// almost full
|
||||
expect(state[pluginKey][0]).toEqual({method: 'test', params: {i: 0}});
|
||||
expect(state[pluginKey].length).toBe(queueSize); // ~5000
|
||||
expect(state[pluginKey][queueSize - 1]).toEqual({
|
||||
method: 'test',
|
||||
params: {i: queueSize - 1}, // ~4999
|
||||
});
|
||||
|
||||
state = pluginMessageQueue(
|
||||
state,
|
||||
queueMessages(pluginKey, [{method: 'test', params: {i: ++i}}], queueSize),
|
||||
);
|
||||
|
||||
const newLength = Math.ceil(0.9 * queueSize) + 1; // ~4500
|
||||
expect(state[pluginKey].length).toBe(newLength);
|
||||
expect(state[pluginKey][0]).toEqual({
|
||||
method: 'test',
|
||||
params: {i: queueSize - newLength + 1}, // ~500
|
||||
});
|
||||
expect(state[pluginKey][newLength - 1]).toEqual({
|
||||
method: 'test',
|
||||
params: {i: i}, // ~50001
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ function createMockFlipperPluginWithNoPersistedState(id: string) {
|
||||
}
|
||||
|
||||
test('getActivePersistentPlugins, where the non persistent plugins getting excluded', async () => {
|
||||
const {store, device, client} = await createMockFlipperWithPlugin(
|
||||
const {store} = await createMockFlipperWithPlugin(
|
||||
createMockFlipperPluginWithDefaultPersistedState('ClientPlugin1'),
|
||||
{
|
||||
additionalPlugins: [
|
||||
@@ -85,11 +85,6 @@ test('getActivePersistentPlugins, where the non persistent plugins getting exclu
|
||||
);
|
||||
const state = store.getState();
|
||||
|
||||
state.pluginStates = {
|
||||
[getPluginKey(client.id, device, 'ClientPlugin1')]: {msg: 'DevicePlugin1'},
|
||||
[getPluginKey(client.id, device, 'ClientPlugin4')]: {msg: 'ClientPlugin2'},
|
||||
};
|
||||
|
||||
const list = getExportablePlugins(state);
|
||||
expect(list).toEqual([
|
||||
{
|
||||
@@ -97,23 +92,23 @@ test('getActivePersistentPlugins, where the non persistent plugins getting exclu
|
||||
label: 'ClientPlugin1',
|
||||
},
|
||||
{
|
||||
id: 'ClientPlugin4',
|
||||
label: 'ClientPlugin4',
|
||||
id: 'ClientPlugin2',
|
||||
label: 'ClientPlugin2',
|
||||
},
|
||||
{
|
||||
id: 'ClientPlugin5',
|
||||
label: 'ClientPlugin5',
|
||||
},
|
||||
// { Never activated, and no data received
|
||||
// id: 'ClientPlugin5',
|
||||
// label: 'ClientPlugin5',
|
||||
// },
|
||||
]);
|
||||
});
|
||||
|
||||
test('getActivePersistentPlugins, where the plugins not in pluginState or queue gets excluded', async () => {
|
||||
test('getActivePersistentPlugins, with message queue', async () => {
|
||||
const {store, device, client} = await createMockFlipperWithPlugin(
|
||||
createMockFlipperPluginWithDefaultPersistedState('Plugin1'),
|
||||
{
|
||||
additionalPlugins: [
|
||||
createMockDeviceFlipperPlugin('DevicePlugin2'),
|
||||
createMockFlipperPluginWithDefaultPersistedState('ClientPlugin1'),
|
||||
createMockFlipperPluginWithNoPersistedState('ClientPlugin1'),
|
||||
createMockFlipperPluginWithDefaultPersistedState('ClientPlugin2'),
|
||||
createMockFlipperPluginWithDefaultPersistedState('ClientPlugin3'),
|
||||
],
|
||||
@@ -122,9 +117,6 @@ test('getActivePersistentPlugins, where the plugins not in pluginState or queue
|
||||
|
||||
const state = store.getState();
|
||||
|
||||
state.pluginStates = {
|
||||
[getPluginKey(client.id, device, 'ClientPlugin2')]: {msg: 'ClientPlugin2'},
|
||||
};
|
||||
state.pluginMessageQueue = {
|
||||
[getPluginKey(client.id, device, 'ClientPlugin3')]: [
|
||||
{method: 'msg', params: {msg: 'ClientPlugin3'}},
|
||||
|
||||
@@ -70,8 +70,7 @@ export function createSandyPluginWrapper<S, A extends BaseAction, P>(
|
||||
if (
|
||||
Plugin.persistedStateReducer ||
|
||||
Plugin.exportPersistedState ||
|
||||
Plugin.defaultPersistedState ||
|
||||
Plugin.serializePersistedState
|
||||
Plugin.defaultPersistedState
|
||||
) {
|
||||
client.onExport(async (idler, onStatusMessage) => {
|
||||
const state = Plugin.exportPersistedState
|
||||
|
||||
@@ -11,21 +11,14 @@ import os from 'os';
|
||||
import path from 'path';
|
||||
import electron from 'electron';
|
||||
import {getInstance as getLogger} from '../fb-stubs/Logger';
|
||||
import {Store, State as ReduxState, MiddlewareAPI} from '../reducers';
|
||||
import {Store, MiddlewareAPI} from '../reducers';
|
||||
import {DeviceExport} from '../devices/BaseDevice';
|
||||
import {State as PluginStatesState} from '../reducers/pluginStates';
|
||||
import {State as PluginsState} from '../reducers/plugins';
|
||||
import {PluginNotification} from '../reducers/notifications';
|
||||
import Client, {ClientExport, ClientQuery} from '../Client';
|
||||
import {getAppVersion} from './info';
|
||||
import {pluginKey} from '../reducers/pluginStates';
|
||||
import {
|
||||
callClient,
|
||||
supportsMethod,
|
||||
PluginDefinition,
|
||||
DevicePluginMap,
|
||||
ClientPluginMap,
|
||||
} from '../plugin';
|
||||
import {pluginKey} from '../utils/pluginUtils';
|
||||
import {DevicePluginMap, ClientPluginMap} from '../plugin';
|
||||
import {default as BaseDevice} from '../devices/BaseDevice';
|
||||
import {default as ArchivedDevice} from '../devices/ArchivedDevice';
|
||||
import fs from 'fs';
|
||||
@@ -34,7 +27,6 @@ import {remote, OpenDialogOptions} from 'electron';
|
||||
import {readCurrentRevision} from './packageMetadata';
|
||||
import {tryCatchReportPlatformFailures} from './metrics';
|
||||
import {promisify} from 'util';
|
||||
import promiseTimeout from './promiseTimeout';
|
||||
import {TestIdler} from './Idler';
|
||||
import {setStaticView} from '../reducers/connections';
|
||||
import {
|
||||
@@ -42,10 +34,10 @@ import {
|
||||
SupportFormRequestDetailsState,
|
||||
} from '../reducers/supportForm';
|
||||
import {setSelectPluginsToExportActiveSheet} from '../reducers/application';
|
||||
import {deconstructClientId, deconstructPluginKey} from '../utils/clientUtils';
|
||||
import {deconstructClientId} from '../utils/clientUtils';
|
||||
import {performance} from 'perf_hooks';
|
||||
import {processMessageQueue} from './messageQueue';
|
||||
import {getPluginTitle, isSandyPlugin} from './pluginUtils';
|
||||
import {getPluginTitle} from './pluginUtils';
|
||||
import {capture} from './screenshot';
|
||||
import {uploadFlipperMedia} from '../fb-stubs/user';
|
||||
import {Idler} from 'flipper-plugin';
|
||||
@@ -71,7 +63,6 @@ export type ExportType = {
|
||||
device: DeviceExport | null;
|
||||
deviceScreenshot: string | null;
|
||||
store: {
|
||||
pluginStates: PluginStatesExportState;
|
||||
activeNotifications: Array<PluginNotification>;
|
||||
};
|
||||
// The GraphQL plugin relies on this format for generating
|
||||
@@ -80,15 +71,6 @@ export type ExportType = {
|
||||
supportRequestDetails?: SupportFormRequestDetailsState;
|
||||
};
|
||||
|
||||
type ProcessPluginStatesOptions = {
|
||||
clients: Array<ClientExport>;
|
||||
serial: string;
|
||||
allPluginStates: PluginStatesState;
|
||||
devicePlugins: DevicePluginMap;
|
||||
selectedPlugins: Array<string>;
|
||||
statusUpdate?: (msg: string) => void;
|
||||
};
|
||||
|
||||
type ProcessNotificationStatesOptions = {
|
||||
clients: Array<ClientExport>;
|
||||
serial: string;
|
||||
@@ -101,7 +83,6 @@ type PluginsToProcess = {
|
||||
pluginKey: string;
|
||||
pluginId: string;
|
||||
pluginName: string;
|
||||
pluginClass: PluginDefinition;
|
||||
client: Client;
|
||||
}[];
|
||||
|
||||
@@ -110,7 +91,6 @@ type AddSaltToDeviceSerialOptions = {
|
||||
device: BaseDevice;
|
||||
deviceScreenshot: string | null;
|
||||
clients: Array<ClientExport>;
|
||||
pluginStates: PluginStatesExportState;
|
||||
pluginStates2: SandyPluginStates;
|
||||
devicePluginStates: Record<string, any>;
|
||||
pluginNotification: Array<PluginNotification>;
|
||||
@@ -152,51 +132,6 @@ export function processClients(
|
||||
return filteredClients;
|
||||
}
|
||||
|
||||
export function processPluginStates(
|
||||
options: ProcessPluginStatesOptions,
|
||||
): PluginStatesState {
|
||||
const {
|
||||
clients,
|
||||
serial,
|
||||
allPluginStates,
|
||||
devicePlugins,
|
||||
selectedPlugins,
|
||||
statusUpdate,
|
||||
} = options;
|
||||
|
||||
let pluginStates: PluginStatesState = {};
|
||||
statusUpdate &&
|
||||
statusUpdate('Filtering the plugin states for the filtered Clients...');
|
||||
for (const key in allPluginStates) {
|
||||
const plugin = deconstructPluginKey(key);
|
||||
|
||||
const pluginName = plugin.pluginName;
|
||||
if (
|
||||
pluginName &&
|
||||
selectedPlugins.length > 0 &&
|
||||
!selectedPlugins.includes(pluginName)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (plugin.type === 'client') {
|
||||
if (!clients.some((c) => c.id.includes(plugin.client))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (plugin.type === 'device') {
|
||||
if (
|
||||
!pluginName ||
|
||||
!devicePlugins.has(pluginName) ||
|
||||
serial !== plugin.client
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pluginStates = {...pluginStates, [key]: allPluginStates[key]};
|
||||
}
|
||||
return pluginStates;
|
||||
}
|
||||
|
||||
export function processNotificationStates(
|
||||
options: ProcessNotificationStatesOptions,
|
||||
): Array<PluginNotification> {
|
||||
@@ -216,41 +151,6 @@ export function processNotificationStates(
|
||||
return activeNotifications;
|
||||
}
|
||||
|
||||
const serializePluginStates = async (
|
||||
pluginStates: PluginStatesState,
|
||||
clientPlugins: ClientPluginMap,
|
||||
devicePlugins: DevicePluginMap,
|
||||
statusUpdate?: (msg: string) => void,
|
||||
idler?: Idler,
|
||||
): Promise<PluginStatesExportState> => {
|
||||
const pluginsMap = new Map<string, PluginDefinition>([
|
||||
...clientPlugins.entries(),
|
||||
...devicePlugins.entries(),
|
||||
]);
|
||||
const pluginExportState: PluginStatesExportState = {};
|
||||
for (const key in pluginStates) {
|
||||
const pluginName = deconstructPluginKey(key).pluginName;
|
||||
statusUpdate && statusUpdate(`Serialising ${pluginName}...`);
|
||||
const serializationMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:serialization-per-plugin`;
|
||||
performance.mark(serializationMarker);
|
||||
const pluginClass = pluginName ? pluginsMap.get(pluginName) : null;
|
||||
if (isSandyPlugin(pluginClass)) {
|
||||
continue; // Those are already processed by `exportSandyPluginStates`
|
||||
} else if (pluginClass) {
|
||||
pluginExportState[key] = await pluginClass.serializePersistedState(
|
||||
pluginStates[key],
|
||||
statusUpdate,
|
||||
idler,
|
||||
pluginName,
|
||||
);
|
||||
getLogger().trackTimeSince(serializationMarker, serializationMarker, {
|
||||
plugin: pluginName,
|
||||
});
|
||||
}
|
||||
}
|
||||
return pluginExportState;
|
||||
};
|
||||
|
||||
async function exportSandyPluginStates(
|
||||
pluginsToProcess: PluginsToProcess,
|
||||
idler: Idler,
|
||||
@@ -258,8 +158,8 @@ async function exportSandyPluginStates(
|
||||
): Promise<SandyPluginStates> {
|
||||
const res: SandyPluginStates = {};
|
||||
for (const key in pluginsToProcess) {
|
||||
const {pluginId, client, pluginClass} = pluginsToProcess[key];
|
||||
if (isSandyPlugin(pluginClass) && client.sandyPluginStates.has(pluginId)) {
|
||||
const {pluginId, client} = pluginsToProcess[key];
|
||||
if (client.sandyPluginStates.has(pluginId)) {
|
||||
if (!res[client.id]) {
|
||||
res[client.id] = {};
|
||||
}
|
||||
@@ -276,33 +176,6 @@ async function exportSandyPluginStates(
|
||||
return res;
|
||||
}
|
||||
|
||||
const deserializePluginStates = (
|
||||
pluginStatesExportState: PluginStatesExportState,
|
||||
clientPlugins: ClientPluginMap,
|
||||
devicePlugins: DevicePluginMap,
|
||||
): PluginStatesState => {
|
||||
const pluginsMap = new Map<string, PluginDefinition>([
|
||||
...clientPlugins.entries(),
|
||||
...devicePlugins.entries(),
|
||||
]);
|
||||
const pluginsState: PluginStatesState = {};
|
||||
for (const key in pluginStatesExportState) {
|
||||
const pluginName = deconstructPluginKey(key).pluginName;
|
||||
if (!pluginName || !pluginsMap.get(pluginName)) {
|
||||
continue;
|
||||
}
|
||||
const pluginClass = pluginsMap.get(pluginName);
|
||||
if (isSandyPlugin(pluginClass)) {
|
||||
pluginsState[key] = pluginStatesExportState[key];
|
||||
} else if (pluginClass) {
|
||||
pluginsState[key] = pluginClass.deserializePersistedState(
|
||||
pluginStatesExportState[key],
|
||||
);
|
||||
}
|
||||
}
|
||||
return pluginsState;
|
||||
};
|
||||
|
||||
function replaceSerialsInKeys<T extends Record<string, any>>(
|
||||
collection: T,
|
||||
baseSerial: string,
|
||||
@@ -311,9 +184,7 @@ function replaceSerialsInKeys<T extends Record<string, any>>(
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in collection) {
|
||||
if (!key.includes(baseSerial)) {
|
||||
throw new Error(
|
||||
`Error while exporting, plugin state (${key}) does not have ${baseSerial} in its key`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
result[key.replace(baseSerial, newSerial)] = collection[key];
|
||||
}
|
||||
@@ -325,7 +196,6 @@ async function addSaltToDeviceSerial({
|
||||
device,
|
||||
deviceScreenshot,
|
||||
clients,
|
||||
pluginStates,
|
||||
pluginNotification,
|
||||
statusUpdate,
|
||||
pluginStates2,
|
||||
@@ -354,11 +224,6 @@ async function addSaltToDeviceSerial({
|
||||
statusUpdate(
|
||||
'Adding salt to the selected device id in the plugin states...',
|
||||
);
|
||||
const updatedPluginStates = replaceSerialsInKeys(
|
||||
pluginStates,
|
||||
serial,
|
||||
newSerial,
|
||||
);
|
||||
const updatedPluginStates2 = replaceSerialsInKeys(
|
||||
pluginStates2,
|
||||
serial,
|
||||
@@ -385,7 +250,6 @@ async function addSaltToDeviceSerial({
|
||||
device: {...newDevice.toJSON(), pluginStates: devicePluginStates},
|
||||
deviceScreenshot: deviceScreenshot,
|
||||
store: {
|
||||
pluginStates: updatedPluginStates,
|
||||
activeNotifications: updatedPluginNotifications,
|
||||
},
|
||||
pluginStates2: updatedPluginStates2,
|
||||
@@ -395,7 +259,6 @@ async function addSaltToDeviceSerial({
|
||||
type ProcessStoreOptions = {
|
||||
activeNotifications: Array<PluginNotification>;
|
||||
device: BaseDevice | null;
|
||||
pluginStates: PluginStatesState;
|
||||
pluginStates2: SandyPluginStates;
|
||||
clients: Array<ClientExport>;
|
||||
devicePlugins: DevicePluginMap;
|
||||
@@ -409,11 +272,9 @@ export async function processStore(
|
||||
{
|
||||
activeNotifications,
|
||||
device,
|
||||
pluginStates,
|
||||
pluginStates2,
|
||||
clients,
|
||||
devicePlugins,
|
||||
clientPlugins,
|
||||
salt,
|
||||
selectedPlugins,
|
||||
statusUpdate,
|
||||
@@ -435,14 +296,7 @@ export async function processStore(
|
||||
})
|
||||
: null;
|
||||
const processedClients = processClients(clients, serial, statusUpdate);
|
||||
const processedPluginStates = processPluginStates({
|
||||
clients: processedClients,
|
||||
serial,
|
||||
allPluginStates: pluginStates,
|
||||
devicePlugins,
|
||||
selectedPlugins,
|
||||
statusUpdate,
|
||||
});
|
||||
|
||||
const processedActiveNotifications = processNotificationStates({
|
||||
clients: processedClients,
|
||||
serial,
|
||||
@@ -451,14 +305,6 @@ export async function processStore(
|
||||
statusUpdate,
|
||||
});
|
||||
|
||||
const exportPluginState = await serializePluginStates(
|
||||
processedPluginStates,
|
||||
clientPlugins,
|
||||
devicePlugins,
|
||||
statusUpdate,
|
||||
idler,
|
||||
);
|
||||
|
||||
const devicePluginStates = await device.exportState(
|
||||
idler,
|
||||
statusUpdate,
|
||||
@@ -478,7 +324,6 @@ export async function processStore(
|
||||
device,
|
||||
deviceScreenshot: deviceScreenshotLink,
|
||||
clients: processedClients,
|
||||
pluginStates: exportPluginState,
|
||||
pluginNotification: processedActiveNotifications,
|
||||
statusUpdate,
|
||||
selectedPlugins,
|
||||
@@ -492,97 +337,17 @@ export async function processStore(
|
||||
throw new Error('Selected device is null, please select a device');
|
||||
}
|
||||
|
||||
export async function fetchMetadata(
|
||||
pluginsToProcess: PluginsToProcess,
|
||||
pluginStates: PluginStatesState,
|
||||
state: ReduxState,
|
||||
statusUpdate: (msg: string) => void,
|
||||
idler: Idler,
|
||||
): Promise<{
|
||||
pluginStates: PluginStatesState;
|
||||
errors: {[plugin: string]: Error} | null;
|
||||
}> {
|
||||
const newPluginState = {...pluginStates};
|
||||
let errorObject: {[plugin: string]: Error} | null = null;
|
||||
|
||||
for (const {
|
||||
pluginName,
|
||||
pluginId,
|
||||
pluginClass,
|
||||
client,
|
||||
pluginKey,
|
||||
} of pluginsToProcess) {
|
||||
const exportState =
|
||||
pluginClass && !isSandyPlugin(pluginClass)
|
||||
? pluginClass.exportPersistedState
|
||||
: null;
|
||||
if (exportState) {
|
||||
const fetchMetaDataMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:fetch-meta-data-per-plugin`;
|
||||
const isConnected = client.connected.get();
|
||||
performance.mark(fetchMetaDataMarker);
|
||||
try {
|
||||
statusUpdate &&
|
||||
statusUpdate(`Fetching metadata for plugin ${pluginName}...`);
|
||||
const data = await promiseTimeout(
|
||||
240000, // Fetching MobileConfig data takes ~ 3 mins, thus keeping timeout at 4 mins.
|
||||
exportState(
|
||||
isConnected ? callClient(client, pluginId) : undefined,
|
||||
newPluginState[pluginKey],
|
||||
state,
|
||||
idler,
|
||||
statusUpdate,
|
||||
isConnected
|
||||
? supportsMethod(client, pluginId)
|
||||
: () => Promise.resolve(false),
|
||||
),
|
||||
`Timed out while collecting data for ${pluginName}`,
|
||||
);
|
||||
if (!data) {
|
||||
throw new Error(
|
||||
`Metadata returned by the ${pluginName} is undefined`,
|
||||
);
|
||||
}
|
||||
getLogger().trackTimeSince(fetchMetaDataMarker, fetchMetaDataMarker, {
|
||||
pluginId,
|
||||
});
|
||||
newPluginState[pluginKey] = data;
|
||||
} catch (e) {
|
||||
if (!errorObject) {
|
||||
errorObject = {};
|
||||
}
|
||||
errorObject[pluginName] = e;
|
||||
getLogger().trackTimeSince(fetchMetaDataMarker, fetchMetaDataMarker, {
|
||||
pluginId,
|
||||
error: e,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {pluginStates: newPluginState, errors: errorObject};
|
||||
}
|
||||
|
||||
async function processQueues(
|
||||
store: MiddlewareAPI,
|
||||
pluginsToProcess: PluginsToProcess,
|
||||
statusUpdate?: (msg: string) => void,
|
||||
idler?: Idler,
|
||||
) {
|
||||
for (const {
|
||||
pluginName,
|
||||
pluginId,
|
||||
pluginKey,
|
||||
pluginClass,
|
||||
client,
|
||||
} of pluginsToProcess) {
|
||||
if (isSandyPlugin(pluginClass) || pluginClass.persistedStateReducer) {
|
||||
for (const {pluginName, pluginId, pluginKey, client} of pluginsToProcess) {
|
||||
client.flushMessageBuffer();
|
||||
const processQueueMarker = `${EXPORT_FLIPPER_TRACE_EVENT}:process-queue-per-plugin`;
|
||||
performance.mark(processQueueMarker);
|
||||
const plugin = isSandyPlugin(pluginClass)
|
||||
? client.sandyPluginStates.get(pluginId)
|
||||
: pluginClass;
|
||||
const plugin = client.sandyPluginStates.get(pluginId);
|
||||
if (!plugin) continue;
|
||||
await processMessageQueue(
|
||||
plugin,
|
||||
@@ -603,7 +368,6 @@ async function processQueues(
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function determinePluginsToProcess(
|
||||
clients: Array<Client>,
|
||||
@@ -638,7 +402,6 @@ export function determinePluginsToProcess(
|
||||
client,
|
||||
pluginId: plugin,
|
||||
pluginName: getPluginTitle(pluginClass),
|
||||
pluginClass,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -671,14 +434,6 @@ async function getStoreExport(
|
||||
performance.mark(fetchMetaDataMarker);
|
||||
|
||||
const client = clients.find((client) => client.id === selectedApp);
|
||||
const metadata = await fetchMetadata(
|
||||
pluginsToProcess,
|
||||
state.pluginStates,
|
||||
state,
|
||||
statusUpdate,
|
||||
idler,
|
||||
);
|
||||
const newPluginState = metadata.pluginStates;
|
||||
|
||||
const pluginStates2 = pluginsToProcess
|
||||
? await exportSandyPluginStates(pluginsToProcess, idler, statusUpdate)
|
||||
@@ -687,7 +442,6 @@ async function getStoreExport(
|
||||
getLogger().trackTimeSince(fetchMetaDataMarker, fetchMetaDataMarker, {
|
||||
plugins: state.plugins.selectedPlugins,
|
||||
});
|
||||
const {errors} = metadata;
|
||||
|
||||
const {activeNotifications} = state.notifications;
|
||||
const {devicePlugins, clientPlugins} = state.plugins;
|
||||
@@ -695,7 +449,6 @@ async function getStoreExport(
|
||||
{
|
||||
activeNotifications,
|
||||
device: selectedDevice,
|
||||
pluginStates: newPluginState,
|
||||
pluginStates2,
|
||||
clients: client ? [client.toJSON()] : [],
|
||||
devicePlugins,
|
||||
@@ -706,7 +459,7 @@ async function getStoreExport(
|
||||
},
|
||||
idler,
|
||||
);
|
||||
return {exportData, fetchMetaDataErrors: errors};
|
||||
return {exportData, fetchMetaDataErrors: null};
|
||||
}
|
||||
|
||||
export async function exportStore(
|
||||
@@ -810,34 +563,9 @@ export function importDataToStore(source: string, data: string, store: Store) {
|
||||
payload: archivedDevice,
|
||||
});
|
||||
|
||||
const {pluginStates} = json.store;
|
||||
const processedPluginStates: PluginStatesState = deserializePluginStates(
|
||||
pluginStates,
|
||||
store.getState().plugins.clientPlugins,
|
||||
store.getState().plugins.devicePlugins,
|
||||
);
|
||||
const keys = Object.keys(processedPluginStates);
|
||||
keys.forEach((key) => {
|
||||
store.dispatch({
|
||||
type: 'SET_PLUGIN_STATE',
|
||||
payload: {
|
||||
pluginKey: key,
|
||||
state: processedPluginStates[key],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
clients.forEach((client: {id: string; query: ClientQuery}) => {
|
||||
const sandyPluginStates = json.pluginStates2[client.id] || {};
|
||||
const clientPlugins: Set<string> = new Set([
|
||||
...keys
|
||||
.filter((key) => {
|
||||
const plugin = deconstructPluginKey(key);
|
||||
return plugin.type === 'client' && client.id === plugin.client;
|
||||
})
|
||||
.map((pluginKey) => deconstructPluginKey(pluginKey).pluginName),
|
||||
...Object.keys(sandyPluginStates),
|
||||
]);
|
||||
const clientPlugins = new Set(Object.keys(sandyPluginStates));
|
||||
store.dispatch({
|
||||
type: 'NEW_CLIENT',
|
||||
payload: new Client(
|
||||
|
||||
@@ -7,9 +7,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {PersistedStateReducer, FlipperDevicePlugin} from '../plugin';
|
||||
import type {State, MiddlewareAPI} from '../reducers/index';
|
||||
import {setPluginState} from '../reducers/pluginStates';
|
||||
import {FlipperDevicePlugin} from '../plugin';
|
||||
import type {MiddlewareAPI} from '../reducers/index';
|
||||
import {
|
||||
clearMessageQueue,
|
||||
queueMessages,
|
||||
@@ -23,31 +22,7 @@ import {defaultEnabledBackgroundPlugins} from './pluginUtils';
|
||||
import {batch, Idler, _SandyPluginInstance} from 'flipper-plugin';
|
||||
import {addBackgroundStat} from './pluginStats';
|
||||
|
||||
function processMessageClassic(
|
||||
state: State,
|
||||
pluginKey: string,
|
||||
plugin: {
|
||||
id: string;
|
||||
persistedStateReducer: PersistedStateReducer | null;
|
||||
},
|
||||
message: Message,
|
||||
): State {
|
||||
const reducerStartTime = Date.now();
|
||||
try {
|
||||
const newPluginState = plugin.persistedStateReducer!(
|
||||
state,
|
||||
message.method,
|
||||
message.params,
|
||||
);
|
||||
addBackgroundStat(plugin.id, Date.now() - reducerStartTime);
|
||||
return newPluginState;
|
||||
} catch (e) {
|
||||
console.error(`Failed to process event for plugin ${plugin.id}`, e);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function processMessagesSandy(
|
||||
function processMessagesImmediately(
|
||||
plugin: _SandyPluginInstance,
|
||||
messages: Message[],
|
||||
) {
|
||||
@@ -63,60 +38,20 @@ function processMessagesSandy(
|
||||
}
|
||||
}
|
||||
|
||||
export function processMessagesImmediately(
|
||||
store: MiddlewareAPI,
|
||||
pluginKey: string,
|
||||
plugin:
|
||||
| {
|
||||
defaultPersistedState: any;
|
||||
id: string;
|
||||
persistedStateReducer: PersistedStateReducer | null;
|
||||
}
|
||||
| _SandyPluginInstance,
|
||||
messages: Message[],
|
||||
) {
|
||||
if (plugin instanceof _SandyPluginInstance) {
|
||||
processMessagesSandy(plugin, messages);
|
||||
} else {
|
||||
const persistedState = getCurrentPluginState(store, plugin, pluginKey);
|
||||
const newPluginState = messages.reduce(
|
||||
(state, message) =>
|
||||
processMessageClassic(state, pluginKey, plugin, message),
|
||||
persistedState,
|
||||
);
|
||||
if (persistedState !== newPluginState) {
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey,
|
||||
state: newPluginState,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function processMessagesLater(
|
||||
store: MiddlewareAPI,
|
||||
pluginKey: string,
|
||||
plugin:
|
||||
| {
|
||||
defaultPersistedState: any;
|
||||
id: string;
|
||||
persistedStateReducer: PersistedStateReducer | null;
|
||||
maxQueueSize?: number;
|
||||
}
|
||||
| _SandyPluginInstance,
|
||||
plugin: _SandyPluginInstance,
|
||||
messages: Message[],
|
||||
) {
|
||||
const pluginId =
|
||||
plugin instanceof _SandyPluginInstance ? plugin.definition.id : plugin.id;
|
||||
const pluginId = plugin.definition.id;
|
||||
const isSelected =
|
||||
pluginKey === getSelectedPluginKey(store.getState().connections);
|
||||
switch (true) {
|
||||
// Navigation events are always processed immediately, to make sure the navbar stays up to date, see also T69991064
|
||||
case pluginId === 'Navigation':
|
||||
case isSelected && getPendingMessages(store, pluginKey).length === 0:
|
||||
processMessagesImmediately(store, pluginKey, plugin, messages);
|
||||
processMessagesImmediately(plugin, messages);
|
||||
break;
|
||||
case isSelected:
|
||||
case plugin instanceof _SandyPluginInstance:
|
||||
@@ -129,13 +64,7 @@ export function processMessagesLater(
|
||||
pluginId,
|
||||
):
|
||||
store.dispatch(
|
||||
queueMessages(
|
||||
pluginKey,
|
||||
messages,
|
||||
plugin instanceof _SandyPluginInstance
|
||||
? DEFAULT_MAX_QUEUE_SIZE
|
||||
: plugin.maxQueueSize,
|
||||
),
|
||||
queueMessages(pluginKey, messages, DEFAULT_MAX_QUEUE_SIZE),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@@ -148,21 +77,12 @@ export function processMessagesLater(
|
||||
}
|
||||
|
||||
export async function processMessageQueue(
|
||||
plugin:
|
||||
| {
|
||||
defaultPersistedState: any;
|
||||
id: string;
|
||||
persistedStateReducer: PersistedStateReducer | null;
|
||||
}
|
||||
| _SandyPluginInstance,
|
||||
plugin: _SandyPluginInstance,
|
||||
pluginKey: string,
|
||||
store: MiddlewareAPI,
|
||||
progressCallback?: (progress: {current: number; total: number}) => void,
|
||||
idler: Idler = new IdlerImpl(),
|
||||
): Promise<boolean> {
|
||||
if (!_SandyPluginInstance.is(plugin) && !plugin.persistedStateReducer) {
|
||||
return true;
|
||||
}
|
||||
const total = getPendingMessages(store, pluginKey).length;
|
||||
let progress = 0;
|
||||
do {
|
||||
@@ -171,25 +91,11 @@ export async function processMessageQueue(
|
||||
break;
|
||||
}
|
||||
// there are messages to process! lets do so until we have to idle
|
||||
// persistedState is irrelevant for SandyPlugins, as they store state locally
|
||||
const persistedState = _SandyPluginInstance.is(plugin)
|
||||
? undefined
|
||||
: getCurrentPluginState(store, plugin, pluginKey);
|
||||
let offset = 0;
|
||||
let newPluginState = persistedState;
|
||||
batch(() => {
|
||||
do {
|
||||
if (_SandyPluginInstance.is(plugin)) {
|
||||
// Optimization: we could send a batch of messages here
|
||||
processMessagesSandy(plugin, [messages[offset]]);
|
||||
} else {
|
||||
newPluginState = processMessageClassic(
|
||||
newPluginState,
|
||||
pluginKey,
|
||||
plugin,
|
||||
messages[offset],
|
||||
);
|
||||
}
|
||||
processMessagesImmediately(plugin, [messages[offset]]);
|
||||
offset++;
|
||||
progress++;
|
||||
|
||||
@@ -203,17 +109,6 @@ export async function processMessageQueue(
|
||||
// resistent to kicking off this process twice; grabbing, processing messages, saving state is done synchronosly
|
||||
// until the idler has to break
|
||||
store.dispatch(clearMessageQueue(pluginKey, offset));
|
||||
if (
|
||||
!_SandyPluginInstance.is(plugin) &&
|
||||
newPluginState !== persistedState
|
||||
) {
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey,
|
||||
state: newPluginState,
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (idler.isCancelled()) {
|
||||
@@ -232,15 +127,3 @@ function getPendingMessages(
|
||||
): Message[] {
|
||||
return store.getState().pluginMessageQueue[pluginKey] || [];
|
||||
}
|
||||
|
||||
function getCurrentPluginState(
|
||||
store: MiddlewareAPI,
|
||||
plugin: {defaultPersistedState: any},
|
||||
pluginKey: string,
|
||||
) {
|
||||
// possible optimization: don't spread default state here by put proper default state when initializing clients
|
||||
return {
|
||||
...plugin.defaultPersistedState,
|
||||
...store.getState().pluginStates[pluginKey],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,17 +7,9 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
FlipperDevicePlugin,
|
||||
FlipperBasePlugin,
|
||||
PluginDefinition,
|
||||
DevicePluginDefinition,
|
||||
ClientPluginDefinition,
|
||||
} from '../plugin';
|
||||
import {PluginDefinition} from '../plugin';
|
||||
import type {State} from '../reducers';
|
||||
import type {State as PluginStatesState} from '../reducers/pluginStates';
|
||||
import type {State as PluginsState} from '../reducers/plugins';
|
||||
import {_SandyPluginDefinition} from 'flipper-plugin';
|
||||
import type BaseDevice from '../devices/BaseDevice';
|
||||
import type Client from '../Client';
|
||||
import type {
|
||||
@@ -29,9 +21,9 @@ import type {
|
||||
import {getLatestCompatibleVersionOfEachPlugin} from '../dispatcher/plugins';
|
||||
|
||||
export type PluginLists = {
|
||||
devicePlugins: DevicePluginDefinition[];
|
||||
metroPlugins: DevicePluginDefinition[];
|
||||
enabledPlugins: ClientPluginDefinition[];
|
||||
devicePlugins: PluginDefinition[];
|
||||
metroPlugins: PluginDefinition[];
|
||||
enabledPlugins: PluginDefinition[];
|
||||
disabledPlugins: PluginDefinition[];
|
||||
unavailablePlugins: [plugin: PluginDetails, reason: string][];
|
||||
downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[];
|
||||
@@ -86,33 +78,12 @@ export function getPluginKey(
|
||||
return `unknown#${pluginID}`;
|
||||
}
|
||||
|
||||
export function isSandyPlugin(
|
||||
plugin?: PluginDefinition | null,
|
||||
): plugin is _SandyPluginDefinition {
|
||||
return plugin instanceof _SandyPluginDefinition;
|
||||
}
|
||||
|
||||
export function getPersistedState<PersistedState>(
|
||||
pluginKey: string,
|
||||
persistingPlugin: typeof FlipperBasePlugin | null,
|
||||
pluginStates: PluginStatesState,
|
||||
): PersistedState | null {
|
||||
if (!persistingPlugin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const persistedState: PersistedState = {
|
||||
...persistingPlugin.defaultPersistedState,
|
||||
...pluginStates[pluginKey],
|
||||
export const pluginKey = (serial: string, pluginName: string): string => {
|
||||
return `${serial}#${pluginName}`;
|
||||
};
|
||||
return persistedState;
|
||||
}
|
||||
|
||||
export function computeExportablePlugins(
|
||||
state: Pick<
|
||||
State,
|
||||
'plugins' | 'connections' | 'pluginStates' | 'pluginMessageQueue'
|
||||
>,
|
||||
state: Pick<State, 'plugins' | 'connections' | 'pluginMessageQueue'>,
|
||||
device: BaseDevice | null,
|
||||
client: Client | null,
|
||||
availablePlugins: PluginLists,
|
||||
@@ -131,25 +102,14 @@ export function computeExportablePlugins(
|
||||
}
|
||||
|
||||
function isExportablePlugin(
|
||||
{
|
||||
pluginStates,
|
||||
pluginMessageQueue,
|
||||
}: Pick<State, 'pluginStates' | 'pluginMessageQueue'>,
|
||||
{pluginMessageQueue}: Pick<State, 'pluginMessageQueue'>,
|
||||
device: BaseDevice | null,
|
||||
client: Client | null,
|
||||
plugin: PluginDefinition,
|
||||
): boolean {
|
||||
// can generate an export when requested
|
||||
if (!isSandyPlugin(plugin) && plugin.exportPersistedState) {
|
||||
return true;
|
||||
}
|
||||
const pluginKey = isDevicePluginDefinition(plugin)
|
||||
? getPluginKey(undefined, device, plugin.id)
|
||||
: getPluginKey(client?.id, undefined, plugin.id);
|
||||
// plugin has exportable redux state
|
||||
if (pluginStates[pluginKey]) {
|
||||
return true;
|
||||
}
|
||||
// plugin has exportable sandy state
|
||||
if (client?.sandyPluginStates.get(plugin.id)?.isPersistable()) {
|
||||
return true;
|
||||
@@ -158,10 +118,7 @@ function isExportablePlugin(
|
||||
return true;
|
||||
}
|
||||
// plugin has pending messages and a persisted state reducer or isSandy
|
||||
if (
|
||||
pluginMessageQueue[pluginKey] &&
|
||||
((plugin as any).defaultPersistedState || isSandyPlugin(plugin))
|
||||
) {
|
||||
if (pluginMessageQueue[pluginKey]) {
|
||||
return true;
|
||||
}
|
||||
// nothing to serialize
|
||||
@@ -201,11 +158,8 @@ export function isDevicePlugin(activePlugin: ActivePluginListItem) {
|
||||
|
||||
export function isDevicePluginDefinition(
|
||||
definition: PluginDefinition,
|
||||
): definition is DevicePluginDefinition {
|
||||
return (
|
||||
(definition as any).prototype instanceof FlipperDevicePlugin ||
|
||||
(definition instanceof _SandyPluginDefinition && definition.isDevicePlugin)
|
||||
);
|
||||
): boolean {
|
||||
return definition.isDevicePlugin;
|
||||
}
|
||||
|
||||
export function getPluginTooltip(details: PluginDetails): string {
|
||||
@@ -234,9 +188,9 @@ export function computePluginLists(
|
||||
metroDevice: BaseDevice | null,
|
||||
client: Client | null,
|
||||
): {
|
||||
devicePlugins: DevicePluginDefinition[];
|
||||
metroPlugins: DevicePluginDefinition[];
|
||||
enabledPlugins: ClientPluginDefinition[];
|
||||
devicePlugins: PluginDefinition[];
|
||||
metroPlugins: PluginDefinition[];
|
||||
enabledPlugins: PluginDefinition[];
|
||||
disabledPlugins: PluginDefinition[];
|
||||
unavailablePlugins: [plugin: PluginDetails, reason: string][];
|
||||
downloadablePlugins: (DownloadablePluginDetails | BundledPluginDetails)[];
|
||||
@@ -247,17 +201,13 @@ export function computePluginLists(
|
||||
...plugins.bundledPlugins.values(),
|
||||
...plugins.marketplacePlugins,
|
||||
]).filter((p) => !plugins.loadedPlugins.has(p.id));
|
||||
const devicePlugins: DevicePluginDefinition[] = [
|
||||
...plugins.devicePlugins.values(),
|
||||
]
|
||||
const devicePlugins: PluginDefinition[] = [...plugins.devicePlugins.values()]
|
||||
.filter((p) => device?.supportsPlugin(p))
|
||||
.filter((p) => enabledDevicePluginsState.has(p.id));
|
||||
const metroPlugins: DevicePluginDefinition[] = [
|
||||
...plugins.devicePlugins.values(),
|
||||
]
|
||||
const metroPlugins: PluginDefinition[] = [...plugins.devicePlugins.values()]
|
||||
.filter((p) => metroDevice?.supportsPlugin(p))
|
||||
.filter((p) => enabledDevicePluginsState.has(p.id));
|
||||
const enabledPlugins: ClientPluginDefinition[] = [];
|
||||
const enabledPlugins: PluginDefinition[] = [];
|
||||
const disabledPlugins: PluginDefinition[] = [
|
||||
...plugins.devicePlugins.values(),
|
||||
]
|
||||
|
||||
@@ -49,6 +49,6 @@ export function getFlipperLibImplementation(): FlipperLib {
|
||||
return flipperLibInstance;
|
||||
}
|
||||
|
||||
export function setFlipperLibImplementation(impl: FlipperLib) {
|
||||
export function setFlipperLibImplementation(impl: FlipperLib | undefined) {
|
||||
flipperLibInstance = impl;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import styled from '@emotion/styled';
|
||||
import React, {MouseEvent, KeyboardEvent} from 'react';
|
||||
import {theme} from '../theme';
|
||||
import {Layout} from '../Layout';
|
||||
import {tryGetFlipperLibImplementation} from 'flipper-plugin/src/plugin/FlipperLib';
|
||||
import {_getFlipperLibImplementation} from 'flipper-plugin';
|
||||
import {DownOutlined, RightOutlined} from '@ant-design/icons';
|
||||
|
||||
const {Text} = Typography;
|
||||
@@ -221,7 +221,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
|
||||
{
|
||||
label: 'Copy',
|
||||
click: () => {
|
||||
tryGetFlipperLibImplementation()?.writeTextToClipboard(
|
||||
_getFlipperLibImplementation()?.writeTextToClipboard(
|
||||
props.onCopyExpandedTree(props.element, 0),
|
||||
);
|
||||
},
|
||||
@@ -229,7 +229,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
|
||||
{
|
||||
label: 'Copy expanded child elements',
|
||||
click: () =>
|
||||
tryGetFlipperLibImplementation()?.writeTextToClipboard(
|
||||
_getFlipperLibImplementation()?.writeTextToClipboard(
|
||||
props.onCopyExpandedTree(props.element, 255),
|
||||
),
|
||||
},
|
||||
@@ -253,7 +253,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
|
||||
return {
|
||||
label: `Copy ${o.name}`,
|
||||
click: () => {
|
||||
tryGetFlipperLibImplementation()?.writeTextToClipboard(o.value);
|
||||
_getFlipperLibImplementation()?.writeTextToClipboard(o.value);
|
||||
},
|
||||
};
|
||||
}),
|
||||
@@ -555,7 +555,7 @@ export class Elements extends PureComponent<ElementsProps, ElementsState> {
|
||||
) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
tryGetFlipperLibImplementation()?.writeTextToClipboard(
|
||||
_getFlipperLibImplementation()?.writeTextToClipboard(
|
||||
selectedElement.name,
|
||||
);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user