Summary: This diff moves RenderHost initialisation to jest, which is thereby treated as just another 'Host' like flipper-ui, the electron app etc. A benefit is that it provides a mocked flipperServer by default that can be used to mock or intercept requests. See LaunchEmulator.spec as example. Also made the jest setup scripts strongly typed by converting them to TS. This change allows the test stub configuration, which was OS dependent, out of flipper-ui-core. Reviewed By: nikoant Differential Revision: D32668632 fbshipit-source-id: fac0c09812b000fd7d1acb75010c35573087c99f
190 lines
5.2 KiB
TypeScript
190 lines
5.2 KiB
TypeScript
/**
|
|
* 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 {
|
|
_NuxManagerContext,
|
|
_createNuxManager,
|
|
_setGlobalInteractionReporter,
|
|
_LoggerContext,
|
|
} from 'flipper-plugin';
|
|
// eslint-disable-next-line no-restricted-imports,flipper/no-electron-remote-imports
|
|
import {
|
|
ipcRenderer,
|
|
remote,
|
|
SaveDialogReturnValue,
|
|
clipboard,
|
|
shell,
|
|
} from 'electron';
|
|
import fs from 'fs';
|
|
import {setupMenuBar} from './setupMenuBar';
|
|
import {FlipperServer, FlipperServerConfig} from 'flipper-common';
|
|
import type {RenderHost} from 'flipper-ui-core';
|
|
|
|
export function initializeElectron(
|
|
flipperServer: FlipperServer,
|
|
flipperServerConfig: FlipperServerConfig,
|
|
) {
|
|
const execPath = process.execPath || remote.process.execPath;
|
|
const isProduction = !/node_modules[\\/]electron[\\/]/.test(execPath);
|
|
|
|
function restart(update: boolean = false) {
|
|
if (isProduction) {
|
|
if (update) {
|
|
const options = {
|
|
args: process.argv
|
|
.splice(0, 1)
|
|
.filter((arg) => arg !== '--no-launcher' && arg !== '--no-updater'),
|
|
};
|
|
remote.app.relaunch(options);
|
|
} else {
|
|
remote.app.relaunch();
|
|
}
|
|
remote.app.exit();
|
|
} else {
|
|
// Relaunching the process with the standard way doesn't work in dev mode.
|
|
// So instead we're sending a signal to dev server to kill the current instance of electron and launch new.
|
|
fetch(`${flipperServerConfig.env.DEV_SERVER_URL}/_restartElectron`, {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
}
|
|
|
|
window.FlipperRenderHostInstance = {
|
|
processId: remote.process.pid,
|
|
isProduction,
|
|
readTextFromClipboard() {
|
|
return clipboard.readText();
|
|
},
|
|
writeTextToClipboard(text: string) {
|
|
clipboard.writeText(text);
|
|
},
|
|
async showSaveDialog(options) {
|
|
return (await remote.dialog.showSaveDialog(options))?.filePath;
|
|
},
|
|
async showOpenDialog({filter, defaultPath}) {
|
|
const result = await remote.dialog.showOpenDialog({
|
|
defaultPath,
|
|
properties: ['openFile'],
|
|
filters: filter ? [filter] : undefined,
|
|
});
|
|
return result.filePaths?.[0];
|
|
},
|
|
showSelectDirectoryDialog(defaultPath = path.resolve('/')) {
|
|
return remote.dialog
|
|
.showOpenDialog({
|
|
properties: ['openDirectory'],
|
|
defaultPath,
|
|
})
|
|
.then((result: SaveDialogReturnValue & {filePaths: string[]}) => {
|
|
if (result.filePath) {
|
|
return result.filePath.toString();
|
|
}
|
|
// Electron typings seem of here, just in case,
|
|
// (can be tested with settings dialog)
|
|
// handle both situations
|
|
if (result.filePaths) {
|
|
return result.filePaths[0];
|
|
}
|
|
return undefined;
|
|
});
|
|
},
|
|
importFile: (async ({
|
|
defaultPath,
|
|
extensions,
|
|
title,
|
|
encoding = 'utf-8',
|
|
multi,
|
|
} = {}) => {
|
|
let {filePaths} = await remote.dialog.showOpenDialog({
|
|
defaultPath,
|
|
properties: [
|
|
'openFile',
|
|
...(multi ? (['multiSelections'] as const) : []),
|
|
],
|
|
filters: extensions ? [{extensions, name: ''}] : undefined,
|
|
title,
|
|
});
|
|
|
|
if (!filePaths.length) {
|
|
return;
|
|
}
|
|
|
|
if (!multi) {
|
|
filePaths = [filePaths[0]];
|
|
}
|
|
|
|
const descriptors = await Promise.all(
|
|
filePaths.map(async (filePath) => {
|
|
const fileName = path.basename(filePath);
|
|
|
|
const data = await fs.promises.readFile(filePath, {encoding});
|
|
return {
|
|
data,
|
|
name: fileName,
|
|
path: filePath,
|
|
};
|
|
}),
|
|
);
|
|
|
|
return multi ? descriptors : descriptors[0];
|
|
}) as RenderHost['importFile'],
|
|
async exportFile(data, {defaultPath, encoding = 'utf-8'} = {}) {
|
|
const {filePath} = await remote.dialog.showSaveDialog({
|
|
defaultPath,
|
|
});
|
|
|
|
if (!filePath) {
|
|
return;
|
|
}
|
|
|
|
await fs.promises.writeFile(filePath, data, {encoding});
|
|
return filePath;
|
|
},
|
|
openLink(url: string) {
|
|
shell.openExternal(url);
|
|
},
|
|
registerShortcut(shortcut, callback) {
|
|
remote.globalShortcut.register(shortcut, callback);
|
|
return () => remote.globalShortcut.unregister(shortcut);
|
|
},
|
|
hasFocus() {
|
|
return remote.getCurrentWindow().isFocused();
|
|
},
|
|
onIpcEvent(event, callback) {
|
|
ipcRenderer.on(event, (_ev, ...args: any[]) => {
|
|
callback(...(args as any));
|
|
});
|
|
},
|
|
sendIpcEvent(event, ...args: any[]) {
|
|
ipcRenderer.send(event, ...args);
|
|
},
|
|
shouldUseDarkColors() {
|
|
return remote.nativeTheme.shouldUseDarkColors;
|
|
},
|
|
restartFlipper() {
|
|
restart();
|
|
},
|
|
loadDefaultPlugins: getDefaultPluginsIndex,
|
|
serverConfig: flipperServerConfig,
|
|
GK(gatekeeper) {
|
|
return flipperServerConfig.gatekeepers[gatekeeper] ?? false;
|
|
},
|
|
flipperServer,
|
|
} as RenderHost;
|
|
|
|
setupMenuBar();
|
|
}
|
|
|
|
function getDefaultPluginsIndex() {
|
|
// eslint-disable-next-line import/no-unresolved
|
|
const index = require('../defaultPlugins');
|
|
return index.default || index;
|
|
}
|