Add unit tests for selection changes and plugin list computation
Summary: Tests to verify the more complex selection logic resulting from the Metro device exception. Also verifies the logic that computes the available plugins Reviewed By: nikoant Differential Revision: D24445555 fbshipit-source-id: 70110c4470e6aa1356e814aa40744b65c21cad89
This commit is contained in:
committed by
Facebook GitHub Bot
parent
99dfeacdf8
commit
4f7294c96d
@@ -47,7 +47,7 @@ async function isMetroRunning(): Promise<boolean> {
|
||||
});
|
||||
}
|
||||
|
||||
async function registerDevice(
|
||||
export async function registerMetroDevice(
|
||||
ws: WebSocket | undefined,
|
||||
store: Store,
|
||||
logger: Logger,
|
||||
@@ -117,7 +117,7 @@ export default (store: Store, logger: Logger) => {
|
||||
_ws.onopen = () => {
|
||||
clearTimeout(guard);
|
||||
ws = _ws;
|
||||
registerDevice(ws, store, logger);
|
||||
registerMetroDevice(ws, store, logger);
|
||||
};
|
||||
|
||||
_ws.onclose = _ws.onerror = () => {
|
||||
@@ -138,7 +138,7 @@ export default (store: Store, logger: Logger) => {
|
||||
`Flipper did find a running Metro instance, but couldn't connect to the logs. Probably your React Native version is too old to support Flipper. Cause: Failed to get a connection to ${METRO_LOGS_ENDPOINT} in a timely fashion`,
|
||||
),
|
||||
);
|
||||
registerDevice(undefined, store, logger);
|
||||
registerMetroDevice(undefined, store, logger);
|
||||
// Note: no scheduleNext, we won't retry until restart
|
||||
}, 5000);
|
||||
} else {
|
||||
|
||||
@@ -314,7 +314,7 @@ function getPluginTooltip(details: PluginDetails): string {
|
||||
}`;
|
||||
}
|
||||
|
||||
function findBestClient(
|
||||
export function findBestClient(
|
||||
clients: Client[],
|
||||
selectedApp: string | null,
|
||||
userPreferredApp: string | null,
|
||||
@@ -322,13 +322,13 @@ function findBestClient(
|
||||
return clients.find((c) => c.id === (selectedApp || userPreferredApp));
|
||||
}
|
||||
|
||||
function findMetroDevice(
|
||||
export function findMetroDevice(
|
||||
devices: State['connections']['devices'],
|
||||
): BaseDevice | undefined {
|
||||
return devices?.find((device) => device.os === 'Metro' && !device.isArchived);
|
||||
}
|
||||
|
||||
function findBestDevice(
|
||||
export function findBestDevice(
|
||||
client: Client | undefined,
|
||||
devices: State['connections']['devices'],
|
||||
selectedDevice: BaseDevice | null,
|
||||
@@ -353,7 +353,7 @@ function findBestDevice(
|
||||
return selected;
|
||||
}
|
||||
|
||||
function computePluginLists(
|
||||
export function computePluginLists(
|
||||
device: BaseDevice | undefined,
|
||||
metroDevice: BaseDevice | undefined,
|
||||
client: Client | undefined,
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* 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 {
|
||||
createMockFlipperWithPlugin,
|
||||
MockFlipperResult,
|
||||
} from '../../../test-utils/createMockFlipperWithPlugin';
|
||||
import {
|
||||
findBestClient,
|
||||
findBestDevice,
|
||||
findMetroDevice,
|
||||
computePluginLists,
|
||||
} from '../PluginList';
|
||||
import {FlipperPlugin} from '../../../plugin';
|
||||
import MetroDevice from '../../../devices/MetroDevice';
|
||||
import BaseDevice from '../../../devices/BaseDevice';
|
||||
import {SandyPluginDefinition} from 'flipper-plugin';
|
||||
import {createMockPluginDetails} from 'flipper-plugin/src/test-utils/test-utils';
|
||||
import {selectPlugin, starPlugin} from '../../../reducers/connections';
|
||||
import {registerMetroDevice} from '../../../dispatcher/metroDevice';
|
||||
import {addGatekeepedPlugins, registerPlugins} from '../../../reducers/plugins';
|
||||
|
||||
// eslint-disable-next-line
|
||||
import * as LogsPluginModule from '../../../../../plugins/logs/index';
|
||||
|
||||
const logsPlugin = new SandyPluginDefinition(
|
||||
createMockPluginDetails({id: 'DeviceLogs'}),
|
||||
LogsPluginModule,
|
||||
);
|
||||
|
||||
class TestPlugin extends FlipperPlugin<any, any, any> {}
|
||||
|
||||
describe('basic findBestDevice', () => {
|
||||
let flipper: MockFlipperResult;
|
||||
beforeEach(async () => {
|
||||
flipper = await createMockFlipperWithPlugin(TestPlugin);
|
||||
});
|
||||
|
||||
test('findBestDevice prefers selected device', () => {
|
||||
const {client, device} = flipper;
|
||||
const {connections} = flipper.store.getState();
|
||||
expect(
|
||||
findBestDevice(
|
||||
client,
|
||||
connections.devices,
|
||||
device,
|
||||
undefined,
|
||||
device.title,
|
||||
),
|
||||
).toBe(device);
|
||||
});
|
||||
|
||||
test('findBestDevice picks device of current client', () => {
|
||||
const {client, device} = flipper;
|
||||
const {connections} = flipper.store.getState();
|
||||
expect(
|
||||
findBestDevice(client, connections.devices, null, undefined, null),
|
||||
).toBe(device);
|
||||
});
|
||||
|
||||
test('findBestDevice picks preferred device if no client and device', () => {
|
||||
const {device} = flipper;
|
||||
const {connections} = flipper.store.getState();
|
||||
expect(
|
||||
findBestDevice(
|
||||
undefined,
|
||||
connections.devices,
|
||||
null,
|
||||
undefined,
|
||||
device.title,
|
||||
),
|
||||
).toBe(device);
|
||||
});
|
||||
});
|
||||
|
||||
describe('basic findBestDevice with metro present', () => {
|
||||
let flipper: MockFlipperResult;
|
||||
let metro: MetroDevice;
|
||||
let testDevice: BaseDevice;
|
||||
|
||||
beforeEach(async () => {
|
||||
flipper = await createMockFlipperWithPlugin(logsPlugin);
|
||||
testDevice = flipper.device;
|
||||
// flipper.store.dispatch(registerPlugins([LogsPlugin]))
|
||||
await registerMetroDevice(undefined, flipper.store, flipper.logger);
|
||||
metro = findMetroDevice(
|
||||
flipper.store.getState().connections.devices,
|
||||
)! as MetroDevice;
|
||||
});
|
||||
|
||||
test('findMetroDevice', () => {
|
||||
expect(metro).toBeInstanceOf(MetroDevice);
|
||||
});
|
||||
|
||||
test('correct base selection state', () => {
|
||||
const {connections} = flipper.store.getState();
|
||||
expect(connections).toMatchObject({
|
||||
devices: [testDevice, metro],
|
||||
selectedDevice: testDevice,
|
||||
selectedPlugin: 'DeviceLogs',
|
||||
userPreferredDevice: 'MockAndroidDevice',
|
||||
userPreferredPlugin: 'DeviceLogs',
|
||||
userPreferredApp: 'TestApp#Android#MockAndroidDevice#serial',
|
||||
});
|
||||
expect(
|
||||
findBestClient(
|
||||
connections.clients,
|
||||
connections.selectedApp,
|
||||
connections.userPreferredApp,
|
||||
),
|
||||
).toBe(flipper.client);
|
||||
});
|
||||
|
||||
test('selecting Metro Logs works but keeps normal device preferred', () => {
|
||||
flipper.store.dispatch(
|
||||
selectPlugin({
|
||||
selectedPlugin: logsPlugin.id,
|
||||
selectedApp: null,
|
||||
selectedDevice: metro,
|
||||
deepLinkPayload: null,
|
||||
}),
|
||||
);
|
||||
expect(flipper.store.getState().connections).toMatchObject({
|
||||
devices: [testDevice, metro],
|
||||
selectedApp: null,
|
||||
selectedDevice: metro,
|
||||
selectedPlugin: 'DeviceLogs',
|
||||
userPreferredDevice: 'MockAndroidDevice', // Not metro!
|
||||
userPreferredPlugin: 'DeviceLogs',
|
||||
userPreferredApp: 'TestApp#Android#MockAndroidDevice#serial',
|
||||
});
|
||||
const {connections} = flipper.store.getState();
|
||||
// find best device is still metro
|
||||
expect(
|
||||
findBestDevice(
|
||||
undefined,
|
||||
connections.devices,
|
||||
connections.selectedDevice,
|
||||
metro,
|
||||
connections.userPreferredDevice,
|
||||
),
|
||||
).toBe(testDevice);
|
||||
// find best client still returns app
|
||||
expect(
|
||||
findBestClient(
|
||||
connections.clients,
|
||||
connections.selectedApp,
|
||||
connections.userPreferredApp,
|
||||
),
|
||||
).toBe(flipper.client);
|
||||
});
|
||||
|
||||
test('computePluginLists', () => {
|
||||
const state = flipper.store.getState();
|
||||
expect(
|
||||
computePluginLists(
|
||||
testDevice,
|
||||
metro,
|
||||
flipper.client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
),
|
||||
).toEqual({
|
||||
devicePlugins: [logsPlugin],
|
||||
metroPlugins: [logsPlugin],
|
||||
enabledPlugins: [],
|
||||
disabledPlugins: [],
|
||||
unavailablePlugins: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('computePluginLists with problematic plugins', () => {
|
||||
const noopPlugin = {
|
||||
plugin() {},
|
||||
Component() {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
const unsupportedDevicePlugin = new SandyPluginDefinition(
|
||||
createMockPluginDetails({
|
||||
id: 'unsupportedDevicePlugin',
|
||||
title: 'Unsupported Device Plugin',
|
||||
}),
|
||||
{
|
||||
devicePlugin() {
|
||||
return {};
|
||||
},
|
||||
supportsDevice() {
|
||||
return false;
|
||||
},
|
||||
Component() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const unsupportedPlugin = new SandyPluginDefinition(
|
||||
createMockPluginDetails({
|
||||
id: 'unsupportedPlugin',
|
||||
title: 'Unsupported Plugin',
|
||||
}),
|
||||
noopPlugin,
|
||||
);
|
||||
|
||||
const gateKeepedPlugin = createMockPluginDetails({
|
||||
id: 'gateKeepedPlugin',
|
||||
title: 'Gatekeeped Plugin',
|
||||
gatekeeper: 'not for you',
|
||||
});
|
||||
|
||||
const plugin1 = new SandyPluginDefinition(
|
||||
createMockPluginDetails({
|
||||
id: 'plugin1',
|
||||
title: 'Plugin 1',
|
||||
}),
|
||||
{
|
||||
plugin() {},
|
||||
Component() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const plugin2 = new SandyPluginDefinition(
|
||||
createMockPluginDetails({
|
||||
id: 'plugin2',
|
||||
title: 'Plugin 2',
|
||||
}),
|
||||
noopPlugin,
|
||||
);
|
||||
|
||||
flipper.store.dispatch(
|
||||
registerPlugins([
|
||||
unsupportedDevicePlugin,
|
||||
unsupportedPlugin,
|
||||
plugin1,
|
||||
plugin2,
|
||||
]),
|
||||
);
|
||||
flipper.store.dispatch(addGatekeepedPlugins([gateKeepedPlugin]));
|
||||
|
||||
// ok, this is a little hackish
|
||||
flipper.client.plugins = ['plugin1', 'plugin2'];
|
||||
|
||||
let state = flipper.store.getState();
|
||||
expect(
|
||||
computePluginLists(
|
||||
testDevice,
|
||||
metro,
|
||||
flipper.client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
),
|
||||
).toEqual({
|
||||
devicePlugins: [logsPlugin],
|
||||
metroPlugins: [logsPlugin],
|
||||
enabledPlugins: [],
|
||||
disabledPlugins: [plugin1, plugin2],
|
||||
unavailablePlugins: [
|
||||
[
|
||||
gateKeepedPlugin,
|
||||
"This plugin is only available to members of gatekeeper 'not for you'",
|
||||
],
|
||||
[
|
||||
unsupportedDevicePlugin.details,
|
||||
"Device plugin 'Unsupported Device Plugin' is not supported by the current device type.",
|
||||
],
|
||||
[
|
||||
unsupportedPlugin.details,
|
||||
"Plugin 'Unsupported Plugin' is not loaded by the client application",
|
||||
],
|
||||
],
|
||||
});
|
||||
|
||||
flipper.store.dispatch(
|
||||
starPlugin({
|
||||
plugin: plugin2,
|
||||
selectedApp: flipper.client.query.app,
|
||||
}),
|
||||
);
|
||||
state = flipper.store.getState();
|
||||
expect(
|
||||
computePluginLists(
|
||||
testDevice,
|
||||
metro,
|
||||
flipper.client,
|
||||
state.plugins,
|
||||
state.connections.userStarredPlugins,
|
||||
),
|
||||
).toMatchObject({
|
||||
enabledPlugins: [plugin2],
|
||||
disabledPlugins: [plugin1],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -39,7 +39,7 @@ import {getPluginKey, isDevicePluginDefinition} from '../utils/pluginUtils';
|
||||
import {getInstance} from '../fb-stubs/Logger';
|
||||
import {setFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
||||
|
||||
type MockFlipperResult = {
|
||||
export type MockFlipperResult = {
|
||||
client: Client;
|
||||
device: BaseDevice;
|
||||
store: Store;
|
||||
|
||||
Reference in New Issue
Block a user