Skip to content
Open
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
19 changes: 16 additions & 3 deletions frontend/e2e/pages/web-terminal-config-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,21 @@ export class WebTerminalConfigPage extends BasePage {
private readonly saveButton = this.page.getByTestId('save-button');
private readonly successAlert = this.page.getByTestId('success-alert');

async navigateToWebTerminalConfig(): Promise<void> {
async navigateToWebTerminalConfig(maxRetries = 2): Promise<void> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
await this.openCustomizeDrawer();
await this.clickWebTerminalTab();
return;
} catch (error) {
if (attempt >= maxRetries) throw error;
// eslint-disable-next-line playwright/no-wait-for-timeout -- ConsolePlugin may not be registered yet, reload and retry
await this.page.waitForTimeout(5_000);
}
}
}

private async openCustomizeDrawer(): Promise<void> {
await this.goTo('/k8s/cluster/operator.openshift.io~v1~Console/cluster');
await this.waitForLoadingComplete(10_000);
const customizeButton = this.page.getByRole('button', { name: 'Customize' });
Expand All @@ -30,12 +44,11 @@ export class WebTerminalConfigPage extends BasePage {
await this.robustClick(customizeAction);
}
await this.waitForLoadingComplete(10_000);
await this.clickWebTerminalTab();
}

async clickWebTerminalTab(): Promise<void> {
const tab = this.page.getByRole('tab', { name: 'Web Terminal' });
await this.robustClick(tab, { timeout: 60_000 });
await this.robustClick(tab, { timeout: 20_000 });
await this.waitForLoadingComplete(5_000);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import KubernetesClient from '../../../clients/kubernetes-client';
import * as k8s from '@kubernetes/client-node';
import { test as setup } from '@playwright/test';

import KubernetesClient from '../clients/kubernetes-client';

const SUBSCRIPTION_GROUP = 'operators.coreos.com';
const SUBSCRIPTION_VERSION = 'v1alpha1';
Expand All @@ -21,9 +24,18 @@ const webTerminalSubscription = {
},
};

