1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/integration/__tests__/cluster-pages.tests.ts
Alex Andreev cbee79176d Fix selector
Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
2022-11-28 16:15:17 +03:00

509 lines
18 KiB
TypeScript

/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/*
Cluster tests are run if there is a pre-existing minikube cluster. Before running cluster tests the TEST_NAMESPACE
namespace is removed, if it exists, from the minikube cluster. Resources are created as part of the cluster tests in the
TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube
cluster and vice versa.
*/
import * as utils from "../helpers/utils";
import { minikubeReady } from "../helpers/minikube";
import type { Frame, Page } from "playwright";
import { groupBy, toPairs } from "lodash/fp";
import { pipeline } from "@ogre-tools/fp";
import { describeIf } from "../../src/test-utils/skippers";
const TEST_NAMESPACE = "integration-tests";
describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
let window: Page;
let cleanup: undefined | (() => Promise<void>);
let frame: Frame;
beforeEach(async () => {
({ window, cleanup } = await utils.start());
await utils.clickWelcomeButton(window);
frame = await utils.lauchMinikubeClusterFromCatalog(window);
}, 10 * 60 * 1000);
afterEach(async () => {
await cleanup?.();
}, 10 * 60 * 1000);
it("shows cluster context menu in sidebar", async () => {
await frame.click(`[data-testid="sidebar-cluster-dropdown"]`);
await frame.waitForSelector(`.Menu >> text="Add to Hotbar"`);
await frame.waitForSelector(`.Menu >> text="Settings"`);
await frame.waitForSelector(`.Menu >> text="Disconnect"`);
await frame.waitForSelector(`.Menu >> text="Remove"`);
});
// FIXME: failed locally since metrics might already exist, cc @aleksfront
it.skip("opens cluster settings by clicking link in no-metrics area", async () => {
await frame.locator("text=Open cluster settings >> nth=0").click();
await window.waitForSelector(`[data-testid="metrics-header"]`);
});
it(
"should navigate around common cluster pages",
async () => {
const scenariosByParent = pipeline(
scenarios,
groupBy("parentSidebarItemTestId"),
toPairs,
);
for (const [parentSidebarItemTestId, scenarios] of scenariosByParent) {
if (parentSidebarItemTestId !== "null") {
await frame.click(`[data-testid="${parentSidebarItemTestId}"]`);
}
for (const scenario of scenarios) {
await frame.click(`[data-testid="${scenario.sidebarItemTestId}"]`);
await frame.waitForSelector(
scenario.expectedSelector,
selectorTimeout,
);
}
}
},
10 * 60 * 1000,
);
it(
"show logs and highlight the log search entries",
async () => {
await navigateToPods(frame);
const namespacesSelector = await frame.waitForSelector(
".NamespaceSelect",
);
await namespacesSelector.click();
await namespacesSelector.type("kube-system");
await namespacesSelector.press("Enter");
await namespacesSelector.click();
const kubeApiServerRow = await frame.waitForSelector(
"div.TableCell >> text=kube-apiserver",
);
await kubeApiServerRow.click();
await frame.waitForSelector(".Drawer", { state: "visible" });
const showPodLogsIcon = await frame.waitForSelector(
".Drawer .drawer-title .Icon >> text=subject",
);
showPodLogsIcon.click();
// Check if controls are available
await frame.waitForSelector(".Dock.isOpen");
await frame.waitForSelector("[data-testid=pod-log-list]");
await frame.waitForSelector(".LogResourceSelector");
const logSearchInput = await frame.waitForSelector(
".LogSearch .SearchInput input",
);
await logSearchInput.type(":");
await frame.waitForSelector("[data-testid=search-overlay-active]");
const showTimestampsCheckbox = await frame.waitForSelector(
"[data-testid='log-controls'] .show-timestamps",
);
await showTimestampsCheckbox.click();
const showPreviousCheckbox = await frame.waitForSelector(
"[data-testid='log-controls'] .show-previous",
);
await showPreviousCheckbox.click();
const showWrapLogsCheckbox = await frame.waitForSelector(
"[data-testid='log-controls'] .wrap-logs",
);
await showWrapLogsCheckbox.click();
},
10 * 60 * 1000,
);
it(
"should create a pod with logs and wrap log lines",
async () => {
await navigateToNamespaces(frame);
await frame.waitForSelector("div.TableCell >> text='default'");
try {
await frame.click("li.MenuItem.create-resource-tab", {
// NOTE: the following shouldn't be required, but is because without it a TypeError is thrown
// see: https://github.com/microsoft/playwright/issues/8229
position: {
y: 0,
x: 0,
},
});
} catch (error) {
console.log(error);
await frame.waitForTimeout(100_000);
}
const testPodName = "pod-with-long-logs";
const monacoEditor = await frame.waitForSelector(`.Dock.isOpen [data-test-id="monaco-editor"]`);
await monacoEditor.click();
await monacoEditor.type("apiVersion: v1", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type("kind: Pod", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type("metadata:", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(` name: ${testPodName}`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(`namespace: ${TEST_NAMESPACE}`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.press("Backspace", { delay: 10 });
await monacoEditor.type("spec:", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(" containers:", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(`- name: ${testPodName}`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(" image: busybox:1.28", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(`args: [/bin/sh, -c, 'echo "Short loin exercitation sint, shankle ea andouille consequat. Lorem shoulder dolor ipsum sed quis ham venison ad ribeye filet mignon pancetta. Ham pork loin filet mignon duis, voluptate ullamco minim ea. Turducken minim excepteur, t-bone venison jowl capicola proident biltong pariatur filet mignon irure. Burgdoggen tail irure drumstick fatback fugiat, qui venison duis prosciutto pork chop.","Short ribs pork rump landjaeger, brisket adipisicing non anim magna duis sunt venison pastrami. Kevin consectetur deserunt lorem. Sirloin nisi pig culpa chislic officia enim in. Brisket prosciutto quis jerky chuck rump filet mignon, labore venison elit est. In kevin drumstick, ut elit pig short loin. Boudin labore ut id nisi. Non quis ullamco biltong.","Leberkas cillum dolore ad chuck ball tip laboris pork chop picanha dolore sunt landjaeger. Occaecat cow exercitation, lorem reprehenderit laboris excepteur venison salami tenderloin irure in ut eu ham. Irure incididunt swine, cupim adipisicing drumstick cupidatat salami tail eiusmod. Irure sausage commodo veniam strip steak do. Ball tip shankle capicola deserunt swine, jowl pork loin bacon spare ribs.","Cow tongue pig meatball short ribs fugiat spare ribs labore kevin. Tempor reprehenderit irure veniam, andouille deserunt kevin dolore magna ad. Ham hock veniam alcatra ea ribeye aute commodo ground round. Ut nisi sunt dolore, sirloin velit officia flank ad short ribs pancetta deserunt pastrami leberkas. Turducken ham hock short ribs, tenderloin swine eiusmod consequat irure excepteur.","Veniam short ribs reprehenderit tempor ea velit doner lorem shankle exercitation do. Mollit veniam shoulder fugiat dolore, duis nostrud tenderloin. Ut exercitation quis, ex cupidatat shank dolore pastrami irure ut pork chop. Nulla boudin tail irure deserunt, cupidatat flank cillum cow pork loin hamburger shoulder ut ipsum. Turducken eu chicken in qui pig. Sunt tongue esse, ut ex ut hamburger spare ribs culpa.";']`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await frame.click(".Dock .Button >> text='Create'");
await frame.waitForSelector(`.TableCell >> text=${testPodName}`);
await frame.click(".TableRow .TableCell.menu");
await frame.click(".MenuItem >> text=Logs");
await frame.waitForSelector(".Dock.isOpen");
const logLine = await frame.waitForSelector("[data-testid=pod-log-list] [data-index=0]");
let boundingBox = await logLine.boundingBox();
expect(boundingBox?.height).toBeLessThan(30);
const showWrapLogsCheckbox = await frame.waitForSelector(
"[data-testid='log-controls'] .wrap-logs",
);
await showWrapLogsCheckbox.click();
boundingBox = await logLine.boundingBox();
expect(boundingBox?.height).toBeGreaterThan(100);
},
10 * 60 * 1000,
);
it(
"should show the default namespaces",
async () => {
await navigateToNamespaces(frame);
await frame.waitForSelector("div.TableCell >> text='default'");
await frame.waitForSelector("div.TableCell >> text='kube-system'");
},
10 * 60 * 1000,
);
it(
`should create the ${TEST_NAMESPACE} and a pod in the namespace and then remove that pod via the context menu`,
async () => {
await navigateToNamespaces(frame);
await frame.click("button.add-button");
await frame.waitForSelector(
"div.AddNamespaceDialog >> text='Create Namespace'",
);
const namespaceNameInput = await frame.waitForSelector(
".AddNamespaceDialog input",
);
await namespaceNameInput.type(TEST_NAMESPACE);
await namespaceNameInput.press("Enter");
await frame.waitForSelector(`div.TableCell >> text=${TEST_NAMESPACE}`);
await navigateToPods(frame);
const namespacesSelector = await frame.waitForSelector(
".NamespaceSelect",
);
await namespacesSelector.click();
await namespacesSelector.type(TEST_NAMESPACE);
await namespacesSelector.press("Enter");
await namespacesSelector.click();
await frame.click(".Icon.new-dock-tab");
try {
await frame.click("li.MenuItem.create-resource-tab", {
// NOTE: the following shouldn't be required, but is because without it a TypeError is thrown
// see: https://github.com/microsoft/playwright/issues/8229
position: {
y: 0,
x: 0,
},
});
} catch (error) {
console.log(error);
await frame.waitForTimeout(100_000);
}
const testPodName = "nginx-create-pod-test";
const monacoEditor = await frame.waitForSelector(`.Dock.isOpen [data-test-id="monaco-editor"]`);
await monacoEditor.click();
await monacoEditor.type("apiVersion: v1", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type("kind: Pod", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type("metadata:", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(` name: ${testPodName}`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(`namespace: ${TEST_NAMESPACE}`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.press("Backspace", { delay: 10 });
await monacoEditor.type("spec:", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(" containers:", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(`- name: ${testPodName}`, { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await monacoEditor.type(" image: nginx:alpine", { delay: 10 });
await monacoEditor.press("Enter", { delay: 10 });
await frame.click(".Dock .Button >> text='Create'");
await frame.waitForSelector(`.TableCell >> text=${testPodName}`);
await frame.click(".TableRow .TableCell.menu");
await frame.click(".MenuItem >> text=Delete");
await frame.click("button >> text=Remove");
await frame.waitForSelector(`.TableCell >> text=${testPodName}`, { state: "detached" });
},
10 * 60 * 1000,
);
});
const selectorTimeout = { timeout: 30000 };
const scenarios = [
{
expectedSelector: "div[data-testid='cluster-overview-page'] div.label",
parentSidebarItemTestId: null,
sidebarItemTestId: "sidebar-item-link-for-cluster-overview",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: null,
sidebarItemTestId: "sidebar-item-link-for-nodes",
},
{
expectedSelector: 'h5 >> text="Overview"',
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-overview",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-pods",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-deployments",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-daemon-sets",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-stateful-sets",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-replica-sets",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-jobs",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-workloads",
sidebarItemTestId: "sidebar-item-link-for-cron-jobs",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-config",
sidebarItemTestId: "sidebar-item-link-for-config-maps",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-config",
sidebarItemTestId: "sidebar-item-link-for-secrets",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-config",
sidebarItemTestId: "sidebar-item-link-for-resource-quotas",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-config",
sidebarItemTestId: "sidebar-item-link-for-limit-ranges",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-config",
sidebarItemTestId: "sidebar-item-link-for-horizontal-pod-auto-scalers",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-config",
sidebarItemTestId: "sidebar-item-link-for-pod-disruption-budgets",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-network",
sidebarItemTestId: "sidebar-item-link-for-services",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-network",
sidebarItemTestId: "sidebar-item-link-for-endpoints",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-network",
sidebarItemTestId: "sidebar-item-link-for-ingresses",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-network",
sidebarItemTestId: "sidebar-item-link-for-network-policies",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-storage",
sidebarItemTestId: "sidebar-item-link-for-persistent-volume-claims",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-storage",
sidebarItemTestId: "sidebar-item-link-for-persistent-volumes",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-storage",
sidebarItemTestId: "sidebar-item-link-for-storage-classes",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: null,
sidebarItemTestId: "sidebar-item-link-for-namespaces",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: null,
sidebarItemTestId: "sidebar-item-link-for-events",
},
{
expectedSelector: "div.HelmCharts input",
parentSidebarItemTestId: "sidebar-item-link-for-helm",
sidebarItemTestId: "sidebar-item-link-for-charts",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-helm",
sidebarItemTestId: "sidebar-item-link-for-releases",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-service-accounts",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-cluster-roles",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-roles",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-cluster-role-bindings",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: "sidebar-item-link-for-user-management",
sidebarItemTestId: "sidebar-item-link-for-role-bindings",
},
{
expectedSelector: "h5.title",
parentSidebarItemTestId: null,
sidebarItemTestId: "sidebar-item-link-for-custom-resources",
},
];
const navigateToPods = async (frame: Frame) => {
await frame.click(`[data-testid="sidebar-item-link-for-workloads"]`);
await frame.click(`[data-testid="sidebar-item-link-for-pods"]`);
};
const navigateToNamespaces = async (frame: Frame) => {
await frame.click(`[data-testid="sidebar-item-link-for-namespaces"]`);
};