diff --git a/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap index 8c290e6d2..98840bc61 100644 --- a/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap +++ b/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -21,6 +21,7 @@ Object { "deviceType": "physical", "logs": Array [], "os": "Android", + "pluginStates": Object {}, "serial": "serial", "title": "MockAndroidDevice", }, @@ -30,6 +31,7 @@ Object { "deviceType": "physical", "logs": Array [], "os": "Android", + "pluginStates": Object {}, "serial": "serial", "title": "MockAndroidDevice", }, diff --git a/desktop/app/src/devices/BaseDevice.tsx b/desktop/app/src/devices/BaseDevice.tsx index 6ef409d17..e76bee6b8 100644 --- a/desktop/app/src/devices/BaseDevice.tsx +++ b/desktop/app/src/devices/BaseDevice.tsx @@ -33,6 +33,7 @@ export type DeviceExport = { deviceType: DeviceType; serial: string; logs: Array; + pluginStates: Record; }; export default class BaseDevice { @@ -91,12 +92,21 @@ export default class BaseDevice { } toJSON(): DeviceExport { + const pluginStates: Record = {}; + + for (const instance of this.sandyPluginStates.values()) { + if (instance.isPersistable()) { + pluginStates[instance.definition.id] = instance.exportState(); + } + } + return { os: this.os, title: this.title, deviceType: this.deviceType, serial: this.serial, logs: this.getLogs(), + pluginStates, }; } @@ -175,17 +185,20 @@ export default class BaseDevice { return null; } - loadDevicePlugins(devicePlugins?: DevicePluginMap) { + loadDevicePlugins( + devicePlugins?: DevicePluginMap, + pluginStates?: Record, + ) { if (!devicePlugins) { return; } const plugins = Array.from(devicePlugins.values()); for (const plugin of plugins) { - this.loadDevicePlugin(plugin); + this.loadDevicePlugin(plugin, pluginStates?.[plugin.id]); } } - loadDevicePlugin(plugin: DevicePluginDefinition) { + loadDevicePlugin(plugin: DevicePluginDefinition, initialState?: any) { if (plugin instanceof _SandyPluginDefinition) { if (plugin.asDevicePluginModule().supportsDevice(this as any)) { this.devicePlugins.push(plugin.id); @@ -195,8 +208,9 @@ export default class BaseDevice { getFlipperLibImplementation(), plugin, this, + initialState, ), - ); // TODO T70582933: pass initial state if applicable + ); } } else { if (plugin.supportsDevice(this)) { diff --git a/desktop/app/src/utils/__tests__/exportData.node.tsx b/desktop/app/src/utils/__tests__/exportData.node.tsx index f829cd108..c1199e0ca 100644 --- a/desktop/app/src/utils/__tests__/exportData.node.tsx +++ b/desktop/app/src/utils/__tests__/exportData.node.tsx @@ -27,6 +27,7 @@ import { _SandyPluginDefinition, createState, PluginClient, + DevicePluginClient, } from 'flipper-plugin'; import {selectPlugin} from '../../reducers/connections'; @@ -1208,3 +1209,97 @@ test('Sandy plugins are imported properly', async () => { } `); }); + +const sandyDeviceTestPlugin = new _SandyPluginDefinition( + TestUtils.createMockPluginDetails(), + { + supportsDevice: () => true, + devicePlugin(client: DevicePluginClient) { + const counter = createState(0, {persist: 'counter'}); + const _somethingElse = createState(0); + const anotherState = createState({testCount: 0}, {persist: 'otherState'}); + + client.device.onLogEntry(() => { + counter.set(counter.get() + 1); + anotherState.update((draft) => { + draft.testCount -= 1; + }); + }); + return { + counter, + }; + }, + Component() { + return null; + }, + }, +); + +test('Sandy device plugins are exported / imported properly', async () => { + const data = { + clients: [], + device: { + deviceType: 'archivedPhysical', + logs: [], + os: 'Android', + serial: '2e52cea6-94b0-4ea1-b9a8-c9135ede14ca-serial', + title: 'MockAndroidDevice', + pluginStates: { + [sandyDeviceTestPlugin.id]: { + otherState: { + testCount: -3, + }, + counter: 3, + }, + }, + }, + deviceScreenshot: null, + fileVersion: '0.9.99', + flipperReleaseRevision: undefined, + pluginStates2: {}, + store: { + activeNotifications: [], + pluginStates: {}, + }, + }; + + const {device, store} = await renderMockFlipperWithPlugin( + sandyDeviceTestPlugin, + ); + + await importDataToStore('unittest.json', JSON.stringify(data), store); + + const device2 = store.getState().connections.devices[1]; + expect(device2).not.toBeFalsy(); + expect(device2).not.toBe(device); + expect(device2.devicePlugins).toEqual([TestPlugin.id]); + + const {counter} = device2.sandyPluginStates.get(TestPlugin.id)?.instanceApi; + counter.set(counter.get() + 1); + + expect(device.toJSON().pluginStates[TestPlugin.id]).toMatchInlineSnapshot(` + Object { + "counter": 0, + "otherState": Object { + "testCount": 0, + }, + } + `); + expect(device2.toJSON()).toMatchInlineSnapshot(` + Object { + "deviceType": "archivedPhysical", + "logs": Array [], + "os": "Android", + "pluginStates": Object { + "TestPlugin": Object { + "counter": 4, + "otherState": Object { + "testCount": -3, + }, + }, + }, + "serial": "2e52cea6-94b0-4ea1-b9a8-c9135ede14ca-serial", + "title": "MockAndroidDevice", + } + `); +}); diff --git a/desktop/app/src/utils/exportData.tsx b/desktop/app/src/utils/exportData.tsx index 95f592e45..a35b34c2e 100644 --- a/desktop/app/src/utils/exportData.tsx +++ b/desktop/app/src/utils/exportData.tsx @@ -791,7 +791,10 @@ export function importDataToStore(source: string, data: string, store: Store) { }); return; } - archivedDevice.loadDevicePlugins(store.getState().plugins.devicePlugins); + archivedDevice.loadDevicePlugins( + store.getState().plugins.devicePlugins, + device.pluginStates, + ); store.dispatch({ type: 'REGISTER_DEVICE', payload: archivedDevice,