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

chore: Update tests to use more business logic

- To have a higher confidence in the code, given that the old tests didn't save us
  from modifying the code before.

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-04-21 09:41:51 -04:00
parent bc149f0627
commit 5dc94f291b
9 changed files with 197 additions and 866 deletions

View File

@ -2099,9 +2099,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeChanged: some-changed-value somePropertyToBeChanged: some-changed-value
someAddedProperty: some-new-value someAddedProperty: some-new-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -4435,9 +4435,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -5962,9 +5962,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -5976,7 +5976,7 @@ metadata:
</body> </body>
`; `;
exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given no changes in the configuration, when selecting to save when saving resolves with failure renders 1`] = ` exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace given no changes in the configuration, when selecting to save when saving fails renders 1`] = `
<body> <body>
<div> <div>
<div <div
@ -6714,9 +6714,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -7466,9 +7466,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
labels: labels:
k8slens-edit-resource-version: some-api-version k8slens-edit-resource-version: some-api-version
@ -8220,9 +8220,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -9568,9 +9568,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -9582,7 +9582,7 @@ metadata:
</body> </body>
`; `;
exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace when selecting to save and close when saving resolves with failure renders 1`] = ` exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves with namespace when selecting to save and close when saving failings renders 1`] = `
<body> <body>
<div> <div>
<div <div
@ -10320,9 +10320,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
</textarea> </textarea>
</div> </div>
@ -10920,686 +10920,3 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
</div> </div>
</body> </body>
`; `;
exports[`cluster/namespaces - edit namespace from new tab when navigating to namespaces when namespaces resolve when clicking the context menu for a namespace when clicking to edit namespace when call for namespace resolves without namespace renders 1`] = `
<body>
<div>
<div
class="Animate slide-right Drawer KubeObjectDetails flex column right enter leave"
style="--size: 725px; --enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="drawer-wrapper flex column"
>
<div
class="drawer-title flex align-center"
>
<div
class="drawer-title-text flex gaps align-center"
>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
<div>
Close
</div>
</div>
<div
class="drawer-content flex column box grow"
/>
</div>
<div
class="ResizingAnchor horizontal leading"
/>
</div>
<div
class="Notifications flex column align-flex-end"
/>
<div
class="mainLayout"
style="--sidebar-width: 200px;"
>
<div
class="sidebar"
>
<div
class="flex flex-col"
data-testid="cluster-sidebar"
>
<div
class="SidebarCluster"
>
<div
class="Avatar rounded loadingAvatar"
style="width: 40px; height: 40px;"
>
??
</div>
<div
class="loadingClusterName"
/>
</div>
<div
class="sidebarNav sidebar-active-status"
>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-workloads"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-workloads"
href="/"
>
<i
class="Icon svg focusable"
>
<span
class="icon"
/>
</i>
<span>
Workloads
</span>
<i
class="Icon expandIcon material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-config"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-config"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="list"
>
list
</span>
</i>
<span>
Config
</span>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-network"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-network"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="device_hub"
>
device_hub
</span>
</i>
<span>
Network
</span>
<i
class="Icon expandIcon material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-storage"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-storage"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="storage"
>
storage
</span>
</i>
<span>
Storage
</span>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="true"
data-testid="sidebar-item-namespaces"
>
<a
aria-current="page"
class="navItem active"
data-testid="sidebar-item-link-for-namespaces"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="layers"
>
layers
</span>
</i>
<span>
Namespaces
</span>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-helm"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-helm"
href="/"
>
<i
class="Icon svg focusable"
>
<span
class="icon"
/>
</i>
<span>
Helm
</span>
<i
class="Icon expandIcon material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-user-management"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-user-management"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="security"
>
security
</span>
</i>
<span>
Access Control
</span>
</a>
</div>
<div
class="SidebarItem"
data-is-active-test="false"
data-testid="sidebar-item-custom-resources"
>
<a
class="navItem"
data-testid="sidebar-item-link-for-custom-resources"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="extension"
>
extension
</span>
</i>
<span>
Custom Resources
</span>
<i
class="Icon expandIcon material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
</div>
</div>
<div
class="ResizingAnchor horizontal trailing"
/>
</div>
<div
class="contents"
>
<div
class="TabLayout"
data-testid="tab-layout"
>
<main
class=""
>
<div
class="ItemListLayout flex column KubeObjectListLayout Namespaces"
>
<div
class="header flex gaps align-center"
>
<h5
class="title"
>
Namespaces
</h5>
<div
class="info-panel box grow"
>
0 items
</div>
<div
class="Input SearchInput"
>
<label
class="input-area flex gaps align-center"
id=""
>
<input
class="input box grow"
placeholder="Search Namespaces..."
spellcheck="false"
value=""
/>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="search"
>
search
</span>
</i>
</label>
<div
class="input-info flex gaps"
/>
</div>
</div>
<div
class="items box grow flex column"
>
<div
class="Table flex column KubeObjectListLayout Namespaces box grow dark selectable scrollable sortable autoSize virtual"
>
<div
class="TableHead sticky nowrap topLine"
>
<div
class="TableCell checkbox"
>
<label
class="Checkbox flex align-center"
>
<input
type="checkbox"
/>
<i
class="box flex align-center"
/>
</label>
</div>
<div
class="TableCell name nowrap sorting"
id="name"
>
<div
class="content"
>
Name
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell warning nowrap"
>
<div
class="content"
/>
</div>
<div
class="TableCell labels scrollable nowrap sorting"
id="labels"
>
<div
class="content"
>
Labels
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell age nowrap sorting"
id="age"
>
<div
class="content"
>
Age
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell status nowrap sorting"
id="status"
>
<div
class="content"
>
Status
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell menu nowrap"
>
<div
class="content"
>
<i
class="Icon material interactive focusable"
id="menu-actions-for-item-object-list-content"
tabindex="0"
>
<span
class="icon"
data-icon-name="more_vert"
>
more_vert
</span>
</i>
</div>
</div>
</div>
<div
class="NoItems flex box grow"
>
<div
class="box center"
>
Item list is empty
</div>
</div>
</div>
<div
class="AddRemoveButtons flex gaps"
>
<button
class="Button add-button primary big round"
type="button"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="add"
>
add
</span>
</i>
</button>
<div>
Add Namespace
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<div
class="footer"
>
<div
class="Dock isOpen"
tabindex="-1"
>
<div
class="ResizingAnchor vertical leading"
/>
<div
class="tabs-container flex align-center"
>
<div
class="dockTabs"
role="tablist"
>
<div
class="Tabs tabs"
>
<div
class="Tab flex gaps align-center DockTab active"
data-testid="dock-tab-for-some-first-tab-id"
id="tab-some-first-tab-id"
role="tab"
tabindex="0"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="edit"
>
edit
</span>
</i>
<div
class="label"
>
<div
class="flex align-center"
>
<span
class="title"
>
Namespace: some-name
</span>
<div
class="close"
>
<i
class="Icon material interactive focusable small"
data-testid="dock-tab-close-for-some-first-tab-id"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
<div
data-testid="tooltip-content-for-dock-tab-close-for-some-first-tab-id"
>
Close ⌘+W
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="toolbar flex gaps align-center box grow"
>
<div
class="dock-menu box grow"
>
<i
class="Icon new-dock-tab material interactive focusable"
id="menu-actions-for-dock"
tabindex="0"
>
<span
class="icon"
data-icon-name="add"
>
add
</span>
</i>
<div>
New tab
</div>
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="fullscreen"
>
fullscreen
</span>
</i>
<div>
Fit to window
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
<div>
Minimize
</div>
</div>
</div>
<div
class="tab-content edit-resource"
data-testid="dock-tab-content-for-some-first-tab-id"
style="flex-basis: 300px;"
>
<div
class="EditResource flex column"
>
<div
class="notice"
>
Resource not found
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
`;

