From be3bd7937efdb1d266dab6f9aa22eb21e2d2831c Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 9 Mar 2021 18:30:36 +0200 Subject: [PATCH] integration tests: refactoring & fixes Signed-off-by: Roman --- integration/__tests__/app.tests.ts | 77 +-- integration/__tests__/cluster-common.tests.ts | 64 ++ integration/__tests__/cluster-pages.tests.ts | 611 +++++++----------- .../__tests__/command-palette.tests.ts | 21 +- integration/__tests__/logs.tests.ts | 56 ++ integration/__tests__/workspace.tests.ts | 68 +- integration/helpers/index.ts | 7 + integration/helpers/minikube.ts | 7 + integration/helpers/selectors.ts | 13 + integration/helpers/utils.ts | 23 +- 10 files changed, 457 insertions(+), 490 deletions(-) create mode 100644 integration/__tests__/cluster-common.tests.ts create mode 100644 integration/__tests__/logs.tests.ts create mode 100644 integration/helpers/index.ts create mode 100644 integration/helpers/selectors.ts diff --git a/integration/__tests__/app.tests.ts b/integration/__tests__/app.tests.ts index 56d4cc7774..716082d12f 100644 --- a/integration/__tests__/app.tests.ts +++ b/integration/__tests__/app.tests.ts @@ -1,55 +1,42 @@ -import { Application } from "spectron"; -import * as utils from "../helpers/utils"; -import { listHelmRepositories } from "../helpers/utils"; -import { fail } from "assert"; +import { clickWhatsNew, listHelmRepositories, setupAppLifecycle } from "../helpers"; jest.setTimeout(60000); -describe("Lens integration tests", () => { - let app: Application; +describe("App start", () => { + const runtime = setupAppLifecycle(); - describe("app start", () => { - beforeAll(async () => app = await utils.appStart(), 20000); + it('shows "whats new"', async () => { + await clickWhatsNew(runtime.app); + }); - afterAll(async () => { - if (app?.isRunning()) { - await utils.tearDown(app); + it('shows "add cluster"', async () => { + await runtime.app.electron.ipcRenderer.send("test-menu-item-click", "File", "Add Cluster"); + await runtime.app.client.waitUntilTextExists("h2", "Add Cluster"); + }); + + describe("preferences page", () => { + it('shows "preferences"', async () => { + const appName: string = process.platform === "darwin" ? "Lens" : "File"; + + await runtime.app.electron.ipcRenderer.send("test-menu-item-click", appName, "Preferences"); + await runtime.app.client.waitUntilTextExists("h2", "Preferences"); + }); + + it("ensures helm repos", async () => { + const repos = await listHelmRepositories(); + + if (!repos[0]) { + fail("Lens failed to add Bitnami repository"); } - }); - it('shows "whats new"', async () => { - await utils.clickWhatsNew(app); - }); - - it('shows "add cluster"', async () => { - await app.electron.ipcRenderer.send("test-menu-item-click", "File", "Add Cluster"); - await app.client.waitUntilTextExists("h2", "Add Cluster"); - }); - - describe("preferences page", () => { - it('shows "preferences"', async () => { - const appName: string = process.platform === "darwin" ? "Lens" : "File"; - - await app.electron.ipcRenderer.send("test-menu-item-click", appName, "Preferences"); - await app.client.waitUntilTextExists("h2", "Preferences"); - }); - - it("ensures helm repos", async () => { - const repos = await listHelmRepositories(); - - if (!repos[0]) { - fail("Lens failed to add Bitnami repository"); - } - - await app.client.waitUntilTextExists("div.repos #message-bitnami", repos[0].name); // wait for the helm-cli to fetch the repo(s) - await app.client.click("#HelmRepoSelect"); // click the repo select to activate the drop-down - await app.client.waitUntilTextExists("div.Select__option", ""); // wait for at least one option to appear (any text) - }); - }); - - it.skip('quits Lens"', async () => { - await app.client.keys(["Meta", "Q"]); - await app.client.keys("Meta"); + await runtime.app.client.waitUntilTextExists("div.repos #message-bitnami", repos[0].name); // wait for the helm-cli to fetch the repo(s) + await runtime.app.client.click("#HelmRepoSelect"); // click the repo select to activate the drop-down + await runtime.app.client.waitUntilTextExists("div.Select__option", ""); // wait for at least one option to appear (any text) }); }); + + it.skip('quits Lens"', async () => { + await runtime.app.client.keys(["Meta", "Q"]); + await runtime.app.client.keys("Meta"); + }); }); diff --git a/integration/__tests__/cluster-common.tests.ts b/integration/__tests__/cluster-common.tests.ts new file mode 100644 index 0000000000..1d65c5b4f2 --- /dev/null +++ b/integration/__tests__/cluster-common.tests.ts @@ -0,0 +1,64 @@ +import { addClusterAndOpen, BACKSPACE, describeIf, getMainMenuSelectors, minikubeReady, setupAppLifecycle } from "../helpers"; + +jest.setTimeout(60000); + +describe("Lens cluster common tests", () => { + const TEST_NAMESPACE = "cluster-common-int-tests"; + const ready = minikubeReady(TEST_NAMESPACE); + + describeIf(ready)("cluster operations", () => { + const runtime = setupAppLifecycle(); + + beforeAll(async () => { + await addClusterAndOpen(runtime.app); + }); + + it("shows default namespace", async () => { + await runtime.app.client.click('a[href="/namespaces"]'); + await runtime.app.client.waitUntilTextExists("div.TableCell", "default"); + await runtime.app.client.waitUntilTextExists("div.TableCell", "kube-system"); + }); + + it(`creates ${TEST_NAMESPACE} namespace`, async () => { + await runtime.app.client.click('a[href="/namespaces"]'); + await runtime.app.client.waitUntilTextExists("div.TableCell", "default"); + await runtime.app.client.waitUntilTextExists("div.TableCell", "kube-system"); + await runtime.app.client.click("button.add-button"); + await runtime.app.client.waitUntilTextExists("div.AddNamespaceDialog", "Create Namespace"); + await runtime.app.client.keys(`${TEST_NAMESPACE}\n`); + await runtime.app.client.waitForExist(`.name=${TEST_NAMESPACE}`); + }); + + it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => { + await runtime.app.client.click(getMainMenuSelectors("workloads").expandIcon); + await runtime.app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); + await runtime.app.client.click('a[href^="/pods"]'); + + await runtime.app.client.click(".NamespaceSelect"); + await runtime.app.client.keys(TEST_NAMESPACE); + await runtime.app.client.keys("Enter");// "\uE007" + await runtime.app.client.click(".Icon.new-dock-tab"); + await runtime.app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource"); + await runtime.app.client.click("li.MenuItem.create-resource-tab"); + await runtime.app.client.waitForVisible(".CreateResource div.ace_content"); + // Write pod manifest to editor + await runtime.app.client.keys("apiVersion: v1\n"); + await runtime.app.client.keys("kind: Pod\n"); + await runtime.app.client.keys("metadata:\n"); + await runtime.app.client.keys(" name: nginx-create-pod-test\n"); + await runtime.app.client.keys(`namespace: ${TEST_NAMESPACE}\n`); + await runtime.app.client.keys(`${BACKSPACE}spec:\n`); + await runtime.app.client.keys(" containers:\n"); + await runtime.app.client.keys("- name: nginx-create-pod-test\n"); + await runtime.app.client.keys(" image: nginx:alpine\n"); + // Create deployment + await runtime.app.client.waitForEnabled("button.Button=Create & Close"); + await runtime.app.client.click("button.Button=Create & Close"); + // Wait until first bits of pod appears on dashboard + await runtime.app.client.waitForExist(".name=nginx-create-pod-test"); + // Open pod details + await runtime.app.client.click(".name=nginx-create-pod-test"); + await runtime.app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx-create-pod-test"); + }); + }); +}); diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts index 26d519bb3e..0ce4cd3417 100644 --- a/integration/__tests__/cluster-pages.tests.ts +++ b/integration/__tests__/cluster-pages.tests.ts @@ -4,407 +4,242 @@ TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube cluster and vice versa. */ -import { Application } from "spectron"; -import * as utils from "../helpers/utils"; -import { addMinikubeCluster, minikubeReady, waitForMinikubeDashboard } from "../helpers/minikube"; +import { addClusterAndOpen, describeIf, getMainMenuSelectors, minikubeReady, setupAppLifecycle } from "../helpers"; -jest.setTimeout(60000); +jest.setTimeout(5000); + +type SidebarItem = { + testId: string; + expectedSelector?: string; + expectedText?: string; + subMenu?: { + href: string; + expectedSelector: string; + expectedText: string; + }[]; +}; describe("Lens cluster pages", () => { - const TEST_NAMESPACE = "integration-tests"; - const BACKSPACE = "\uE003"; - let app: Application; + const TEST_NAMESPACE = "cluster-pages-int-tests"; const ready = minikubeReady(TEST_NAMESPACE); - utils.describeIf(ready)("test common pages", () => { - let clusterAdded = false; - const addCluster = async () => { - await utils.clickWhatsNew(app); - await addMinikubeCluster(app); - await waitForMinikubeDashboard(app); - await app.client.click('a[href="/nodes"]'); - await app.client.waitUntilTextExists("div.TableCell", "Ready"); - }; + describeIf(ready)("cluster pages", () => { + const runtime = setupAppLifecycle(); - describe("cluster add", () => { - beforeAll(async () => app = await utils.appStart(), 20000); - - afterAll(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it("allows to add a cluster", async () => { - await addCluster(); - clusterAdded = true; - }); + beforeAll(async () => { + await addClusterAndOpen(runtime.app); }); - const appStartAddCluster = async () => { - if (clusterAdded) { - app = await utils.appStart(); - await addCluster(); - } - }; - - function getMainMenuSelectors(itemId: string) { - const baseSelector = `.Sidebar [data-test-id="${itemId}"]`; - - return { - sidebarItemRoot: baseSelector, - expandIcon: `${baseSelector} .expand-icon`, - pageLink(href: string) { - return `${baseSelector} a[href^="/${href}"]`; - } - }; - } - - describe("cluster pages", () => { - - beforeAll(appStartAddCluster, 40000); - - afterAll(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - type SidebarItem = { - testId: string; - expectedSelector?: string; - expectedText?: string; - subMenu?: { - href: string; - expectedSelector: string; - expectedText: string; - }[]; - }; - - const sidebarMenu: SidebarItem[] = [ - { - testId: "cluster", - expectedSelector: "div.ClusterOverview div.label", - expectedText: "Master", - }, - { - testId: "nodes", - expectedSelector: "h5.title", - expectedText: "Nodes" - }, - { - testId: "workloads", - subMenu: [ - { - href: "workloads", - expectedSelector: "h5", - expectedText: "Overview", - }, - { - href: "pods", - expectedSelector: "h5.title", - expectedText: "Pods" - }, - { - href: "deployments", - expectedSelector: "h5.title", - expectedText: "Deployments" - }, - { - href: "daemonsets", - expectedSelector: "h5.title", - expectedText: "Daemon Sets" - }, - { - href: "statefulsets", - expectedSelector: "h5.title", - expectedText: "Stateful Sets" - }, - { - href: "replicasets", - expectedSelector: "h5.title", - expectedText: "Replica Sets" - }, - { - href: "jobs", - expectedSelector: "h5.title", - expectedText: "Jobs" - }, - { - href: "cronjobs", - expectedSelector: "h5.title", - expectedText: "Cron Jobs" - }] - }, - { - testId: "config", - subMenu: [ - { - href: "configmaps", - expectedSelector: "h5.title", - expectedText: "Config Maps" - }, - { - href: "secrets", - expectedSelector: "h5.title", - expectedText: "Secrets" - }, - { - href: "resourcequotas", - expectedSelector: "h5.title", - expectedText: "Resource Quotas" - }, - { - href: "limitranges", - expectedSelector: "h5.title", - expectedText: "Limit Ranges" - }, - { - href: "hpa", - expectedSelector: "h5.title", - expectedText: "Horizontal Pod Autoscalers" - }, - { - href: "poddisruptionbudgets", - expectedSelector: "h5.title", - expectedText: "Pod Disruption Budgets" - }] - }, - { - testId: "networks", - subMenu: [ - { - href: "services", - expectedSelector: "h5.title", - expectedText: "Services" - }, - { - href: "endpoints", - expectedSelector: "h5.title", - expectedText: "Endpoints" - }, - { - href: "ingresses", - expectedSelector: "h5.title", - expectedText: "Ingresses" - }, - { - href: "network-policies", - expectedSelector: "h5.title", - expectedText: "Network Policies" - }] - }, - { - testId: "storage", - subMenu: [ - { - href: "persistent-volume-claims", - expectedSelector: "h5.title", - expectedText: "Persistent Volume Claims" - }, - { - href: "persistent-volumes", - expectedSelector: "h5.title", - expectedText: "Persistent Volumes" - }, - { - href: "storage-classes", - expectedSelector: "h5.title", - expectedText: "Storage Classes" - }] - }, - { - testId: "namespaces", - expectedSelector: "h5.title", - expectedText: "Namespaces", - }, - { - testId: "events", - expectedSelector: "h5.title", - expectedText: "Events", - }, - { - testId: "apps", - subMenu: [ - { - href: "apps/charts", - expectedSelector: "div.HelmCharts input", - expectedText: "" - }, - { - href: "apps/releases", - expectedSelector: "h5.title", - expectedText: "Releases" - }] - }, - { - testId: "users", - subMenu: [ - { - href: "service-accounts", - expectedSelector: "h5.title", - expectedText: "Service Accounts" - }, - { - href: "role-bindings", - expectedSelector: "h5.title", - expectedText: "Role Bindings" - }, - { - href: "roles", - expectedSelector: "h5.title", - expectedText: "Roles" - }, - { - href: "pod-security-policies", - expectedSelector: "h5.title", - expectedText: "Pod Security Policies" - }] - }, - { - testId: "custom-resources", - subMenu: [{ - href: "crd/definitions", + const items: SidebarItem[] = [ + { + testId: "cluster", + expectedSelector: "div.ClusterOverview div.label", + expectedText: "Master", + }, + { + testId: "nodes", + expectedSelector: "h5.title", + expectedText: "Nodes" + }, + { + testId: "workloads", + subMenu: [ + { + href: "workloads", + expectedSelector: "h5", + expectedText: "Overview", + }, + { + href: "pods", expectedSelector: "h5.title", - expectedText: "Custom Resources" + expectedText: "Pods" + }, + { + href: "deployments", + expectedSelector: "h5.title", + expectedText: "Deployments" + }, + { + href: "daemonsets", + expectedSelector: "h5.title", + expectedText: "Daemon Sets" + }, + { + href: "statefulsets", + expectedSelector: "h5.title", + expectedText: "Stateful Sets" + }, + { + href: "replicasets", + expectedSelector: "h5.title", + expectedText: "Replica Sets" + }, + { + href: "jobs", + expectedSelector: "h5.title", + expectedText: "Jobs" + }, + { + href: "cronjobs", + expectedSelector: "h5.title", + expectedText: "Cron Jobs" }] - }]; + }, + { + testId: "config", + subMenu: [ + { + href: "configmaps", + expectedSelector: "h5.title", + expectedText: "Config Maps" + }, + { + href: "secrets", + expectedSelector: "h5.title", + expectedText: "Secrets" + }, + { + href: "resourcequotas", + expectedSelector: "h5.title", + expectedText: "Resource Quotas" + }, + { + href: "limitranges", + expectedSelector: "h5.title", + expectedText: "Limit Ranges" + }, + { + href: "hpa", + expectedSelector: "h5.title", + expectedText: "Horizontal Pod Autoscalers" + }, + { + href: "poddisruptionbudgets", + expectedSelector: "h5.title", + expectedText: "Pod Disruption Budgets" + }] + }, + { + testId: "networks", + subMenu: [ + { + href: "services", + expectedSelector: "h5.title", + expectedText: "Services" + }, + { + href: "endpoints", + expectedSelector: "h5.title", + expectedText: "Endpoints" + }, + { + href: "ingresses", + expectedSelector: "h5.title", + expectedText: "Ingresses" + }, + { + href: "network-policies", + expectedSelector: "h5.title", + expectedText: "Network Policies" + }] + }, + { + testId: "storage", + subMenu: [ + { + href: "persistent-volume-claims", + expectedSelector: "h5.title", + expectedText: "Persistent Volume Claims" + }, + { + href: "persistent-volumes", + expectedSelector: "h5.title", + expectedText: "Persistent Volumes" + }, + { + href: "storage-classes", + expectedSelector: "h5.title", + expectedText: "Storage Classes" + }] + }, + { + testId: "namespaces", + expectedSelector: "h5.title", + expectedText: "Namespaces", + }, + { + testId: "events", + expectedSelector: "h5.title", + expectedText: "Events", + }, + { + testId: "apps", + subMenu: [ + { + href: "apps/charts", + expectedSelector: "div.HelmCharts input", + expectedText: "" + }, + { + href: "apps/releases", + expectedSelector: "h5.title", + expectedText: "Releases" + }] + }, + { + testId: "users", + subMenu: [ + { + href: "service-accounts", + expectedSelector: "h5.title", + expectedText: "Service Accounts" + }, + { + href: "role-bindings", + expectedSelector: "h5.title", + expectedText: "Role Bindings" + }, + { + href: "roles", + expectedSelector: "h5.title", + expectedText: "Roles" + }, + { + href: "pod-security-policies", + expectedSelector: "h5.title", + expectedText: "Pod Security Policies" + }] + }, + { + testId: "custom-resources", + subMenu: [{ + href: "crd/definitions", + expectedSelector: "h5.title", + expectedText: "Custom Resources" + }] + }]; - sidebarMenu.forEach(({ testId, expectedSelector, expectedText, subMenu }) => { - const { sidebarItemRoot, expandIcon, pageLink } = getMainMenuSelectors(testId); + items.forEach(({ testId, expectedSelector, expectedText, subMenu }) => { + const { sidebarItemRoot, expandIcon, pageLink } = getMainMenuSelectors(testId); - if (subMenu) { - it(`expands submenu for pages in "${testId}"`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(expandIcon); - await app.client.waitForExist(pageLink(subMenu[0].href)); + if (subMenu) { + it(`expands submenu for pages in "${testId}"`, async () => { + await runtime.app.client.click(expandIcon); + await runtime.app.client.waitForExist(pageLink(subMenu[0].href)); + }); + subMenu.forEach(({ href, expectedText, expectedSelector }) => { + it(`opens page "${expectedText.toLowerCase() || href}"`, async () => { + await runtime.app.client.click(pageLink(href)); + await runtime.app.client.waitUntilTextExists(expectedSelector, expectedText); }); - subMenu.forEach(({ href, expectedText, expectedSelector }) => { - it(`opens page "${expectedText.toLowerCase() || href}"`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(pageLink(href)); - await app.client.waitUntilTextExists(expectedSelector, expectedText); - }); - }); - } else { - it(`opens page "${testId}"`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(sidebarItemRoot); - await app.client.waitUntilTextExists(expectedSelector, expectedText); - }); - } - }); - }); - - describe("viewing pod logs", () => { - beforeEach(appStartAddCluster, 40000); - - afterEach(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it(`shows a logs for a pod`, async () => { - expect(clusterAdded).toBe(true); - // Go to Pods page - await app.client.click(getMainMenuSelectors("workloads").expandIcon); - await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); - await app.client.click('a[href^="/pods"]'); - await app.client.click(".NamespaceSelect"); - await app.client.keys("kube-system"); - await app.client.keys("Enter");// "\uE007" - await app.client.waitUntilTextExists("div.TableCell", "kube-apiserver"); - let podMenuItemEnabled = false; - - // Wait until extensions are enabled on renderer - while (!podMenuItemEnabled) { - const logs = await app.client.getRenderProcessLogs(); - - podMenuItemEnabled = !!logs.find(entry => entry.message.includes("[EXTENSION]: enabled lens-pod-menu@")); - - if (!podMenuItemEnabled) { - await new Promise(r => setTimeout(r, 1000)); - } - } - await new Promise(r => setTimeout(r, 500)); // Give some extra time to prepare extensions - // Open logs tab in dock - await app.client.click(".list .TableRow:first-child"); - await app.client.waitForVisible(".Drawer"); - await app.client.click(".drawer-title .Menu li:nth-child(2)"); - // Check if controls are available - await app.client.waitForVisible(".LogList .VirtualList"); - await app.client.waitForVisible(".LogResourceSelector"); - //await app.client.waitForVisible(".LogSearch .SearchInput"); - await app.client.waitForVisible(".LogSearch .SearchInput input"); - // Search for semicolon - await app.client.keys(":"); - await app.client.waitForVisible(".LogList .list span.active"); - // Click through controls - await app.client.click(".LogControls .show-timestamps"); - await app.client.click(".LogControls .show-previous"); - }); - }); - - describe("cluster operations", () => { - beforeEach(appStartAddCluster, 40000); - - afterEach(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); - - it("shows default namespace", async () => { - expect(clusterAdded).toBe(true); - await app.client.click('a[href="/namespaces"]'); - await app.client.waitUntilTextExists("div.TableCell", "default"); - await app.client.waitUntilTextExists("div.TableCell", "kube-system"); - }); - - it(`creates ${TEST_NAMESPACE} namespace`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click('a[href="/namespaces"]'); - await app.client.waitUntilTextExists("div.TableCell", "default"); - await app.client.waitUntilTextExists("div.TableCell", "kube-system"); - await app.client.click("button.add-button"); - await app.client.waitUntilTextExists("div.AddNamespaceDialog", "Create Namespace"); - await app.client.keys(`${TEST_NAMESPACE}\n`); - await app.client.waitForExist(`.name=${TEST_NAMESPACE}`); - }); - - it(`creates a pod in ${TEST_NAMESPACE} namespace`, async () => { - expect(clusterAdded).toBe(true); - await app.client.click(getMainMenuSelectors("workloads").expandIcon); - await app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); - await app.client.click('a[href^="/pods"]'); - - await app.client.click(".NamespaceSelect"); - await app.client.keys(TEST_NAMESPACE); - await app.client.keys("Enter");// "\uE007" - await app.client.click(".Icon.new-dock-tab"); - await app.client.waitUntilTextExists("li.MenuItem.create-resource-tab", "Create resource"); - await app.client.click("li.MenuItem.create-resource-tab"); - await app.client.waitForVisible(".CreateResource div.ace_content"); - // Write pod manifest to editor - await app.client.keys("apiVersion: v1\n"); - await app.client.keys("kind: Pod\n"); - await app.client.keys("metadata:\n"); - await app.client.keys(" name: nginx-create-pod-test\n"); - await app.client.keys(`namespace: ${TEST_NAMESPACE}\n`); - await app.client.keys(`${BACKSPACE}spec:\n`); - await app.client.keys(" containers:\n"); - await app.client.keys("- name: nginx-create-pod-test\n"); - await app.client.keys(" image: nginx:alpine\n"); - // Create deployment - await app.client.waitForEnabled("button.Button=Create & Close"); - await app.client.click("button.Button=Create & Close"); - // Wait until first bits of pod appears on dashboard - await app.client.waitForExist(".name=nginx-create-pod-test"); - // Open pod details - await app.client.click(".name=nginx-create-pod-test"); - await app.client.waitUntilTextExists("div.drawer-title-text", "Pod: nginx-create-pod-test"); - }); + }); + } else { + it(`opens page "${testId}"`, async () => { + await runtime.app.client.click(sidebarItemRoot); + await runtime.app.client.waitUntilTextExists(expectedSelector, expectedText); + }); + } }); }); + }); diff --git a/integration/__tests__/command-palette.tests.ts b/integration/__tests__/command-palette.tests.ts index 789806c445..135c5af2cc 100644 --- a/integration/__tests__/command-palette.tests.ts +++ b/integration/__tests__/command-palette.tests.ts @@ -1,25 +1,16 @@ -import { Application } from "spectron"; -import * as utils from "../helpers/utils"; +import { setupAppLifecycle, clickWhatsNew } from "../helpers/utils"; jest.setTimeout(60000); describe("Lens command palette", () => { - let app: Application; + const runtime = setupAppLifecycle(); describe("menu", () => { - beforeAll(async () => app = await utils.appStart(), 20000); - - afterAll(async () => { - if (app?.isRunning()) { - await utils.tearDown(app); - } - }); - it("opens command dialog from menu", async () => { - await utils.clickWhatsNew(app); - await app.electron.ipcRenderer.send("test-menu-item-click", "View", "Command Palette..."); - await app.client.waitUntilTextExists(".Select__option", "Preferences: Open"); - await app.client.keys("Escape"); + await clickWhatsNew(runtime.app); + await runtime.app.electron.ipcRenderer.send("test-menu-item-click", "View", "Command Palette..."); + await runtime.app.client.waitUntilTextExists(".Select__option", "Preferences: Open"); + await runtime.app.client.keys("Escape"); }); }); }); diff --git a/integration/__tests__/logs.tests.ts b/integration/__tests__/logs.tests.ts new file mode 100644 index 0000000000..1a73198c1d --- /dev/null +++ b/integration/__tests__/logs.tests.ts @@ -0,0 +1,56 @@ +import { addClusterAndOpen, describeIf, getMainMenuSelectors, minikubeReady, setupAppLifecycle } from "../helpers"; +import { delay } from "../../src/common/utils"; + +jest.setTimeout(60000); + +describe("Lens logs tests", () => { + const ready = minikubeReady("logs-int-tests"); + + describeIf(ready)("Pod logs", () => { + const runtime = setupAppLifecycle(); + + beforeAll(async () => { + await addClusterAndOpen(runtime.app); + }); + + it(`shows a logs for a pod`, async () => { + // Go to Pods page + await runtime.app.client.click(getMainMenuSelectors("workloads").expandIcon); + await runtime.app.client.waitUntilTextExists('a[href^="/pods"]', "Pods"); + await runtime.app.client.click('a[href^="/pods"]'); + await runtime.app.client.click(".NamespaceSelect"); + await runtime.app.client.keys("kube-system"); + await runtime.app.client.keys("Enter");// "\uE007" + await runtime.app.client.waitUntilTextExists("div.TableCell", "kube-apiserver"); + let podMenuItemEnabled = false; + + // Wait until extensions are enabled on renderer + while (!podMenuItemEnabled) { + const logs = await runtime.app.client.getRenderProcessLogs(); + + podMenuItemEnabled = !!logs.find(entry => entry.message.includes("[EXTENSION]: enabled lens-pod-menu@")); + + if (!podMenuItemEnabled) { + await delay(1000); + } + } + await delay(500); // Give some extra time to prepare extensions + // Open logs tab in dock + await runtime.app.client.click(".list .TableRow:first-child"); + await runtime.app.client.waitForVisible(".Drawer"); + await runtime.app.client.click(".drawer-title .Menu li:nth-child(2)"); + // Check if controls are available + await runtime.app.client.waitForVisible(".LogList .VirtualList"); + await runtime.app.client.waitForVisible(".LogResourceSelector"); + //await runtime.app.client.waitForVisible(".LogSearch .SearchInput"); + await runtime.app.client.waitForVisible(".LogSearch .SearchInput input"); + // Search for semicolon + await runtime.app.client.keys(":"); + await runtime.app.client.waitForVisible(".LogList .list span.active"); + // Click through controls + await runtime.app.client.click(".LogControls .show-timestamps"); + await runtime.app.client.click(".LogControls .show-previous"); + }); + }); + +}); diff --git a/integration/__tests__/workspace.tests.ts b/integration/__tests__/workspace.tests.ts index 54cc573c14..f8a2bd1958 100644 --- a/integration/__tests__/workspace.tests.ts +++ b/integration/__tests__/workspace.tests.ts @@ -1,71 +1,59 @@ -import { Application } from "spectron"; -import * as utils from "../helpers/utils"; -import { addMinikubeCluster, minikubeReady } from "../helpers/minikube"; +import { addMinikubeCluster, clickWhatsNew, describeIf, minikubeReady, setupAppLifecycle } from "../helpers"; jest.setTimeout(60000); describe("Lens workspace tests", () => { - let app: Application; - const ready = minikubeReady("workspace-int-tests"); + const ready = minikubeReady("workspaces-int-tests"); - utils.describeIf(ready)("workspaces", () => { - beforeAll(async () => { - app = await utils.appStart(); - await utils.clickWhatsNew(app); - }, 20000); - - afterAll(async () => { - if (app && app.isRunning()) { - return utils.tearDown(app); - } - }); + describeIf(ready)("workspaces", () => { + const runtime = setupAppLifecycle(clickWhatsNew); const switchToWorkspace = async (name: string) => { - await app.client.click("[data-test-id=current-workspace]"); - await app.client.keys(name); - await app.client.keys("Enter"); - await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); + await runtime.app.client.click("[data-test-id=current-workspace]"); + await runtime.app.client.keys(name); + await runtime.app.client.keys("Enter"); + await runtime.app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); }; const createWorkspace = async (name: string) => { - await app.client.click("[data-test-id=current-workspace]"); - await app.client.keys("add workspace"); - await app.client.keys("Enter"); - await app.client.keys(name); - await app.client.keys("Enter"); - await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); + await runtime.app.client.click("[data-test-id=current-workspace]"); + await runtime.app.client.keys("add workspace"); + await runtime.app.client.keys("Enter"); + await runtime.app.client.keys(name); + await runtime.app.client.keys("Enter"); + await runtime.app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); }; it("creates new workspace", async () => { const name = "test-workspace"; await createWorkspace(name); - await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); + await runtime.app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", name); }); it("edits current workspaces", async () => { await createWorkspace("to-be-edited"); - await app.client.click("[data-test-id=current-workspace]"); - await app.client.keys("edit current workspace"); - await app.client.keys("Enter"); - await app.client.keys("edited-workspace"); - await app.client.keys("Enter"); - await app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", "edited-workspace"); + await runtime.app.client.click("[data-test-id=current-workspace]"); + await runtime.app.client.keys("edit current workspace"); + await runtime.app.client.keys("Enter"); + await runtime.app.client.keys("edited-workspace"); + await runtime.app.client.keys("Enter"); + await runtime.app.client.waitUntilTextExists("[data-test-id=current-workspace-name]", "edited-workspace"); }); it("adds cluster in default workspace", async () => { await switchToWorkspace("default"); - await addMinikubeCluster(app); - await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); - await app.client.waitForExist(`iframe[name="minikube"]`); - await app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); + await addMinikubeCluster(runtime.app); + await runtime.app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); + await runtime.app.client.waitForExist(`iframe[name="minikube"]`); + await runtime.app.client.waitForVisible(".ClustersMenu .ClusterIcon.active"); }); it("adds cluster in test-workspace", async () => { await switchToWorkspace("test-workspace"); - await addMinikubeCluster(app); - await app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); - await app.client.waitForExist(`iframe[name="minikube"]`); + await addMinikubeCluster(runtime.app); + await runtime.app.client.waitUntilTextExists("pre.kube-auth-out", "Authentication proxy started"); + await runtime.app.client.waitForExist(`iframe[name="minikube"]`); }); }); }); diff --git a/integration/helpers/index.ts b/integration/helpers/index.ts new file mode 100644 index 0000000000..4d73650078 --- /dev/null +++ b/integration/helpers/index.ts @@ -0,0 +1,7 @@ +// Common utils for integration tests + +export const BACKSPACE = "\uE003"; + +export * from "./utils"; +export * from "./minikube"; +export * from "./selectors"; diff --git a/integration/helpers/minikube.ts b/integration/helpers/minikube.ts index 67ef0145d5..42f2646699 100644 --- a/integration/helpers/minikube.ts +++ b/integration/helpers/minikube.ts @@ -1,5 +1,6 @@ import { spawnSync } from "child_process"; import { Application } from "spectron"; +import { clickWhatsNew } from "./utils"; export function minikubeReady(testNamespace: string): boolean { // determine if minikube is running @@ -57,3 +58,9 @@ export async function waitForMinikubeDashboard(app: Application) { await app.client.frame("minikube"); await app.client.waitUntilTextExists("span.link-text", "Cluster"); } + +export async function addClusterAndOpen(app: Application) { + await clickWhatsNew(app); + await addMinikubeCluster(app); + await waitForMinikubeDashboard(app); +} diff --git a/integration/helpers/selectors.ts b/integration/helpers/selectors.ts new file mode 100644 index 0000000000..5228632b4a --- /dev/null +++ b/integration/helpers/selectors.ts @@ -0,0 +1,13 @@ +// CSS-selectors for searching elements to interact in tests + +export function getMainMenuSelectors(itemId: string) { + const baseSelector = `.Sidebar [data-test-id="${itemId}"]`; + + return { + sidebarItemRoot: baseSelector, + expandIcon: `${baseSelector} .expand-icon`, + pageLink(href: string) { + return `${baseSelector} a[href^="/${href}"]`; + } + }; +} diff --git a/integration/helpers/utils.ts b/integration/helpers/utils.ts index f96c9124e2..17d628d7df 100644 --- a/integration/helpers/utils.ts +++ b/integration/helpers/utils.ts @@ -37,7 +37,7 @@ export async function appStart() { await app.start(); // Wait for splash screen to be closed - while (await app.client.getWindowCount() > 1); + while (await app.client.getWindowCount() > 1) ; await app.client.windowByIndex(0); await app.client.waitUntilWindowLoaded(); @@ -71,7 +71,7 @@ type HelmRepository = { url: string; }; -export async function listHelmRepositories(retries = 0): Promise{ +export async function listHelmRepositories(retries = 0): Promise { if (retries < 5) { try { const { stdout: reposJson } = await promiseExec("helm repo list -o json"); @@ -86,3 +86,22 @@ export async function listHelmRepositories(retries = 0): Promise void, timeoutMs?: number) { + const runtime = { + app: null as Application, + }; + + beforeAll(async () => { + runtime.app = await appStart(); + await onStart?.(runtime.app); + }, timeoutMs); + + afterAll(async () => { + if (runtime.app?.isRunning()) { + await tearDown(runtime.app); + } + }); + + return runtime; +}