diff --git a/desktop/app/src/__tests__/PluginContainer.node.tsx b/desktop/app/src/__tests__/PluginContainer.node.tsx index 8a63baa94..a0fe4cbf0 100644 --- a/desktop/app/src/__tests__/PluginContainer.node.tsx +++ b/desktop/app/src/__tests__/PluginContainer.node.tsx @@ -16,6 +16,7 @@ import { SandyPluginDefinition, FlipperClient, TestUtils, + usePlugin, } from 'flipper-plugin'; import {selectPlugin} from '../reducers/connections'; @@ -92,8 +93,17 @@ test('PluginContainer can render Sandy plugins', async () => { function MySandyPlugin() { renders++; - const sandyContext = useContext(SandyPluginContext); - expect(sandyContext).not.toBe(null); + const sandyApi = usePlugin(plugin); + expect(Object.keys(sandyApi)).toEqual([ + 'connectedStub', + 'disconnectedStub', + ]); + expect(() => { + // eslint-disable-next-line + usePlugin(function bla() { + return {}; + }); + }).toThrowError(/didn't match the type of the requested plugin/); return
Hello from Sandy
; } diff --git a/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx b/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx index 4ef442af8..66c533742 100644 --- a/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx +++ b/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx @@ -9,6 +9,7 @@ import * as React from 'react'; import {FlipperClient} from '../plugin/Plugin'; +import {usePlugin} from '../plugin/PluginContext'; type Events = { inc: { @@ -67,6 +68,11 @@ export function plugin(client: FlipperClient) { } export function Component() { - // TODO T69105011 add test for usePlugin including type assertions - return

Hi from test plugin

; + const api = usePlugin(plugin); + + // @ts-expect-error + api.bla; + + // TODO N.b.: state updates won't be visible + return

Hi from test plugin {api.state.count}

; } diff --git a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx index 705144680..911909da5 100644 --- a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx @@ -53,7 +53,8 @@ test('it can render a plugin', () => {

- Hi from test plugin + Hi from test plugin + 0

diff --git a/desktop/flipper-plugin/src/index.ts b/desktop/flipper-plugin/src/index.ts index d6d6ad9c9..4470f96b1 100644 --- a/desktop/flipper-plugin/src/index.ts +++ b/desktop/flipper-plugin/src/index.ts @@ -12,7 +12,7 @@ import * as TestUtilites from './test-utils/test-utils'; export {SandyPluginInstance, FlipperClient} from './plugin/Plugin'; export {SandyPluginDefinition} from './plugin/SandyPluginDefinition'; export {SandyPluginRenderer} from './plugin/PluginRenderer'; -export {SandyPluginContext} from './plugin/PluginContext'; +export {SandyPluginContext, usePlugin} from './plugin/PluginContext'; // It's not ideal that this exists in flipper-plugin sources directly, // but is the least pain for plugin authors. diff --git a/desktop/flipper-plugin/src/plugin/PluginContext.tsx b/desktop/flipper-plugin/src/plugin/PluginContext.tsx index 49cdca3ad..d9bff9c5f 100644 --- a/desktop/flipper-plugin/src/plugin/PluginContext.tsx +++ b/desktop/flipper-plugin/src/plugin/PluginContext.tsx @@ -7,9 +7,28 @@ * @format */ -import {createContext} from 'react'; -import {SandyPluginInstance} from './Plugin'; +import {createContext, useContext} from 'react'; +import {SandyPluginInstance, FlipperPluginFactory} from './Plugin'; export const SandyPluginContext = createContext< SandyPluginInstance | undefined >(undefined); + +export function usePlugin>( + plugin: PluginFactory, +): ReturnType { + const pluginInstance = useContext(SandyPluginContext); + if (!pluginInstance) { + throw new Error('Plugin context not available'); + } + // In principle we don't *need* the plugin, but having it passed it makes sure the + // return of this function is strongly typed, without the user needing to create it's own + // context. + // But since we pass it, let's make sure the user did request the proper context + if (pluginInstance.definition.module.plugin !== plugin) { + throw new Error( + `Plugin context (${pluginInstance.definition.module.plugin}) didn't match the type of the requested plugin (${plugin})`, + ); + } + return pluginInstance.instanceApi; +}