mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Restructure to monorepo (#6907)
* wip: restructure to monorepo Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * refactor create-release-pr to a package Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * build fixes Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * github workflow fixes Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix typo Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * add webpack-env types to core Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix github workflows Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * refactor/fix integration tests Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * lint fix Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * yarn run dev Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * eslint settings for vscode Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * move templates to right package Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * open-lens build fixes Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * integration test fix Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix nx task dependencies Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * use bash shell for unit tests in test workflow Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix test:unit for windows Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix win-ca webpack error in open-lens Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix win-ca webpack error in open-lens Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * fix build:app on windows Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * remove ELECTRON_BUILDER_EXTRA_ARGS Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * sync src/ from master Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> * remove Makefile from core Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com> Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
0066ca4ea2
commit
2657df2293
3321 files changed
+28699
-11447
No files matched your search
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 { IpcMainEvent } from "electron";
|
||||
import { enlistMessageChannelListenerInjectionToken } from "../../../../common/utils/channel/enlist-message-channel-listener-injection-token";
|
||||
import ipcMainInjectionToken from "../../../../common/ipc/ipc-main-injection-token";
|
||||
|
||||
const enlistMessageChannelListenerInjectable = getInjectable({
|
||||
id: "enlist-message-channel-listener-for-main",
|
||||
|
||||
instantiate: (di) => {
|
||||
const ipcMain = di.inject(ipcMainInjectionToken);
|
||||
|
||||
return ({ channel, handler }) => {
|
||||
const nativeOnCallback = (_: IpcMainEvent, message: unknown) => {
|
||||
handler(message);
|
||||
};
|
||||
|
||||
ipcMain.on(channel.id, nativeOnCallback);
|
||||
|
||||
return () => {
|
||||
ipcMain.off(channel.id, nativeOnCallback);
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: enlistMessageChannelListenerInjectionToken,
|
||||
});
|
||||
|
||||
export default enlistMessageChannelListenerInjectable;
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
||||
import ipcMainInjectable from "../ipc-main/ipc-main.injectable";
|
||||
import type { EnlistMessageChannelListener } from "../../../../common/utils/channel/enlist-message-channel-listener-injection-token";
|
||||
import { enlistMessageChannelListenerInjectionToken } from "../../../../common/utils/channel/enlist-message-channel-listener-injection-token";
|
||||
import type { IpcMain, IpcMainEvent } from "electron";
|
||||
|
||||
describe("enlist message channel listener in main", () => {
|
||||
let enlistMessageChannelListener: EnlistMessageChannelListener;
|
||||
let ipcMainStub: IpcMain;
|
||||
let onMock: jest.Mock;
|
||||
let offMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
onMock = jest.fn();
|
||||
offMock = jest.fn();
|
||||
|
||||
ipcMainStub = {
|
||||
on: onMock,
|
||||
off: offMock,
|
||||
} as unknown as IpcMain;
|
||||
|
||||
di.override(ipcMainInjectable, () => ipcMainStub);
|
||||
|
||||
enlistMessageChannelListener = di.inject(
|
||||
enlistMessageChannelListenerInjectionToken,
|
||||
);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let handlerMock: jest.Mock;
|
||||
let disposer: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
handlerMock = jest.fn();
|
||||
|
||||
disposer = enlistMessageChannelListener({
|
||||
channel: { id: "some-channel-id" },
|
||||
handler: handlerMock,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call handler yet", () => {
|
||||
expect(handlerMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("registers the listener", () => {
|
||||
expect(onMock).toHaveBeenCalledWith(
|
||||
"some-channel-id",
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not de-register the listener yet", () => {
|
||||
expect(offMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when message arrives", () => {
|
||||
beforeEach(() => {
|
||||
onMock.mock.calls[0][1]({} as IpcMainEvent, "some-message");
|
||||
});
|
||||
|
||||
it("calls the handler with the message", () => {
|
||||
expect(handlerMock).toHaveBeenCalledWith("some-message");
|
||||
});
|
||||
|
||||
it("when disposing the listener, de-registers the listener", () => {
|
||||
disposer();
|
||||
|
||||
expect(offMock).toHaveBeenCalledWith("some-channel-id", expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
it("given number as message, when message arrives, calls the handler with the message", () => {
|
||||
onMock.mock.calls[0][1]({} as IpcMainEvent, 42);
|
||||
|
||||
expect(handlerMock).toHaveBeenCalledWith(42);
|
||||
});
|
||||
|
||||
it("given boolean as message, when message arrives, calls the handler with the message", () => {
|
||||
onMock.mock.calls[0][1]({} as IpcMainEvent, true);
|
||||
|
||||
expect(handlerMock).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("given object as message, when message arrives, calls the handler with the message", () => {
|
||||
onMock.mock.calls[0][1]({} as IpcMainEvent, { some: "object" });
|
||||
|
||||
expect(handlerMock).toHaveBeenCalledWith({ some: "object" });
|
||||
});
|
||||
});
|
||||
});
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 { IpcMainInvokeEvent } from "electron";
|
||||
import type { Disposer } from "../../../../common/utils";
|
||||
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
|
||||
import type { RequestChannelListener } from "./listener-tokens";
|
||||
import ipcMainInjectionToken from "../../../../common/ipc/ipc-main-injection-token";
|
||||
|
||||
export type EnlistRequestChannelListener = <TChannel extends RequestChannel<unknown, unknown>>(listener: RequestChannelListener<TChannel>) => Disposer;
|
||||
|
||||
const enlistRequestChannelListenerInjectable = getInjectable({
|
||||
id: "enlist-request-channel-listener-for-main",
|
||||
|
||||
instantiate: (di): EnlistRequestChannelListener => {
|
||||
const ipcMain = di.inject(ipcMainInjectionToken);
|
||||
|
||||
return ({ channel, handler }) => {
|
||||
const nativeHandleCallback = (_: IpcMainInvokeEvent, request: unknown) => handler(request);
|
||||
|
||||
ipcMain.handle(channel.id, nativeHandleCallback);
|
||||
|
||||
return () => {
|
||||
ipcMain.off(channel.id, nativeHandleCallback);
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default enlistRequestChannelListenerInjectable;
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";
|
||||
import ipcMainInjectable from "../ipc-main/ipc-main.injectable";
|
||||
import type { IpcMain, IpcMainInvokeEvent } from "electron";
|
||||
import { getPromiseStatus } from "../../../../common/test-utils/get-promise-status";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
|
||||
import type { EnlistRequestChannelListener } from "./enlist-request-channel-listener.injectable";
|
||||
import enlistRequestChannelListenerInjectable from "./enlist-request-channel-listener.injectable";
|
||||
import type { RequestChannelHandler } from "./listener-tokens";
|
||||
|
||||
type TestRequestChannel = RequestChannel<unknown, unknown>;
|
||||
|
||||
const testRequestChannel: TestRequestChannel = {
|
||||
id: "some-channel-id",
|
||||
};
|
||||
|
||||
describe("enlist request channel listener in main", () => {
|
||||
let enlistRequestChannelListener: EnlistRequestChannelListener;
|
||||
let ipcMainStub: IpcMain;
|
||||
let handleMock: jest.Mock;
|
||||
let offMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
handleMock = jest.fn();
|
||||
offMock = jest.fn();
|
||||
|
||||
ipcMainStub = {
|
||||
handle: handleMock,
|
||||
off: offMock,
|
||||
} as unknown as IpcMain;
|
||||
|
||||
di.override(ipcMainInjectable, () => ipcMainStub);
|
||||
|
||||
enlistRequestChannelListener = di.inject(enlistRequestChannelListenerInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let handlerMock: AsyncFnMock<RequestChannelHandler<TestRequestChannel>>;
|
||||
let disposer: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
handlerMock = asyncFn();
|
||||
|
||||
disposer = enlistRequestChannelListener({
|
||||
channel: testRequestChannel,
|
||||
handler: handlerMock,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call handler yet", () => {
|
||||
expect(handlerMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("registers the listener", () => {
|
||||
expect(handleMock).toHaveBeenCalledWith(
|
||||
"some-channel-id",
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not de-register the listener yet", () => {
|
||||
expect(offMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when request arrives", () => {
|
||||
let actualPromise: Promise<any>;
|
||||
|
||||
beforeEach(() => {
|
||||
actualPromise = handleMock.mock.calls[0][1](
|
||||
{} as IpcMainInvokeEvent,
|
||||
"some-request",
|
||||
);
|
||||
});
|
||||
|
||||
it("calls the handler with the request", () => {
|
||||
expect(handlerMock).toHaveBeenCalledWith("some-request");
|
||||
});
|
||||
|
||||
it("does not resolve yet", async () => {
|
||||
const promiseStatus = await getPromiseStatus(actualPromise);
|
||||
|
||||
expect(promiseStatus.fulfilled).toBe(false);
|
||||
});
|
||||
|
||||
describe("when handler resolves with response, listener resolves with the response", () => {
|
||||
beforeEach(async () => {
|
||||
await handlerMock.resolve("some-response");
|
||||
});
|
||||
|
||||
it("resolves with the response", async () => {
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toBe("some-response");
|
||||
});
|
||||
|
||||
it("when disposing the listener, de-registers the listener", () => {
|
||||
disposer();
|
||||
|
||||
expect(offMock).toHaveBeenCalledWith("some-channel-id", expect.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
it("given number as response, when handler resolves with response, listener resolves with stringified response", async () => {
|
||||
await handlerMock.resolve(42);
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toBe(42);
|
||||
});
|
||||
|
||||
it("given boolean as response, when handler resolves with response, listener resolves with stringified response", async () => {
|
||||
await handlerMock.resolve(true);
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toBe(true);
|
||||
});
|
||||
|
||||
it("given object as response, when handler resolves with response, listener resolves with response", async () => {
|
||||
await handlerMock.resolve({ some: "object" });
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual({ some: "object" });
|
||||
});
|
||||
});
|
||||
|
||||
it("given number as request, when request arrives, calls the handler with the request", () => {
|
||||
handleMock.mock.calls[0][1]({} as IpcMainInvokeEvent, 42);
|
||||
|
||||
expect(handlerMock).toHaveBeenCalledWith(42);
|
||||
});
|
||||
|
||||
it("given boolean as request, when request arrives, calls the handler with the request", () => {
|
||||
handleMock.mock.calls[0][1]({} as IpcMainInvokeEvent, true);
|
||||
|
||||
expect(handlerMock).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("given object as request, when request arrives, calls the handler with the request", () => {
|
||||
handleMock.mock.calls[0][1]({} as IpcMainInvokeEvent, { some: "object" });
|
||||
|
||||
expect(handlerMock).toHaveBeenCalledWith({ some: "object" });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { DiContainerForInjection } from "@ogre-tools/injectable";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
|
||||
|
||||
export type RequestChannelHandler<Channel> = Channel extends RequestChannel<infer Request, infer Response>
|
||||
? (req: Request) => Promise<Response> | Response
|
||||
: never;
|
||||
|
||||
export interface RequestChannelListener<Channel> {
|
||||
channel: Channel;
|
||||
handler: RequestChannelHandler<Channel>;
|
||||
}
|
||||
|
||||
|
||||
export const requestChannelListenerInjectionToken = getInjectionToken<RequestChannelListener<RequestChannel<unknown, unknown>>>( {
|
||||
id: "request-channel-listener",
|
||||
});
|
||||
|
||||
export interface GetRequestChannelListenerInjectableInfo<
|
||||
Channel extends RequestChannel<Request, Response>,
|
||||
Request,
|
||||
Response,
|
||||
> {
|
||||
channel: Channel;
|
||||
handler: (di: DiContainerForInjection) => RequestChannelHandler<Channel>;
|
||||
}
|
||||
|
||||
export function getRequestChannelListenerInjectable<
|
||||
Channel extends RequestChannel<Request, Response>,
|
||||
Request,
|
||||
Response,
|
||||
>(info: GetRequestChannelListenerInjectableInfo<Channel, Request, Response>) {
|
||||
return getInjectable({
|
||||
id: `${info.channel.id}-listener`,
|
||||
instantiate: (di) => ({
|
||||
channel: info.channel,
|
||||
handler: info.handler(di),
|
||||
}),
|
||||
injectionToken: requestChannelListenerInjectionToken,
|
||||
});
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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 { disposer } from "../../../../common/utils";
|
||||
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
|
||||
import { getStartableStoppable } from "../../../../common/utils/get-startable-stoppable";
|
||||
import enlistRequestChannelListenerInjectable from "./enlist-request-channel-listener.injectable";
|
||||
import { requestChannelListenerInjectionToken } from "./listener-tokens";
|
||||
|
||||
const listeningOnRequestChannelsInjectable = getInjectable({
|
||||
id: "listening-on-request-channels",
|
||||
instantiate: (di) => {
|
||||
const enlistRequestChannelListener = di.inject(enlistRequestChannelListenerInjectable);
|
||||
const requestChannelListeners = di.injectMany(requestChannelListenerInjectionToken);
|
||||
|
||||
return getStartableStoppable("listening-on-request-channels", () => {
|
||||
const seenChannels = new Set<RequestChannel<unknown, unknown>>();
|
||||
|
||||
for (const listener of requestChannelListeners) {
|
||||
if (seenChannels.has(listener.channel)) {
|
||||
throw new Error(`Tried to register a multiple channel handlers for "${listener.channel.id}", only one handler is supported for a request channel.`);
|
||||
}
|
||||
|
||||
seenChannels.add(listener.channel);
|
||||
}
|
||||
|
||||
return disposer(requestChannelListeners.map(enlistRequestChannelListener));
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default listeningOnRequestChannelsInjectable;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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 { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import listeningOnMessageChannelsInjectable from "../../../../common/utils/channel/listening-on-message-channels.injectable";
|
||||
import listeningOnRequestChannelsInjectable from "./listening-on-request-channels.injectable";
|
||||
|
||||
const startListeningOnChannelsInjectable = getInjectable({
|
||||
id: "start-listening-on-channels-main",
|
||||
|
||||
instantiate: (di) => {
|
||||
const listeningOnMessageChannels = di.inject(listeningOnMessageChannelsInjectable);
|
||||
const listeningOnRequestChannels = di.inject(listeningOnRequestChannelsInjectable);
|
||||
|
||||
return {
|
||||
id: "start-listening-on-channels-main",
|
||||
run: () => {
|
||||
listeningOnMessageChannels.start();
|
||||
listeningOnRequestChannels.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default startListeningOnChannelsInjectable;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { IpcMain } from "electron";
|
||||
import { getGlobalOverride } from "../../../../common/test-utils/get-global-override";
|
||||
import ipcMainInjectable from "./ipc-main.injectable";
|
||||
|
||||
export default getGlobalOverride(ipcMainInjectable, () => ({
|
||||
handle: () => {},
|
||||
on: () => {},
|
||||
off: () => {},
|
||||
}) as unknown as IpcMain);
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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 { ipcMain } from "electron";
|
||||
import ipcMainInjectionToken from "../../../../common/ipc/ipc-main-injection-token";
|
||||
|
||||
const ipcMainInjectable = getInjectable({
|
||||
id: "ipc-main",
|
||||
instantiate: () => ipcMain,
|
||||
causesSideEffects: true,
|
||||
injectionToken: ipcMainInjectionToken,
|
||||
});
|
||||
|
||||
export default ipcMainInjectable;
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 { SendMessageToChannel } from "../../../common/utils/channel/message-to-channel-injection-token";
|
||||
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
|
||||
import getVisibleWindowsInjectable from "../../start-main-application/lens-window/get-visible-windows.injectable";
|
||||
import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
|
||||
|
||||
const messageToChannelInjectable = getInjectable({
|
||||
id: "message-to-channel",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getVisibleWindows = di.inject(getVisibleWindowsInjectable);
|
||||
const clusterFrames = di.inject(clusterFramesInjectable);
|
||||
|
||||
return ((channel, data) => {
|
||||
for (const window of getVisibleWindows()) {
|
||||
window.send({ channel: channel.id, data });
|
||||
|
||||
clusterFrames.forEach(frameInfo => {
|
||||
window.send({ channel: channel.id, data, frameInfo });
|
||||
});
|
||||
}
|
||||
}) as SendMessageToChannel;
|
||||
},
|
||||
|
||||
injectionToken: sendMessageToChannelInjectionToken,
|
||||
});
|
||||
|
||||
export default messageToChannelInjectable;
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import getVisibleWindowsInjectable from "../../start-main-application/lens-window/get-visible-windows.injectable";
|
||||
import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
|
||||
import type { MessageChannel } from "../../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { ClusterFrameInfo } from "../../../common/cluster-frames";
|
||||
|
||||
describe("message-to-channel", () => {
|
||||
let di: DiContainer;
|
||||
let sendToWindowMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
sendToWindowMock = jest.fn();
|
||||
|
||||
di.override(getVisibleWindowsInjectable, () => () => [
|
||||
{
|
||||
id: "some-window",
|
||||
send: sendToWindowMock,
|
||||
show: () => {},
|
||||
reload: () => {},
|
||||
isStarting: false,
|
||||
start: async () => {},
|
||||
close: () => {},
|
||||
isVisible: true,
|
||||
},
|
||||
|
||||
{
|
||||
id: "some-other-window",
|
||||
send: sendToWindowMock,
|
||||
show: () => {},
|
||||
reload: () => {},
|
||||
isStarting: false,
|
||||
start: async () => {},
|
||||
close: () => {},
|
||||
isVisible: true,
|
||||
},
|
||||
]);
|
||||
|
||||
di.override(
|
||||
clusterFramesInjectable,
|
||||
() =>
|
||||
new Map<string, ClusterFrameInfo>([
|
||||
[
|
||||
"some-cluster-id",
|
||||
{ frameId: 42, processId: 84 },
|
||||
],
|
||||
[
|
||||
"some-other-cluster-id",
|
||||
{ frameId: 126, processId: 168 },
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
describe("when sending message", () => {
|
||||
beforeEach(() => {
|
||||
const sendMessageToChannel = di.inject(
|
||||
sendMessageToChannelInjectionToken,
|
||||
);
|
||||
|
||||
sendMessageToChannel(someChannel, 42);
|
||||
});
|
||||
|
||||
it("sends to each window and cluster frames", () => {
|
||||
expect(sendToWindowMock.mock.calls).toEqual([
|
||||
[{ channel: "some-channel-id", data: 42 }],
|
||||
|
||||
[
|
||||
{
|
||||
channel: "some-channel-id",
|
||||
data: 42,
|
||||
frameInfo: { frameId: 42, processId: 84 },
|
||||
},
|
||||
],
|
||||
|
||||
[
|
||||
{
|
||||
channel: "some-channel-id",
|
||||
data: 42,
|
||||
frameInfo: { frameId: 126, processId: 168 },
|
||||
},
|
||||
],
|
||||
|
||||
[{ channel: "some-channel-id", data: 42 }],
|
||||
|
||||
[
|
||||
{
|
||||
channel: "some-channel-id",
|
||||
data: 42,
|
||||
frameInfo: { frameId: 42, processId: 84 },
|
||||
},
|
||||
],
|
||||
|
||||
[
|
||||
{
|
||||
channel: "some-channel-id",
|
||||
data: 42,
|
||||
frameInfo: { frameId: 126, processId: 168 },
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const someChannel: MessageChannel<number> = {
|
||||
id: "some-channel-id",
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
const anyKubeconfig = /^kubeconfig$/i;
|
||||
|
||||
/**
|
||||
* This function deletes all keys of the form /^kubeconfig$/i, returning a new
|
||||
* object.
|
||||
*
|
||||
* This is needed because `kubectl` checks for other version of kubeconfig
|
||||
* before KUBECONFIG and we only set KUBECONFIG.
|
||||
* @param env The current copy of env
|
||||
*/
|
||||
export function clearKubeconfigEnvVars(env: Partial<Record<string, string>>): Partial<Record<string, string>> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(env)
|
||||
.filter(([key]) => anyKubeconfig.exec(key) === null),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "../../common/test-utils/get-global-override";
|
||||
import commandLineArgumentsInjectable from "./command-line-arguments.injectable";
|
||||
|
||||
export default getGlobalOverride(commandLineArgumentsInjectable, () => []);
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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";
|
||||
|
||||
const commandLineArgumentsInjectable = getInjectable({
|
||||
id: "command-line-arguments",
|
||||
instantiate: () => process.argv,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default commandLineArgumentsInjectable;
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { Readable } from "stream";
|
||||
import URLParse from "url-parse";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
|
||||
export interface GetPortFromStreamArgs {
|
||||
/**
|
||||
* Should be case insensitive
|
||||
* Must have a named matching group called `address`
|
||||
*/
|
||||
lineRegex: {
|
||||
match: (line: string) => {
|
||||
matched: boolean;
|
||||
groups?: {
|
||||
address?: string;
|
||||
};
|
||||
raw?: RegExpExecArray;
|
||||
};
|
||||
rawMatcher: string;
|
||||
};
|
||||
/**
|
||||
* Called when the port is found
|
||||
*/
|
||||
onFind?: () => void;
|
||||
/**
|
||||
* Timeout for how long to wait for the port.
|
||||
* Default: 15s
|
||||
*/
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export type GetPortFromStream = (stream: Readable, args: GetPortFromStreamArgs) => Promise<number>;
|
||||
|
||||
const getPortFromStreamInjectable = getInjectable({
|
||||
id: "get-port-from-stream",
|
||||
instantiate: (di): GetPortFromStream => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return (stream, args) => {
|
||||
const logLines: string[] = [];
|
||||
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
const handler = (data: unknown) => {
|
||||
const logItem = String(data);
|
||||
const match = args.lineRegex.match(logItem);
|
||||
|
||||
logLines.push(logItem);
|
||||
|
||||
if (match.matched) {
|
||||
// use unknown protocol so that there is no default port
|
||||
const addr = new URLParse(`s://${match.groups?.address?.trim()}`);
|
||||
|
||||
args.onFind?.();
|
||||
stream.off("data", handler);
|
||||
clearTimeout(timeoutID);
|
||||
resolve(+addr.port);
|
||||
}
|
||||
};
|
||||
const timeoutID = setTimeout(() => {
|
||||
stream.off("data", handler);
|
||||
logger.warn(`[getPortFrom]: failed to retrieve port via ${args.lineRegex.rawMatcher}`, logLines);
|
||||
reject(new Error("failed to retrieve port from stream"));
|
||||
}, args.timeout ?? 15000);
|
||||
|
||||
stream.on("data", handler);
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default getPortFromStreamInjectable;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type http from "http";
|
||||
|
||||
/**
|
||||
* Respond to a HTTP request with a body of JSON data
|
||||
* @param res The HTTP response to write data to
|
||||
* @param content The data or its JSON stringified version of it
|
||||
* @param status [200] The status code to respond with
|
||||
*/
|
||||
export function respondJson(res: http.ServerResponse, content: Object | string, status = 200) {
|
||||
const normalizedContent = typeof content === "object"
|
||||
? JSON.stringify(content)
|
||||
: content;
|
||||
|
||||
respond(res, normalizedContent, "application/json", status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to a HTTP request with a body of plain text data
|
||||
* @param res The HTTP response to write data to
|
||||
* @param content The string data to respond with
|
||||
* @param status [200] The status code to respond with
|
||||
*/
|
||||
export function respondText(res: http.ServerResponse, content: string, status = 200) {
|
||||
respond(res, content, "text/plain", status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to a HTTP request with a body of plain text data
|
||||
* @param res The HTTP response to write data to
|
||||
* @param content The string data to respond with
|
||||
* @param contentType The HTTP Content-Type header value
|
||||
* @param status [200] The status code to respond with
|
||||
*/
|
||||
export function respond(res: http.ServerResponse, content: string, contentType: string, status = 200) {
|
||||
res.setHeader("Content-Type", contentType);
|
||||
res.statusCode = status;
|
||||
res.end(content);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
export function getBoolean(query: URLSearchParams, key: string): boolean {
|
||||
const value = query.get(key);
|
||||
|
||||
switch (value?.toLowerCase()) {
|
||||
case "false":
|
||||
case "f":
|
||||
case "0":
|
||||
case null:
|
||||
case undefined:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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 electron from "electron";
|
||||
|
||||
const electronInjectable = getInjectable({
|
||||
id: "electron",
|
||||
instantiate: () => electron,
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default electronInjectable;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { resolveSystemProxyChannel } from "../../../common/utils/resolve-system-proxy/resolve-system-proxy-channel";
|
||||
import { getRequestChannelListenerInjectable } from "../channel/channel-listeners/listener-tokens";
|
||||
import resolveSystemProxyInjectable from "./resolve-system-proxy.injectable";
|
||||
|
||||
const resolveSystemProxyChannelResponderInjectable = getRequestChannelListenerInjectable({
|
||||
channel: resolveSystemProxyChannel,
|
||||
handler: (di) => di.inject(resolveSystemProxyInjectable),
|
||||
});
|
||||
|
||||
export default resolveSystemProxyChannelResponderInjectable;
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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 electronInjectable from "./electron.injectable";
|
||||
import withErrorLoggingInjectable from "../../../common/utils/with-error-logging/with-error-logging.injectable";
|
||||
|
||||
const resolveSystemProxyFromElectronInjectable = getInjectable({
|
||||
id: "resolve-system-proxy-from-electron",
|
||||
|
||||
instantiate: (di) => {
|
||||
const electron = di.inject(electronInjectable);
|
||||
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||
|
||||
const withErrorLogging = withErrorLoggingFor(() => "Error resolving proxy");
|
||||
|
||||
return withErrorLogging(async (url: string) => {
|
||||
const webContent = electron.webContents
|
||||
.getAllWebContents()
|
||||
.find((x) => !x.isDestroyed());
|
||||
|
||||
if (!webContent) {
|
||||
throw new Error(`Tried to resolve proxy for "${url}", but no browser window was available`);
|
||||
}
|
||||
|
||||
return await webContent.session.resolveProxy(url);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default resolveSystemProxyFromElectronInjectable;
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import resolveSystemProxyFromElectronInjectable from "./resolve-system-proxy-from-electron.injectable";
|
||||
import electronInjectable from "./electron.injectable";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import type electron from "electron";
|
||||
import { getPromiseStatus } from "../../../common/test-utils/get-promise-status";
|
||||
import logErrorInjectable from "../../../common/log-error.injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
|
||||
describe("technical: resolve-system-proxy-from-electron", () => {
|
||||
let resolveSystemProxyMock: AsyncFnMock<(url: string) => Promise<string>>;
|
||||
let logErrorMock: jest.Mock;
|
||||
let di: DiContainer;
|
||||
let actualPromise: Promise<string>;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
logErrorMock = jest.fn();
|
||||
di.override(logErrorInjectable, () => logErrorMock);
|
||||
});
|
||||
|
||||
describe("given there are non-destroyed Lens windows, when called with URL", () => {
|
||||
beforeEach(() => {
|
||||
resolveSystemProxyMock = asyncFn();
|
||||
|
||||
di.override(
|
||||
electronInjectable,
|
||||
|
||||
() =>
|
||||
({
|
||||
webContents: {
|
||||
getAllWebContents: () => [
|
||||
{
|
||||
isDestroyed: () => true,
|
||||
|
||||
session: {
|
||||
resolveProxy: () => {
|
||||
throw new Error("should never come here");
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
isDestroyed: () => false,
|
||||
session: { resolveProxy: resolveSystemProxyMock },
|
||||
},
|
||||
|
||||
{
|
||||
isDestroyed: () => false,
|
||||
|
||||
session: {
|
||||
resolveProxy: () => {
|
||||
throw new Error("should never come here");
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as unknown as typeof electron),
|
||||
);
|
||||
|
||||
const resolveSystemProxyFromElectron = di.inject(
|
||||
resolveSystemProxyFromElectronInjectable,
|
||||
);
|
||||
|
||||
actualPromise = resolveSystemProxyFromElectron("some-url");
|
||||
});
|
||||
|
||||
it("calls to resolve proxy from the first window", () => {
|
||||
expect(resolveSystemProxyMock).toHaveBeenCalledWith("some-url");
|
||||
});
|
||||
|
||||
it("does not resolve yet", async () => {
|
||||
const promiseStatus = await getPromiseStatus(actualPromise);
|
||||
|
||||
expect(promiseStatus.fulfilled).toBe(false);
|
||||
});
|
||||
|
||||
it("when call for proxy, resolves with the proxy", async () => {
|
||||
resolveSystemProxyMock.resolve("some-proxy");
|
||||
|
||||
expect(await actualPromise).toBe("some-proxy");
|
||||
});
|
||||
});
|
||||
|
||||
describe("given there are only destroyed Lens windows, when called with URL", () => {
|
||||
let error: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
di.override(
|
||||
electronInjectable,
|
||||
() =>
|
||||
({
|
||||
webContents: {
|
||||
getAllWebContents: () => [
|
||||
{
|
||||
isDestroyed: () => true,
|
||||
|
||||
session: {
|
||||
resolveProxy: () => {
|
||||
throw new Error("should never come here");
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as unknown as typeof electron),
|
||||
);
|
||||
|
||||
resolveSystemProxyMock = asyncFn();
|
||||
|
||||
const resolveSystemProxyFromElectron = di.inject(
|
||||
resolveSystemProxyFromElectronInjectable,
|
||||
);
|
||||
|
||||
try {
|
||||
await resolveSystemProxyFromElectron("some-url");
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
});
|
||||
|
||||
it("throws error", () => {
|
||||
expect(error.message).toBe('Tried to resolve proxy for "some-url", but no browser window was available');
|
||||
});
|
||||
|
||||
it("logs error", () => {
|
||||
expect(logErrorMock).toHaveBeenCalledWith("Error resolving proxy", error);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 { resolveSystemProxyInjectionToken } from "../../../common/utils/resolve-system-proxy/resolve-system-proxy-injection-token";
|
||||
import resolveSystemProxyFromElectronInjectable from "./resolve-system-proxy-from-electron.injectable";
|
||||
|
||||
const resolveSystemProxyInjectable = getInjectable({
|
||||
id: "resolve-system-proxy-for-main",
|
||||
|
||||
instantiate: (di) => {
|
||||
const resolveSystemProxyFromElectron = di.inject(resolveSystemProxyFromElectronInjectable);
|
||||
|
||||
return (url) => resolveSystemProxyFromElectron(url);
|
||||
},
|
||||
|
||||
injectionToken: resolveSystemProxyInjectionToken,
|
||||
});
|
||||
|
||||
export default resolveSystemProxyInjectable;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { syncBoxInitialValueChannel } from "../../../common/utils/sync-box/channels";
|
||||
import { syncBoxInjectionToken } from "../../../common/utils/sync-box/sync-box-injection-token";
|
||||
import { getRequestChannelListenerInjectable } from "../channel/channel-listeners/listener-tokens";
|
||||
|
||||
const syncBoxInitialValueChannelListenerInjectable = getRequestChannelListenerInjectable({
|
||||
channel: syncBoxInitialValueChannel,
|
||||
handler: (di) => {
|
||||
const syncBoxes = di.injectMany(syncBoxInjectionToken);
|
||||
|
||||
return () => syncBoxes.map((box) => ({
|
||||
id: box.id,
|
||||
value: box.value.get(),
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
export default syncBoxInitialValueChannelListenerInjectable;
|
||||
Reference in new issue
Block a user