Yarn workspaces
Summary: 1) moved "sonar/desktop/src" to "sonar/desktop/app/src", so "app" is now a separate package containing the core Flipper app code 2) Configured yarn workspaces with the root in "sonar/desktop": app, static, pkg, doctor, headless-tests. Plugins are not included for now, I plan to do this later. Reviewed By: jknoxville Differential Revision: D20535782 fbshipit-source-id: 600b2301960f37c7d72166e0d04eba462bec9fc1
This commit is contained in:
committed by
Facebook GitHub Bot
parent
676d7bbd24
commit
863f89351e
210
desktop/app/src/utils/pluginStateRecorder.tsx
Normal file
210
desktop/app/src/utils/pluginStateRecorder.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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 path from 'path';
|
||||
import fs from 'fs';
|
||||
import {Store, State} from '../reducers';
|
||||
import {getPluginKey} from './pluginUtils';
|
||||
import {serialize} from './serialization';
|
||||
|
||||
let pluginRecordingState: {
|
||||
recording: string;
|
||||
pluginName: string;
|
||||
startState: any;
|
||||
events: [string, any][];
|
||||
endState: any;
|
||||
} = initialRecordingState();
|
||||
|
||||
function initialRecordingState(): typeof pluginRecordingState {
|
||||
return {
|
||||
recording: '',
|
||||
startState: undefined,
|
||||
events: [],
|
||||
endState: undefined,
|
||||
pluginName: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function flipperRecorderAddEvent(
|
||||
pluginKey: string,
|
||||
method: string,
|
||||
params: any,
|
||||
) {
|
||||
if (pluginRecordingState.recording === pluginKey) {
|
||||
pluginRecordingState.events.push([method, params]);
|
||||
}
|
||||
}
|
||||
|
||||
async function flipperStartPluginRecording(state: State) {
|
||||
if (pluginRecordingState.recording) {
|
||||
throw new Error('A plugin recording is already running');
|
||||
}
|
||||
const app = state.connections.selectedApp;
|
||||
const client = state.connections.clients.find(client => client.id === app);
|
||||
if (!app || !client) {
|
||||
throw new Error('Can only record plugin states if a device is selected');
|
||||
}
|
||||
const selectedPlugin = state.connections.selectedPlugin;
|
||||
const pluginKey = getPluginKey(client.id, null, selectedPlugin!);
|
||||
const plugin = state.plugins.clientPlugins.get(selectedPlugin!);
|
||||
if (!selectedPlugin || !plugin) {
|
||||
throw new Error('Can only record plugin states if a plugin is selected');
|
||||
}
|
||||
pluginRecordingState = {
|
||||
recording: pluginKey,
|
||||
startState: undefined,
|
||||
events: [],
|
||||
endState: undefined,
|
||||
pluginName: selectedPlugin,
|
||||
};
|
||||
|
||||
// Note that we don't use the plugin's own serializeState, as that might interact with the
|
||||
// device state, and is used for exporting Flipper traces.
|
||||
pluginRecordingState.startState = await serialize(
|
||||
state.pluginStates[pluginKey] || plugin.defaultPersistedState,
|
||||
);
|
||||
|
||||
console.log(
|
||||
`Started recordig the states of plugin ${selectedPlugin}..... Use window.flipperStopPluginRecording() to finish this process`,
|
||||
);
|
||||
}
|
||||
|
||||
async function flipperStopPluginRecording(state: State) {
|
||||
if (!pluginRecordingState.recording) {
|
||||
throw new Error('No plugin recording is running. ');
|
||||
}
|
||||
if (!pluginRecordingState.events.length) {
|
||||
console.warn('No events were captured, cancelling recording');
|
||||
pluginRecordingState = initialRecordingState();
|
||||
return;
|
||||
}
|
||||
|
||||
pluginRecordingState.endState = await serialize(
|
||||
state.pluginStates[pluginRecordingState.recording],
|
||||
);
|
||||
|
||||
const pluginName = pluginRecordingState.pluginName;
|
||||
const snapShotFileContents = JSON.stringify(pluginRecordingState);
|
||||
const snapShotFileName = `${pluginName}.pluginSnapshot.json`;
|
||||
const testFileName = `${pluginName}EventsRunner.tsx`;
|
||||
const outDir = getOutputDir(pluginName);
|
||||
|
||||
const testFileContents = generateTestSuite(pluginName, snapShotFileName);
|
||||
|
||||
await fs.promises.writeFile(
|
||||
path.join(outDir, snapShotFileName),
|
||||
snapShotFileContents,
|
||||
'utf8',
|
||||
);
|
||||
await fs.promises.writeFile(
|
||||
path.join(outDir, testFileName),
|
||||
testFileContents,
|
||||
'utf8',
|
||||
);
|
||||
|
||||
console.log(
|
||||
`Finished recording ${pluginRecordingState.events.length} for plugin ${
|
||||
pluginRecordingState.recording
|
||||
}. Generated files ${path.join(outDir, testFileName)} and ${path.join(
|
||||
outDir,
|
||||
snapShotFileName,
|
||||
)}. Move them to the '__tests__ folder of your plugin to incorporate them`,
|
||||
);
|
||||
pluginRecordingState = initialRecordingState();
|
||||
}
|
||||
|
||||
export function registerRecordingHooks(store: Store) {
|
||||
Object.assign(window, {
|
||||
flipperStartPluginRecording() {
|
||||
flipperStartPluginRecording(store.getState());
|
||||
},
|
||||
flipperStopPluginRecording() {
|
||||
flipperStopPluginRecording(store.getState());
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getOutputDir(pluginName: string) {
|
||||
const outDir = path.join(process.cwd(), '..');
|
||||
const fbPluginDir = path.join(
|
||||
outDir,
|
||||
'plugins',
|
||||
'fb',
|
||||
pluginName.toLowerCase(),
|
||||
'__tests__',
|
||||
);
|
||||
const defaultPluginDir = path.join(
|
||||
outDir,
|
||||
'plugins',
|
||||
pluginName.toLowerCase(),
|
||||
'__tests__',
|
||||
);
|
||||
|
||||
if (fs.existsSync(fbPluginDir)) {
|
||||
return fbPluginDir;
|
||||
} else if (fs.existsSync(defaultPluginDir)) {
|
||||
return defaultPluginDir;
|
||||
}
|
||||
return outDir;
|
||||
}
|
||||
|
||||
function generateTestSuite(pluginName: string, snapShotFileName: string) {
|
||||
return `\
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
// This file was initially generated by using \`flipperStartPluginRecording()\` in Flipper console
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {deserialize} from 'flipper';
|
||||
import Plugin from '../';
|
||||
|
||||
test('Verify events produce a consistent end state for plugin ${pluginName}', async () => {
|
||||
const snapshotData: {
|
||||
startState: string;
|
||||
endState: string;
|
||||
events: [string, any][];
|
||||
} = JSON.parse(
|
||||
await fs.promises.readFile(
|
||||
path.join(__dirname, '${snapShotFileName}'),
|
||||
'utf8',
|
||||
),
|
||||
);
|
||||
|
||||
const startState: typeof Plugin.defaultPersistedState = deserialize(
|
||||
snapshotData.startState,
|
||||
);
|
||||
const endState: typeof Plugin.defaultPersistedState = deserialize(
|
||||
snapshotData.endState,
|
||||
);
|
||||
const startTime = Date.now();
|
||||
|
||||
const generatedEndState = snapshotData.events.reduce(
|
||||
(store, [method, params]) =>
|
||||
Plugin.persistedStateReducer(store, method, params),
|
||||
startState,
|
||||
);
|
||||
|
||||
const totalTime = Date.now() - startTime;
|
||||
|
||||
expect(generatedEndState).toEqual(endState);
|
||||
console.log(
|
||||
\`Reducer took $\{totalTime\}ms. to process $\{snapshotData.events.length\} events\`,
|
||||
);
|
||||
});
|
||||
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user