diff --git a/src/Client.tsx b/src/Client.tsx index f4d094799..3001fa4c7 100644 --- a/src/Client.tsx +++ b/src/Client.tsx @@ -207,7 +207,7 @@ export default class Client extends EventEmitter { const client = this; // node.js doesn't support requestIdleCallback this.rIC = - typeof window === 'undefined' + typeof window === 'undefined' || !window.requestIdleCallback ? (cb: Function, _: any) => { cb(); } diff --git a/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap new file mode 100644 index 000000000..627a75731 --- /dev/null +++ b/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`can create a Fake flipper 1`] = ` +Object { + "androidEmulators": Array [], + "clients": Array [ + Object { + "id": "TestApp#Android#unit_test#serial", + "query": Object { + "app": "TestApp", + "device": "unit_test", + "device_id": "serial", + "os": "Android", + }, + }, + ], + "deepLinkPayload": null, + "devices": Array [ + Object { + "deviceType": "physical", + "logs": Array [], + "os": "Android", + "serial": "serial", + "title": "title", + }, + ], + "errors": Array [], + "selectedApp": "TestApp#Android#unit_test#serial", + "selectedDevice": Object { + "deviceType": "physical", + "logs": Array [], + "os": "Android", + "serial": "serial", + "title": "title", + }, + "selectedPlugin": "TestPlugin", + "staticView": null, + "uninitializedClients": Array [], + "userPreferredApp": null, + "userPreferredDevice": null, + "userPreferredPlugin": "TestPlugin", + "userStarredPlugins": Object {}, +} +`; + +exports[`can create a Fake flipper 2`] = ` +Object { + "clientPlugins": Map { + "TestPlugin" => [Function], + }, + "devicePlugins": Map {}, + "disabledPlugins": Array [], + "failedPlugins": Array [], + "gatekeepedPlugins": Array [], + "selectedPlugins": Array [], +} +`; diff --git a/src/__tests__/createMockFlipperWithPlugin.node.tsx b/src/__tests__/createMockFlipperWithPlugin.node.tsx new file mode 100644 index 000000000..00de2968c --- /dev/null +++ b/src/__tests__/createMockFlipperWithPlugin.node.tsx @@ -0,0 +1,63 @@ +/** + * 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} from '../test-utils/createMockFlipperWithPlugin'; +import {FlipperPlugin} from '../plugin'; + +interface PersistedState { + count: 1; +} + +class TestPlugin extends FlipperPlugin { + static id = 'TestPlugin'; + + static defaultPersistedState = { + count: 0, + }; + + static persistedStateReducer( + persistedState: PersistedState, + method: string, + _payload: {}, + ) { + if (method === 'inc') { + return Object.assign({}, persistedState, { + count: persistedState.count + 1, + }); + } + return persistedState; + } + + render() { + return null; + } +} + +test('can create a Fake flipper', async () => { + await createMockFlipperWithPlugin( + TestPlugin, + async ({client, device, store, sendMessage}) => { + expect(client).toBeTruthy(); + expect(device).toBeTruthy(); + expect(store).toBeTruthy(); + expect(sendMessage).toBeTruthy(); + expect(client.plugins.includes(TestPlugin.id)).toBe(true); + expect(store.getState().connections).toMatchSnapshot(); + expect(store.getState().plugins).toMatchSnapshot(); + sendMessage('inc', {}); + expect(store.getState().pluginStates).toMatchInlineSnapshot(` + Object { + "TestApp#Android#unit_test#serial#TestPlugin": Object { + "count": 1, + }, + } + `); + }, + ); +}); diff --git a/src/test-utils/createMockFlipperWithPlugin.tsx b/src/test-utils/createMockFlipperWithPlugin.tsx new file mode 100644 index 000000000..c15843716 --- /dev/null +++ b/src/test-utils/createMockFlipperWithPlugin.tsx @@ -0,0 +1,113 @@ +/** + * 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 {createStore} from 'redux'; + +import {selectPlugin} from '../reducers/connections'; +import BaseDevice from '../devices/BaseDevice'; + +import reducers, {Store} from '../reducers/index'; +import Client, {ClientQuery} from '../Client'; + +import {buildClientId} from '../utils/clientUtils'; +import {Logger} from '../fb-interfaces/Logger'; +import {FlipperPlugin} from '../plugin'; +import {registerPlugins} from '../reducers/plugins'; + +export function createStubLogger(): Logger { + return { + ...console, + track: console.info, + trackTimeSince: console.info, + }; +} + +export async function createMockFlipperWithPlugin( + pluginClazz: typeof FlipperPlugin, + callback: (args: { + client: Client; + device: BaseDevice; + store: Store; + sendMessage(method: string, params: any): void; + }) => Promise, +) { + const store = createStore(reducers); + const device = new BaseDevice('serial', 'physical', 'title', 'Android'); + const logger = createStubLogger(); + + store.dispatch(registerPlugins([pluginClazz])); + + store.dispatch({ + type: 'REGISTER_DEVICE', + payload: 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, + }); + + const client = new Client( + id, + query, + null, // create a stub connection to avoid this plugin to be archived? + logger, + store, + [pluginClazz.name], + device, + ); + + // yikes + client._deviceSet = true; + // client.getDeviceSync = () => device; + client.device = { + then() { + return device; + }, + } as any; + + store.dispatch({ + type: 'NEW_CLIENT', + payload: client, + }); + + store.dispatch( + selectPlugin({ + selectedPlugin: pluginClazz.name, + selectedApp: client.query.app, + deepLinkPayload: null, + }), + ); + + await callback({ + client, + device: device as any, + store, + sendMessage(method, params) { + client.onMessage( + JSON.stringify({ + method: 'execute', + params: { + api: pluginClazz.id, + method, + params, + }, + }), + ); + }, + }); +}