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,
|
ws: WebSocket | undefined,
|
||||||
store: Store,
|
store: Store,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
@@ -117,7 +117,7 @@ export default (store: Store, logger: Logger) => {
|
|||||||
_ws.onopen = () => {
|
_ws.onopen = () => {
|
||||||
clearTimeout(guard);
|
clearTimeout(guard);
|
||||||
ws = _ws;
|
ws = _ws;
|
||||||
registerDevice(ws, store, logger);
|
registerMetroDevice(ws, store, logger);
|
||||||
};
|
};
|
||||||
|
|
||||||
_ws.onclose = _ws.onerror = () => {
|
_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`,
|
`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
|
// Note: no scheduleNext, we won't retry until restart
|
||||||
}, 5000);
|
}, 5000);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ function getPluginTooltip(details: PluginDetails): string {
|
|||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findBestClient(
|
export function findBestClient(
|
||||||
clients: Client[],
|
clients: Client[],
|
||||||
selectedApp: string | null,
|
selectedApp: string | null,
|
||||||
userPreferredApp: string | null,
|
userPreferredApp: string | null,
|
||||||
@@ -322,13 +322,13 @@ function findBestClient(
|
|||||||
return clients.find((c) => c.id === (selectedApp || userPreferredApp));
|
return clients.find((c) => c.id === (selectedApp || userPreferredApp));
|
||||||
}
|
}
|
||||||
|
|
||||||
function findMetroDevice(
|
export function findMetroDevice(
|
||||||
devices: State['connections']['devices'],
|
devices: State['connections']['devices'],
|
||||||
): BaseDevice | undefined {
|
): BaseDevice | undefined {
|
||||||
return devices?.find((device) => device.os === 'Metro' && !device.isArchived);
|
return devices?.find((device) => device.os === 'Metro' && !device.isArchived);
|
||||||
}
|
}
|
||||||
|
|
||||||
function findBestDevice(
|
export function findBestDevice(
|
||||||
client: Client | undefined,
|
client: Client | undefined,
|
||||||
devices: State['connections']['devices'],
|
devices: State['connections']['devices'],
|
||||||
selectedDevice: BaseDevice | null,
|
selectedDevice: BaseDevice | null,
|
||||||
@@ -353,7 +353,7 @@ function findBestDevice(
|
|||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
function computePluginLists(
|
export function computePluginLists(
|
||||||
device: BaseDevice | undefined,
|
device: BaseDevice | undefined,
|
||||||
metroDevice: BaseDevice | undefined,
|
metroDevice: BaseDevice | undefined,
|
||||||
client: Client | 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 {getInstance} from '../fb-stubs/Logger';
|
||||||
import {setFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
import {setFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
||||||
|
|
||||||
type MockFlipperResult = {
|
export type MockFlipperResult = {
|
||||||
client: Client;
|
client: Client;
|
||||||
device: BaseDevice;
|
device: BaseDevice;
|
||||||
store: Store;
|
store: Store;
|
||||||
|
|||||||
Reference in New Issue
Block a user