Make sure Sandy Device Plugins can be unit testsed
Summary: Add unit tests to verify that the unit test utilities for for Sandy device plugins work as expected. Fixed a bug revealed by that and cleaned up some TODO's Reviewed By: jknoxville, passy, nikoant Differential Revision: D22693928 fbshipit-source-id: 93162db19d826d0cd7f642cef1447fd756261ac8
This commit is contained in:
committed by
Facebook GitHub Bot
parent
91ed4e31c0
commit
1e956e1bf5
@@ -265,6 +265,7 @@ test('requirePlugin loads valid Sandy plugin', () => {
|
||||
title: 'Sample',
|
||||
version: '1.0.0',
|
||||
});
|
||||
expect(plugin.isDevicePlugin).toBe(false);
|
||||
expect(typeof plugin.module.Component).toBe('function');
|
||||
expect(plugin.module.Component.displayName).toBe('FlipperPlugin(Sample)');
|
||||
expect(typeof plugin.asPluginModule().plugin).toBe('function');
|
||||
@@ -286,3 +287,38 @@ test('requirePlugin errors on invalid Sandy plugin', () => {
|
||||
`"Flipper plugin 'Sample' should export named function called 'plugin'"`,
|
||||
);
|
||||
});
|
||||
|
||||
test('requirePlugin loads valid Sandy Device plugin', () => {
|
||||
const name = 'pluginID';
|
||||
const requireFn = requirePlugin([], {}, require);
|
||||
const plugin = requireFn({
|
||||
...samplePluginDetails,
|
||||
name,
|
||||
entry: path.join(
|
||||
__dirname,
|
||||
'../../../../flipper-plugin/src/__tests__/DeviceTestPlugin',
|
||||
),
|
||||
version: '1.0.0',
|
||||
flipperSDKVersion: '0.0.0',
|
||||
}) as SandyPluginDefinition;
|
||||
expect(plugin).not.toBeNull();
|
||||
// @ts-ignore
|
||||
expect(plugin).toBeInstanceOf(SandyPluginDefinition);
|
||||
expect(plugin.id).toBe('Sample');
|
||||
expect(plugin.details).toMatchObject({
|
||||
flipperSDKVersion: '0.0.0',
|
||||
id: 'Sample',
|
||||
isDefault: false,
|
||||
main: 'dist/bundle.js',
|
||||
name: 'pluginID',
|
||||
source: 'src/index.js',
|
||||
specVersion: 2,
|
||||
title: 'Sample',
|
||||
version: '1.0.0',
|
||||
});
|
||||
expect(plugin.isDevicePlugin).toBe(true);
|
||||
expect(typeof plugin.module.Component).toBe('function');
|
||||
expect(plugin.module.Component.displayName).toBe('FlipperPlugin(Sample)');
|
||||
expect(typeof plugin.asDevicePluginModule().devicePlugin).toBe('function');
|
||||
expect(typeof plugin.asDevicePluginModule().supportsDevice).toBe('function');
|
||||
});
|
||||
|
||||
60
desktop/flipper-plugin/src/__tests__/DeviceTestPlugin.tsx
Normal file
60
desktop/flipper-plugin/src/__tests__/DeviceTestPlugin.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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 * as React from 'react';
|
||||
import {DevicePluginClient, Device} from '../plugin/DevicePlugin';
|
||||
import {usePlugin} from '../plugin/PluginContext';
|
||||
import {createState, useValue} from '../state/atom';
|
||||
|
||||
export function supportsDevice(_device: Device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function devicePlugin(client: DevicePluginClient) {
|
||||
const logStub = jest.fn();
|
||||
const activateStub = jest.fn();
|
||||
const deactivateStub = jest.fn();
|
||||
const destroyStub = jest.fn();
|
||||
const state = createState(
|
||||
{
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
persist: 'counter',
|
||||
},
|
||||
);
|
||||
|
||||
client.device.onLogEntry((entry) => {
|
||||
state.update((d) => {
|
||||
d.count++;
|
||||
});
|
||||
logStub(entry);
|
||||
});
|
||||
client.onActivate(activateStub);
|
||||
client.onDeactivate(deactivateStub);
|
||||
client.onDestroy(destroyStub);
|
||||
|
||||
return {
|
||||
logStub,
|
||||
activateStub,
|
||||
deactivateStub,
|
||||
destroyStub,
|
||||
state,
|
||||
};
|
||||
}
|
||||
|
||||
export function Component() {
|
||||
const api = usePlugin(devicePlugin);
|
||||
const count = useValue(api.state).count;
|
||||
|
||||
// @ts-expect-error
|
||||
api.bla;
|
||||
|
||||
return <h1>Hi from test plugin {count}</h1>;
|
||||
}
|
||||
151
desktop/flipper-plugin/src/__tests__/test-utils-device.node.tsx
Normal file
151
desktop/flipper-plugin/src/__tests__/test-utils-device.node.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 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 * as TestUtils from '../test-utils/test-utils';
|
||||
import * as testPlugin from './DeviceTestPlugin';
|
||||
import {createState} from '../state/atom';
|
||||
|
||||
const testLogMessage = {
|
||||
date: new Date(),
|
||||
message: 'test',
|
||||
pid: 0,
|
||||
tid: 0,
|
||||
tag: 'bla',
|
||||
type: 'warn',
|
||||
app: 'TestApp',
|
||||
} as const;
|
||||
|
||||
test('it can start a device plugin and listen to lifecycle events', () => {
|
||||
const {instance, ...p} = TestUtils.startDevicePlugin(testPlugin);
|
||||
|
||||
// @ts-expect-error
|
||||
p.bla;
|
||||
// @ts-expect-error
|
||||
instance.bla;
|
||||
|
||||
// startPlugin starts activated
|
||||
expect(instance.activateStub).toBeCalledTimes(1);
|
||||
expect(instance.deactivateStub).toBeCalledTimes(0);
|
||||
expect(instance.destroyStub).toBeCalledTimes(0);
|
||||
|
||||
// calling activate is a noop
|
||||
p.activate();
|
||||
expect(instance.activateStub).toBeCalledTimes(1);
|
||||
expect(instance.deactivateStub).toBeCalledTimes(0);
|
||||
expect(instance.destroyStub).toBeCalledTimes(0);
|
||||
|
||||
p.sendLogEntry(testLogMessage);
|
||||
expect(instance.logStub).toBeCalledWith(testLogMessage);
|
||||
expect(instance.state.get().count).toBe(1);
|
||||
|
||||
expect(instance.activateStub).toBeCalledTimes(1);
|
||||
expect(instance.deactivateStub).toBeCalledTimes(0);
|
||||
expect(instance.destroyStub).toBeCalledTimes(0);
|
||||
|
||||
p.deactivate();
|
||||
p.activate();
|
||||
|
||||
expect(instance.activateStub).toBeCalledTimes(2);
|
||||
expect(instance.deactivateStub).toBeCalledTimes(1);
|
||||
expect(instance.destroyStub).toBeCalledTimes(0);
|
||||
|
||||
p.destroy();
|
||||
expect(instance.activateStub).toBeCalledTimes(2);
|
||||
expect(instance.deactivateStub).toBeCalledTimes(2);
|
||||
expect(instance.destroyStub).toBeCalledTimes(1);
|
||||
|
||||
// cannot interact with destroyed plugin
|
||||
expect(() => {
|
||||
p.activate();
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"Plugin has been destroyed already"`);
|
||||
});
|
||||
|
||||
test('it can render a device plugin', () => {
|
||||
const {renderer, instance, sendLogEntry} = TestUtils.renderDevicePlugin(
|
||||
testPlugin,
|
||||
);
|
||||
|
||||
expect(renderer.baseElement).toMatchInlineSnapshot(`
|
||||
<body>
|
||||
<div>
|
||||
<h1>
|
||||
Hi from test plugin
|
||||
0
|
||||
</h1>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
sendLogEntry(testLogMessage);
|
||||
|
||||
expect(renderer.baseElement).toMatchInlineSnapshot(`
|
||||
<body>
|
||||
<div>
|
||||
<h1>
|
||||
Hi from test plugin
|
||||
1
|
||||
</h1>
|
||||
</div>
|
||||
</body>
|
||||
`);
|
||||
|
||||
// @ts-ignore
|
||||
expect(instance.state.listeners.length).toBe(1);
|
||||
renderer.unmount();
|
||||
// @ts-ignore
|
||||
expect(instance.state.listeners.length).toBe(0);
|
||||
});
|
||||
|
||||
test('device plugins support non-serializable state', async () => {
|
||||
const {exportState} = TestUtils.startPlugin({
|
||||
plugin() {
|
||||
const field1 = createState(true);
|
||||
const field2 = createState(
|
||||
{
|
||||
test: 3,
|
||||
},
|
||||
{
|
||||
persist: 'field2',
|
||||
},
|
||||
);
|
||||
return {
|
||||
field1,
|
||||
field2,
|
||||
};
|
||||
},
|
||||
Component() {
|
||||
return null;
|
||||
},
|
||||
});
|
||||
// states are serialized in creation order
|
||||
expect(exportState()).toEqual({field2: {test: 3}});
|
||||
});
|
||||
|
||||
test('device plugins support restoring state', async () => {
|
||||
const {exportState} = TestUtils.startPlugin(
|
||||
{
|
||||
plugin() {
|
||||
const field1 = createState(1, {persist: 'field1'});
|
||||
const field2 = createState(2);
|
||||
const field3 = createState(3, {persist: 'field3'});
|
||||
expect(field1.get()).toBe('a');
|
||||
expect(field2.get()).toBe(2);
|
||||
expect(field3.get()).toBe('b');
|
||||
return {};
|
||||
},
|
||||
Component() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
initialState: {field1: 'a', field3: 'b'},
|
||||
},
|
||||
);
|
||||
expect(exportState()).toEqual({field1: 'a', field3: 'b'});
|
||||
});
|
||||
@@ -137,28 +137,9 @@ export class SandyDevicePluginInstance {
|
||||
this.activated = true;
|
||||
this.events.emit('activate');
|
||||
}
|
||||
// TODO:
|
||||
// const pluginId = this.definition.id;
|
||||
// if (!this.realClient.isBackgroundPlugin(pluginId)) {
|
||||
// this.realClient.initPlugin(pluginId); // will call connect() if needed
|
||||
// }
|
||||
}
|
||||
|
||||
// the plugin is deselected in the UI
|
||||
deactivate() {
|
||||
// TODO:
|
||||
// if (this.destroyed) {
|
||||
// // this can happen if the plugin is disabled while active in the UI.
|
||||
// // In that case deinit & destroy is already triggered from the STAR_PLUGIN action
|
||||
// return;
|
||||
// }
|
||||
// const pluginId = this.definition.id;
|
||||
// if (!this.realClient.isBackgroundPlugin(pluginId)) {
|
||||
// this.realClient.deinitPlugin(pluginId);
|
||||
// }
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.assertNotDestroyed();
|
||||
if (this.activated) {
|
||||
this.activated = false;
|
||||
@@ -168,10 +149,7 @@ export class SandyDevicePluginInstance {
|
||||
|
||||
destroy() {
|
||||
this.assertNotDestroyed();
|
||||
// TODO:
|
||||
// if (this.activated) {
|
||||
// this.realClient.deinitPlugin(this.definition.id);
|
||||
// }
|
||||
this.deactivate();
|
||||
this.events.emit('destroy');
|
||||
this.destroyed = true;
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
|
||||
createMockPluginDetails(),
|
||||
module,
|
||||
);
|
||||
if (definition.isDevicePlugin) {
|
||||
if (!definition.isDevicePlugin) {
|
||||
throw new Error(
|
||||
'Use `startPlugin` or `renderPlugin` to test non-device plugins',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user