mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Replace jest.mock's with overriding dependencies (#6014)
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
1 parent
03d2389f01
commit
81f7bd3c2c
50 files changed
+788
-605
No files matched your search
@@ -10,8 +10,10 @@ import extensionLoaderInjectable from "../extension-loader/extension-loader.inje
|
||||
import { runInAction } from "mobx";
|
||||
import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable";
|
||||
import mockFs from "mock-fs";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import { delay } from "../../renderer/utils";
|
||||
import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
|
||||
import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable";
|
||||
import type { IpcRenderer } from "electron";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
@@ -19,10 +21,14 @@ const manifestPath = "manifest/path";
|
||||
const manifestPath2 = "manifest/path2";
|
||||
const manifestPath3 = "manifest/path3";
|
||||
|
||||
jest.mock(
|
||||
"electron",
|
||||
() => ({
|
||||
ipcRenderer: {
|
||||
describe("ExtensionLoader", () => {
|
||||
let extensionLoader: ExtensionLoader;
|
||||
let updateExtensionStateMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(ipcRendererInjectable, () => ({
|
||||
invoke: jest.fn(async (channel: string) => {
|
||||
if (channel === "extension-loader:main:state") {
|
||||
return [
|
||||
@@ -59,59 +65,46 @@ jest.mock(
|
||||
|
||||
return [];
|
||||
}),
|
||||
on: jest.fn(
|
||||
(channel: string, listener: (event: any, ...args: any[]) => void) => {
|
||||
if (channel === "extension-loader:main:state") {
|
||||
// First initialize with extensions 1 and 2
|
||||
// and then broadcast event to remove extension 2 and add extension number 3
|
||||
setTimeout(() => {
|
||||
listener({}, [
|
||||
[
|
||||
|
||||
on: (channel: string, listener: (event: any, ...args: any[]) => void) => {
|
||||
if (channel === "extension-loader:main:state") {
|
||||
// First initialize with extensions 1 and 2
|
||||
// and then broadcast event to remove extension 2 and add extension number 3
|
||||
setTimeout(() => {
|
||||
listener({}, [
|
||||
[
|
||||
manifestPath,
|
||||
{
|
||||
manifest: {
|
||||
name: "TestExtension",
|
||||
version: "1.0.0",
|
||||
},
|
||||
id: manifestPath,
|
||||
absolutePath: "/test/1",
|
||||
manifestPath,
|
||||
{
|
||||
manifest: {
|
||||
name: "TestExtension",
|
||||
version: "1.0.0",
|
||||
},
|
||||
id: manifestPath,
|
||||
absolutePath: "/test/1",
|
||||
manifestPath,
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
manifestPath3,
|
||||
{
|
||||
manifest: {
|
||||
name: "TestExtension3",
|
||||
version: "3.0.0",
|
||||
},
|
||||
],
|
||||
[
|
||||
manifestPath3,
|
||||
{
|
||||
manifest: {
|
||||
name: "TestExtension3",
|
||||
version: "3.0.0",
|
||||
},
|
||||
id: manifestPath3,
|
||||
absolutePath: "/test/3",
|
||||
manifestPath: manifestPath3,
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
),
|
||||
},
|
||||
}),
|
||||
{
|
||||
virtual: true,
|
||||
},
|
||||
);
|
||||
|
||||
describe("ExtensionLoader", () => {
|
||||
let extensionLoader: ExtensionLoader;
|
||||
let updateExtensionStateMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
id: manifestPath3,
|
||||
absolutePath: "/test/3",
|
||||
manifestPath: manifestPath3,
|
||||
isBundled: false,
|
||||
isEnabled: true,
|
||||
},
|
||||
],
|
||||
]);
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
}) as unknown as IpcRenderer);
|
||||
|
||||
mockFs();
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ import installExtensionInjectable from "../extension-installer/install-extension
|
||||
import extensionPackageRootDirectoryInjectable from "../extension-installer/extension-package-root-directory/extension-package-root-directory.injectable";
|
||||
import installExtensionsInjectable from "../extension-installer/install-extensions/install-extensions.injectable";
|
||||
import staticFilesDirectoryInjectable from "../../common/vars/static-files-directory.injectable";
|
||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
import watchInjectable from "../../common/fs/watch/watch.injectable";
|
||||
|
||||
const extensionDiscoveryInjectable = getInjectable({
|
||||
id: "extension-discovery",
|
||||
@@ -40,6 +44,10 @@ const extensionDiscoveryInjectable = getInjectable({
|
||||
),
|
||||
|
||||
staticFilesDirectory: di.inject(staticFilesDirectoryInjectable),
|
||||
readJsonFile: di.inject(readJsonFileInjectable),
|
||||
pathExists: di.inject(pathExistsInjectable),
|
||||
watch: di.inject(watchInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,53 +4,29 @@
|
||||
*/
|
||||
|
||||
import type { FSWatcher } from "chokidar";
|
||||
import { watch } from "chokidar";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import { Console } from "console";
|
||||
import * as fse from "fs-extra";
|
||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||
import extensionDiscoveryInjectable from "../extension-discovery/extension-discovery.injectable";
|
||||
import type { ExtensionDiscovery } from "../extension-discovery/extension-discovery";
|
||||
import installExtensionInjectable
|
||||
from "../extension-installer/install-extension/install-extension.injectable";
|
||||
import directoryForUserDataInjectable
|
||||
from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import installExtensionInjectable from "../extension-installer/install-extension/install-extension.injectable";
|
||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import mockFs from "mock-fs";
|
||||
import { delay } from "../../renderer/utils";
|
||||
import { observable, when } from "mobx";
|
||||
import appVersionInjectable from "../../common/vars/app-version.injectable";
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
jest.mock("../../common/ipc");
|
||||
jest.mock("chokidar", () => ({
|
||||
watch: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("fs-extra");
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getVersion: () => "99.99.99",
|
||||
getName: () => "lens",
|
||||
setName: jest.fn(),
|
||||
setPath: jest.fn(),
|
||||
getPath: () => "tmp",
|
||||
getLocale: () => "en",
|
||||
setLoginItemSettings: jest.fn(),
|
||||
},
|
||||
ipcMain: {
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
}));
|
||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||
import watchInjectable from "../../common/fs/watch/watch.injectable";
|
||||
|
||||
console = new Console(process.stdout, process.stderr); // fix mockFS
|
||||
const mockedWatch = watch as jest.MockedFunction<typeof watch>;
|
||||
const mockedFse = fse as jest.Mocked<typeof fse>;
|
||||
|
||||
describe("ExtensionDiscovery", () => {
|
||||
let extensionDiscovery: ExtensionDiscovery;
|
||||
let readJsonFileMock: jest.Mock;
|
||||
let pathExistsMock: jest.Mock;
|
||||
let watchMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
@@ -59,6 +35,15 @@ describe("ExtensionDiscovery", () => {
|
||||
di.override(installExtensionInjectable, () => () => Promise.resolve());
|
||||
di.override(appVersionInjectable, () => "5.0.0");
|
||||
|
||||
readJsonFileMock = jest.fn();
|
||||
di.override(readJsonFileInjectable, () => readJsonFileMock);
|
||||
|
||||
pathExistsMock = jest.fn(() => Promise.resolve(true));
|
||||
di.override(pathExistsInjectable, () => pathExistsMock);
|
||||
|
||||
watchMock = jest.fn();
|
||||
di.override(watchInjectable, () => watchMock);
|
||||
|
||||
mockFs();
|
||||
|
||||
extensionDiscovery = di.inject(extensionDiscoveryInjectable);
|
||||
@@ -72,7 +57,7 @@ describe("ExtensionDiscovery", () => {
|
||||
const letTestFinish = observable.box(false);
|
||||
let addHandler!: (filePath: string) => void;
|
||||
|
||||
mockedFse.readJson.mockImplementation((p) => {
|
||||
readJsonFileMock.mockImplementation((p) => {
|
||||
expect(p).toBe(path.join(os.homedir(), ".k8slens/extensions/my-extension/package.json"));
|
||||
|
||||
return {
|
||||
@@ -84,8 +69,6 @@ describe("ExtensionDiscovery", () => {
|
||||
};
|
||||
});
|
||||
|
||||
mockedFse.pathExists.mockImplementation(() => true);
|
||||
|
||||
const mockWatchInstance = {
|
||||
on: jest.fn((event: string, handler: typeof addHandler) => {
|
||||
if (event === "add") {
|
||||
@@ -96,7 +79,7 @@ describe("ExtensionDiscovery", () => {
|
||||
}),
|
||||
} as unknown as FSWatcher;
|
||||
|
||||
mockedWatch.mockImplementationOnce(() => mockWatchInstance);
|
||||
watchMock.mockImplementationOnce(() => mockWatchInstance);
|
||||
|
||||
// Need to force isLoaded to be true so that the file watching is started
|
||||
extensionDiscovery.isLoaded = true;
|
||||
@@ -139,7 +122,7 @@ describe("ExtensionDiscovery", () => {
|
||||
}),
|
||||
} as unknown as FSWatcher;
|
||||
|
||||
mockedWatch.mockImplementationOnce(() => mockWatchInstance);
|
||||
watchMock.mockImplementationOnce(() => mockWatchInstance);
|
||||
|
||||
// Need to force isLoaded to be true so that the file watching is started
|
||||
extensionDiscovery.isLoaded = true;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { watch } from "chokidar";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { EventEmitter } from "events";
|
||||
import fse from "fs-extra";
|
||||
@@ -12,7 +11,6 @@ import os from "os";
|
||||
import path from "path";
|
||||
import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc";
|
||||
import { isErrnoException, toJS } from "../../common/utils";
|
||||
import logger from "../../main/logger";
|
||||
import type { ExtensionsStore } from "../extensions-store/extensions-store";
|
||||
import type { ExtensionLoader } from "../extension-loader";
|
||||
import type { LensExtensionId, LensExtensionManifest } from "../lens-extension";
|
||||
@@ -21,6 +19,10 @@ import type { ExtensionInstallationStateStore } from "../extension-installation-
|
||||
import type { PackageJson } from "type-fest";
|
||||
import { extensionDiscoveryStateChannel } from "../../common/ipc/extension-handling";
|
||||
import { requestInitialExtensionDiscovery } from "../../renderer/ipc";
|
||||
import type { ReadJson } from "../../common/fs/read-json-file.injectable";
|
||||
import type { Logger } from "../../common/logger";
|
||||
import type { PathExists } from "../../common/fs/path-exists.injectable";
|
||||
import type { Watch } from "../../common/fs/watch/watch.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
extensionLoader: ExtensionLoader;
|
||||
@@ -35,6 +37,10 @@ interface Dependencies {
|
||||
installExtensions: (packageJsonPath: string, packagesJson: PackageJson) => Promise<void>;
|
||||
extensionPackageRootDirectory: string;
|
||||
staticFilesDirectory: string;
|
||||
readJsonFile: ReadJson;
|
||||
pathExists: PathExists;
|
||||
watch: Watch;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export interface InstalledExtension {
|
||||
@@ -155,13 +161,12 @@ export class ExtensionDiscovery {
|
||||
* Dependencies are installed automatically after an extension folder is copied.
|
||||
*/
|
||||
async watchExtensions(): Promise<void> {
|
||||
logger.info(`${logModule} watching extension add/remove in ${this.localFolderPath}`);
|
||||
this.dependencies.logger.info(`${logModule} watching extension add/remove in ${this.localFolderPath}`);
|
||||
|
||||
// Wait until .load() has been called and has been resolved
|
||||
await this.whenLoaded;
|
||||
|
||||
// chokidar works better than fs.watch
|
||||
watch(this.localFolderPath, {
|
||||
this.dependencies.watch(this.localFolderPath, {
|
||||
// For adding and removing symlinks to work, the depth has to be 1.
|
||||
depth: 1,
|
||||
ignoreInitial: true,
|
||||
@@ -206,11 +211,11 @@ export class ExtensionDiscovery {
|
||||
await this.dependencies.installExtension(extension.absolutePath);
|
||||
|
||||
this.extensions.set(extension.id, extension);
|
||||
logger.info(`${logModule} Added extension ${extension.manifest.name}`);
|
||||
this.dependencies.logger.info(`${logModule} Added extension ${extension.manifest.name}`);
|
||||
this.events.emit("add", extension);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`${logModule}: failed to add extension: ${error}`, { error });
|
||||
this.dependencies.logger.error(`${logModule}: failed to add extension: ${error}`, { error });
|
||||
} finally {
|
||||
this.dependencies.extensionInstallationStateStore.clearInstallingFromMain(manifestPath);
|
||||
}
|
||||
@@ -247,13 +252,13 @@ export class ExtensionDiscovery {
|
||||
const lensExtensionId = extension.manifestPath;
|
||||
|
||||
this.extensions.delete(extension.id);
|
||||
logger.info(`${logModule} removed extension ${extensionName}`);
|
||||
this.dependencies.logger.info(`${logModule} removed extension ${extensionName}`);
|
||||
this.events.emit("remove", lensExtensionId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn(`${logModule} extension ${extensionFolderName} not found, can't remove`);
|
||||
this.dependencies.logger.warn(`${logModule} extension ${extensionFolderName} not found, can't remove`);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -275,12 +280,12 @@ export class ExtensionDiscovery {
|
||||
const extension = this.extensions.get(extensionId) ?? this.dependencies.extensionLoader.getExtension(extensionId);
|
||||
|
||||
if (!extension) {
|
||||
return void logger.warn(`${logModule} could not uninstall extension, not found`, { id: extensionId });
|
||||
return void this.dependencies.logger.warn(`${logModule} could not uninstall extension, not found`, { id: extensionId });
|
||||
}
|
||||
|
||||
const { manifest, absolutePath } = extension;
|
||||
|
||||
logger.info(`${logModule} Uninstalling ${manifest.name}`);
|
||||
this.dependencies.logger.info(`${logModule} Uninstalling ${manifest.name}`);
|
||||
|
||||
await this.removeSymlinkByPackageName(manifest.name);
|
||||
|
||||
@@ -296,7 +301,7 @@ export class ExtensionDiscovery {
|
||||
|
||||
this.loadStarted = true;
|
||||
|
||||
logger.info(
|
||||
this.dependencies.logger.info(
|
||||
`${logModule} loading extensions from ${this.dependencies.extensionPackageRootDirectory}`,
|
||||
);
|
||||
|
||||
@@ -358,12 +363,12 @@ export class ExtensionDiscovery {
|
||||
*/
|
||||
protected async getByManifest(manifestPath: string, { isBundled = false } = {}): Promise<InstalledExtension | null> {
|
||||
try {
|
||||
const manifest = await fse.readJson(manifestPath) as LensExtensionManifest;
|
||||
const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest;
|
||||
const id = this.getInstalledManifestPath(manifest.name);
|
||||
const isEnabled = this.dependencies.extensionsStore.isEnabled({ id, isBundled });
|
||||
const extensionDir = path.dirname(manifestPath);
|
||||
const npmPackage = path.join(extensionDir, `${manifest.name}-${manifest.version}.tgz`);
|
||||
const absolutePath = (isProduction && await fse.pathExists(npmPackage)) ? npmPackage : extensionDir;
|
||||
const absolutePath = (isProduction && await this.dependencies.pathExists(npmPackage)) ? npmPackage : extensionDir;
|
||||
const isCompatible = (isBundled && this.dependencies.isCompatibleBundledExtension(manifest)) || this.dependencies.isCompatibleExtension(manifest);
|
||||
|
||||
return {
|
||||
@@ -378,9 +383,9 @@ export class ExtensionDiscovery {
|
||||
} catch (error) {
|
||||
if (isErrnoException(error) && error.code === "ENOTDIR") {
|
||||
// ignore this error, probably from .DS_Store file
|
||||
logger.debug(`${logModule}: failed to load extension manifest through a not-dir-like at ${manifestPath}`);
|
||||
this.dependencies.logger.debug(`${logModule}: failed to load extension manifest through a not-dir-like at ${manifestPath}`);
|
||||
} else {
|
||||
logger.error(`${logModule}: can't load extension manifest at ${manifestPath}: ${error}`);
|
||||
this.dependencies.logger.error(`${logModule}: can't load extension manifest at ${manifestPath}: ${error}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -395,7 +400,7 @@ export class ExtensionDiscovery {
|
||||
const userExtensions = await this.loadFromFolder(this.localFolderPath, bundledExtensions.map((extension) => extension.manifest.name));
|
||||
|
||||
for (const extension of userExtensions) {
|
||||
if ((await fse.pathExists(extension.manifestPath)) === false) {
|
||||
if (!(await this.dependencies.pathExists(extension.manifestPath))) {
|
||||
try {
|
||||
await this.dependencies.installExtension(extension.absolutePath);
|
||||
} catch (error) {
|
||||
@@ -404,7 +409,7 @@ export class ExtensionDiscovery {
|
||||
: String(error || "unknown error");
|
||||
const { name, version } = extension.manifest;
|
||||
|
||||
logger.error(`${logModule}: failed to install user extension ${name}@${version}: ${message}`);
|
||||
this.dependencies.logger.error(`${logModule}: failed to install user extension ${name}@${version}: ${message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,7 +443,7 @@ export class ExtensionDiscovery {
|
||||
extensions.push(extension);
|
||||
}
|
||||
}
|
||||
logger.debug(`${logModule}: ${extensions.length} extensions loaded`, { folderPath, extensions });
|
||||
this.dependencies.logger.debug(`${logModule}: ${extensions.length} extensions loaded`, { folderPath, extensions });
|
||||
|
||||
return extensions;
|
||||
}
|
||||
@@ -473,7 +478,7 @@ export class ExtensionDiscovery {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`${logModule}: ${extensions.length} extensions loaded`, { folderPath, extensions });
|
||||
this.dependencies.logger.debug(`${logModule}: ${extensions.length} extensions loaded`, { folderPath, extensions });
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { ipcRenderer } from "electron";
|
||||
import { ipcMain, ipcRenderer } from "electron";
|
||||
import { isEqual } from "lodash";
|
||||
import type { ObservableMap } from "mobx";
|
||||
import { action, computed, makeObservable, observable, observe, reaction, when } from "mobx";
|
||||
@@ -127,10 +127,10 @@ export class ExtensionLoader {
|
||||
|
||||
@action
|
||||
async init() {
|
||||
if (ipcRenderer) {
|
||||
await this.initRenderer();
|
||||
} else {
|
||||
if (ipcMain) {
|
||||
await this.initMain();
|
||||
} else {
|
||||
await this.initRenderer();
|
||||
}
|
||||
|
||||
await Promise.all([this.whenLoaded]);
|
||||
|
||||
Reference in new issue
Block a user