Introduce DevicePlugin APIs
Summary: This stack introduces Sandy device plugins, they are quite similar to normal plugins, but, a devicePlugin module is organized as ``` export function supportsDevice(device): boolean export function devicePlugin(devicePluginClient) export function Component ``` Device plugins get access to the device meta data and can subscribe to the `onLogEntry` callback and `onDestroy` lifecycle. They will be able to store state just as normal plugins, but can't send or receive methods, so devicePluginClient is a bit limited. This diff only sets up most of the new data structures, and makes sure everything still compiles and no existing tests fail. To prevent this diff from becoming to big, actually loading, rendering and testing device plugins will be done in next diffs Please take a critical look at the api proposed and the (especially) the public names used :) Reviewed By: passy, nikoant Differential Revision: D22691351 fbshipit-source-id: bdbbd7f86d14b646fc9a693ad19f33583a76f26d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6083534025
commit
91ed4e31c0
@@ -24,14 +24,22 @@ import {
|
||||
import {
|
||||
SandyPluginDefinition,
|
||||
FlipperPluginModule,
|
||||
FlipperDevicePluginModule,
|
||||
} from '../plugin/SandyPluginDefinition';
|
||||
import {SandyPluginRenderer} from '../plugin/PluginRenderer';
|
||||
import {act} from '@testing-library/react';
|
||||
import {
|
||||
DeviceLogEntry,
|
||||
SandyDevicePluginInstance,
|
||||
RealFlipperDevice,
|
||||
DeviceLogListener,
|
||||
} from '../plugin/DevicePlugin';
|
||||
|
||||
type Renderer = RenderResult<typeof queries>;
|
||||
|
||||
interface StartPluginOptions {
|
||||
initialState?: Record<string, any>;
|
||||
isArchived?: boolean;
|
||||
}
|
||||
|
||||
type ExtractClientType<Module extends FlipperPluginModule<any>> = Parameters<
|
||||
@@ -105,6 +113,37 @@ interface StartPluginResult<Module extends FlipperPluginModule<any>> {
|
||||
exportState(): any;
|
||||
}
|
||||
|
||||
interface StartDevicePluginResult<Module extends FlipperDevicePluginModule> {
|
||||
/**
|
||||
* the instantiated plugin for this test
|
||||
*/
|
||||
instance: ReturnType<Module['devicePlugin']>;
|
||||
/**
|
||||
* module, from which any other exposed methods can be accessed during testing
|
||||
*/
|
||||
module: Module;
|
||||
/**
|
||||
* Emulates the 'onActivate' event
|
||||
*/
|
||||
activate(): void;
|
||||
/**
|
||||
* Emulatese the 'onDeactivate' event
|
||||
*/
|
||||
deactivate(): void;
|
||||
/**
|
||||
* Emulates the 'destroy' event. After calling destroy this plugin instance won't be usable anymore
|
||||
*/
|
||||
destroy(): void;
|
||||
/**
|
||||
* Emulates sending a log message arriving from the device
|
||||
*/
|
||||
sendLogEntry(logEntry: DeviceLogEntry): void;
|
||||
/**
|
||||
* Grabs the current (exportable) state
|
||||
*/
|
||||
exportState(): any;
|
||||
}
|
||||
|
||||
export function startPlugin<Module extends FlipperPluginModule<any>>(
|
||||
module: Module,
|
||||
options?: StartPluginOptions,
|
||||
@@ -113,6 +152,11 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
|
||||
createMockPluginDetails(),
|
||||
module,
|
||||
);
|
||||
if (definition.isDevicePlugin) {
|
||||
throw new Error(
|
||||
'Use `startDevicePlugin` or `renderDevicePlugin` to test device plugins',
|
||||
);
|
||||
}
|
||||
|
||||
const sendStub = jest.fn();
|
||||
const fakeFlipper: RealFlipperClient = {
|
||||
@@ -199,6 +243,71 @@ export function renderPlugin<Module extends FlipperPluginModule<any>>(
|
||||
};
|
||||
}
|
||||
|
||||
export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
|
||||
module: Module,
|
||||
options?: StartPluginOptions,
|
||||
): StartDevicePluginResult<Module> {
|
||||
const definition = new SandyPluginDefinition(
|
||||
createMockPluginDetails(),
|
||||
module,
|
||||
);
|
||||
if (definition.isDevicePlugin) {
|
||||
throw new Error(
|
||||
'Use `startPlugin` or `renderPlugin` to test non-device plugins',
|
||||
);
|
||||
}
|
||||
|
||||
const testDevice = createMockDevice(options);
|
||||
const pluginInstance = new SandyDevicePluginInstance(
|
||||
testDevice,
|
||||
definition,
|
||||
options?.initialState,
|
||||
);
|
||||
// we start connected
|
||||
pluginInstance.activate();
|
||||
|
||||
const res: StartDevicePluginResult<Module> = {
|
||||
module,
|
||||
instance: pluginInstance.instanceApi,
|
||||
activate: () => pluginInstance.activate(),
|
||||
deactivate: () => pluginInstance.deactivate(),
|
||||
destroy: () => pluginInstance.destroy(),
|
||||
sendLogEntry: (entry) => {
|
||||
act(() => {
|
||||
testDevice.addLogEntry(entry);
|
||||
});
|
||||
},
|
||||
exportState: () => pluginInstance.exportState(),
|
||||
};
|
||||
// @ts-ignore
|
||||
res._backingInstance = pluginInstance;
|
||||
return res;
|
||||
}
|
||||
|
||||
export function renderDevicePlugin<Module extends FlipperDevicePluginModule>(
|
||||
module: Module,
|
||||
options?: StartPluginOptions,
|
||||
): StartDevicePluginResult<Module> & {
|
||||
renderer: Renderer;
|
||||
act: (cb: () => void) => void;
|
||||
} {
|
||||
const res = startDevicePlugin(module, options);
|
||||
// @ts-ignore hidden api
|
||||
const pluginInstance: SandyDevicePluginInstance = res._backingInstance;
|
||||
|
||||
const renderer = render(<SandyPluginRenderer plugin={pluginInstance} />);
|
||||
|
||||
return {
|
||||
...res,
|
||||
renderer,
|
||||
act: testingLibAct,
|
||||
destroy: () => {
|
||||
renderer.unmount();
|
||||
pluginInstance.destroy();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createMockPluginDetails(
|
||||
details?: Partial<PluginDetails>,
|
||||
): PluginDetails {
|
||||
@@ -216,3 +325,20 @@ export function createMockPluginDetails(
|
||||
...details,
|
||||
};
|
||||
}
|
||||
|
||||
function createMockDevice(options?: StartPluginOptions): RealFlipperDevice {
|
||||
const logListeners: (undefined | DeviceLogListener)[] = [];
|
||||
return {
|
||||
isArchived: !!options?.isArchived,
|
||||
addLogListener(cb) {
|
||||
logListeners.push(cb);
|
||||
return (logListeners.length - 1) as any;
|
||||
},
|
||||
removeLogListener(idx) {
|
||||
logListeners[idx as any] = undefined;
|
||||
},
|
||||
addLogEntry(entry: DeviceLogEntry) {
|
||||
logListeners.forEach((f) => f?.(entry));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user