1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Fixing namespace-select-filter tests

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-11-29 10:45:55 -05:00
parent 4847cfc35f
commit 31f0b8ed3a
8 changed files with 629 additions and 611 deletions

View File

@ -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<Response> = {
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<Response> = {
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;

View File

@ -104,7 +104,8 @@ export class JsonApi<Data = JsonApiData, Params extends JsonApiParams<Data> = 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<Data = JsonApiData, Params extends JsonApiParams<Data> = 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;

View File

@ -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);

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NamespaceSelectFilter /> renders 1`] = `
exports[`<NamespaceSelectFilter /> once the subscribe resolves renders 1`] = `
<body>
<div>
<div
@ -86,7 +86,7 @@ exports[`<NamespaceSelectFilter /> renders 1`] = `
</body>
`;
exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
exports[`<NamespaceSelectFilter /> once the subscribe resolves when clicked renders 1`] = `
<body>
<div>
<div
@ -229,286 +229,6 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-2"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-2
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-2-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-3"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-3
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-3-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-4"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-4
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-4-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-5"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-5
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-5-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-6"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-6
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-6-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-7"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-7
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-7-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-8"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-8
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-8-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-9"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-9
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-9-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-10"
tabindex="-1"
>
<div
class="flex gaps align-center"
@ -542,7 +262,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-11"
id="react-select-namespace-select-filter-option-3"
tabindex="-1"
>
<div
@ -577,7 +297,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-12"
id="react-select-namespace-select-filter-option-4"
tabindex="-1"
>
<div
@ -612,7 +332,7 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-13"
id="react-select-namespace-select-filter-option-5"
tabindex="-1"
>
<div
@ -644,13 +364,293 @@ exports[`<NamespaceSelectFilter /> when clicked renders 1`] = `
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-6"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-2
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-2-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-7"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-3
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-3-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-8"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-4
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-4-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-9"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-5
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-5-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-10"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-6
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-6-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-11"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-7
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-7-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-12"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-8
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-8-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-13"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-9
</span>
<i
class="Icon box right material focusable small"
data-testid="namespace-select-filter-option-test-9-selected"
>
<span
class="icon"
data-icon-name="check"
>
check
</span>
</i>
</div>
</div>
</div>
</div>
</div>
</body>
`;
exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked renders 1`] = `
exports[`<NamespaceSelectFilter /> once the subscribe resolves when clicked when 'test-2' is clicked renders 1`] = `
<body>
<div>
<div
@ -736,7 +736,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked renders
</body>
`;
exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when clicked again renders 1`] = `
exports[`<NamespaceSelectFilter /> once the subscribe resolves when clicked when 'test-2' is clicked when clicked again renders 1`] = `
<body>
<div>
<div
@ -918,7 +918,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-3
test-10
</span>
</div>
</div>
@ -942,7 +942,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-4
test-11
</span>
</div>
</div>
@ -966,7 +966,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-5
test-12
</span>
</div>
</div>
@ -990,7 +990,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-6
test-13
</span>
</div>
</div>
@ -1014,7 +1014,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-7
test-3
</span>
</div>
</div>
@ -1038,7 +1038,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-8
test-4
</span>
</div>
</div>
@ -1062,7 +1062,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-9
test-5
</span>
</div>
</div>
@ -1086,7 +1086,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-10
test-6
</span>
</div>
</div>
@ -1110,7 +1110,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-11
test-7
</span>
</div>
</div>
@ -1134,7 +1134,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-12
test-8
</span>
</div>
</div>
@ -1158,7 +1158,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</i>
<span>
test-13
test-9
</span>
</div>
</div>
@ -1168,7 +1168,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</body>
`;
exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked renders 1`] = `
exports[`<NamespaceSelectFilter /> once the subscribe resolves when clicked when 'test-2' is clicked when clicked again when 'test-1' is clicked renders 1`] = `
<body>
<div>
<div
@ -1254,7 +1254,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</body>
`;
exports[`<NamespaceSelectFilter /> 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[`<NamespaceSelectFilter /> 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`] = `
<body>
<div>
<div
@ -1397,6 +1397,102 @@ exports[`<NamespaceSelectFilter /> 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"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-10
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-3"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-11
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-4"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-12
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-5"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-13
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-6"
tabindex="-1"
>
<div
class="flex gaps align-center"
@ -1419,7 +1515,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option Select__option--is-selected css-tr4s17-option"
id="react-select-namespace-select-filter-option-3"
id="react-select-namespace-select-filter-option-7"
tabindex="-1"
>
<div
@ -1454,7 +1550,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-4"
id="react-select-namespace-select-filter-option-8"
tabindex="-1"
>
<div
@ -1478,7 +1574,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-5"
id="react-select-namespace-select-filter-option-9"
tabindex="-1"
>
<div
@ -1502,7 +1598,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-6"
id="react-select-namespace-select-filter-option-10"
tabindex="-1"
>
<div
@ -1526,7 +1622,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-7"
id="react-select-namespace-select-filter-option-11"
tabindex="-1"
>
<div
@ -1550,7 +1646,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-8"
id="react-select-namespace-select-filter-option-12"
tabindex="-1"
>
<div
@ -1574,7 +1670,7 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-9"
id="react-select-namespace-select-filter-option-13"
tabindex="-1"
>
<div
@ -1595,102 +1691,6 @@ exports[`<NamespaceSelectFilter /> when clicked when 'test-2' is clicked when cl
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-10"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-10
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-11"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-11
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-12"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-12
</span>
</div>
</div>
<div
aria-disabled="false"
class="Select__option css-10wo9uf-option"
id="react-select-namespace-select-filter-option-13"
tabindex="-1"
>
<div
class="flex gaps align-center"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
test-13
</span>
</div>
</div>
</div>
</div>
</div>

View File

@ -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("<NamespaceSelectFilter />", () => {
let di: DiContainer;
let namespaceStore: NamespaceStore;
let fetchMock: AsyncFnMock<Fetch>;
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((
<NamespaceSelectFilter id="namespace-select-filter" />
));
});
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("<NamespaceSelectFilter />", () => {
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("<NamespaceSelectFilter />", () => {
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");
});
});
});
});
});

View File

@ -29,8 +29,19 @@ export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> {
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<Namespace, NamespaceApi> {
});
}
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

View File

@ -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,
});

View File

@ -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<Response> = {
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<Response> = {
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;
};