From f9b72ac69e7c8b3103f6a26eafd172fe69d50abf Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 8 Dec 2021 04:25:28 -0800 Subject: [PATCH] Move RenderHost initialisation to Jest Summary: This diff moves RenderHost initialisation to jest, which is thereby treated as just another 'Host' like flipper-ui, the electron app etc. A benefit is that it provides a mocked flipperServer by default that can be used to mock or intercept requests. See LaunchEmulator.spec as example. Also made the jest setup scripts strongly typed by converting them to TS. This change allows the test stub configuration, which was OS dependent, out of flipper-ui-core. Reviewed By: nikoant Differential Revision: D32668632 fbshipit-source-id: fac0c09812b000fd7d1acb75010c35573087c99f --- .../app/src/electron/initializeElectron.tsx | 12 +- .../src/test-utils/test-utils.tsx | 7 +- desktop/flipper-ui-core/src/RenderHost.tsx | 93 +--------- .../createMockFlipperWithPlugin.node.tsx.snap | 7 - .../src/dispatcher/flipperServer.tsx | 5 - .../src/reducers/connections.tsx | 20 +- .../appinspect/LaunchEmulator.tsx | 22 +-- .../__tests__/LaunchEmulator.spec.tsx | 53 +++--- .../src/test-utils/MockFlipper.tsx | 7 +- .../src/test-utils/TestDevice.tsx | 24 +-- desktop/jest.config.js | 4 +- desktop/scripts/jest-setup-after.js | 72 -------- desktop/scripts/jest-setup-after.ts | 172 ++++++++++++++++++ .../scripts/{jest-setup.js => jest-setup.ts} | 9 +- 14 files changed, 237 insertions(+), 270 deletions(-) delete mode 100644 desktop/scripts/jest-setup-after.js create mode 100644 desktop/scripts/jest-setup-after.ts rename desktop/scripts/{jest-setup.js => jest-setup.ts} (87%) diff --git a/desktop/app/src/electron/initializeElectron.tsx b/desktop/app/src/electron/initializeElectron.tsx index 1d3e9d3c7..d8ba1cb45 100644 --- a/desktop/app/src/electron/initializeElectron.tsx +++ b/desktop/app/src/electron/initializeElectron.tsx @@ -22,18 +22,10 @@ import { clipboard, shell, } from 'electron'; -import type {RenderHost} from 'flipper-ui-core'; import fs from 'fs'; import {setupMenuBar} from './setupMenuBar'; import {FlipperServer, FlipperServerConfig} from 'flipper-common'; - -declare global { - interface Window { - // We store this as a global, to make sure the renderHost is available - // before flipper-ui-core is loaded and needs those during module initialisation - FlipperRenderHostInstance: RenderHost; - } -} +import type {RenderHost} from 'flipper-ui-core'; export function initializeElectron( flipperServer: FlipperServer, @@ -185,7 +177,7 @@ export function initializeElectron( return flipperServerConfig.gatekeepers[gatekeeper] ?? false; }, flipperServer, - }; + } as RenderHost; setupMenuBar(); } diff --git a/desktop/flipper-plugin/src/test-utils/test-utils.tsx b/desktop/flipper-plugin/src/test-utils/test-utils.tsx index d9ed57f29..83fbfa6a0 100644 --- a/desktop/flipper-plugin/src/test-utils/test-utils.tsx +++ b/desktop/flipper-plugin/src/test-utils/test-utils.tsx @@ -596,13 +596,14 @@ export function createFlipperServerMock( exec: jest .fn() .mockImplementation( - (cmd: keyof FlipperServerCommands, ...args: any[]) => { + async (cmd: keyof FlipperServerCommands, ...args: any[]) => { if (overrides?.[cmd]) { return (overrides[cmd] as any)(...args); } - return Promise.reject( - new Error(`FlipperServerMock exec not implemented: ${cmd}}`), + console.warn( + `Empty server response stubbed for command '${cmd}', set 'getRenderHostInstance().flipperServer.exec' in your test to override the behavior.`, ); + return undefined; }, ), close: jest.fn(), diff --git a/desktop/flipper-ui-core/src/RenderHost.tsx b/desktop/flipper-ui-core/src/RenderHost.tsx index 86db85d8b..90ce1cd84 100644 --- a/desktop/flipper-ui-core/src/RenderHost.tsx +++ b/desktop/flipper-ui-core/src/RenderHost.tsx @@ -11,12 +11,7 @@ import type {NotificationEvents} from './dispatcher/notifications'; import type {PluginNotification} from './reducers/notifications'; import type {NotificationConstructorOptions} from 'electron'; import {FlipperLib} from 'flipper-plugin'; -import { - FlipperServer, - FlipperServerConfig, - ReleaseChannel, - Tristate, -} from 'flipper-common'; +import {FlipperServer, FlipperServerConfig} from 'flipper-common'; // Events that are emitted from the main.ts ovr the IPC process bridge in Electron type MainProcessEvents = { @@ -101,7 +96,7 @@ export interface RenderHost { openLink(url: string): void; loadDefaultPlugins(): Record; GK(gatekeeper: string): boolean; - flipperServer?: FlipperServer; + flipperServer: FlipperServer; serverConfig: FlipperServerConfig; } @@ -111,87 +106,3 @@ export function getRenderHostInstance(): RenderHost { } return window.FlipperRenderHostInstance; } - -if (process.env.NODE_ENV === 'test') { - const {tmpdir} = require('os'); - const {resolve} = require('path'); - - const rootPath = resolve(__dirname, '..', '..'); - const stubConfig: FlipperServerConfig = { - env: {...process.env}, - gatekeepers: { - TEST_PASSING_GK: true, - TEST_FAILING_GK: false, - }, - isProduction: false, - launcherSettings: { - ignoreLocalPin: false, - releaseChannel: ReleaseChannel.DEFAULT, - }, - paths: { - appPath: rootPath, - desktopPath: `/dev/null`, - execPath: process.execPath, - homePath: `/dev/null`, - staticPath: resolve(rootPath, 'static'), - tempPath: tmpdir(), - }, - processConfig: { - disabledPlugins: new Set(), - lastWindowPosition: null, - launcherEnabled: false, - launcherMsg: null, - screenCapturePath: `/dev/null`, - }, - settings: { - androidHome: `/dev/null`, - darkMode: 'light', - enableAndroid: false, - enableIOS: false, - enablePhysicalIOS: false, - enablePrefetching: Tristate.False, - idbPath: `/dev/null`, - reactNative: { - shortcuts: {enabled: false, openDevMenu: '', reload: ''}, - }, - showWelcomeAtStartup: false, - suppressPluginErrors: false, - }, - validWebSocketOrigins: [], - }; - - window.FlipperRenderHostInstance = { - processId: -1, - isProduction: false, - readTextFromClipboard() { - return ''; - }, - writeTextToClipboard() {}, - async importFile() { - return undefined; - }, - async exportFile() { - return undefined; - }, - registerShortcut() { - return () => undefined; - }, - hasFocus() { - return true; - }, - onIpcEvent() {}, - sendIpcEvent() {}, - shouldUseDarkColors() { - return false; - }, - restartFlipper() {}, - openLink() {}, - serverConfig: stubConfig, - loadDefaultPlugins() { - return {}; - }, - GK(gk) { - return stubConfig.gatekeepers[gk] ?? false; - }, - }; -} diff --git a/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap index 1cc7fdeb8..058f4f8ce 100644 --- a/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap +++ b/desktop/flipper-ui-core/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -35,13 +35,6 @@ Object { "TestPlugin", ], }, - "flipperServer": Object { - "close": [MockFunction], - "connect": [Function], - "exec": [MockFunction], - "off": [MockFunction], - "on": [MockFunction], - }, "pluginMenuEntries": Array [], "selectedAppId": "TestApp#Android#MockAndroidDevice#serial", "selectedAppPluginListRevision": 0, diff --git a/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx b/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx index 97e38b843..5b8427d86 100644 --- a/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/flipperServer.tsx @@ -27,11 +27,6 @@ export function connectFlipperServerToStore( store: Store, logger: Logger, ) { - store.dispatch({ - type: 'SET_FLIPPER_SERVER', - payload: server, - }); - server.on('notification', ({type, title, description}) => { const text = `[$type] ${title}: ${description}`; console.warn(text); diff --git a/desktop/flipper-ui-core/src/reducers/connections.tsx b/desktop/flipper-ui-core/src/reducers/connections.tsx index adca1785e..f31598e24 100644 --- a/desktop/flipper-ui-core/src/reducers/connections.tsx +++ b/desktop/flipper-ui-core/src/reducers/connections.tsx @@ -12,12 +12,7 @@ import {produce} from 'immer'; import type BaseDevice from '../devices/BaseDevice'; import type Client from '../Client'; -import type { - UninitializedClient, - DeviceOS, - Logger, - FlipperServer, -} from 'flipper-common'; +import type {UninitializedClient, DeviceOS, Logger} from 'flipper-common'; import type {Actions} from '.'; import {WelcomeScreenStaticView} from '../sandy-chrome/WelcomeScreen'; import {isDevicePluginDefinition} from '../utils/pluginUtils'; @@ -79,7 +74,6 @@ type StateV2 = { deepLinkPayload: unknown; staticView: StaticView; selectedAppPluginListRevision: number; - flipperServer: FlipperServer | undefined; }; type StateV1 = Omit & { @@ -165,10 +159,6 @@ export type Action = | { type: 'APP_PLUGIN_LIST_CHANGED'; } - | { - type: 'SET_FLIPPER_SERVER'; - payload: FlipperServer; - } | RegisterPluginAction; const DEFAULT_PLUGIN = 'DeviceLogs'; @@ -195,18 +185,10 @@ const INITAL_STATE: State = { deepLinkPayload: null, staticView: WelcomeScreenStaticView, selectedAppPluginListRevision: 0, - flipperServer: undefined, }; export default (state: State = INITAL_STATE, action: Actions): State => { switch (action.type) { - case 'SET_FLIPPER_SERVER': { - return { - ...state, - flipperServer: action.payload, - }; - } - case 'SET_STATIC_VIEW': { const {payload, deepLinkPayload} = action; return { diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx index d7522a1a4..6bbdc3007 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -20,6 +20,7 @@ import {useStore} from '../../utils/useStore'; import {Layout, renderReactRoot, withTrackingScope} from 'flipper-plugin'; import {Provider} from 'react-redux'; import {IOSDeviceParams} from 'flipper-common'; +import {getRenderHostInstance} from '../../RenderHost'; const COLD_BOOT = 'cold-boot'; @@ -37,7 +38,6 @@ function LaunchEmulatorContainer({onClose}: {onClose: () => void}) { export const LaunchEmulatorDialog = withTrackingScope( function LaunchEmulatorDialog({onClose}: {onClose: () => void}) { - const flipperServer = useStore((state) => state.connections.flipperServer); const iosEnabled = useStore((state) => state.settingsState.enableIOS); const androidEnabled = useStore( (state) => state.settingsState.enableAndroid, @@ -49,8 +49,8 @@ export const LaunchEmulatorDialog = withTrackingScope( if (!iosEnabled) { return; } - flipperServer! - .exec('ios-get-simulators', false) + getRenderHostInstance() + .flipperServer.exec('ios-get-simulators', false) .then((emulators) => { setIosEmulators( emulators.filter( @@ -63,21 +63,21 @@ export const LaunchEmulatorDialog = withTrackingScope( .catch((e) => { console.warn('Failed to find simulators', e); }); - }, [iosEnabled, flipperServer]); + }, [iosEnabled]); useEffect(() => { if (!androidEnabled) { return; } - flipperServer! - .exec('android-get-emulators') + getRenderHostInstance() + .flipperServer.exec('android-get-emulators') .then((emulators) => { setAndroidEmulators(emulators); }) .catch((e) => { console.warn('Failed to find emulators', e); }); - }, [androidEnabled, flipperServer]); + }, [androidEnabled]); const items = [ ...(androidEmulators.length > 0 @@ -85,8 +85,8 @@ export const LaunchEmulatorDialog = withTrackingScope( : []), ...androidEmulators.map((name) => { const launch = (coldBoot: boolean) => { - flipperServer! - .exec('android-launch-emulator', name, coldBoot) + getRenderHostInstance() + .flipperServer.exec('android-launch-emulator', name, coldBoot) .then(onClose) .catch((e) => { console.error('Failed to start emulator: ', e); @@ -123,8 +123,8 @@ export const LaunchEmulatorDialog = withTrackingScope(