View File

@ -12,8 +12,6 @@ import createEditResourceTabInjectable from "../../../renderer/components/dock/e
import getRandomIdForEditResourceTabInjectable from "../../../renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable"; import getRandomIdForEditResourceTabInjectable from "../../../renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable";
import type { AsyncFnMock } from "@async-fn/jest"; import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest"; import asyncFn from "@async-fn/jest";
import type { RequestPatchKubeResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/request-patch-kube-resource.injectable";
import requestPatchKubeResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/request-patch-kube-resource.injectable";
import dockStoreInjectable from "../../../renderer/components/dock/dock/store.injectable"; import dockStoreInjectable from "../../../renderer/components/dock/dock/store.injectable";
import { Namespace } from "../../../common/k8s-api/endpoints"; import { Namespace } from "../../../common/k8s-api/endpoints";
import showSuccessNotificationInjectable from "../../../renderer/components/notifications/show-success-notification.injectable"; import showSuccessNotificationInjectable from "../../../renderer/components/notifications/show-success-notification.injectable";
@ -21,13 +19,17 @@ import showErrorNotificationInjectable from "../../../renderer/components/notifi
import readJsonFileInjectable from "../../../common/fs/read-json-file.injectable"; import readJsonFileInjectable from "../../../common/fs/read-json-file.injectable";
import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable";
import type { RequestKubeResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/request-kube-resource.injectable"; import type { ApiKubePatch } from "../../../renderer/k8s/api-kube-patch.injectable";
import requestKubeResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/request-kube-resource.injectable"; import type { ApiKubeGet } from "../../../renderer/k8s/api-kube-get.injectable";
import apiKubePatchInjectable from "../../../renderer/k8s/api-kube-patch.injectable";
import apiKubeGetInjectable from "../../../renderer/k8s/api-kube-get.injectable";
import type { KubeJsonApiData } from "../../../common/k8s-api/kube-json-api";
import type { BaseKubeJsonApiObjectMetadata, KubeObjectScope } from "../../../common/k8s-api/kube-object";
describe("cluster/namespaces - edit namespace from new tab", () => { describe("cluster/namespaces - edit namespace from new tab", () => {
let builder: ApplicationBuilder; let builder: ApplicationBuilder;
let requestKubeResourceMock: AsyncFnMock<RequestKubeResource>; let apiKubePatchMock: AsyncFnMock<ApiKubePatch>;
let requestPatchKubeResourceMock: AsyncFnMock<RequestPatchKubeResource>; let apiKubeGetMock: AsyncFnMock<ApiKubeGet>;
let showSuccessNotificationMock: jest.Mock; let showSuccessNotificationMock: jest.Mock;
let showErrorNotificationMock: jest.Mock; let showErrorNotificationMock: jest.Mock;
@ -57,11 +59,11 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
.mockReturnValueOnce("some-second-tab-id"), .mockReturnValueOnce("some-second-tab-id"),
); );
requestKubeResourceMock = asyncFn(); apiKubePatchMock = asyncFn();
windowDi.override(requestKubeResourceInjectable, () => requestKubeResourceMock); windowDi.override(apiKubePatchInjectable, () => apiKubePatchMock);
requestPatchKubeResourceMock = asyncFn(); apiKubeGetMock = asyncFn();
windowDi.override(requestPatchKubeResourceInjectable, () => requestPatchKubeResourceMock); windowDi.override(apiKubeGetInjectable, () => apiKubeGetMock);
}); });
builder.afterWindowStart(() => { builder.afterWindowStart(() => {
@ -156,16 +158,16 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
}); });
it("calls for namespace", () => { it("calls for namespace", () => {
expect(requestKubeResourceMock).toHaveBeenCalledWith( expect(apiKubeGetMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-uid", "/apis/some-api-version/namespaces/some-uid",
); );
}); });
describe("when call for namespace resolves with namespace", () => { describe("when call for namespace resolves with namespace", () => {
let someNamespace: Namespace; let someNamespaceData: KubeJsonApiData<BaseKubeJsonApiObjectMetadata<KubeObjectScope.Cluster>, unknown, unknown>;
beforeEach(async () => { beforeEach(async () => {
someNamespace = new Namespace({ someNamespaceData = ({
apiVersion: "some-api-version", apiVersion: "some-api-version",
kind: "Namespace", kind: "Namespace",
@ -173,16 +175,12 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
uid: "some-uid", uid: "some-uid",
name: "some-name", name: "some-name",
resourceVersion: "some-resource-version", resourceVersion: "some-resource-version",
selfLink: "/apis/some-api-version/namespaces/some-uid",
somePropertyToBeRemoved: "some-value", somePropertyToBeRemoved: "some-value",
somePropertyToBeChanged: "some-old-value", somePropertyToBeChanged: "some-old-value",
}, },
}); });
await requestKubeResourceMock.resolve({ await apiKubeGetMock.resolve(someNamespaceData);
callWasSuccessful: true,
response: someNamespace,
});
}); });
it("renders", () => { it("renders", () => {
@ -206,9 +204,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
`); `);
}); });
@ -226,15 +224,22 @@ metadata:
}); });
it("calls for save with just the adding version label", () => { it("calls for save with just the adding version label", () => {
expect(requestPatchKubeResourceMock).toHaveBeenCalledWith( expect(apiKubePatchMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-uid", "/apis/some-api-version/namespaces/some-uid",
[{ {
op: "add", data: [{
path: "/metadata/labels", op: "add",
value: { path: "/metadata/labels",
"k8slens-edit-resource-version": "some-api-version", value: {
"k8slens-edit-resource-version": "some-api-version",
},
}],
},
{
headers: {
"content-type": "application/json-patch+json",
}, },
}], },
); );
}); });
@ -262,9 +267,11 @@ metadata:
describe("when saving resolves with success", () => { describe("when saving resolves with success", () => {
beforeEach(async () => { beforeEach(async () => {
await requestPatchKubeResourceMock.resolve({ await apiKubePatchMock.resolve({
callWasSuccessful: true, kind: "Namespace",
response: { name: "some-name", kind: "Namespace" }, metadata: {
name: "some-name",
},
}); });
}); });
@ -309,12 +316,9 @@ metadata:
}); });
}); });
describe("when saving resolves with failure", () => { describe("when saving fails", () => {
beforeEach(async () => { beforeEach(async () => {
await requestPatchKubeResourceMock.resolve({ await apiKubePatchMock.reject(new Error("some-error"));
callWasSuccessful: false,
error: "some-error",
});
}); });
it("renders", () => { it("renders", () => {
@ -380,9 +384,11 @@ metadata:
describe("when saving resolves with success", () => { describe("when saving resolves with success", () => {
beforeEach(async () => { beforeEach(async () => {
await requestPatchKubeResourceMock.resolve({ await apiKubePatchMock.resolve({
callWasSuccessful: true, kind: "Namespace",
response: { name: "some-name", kind: "Namespace" }, metadata: {
name: "some-name",
},
}); });
}); });
@ -397,12 +403,9 @@ metadata:
}); });
}); });
describe("when saving resolves with failure", () => { describe("when saving failings", () => {
beforeEach(async () => { beforeEach(async () => {
await requestPatchKubeResourceMock.resolve({ await apiKubePatchMock.reject(new Error("some-error"));
callWasSuccessful: false,
error: "Some error",
});
}); });
it("renders", () => { it("renders", () => {
@ -451,9 +454,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeChanged: some-changed-value somePropertyToBeChanged: some-changed-value
someAddedProperty: some-new-value someAddedProperty: some-new-value
selfLink: /apis/some-api-version/namespaces/some-uid
`, `,
}, },
}); });
@ -474,9 +477,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeChanged: some-changed-value somePropertyToBeChanged: some-changed-value
someAddedProperty: some-new-value someAddedProperty: some-new-value
selfLink: /apis/some-api-version/namespaces/some-uid
`); `);
}); });
@ -499,9 +502,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
`, `,
draft: `apiVersion: some-api-version draft: `apiVersion: some-api-version
kind: Namespace kind: Namespace
@ -509,9 +512,9 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeChanged: some-changed-value somePropertyToBeChanged: some-changed-value
someAddedProperty: some-new-value someAddedProperty: some-new-value
selfLink: /apis/some-api-version/namespaces/some-uid
`, `,
}); });
}); });
@ -526,41 +529,46 @@ metadata:
}); });
it("calls for save with changed configuration", () => { it("calls for save with changed configuration", () => {
expect(requestPatchKubeResourceMock).toHaveBeenCalledWith( expect(apiKubePatchMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-uid", "/apis/some-api-version/namespaces/some-uid",
[ {
{ data: [
op: "remove", {
path: "/metadata/somePropertyToBeRemoved", op: "remove",
}, path: "/metadata/somePropertyToBeRemoved",
{
op: "add",
path: "/metadata/someAddedProperty",
value: "some-new-value",
},
{
op: "add",
path: "/metadata/labels",
value: {
"k8slens-edit-resource-version": "some-api-version",
}, },
{
op: "add",
path: "/metadata/someAddedProperty",
value: "some-new-value",
},
{
op: "add",
path: "/metadata/labels",
value: {
"k8slens-edit-resource-version": "some-api-version",
},
},
{
op: "replace",
path: "/metadata/somePropertyToBeChanged",
value: "some-changed-value",
},
],
},
{
headers: {
"content-type": "application/json-patch+json",
}, },
{ },
op: "replace",
path: "/metadata/somePropertyToBeChanged",
value: "some-changed-value",
},
],
); );
}); });
it("given save resolves and another change in configuration, when saving, calls for save with changed configuration", async () => { it("given save resolves and another change in configuration, when saving, calls for save with changed configuration", async () => {
await requestPatchKubeResourceMock.resolve({ await apiKubePatchMock.resolve({
callWasSuccessful: true, kind: "Namespace",
metadata: {
response: {
name: "some-name", name: "some-name",
kind: "Namespace",
}, },
}); });
@ -585,7 +593,7 @@ metadata:
}); });
requestPatchKubeResourceMock.mockClear(); apiKubePatchMock.mockClear();
const saveButton = rendered.getByTestId( const saveButton = rendered.getByTestId(
"save-edit-resource-from-tab-for-some-first-tab-id", "save-edit-resource-from-tab-for-some-first-tab-id",
@ -593,15 +601,22 @@ metadata:
fireEvent.click(saveButton); fireEvent.click(saveButton);
expect(requestPatchKubeResourceMock).toHaveBeenCalledWith( expect(apiKubePatchMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-uid", "/apis/some-api-version/namespaces/some-uid",
[ {
{ data: [
op: "add", {
path: "/metadata/someOtherAddedProperty", op: "add",
value: "some-other-new-value", path: "/metadata/someOtherAddedProperty",
value: "some-other-new-value",
},
],
},
{
headers: {
"content-type": "application/json-patch+json",
}, },
], },
); );
}); });
}); });
@ -692,7 +707,7 @@ metadata:
describe("given clicking the context menu for second namespace, when clicking to edit namespace", () => { describe("given clicking the context menu for second namespace, when clicking to edit namespace", () => {
beforeEach(() => { beforeEach(() => {
requestKubeResourceMock.mockClear(); apiKubeGetMock.mockClear();
// TODO: Make implementation match the description // TODO: Make implementation match the description
const namespaceStub = new Namespace(someOtherNamespaceDataStub); const namespaceStub = new Namespace(someOtherNamespaceDataStub);
@ -725,7 +740,7 @@ metadata:
}); });
it("calls for second namespace", () => { it("calls for second namespace", () => {
expect(requestKubeResourceMock).toHaveBeenCalledWith( expect(apiKubeGetMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-other-uid", "/apis/some-api-version/namespaces/some-other-uid",
); );
}); });
@ -747,10 +762,7 @@ metadata:
}, },
}); });
await requestKubeResourceMock.resolve({ await apiKubeGetMock.resolve(someOtherNamespace);
callWasSuccessful: true,
response: someOtherNamespace,
});
}); });
it("renders", () => { it("renders", () => {
@ -773,7 +785,7 @@ metadata:
}); });
it("when selecting to save, calls for save of second namespace with just the add edit version label", () => { it("when selecting to save, calls for save of second namespace with just the add edit version label", () => {
requestPatchKubeResourceMock.mockClear(); apiKubePatchMock.mockClear();
const saveButton = rendered.getByTestId( const saveButton = rendered.getByTestId(
"save-edit-resource-from-tab-for-some-second-tab-id", "save-edit-resource-from-tab-for-some-second-tab-id",
@ -781,21 +793,28 @@ metadata:
fireEvent.click(saveButton); fireEvent.click(saveButton);
expect(requestPatchKubeResourceMock).toHaveBeenCalledWith( expect(apiKubePatchMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-other-uid", "/apis/some-api-version/namespaces/some-other-uid",
[{ {
op: "add", data: [{
path: "/metadata/labels", op: "add",
value: { path: "/metadata/labels",
"k8slens-edit-resource-version": "some-api-version", value: {
"k8slens-edit-resource-version": "some-api-version",
},
}],
},
{
headers: {
"content-type": "application/json-patch+json",
}, },
}], },
); );
}); });
describe("when clicking dock tab for the first namespace", () => { describe("when clicking dock tab for the first namespace", () => {
beforeEach(() => { beforeEach(() => {
requestKubeResourceMock.mockClear(); apiKubeGetMock.mockClear();
const tab = rendered.getByTestId("dock-tab-for-some-first-tab-id"); const tab = rendered.getByTestId("dock-tab-for-some-first-tab-id");
@ -825,7 +844,7 @@ metadata:
}); });
it("does not call for namespace", () => { it("does not call for namespace", () => {
expect(requestKubeResourceMock).not.toHaveBeenCalledWith("/apis/some-api-version/namespaces/some-uid"); expect(apiKubeGetMock).not.toHaveBeenCalledWith("/apis/some-api-version/namespaces/some-uid");
}); });
it("has configuration in the editor", () => { it("has configuration in the editor", () => {
@ -839,14 +858,14 @@ metadata:
uid: some-uid uid: some-uid
name: some-name name: some-name
resourceVersion: some-resource-version resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value somePropertyToBeChanged: some-old-value
selfLink: /apis/some-api-version/namespaces/some-uid
`); `);
}); });
it("when selecting to save, calls for save of first namespace with just the new edit version label", () => { it("when selecting to save, calls for save of first namespace with just the new edit version label", () => {
requestPatchKubeResourceMock.mockClear(); apiKubePatchMock.mockClear();
const saveButton = rendered.getByTestId( const saveButton = rendered.getByTestId(
"save-edit-resource-from-tab-for-some-first-tab-id", "save-edit-resource-from-tab-for-some-first-tab-id",
@ -854,15 +873,22 @@ metadata:
fireEvent.click(saveButton); fireEvent.click(saveButton);
expect(requestPatchKubeResourceMock).toHaveBeenCalledWith( expect(apiKubePatchMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-uid", "/apis/some-api-version/namespaces/some-uid",
[ { {
op: "add", data: [{
path: "/metadata/labels", op: "add",
value: { path: "/metadata/labels",
"k8slens-edit-resource-version": "some-api-version", value: {
"k8slens-edit-resource-version": "some-api-version",
},
}],
},
{
headers: {
"content-type": "application/json-patch+json",
}, },
}], },
); );
}); });
}); });
@ -870,41 +896,9 @@ metadata:
}); });
}); });
describe("when call for namespace resolves without namespace", () => {
beforeEach(async () => {
await requestKubeResourceMock.resolve({
callWasSuccessful: true,
response: undefined,
});
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("still shows the dock tab for editing namespace", () => {
expect(
rendered.getByTestId("dock-tab-for-some-first-tab-id"),
).toBeInTheDocument();
});
it("shows error message", () => {
expect(
rendered.getByTestId("dock-tab-content-for-some-first-tab-id"),
).toHaveTextContent("Resource not found");
});
it("does not show error notification", () => {
expect(showErrorNotificationMock).not.toHaveBeenCalled();
});
});
describe("when call for namespace resolves with failure", () => { describe("when call for namespace resolves with failure", () => {
beforeEach(async () => { beforeEach(async () => {
await requestKubeResourceMock.resolve({ await apiKubeGetMock.reject(new Error("some-error-missing-namespace"));
callWasSuccessful: false,
error: "some-error",
});
}); });
it("renders", () => { it("renders", () => {

View File

@ -28,8 +28,8 @@ const editResourceModelInjectable = getInjectable({
const store = di.inject(editResourceTabStoreInjectable); const store = di.inject(editResourceTabStoreInjectable);
const model = new EditResourceModel({ const model = new EditResourceModel({
callForResource: di.inject(requestKubeResourceInjectable), requestKubeResource: di.inject(requestKubeResourceInjectable),
callForPatchResource: di.inject(requestPatchKubeResourceInjectable), requestPatchKubeResource: di.inject(requestPatchKubeResourceInjectable),
showSuccessNotification: di.inject(showSuccessNotificationInjectable), showSuccessNotification: di.inject(showSuccessNotificationInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable),
store, store,
@ -50,8 +50,8 @@ const editResourceModelInjectable = getInjectable({
export default editResourceModelInjectable; export default editResourceModelInjectable;
interface Dependencies { interface Dependencies {
callForResource: RequestKubeResource; requestKubeResource: RequestKubeResource;
callForPatchResource: RequestPatchKubeResource; requestPatchKubeResource: RequestPatchKubeResource;
waitForEditingResource: () => Promise<EditingResource>; waitForEditingResource: () => Promise<EditingResource>;
showSuccessNotification: ShowNotification; showSuccessNotification: ShowNotification;
showErrorNotification: ShowNotification; showErrorNotification: ShowNotification;
@ -134,7 +134,7 @@ export class EditResourceModel {
load = async (): Promise<void> => { load = async (): Promise<void> => {
await this.dependencies.waitForEditingResource(); await this.dependencies.waitForEditingResource();
let result = await this.dependencies.callForResource(this.selfLink); let result = await this.dependencies.requestKubeResource(this.selfLink);
if (!result.callWasSuccessful) { if (!result.callWasSuccessful) {
return void this.dependencies.showErrorNotification(`Loading resource failed: ${result.error}`); return void this.dependencies.showErrorNotification(`Loading resource failed: ${result.error}`);
@ -149,7 +149,7 @@ export class EditResourceModel {
parsed.apiVersion = result.response.metadata.labels[EditResourceLabelName]; parsed.apiVersion = result.response.metadata.labels[EditResourceLabelName];
result = await this.dependencies.callForResource(createKubeApiURL(parsed)); result = await this.dependencies.requestKubeResource(createKubeApiURL(parsed));
} }
if (!result.callWasSuccessful) { if (!result.callWasSuccessful) {
@ -205,7 +205,7 @@ export class EditResourceModel {
return null; return null;
} }
const result = await this.dependencies.callForPatchResource(selfLink, patches); const result = await this.dependencies.requestPatchKubeResource(selfLink, patches);
if (!result.callWasSuccessful) { if (!result.callWasSuccessful) {
this.dependencies.showErrorNotification(( this.dependencies.showErrorNotification((

View File

@ -1,8 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverrideForFunction } from "@k8slens/test-utils";
import requestKubeResourceInjectable from "./request-kube-resource.injectable";
export default getGlobalOverrideForFunction(requestKubeResourceInjectable);

View File

@ -7,10 +7,10 @@ import type { KubeObjectMetadata, KubeObjectScope } from "../../../../../common/
import { KubeObject } from "../../../../../common/k8s-api/kube-object"; import { KubeObject } from "../../../../../common/k8s-api/kube-object";
import type { AsyncResult } from "@k8slens/utilities"; import type { AsyncResult } from "@k8slens/utilities";
import { getErrorMessage } from "../../../../../common/utils/get-error-message"; import { getErrorMessage } from "../../../../../common/utils/get-error-message";
import apiKubeInjectable from "../../../../k8s/api-kube.injectable";
import type { Writable } from "type-fest"; import type { Writable } from "type-fest";
import type { KubeJsonApiData } from "../../../../../common/k8s-api/kube-json-api"; import type { KubeJsonApiData } from "../../../../../common/k8s-api/kube-json-api";
import { parseKubeApi } from "../../../../../common/k8s-api/kube-api-parse"; import { parseKubeApi } from "../../../../../common/k8s-api/kube-api-parse";
import apiKubeGetInjectable from "../../../../k8s/api-kube-get.injectable";
export type RequestKubeResource = (selfLink: string) => AsyncResult<KubeObject | undefined>; export type RequestKubeResource = (selfLink: string) => AsyncResult<KubeObject | undefined>;
@ -18,7 +18,7 @@ const requestKubeResourceInjectable = getInjectable({
id: "request-kube-resource", id: "request-kube-resource",
instantiate: (di): RequestKubeResource => { instantiate: (di): RequestKubeResource => {
const apiKube = di.inject(apiKubeInjectable); const apiKubeGet = di.inject(apiKubeGetInjectable);
return async (selfLink) => { return async (selfLink) => {
const parsed = parseKubeApi(selfLink); const parsed = parseKubeApi(selfLink);
@ -28,7 +28,7 @@ const requestKubeResourceInjectable = getInjectable({
} }
try { try {
const rawData = await apiKube.get(selfLink) as KubeJsonApiData<KubeObjectMetadata<KubeObjectScope>, unknown, unknown>; const rawData = await apiKubeGet(selfLink) as KubeJsonApiData<KubeObjectMetadata<KubeObjectScope>, unknown, unknown>;
(rawData.metadata as Writable<typeof rawData.metadata>).selfLink = selfLink; (rawData.metadata as Writable<typeof rawData.metadata>).selfLink = selfLink;
@ -41,8 +41,6 @@ const requestKubeResourceInjectable = getInjectable({
} }
}; };
}, },
causesSideEffects: true,
}); });
export default requestKubeResourceInjectable; export default requestKubeResourceInjectable;

View File

@ -1,8 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverrideForFunction } from "@k8slens/test-utils";
import requestPatchKubeResourceInjectable from "./request-patch-kube-resource.injectable";
export default getGlobalOverrideForFunction(requestPatchKubeResourceInjectable);

View File

@ -6,19 +6,19 @@ import { getInjectable } from "@ogre-tools/injectable";
import type { AsyncResult } from "@k8slens/utilities"; import type { AsyncResult } from "@k8slens/utilities";
import type { JsonPatch } from "../../../../../common/k8s-api/kube-object.store"; import type { JsonPatch } from "../../../../../common/k8s-api/kube-object.store";
import { getErrorMessage } from "../../../../../common/utils/get-error-message"; import { getErrorMessage } from "../../../../../common/utils/get-error-message";
import apiKubeInjectable from "../../../../k8s/api-kube.injectable";
import { patchTypeHeaders } from "../../../../../common/k8s-api/kube-api"; import { patchTypeHeaders } from "../../../../../common/k8s-api/kube-api";
import apiKubePatchInjectable from "../../../../k8s/api-kube-patch.injectable";
export type RequestPatchKubeResource = (selfLink: string, patch: JsonPatch) => AsyncResult<{ name: string; kind: string }>; export type RequestPatchKubeResource = (selfLink: string, patch: JsonPatch) => AsyncResult<{ name: string; kind: string }>;
const requestPatchKubeResourceInjectable = getInjectable({ const requestPatchKubeResourceInjectable = getInjectable({
id: "request-patch-kube-resource", id: "request-patch-kube-resource",
instantiate: (di): RequestPatchKubeResource => { instantiate: (di): RequestPatchKubeResource => {
const apiKube = di.inject(apiKubeInjectable); const apiKubePatch = di.inject(apiKubePatchInjectable);
return async (selfLink, patch) => { return async (selfLink, patch) => {
try { try {
const kubeObject = await apiKube.patch(selfLink, { data: patch }, { const { metadata, kind } = await apiKubePatch(selfLink, { data: patch }, {
headers: { headers: {
"content-type": patchTypeHeaders.json, "content-type": patchTypeHeaders.json,
}, },
@ -26,7 +26,7 @@ const requestPatchKubeResourceInjectable = getInjectable({
return { return {
callWasSuccessful: true, callWasSuccessful: true,
response: { name: kubeObject.metadata.name, kind: kubeObject.kind }, response: { name: metadata.name, kind },
}; };
} catch (e) { } catch (e) {
return { return {
@ -36,8 +36,6 @@ const requestPatchKubeResourceInjectable = getInjectable({
} }
}; };
}, },
causesSideEffects: true,
}); });
export default requestPatchKubeResourceInjectable; export default requestPatchKubeResourceInjectable;

View File

@ -0,0 +1,20 @@
/**
* 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 { KubeJsonApi } from "../../common/k8s-api/kube-json-api";
import apiKubeInjectable from "./api-kube.injectable";
export type ApiKubeGet = KubeJsonApi["get"];
const apiKubeGetInjectable = getInjectable({
id: "api-kube-get",
instantiate: (di): ApiKubeGet => {
const apiKube = di.inject(apiKubeInjectable);
return (...params) => apiKube.get(...params);
},
});
export default apiKubeGetInjectable;

View File

@ -0,0 +1,20 @@
/**
* 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 { KubeJsonApi } from "../../common/k8s-api/kube-json-api";
import apiKubeInjectable from "./api-kube.injectable";
export type ApiKubePatch = KubeJsonApi["patch"];
const apiKubePatchInjectable = getInjectable({
id: "api-kube-patch",
instantiate: (di): ApiKubePatch => {
const apiKube = di.inject(apiKubeInjectable);
return (...params) => apiKube.patch(...params);
},
});
export default apiKubePatchInjectable;