export async function ensureWebTerminalOperatorInstalled(
k8sClient: KubernetesClient,
): Promise<void> {
setup('install Web Terminal operator if not present', async () => {
setup.setTimeout(600_000);

const k8sClient = new KubernetesClient(
{
clusterUrl: process.env.CLUSTER_URL || '',
username: process.env.OPENSHIFT_USERNAME || 'kubeadmin',
password: process.env.BRIDGE_KUBEADMIN_PASSWORD || '',
},
process.env.KUBECONFIG,
);

try {
await k8sClient.getCustomResource(
SUBSCRIPTION_GROUP,
Expand All @@ -32,22 +44,28 @@ export async function ensureWebTerminalOperatorInstalled(
SUBSCRIPTION_PLURAL,
'web-terminal',
);
return;
} catch {
// Subscription doesn't exist — create it
// Subscription exists — fall through to readiness poll
} catch (err) {
if (!(err instanceof k8s.ApiException && err.code === 404)) {
throw err;
}
try {
await k8sClient.createCustomResource(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
OPERATOR_NAMESPACE,
SUBSCRIPTION_PLURAL,
webTerminalSubscription,
);
} catch (err) {
if (!(err instanceof k8s.ApiException && err.code === 409)) {
throw err;
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

await k8sClient.createCustomResource(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
OPERATOR_NAMESPACE,
SUBSCRIPTION_PLURAL,
webTerminalSubscription,
);

const maxWaitMs = 300_000;
const pollIntervalMs = 10_000;
const deadline = Date.now() + maxWaitMs;
const deadline = Date.now() + 300_000;

while (Date.now() < deadline) {
const pods = await k8sClient.getPods(OPERATOR_NAMESPACE);
Expand All @@ -64,41 +82,4 @@ export async function ensureWebTerminalOperatorInstalled(
}

throw new Error('Web Terminal operator controller pod not ready within 5 minutes');
}

const CSV_PLURAL = 'clusterserviceversions';

export async function uninstallWebTerminalOperator(
k8sClient: KubernetesClient,
): Promise<void> {
try {
await k8sClient.deleteCustomResource(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
OPERATOR_NAMESPACE,
SUBSCRIPTION_PLURAL,
'web-terminal',
);

const csvs = await k8sClient.listCustomResources(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
OPERATOR_NAMESPACE,
CSV_PLURAL,
);
const webTerminalCsv = csvs.find(
(csv) => (csv as any).metadata?.name?.startsWith('web-terminal'),
);
if (webTerminalCsv) {
await k8sClient.deleteCustomResource(
SUBSCRIPTION_GROUP,
SUBSCRIPTION_VERSION,
OPERATOR_NAMESPACE,
CSV_PLURAL,
(webTerminalCsv as any).metadata.name,
);
}
} catch (err) {
console.warn('[Cleanup] Failed to uninstall Web Terminal operator:', err);
}
}
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
import { test, expect } from '../../../fixtures';
import { WebTerminalPage } from '../../../pages/web-terminal-page';
import {
ensureWebTerminalOperatorInstalled,
uninstallWebTerminalOperator,
} from '../utils/web-terminal-operator';

const INACTIVITY_MESSAGE = 'The terminal connection has closed due to inactivity.';
const TERMINAL_IDLING_TIMEOUT = Number(process.env.TERMINAL_IDLING_TIMEOUT) || 200_000;
const TEST_NAMESPACE = 'aut-terminal-basic';

test.describe('Web Terminal basic user', () => {
test.beforeAll(async ({ k8sClient }) => {
await ensureWebTerminalOperatorInstalled(k8sClient);
});

test.beforeEach(async ({ k8sClient, cleanup }) => {
await k8sClient.createNamespace(TEST_NAMESPACE);
cleanup.trackNamespace(TEST_NAMESPACE);
});

test.afterAll(async ({ k8sClient }) => {
await uninstallWebTerminalOperator(k8sClient);
});

test('open terminal with advanced timeout', async ({ page }) => {
const webTerminal = new WebTerminalPage(page);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import type { Page } from '@playwright/test';
import { test, expect } from '../../../fixtures';
import type KubernetesClient from '../../../clients/kubernetes-client';
import { WebTerminalPage } from '../../../pages/web-terminal-page';
import {
ensureWebTerminalOperatorInstalled,
uninstallWebTerminalOperator,
} from '../utils/web-terminal-operator';

const DEVWORKSPACE_GROUP = 'workspace.devfile.io';
const DEVWORKSPACE_VERSION = 'v1alpha2';
Expand Down Expand Up @@ -38,19 +34,11 @@ async function verifyDevWorkspaceRunning(
}

test.describe('Web Terminal for Developer user', () => {
test.beforeAll(async ({ k8sClient }) => {
await ensureWebTerminalOperatorInstalled(k8sClient);
});

test.beforeEach(async ({ k8sClient, cleanup }) => {
await k8sClient.createNamespace(EXISTING_PROJECT);
cleanup.trackNamespace(EXISTING_PROJECT);
});

test.afterAll(async ({ k8sClient }) => {
await uninstallWebTerminalOperator(k8sClient);
});

test(
'create new project and use Web Terminal',
async ({ page, k8sClient, cleanup }) => {
Expand Down
12 changes: 0 additions & 12 deletions frontend/e2e/tests/webterminal/web-terminal-admin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { test, expect } from '../../fixtures';
import type KubernetesClient from '../../clients/kubernetes-client';
import { getEditorContent, warmupSPA } from '../../pages/base-page';
import { WebTerminalPage } from '../../pages/web-terminal-page';
import {
ensureWebTerminalOperatorInstalled,
uninstallWebTerminalOperator,
} from './utils/web-terminal-operator';

const DEVWORKSPACE_GROUP = 'workspace.devfile.io';
const DEVWORKSPACE_VERSION = 'v1alpha2';
Expand Down Expand Up @@ -43,10 +39,6 @@ async function verifyDevWorkspaceUid(
}

test.describe('Web Terminal for Admin user', () => {
test.beforeAll(async ({ k8sClient }) => {
await ensureWebTerminalOperatorInstalled(k8sClient);
});

test.beforeEach(async ({ k8sClient }) => {
const devWorkspaces = await k8sClient.listCustomResources(
DEVWORKSPACE_GROUP,
Expand Down Expand Up @@ -81,10 +73,6 @@ test.describe('Web Terminal for Admin user', () => {
}
});

test.afterAll(async ({ k8sClient }) => {
await uninstallWebTerminalOperator(k8sClient);
});

test(
'open and close multiple terminal tabs',
async ({ page }) => {
Expand Down
12 changes: 0 additions & 12 deletions frontend/e2e/tests/webterminal/web-terminal-config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
import { test, expect } from '../../fixtures';
import { warmupSPA } from '../../pages/base-page';
import { WebTerminalConfigPage } from '../../pages/web-terminal-config-page';
import {
ensureWebTerminalOperatorInstalled,
uninstallWebTerminalOperator,
} from './utils/web-terminal-operator';

const TEST_IMAGE_805 =
'registry.redhat.io/web-terminal/web-terminal-tooling-rhel8@sha256:9ff1f660fccd3a2f0515ba997d48ad87d2ba47c40b67062c74580bbea9446805';
const TEST_IMAGE_806 =
'registry.redhat.io/web-terminal/web-terminal-tooling-rhel8@sha256:9ff1f660fccd3a2f0515ba997d48ad87d2ba47c40b67062c74580bbea9446806';

test.describe('Customization of web terminal options', () => {
test.beforeAll(async ({ k8sClient }) => {
await ensureWebTerminalOperatorInstalled(k8sClient);
});

test.beforeEach(async ({ page }) => {
await warmupSPA(page);
});

test.afterAll(async ({ k8sClient }) => {
await uninstallWebTerminalOperator(k8sClient);
});

test(
'navigate to Web Terminal Configuration page',
async ({ page }) => {
Expand Down
17 changes: 15 additions & 2 deletions frontend/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,23 @@ export default defineConfig({
dependencies: ['cluster-setup'],
},

{
name: 'webterminal-setup',
testDir: path.resolve(__dirname, 'e2e', 'setup'),
testMatch: 'webterminal.setup.ts',
dependencies: ['cluster-setup'],
},

...packages.map((pkg) => ({
name: pkg,
testDir: path.resolve(__dirname, 'e2e', 'tests', pkg),
testIgnore: '**/developer/**',
dependencies: pkg === 'knative' ? ['admin-auth', 'knative-setup'] : ['admin-auth'],
dependencies:
pkg === 'knative'
? ['admin-auth', 'knative-setup']
: pkg === 'webterminal'
? ['admin-auth', 'webterminal-setup']
: ['admin-auth'],
use: {
...chrome,
storageState: adminStorageState,
Expand All @@ -149,7 +161,8 @@ export default defineConfig({
? devPackages.map((pkg) => ({
name: `${pkg}-developer`,
testDir: path.resolve(__dirname, 'e2e', 'tests', pkg, 'developer'),
dependencies: ['developer-auth'],
dependencies:
pkg === 'webterminal' ? ['developer-auth', 'webterminal-setup'] : ['developer-auth'],
use: {
...chrome,
storageState: developerStorageState,
Expand Down