diff --git a/package.json b/package.json index f8434e6658..4d08fa57c5 100644 --- a/package.json +++ b/package.json @@ -304,6 +304,7 @@ }, "devDependencies": { "@async-fn/jest": "1.6.4", + "@hapi/shot": "^6.0.0", "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.60", @@ -406,7 +407,6 @@ "memfs": "^3.4.13", "memorystream": "^0.3.1", "mini-css-extract-plugin": "^2.7.2", - "mock-http": "^1.1.0", "monaco-editor": "^0.29.1", "monaco-editor-webpack-plugin": "^5.0.0", "node-gyp": "^8.3.0", diff --git a/src/common/test-utils/use-fake-time.ts b/src/common/test-utils/use-fake-time.ts index e455984861..7d57ef648d 100644 --- a/src/common/test-utils/use-fake-time.ts +++ b/src/common/test-utils/use-fake-time.ts @@ -19,7 +19,13 @@ export const advanceFakeTime = (milliseconds: number) => { export const testUsingFakeTime = (dateTime = "2015-10-21T07:28:00Z") => { usingFakeTime = true; - jest.useFakeTimers(); + jest.useFakeTimers({ + doNotFake: [ + "nextTick", + "setImmediate", + "clearImmediate", + ], + }); jest.setSystemTime(new Date(dateTime)); }; diff --git a/src/features/quitting-and-restarting-the-app/opening-application-window-using-tray.test.ts b/src/features/quitting-and-restarting-the-app/opening-application-window-using-tray.test.ts index 23b8d55992..aa0e5061ef 100644 --- a/src/features/quitting-and-restarting-the-app/opening-application-window-using-tray.test.ts +++ b/src/features/quitting-and-restarting-the-app/opening-application-window-using-tray.test.ts @@ -132,7 +132,7 @@ describe("opening application window using tray", () => { }); it("starts loading of content for the application window", () => { - expect(callForApplicationWindowHtmlMock).toHaveBeenCalledWith("https://lens.app:42"); + expect(callForApplicationWindowHtmlMock).toHaveBeenCalledWith("https://lens.app:23456"); }); describe("given static HTML of application window has not resolved yet, when opening from tray again", () => { diff --git a/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts b/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts index a7a6e4ae60..4777a920f9 100644 --- a/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts +++ b/src/features/quitting-and-restarting-the-app/quitting-the-app-using-application-menu.test.ts @@ -64,14 +64,10 @@ describe("quitting the app using application menu", () => { expect(exitAppMock).not.toHaveBeenCalled(); }); - describe("after sufficient time passes", () => { - beforeEach(() => { - advanceFakeTime(1000); - }); + it("after sufficient time passes, terminates application", () => { + advanceFakeTime(1000); - it("terminates application", () => { - expect(exitAppMock).toHaveBeenCalled(); - }); + expect(exitAppMock).toHaveBeenCalled(); }); }); }); diff --git a/src/main/lens-proxy/lens-proxy.global-override-for-injectable.ts b/src/main/lens-proxy/lens-proxy.global-override-for-injectable.ts index 7539163abe..93004664cd 100644 --- a/src/main/lens-proxy/lens-proxy.global-override-for-injectable.ts +++ b/src/main/lens-proxy/lens-proxy.global-override-for-injectable.ts @@ -13,7 +13,7 @@ export default getGlobalOverride(lensProxyInjectable, (di) => { return ({ close: () => { }, listen: async () => { - lensProxyPort.set(12345); + lensProxyPort.set(23456); }, }); }); diff --git a/src/main/router/route-request.injectable.ts b/src/main/router/route-request.injectable.ts index 9af8381638..3c8c2612be 100644 --- a/src/main/router/route-request.injectable.ts +++ b/src/main/router/route-request.injectable.ts @@ -2,7 +2,7 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable"; +import type { Injectable, InjectionToken } from "@ogre-tools/injectable"; import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; import type { Route, LensApiRequest } from "./route"; import createHandlerForRouteInjectable from "./create-handler-for-route.injectable"; @@ -27,22 +27,16 @@ export function getRouteInjectable( export type RouteRequest = (cluster: Cluster | undefined, req: IncomingMessage, res: ServerResponse) => Promise; -const createRouter = (di: DiContainerForInjection) => { - const routes = di.injectMany(routeInjectionToken); - const createHandlerForRoute = di.inject(createHandlerForRouteInjectable); - const router = new Call.Router(); - - for (const route of routes) { - router.add({ method: route.method, path: route.path }, createHandlerForRoute(route)); - } - - return router; -}; - const routeRequestInjectable = getInjectable({ id: "route-request", instantiate: (di): RouteRequest => { - const router = createRouter(di); + const routes = di.injectMany(routeInjectionToken); + const createHandlerForRoute = di.inject(createHandlerForRouteInjectable); + const router = new Call.Router(); + + for (const route of routes) { + router.add({ method: route.method, path: route.path }, createHandlerForRoute(route)); + } return async (cluster, req, res) => { if (!req.url || !req.method) { @@ -50,7 +44,7 @@ const routeRequestInjectable = getInjectable({ } const url = new URL(req.url, "https://localhost"); - const matchingRoute = router.route(req.method, url.pathname); + const matchingRoute = router.route(req.method?.toLowerCase() ?? "get", url.pathname); if (matchingRoute instanceof Error) { return false; diff --git a/src/main/router/write-server-response.ts b/src/main/router/write-server-response.ts index f483f6b188..648b22fad0 100644 --- a/src/main/router/write-server-response.ts +++ b/src/main/router/write-server-response.ts @@ -8,10 +8,8 @@ import { object } from "../../common/utils"; export interface LensServerResponse { statusCode: number; - content: any; - headers: { - [name: string]: string; - }; + content: unknown; + headers: Partial>; } export const writeServerResponseFor = (serverResponse: ServerResponse) => ({ diff --git a/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts b/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts index 6c2e071abc..e40a0ea9c4 100644 --- a/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts +++ b/src/main/start-main-application/runnables/setup-lens-proxy.injectable.ts @@ -31,8 +31,10 @@ const setupLensProxyInjectable = getInjectable({ try { logger.info("🔌 Starting LensProxy"); await lensProxy.listen(); // lensProxy.port available - } catch (error: any) { - showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`); + } catch (error) { + const message = error instanceof Error ? error.message : "unknown error"; + + showErrorPopup("Lens Error", `Could not start proxy: ${message}`); return exitApp(); } diff --git a/src/renderer/components/test-utils/get-application-builder.tsx b/src/renderer/components/test-utils/get-application-builder.tsx index b1d1066d23..196fcb817a 100644 --- a/src/renderer/components/test-utils/get-application-builder.tsx +++ b/src/renderer/components/test-utils/get-application-builder.tsx @@ -35,7 +35,6 @@ import { overrideChannels } from "../../../test-utils/channel-fakes/override-cha import assert from "assert"; import { openMenu } from "react-select-event"; import userEvent from "@testing-library/user-event"; -import lensProxyPortInjectable from "../../../main/lens-proxy/lens-proxy-port.injectable"; import type { Route } from "../../../common/front-end-routing/front-end-route-injection-token"; import type { NavigateToRouteOptions } from "../../../common/front-end-routing/navigate-to-route-injection-token"; import { navigateToRouteInjectionToken } from "../../../common/front-end-routing/navigate-to-route-injection-token"; @@ -72,9 +71,8 @@ import { testUsingFakeTime } from "../../../common/test-utils/use-fake-time"; import type { LensFetch } from "../../../common/fetch/lens-fetch.injectable"; import lensFetchInjectable from "../../../common/fetch/lens-fetch.injectable"; import handleLensRequestInjectable from "../../../main/lens-proxy/handle-lens-request.injectable"; -import httpMocks from "node-mocks-http"; import nodeFetchModuleInjectable from "../../../common/fetch/fetch-module.injectable"; -import stream from "stream"; +import * as Shot from "@hapi/shot"; type Callback = (di: DiContainer) => void | Promise; @@ -223,26 +221,28 @@ export const getApplicationBuilder = () => { const handleLensRequest = mainDi.inject(handleLensRequestInjectable); const { Headers, Response } = di.inject(nodeFetchModuleInjectable); - const url = new URL(pathnameAndQuery, "https://127.0.0.1"); - const req = httpMocks.createRequest({ - method: (init?.method ?? "get").toUpperCase() as any, - url: url.pathname, - params: url.searchParams, - body: (init?.body ?? undefined) as any, - headers: new Headers(init?.headers) as any, - }); - const duplex = new stream.Duplex(); - const res = httpMocks.createResponse({ - req, - writableStream: duplex, - }); + const response = await Shot.inject( + handleLensRequest.handle as Shot.MaybeInjectionListener, + { + url: pathnameAndQuery, + headers: new Headers(init?.headers).raw(), + method: init?.method, + payload: init?.body ?? undefined, + }, + ); - await handleLensRequest.handle(req, res); - - return new Response(duplex, { - headers: new Headers(res._getHeaders() as Record), - status: res._getStatusCode(), - statusText: res._getStatusMessage(), + return new Response(response.rawPayload, { + headers: Object.entries(response.headers).map(([key, value]) => { + if (value === undefined) { + return [key]; + } else if (Array.isArray(value)) { + return [key, value.join(",")]; + } else { + return [key, value.toString()]; + } + }), + status: response.statusCode, + statusText: response.statusMessage, }); }; @@ -329,8 +329,6 @@ export const getApplicationBuilder = () => { const startMainApplication = mainDi.inject(startMainApplicationInjectable); const startApplication = async ({ shouldStartHidden }: { shouldStartHidden: boolean }) => { - mainDi.inject(lensProxyPortInjectable).set(42); - for (const callback of beforeApplicationStartCallbacks) { await callback(mainDi); } diff --git a/yarn.lock b/yarn.lock index fba376de4d..c8f38ae883 100644 --- a/yarn.lock +++ b/yarn.lock @@ -925,6 +925,14 @@ "@hapi/hoek" "9.x.x" "@hapi/nigel" "4.x.x" +"@hapi/shot@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@hapi/shot/-/shot-6.0.0.tgz#ed87a26dcb25c930293ae690830d3479dbf4e75a" + integrity sha512-RLGgzXy9GciJDunhY40NbVnLgYqp5gfBooZ2fOkAr4KbCEav/SJtYQS1N+knR7WFGzy8aooCR3XBUPI4ghHAkQ== + dependencies: + "@hapi/hoek" "^10.0.0" + "@hapi/validate" "^2.0.0" + "@hapi/subtext@^7.0.4": version "7.0.4" resolved "https://registry.yarnpkg.com/@hapi/subtext/-/subtext-7.0.4.tgz#aa46e4b45aad8115938334d5a3620a17b3b33ee5" @@ -945,6 +953,21 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@hapi/topo@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-6.0.0.tgz#6548e23e0a3d3b117eb0671dba49f654c9224c21" + integrity sha512-aorJvN1Q1n5xrZuA50Z4X6adI6VAM2NalIVm46ALL9LUvdoqhof3JPY69jdJH8asM3PsWr2SUVYzp57EqUP41A== + dependencies: + "@hapi/hoek" "^10.0.0" + +"@hapi/validate@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@hapi/validate/-/validate-2.0.0.tgz#16595de18b2c29621f06f4b29dcc39750c4b94a3" + integrity sha512-w5m8MvBgqGndbMIB+AWmXTb8CLtF1DlIxbnbAHNAo7aFuNQuI1Ywc2e0zDLK5fbFXDoqRzNrHnC7JjNJ+hDigw== + dependencies: + "@hapi/hoek" "^10.0.0" + "@hapi/topo" "^6.0.0" + "@hapi/vise@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@hapi/vise/-/vise-4.0.0.tgz#c6a94fe121b94a53bf99e7489f7fcc74c104db02" @@ -9361,11 +9384,6 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mergee@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mergee/-/mergee-1.0.0.tgz#027c5addc650f6ecbe4bf56100bd00dae763fda7" - integrity sha512-hbbXD4LOcxVkpS+mp3BMEhkSDf+lTVENFeEeqACgjjL8WrgKuW2EyLT0fOHyTbyDiuRLZJZ1HrHNeiX4iOd79Q== - methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -9609,13 +9627,6 @@ mobx@^6.3.0, mobx@^6.7.0: resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.7.0.tgz#2d805610fee1801fd015c54fd5400d2601aa3768" integrity sha512-1kBLBdSNG2bA522HQdbsTvwAwYf9hq9FWxmlhX7wTsJUAI54907J+ozfGW+LoYUo06vjit748g6QH1AAGLNebw== -mock-http@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mock-http/-/mock-http-1.1.0.tgz#b89380a718a103fc5801095804bedd0b20f7638c" - integrity sha512-H2HMGaHNQPWY8PdeEw4RFux2WEOHD6eJAtN3+iFELik5kGjPKAcoyPWcsC2vgDiTa2yimAEDssmMed51e+cBKQ== - dependencies: - mergee "^1.0.0" - moment-timezone@^0.5.40: version "0.5.40" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.40.tgz#c148f5149fd91dd3e29bf481abc8830ecba16b89"