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

Fixing edit-namespace-from-new-tab test

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-01-04 15:38:30 -05:00
parent 79d14b59ce
commit 93d5f73089
5 changed files with 148 additions and 323 deletions

View File

@ -721,48 +721,7 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
> />
<div
class="Animate opacity notification flex error enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Loading resource failed: some-error
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_9"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
<div <div
class="mainLayout" class="mainLayout"
style="--sidebar-width: 200px;" style="--sidebar-width: 200px;"
@ -6060,52 +6019,7 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
> />
<div
class="Animate opacity notification flex error enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
<p>
Failed to save resource:
some-error
</p>
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_1"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
<div <div
class="mainLayout" class="mainLayout"
style="--sidebar-width: 200px;" style="--sidebar-width: 200px;"
@ -9709,52 +9623,7 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
</div> </div>
<div <div
class="Notifications flex column align-flex-end" class="Notifications flex column align-flex-end"
> />
<div
class="Animate opacity notification flex error enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
<p>
Failed to save resource:
Some error
</p>
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_8"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
<div <div
class="mainLayout" class="mainLayout"
style="--sidebar-width: 200px;" style="--sidebar-width: 200px;"

View File

@ -27,8 +27,8 @@ import { controlWhenStoragesAreReady } from "../../../renderer/utils/create-stor
describe("cluster/namespaces - edit namespace from new tab", () => { describe("cluster/namespaces - edit namespace from new tab", () => {
let builder: ApplicationBuilder; let builder: ApplicationBuilder;
let callForNamespaceMock: AsyncFnMock<CallForResource>; let callForResourceMock: AsyncFnMock<CallForResource>;
let callForPatchNamespaceMock: AsyncFnMock<CallForPatchResource>; let callForPatchResourceMock: AsyncFnMock<CallForPatchResource>;
let showSuccessNotificationMock: jest.Mock; let showSuccessNotificationMock: jest.Mock;
let showErrorNotificationMock: jest.Mock; let showErrorNotificationMock: jest.Mock;
let storagesAreReady: () => Promise<void>; let storagesAreReady: () => Promise<void>;
@ -38,12 +38,6 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
builder.setEnvironmentToClusterFrame(); builder.setEnvironmentToClusterFrame();
callForNamespaceMock = asyncFn();
callForPatchNamespaceMock = asyncFn();
showSuccessNotificationMock = jest.fn();
showErrorNotificationMock = jest.fn();
builder.beforeWindowStart((windowDi) => { builder.beforeWindowStart((windowDi) => {
windowDi.override( windowDi.override(
directoryForLensLocalStorageInjectable, directoryForLensLocalStorageInjectable,
@ -54,15 +48,11 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
storagesAreReady = controlWhenStoragesAreReady(windowDi); storagesAreReady = controlWhenStoragesAreReady(windowDi);
windowDi.override( showSuccessNotificationMock = jest.fn();
showSuccessNotificationInjectable, windowDi.override(showSuccessNotificationInjectable, () => showSuccessNotificationMock);
() => showSuccessNotificationMock,
);
windowDi.override( showErrorNotificationMock = jest.fn();
showErrorNotificationInjectable, windowDi.override(showErrorNotificationInjectable, () => showErrorNotificationMock);
() => showErrorNotificationMock,
);
windowDi.override(getRandomIdForEditResourceTabInjectable, () => windowDi.override(getRandomIdForEditResourceTabInjectable, () =>
jest jest
@ -71,31 +61,11 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
.mockReturnValueOnce("some-second-tab-id"), .mockReturnValueOnce("some-second-tab-id"),
); );
windowDi.override(callForResourceInjectable, () => async (selfLink: string) => { callForResourceMock = asyncFn();
if ( windowDi.override(callForResourceInjectable, () => callForResourceMock);
[
"/apis/some-api-version/namespaces/some-uid",
"/apis/some-api-version/namespaces/some-other-uid",
].includes(selfLink)
) {
return await callForNamespaceMock(selfLink);
}
return undefined; callForPatchResourceMock = asyncFn();
}); windowDi.override(callForPatchResourceInjectable, () => callForPatchResourceMock);
windowDi.override(callForPatchResourceInjectable, () => async (namespace, ...args) => {
if (
[
"/apis/some-api-version/namespaces/some-uid",
"/apis/some-api-version/namespaces/some-other-uid",
].includes(namespace.selfLink)
) {
return await callForPatchNamespaceMock(namespace, ...args);
}
return undefined;
});
}); });
builder.allowKubeResource({ builder.allowKubeResource({
@ -115,14 +85,11 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
windowDi = builder.applicationWindow.only.di; windowDi = builder.applicationWindow.only.di;
const navigateToNamespaces = windowDi.inject( const navigateToNamespaces = windowDi.inject(navigateToNamespacesInjectable);
navigateToNamespacesInjectable, const dockStore = windowDi.inject(dockStoreInjectable);
);
navigateToNamespaces(); navigateToNamespaces();
const dockStore = windowDi.inject(dockStoreInjectable);
// TODO: Make TerminalWindow unit testable to allow realistic behaviour // TODO: Make TerminalWindow unit testable to allow realistic behaviour
dockStore.closeTab("terminal"); dockStore.closeTab("terminal");
}); });
@ -193,7 +160,7 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
}); });
it("calls for namespace", () => { it("calls for namespace", () => {
expect(callForNamespaceMock).toHaveBeenCalledWith( expect(callForResourceMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-uid", "/apis/some-api-version/namespaces/some-uid",
); );
}); });
@ -216,7 +183,7 @@ describe("cluster/namespaces - edit namespace from new tab", () => {
}, },
}); });
await callForNamespaceMock.resolve({ await callForResourceMock.resolve({
callWasSuccessful: true, callWasSuccessful: true,
response: someNamespace, response: someNamespace,
}); });
@ -263,7 +230,7 @@ metadata:
}); });
it("calls for save with empty values", () => { it("calls for save with empty values", () => {
expect(callForPatchNamespaceMock).toHaveBeenCalledWith( expect(callForPatchResourceMock).toHaveBeenCalledWith(
someNamespace, someNamespace,
[], [],
); );
@ -293,7 +260,7 @@ metadata:
describe("when saving resolves with success", () => { describe("when saving resolves with success", () => {
beforeEach(async () => { beforeEach(async () => {
await callForPatchNamespaceMock.resolve({ await callForPatchResourceMock.resolve({
callWasSuccessful: true, callWasSuccessful: true,
response: { name: "some-name", kind: "Namespace" }, response: { name: "some-name", kind: "Namespace" },
}); });
@ -342,7 +309,7 @@ metadata:
describe("when saving resolves with failure", () => { describe("when saving resolves with failure", () => {
beforeEach(async () => { beforeEach(async () => {
await callForPatchNamespaceMock.resolve({ await callForPatchResourceMock.resolve({
callWasSuccessful: false, callWasSuccessful: false,
error: "some-error", error: "some-error",
}); });
@ -411,7 +378,7 @@ metadata:
describe("when saving resolves with success", () => { describe("when saving resolves with success", () => {
beforeEach(async () => { beforeEach(async () => {
await callForPatchNamespaceMock.resolve({ await callForPatchResourceMock.resolve({
callWasSuccessful: true, callWasSuccessful: true,
response: { name: "some-name", kind: "Namespace" }, response: { name: "some-name", kind: "Namespace" },
}); });
@ -430,7 +397,7 @@ metadata:
describe("when saving resolves with failure", () => { describe("when saving resolves with failure", () => {
beforeEach(async () => { beforeEach(async () => {
await callForPatchNamespaceMock.resolve({ await callForPatchResourceMock.resolve({
callWasSuccessful: false, callWasSuccessful: false,
error: "Some error", error: "Some error",
}); });
@ -558,7 +525,7 @@ metadata:
}); });
it("calls for save with changed configuration", () => { it("calls for save with changed configuration", () => {
expect(callForPatchNamespaceMock).toHaveBeenCalledWith( expect(callForPatchResourceMock).toHaveBeenCalledWith(
someNamespace, someNamespace,
[ [
{ {
@ -580,7 +547,7 @@ metadata:
}); });
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 callForPatchNamespaceMock.resolve({ await callForPatchResourceMock.resolve({
callWasSuccessful: true, callWasSuccessful: true,
response: { response: {
@ -610,7 +577,7 @@ metadata:
}); });
callForPatchNamespaceMock.mockClear(); callForPatchResourceMock.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",
@ -618,7 +585,7 @@ metadata:
fireEvent.click(saveButton); fireEvent.click(saveButton);
expect(callForPatchNamespaceMock).toHaveBeenCalledWith( expect(callForPatchResourceMock).toHaveBeenCalledWith(
someNamespace, someNamespace,
[ [
{ {
@ -717,7 +684,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(() => {
callForNamespaceMock.mockClear(); callForResourceMock.mockClear();
// TODO: Make implementation match the description // TODO: Make implementation match the description
const namespaceStub = new Namespace(someOtherNamespaceDataStub); const namespaceStub = new Namespace(someOtherNamespaceDataStub);
@ -750,7 +717,7 @@ metadata:
}); });
it("calls for second namespace", () => { it("calls for second namespace", () => {
expect(callForNamespaceMock).toHaveBeenCalledWith( expect(callForResourceMock).toHaveBeenCalledWith(
"/apis/some-api-version/namespaces/some-other-uid", "/apis/some-api-version/namespaces/some-other-uid",
); );
}); });
@ -772,7 +739,7 @@ metadata:
}, },
}); });
await callForNamespaceMock.resolve({ await callForResourceMock.resolve({
callWasSuccessful: true, callWasSuccessful: true,
response: someOtherNamespace, response: someOtherNamespace,
}); });
@ -798,7 +765,7 @@ metadata:
}); });
it("when selecting to save, calls for save of second namespace", () => { it("when selecting to save, calls for save of second namespace", () => {
callForPatchNamespaceMock.mockClear(); callForPatchResourceMock.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",
@ -806,7 +773,7 @@ metadata:
fireEvent.click(saveButton); fireEvent.click(saveButton);
expect(callForPatchNamespaceMock).toHaveBeenCalledWith( expect(callForPatchResourceMock).toHaveBeenCalledWith(
someOtherNamespace, someOtherNamespace,
[], [],
); );
@ -814,7 +781,7 @@ metadata:
describe("when clicking dock tab for the first namespace", () => { describe("when clicking dock tab for the first namespace", () => {
beforeEach(() => { beforeEach(() => {
callForNamespaceMock.mockClear(); callForResourceMock.mockClear();
const tab = rendered.getByTestId("dock-tab-for-some-first-tab-id"); const tab = rendered.getByTestId("dock-tab-for-some-first-tab-id");
@ -844,7 +811,7 @@ metadata:
}); });
it("does not call for namespace", () => { it("does not call for namespace", () => {
expect(callForNamespaceMock).not.toHaveBeenCalled(); expect(callForResourceMock).not.toHaveBeenCalledWith("/apis/some-api-version/namespaces/some-uid");
}); });
it("has configuration in the editor", () => { it("has configuration in the editor", () => {
@ -865,7 +832,7 @@ metadata:
}); });
it("when selecting to save, calls for save of first namespace", () => { it("when selecting to save, calls for save of first namespace", () => {
callForPatchNamespaceMock.mockClear(); callForPatchResourceMock.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",
@ -873,7 +840,7 @@ metadata:
fireEvent.click(saveButton); fireEvent.click(saveButton);
expect(callForPatchNamespaceMock).toHaveBeenCalledWith( expect(callForPatchResourceMock).toHaveBeenCalledWith(
someNamespace, someNamespace,
[], [],
); );
@ -885,7 +852,7 @@ metadata:
describe("when call for namespace resolves without namespace", () => { describe("when call for namespace resolves without namespace", () => {
beforeEach(async () => { beforeEach(async () => {
await callForNamespaceMock.resolve({ await callForResourceMock.resolve({
callWasSuccessful: true, callWasSuccessful: true,
response: undefined, response: undefined,
}); });
@ -914,7 +881,7 @@ metadata:
describe("when call for namespace resolves with failure", () => { describe("when call for namespace resolves with failure", () => {
beforeEach(async () => { beforeEach(async () => {
await callForNamespaceMock.resolve({ await callForResourceMock.resolve({
callWasSuccessful: false, callWasSuccessful: false,
error: "some-error", error: "some-error",
}); });

View File

@ -7,8 +7,8 @@ import type { CallForResource } from "./call-for-resource/call-for-resource.inje
import callForResourceInjectable from "./call-for-resource/call-for-resource.injectable"; import callForResourceInjectable from "./call-for-resource/call-for-resource.injectable";
import { waitUntilDefined } from "../../../../../common/utils"; import { waitUntilDefined } from "../../../../../common/utils";
import editResourceTabStoreInjectable from "../store.injectable"; import editResourceTabStoreInjectable from "../store.injectable";
import type { EditingResource, EditResourceTabStore } from "../store"; import type { EditResourceTabStore } from "../store";
import { action, computed, observable, runInAction } from "mobx"; import { action, computed, makeObservable, observable, runInAction } from "mobx";
import type { KubeObject } from "../../../../../common/k8s-api/kube-object"; import type { KubeObject } from "../../../../../common/k8s-api/kube-object";
import yaml from "js-yaml"; import yaml from "js-yaml";
import assert from "assert"; import assert from "assert";
@ -24,18 +24,13 @@ const editResourceModelInjectable = getInjectable({
id: "edit-resource-model", id: "edit-resource-model",
instantiate: async (di, tabId: string) => { instantiate: async (di, tabId: string) => {
const store = di.inject(editResourceTabStoreInjectable);
const model = new EditResourceModel({ const model = new EditResourceModel({
callForResource: di.inject(callForResourceInjectable), callForResource: di.inject(callForResourceInjectable),
callForPatchResource: di.inject(callForPatchResourceInjectable), callForPatchResource: di.inject(callForPatchResourceInjectable),
showSuccessNotification: di.inject(showSuccessNotificationInjectable), showSuccessNotification: di.inject(showSuccessNotificationInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable),
store, store: di.inject(editResourceTabStoreInjectable),
tabId, tabId,
waitForEditingResource: () =>
waitUntilDefined(() => store.getData(tabId)),
}); });
await model.load(); await model.load();
@ -53,15 +48,15 @@ export default editResourceModelInjectable;
interface Dependencies { interface Dependencies {
callForResource: CallForResource; callForResource: CallForResource;
callForPatchResource: CallForPatchResource; callForPatchResource: CallForPatchResource;
waitForEditingResource: () => Promise<EditingResource>;
showSuccessNotification: ShowNotification; showSuccessNotification: ShowNotification;
showErrorNotification: ShowNotification; showErrorNotification: ShowNotification;
store: EditResourceTabStore; readonly store: EditResourceTabStore;
tabId: string; readonly tabId: string;
} }
export class EditResourceModel { export class EditResourceModel {
constructor(private dependencies: Dependencies) { constructor(private readonly dependencies: Dependencies) {
makeObservable(this);
} }
readonly configuration = { readonly configuration = {
@ -81,103 +76,103 @@ export class EditResourceModel {
}, },
}; };
@observable private _resource: KubeObject | undefined; @observable private _resource: KubeObject | undefined;
@computed get shouldShowErrorAboutNoResource() { @computed get shouldShowErrorAboutNoResource() {
return !this._resource; return !this._resource;
} }
@computed get resource() { @computed get resource() {
assert(this._resource, "Resource does not have data"); assert(this._resource, "Resource does not have data");
return this._resource; return this._resource;
} }
@computed get editingResource() { @computed get editingResource() {
const resource = this.dependencies.store.getData(this.dependencies.tabId); const resource = this.dependencies.store.getData(this.dependencies.tabId);
assert(resource, "Resource is not present in the store"); assert(resource, "Resource is not present in the store");
return resource; return resource;
} }
@computed private get selfLink() { @computed private get selfLink() {
return this.editingResource.resource; return this.editingResource.resource;
} }
load = async () => { load = async () => {
await this.dependencies.waitForEditingResource(); await waitUntilDefined(() => this.dependencies.store.getData(this.dependencies.tabId));
const result = await this.dependencies.callForResource(this.selfLink); const result = await this.dependencies.callForResource(this.selfLink);
if (!result.callWasSuccessful) { if (!result.callWasSuccessful) {
this.dependencies.showErrorNotification( this.dependencies.showErrorNotification(
`Loading resource failed: ${result.error}`, `Loading resource failed: ${result.error}`,
); );
return; return;
} }
const resource = result.response; runInAction(() => {
this._resource = result.response;
runInAction(() => { if (this._resource) {
this._resource = resource; this.editingResource.firstDraft = yaml.dump(
}); this._resource.toPlainObject(),
);
}
});
};
if (!resource) { get namespace() {
return; return this.resource.metadata.namespace || "default";
} }
runInAction(() => { get name() {
this.editingResource.firstDraft = yaml.dump(resource.toPlainObject()); return this.resource.metadata.name;
}); }
};
get namespace() { get kind() {
return this.resource.metadata.namespace || "default"; return this.resource.kind;
} }
get name() { save = async () => {
return this.resource.metadata.name; const currentValue = this.configuration.value.get();
} const currentVersion = yaml.load(currentValue);
const firstVersion = yaml.load(
this.editingResource.firstDraft ?? currentValue,
);
const patches = createPatch(firstVersion, currentVersion);
get kind() { const result = await this.dependencies.callForPatchResource(
return this.resource.kind; this.resource,
} patches,
);
save = async () => { if (!result.callWasSuccessful) {
const currentValue = this.configuration.value.get(); this.dependencies.showErrorNotification((
const currentVersion = yaml.load(currentValue); <p>
const firstVersion = yaml.load(this.editingResource.firstDraft ?? currentValue); Failed to save resource:
const patches = createPatch(firstVersion, currentVersion); {" "}
{result.error}
</p>
));
const result = await this.dependencies.callForPatchResource(this.resource, patches); return;
}
if (!result.callWasSuccessful) { const { kind, name } = result.response;
this.dependencies.showErrorNotification(
<p>
Failed to save resource:
{" "}
{result.error}
</p>,
);
return; this.dependencies.showSuccessNotification((
} <p>
{`${kind} `}
<b>{name}</b>
{" updated."}
</p>
));
const { kind, name } = result.response; runInAction(() => {
this.editingResource.firstDraft = currentValue;
this.dependencies.showSuccessNotification( });
<p> };
{kind}
{" "}
<b>{name}</b>
{" updated."}
</p>,
);
runInAction(() => {
this.editingResource.firstDraft = currentValue;
});
};
} }

View File

@ -22,17 +22,19 @@ interface Dependencies {
model: EditResourceModel; model: EditResourceModel;
} }
const NonInjectedEditResource = observer( const NonInjectedEditResource = observer(({
({ model, tabId }: EditResourceProps & Dependencies) => { model,
return ( tabId,
<div className="EditResource flex column"> }: EditResourceProps & Dependencies) => (
{model.shouldShowErrorAboutNoResource && ( <div className="EditResource flex column">
{
model.shouldShowErrorAboutNoResource
? (
<Notice> <Notice>
Resource not found Resource not found
</Notice> </Notice>
)} )
: (
{!model.shouldShowErrorAboutNoResource && (
<> <>
<InfoPanel <InfoPanel
tabId={tabId} tabId={tabId}
@ -54,31 +56,25 @@ const NonInjectedEditResource = observer(
<span>Namespace:</span> <span>Namespace:</span>
<Badge label={model.namespace} /> <Badge label={model.namespace} />
</div> </div>
)} )} />
/>
<EditorPanel <EditorPanel
tabId={tabId} tabId={tabId}
value={model.configuration.value.get()} value={model.configuration.value.get()}
onChange={model.configuration.onChange} onChange={model.configuration.onChange}
onError={model.configuration.error.onChange} onError={model.configuration.error.onChange} />
/>
</> </>
)} )
</div> }
); </div>
}, ),
); );
export const EditResource = withInjectables<Dependencies, EditResourceProps>( export const EditResource = withInjectables<Dependencies, EditResourceProps>(NonInjectedEditResource, {
NonInjectedEditResource, getPlaceholder: () => (
{ <Spinner center data-testid="edit-resource-tab-spinner" />
getPlaceholder: () => ( ),
<Spinner center data-testid="edit-resource-tab-spinner" /> getProps: async (di, props) => ({
), ...props,
model: await di.inject(editResourceModelInjectable, props.tabId),
getProps: async (di, props) => ({ }),
model: await di.inject(editResourceModelInjectable, props.tabId), });
...props,
}),
},
);

View File

@ -512,9 +512,10 @@ export const getApplicationBuilder = () => {
); );
windowDi.override(hostedClusterIdInjectable, () => clusterStub.id); windowDi.override(hostedClusterIdInjectable, () => clusterStub.id);
windowDi.override(hostedClusterInjectable, () => clusterStub);
// TODO: Figure out a way to remove this stub. // TODO: Figure out a way to remove this stub.
const namespaceStoreStub = { windowDi.override(namespaceStoreInjectable, () => ({
isLoaded: true, isLoaded: true,
get contextNamespaces() { get contextNamespaces() {
return Array.from(selectedNamespaces); return Array.from(selectedNamespaces);
@ -531,10 +532,7 @@ export const getApplicationBuilder = () => {
pickOnlySelected: () => [], pickOnlySelected: () => [],
isSelectedAll: () => false, isSelectedAll: () => false,
getTotalCount: () => namespaceItems.length, getTotalCount: () => namespaceItems.length,
} as Partial<NamespaceStore> as NamespaceStore; } as Partial<NamespaceStore> as NamespaceStore));
windowDi.override(namespaceStoreInjectable, () => namespaceStoreStub);
windowDi.override(hostedClusterInjectable, () => clusterStub);
}); });
return builder; return builder;