diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index dabcc68480..d8430a33ce 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -18,65 +18,9 @@ import { flushPromises } from "../../test-utils/flush-promises"; import createKubeJsonApiInjectable from "../create-kube-json-api.injectable"; import type { IKubeWatchEvent } from "../kube-watch-event"; import type { KubeJsonApiDataFor } from "../kube-object"; -import type { Response, Headers as NodeFetchHeaders } from "node-fetch"; import AbortController from "abort-controller"; import setupAutoRegistrationInjectable from "../../../renderer/before-frame-starts/runnables/setup-auto-registration.injectable"; - -const createMockResponseFromString = (url: string, data: string, statusCode = 200) => { - const res: jest.Mocked = { - buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }), - clone: jest.fn(() => res), - arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }), - blob: jest.fn(async () => { throw new Error("blob() is not supported"); }), - body: new PassThrough(), - bodyUsed: false, - headers: new Headers() as NodeFetchHeaders, - json: jest.fn(async () => JSON.parse(await res.text())), - ok: 200 <= statusCode && statusCode < 300, - redirected: 300 <= statusCode && statusCode < 400, - size: data.length, - status: statusCode, - statusText: "some-text", - text: jest.fn(async () => data), - type: "basic", - url, - formData: jest.fn(async () => { throw new Error("formData() is not supported"); }), - }; - - return res; -}; - -const createMockResponseFromStream = (url: string, stream: NodeJS.ReadableStream, statusCode = 200) => { - const res: jest.Mocked = { - buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }), - clone: jest.fn(() => res), - arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }), - blob: jest.fn(async () => { throw new Error("blob() is not supported"); }), - body: stream, - bodyUsed: false, - headers: new Headers() as NodeFetchHeaders, - json: jest.fn(async () => JSON.parse(await res.text())), - ok: 200 <= statusCode && statusCode < 300, - redirected: 300 <= statusCode && statusCode < 400, - size: 10, - status: statusCode, - statusText: "some-text", - text: jest.fn(() => { - const chunks: Buffer[] = []; - - return new Promise((resolve, reject) => { - stream.on("data", (chunk) => chunks.push(Buffer.from(chunk))); - stream.on("error", (err) => reject(err)); - stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); - }); - }), - type: "basic", - url, - formData: jest.fn(async () => { throw new Error("formData() is not supported"); }), - }; - - return res; -}; +import { createMockResponseFromStream, createMockResponseFromString } from "../../../test-utils/mock-responses"; describe("createKubeApiForRemoteCluster", () => { let createKubeApiForRemoteCluster: CreateKubeApiForRemoteCluster; diff --git a/src/common/k8s-api/json-api.ts b/src/common/k8s-api/json-api.ts index c3e07bfa94..ed58b57258 100644 --- a/src/common/k8s-api/json-api.ts +++ b/src/common/k8s-api/json-api.ts @@ -104,7 +104,8 @@ export class JsonApi = Js ); const { query } = params ?? {}; - if (query) { + if (query && Object.keys(query).length > 0) { + console.log(query); const queryString = stringify(query as unknown as QueryParams); reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; @@ -171,7 +172,8 @@ export class JsonApi = Js reqInit.body = JSON.stringify(data); } - if (query) { + if (query && Object.keys(query).length > 0) { + console.log(query); const queryString = stringify(query as unknown as QueryParams); reqUrl += (reqUrl.includes("?") ? "&" : "?") + queryString; diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index 5722fef643..a82590d758 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -208,11 +208,7 @@ export abstract class KubeObjectStore< } } - const items = await res ?? []; - - console.trace("loadItems", this.api, [...items]); - - return items; + return await res ?? []; } this.loadedNamespaces.set(namespaces); @@ -253,8 +249,6 @@ export abstract class KubeObjectStore< try { const items = await this.loadItems({ namespaces, reqInit, onLoadFailure }); - console.log("loadAll", this.api, [...items]); - this.mergeItems(items, { merge, namespaces }); this.isLoaded = true; @@ -287,8 +281,6 @@ export abstract class KubeObjectStore< protected mergeItems(partialItems: K[], { merge = true, updateStore = true, sort = true, filter = true, namespaces }: MergeItemsOptions): K[] { let items = partialItems; - console.log("mergeItems", this.api, [...partialItems]); - // update existing items if (merge && this.api.isNamespaced) { const ns = new Set(namespaces); diff --git a/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap b/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap index 664b0daece..335cc1d159 100644 --- a/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap +++ b/src/renderer/components/+namespaces/__snapshots__/namespace-select-filter.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` renders 1`] = ` +exports[` once the subscribe resolves renders 1`] = `
renders 1`] = ` `; -exports[` when clicked renders 1`] = ` +exports[` once the subscribe resolves when clicked renders 1`] = `
when clicked renders 1`] = ` class="Select__option Select__option--is-selected css-tr4s17-option" id="react-select-namespace-select-filter-option-2" tabindex="-1" - > -
- - - layers - - - - test-2 - - - - check - - -
-
-
-
- - - layers - - - - test-3 - - - - check - - -
-
-
-
- - - layers - - - - test-4 - - - - check - - -
-
-
-
- - - layers - - - - test-5 - - - - check - - -
-
-
-
- - - layers - - - - test-6 - - - - check - - -
-
-
-
- - - layers - - - - test-7 - - - - check - - -
-
-
-
- - - layers - - - - test-8 - - - - check - - -
-
-
-
- - - layers - - - - test-9 - - - - check - - -
-
-
when clicked renders 1`] = `
when clicked renders 1`] = `
when clicked renders 1`] = `
when clicked renders 1`] = `
+
+
+ + + layers + + + + test-2 + + + + check + + +
+
+
+
+ + + layers + + + + test-3 + + + + check + + +
+
+
+
+ + + layers + + + + test-4 + + + + check + + +
+
+
+
+ + + layers + + + + test-5 + + + + check + + +
+
+
+
+ + + layers + + + + test-6 + + + + check + + +
+
+
+
+ + + layers + + + + test-7 + + + + check + + +
+
+
+
+ + + layers + + + + test-8 + + + + check + + +
+
+
+
+ + + layers + + + + test-9 + + + + check + + +
+
`; -exports[` when clicked when 'test-2' is clicked renders 1`] = ` +exports[` once the subscribe resolves when clicked when 'test-2' is clicked renders 1`] = `
when clicked when 'test-2' is clicked renders `; -exports[` when clicked when 'test-2' is clicked when clicked again renders 1`] = ` +exports[` once the subscribe resolves when clicked when 'test-2' is clicked when clicked again renders 1`] = `
when clicked when 'test-2' is clicked when cl - test-3 + test-10
@@ -942,7 +942,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-4 + test-11
@@ -966,7 +966,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-5 + test-12
@@ -990,7 +990,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-6 + test-13
@@ -1014,7 +1014,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-7 + test-3
@@ -1038,7 +1038,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-8 + test-4 @@ -1062,7 +1062,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-9 + test-5 @@ -1086,7 +1086,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-10 + test-6 @@ -1110,7 +1110,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-11 + test-7 @@ -1134,7 +1134,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-12 + test-8 @@ -1158,7 +1158,7 @@ exports[` when clicked when 'test-2' is clicked when cl - test-13 + test-9 @@ -1168,7 +1168,7 @@ exports[` when clicked when 'test-2' is clicked when cl `; -exports[` when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked renders 1`] = ` +exports[` once the subscribe resolves when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked renders 1`] = `
when clicked when 'test-2' is clicked when cl `; -exports[` when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked when clicked again, then holding down multi select key when 'test-3' is clicked renders 1`] = ` +exports[` once the subscribe resolves when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked when clicked again, then holding down multi select key when 'test-3' is clicked renders 1`] = `
when clicked when 'test-2' is clicked when cl class="Select__option css-10wo9uf-option" id="react-select-namespace-select-filter-option-2" tabindex="-1" + > +
+ + + layers + + + + test-10 + +
+
+
+
+ + + layers + + + + test-11 + +
+
+
+
+ + + layers + + + + test-12 + +
+
+
+
+ + + layers + + + + test-13 + +
+
+
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
when clicked when 'test-2' is clicked when cl
-
-
- - - layers - - - - test-10 - -
-
-
-
- - - layers - - - - test-11 - -
-
-
-
- - - layers - - - - test-12 - -
-
-
-
- - - layers - - - - test-13 - -
-
diff --git a/src/renderer/components/+namespaces/namespace-select-filter.test.tsx b/src/renderer/components/+namespaces/namespace-select-filter.test.tsx index a81e32e263..ae9ab70020 100644 --- a/src/renderer/components/+namespaces/namespace-select-filter.test.tsx +++ b/src/renderer/components/+namespaces/namespace-select-filter.test.tsx @@ -3,14 +3,22 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import type { AsyncFnMock } from "@async-fn/jest"; +import asyncFn from "@async-fn/jest"; import type { DiContainer } from "@ogre-tools/injectable"; import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; import React from "react"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import type { Fetch } from "../../../common/fetch/fetch.injectable"; +import fetchInjectable from "../../../common/fetch/fetch.injectable"; import { Namespace } from "../../../common/k8s-api/endpoints"; +import { createMockResponseFromString } from "../../../test-utils/mock-responses"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; +import subscribeStoresInjectable from "../../kube-watch-api/subscribe-stores.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; +import type { Disposer } from "../../utils"; +import { disposer } from "../../utils"; import { renderFor } from "../test-utils/renderFor"; import { NamespaceSelectFilter } from "./namespace-select-filter"; import type { NamespaceStore } from "./store"; @@ -32,147 +40,185 @@ function createNamespace(name: string): Namespace { describe("", () => { let di: DiContainer; let namespaceStore: NamespaceStore; + let fetchMock: AsyncFnMock; let result: RenderResult; + let cleanup: Disposer; beforeEach(() => { di = getDiForUnitTesting({ doGeneralOverrides: true }); di.override(directoryForUserDataInjectable, () => "/some-directory"); di.override(storesAndApisCanBeCreatedInjectable, () => true); + di.unoverride(subscribeStoresInjectable); + namespaceStore = di.inject(namespaceStoreInjectable); - const render = renderFor(di); + const subscribeStores = di.inject(subscribeStoresInjectable); - namespaceStore.items.replace([ - createNamespace("test-1"), - createNamespace("test-2"), - createNamespace("test-3"), - createNamespace("test-4"), - createNamespace("test-5"), - createNamespace("test-6"), - createNamespace("test-7"), - createNamespace("test-8"), - createNamespace("test-9"), - createNamespace("test-10"), - createNamespace("test-11"), - createNamespace("test-12"), - createNamespace("test-13"), - ]); + cleanup = disposer(subscribeStores([namespaceStore])); + + fetchMock = asyncFn(); + di.override(fetchInjectable, () => fetchMock); + + const render = renderFor(di); result = render(( )); }); - it("renders", () => { - expect(result.baseElement).toMatchSnapshot(); + afterEach(() => { + cleanup(); }); - describe("when clicked", () => { - beforeEach(() => { - result.getByTestId("namespace-select-filter").click(); + describe("once the subscribe resolves", () => { + beforeEach(async () => { + await fetchMock.resolveSpecific([ + "http://127.0.0.1:12345/api-kube/api/v1/namespaces", + ], createMockResponseFromString("http://127.0.0.1:12345/api-kube/api/v1/namespaces", JSON.stringify({ + apiVersion: "v1", + kind: "NamespaceList", + metadata: {}, + items: [ + createNamespace("test-1"), + createNamespace("test-2"), + createNamespace("test-3"), + createNamespace("test-4"), + createNamespace("test-5"), + createNamespace("test-6"), + createNamespace("test-7"), + createNamespace("test-8"), + createNamespace("test-9"), + createNamespace("test-10"), + createNamespace("test-11"), + createNamespace("test-12"), + createNamespace("test-13"), + ], + }))); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); - it("opens menu", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull(); - }); - - describe("when 'test-2' is clicked", () => { + describe("when clicked", () => { beforeEach(() => { - result.getByText("test-2").click(); + result.getByTestId("namespace-select-filter").click(); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); - it("has only 'test-2' is selected in the store", () => { - expect(namespaceStore.contextNamespaces).toEqual(["test-2"]); + it("opens menu", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull(); }); - it("closes menu", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull(); - }); - - describe("when clicked again", () => { + describe("when 'test-2' is clicked", () => { beforeEach(() => { - result.getByTestId("namespace-select-filter").click(); + result.getByText("test-2").click(); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); - it("shows 'test-2' as selected", () => { - expect(result.queryByTestId("namespace-select-filter-option-test-2-selected")).not.toBeNull(); + it("has only 'test-2' is selected in the store", () => { + expect(namespaceStore.contextNamespaces).toEqual(["test-2"]); }); - it("does not show 'test-1' as selected", () => { - expect(result.queryByTestId("namespace-select-filter-option-test-1-selected")).toBeNull(); + it("closes menu", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull(); }); - describe("when 'test-1' is clicked", () => { + describe("when clicked again", () => { beforeEach(() => { - result.getByText("test-1").click(); + result.getByTestId("namespace-select-filter").click(); }); it("renders", () => { expect(result.baseElement).toMatchSnapshot(); }); - it("has only 'test-1' is selected in the store", () => { - expect(namespaceStore.contextNamespaces).toEqual(["test-1"]); + it("shows 'test-2' as selected", () => { + expect(result.queryByTestId("namespace-select-filter-option-test-2-selected")).not.toBeNull(); }); - it("closes menu", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull(); + it("does not show 'test-1' as selected", () => { + expect(result.queryByTestId("namespace-select-filter-option-test-1-selected")).toBeNull(); }); - describe("when clicked again, then holding down multi select key", () => { + describe("when 'test-1' is clicked", () => { beforeEach(() => { - const filter = result.getByTestId("namespace-select-filter"); - - filter.click(); - fireEvent.keyDown(filter, { key: "Meta" }); + result.getByText("test-1").click(); }); - describe("when 'test-3' is clicked", () => { + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("has only 'test-1' is selected in the store", () => { + expect(namespaceStore.contextNamespaces).toEqual(["test-1"]); + }); + + it("closes menu", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull(); + }); + + describe("when clicked again, then holding down multi select key", () => { beforeEach(() => { - result.getByText("test-3").click(); + const filter = result.getByTestId("namespace-select-filter"); + + filter.click(); + fireEvent.keyDown(filter, { key: "Meta" }); }); - it("renders", () => { - expect(result.baseElement).toMatchSnapshot(); - }); - - it("has both 'test-1' and 'test-3' as selected in the store", () => { - expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3"])); - }); - - it("keeps menu open", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull(); - }); - - it("does not show 'kube-system' as selected", () => { - expect(result.queryByTestId("namespace-select-filter-option-kube-system-selected")).toBeNull(); - }); - - describe("when 'test-13' is clicked", () => { + describe("when 'test-3' is clicked", () => { beforeEach(() => { - result.getByText("test-13").click(); + result.getByText("test-3").click(); }); - it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => { - expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"])); + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); }); - it("'test-13' is not sorted to the top of the list", () => { - const topLevelElement = result.getByText("test-13").parentElement?.parentElement as HTMLElement; + it("has both 'test-1' and 'test-3' as selected in the store", () => { + expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3"])); + }); - expect(topLevelElement.nextSibling).toBe(null); + it("keeps menu open", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull(); + }); + + it("does not show 'kube-system' as selected", () => { + expect(result.queryByTestId("namespace-select-filter-option-kube-system-selected")).toBeNull(); + }); + + describe("when 'test-13' is clicked", () => { + beforeEach(() => { + result.getByText("test-13").click(); + }); + + it("has all of 'test-1', 'test-3', and 'test-13' selected in the store", () => { + expect(new Set(namespaceStore.contextNamespaces)).toEqual(new Set(["test-1", "test-3", "test-13"])); + }); + + it("'test-13' is not sorted to the top of the list", () => { + const topLevelElement = result.getByText("test-13").parentElement?.parentElement as HTMLElement; + + expect(topLevelElement.previousSibling).not.toBe(null); + }); + }); + + describe("when releasing multi select key", () => { + beforeEach(() => { + const filter = result.getByTestId("namespace-select-filter"); + + fireEvent.keyUp(filter, { key: "Meta" }); + }); + + it("closes menu", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull(); + }); }); }); @@ -183,46 +229,24 @@ describe("", () => { fireEvent.keyUp(filter, { key: "Meta" }); }); - it("closes menu", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).toBeNull(); + it("keeps menu open", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull(); }); }); }); - - describe("when releasing multi select key", () => { - beforeEach(() => { - const filter = result.getByTestId("namespace-select-filter"); - - fireEvent.keyUp(filter, { key: "Meta" }); - }); - - it("keeps menu open", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-listbox")).not.toBeNull(); - }); - }); }); }); }); - }); - describe("when multi-selection key is pressed", () => { - beforeEach(() => { - const filter = result.getByTestId("namespace-select-filter"); - - fireEvent.keyDown(filter, { key: "Meta" }); - }); - - it("should show placeholder text as 'All namespaces'", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).toHaveTextContent("All namespaces"); - }); - - describe("when 'test-2' is clicked", () => { + describe("when multi-selection key is pressed", () => { beforeEach(() => { - result.getByText("test-2").click(); + const filter = result.getByTestId("namespace-select-filter"); + + fireEvent.keyDown(filter, { key: "Meta" }); }); - it("should not show placeholder text as 'All namespaces'", () => { - expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces"); + it("should show placeholder text as 'All namespaces'", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).toHaveTextContent("All namespaces"); }); describe("when 'test-2' is clicked", () => { @@ -230,20 +254,30 @@ describe("", () => { result.getByText("test-2").click(); }); - it("should not show placeholder as 'All namespaces'", () => { + it("should not show placeholder text as 'All namespaces'", () => { expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces"); }); - describe("when multi-selection key is raised", () => { + describe("when 'test-2' is clicked", () => { beforeEach(() => { - const filter = result.getByTestId("namespace-select-filter"); - - fireEvent.keyUp(filter, { key: "Meta" }); + result.getByText("test-2").click(); }); - it("should show placeholder text as 'All namespaces'", () => { + it("should not show placeholder as 'All namespaces'", () => { expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces"); }); + + describe("when multi-selection key is raised", () => { + beforeEach(() => { + const filter = result.getByTestId("namespace-select-filter"); + + fireEvent.keyUp(filter, { key: "Meta" }); + }); + + it("should show placeholder text as 'All namespaces'", () => { + expect(result.baseElement.querySelector("#react-select-namespace-select-filter-placeholder")).not.toHaveTextContent("All namespaces"); + }); + }); }); }); }); diff --git a/src/renderer/components/+namespaces/store.ts b/src/renderer/components/+namespaces/store.ts index 794039e662..4c178a8a96 100644 --- a/src/renderer/components/+namespaces/store.ts +++ b/src/renderer/components/+namespaces/store.ts @@ -29,8 +29,19 @@ export class NamespaceStore extends KubeObjectStore { private async init() { await this.dependencies.storage.whenReady; - this.selectNamespaces(this.initialNamespaces); - this.autoLoadAllowedNamespaces(); + const { allowedNamespaces } = this; + const selectedNamespaces = this.dependencies.storage.get(); // raw namespaces, undefined on first load + + // return previously saved namespaces from local-storage (if any) + if (Array.isArray(selectedNamespaces)) { + this.selectNamespaces(selectedNamespaces.filter(namespace => allowedNamespaces.includes(namespace))); + } else if (allowedNamespaces.includes("default")) { + this.selectNamespaces(["default"]); + } else if (allowedNamespaces.length) { + this.selectNamespaces([allowedNamespaces[0]]); + } else { + this.selectNamespaces([]); + } } public onContextChange(callback: (namespaces: string[]) => void, opts: { fireImmediately?: boolean } = {}): IReactionDisposer { @@ -40,32 +51,6 @@ export class NamespaceStore extends KubeObjectStore { }); } - private autoLoadAllowedNamespaces(): IReactionDisposer { - return reaction(() => this.allowedNamespaces, namespaces => this.loadAll({ namespaces }), { - fireImmediately: true, - equals: comparer.shallow, - }); - } - - private get initialNamespaces(): string[] { - const { allowedNamespaces } = this; - const selectedNamespaces = this.dependencies.storage.get(); // raw namespaces, undefined on first load - - // return previously saved namespaces from local-storage (if any) - if (Array.isArray(selectedNamespaces)) { - return selectedNamespaces.filter(namespace => allowedNamespaces.includes(namespace)); - } - - // otherwise select "default" or first allowed namespace - if (allowedNamespaces.includes("default")) { - return ["default"]; - } else if (allowedNamespaces.length) { - return [allowedNamespaces[0]]; - } - - return []; - } - /** * @private * The current value (list of namespaces names) in the storage layer diff --git a/src/renderer/kube-watch-api/subscribe-stores.injectable.ts b/src/renderer/kube-watch-api/subscribe-stores.injectable.ts index f34a3d21f4..81c38c387e 100644 --- a/src/renderer/kube-watch-api/subscribe-stores.injectable.ts +++ b/src/renderer/kube-watch-api/subscribe-stores.injectable.ts @@ -7,7 +7,6 @@ import kubeWatchApiInjectable from "./kube-watch-api.injectable"; const subscribeStoresInjectable = getInjectable({ id: "subscribe-stores", - causesSideEffects: true, instantiate: (di) => di.inject(kubeWatchApiInjectable).subscribeStores, }); diff --git a/src/test-utils/mock-responses.ts b/src/test-utils/mock-responses.ts new file mode 100644 index 0000000000..ce016cabc7 --- /dev/null +++ b/src/test-utils/mock-responses.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { Response, Headers as NodeFetchHeaders } from "node-fetch"; +import { PassThrough } from "stream"; + +export const createMockResponseFromString = (url: string, data: string, statusCode = 200) => { + const res: jest.Mocked = { + buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }), + clone: jest.fn(() => res), + arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }), + blob: jest.fn(async () => { throw new Error("blob() is not supported"); }), + body: new PassThrough(), + bodyUsed: false, + headers: new Headers() as NodeFetchHeaders, + json: jest.fn(async () => JSON.parse(await res.text())), + ok: 200 <= statusCode && statusCode < 300, + redirected: 300 <= statusCode && statusCode < 400, + size: data.length, + status: statusCode, + statusText: "some-text", + text: jest.fn(async () => data), + type: "basic", + url, + formData: jest.fn(async () => { throw new Error("formData() is not supported"); }), + }; + + return res; +}; + +export const createMockResponseFromStream = (url: string, stream: NodeJS.ReadableStream, statusCode = 200) => { + const res: jest.Mocked = { + buffer: jest.fn(async () => { throw new Error("buffer() is not supported"); }), + clone: jest.fn(() => res), + arrayBuffer: jest.fn(async () => { throw new Error("arrayBuffer() is not supported"); }), + blob: jest.fn(async () => { throw new Error("blob() is not supported"); }), + body: stream, + bodyUsed: false, + headers: new Headers() as NodeFetchHeaders, + json: jest.fn(async () => JSON.parse(await res.text())), + ok: 200 <= statusCode && statusCode < 300, + redirected: 300 <= statusCode && statusCode < 400, + size: 10, + status: statusCode, + statusText: "some-text", + text: jest.fn(() => { + const chunks: Buffer[] = []; + + return new Promise((resolve, reject) => { + stream.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + stream.on("error", (err) => reject(err)); + stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); + }); + }), + type: "basic", + url, + formData: jest.fn(async () => { throw new Error("formData() is not supported"); }), + }; + + return res; +};