1
0
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:
Sebastian Malton 2022-01-26 10:27:47 -05:00
parent d9a8d8fc8a
commit b4612200f0
8 changed files with 142 additions and 33 deletions

View File

@ -33,7 +33,7 @@ export const asLegacyGlobalObjectForExtensionApiWithModifications = <
...instantiationParameter,
);
const propertyValue = instance[propertyName] ?? otherFields[propertyName as any];
const propertyValue = instance[propertyName] ?? otherFields[propertyName as any]();
if (typeof propertyValue === "function") {
return function (...args: any[]) {

View File

@ -37,10 +37,6 @@ jest.mock("electron", () => ({
},
}));
const getComponent = (model: LogTabViewModel) => (
<LogResourceSelector model={model} />
);
function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependencies>): LogTabViewModel {
return new LogTabViewModel(tabId, {
getLogs: jest.fn(),
@ -54,6 +50,7 @@ function mockLogTabViewModel(tabId: TabId, deps: Partial<LogTabViewModelDependen
stopLoadingLogs: jest.fn(),
getPodById: jest.fn(),
getPodsByOwnerId: jest.fn(),
createSearchStore: jest.fn(),
...deps,
});
}
@ -144,14 +141,14 @@ describe("<LogResourceSelector />", () => {
it("renders w/o errors", () => {
const model = getOnePodViewModel("foobar");
const { container } = render(getComponent(model));
const { container } = render(<LogResourceSelector model={model} />);
expect(container).toBeInstanceOf(HTMLElement);
});
it("renders proper namespace", async () => {
const model = getOnePodViewModel("foobar");
const { findByTestId } = render(getComponent(model));
const { findByTestId } = render(<LogResourceSelector model={model} />);
const ns = await findByTestId("namespace-badge");
expect(ns).toHaveTextContent("default");
@ -159,7 +156,7 @@ describe("<LogResourceSelector />", () => {
it("renders proper selected items within dropdowns", async () => {
const model = getOnePodViewModel("foobar");
const { findByText } = render(getComponent(model));
const { findByText } = render(<LogResourceSelector model={model} />);
expect(await findByText("dockerExporter")).toBeInTheDocument();
expect(await findByText("docker-exporter")).toBeInTheDocument();
@ -167,7 +164,7 @@ describe("<LogResourceSelector />", () => {
it("renders sibling pods in dropdown", async () => {
const model = getFewPodsTabData("foobar");
const { container, findByText } = render(getComponent(model));
const { container, findByText } = render(<LogResourceSelector model={model} />);
selectEvent.openMenu(container.querySelector(".pod-selector"));
expect(await findByText("deploymentPod2", { selector: ".pod-selector-menu .Select__option" })).toBeInTheDocument();
@ -176,7 +173,7 @@ describe("<LogResourceSelector />", () => {
it("renders sibling containers in dropdown", async () => {
const model = getFewPodsTabData("foobar");
const { findByText, container } = render(getComponent(model));
const { findByText, container } = render(<LogResourceSelector model={model} />);
selectEvent.openMenu(container.querySelector(".container-selector"));
expect(await findByText("node-exporter-1")).toBeInTheDocument();
@ -186,7 +183,7 @@ describe("<LogResourceSelector />", () => {
it("renders pod owner as dropdown title", async () => {
const model = getFewPodsTabData("foobar");
const { findByText, container } = render(getComponent(model));
const { findByText, container } = render(<LogResourceSelector model={model} />);
selectEvent.openMenu(container.querySelector(".pod-selector"));
expect(await findByText("super-deployment")).toBeInTheDocument();
@ -195,7 +192,7 @@ describe("<LogResourceSelector />", () => {
it("updates tab name if selected pod changes", async () => {
const updateTabName = jest.fn();
const model = getFewPodsTabData("foobar", { updateTabName });
const { findByText, container } = render(getComponent(model));
const { findByText, container } = render(<LogResourceSelector model={model} />);
selectEvent.openMenu(container.querySelector(".pod-selector"));

View File

@ -3,4 +3,101 @@
* 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();
});
});

View File

@ -15,6 +15,7 @@ import setLogTabDataInjectable from "./set-log-tab-data.injectable";
import updateTabNameInjectable from "./update-tab-name.injectable";
import stopLoadingLogsInjectable from "./stop-loading-logs.injectable";
import { podsStore } from "../../+workloads-pods/pods.store";
import createSearchStoreInjectable from "../../../search-store/create-search-store.injectable";
export interface InstantiateArgs {
tabId: TabId;
@ -33,6 +34,7 @@ const logsViewModelInjectable = getInjectable({
stopLoadingLogs: di.inject(stopLoadingLogsInjectable),
getPodById: id => podsStore.getById(id),
getPodsByOwnerId: id => podsStore.getPodsByOwnerId(id),
createSearchStore: di.inject(createSearchStoreInjectable),
}),
lifecycle: lifecycleEnum.transient,
});

View File

@ -5,7 +5,7 @@
import type { LogTabData } from "./tab-store";
import { computed, IComputedValue } from "mobx";
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";
export interface LogTabViewModelDependencies {
@ -20,6 +20,7 @@ export interface LogTabViewModelDependencies {
stopLoadingLogs: (tabId: TabId) => void;
getPodById: (id: string) => Pod | undefined;
getPodsByOwnerId: (id: string) => Pod[];
createSearchStore: () => SearchStore;
}
export class LogTabViewModel {
@ -51,7 +52,7 @@ export class LogTabViewModel {
return this.dependencies.getPodById(data.selectedPodId);
});
readonly searchStore = new SearchStore();
readonly searchStore = this.dependencies.createSearchStore();
updateLogTabData = (partialData: Partial<LogTabData>) => {
this.dependencies.setLogTabData(this.tabId, { ...this.logTabData.get(), ...partialData });

View File

@ -41,27 +41,25 @@ async function sendCommand({ selectTab, createTerminalTab, getTerminalApi }: Dep
if (tabId) {
selectTab(tabId);
} else {
const tab = createTerminalTab();
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);
tabId = createTerminalTab().id;
}
await when(() => Boolean(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 (options.enter) {

View File

@ -353,6 +353,7 @@ export class Input extends React.Component<InputProps, State> {
dirty: _dirty, // excluded from passing to input-element
defaultValue,
trim,
blurOnEnter,
...inputProps
} = this.props;
const { focused, dirty, valid, validating, errors } = this.state;

View 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;