diff --git a/src/PluginContainer.tsx b/src/PluginContainer.tsx index 14418475e..026de022f 100644 --- a/src/PluginContainer.tsx +++ b/src/PluginContainer.tsx @@ -417,7 +417,8 @@ export default connect( if (activePlugin && target) { pluginKey = getPluginKey(target.id, activePlugin.id); pluginIsEnabled = pluginIsStarred( - {selectedApp, userStarredPlugins}, + userStarredPlugins, + selectedApp, activePlugin.id, ); } diff --git a/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap index 954a5a4a4..7af97cf04 100644 --- a/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap +++ b/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -5,10 +5,10 @@ Object { "androidEmulators": Array [], "clients": Array [ Object { - "id": "TestApp#Android#unit_test#serial", + "id": "TestApp#Android#MockAndroidDevice#serial", "query": Object { "app": "TestApp", - "device": "unit_test", + "device": "MockAndroidDevice", "device_id": "serial", "os": "Android", }, @@ -25,7 +25,7 @@ Object { }, ], "errors": Array [], - "selectedApp": "TestApp#Android#unit_test#serial", + "selectedApp": "TestApp#Android#MockAndroidDevice#serial", "selectedDevice": Object { "deviceType": "physical", "logs": Array [], @@ -36,7 +36,7 @@ Object { "selectedPlugin": "TestPlugin", "staticView": null, "uninitializedClients": Array [], - "userPreferredApp": "TestApp#Android#unit_test#serial", + "userPreferredApp": "TestApp#Android#MockAndroidDevice#serial", "userPreferredDevice": "MockAndroidDevice", "userPreferredPlugin": "TestPlugin", "userStarredPlugins": Object { diff --git a/src/__tests__/createMockFlipperWithPlugin.node.tsx b/src/__tests__/createMockFlipperWithPlugin.node.tsx index 00de2968c..f5bdd5157 100644 --- a/src/__tests__/createMockFlipperWithPlugin.node.tsx +++ b/src/__tests__/createMockFlipperWithPlugin.node.tsx @@ -53,7 +53,7 @@ test('can create a Fake flipper', async () => { sendMessage('inc', {}); expect(store.getState().pluginStates).toMatchInlineSnapshot(` Object { - "TestApp#Android#unit_test#serial#TestPlugin": Object { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { "count": 1, }, } diff --git a/src/reducers/connections.tsx b/src/reducers/connections.tsx index dbe77472d..6aed84e9f 100644 --- a/src/reducers/connections.tsx +++ b/src/reducers/connections.tsx @@ -621,17 +621,14 @@ export function getSelectedPluginKey(state: State): string | undefined { } export function pluginIsStarred( - state: { - selectedApp: string | null; - userStarredPlugins: State['userStarredPlugins']; - }, + userStarredPlugins: State['userStarredPlugins'], + app: string | null, pluginId: string, ): boolean { - const {selectedApp} = state; - if (!selectedApp) { + if (!app) { return false; } - const appInfo = deconstructClientId(selectedApp); - const starred = state.userStarredPlugins[appInfo.app]; + const appInfo = deconstructClientId(app); + const starred = userStarredPlugins[appInfo.app]; return starred && starred.indexOf(pluginId) > -1; } diff --git a/src/test-utils/createMockFlipperWithPlugin.tsx b/src/test-utils/createMockFlipperWithPlugin.tsx index fd756bc59..28bbc069e 100644 --- a/src/test-utils/createMockFlipperWithPlugin.tsx +++ b/src/test-utils/createMockFlipperWithPlugin.tsx @@ -40,63 +40,72 @@ export async function createMockFlipperWithPlugin( device: BaseDevice; store: Store; sendMessage(method: string, params: any): void; + createDevice(serial: string): BaseDevice; + createClient(device: BaseDevice, name: string): Client; }) => Promise, ) { const store = createStore(reducers); - const device = new BaseDevice( - 'serial', - 'physical', - 'MockAndroidDevice', - 'Android', - ); const logger = createStubLogger(); - store.dispatch(registerPlugins([pluginClazz])); - store.dispatch({ - type: 'REGISTER_DEVICE', - payload: device, - }); + function createDevice(serial: string): BaseDevice { + const device = new BaseDevice( + serial, + 'physical', + 'MockAndroidDevice', + 'Android', + ); + store.dispatch({ + type: 'REGISTER_DEVICE', + payload: device, + }); + return device; + } - const query: ClientQuery = { - app: 'TestApp', - os: 'Android', - device: 'unit_test', - device_id: device.serial, - }; - const id = buildClientId({ - app: query.app, - os: query.os, - device: query.device, - device_id: query.device_id, - }); + function createClient(device: BaseDevice, name: string): Client { + const query: ClientQuery = { + app: name, + os: 'Android', + device: device.title, + device_id: device.serial, + }; + const id = buildClientId({ + app: query.app, + os: query.os, + device: query.device, + device_id: query.device_id, + }); - const client = new Client( - id, - query, - null, // create a stub connection to avoid this plugin to be archived? - logger, - store, - [pluginClazz.id], - device, - ); + const client = new Client( + id, + query, + null, // create a stub connection to avoid this plugin to be archived? + logger, + store, + [pluginClazz.id], + device, + ); - // yikes - client._deviceSet = device; - client.device = { - then() { - return device; - }, - } as any; + // yikes + client._deviceSet = device; + client.device = { + then() { + return device; + }, + } as any; - // As convenience, by default we select the new client, star the plugin, and select it - store.dispatch({ - type: 'NEW_CLIENT', - payload: client, - }); + // As convenience, by default we select the new client, star the plugin, and select it + store.dispatch({ + type: 'NEW_CLIENT', + payload: client, + }); + return client; + } + + const device = createDevice('serial'); + const client = createClient(device, 'TestApp'); store.dispatch(selectDevice(device)); - store.dispatch(selectClient(client.id)); store.dispatch( @@ -131,5 +140,7 @@ export async function createMockFlipperWithPlugin( }), ); }, + createDevice, + createClient, }); } diff --git a/src/utils/__tests__/messageQueue.node.tsx b/src/utils/__tests__/messageQueue.node.tsx index 0e04c2613..4cf960594 100644 --- a/src/utils/__tests__/messageQueue.node.tsx +++ b/src/utils/__tests__/messageQueue.node.tsx @@ -9,8 +9,13 @@ import {FlipperPlugin} from '../../plugin'; import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin'; -import {GK, Store, Client} from 'flipper'; -import {selectPlugin, starPlugin} from '../../reducers/connections'; +import {GK, Store, Client} from '../../'; +import { + selectPlugin, + starPlugin, + selectClient, + selectDevice, +} from '../../reducers/connections'; import {processMessageQueue} from '../messageQueue'; import {getPluginKey} from '../pluginUtils'; import {TestIdler} from '../Idler'; @@ -87,7 +92,7 @@ test('will process event with GK disabled', async () => { sendMessage('inc', {}); expect(store.getState().pluginStates).toMatchInlineSnapshot(` Object { - "TestApp#Android#unit_test#serial#TestPlugin": Object { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { "count": 1, }, } @@ -104,12 +109,12 @@ test('queue - events are processed immediately if plugin is selected', async () expect(store.getState().connections.selectedPlugin).toBe('TestPlugin'); sendMessage('inc', {}); expect(store.getState().pluginStates).toMatchInlineSnapshot(` - Object { - "TestApp#Android#unit_test#serial#TestPlugin": Object { - "count": 1, - }, - } - `); + Object { + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object { + "count": 1, + }, + } + `); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( `Object {}`, ); @@ -135,7 +140,7 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu ); expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` Object { - "TestApp#Android#unit_test#serial#TestPlugin": Array [ + "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [ Object { "method": "inc", "params": Object {}, @@ -193,6 +198,84 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu ); }); +test('queue - events are queued for plugins that are favorite when app is not selected', async () => { + await createMockFlipperWithPlugin( + TestPlugin, + async ({device, store, sendMessage, createClient}) => { + await GK.withWhitelistedGK('flipper_event_queue', async () => { + selectDeviceLogs(store); + expect(store.getState().connections.selectedPlugin).not.toBe( + 'TestPlugin', + ); + + const client2 = 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 { + "method": "inc", + "params": Object { + "delta": 2, + }, + }, + ], + } + `, + ); + }); + }, + ); +}); + +test('queue - events are queued for plugins that are favorite when app is selected on different device', async () => { + await createMockFlipperWithPlugin( + TestPlugin, + async ({client, store, sendMessage, createDevice, createClient}) => { + await GK.withWhitelistedGK('flipper_event_queue', async () => { + selectDeviceLogs(store); + expect(store.getState().connections.selectedPlugin).not.toBe( + 'TestPlugin', + ); + + const device2 = createDevice('serial2'); + const client2 = createClient(device2, client.query.app); // same app id + store.dispatch(selectDevice(device2)); + 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 { + "method": "inc", + "params": Object { + "delta": 2, + }, + }, + ], + } + `, + ); + }); + }, + ); +}); + test('queue - events processing will be paused', async () => { await createMockFlipperWithPlugin( TestPlugin, diff --git a/src/utils/messageQueue.tsx b/src/utils/messageQueue.tsx index 79034e75d..7b8cab0e0 100644 --- a/src/utils/messageQueue.tsx +++ b/src/utils/messageQueue.tsx @@ -18,6 +18,7 @@ import { } from '../reducers/pluginMessageQueue'; import {Idler, BaseIdler} from './Idler'; import {pluginIsStarred, getSelectedPluginKey} from '../reducers/connections'; +import {deconstructPluginKey} from './clientUtils'; const MAX_BACKGROUND_TASK_TIME = 25; @@ -180,7 +181,11 @@ export function processMessageLater( break; case isSelected: case plugin instanceof FlipperDevicePlugin: - case pluginIsStarred(store.getState().connections, plugin.id): + case pluginIsStarred( + store.getState().connections.userStarredPlugins, + deconstructPluginKey(pluginKey).client, + plugin.id, + ): store.dispatch( queueMessage( pluginKey,