Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions frontend/e2e/pages/base-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,19 @@ export async function setEditorContent(page: Page, content: string): Promise<voi
await page.waitForFunction(() => (window as any).monaco?.editor?.getModels()?.[0], {
timeout: 10_000,
});
await page.evaluate((text) => {
(window as any).monaco.editor.getModels()[0].setValue(text);
}, content);
// Monaco can swap its model during initialisation, silently dropping an early
// setValue and leaving the editor empty — which then submits an empty
// definition. Set and verify with retries so the content is guaranteed to
// stick before the caller proceeds.
await expect(async () => {
await page.evaluate((text) => {
(window as any).monaco.editor.getModels()[0].setValue(text);
}, content);
const value = await page.evaluate(() =>
(window as any).monaco.editor.getModels()[0].getValue(),
);
expect(value.trim()).toBe(content.trim());
}).toPass({ timeout: 15_000, intervals: [300, 700, 1500] });
}

export async function warmupSPA(page: Page): Promise<void> {
Expand Down
5 changes: 4 additions & 1 deletion frontend/e2e/tests/console/app/debug-pod.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ test.describe('Debug pod', () => {
page,
k8sClient,
}) => {
test.setTimeout(300_000);
// This test is image-pull and reconcile heavy: it waits for a pod to
// CrashLoopBackOff and then spins up three separate debug pods. On a cold or
// slow CI cluster the default 300s is not enough, so allow more headroom.
test.setTimeout(480_000);

const detailsPage = new DetailsPage(page);
const listPage = new ListPage(page);
Expand Down
22 changes: 18 additions & 4 deletions frontend/e2e/tests/console/nodes/node-groups-filter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import type { Page } from '@playwright/test';

import { test, expect } from '../../../fixtures';
import { warmupSPA } from '../../../pages/base-page';

/**
* E2E tests for Node Groups filtering functionality
* Tests the filter UI, filter behavior, and edit groups button
*/

async function gotoNodesPage(page: Page): Promise<void> {
// Warm the SPA shell first (self-heals a lost session and waits out plugin
// init) so navigating straight to this data-heavy page doesn't race a cold
// bootstrap — the direct goto was timing out on CI before the shell rendered.
await warmupSPA(page);
await page.goto('/k8s/cluster/nodes');
await expect(
page.getByTestId('data-view-table').or(page.getByTestId('page-heading')).first(),
).toBeVisible();
).toBeVisible({ timeout: 30_000 });
}

function groupsFilter(page: Page) {
Expand Down Expand Up @@ -240,8 +245,17 @@ test.describe('Edit Groups Button', () => {
await skipIfEditGroupsButtonHidden(page);

const editButton = page.getByRole('button', { name: /edit groups/i });
if (!(await editButton.isDisabled())) {
test.skip(true, 'Edit groups button is not disabled');
// The button starts disabled while the permission (SelfSubjectAccessReview)
// check is in flight, then enables for users who can edit. Poll until that
// settles: if it becomes enabled the user has permission and this test does
// not apply (e.g. cluster-admin CI runs); only a button that stays disabled
// indicates a genuine lack of permission.
const deadline = Date.now() + 15_000;
while ((await editButton.isDisabled()) && Date.now() < deadline) {
await new Promise((r) => setTimeout(r, 500));
}
if (await editButton.isEnabled()) {
test.skip(true, 'User has permission to edit groups; tooltip not applicable');
return;
}

Expand All @@ -250,7 +264,7 @@ test.describe('Edit Groups Button', () => {
const tooltip = page.locator('[role="tooltip"]').filter({
hasText: /permission.*edit groups.*administrator/i,
});
await expect(tooltip).toBeVisible();
await expect(tooltip).toBeVisible({ timeout: 10_000 });
});

test('should open Groups Editor modal when clicked', async ({ page }) => {
Expand Down
15 changes: 7 additions & 8 deletions frontend/e2e/tests/dev-console/create-from-yaml.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test, expect } from '../../fixtures';
import { warmupSPA } from '../../pages/base-page';
import { setEditorContent, warmupSPA } from '../../pages/base-page';
import { AddPage, ImportYAMLPage } from '../../pages/dev-console/add-page';
import { TopologyPage } from '../../pages/topology-page';

Expand Down Expand Up @@ -68,14 +68,13 @@ test.describe(
});

await test.step('Enter YAML content and create', async () => {
await page.waitForFunction(
() => !!(window as any).monaco?.editor?.getModels()?.[0],
{ timeout: 30_000 },
);
await page.evaluate((yaml) => {
(window as any).monaco.editor.getModels()[0].setValue(yaml);
}, GIT_DC_YAML);
await setEditorContent(page, GIT_DC_YAML);
await yamlPage.getSubmitButton().click();
// The editor issues the create POST asynchronously and redirects to the
// new resource on success. Wait for that redirect before navigating
// away — otherwise the next navigation aborts the in-flight create
// request ("Failed to fetch") and the workload is never created.
await page.waitForURL(/\/deploymentconfigs\/shell-app(\/|$|\?)/, { timeout: 60_000 });
});

await test.step('Verify workload in topology', async () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/e2e/tests/dev-console/import-from-devfile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ test.describe(
await test.step('Verify workload appears in topology', async () => {
await topologyPage.waitForWorkload('node-example');
await topologyPage.clickWorkload('node-example');
await expect(topologyPage.getSidebarTitle()).toContainText('node-example', { timeout: 15_000 });
await expect(topologyPage.getSidebarTitle()).toContainText('node-example', { timeout: 30_000 });
});
});
},
Expand Down
21 changes: 10 additions & 11 deletions frontend/e2e/tests/topology/topology-ci.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,17 @@ test.describe('Perform actions on topology', { tag: ['@smoke'] }, () => {
test('Edit workload application groupings: T-09-TC01', async ({ page }) => {
test.setTimeout(300_000);
const topology = new TopologyPage(page);
const sidebar = new TopologySidebarPage(page);
await createWorkload(page, 'dotnet-edit-test');

await test.step('Prepare for right-click: clear search and close any sidebar', async () => {
// Clear the search field to avoid interference
await topology.search('');
// Close sidebar if open
await topology.closeSidebarIfOpen();
});

await test.step('Right-click workload and select Edit', async () => {
await topology.rightClickOnNode('dotnet-edit-test');
await topology.selectContextMenuAction('Edit dotnet-edit-test');

await test.step('Open workload sidebar and select Edit', async () => {
// Use the sidebar Actions menu rather than the topology right-click context
// menu: PF Topology nodes are SVG groups with no usable bounding box, so a
// real right-click cannot be dispatched reliably in headless runs. This is
// the same interaction path deleteWorkload uses.
await topology.clickOnNode('dotnet-edit-test');
await sidebar.verify();
await sidebar.selectAction('Edit dotnet-edit-test');
});

await test.step('Change application groupings to "app"', async () => {
Expand Down