mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add some tests for LogSearch, fix extension API and sendCommand
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
d9a8d8fc8a
commit
b4612200f0
@ -33,7 +33,7 @@ export const asLegacyGlobalObjectForExtensionApiWithModifications = <
|
|||||||
...instantiationParameter,
|
...instantiationParameter,
|
||||||
);
|
);
|
||||||
|
|
||||||
const propertyValue = instance[propertyName] ?? otherFields[propertyName as any];
|
const propertyValue = instance[propertyName] ?? otherFields[propertyName as any]();
|
||||||
|
|
||||||
if (typeof propertyValue === "function") {
|
if (typeof propertyValue === "function") {
|
||||||
return function (...args: any[]) {
|
return function (...args: any[]) {
|
||||||
|
|||||||
@ -37,10 +37,6 @@ jest.mock("electron", () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const getComponent = (model: LogTabViewModel) => (
|
|
||||||
<LogResourceSelector model={model} />
|
|
||||||
);
|
|
||||||
|
|
||||||
function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependencies>): LogTabViewModel {
|
function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependencies>): LogTabViewModel {
|
||||||
return new LogTabViewModel(tabId, {
|
return new LogTabViewModel(tabId, {
|
||||||
getLogs: jest.fn(),
|
getLogs: jest.fn(),
|
||||||
@ -54,6 +50,7 @@ function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependen
|
|||||||
stopLoadingLogs: jest.fn(),
|
stopLoadingLogs: jest.fn(),
|
||||||
getPodById: jest.fn(),
|
getPodById: jest.fn(),
|
||||||
getPodsByOwnerId: jest.fn(),
|
getPodsByOwnerId: jest.fn(),
|
||||||
|
createSearchStore: jest.fn(),
|
||||||
...deps,
|
...deps,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -144,14 +141,14 @@ describe("<LogResourceSelector />", () => {
|
|||||||
|
|
||||||
it("renders w/o errors", () => {
|
it("renders w/o errors", () => {
|
||||||
const model = getOnePodViewModel("foobar");
|
const model = getOnePodViewModel("foobar");
|
||||||
const { container } = render(getComponent(model));
|
const { container } = render(<LogResourceSelector model={model} />);
|
||||||
|
|
||||||
expect(container).toBeInstanceOf(HTMLElement);
|
expect(container).toBeInstanceOf(HTMLElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders proper namespace", async () => {
|
it("renders proper namespace", async () => {
|
||||||
const model = getOnePodViewModel("foobar");
|
const model = getOnePodViewModel("foobar");
|
||||||
const { findByTestId } = render(getComponent(model));
|
const { findByTestId } = render(<LogResourceSelector model={model} />);
|
||||||
const ns = await findByTestId("namespace-badge");
|
const ns = await findByTestId("namespace-badge");
|
||||||
|
|
||||||
expect(ns).toHaveTextContent("default");
|
expect(ns).toHaveTextContent("default");
|
||||||
@ -159,7 +156,7 @@ describe("<LogResourceSelector />", () => {
|
|||||||
|
|
||||||
it("renders proper selected items within dropdowns", async () => {
|
it("renders proper selected items within dropdowns", async () => {
|
||||||
const model = getOnePodViewModel("foobar");
|
const model = getOnePodViewModel("foobar");
|
||||||
const { findByText } = render(getComponent(model));
|
const { findByText } = render(<LogResourceSelector model={model} />);
|
||||||
|
|
||||||
expect(await findByText("dockerExporter")).toBeInTheDocument();
|
expect(await findByText("dockerExporter")).toBeInTheDocument();
|
||||||
expect(await findByText("docker-exporter")).toBeInTheDocument();
|
expect(await findByText("docker-exporter")).toBeInTheDocument();
|
||||||
@ -167,7 +164,7 @@ describe("<LogResourceSelector />", () => {
|
|||||||
|
|
||||||
it("renders sibling pods in dropdown", async () => {
|
it("renders sibling pods in dropdown", async () => {
|
||||||
const model = getFewPodsTabData("foobar");
|
const model = getFewPodsTabData("foobar");
|
||||||
const { container, findByText } = render(getComponent(model));
|
const { container, findByText } = render(<LogResourceSelector model={model} />);
|
||||||
|
|
||||||
selectEvent.openMenu(container.querySelector(".pod-selector"));
|
selectEvent.openMenu(container.querySelector(".pod-selector"));
|
||||||
expect(await findByText("deploymentPod2", { selector: ".pod-selector-menu .Select__option" })).toBeInTheDocument();
|
expect(await findByText("deploymentPod2", { selector: ".pod-selector-menu .Select__option" })).toBeInTheDocument();
|
||||||
@ -176,7 +173,7 @@ describe("<LogResourceSelector />", () => {
|
|||||||
|
|
||||||
it("renders sibling containers in dropdown", async () => {
|
it("renders sibling containers in dropdown", async () => {
|
||||||
const model = getFewPodsTabData("foobar");
|
const model = getFewPodsTabData("foobar");
|
||||||
const { findByText, container } = render(getComponent(model));
|
const { findByText, container } = render(<LogResourceSelector model={model} />);
|
||||||
|
|
||||||
selectEvent.openMenu(container.querySelector(".container-selector"));
|
selectEvent.openMenu(container.querySelector(".container-selector"));
|
||||||
expect(await findByText("node-exporter-1")).toBeInTheDocument();
|
expect(await findByText("node-exporter-1")).toBeInTheDocument();
|
||||||
@ -186,7 +183,7 @@ describe("<LogResourceSelector />", () => {
|
|||||||
|
|
||||||
it("renders pod owner as dropdown title", async () => {
|
it("renders pod owner as dropdown title", async () => {
|
||||||
const model = getFewPodsTabData("foobar");
|
const model = getFewPodsTabData("foobar");
|
||||||
const { findByText, container } = render(getComponent(model));
|
const { findByText, container } = render(<LogResourceSelector model={model} />);
|
||||||
|
|
||||||
selectEvent.openMenu(container.querySelector(".pod-selector"));
|
selectEvent.openMenu(container.querySelector(".pod-selector"));
|
||||||
expect(await findByText("super-deployment")).toBeInTheDocument();
|
expect(await findByText("super-deployment")).toBeInTheDocument();
|
||||||
@ -195,7 +192,7 @@ describe("<LogResourceSelector />", () => {
|
|||||||
it("updates tab name if selected pod changes", async () => {
|
it("updates tab name if selected pod changes", async () => {
|
||||||
const updateTabName = jest.fn();
|
const updateTabName = jest.fn();
|
||||||
const model = getFewPodsTabData("foobar", { updateTabName });
|
const model = getFewPodsTabData("foobar", { updateTabName });
|
||||||
const { findByText, container } = render(getComponent(model));
|
const { findByText, container } = render(<LogResourceSelector model={model} />);
|
||||||
|
|
||||||
selectEvent.openMenu(container.querySelector(".pod-selector"));
|
selectEvent.openMenu(container.querySelector(".pod-selector"));
|
||||||
|
|
||||||
|
|||||||
@ -3,4 +3,101 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {};
|
import React from "react";
|
||||||
|
import { screen } from "@testing-library/react";
|
||||||
|
import { Pod } from "../../../../../common/k8s-api/endpoints";
|
||||||
|
import { dockerPod } from "./pod.mock";
|
||||||
|
import { getDiForUnitTesting } from "../../../../getDiForUnitTesting";
|
||||||
|
import type { DiRender } from "../../../test-utils/renderFor";
|
||||||
|
import { renderFor } from "../../../test-utils/renderFor";
|
||||||
|
import { LogTabViewModel, LogTabViewModelDependencies } from "../logs-view-model";
|
||||||
|
import type { TabId } from "../../dock/store";
|
||||||
|
import { LogSearch } from "../search";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { SearchStore } from "../../../../search-store/search-store";
|
||||||
|
|
||||||
|
function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependencies>): LogTabViewModel {
|
||||||
|
return new LogTabViewModel(tabId, {
|
||||||
|
getLogs: jest.fn(),
|
||||||
|
getLogsWithoutTimestamps: jest.fn(),
|
||||||
|
getTimestampSplitLogs: jest.fn(),
|
||||||
|
getLogTabData: jest.fn(),
|
||||||
|
setLogTabData: jest.fn(),
|
||||||
|
loadLogs: jest.fn(),
|
||||||
|
reloadLogs: jest.fn(),
|
||||||
|
updateTabName: jest.fn(),
|
||||||
|
stopLoadingLogs: jest.fn(),
|
||||||
|
getPodById: jest.fn(),
|
||||||
|
getPodsByOwnerId: jest.fn(),
|
||||||
|
createSearchStore: () => new SearchStore(),
|
||||||
|
...deps,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOnePodViewModel = (tabId: TabId, deps: Partial<LogTabViewModelDependencies> = {}): LogTabViewModel => {
|
||||||
|
const selectedPod = new Pod(dockerPod);
|
||||||
|
|
||||||
|
return mockLogTabViewModel(tabId, {
|
||||||
|
getLogTabData: () => ({
|
||||||
|
selectedPodId: selectedPod.getId(),
|
||||||
|
selectedContainer: selectedPod.getContainers()[0].name,
|
||||||
|
namespace: selectedPod.getNs(),
|
||||||
|
showPrevious: false,
|
||||||
|
showTimestamps: false,
|
||||||
|
}),
|
||||||
|
getPodById: (id) => {
|
||||||
|
if (id === selectedPod.getId()) {
|
||||||
|
return selectedPod;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
...deps,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("LogSearch tests", () => {
|
||||||
|
let render: DiRender;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
render = renderFor(di);
|
||||||
|
|
||||||
|
await di.runSetups();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders w/o errors", () => {
|
||||||
|
const model = getOnePodViewModel("foobar");
|
||||||
|
const { container } = render(
|
||||||
|
<LogSearch
|
||||||
|
model={model}
|
||||||
|
scrollToOverlay={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toBeInstanceOf(HTMLElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should scroll to new active overlay when clicking the previous button", async () => {
|
||||||
|
const scrollToOverlay = jest.fn();
|
||||||
|
const model = getOnePodViewModel("foobar", {
|
||||||
|
getLogsWithoutTimestamps: () => [
|
||||||
|
"hello",
|
||||||
|
"world",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<LogSearch
|
||||||
|
model={model}
|
||||||
|
scrollToOverlay={scrollToOverlay}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
userEvent.click(await screen.findByPlaceholderText("Search..."));
|
||||||
|
userEvent.keyboard("o");
|
||||||
|
userEvent.click(await screen.findByText("keyboard_arrow_up"));
|
||||||
|
expect(scrollToOverlay).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import setLogTabDataInjectable from "./set-log-tab-data.injectable";
|
|||||||
import updateTabNameInjectable from "./update-tab-name.injectable";
|
import updateTabNameInjectable from "./update-tab-name.injectable";
|
||||||
import stopLoadingLogsInjectable from "./stop-loading-logs.injectable";
|
import stopLoadingLogsInjectable from "./stop-loading-logs.injectable";
|
||||||
import { podsStore } from "../../+workloads-pods/pods.store";
|
import { podsStore } from "../../+workloads-pods/pods.store";
|
||||||
|
import createSearchStoreInjectable from "../../../search-store/create-search-store.injectable";
|
||||||
|
|
||||||
export interface InstantiateArgs {
|
export interface InstantiateArgs {
|
||||||
tabId: TabId;
|
tabId: TabId;
|
||||||
@ -33,6 +34,7 @@ const logsViewModelInjectable = getInjectable({
|
|||||||
stopLoadingLogs: di.inject(stopLoadingLogsInjectable),
|
stopLoadingLogs: di.inject(stopLoadingLogsInjectable),
|
||||||
getPodById: id => podsStore.getById(id),
|
getPodById: id => podsStore.getById(id),
|
||||||
getPodsByOwnerId: id => podsStore.getPodsByOwnerId(id),
|
getPodsByOwnerId: id => podsStore.getPodsByOwnerId(id),
|
||||||
|
createSearchStore: di.inject(createSearchStoreInjectable),
|
||||||
}),
|
}),
|
||||||
lifecycle: lifecycleEnum.transient,
|
lifecycle: lifecycleEnum.transient,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
import type { LogTabData } from "./tab-store";
|
import type { LogTabData } from "./tab-store";
|
||||||
import { computed, IComputedValue } from "mobx";
|
import { computed, IComputedValue } from "mobx";
|
||||||
import type { TabId } from "../dock/store";
|
import type { TabId } from "../dock/store";
|
||||||
import { SearchStore } from "../../../search-store/search-store";
|
import type { SearchStore } from "../../../search-store/search-store";
|
||||||
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
import type { Pod } from "../../../../common/k8s-api/endpoints";
|
||||||
|
|
||||||
export interface LogTabViewModelDependencies {
|
export interface LogTabViewModelDependencies {
|
||||||
@ -20,6 +20,7 @@ export interface LogTabViewModelDependencies {
|
|||||||
stopLoadingLogs: (tabId: TabId) => void;
|
stopLoadingLogs: (tabId: TabId) => void;
|
||||||
getPodById: (id: string) => Pod | undefined;
|
getPodById: (id: string) => Pod | undefined;
|
||||||
getPodsByOwnerId: (id: string) => Pod[];
|
getPodsByOwnerId: (id: string) => Pod[];
|
||||||
|
createSearchStore: () => SearchStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogTabViewModel {
|
export class LogTabViewModel {
|
||||||
@ -51,7 +52,7 @@ export class LogTabViewModel {
|
|||||||
|
|
||||||
return this.dependencies.getPodById(data.selectedPodId);
|
return this.dependencies.getPodById(data.selectedPodId);
|
||||||
});
|
});
|
||||||
readonly searchStore = new SearchStore();
|
readonly searchStore = this.dependencies.createSearchStore();
|
||||||
|
|
||||||
updateLogTabData = (partialData: Partial<LogTabData>) => {
|
updateLogTabData = (partialData: Partial<LogTabData>) => {
|
||||||
this.dependencies.setLogTabData(this.tabId, { ...this.logTabData.get(), ...partialData });
|
this.dependencies.setLogTabData(this.tabId, { ...this.logTabData.get(), ...partialData });
|
||||||
|
|||||||
@ -41,27 +41,25 @@ async function sendCommand({ selectTab, createTerminalTab, getTerminalApi }: Dep
|
|||||||
if (tabId) {
|
if (tabId) {
|
||||||
selectTab(tabId);
|
selectTab(tabId);
|
||||||
} else {
|
} else {
|
||||||
const tab = createTerminalTab();
|
tabId = createTerminalTab().id;
|
||||||
|
|
||||||
tabId = tab.id;
|
|
||||||
await when(() => Boolean(getTerminalApi(tab.id)));
|
|
||||||
|
|
||||||
const shellIsReady = when(() => getTerminalApi(tab.id).isReady);
|
|
||||||
const notifyVeryLong = setTimeout(() => {
|
|
||||||
shellIsReady.cancel();
|
|
||||||
Notifications.info(
|
|
||||||
"If terminal shell is not ready please check your shell init files, if applicable.",
|
|
||||||
{
|
|
||||||
timeout: 4_000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}, 10_000);
|
|
||||||
|
|
||||||
await shellIsReady.catch(noop);
|
|
||||||
clearTimeout(notifyVeryLong);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await when(() => Boolean(getTerminalApi(tabId)));
|
||||||
|
|
||||||
const terminalApi = getTerminalApi(tabId);
|
const terminalApi = getTerminalApi(tabId);
|
||||||
|
const shellIsReady = when(() =>terminalApi.isReady);
|
||||||
|
const notifyVeryLong = setTimeout(() => {
|
||||||
|
shellIsReady.cancel();
|
||||||
|
Notifications.info(
|
||||||
|
"If terminal shell is not ready please check your shell init files, if applicable.",
|
||||||
|
{
|
||||||
|
timeout: 4_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}, 10_000);
|
||||||
|
|
||||||
|
await shellIsReady.catch(noop);
|
||||||
|
clearTimeout(notifyVeryLong);
|
||||||
|
|
||||||
if (terminalApi) {
|
if (terminalApi) {
|
||||||
if (options.enter) {
|
if (options.enter) {
|
||||||
|
|||||||
@ -353,6 +353,7 @@ export class Input extends React.Component<InputProps, State> {
|
|||||||
dirty: _dirty, // excluded from passing to input-element
|
dirty: _dirty, // excluded from passing to input-element
|
||||||
defaultValue,
|
defaultValue,
|
||||||
trim,
|
trim,
|
||||||
|
blurOnEnter,
|
||||||
...inputProps
|
...inputProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { focused, dirty, valid, validating, errors } = this.state;
|
const { focused, dirty, valid, validating, errors } = this.state;
|
||||||
|
|||||||
13
src/renderer/search-store/create-search-store.injectable.ts
Normal file
13
src/renderer/search-store/create-search-store.injectable.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { SearchStore } from "./search-store";
|
||||||
|
|
||||||
|
const createSearchStoreInjectable = getInjectable({
|
||||||
|
instantiate: () => () => new SearchStore(),
|
||||||
|
lifecycle: lifecycleEnum.singleton,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createSearchStoreInjectable;
|
||||||
Loading…
Reference in New Issue
Block a user