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
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||
import { broadcastMessage } from "../ipc";
|
||||
import type { ClusterContextHandler } from "../../main/context-handler/context-handler";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { HttpError } from "@kubernetes/client-node";
|
||||
@@ -25,6 +24,7 @@ import type { CanI } from "./authorization-review.injectable";
|
||||
import type { ListNamespaces } from "./list-namespaces.injectable";
|
||||
import assert from "assert";
|
||||
import type { Logger } from "../logger";
|
||||
import type { BroadcastMessage } from "../ipc/broadcast-message.injectable";
|
||||
|
||||
export interface ClusterDependencies {
|
||||
readonly directoryForKubeConfigs: string;
|
||||
@@ -36,6 +36,7 @@ export interface ClusterDependencies {
|
||||
createAuthorizationReview: (config: KubeConfig) => CanI;
|
||||
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
||||
createVersionDetector: (cluster: Cluster) => VersionDetector;
|
||||
broadcastMessage: BroadcastMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +603,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
*/
|
||||
pushState(state = this.getState()) {
|
||||
this.dependencies.logger.silly(`[CLUSTER]: push-state`, state);
|
||||
broadcastMessage("cluster:state", this.id, state);
|
||||
this.dependencies.broadcastMessage("cluster:state", this.id, state);
|
||||
}
|
||||
|
||||
// get cluster system meta, e.g. use in "logger"
|
||||
@@ -625,7 +626,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
const update: KubeAuthUpdate = { message, isError };
|
||||
|
||||
this.dependencies.logger.debug(`[CLUSTER]: broadcasting connection update`, { ...update, meta: this.getMeta() });
|
||||
broadcastMessage(`cluster:${this.id}:connection-update`, update);
|
||||
this.dependencies.broadcastMessage(`cluster:${this.id}:connection-update`, update);
|
||||
}
|
||||
|
||||
protected async getAllowedNamespaces(proxyConfig: KubeConfig) {
|
||||
@@ -645,7 +646,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
const { response } = error as HttpError & { response: Response };
|
||||
|
||||
this.dependencies.logger.info("[CLUSTER]: listing namespaces is forbidden, broadcasting", { clusterId: this.id, error: response.body });
|
||||
broadcastMessage(clusterListNamespaceForbiddenChannel, this.id);
|
||||
this.dependencies.broadcastMessage(clusterListNamespaceForbiddenChannel, this.id);
|
||||
}
|
||||
|
||||
return namespaceList;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import statInjectable from "./stat.injectable";
|
||||
import { getGlobalOverride } from "../../test-utils/get-global-override";
|
||||
|
||||
export default getGlobalOverride(statInjectable, () => () => {
|
||||
throw new Error("Tried to call stat without explicit override");
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import fsInjectable from "../fs.injectable";
|
||||
|
||||
const statInjectable = getInjectable({
|
||||
id: "stat",
|
||||
|
||||
instantiate: (di) => di.inject(fsInjectable).stat,
|
||||
});
|
||||
|
||||
export default statInjectable;
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AsyncResult } from "../utils/async-result";
|
||||
import { isErrnoException } from "../utils";
|
||||
import type { Stats } from "fs-extra";
|
||||
import { lowerFirst } from "lodash/fp";
|
||||
import statInjectable from "./stat/stat.injectable";
|
||||
|
||||
export type ValidateDirectory = (path: string) => Promise<AsyncResult<undefined>>;
|
||||
|
||||
function getUserReadableFileType(stats: Stats): string {
|
||||
if (stats.isFile()) {
|
||||
return "a file";
|
||||
}
|
||||
|
||||
if (stats.isFIFO()) {
|
||||
return "a pipe";
|
||||
}
|
||||
|
||||
if (stats.isSocket()) {
|
||||
return "a socket";
|
||||
}
|
||||
|
||||
if (stats.isBlockDevice()) {
|
||||
return "a block device";
|
||||
}
|
||||
|
||||
if (stats.isCharacterDevice()) {
|
||||
return "a character device";
|
||||
}
|
||||
|
||||
return "an unknown file type";
|
||||
}
|
||||
|
||||
const validateDirectoryInjectable = getInjectable({
|
||||
id: "validate-directory",
|
||||
|
||||
instantiate: (di): ValidateDirectory => {
|
||||
const stat = di.inject(statInjectable);
|
||||
|
||||
return async (path) => {
|
||||
try {
|
||||
const stats = await stat(path);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return { callWasSuccessful: true, response: undefined };
|
||||
}
|
||||
|
||||
return { callWasSuccessful: false, error: `the provided path is ${getUserReadableFileType(stats)} and not a directory.` };
|
||||
} catch (error) {
|
||||
if (!isErrnoException(error)) {
|
||||
return { callWasSuccessful: false, error: "of an unknown error, please try again." };
|
||||
}
|
||||
|
||||
const humanReadableErrors: Record<string, string> = {
|
||||
ENOENT: "the provided path does not exist.",
|
||||
EACCES: "search permissions is denied for one of the directories in the prefix of the provided path.",
|
||||
ELOOP: "the provided path is a sym-link which points to a chain of sym-links that is too long to resolve. Perhaps it is cyclic.",
|
||||
ENAMETOOLONG: "the pathname is too long to be used.",
|
||||
ENOTDIR: "a prefix of the provided path is not a directory.",
|
||||
};
|
||||
|
||||
const humanReadableError = error.code
|
||||
? humanReadableErrors[error.code]
|
||||
: lowerFirst(String(error));
|
||||
|
||||
return { callWasSuccessful: false, error: humanReadableError };
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default validateDirectoryInjectable;
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getGlobalOverride } from "../../test-utils/get-global-override";
|
||||
import watchInjectable from "./watch.injectable";
|
||||
|
||||
export default getGlobalOverride(watchInjectable, () => () => {
|
||||
throw new Error("Tried to call file system watch without explicit override");
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { FSWatcher, WatchOptions } from "chokidar";
|
||||
import { watch } from "chokidar";
|
||||
|
||||
export type Watch = (path: string, options?: WatchOptions) => FSWatcher;
|
||||
|
||||
// TODO: Introduce wrapper to allow simpler API
|
||||
const watchInjectable = getInjectable({
|
||||
id: "watch",
|
||||
instantiate: (): Watch => watch,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default watchInjectable;
|
||||
@@ -5,9 +5,11 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { broadcastMessage } from "./ipc";
|
||||
|
||||
export type BroadcastMessage = (channel: string, ...args: any[]) => Promise<void>;
|
||||
|
||||
const broadcastMessageInjectable = getInjectable({
|
||||
id: "broadcast-message",
|
||||
instantiate: () => broadcastMessage,
|
||||
instantiate: (): BroadcastMessage => broadcastMessage,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -13,10 +13,17 @@ import logger from "../../main/logger";
|
||||
import type { ClusterFrameInfo } from "../cluster-frames";
|
||||
import { clusterFrameMap } from "../cluster-frames";
|
||||
import type { Disposer } from "../utils";
|
||||
import ipcMainInjectable from "../../main/utils/channel/ipc-main/ipc-main.injectable";
|
||||
import { getLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable";
|
||||
|
||||
export const broadcastMainChannel = "ipc:broadcast-main";
|
||||
|
||||
export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any) {
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
|
||||
const ipcMain = di.inject(ipcMainInjectable);
|
||||
|
||||
ipcMain.handle(channel, async (event, ...args) => {
|
||||
return sanitizePayload(await listener(event, ...args));
|
||||
});
|
||||
@@ -78,12 +85,20 @@ export async function broadcastMessage(channel: string, ...args: any[]): Promise
|
||||
}
|
||||
|
||||
export function ipcMainOn(channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => any): Disposer {
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
|
||||
const ipcMain = di.inject(ipcMainInjectable);
|
||||
|
||||
ipcMain.on(channel, listener);
|
||||
|
||||
return () => ipcMain.off(channel, listener);
|
||||
}
|
||||
|
||||
export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer {
|
||||
const di = getLegacyGlobalDiForExtensionApi();
|
||||
|
||||
const ipcRenderer = di.inject(ipcRendererInjectable);
|
||||
|
||||
ipcRenderer.on(channel, listener);
|
||||
|
||||
return () => ipcRenderer.off(channel, listener);
|
||||
|
||||
@@ -8,7 +8,6 @@ import { matchPath } from "react-router";
|
||||
import { countBy } from "lodash";
|
||||
import { isDefined, iter } from "../utils";
|
||||
import { pathToRegexp } from "path-to-regexp";
|
||||
import logger from "../../main/logger";
|
||||
import type Url from "url-parse";
|
||||
import { RoutingError, RoutingErrorType } from "./error";
|
||||
import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store";
|
||||
@@ -17,6 +16,7 @@ import type { LensExtension } from "../../extensions/lens-extension";
|
||||
import type { RouteHandler, RouteParams } from "../../extensions/registries/protocol-handler";
|
||||
import { when } from "mobx";
|
||||
import { ipcRenderer } from "electron";
|
||||
import type { Logger } from "../logger";
|
||||
|
||||
// IPC channel for protocol actions. Main broadcasts the open-url events to this channel.
|
||||
export const ProtocolHandlerIpcPrefix = "protocol-handler";
|
||||
@@ -66,6 +66,7 @@ export function foldAttemptResults(mainAttempt: RouteAttempt, rendererAttempt: R
|
||||
export interface LensProtocolRouterDependencies {
|
||||
readonly extensionLoader: ExtensionLoader;
|
||||
readonly extensionsStore: ExtensionsStore;
|
||||
readonly logger: Logger;
|
||||
}
|
||||
|
||||
export abstract class LensProtocolRouter {
|
||||
@@ -130,7 +131,7 @@ export abstract class LensProtocolRouter {
|
||||
data.extensionName = extensionName;
|
||||
}
|
||||
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: No handler found`, data);
|
||||
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: No handler found`, data);
|
||||
|
||||
return RouteAttempt.MISSING;
|
||||
}
|
||||
@@ -183,7 +184,7 @@ export abstract class LensProtocolRouter {
|
||||
timeout: 5_000,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.info(
|
||||
this.dependencies.logger.info(
|
||||
`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not installed (${error})`,
|
||||
);
|
||||
|
||||
@@ -193,18 +194,18 @@ export abstract class LensProtocolRouter {
|
||||
const extension = extensionLoader.getInstanceByName(name);
|
||||
|
||||
if (!extension) {
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but does not have a class for ${ipcRenderer ? "renderer" : "main"}`);
|
||||
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but does not have a class for ${ipcRenderer ? "renderer" : "main"}`);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
if (!this.dependencies.extensionsStore.isEnabled(extension)) {
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`);
|
||||
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched`);
|
||||
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched`);
|
||||
|
||||
return extension;
|
||||
}
|
||||
@@ -250,7 +251,7 @@ export abstract class LensProtocolRouter {
|
||||
*/
|
||||
public addInternalHandler(urlSchema: string, handler: RouteHandler): this {
|
||||
pathToRegexp(urlSchema); // verify now that the schema is valid
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: internal registering ${urlSchema}`);
|
||||
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: internal registering ${urlSchema}`);
|
||||
this.internalRoutes.set(urlSchema, handler);
|
||||
|
||||
return this;
|
||||
|
||||
Reference in new issue
Block a user