From f78062df9773e9d468eaafbd4d3873018ffa4ebe Mon Sep 17 00:00:00 2001 From: Robb Hamilton Date: Mon, 27 Jul 2026 16:34:21 -0400 Subject: [PATCH 1/2] CONSOLE-5196: Address PR review feedback - Use top-level import type for Playwright types instead of inline import() expressions (logonoff) - Narrow catch in favorites ConfigMap cleanup to re-throw non-404 errors (coderabbitai) - Add cleanup step to remove all favorites at end of test (logonoff) - Move globalTimeout default (110min) to prow script as GLOBAL_TIMEOUT_MS env var for simplicity (logonoff) - Filter framenavigated to main frame only (fsgreco) - Use Number() for stricter WORKERS env var parsing (fsgreco) - Wait for receiver type dropdown to populate in alertmanager edit form to prevent flakes (new) - Increase robustClick timeout for topology sidebar actions and Web Terminal tab (new) - Register response listener before navigation in poll-console-updates to catch early poll responses (new) Co-Authored-By: Claude Opus 4.6 --- frontend/e2e/clients/kubernetes-client.ts | 12 ++- frontend/e2e/pages/topology-sidebar-page.ts | 2 +- .../e2e/pages/web-terminal-config-page.ts | 2 +- .../console/app/poll-console-updates.spec.ts | 74 +++++++++---------- .../tests/console/favorites/favorites.spec.ts | 16 +++- frontend/playwright.config.ts | 4 +- test-prow-playwright-e2e.sh | 1 + 7 files changed, 60 insertions(+), 51 deletions(-) diff --git a/frontend/e2e/clients/kubernetes-client.ts b/frontend/e2e/clients/kubernetes-client.ts index 9338645e53f..075c66841e0 100644 --- a/frontend/e2e/clients/kubernetes-client.ts +++ b/frontend/e2e/clients/kubernetes-client.ts @@ -13,7 +13,7 @@ export interface ClusterAuthConfig { token?: string; } -function isNotFound(err: unknown): boolean { +export function isNotFound(err: unknown): boolean { if (typeof err === 'object' && err !== null) { const statusCode = (err as any).statusCode ?? (err as any).response?.statusCode; if (statusCode === 404) { @@ -374,12 +374,10 @@ export default class KubernetesClient { const existing = await this.k8sApi.readNamespacedConfigMap({ name, namespace }); const existingData = (existing as any)?.data || {}; const mergedData = { ...existingData, ...patchData }; - await this.k8sApi.patchNamespacedConfigMap({ - name, - namespace, - body: { data: mergedData }, - contentType: k8s.PatchStrategy.MergePatch, - } as any); + await this.mergePatchResource( + `/api/v1/namespaces/${namespace}/configmaps/${name}`, + { data: mergedData }, + ); } async createConfigMap( diff --git a/frontend/e2e/pages/topology-sidebar-page.ts b/frontend/e2e/pages/topology-sidebar-page.ts index 579057ee69e..f244150e632 100644 --- a/frontend/e2e/pages/topology-sidebar-page.ts +++ b/frontend/e2e/pages/topology-sidebar-page.ts @@ -11,7 +11,7 @@ export class TopologySidebarPage extends BasePage { } async clickActionsDropdown(): Promise { - await this.robustClick(this.actionsDropdown); + await this.robustClick(this.actionsDropdown, { timeout: 60_000 }); } async selectAction(action: string): Promise { diff --git a/frontend/e2e/pages/web-terminal-config-page.ts b/frontend/e2e/pages/web-terminal-config-page.ts index 6df68a4e2a2..1df2ff15c49 100644 --- a/frontend/e2e/pages/web-terminal-config-page.ts +++ b/frontend/e2e/pages/web-terminal-config-page.ts @@ -35,7 +35,7 @@ export class WebTerminalConfigPage extends BasePage { async clickWebTerminalTab(): Promise { const tab = this.page.getByRole('tab', { name: 'Web Terminal' }); - await this.robustClick(tab); + await this.robustClick(tab, { timeout: 60_000 }); await this.waitForLoadingComplete(5_000); } diff --git a/frontend/e2e/tests/console/app/poll-console-updates.spec.ts b/frontend/e2e/tests/console/app/poll-console-updates.spec.ts index ee11b791303..69859f9678a 100644 --- a/frontend/e2e/tests/console/app/poll-console-updates.spec.ts +++ b/frontend/e2e/tests/console/app/poll-console-updates.spec.ts @@ -1,3 +1,4 @@ +import type { Frame, Page, Response, Route } from '@playwright/test'; import { test, expect } from '../../../fixtures'; const CHECK_UPDATES_URL = '**/api/check-updates'; @@ -19,14 +20,9 @@ const PLUGIN_MANIFEST_DEFAULT = { name: PLUGIN_NAME, version: '0.0.0' }; const PLUGIN_MANIFEST_DEFAULT2 = { name: PLUGIN_NAME2, version: '0.0.0' }; const PLUGIN_MANIFEST_NEW_VERSION = { name: PLUGIN_NAME, version: '1.0.0' }; -type RouteHandler = (route: import('@playwright/test').Route) => void; +type RouteHandler = (route: Route) => void; -/** - * Creates a mutable route handler whose behavior can be swapped at runtime. - * This avoids Playwright's handler stacking issues (where calling page.route() - * multiple times on the same URL adds stacked handlers). Instead, we register - * ONE handler that delegates to a mutable reference. - */ +// Single handler avoids Playwright's handler stacking when re-routing the same URL. function createMutableHandler(initial: RouteHandler) { let current = initial; const handler: RouteHandler = (route) => current(route); @@ -36,46 +32,48 @@ function createMutableHandler(initial: RouteHandler) { return { handler, setHandler }; } -/** - * Navigate to `/` and wait for the PollConsoleUpdates component to initialize. - * - * The component uses a prev/current ref pattern: it needs at least 2 poll - * cycles (~15s apart) before `stateInitialized` becomes true. In CI, OAuth - * redirects can cause multiple navigations (and component remounts) even - * after the dashboard appears. Each navigation resets the counter because - * a remount means the component starts fresh. - */ -async function navigateAndWaitForInit(page: import('@playwright/test').Page) { - await page.goto('/'); - await expect(page.locator('[data-test-id="dashboard"]').first()).toBeVisible({ timeout: 60_000 }); - +// PollConsoleUpdates needs 2 poll cycles to initialize; main-frame navs reset the counter. +async function navigateAndWaitForInit(page: Page) { let count = 0; + let initResolve: () => void; + let initReject: (err: Error) => void; + const initPromise = new Promise((resolve, reject) => { + initResolve = resolve; + initReject = reject; + }); - const resetOnNav = () => { - count = 0; + const resetOnNav = (frame: Frame) => { + if (frame === page.mainFrame()) count = 0; + }; + const onResponse = (resp: Response) => { + if (resp.url().includes('/api/check-updates') && resp.status() === 200) { + count++; + if (count >= 2) { + page.off('response', onResponse); + initResolve(); + } + } }; + page.on('framenavigated', resetOnNav); + page.on('response', onResponse); + + const timer = setTimeout(() => { + page.off('response', onResponse); + initReject(new Error(`navigateAndWaitForInit: only saw ${count}/2 responses`)); + }, 90_000); try { - await new Promise((resolve, reject) => { - const timer = setTimeout( - () => reject(new Error(`navigateAndWaitForInit: only saw ${count}/2 responses`)), - 90_000, - ); - const handler = (resp: import('@playwright/test').Response) => { - if (resp.url().includes('/api/check-updates') && resp.status() === 200) { - count++; - if (count >= 2) { - clearTimeout(timer); - page.off('response', handler); - resolve(); - } - } - }; - page.on('response', handler); + await page.goto('/'); + await expect(page.locator('[data-test-id="dashboard"]').first()).toBeVisible({ + timeout: 60_000, }); + await initPromise; } finally { + clearTimeout(timer); page.off('framenavigated', resetOnNav); + page.off('response', onResponse); + initPromise.catch(() => {}); } } diff --git a/frontend/e2e/tests/console/favorites/favorites.spec.ts b/frontend/e2e/tests/console/favorites/favorites.spec.ts index d0f5585ba7e..44722c34ccc 100644 --- a/frontend/e2e/tests/console/favorites/favorites.spec.ts +++ b/frontend/e2e/tests/console/favorites/favorites.spec.ts @@ -1,3 +1,4 @@ +import { isNotFound } from '../../../clients/kubernetes-client'; import { test, expect } from '../../../fixtures'; test.describe('Favorites', { tag: ['@admin'] }, () => { @@ -11,8 +12,8 @@ test.describe('Favorites', { tag: ['@admin'] }, () => { 'openshift-console-user-settings', { 'console.favorites': '[]', 'console.lastNamespace': '' }, ); - } catch { - // ConfigMap may not exist yet + } catch (e) { + if (!isNotFound(e)) throw e; } }); @@ -120,5 +121,16 @@ test.describe('Favorites', { tag: ['@admin'] }, () => { await expect(page.getByTestId('favorite-button').first()).toBeDisabled(); }); + await test.step('Remove all favorites to clean up for future runs', async () => { + try { + await k8sClient.patchConfigMap( + 'user-settings-kubeadmin', + 'openshift-console-user-settings', + { 'console.favorites': '[]' }, + ); + } catch (e) { + if (!isNotFound(e)) throw e; + } + }); }); }); diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index d33160c2f81..e301d244394 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -50,7 +50,7 @@ export default defineConfig({ testDir: './e2e/tests', testMatch: '**/*.spec.ts', forbidOnly: isCI, - globalTimeout: parseInt(process.env.GLOBAL_TIMEOUT_MS || '0', 10) || (isCI ? 110 * 60 * 1000 : 0), + globalTimeout: Number(process.env.GLOBAL_TIMEOUT_MS) || 0, retries: isCI ? 1 : 0, timeout: 120_000, reporter: isCI @@ -86,7 +86,7 @@ export default defineConfig({ }, }, - workers: process.env.WORKERS ? parseInt(process.env.WORKERS, 10) : undefined, + workers: process.env.WORKERS ? Number(process.env.WORKERS) || undefined : undefined, projects: [ { diff --git a/test-prow-playwright-e2e.sh b/test-prow-playwright-e2e.sh index 196f4c8bd11..ffded4ce090 100755 --- a/test-prow-playwright-e2e.sh +++ b/test-prow-playwright-e2e.sh @@ -48,6 +48,7 @@ export BRIDGE_BASE_ADDRESS="$(oc get consoles.config.openshift.io cluster -o jso ./contrib/create-user.sh export WORKERS="${WORKERS:-2}" +export GLOBAL_TIMEOUT_MS="${GLOBAL_TIMEOUT_MS:-6600000}" pushd frontend From efc671d53c3fbf21affb8fea5232a49ba41af3e4 Mon Sep 17 00:00:00 2001 From: Robb Hamilton Date: Tue, 28 Jul 2026 13:04:02 -0400 Subject: [PATCH 2/2] CONSOLE-5196: Consolidate alertmanager receiver specs into single serial file All four receiver specs (webhook, email, slack, pagerduty) mutate the same cluster-wide alertmanager-main secret. With WORKERS=2, separate spec files run in parallel, so one test's resetAlertmanagerConfig wipes the receiver another test just created. Consolidating into a single file with serial mode ensures they never overlap. Co-Authored-By: Claude Opus 4.6 --- .../alertmanager/receivers/email.spec.ts | 228 ------- .../alertmanager/receivers/pagerduty.spec.ts | 219 ------- .../alertmanager/receivers/receivers.spec.ts | 563 ++++++++++++++++++ .../alertmanager/receivers/slack.spec.ts | 142 ----- .../alertmanager/receivers/webhook.spec.ts | 90 --- 5 files changed, 563 insertions(+), 679 deletions(-) delete mode 100644 frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/email.spec.ts delete mode 100644 frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/pagerduty.spec.ts create mode 100644 frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/receivers.spec.ts delete mode 100644 frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/slack.spec.ts delete mode 100644 frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/webhook.spec.ts diff --git a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/email.spec.ts b/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/email.spec.ts deleted file mode 100644 index bf5bd09bdf8..00000000000 --- a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/email.spec.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { test, expect } from '../../../../../fixtures'; -import { - AlertmanagerPage, - getGlobalsAndReceiverConfig, -} from '../../../../../pages/alertmanager-page'; -import KubernetesClient from '../../../../../clients/kubernetes-client'; - -test.describe.configure({ mode: 'serial' }); - -test.describe('Alertmanager Email Receiver Form', { tag: ['@admin'] }, () => { - let alertmanager: AlertmanagerPage; - let k8sClient: KubernetesClient; - - const receiverName = `EmailReceiver-${Date.now()}`; - const receiverType = 'email'; - const configName = `${receiverType}_configs`; - const localhost = 'localhost'; - const label = 'severity = warning'; - const emailTo = 'you@there.com'; - const emailFrom = 'me@here.com'; - const emailSmarthost = 'smarthost:8080'; - const username = 'username'; - const password = 'password'; - const identity = 'identity'; - const secret = 'secret'; - const html = 'myhtml'; - - // Default Alertmanager YAML for reset - const defaultAlertmanagerYaml = `global: - resolve_timeout: 5m -inhibit_rules: -- equal: - - namespace - - alertname - source_match: - severity: critical - target_match_re: - severity: warning|info -- equal: - - namespace - - alertname - source_match: - severity: warning - target_match_re: - severity: info -receivers: -- name: Default -- name: Watchdog -- name: Critical -route: - group_by: - - namespace - group_interval: 5m - group_wait: 30s - receiver: Default - repeat_interval: 12h - routes: - - matchers: - - alertname = Watchdog - receiver: Watchdog - - matchers: - - severity = critical - receiver: Critical`; - - test.beforeEach(async ({ page, k8sClient: client }) => { - alertmanager = new AlertmanagerPage(page); - k8sClient = client; - }); - - test.afterEach(async () => { - // Reset alertmanager configuration - await k8sClient.patchSecret('alertmanager-main', 'openshift-monitoring', [ - { - op: 'replace', - path: '/data/alertmanager.yaml', - value: Buffer.from(defaultAlertmanagerYaml).toString('base64'), - }, - ]); - }); - - test('creates and edits Email Receiver correctly', async ({ page }) => { - await test.step('Create Email Receiver with basic configuration', async () => { - await alertmanager.navigateToAlertmanager(); - await alertmanager.createReceiver(receiverName, configName); - - // Verify defaults before smtp change - const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); - await expect(saveAsDefaultCheckbox).toBeDisabled(); - - const emailHelloInput = page.getByTestId('email-hello'); - await expect(emailHelloInput).toHaveValue(localhost); - - const requireTlsCheckbox = page.getByTestId('email-require-tls'); - await expect(requireTlsCheckbox).toBeChecked(); - - // Check advanced configuration defaults - await alertmanager.showAdvancedConfiguration(); - const sendResolvedCheckbox = page.getByTestId('send-resolved-alerts'); - await expect(sendResolvedCheckbox).not.toBeChecked(); - - const emailHtmlInput = page.getByTestId('email-html'); - await expect(emailHtmlInput).toHaveValue('{{ template "email.default.html" . }}'); - - // Fill in required fields - await page.getByTestId('email-to').fill(emailTo); - await page.getByTestId('email-from').fill(emailFrom); - - // Save as default should now be enabled - await expect(saveAsDefaultCheckbox).toBeEnabled(); - - await page.getByTestId('email-smarthost').fill(emailSmarthost); - await page.getByTestId('label-0').fill(label); - - await alertmanager.save(); - }); - - await test.step('Verify Email Receiver was created correctly', async () => { - await alertmanager.validateReceiverInList(receiverName); - - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - // Verify values are NOT in globals - expect(configs.globals).not.toHaveProperty('email_to'); - expect(configs.globals).not.toHaveProperty('smtp_from'); - expect(configs.globals).not.toHaveProperty('smtp_smarthost'); - expect(configs.globals).not.toHaveProperty('smtp_require_tls'); - - // Verify values ARE in receiver config - expect(configs.receiverConfig.to).toBe(emailTo); - expect(configs.receiverConfig.from).toBe(emailFrom); - expect(configs.receiverConfig.smarthost).toBe(emailSmarthost); - // require_tls should not be in receiver config (unchanged from global) - expect(configs.receiverConfig).not.toHaveProperty('require_tls'); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Edit receiver with auth and advanced fields', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - // Verify existing values - await expect(page.getByTestId('email-to')).toHaveValue(emailTo); - - const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); - await expect(saveAsDefaultCheckbox).toBeEnabled(); - await expect(saveAsDefaultCheckbox).not.toBeChecked(); - - await expect(page.getByTestId('email-from')).toHaveValue(emailFrom); - await expect(page.getByTestId('email-hello')).toHaveValue(localhost); - - // Add auth fields - await page.getByTestId('email-auth-username').fill(username); - await page.getByTestId('email-auth-password').fill(password); - await page.getByTestId('email-auth-identity').fill(identity); - await page.getByTestId('email-auth-secret').fill(secret); - - // Uncheck require TLS - await page.getByTestId('email-require-tls').uncheck(); - - // Update advanced fields - await alertmanager.showAdvancedConfiguration(); - await page.getByTestId('send-resolved-alerts').check(); - - const htmlInput = page.getByTestId('email-html'); - await htmlInput.clear(); - await htmlInput.fill(html); - - await alertmanager.save(); - }); - - await test.step('Verify auth and advanced fields were saved correctly', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - // Auth username should NOT be in globals - expect(configs.globals).not.toHaveProperty('smtp_auth_username'); - - // Auth fields should be in receiver config - expect(configs.receiverConfig.auth_username).toBe(username); - expect(configs.receiverConfig.auth_password).toBe(password); - expect(configs.receiverConfig.auth_identity).toBe(identity); - expect(configs.receiverConfig.auth_secret).toBe(secret); - - // require_tls should now be explicitly false in receiver config - expect(configs.receiverConfig.require_tls).toBe(false); - - // Advanced fields - expect(configs.receiverConfig.send_resolved).toBe(true); - expect(configs.receiverConfig.html).toBe(html); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Save fields as global defaults', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); - await expect(saveAsDefaultCheckbox).not.toBeChecked(); - await saveAsDefaultCheckbox.check(); - - await alertmanager.save(); - }); - - await test.step('Verify fields were saved as globals', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - // Verify values are now in globals - expect(configs.globals.smtp_from).toBe(emailFrom); - expect(configs.globals.smtp_hello).toBe(localhost); - expect(configs.globals.smtp_smarthost).toBe(emailSmarthost); - expect(configs.globals.smtp_auth_username).toBe(username); - expect(configs.globals.smtp_auth_password).toBe(password); - expect(configs.globals.smtp_auth_identity).toBe(identity); - expect(configs.globals.smtp_auth_secret).toBe(secret); - expect(configs.globals.smtp_require_tls).toBe(false); - - // Non-global field (to) should still be in receiver config - expect(configs.receiverConfig.to).toBe(emailTo); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - }); -}); diff --git a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/pagerduty.spec.ts b/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/pagerduty.spec.ts deleted file mode 100644 index 844be4c8b5b..00000000000 --- a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/pagerduty.spec.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { test, expect } from '../../../../../fixtures'; -import { - AlertmanagerPage, - getGlobalsAndReceiverConfig, -} from '../../../../../pages/alertmanager-page'; -import KubernetesClient from '../../../../../clients/kubernetes-client'; -import { resetAlertmanagerConfig } from '../alertmanager-test-utils'; - -test.describe.configure({ mode: 'serial' }); - -test.describe('Alertmanager PagerDuty Receiver Form', { tag: ['@admin'] }, () => { - let alertmanager: AlertmanagerPage; - let k8sClient: KubernetesClient; - - const receiverName = `PagerDutyReceiver-${Date.now()}`; - const receiverType = 'pagerduty'; - const configName = `${receiverType}_configs`; - const severity = 'severity'; - const label = `${severity} = warning`; - const pagerDutyClient = '{{ template "pagerduty.default.client" . }}'; - const pagerDutyClientURL = '{{ template "pagerduty.default.clientURL" . }}'; - const pagerDutyURL1 = 'http://pagerduty-url-specific-to-receiver'; - const pagerDutyURL2 = 'http://global-pagerduty-url'; - const pagerDutyURL3 = 'http://pagerduty-url-specific-to-receiver'; - const clientURL = 'http://updated-client-url'; - const pagerDutyDescription = 'new description'; - - test.beforeEach(async ({ page, k8sClient: client }) => { - alertmanager = new AlertmanagerPage(page); - k8sClient = client; - await resetAlertmanagerConfig(k8sClient); - }); - - test.afterEach(async () => { - await resetAlertmanagerConfig(k8sClient); - }); - - test('creates and edits PagerDuty Receiver correctly', async ({ page }) => { - test.setTimeout(180_000); - - await test.step('Create PagerDuty Receiver with basic configuration', async () => { - await alertmanager.navigateToAlertmanager(); - await alertmanager.createReceiver(receiverName, configName); - - await page.getByTestId('integration-key').fill(''); - - // Verify default URL - await expect(page.getByTestId('pagerduty-url')).toHaveValue( - 'https://events.pagerduty.com/v2/enqueue', - ); - - // Check advanced configuration defaults - await alertmanager.showAdvancedConfiguration(); - await expect(page.getByTestId('send-resolved-alerts')).toBeChecked(); - await expect(page.getByTestId('pagerduty-client')).toHaveValue(pagerDutyClient); - await expect(page.getByTestId('pagerduty-client-url')).toHaveValue(pagerDutyClientURL); - await expect(page.getByTestId('pagerduty-description')).toHaveValue( - '{{ template "pagerduty.default.description" .}}', - ); - await expect(page.getByTestId('pagerduty-severity')).toHaveValue('error'); - - await page.getByTestId('label-0').fill(label); - await alertmanager.save(); - }); - - await test.step('Verify PagerDuty Receiver was created correctly', async () => { - await alertmanager.validateReceiverInList(receiverName); - }); - - await test.step('Update pagerduty_url', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - // Save as default checkbox disabled when url equals global url - await expect(page.getByTestId('save-as-default')).toBeDisabled(); - - // Changing url enables Save as default checkbox - const urlInput = page.getByTestId('pagerduty-url'); - await urlInput.clear(); - await urlInput.fill(pagerDutyURL1); - - await expect(page.getByTestId('save-as-default')).toBeEnabled(); - await alertmanager.save(); - }); - - await test.step('Verify pagerduty_url was saved with Receiver and not global', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.globals).not.toHaveProperty('pagerduty_url'); - expect(configs.receiverConfig.url).toBe(pagerDutyURL1); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Save pagerduty_url as global', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - const urlInput = page.getByTestId('pagerduty-url'); - await urlInput.clear(); - await urlInput.fill(pagerDutyURL2); - - const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); - await expect(saveAsDefaultCheckbox).toBeEnabled(); - await saveAsDefaultCheckbox.check(); - - await alertmanager.save(); - }); - - await test.step('Verify pagerduty_url was saved as global', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.globals.pagerduty_url).toBe(pagerDutyURL2); - expect(configs.receiverConfig).not.toHaveProperty('url'); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Add pagerduty_url to receiver with existing global', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - const urlInput = page.getByTestId('pagerduty-url'); - await urlInput.clear(); - await urlInput.fill(pagerDutyURL3); - - const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); - await expect(saveAsDefaultCheckbox).toBeEnabled(); - await expect(saveAsDefaultCheckbox).not.toBeChecked(); - - await alertmanager.save(); - }); - - await test.step( - 'Verify pagerduty_url saved with Receiver and global still exists', - async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.globals.pagerduty_url).toBe(pagerDutyURL2); - expect(configs.receiverConfig.url).toBe(pagerDutyURL3); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }, - ); - - await test.step('Update advanced configuration fields', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - await alertmanager.showAdvancedConfiguration(); - - const sendResolvedCheckbox = page.getByTestId('send-resolved-alerts'); - await expect(sendResolvedCheckbox).toBeChecked(); - await sendResolvedCheckbox.uncheck(); - await expect(sendResolvedCheckbox).not.toBeChecked(); - - await page.getByTestId('pagerduty-client').clear(); - await page.getByTestId('pagerduty-client').fill('updated-client'); - - await page.getByTestId('pagerduty-client-url').clear(); - await page.getByTestId('pagerduty-client-url').fill(clientURL); - - await alertmanager.save(); - }); - - await test.step('Verify changed fields are saved with Receiver', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.receiverConfig.send_resolved).toBe(false); - expect(configs.receiverConfig.client).toBe('updated-client'); - expect(configs.receiverConfig.client_url).toBe('http://updated-client-url'); - expect(configs.receiverConfig.description).toBeUndefined(); - expect(configs.receiverConfig.severity).toBeUndefined(); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Restore defaults, change desc and severity', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - await alertmanager.showAdvancedConfiguration(); - - const sendResolvedCheckbox = page.getByTestId('send-resolved-alerts'); - await expect(sendResolvedCheckbox).not.toBeChecked(); - await sendResolvedCheckbox.check(); - await expect(sendResolvedCheckbox).toBeChecked(); - - await page.getByTestId('pagerduty-client').clear(); - await page.getByTestId('pagerduty-client').fill(pagerDutyClient); - - await page.getByTestId('pagerduty-client-url').clear(); - await page.getByTestId('pagerduty-client-url').fill(pagerDutyClientURL); - - await page.getByTestId('pagerduty-description').clear(); - await page.getByTestId('pagerduty-description').fill(pagerDutyDescription); - - await page.getByTestId('pagerduty-severity').clear(); - await page.getByTestId('pagerduty-severity').fill(severity); - - await alertmanager.save(); - }); - - await test.step('Verify defaults removed from config, desc and severity saved', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.receiverConfig.send_resolved).toBeUndefined(); - expect(configs.receiverConfig.client).toBeUndefined(); - expect(configs.receiverConfig.client_url).toBeUndefined(); - expect(configs.receiverConfig.description).toBe(pagerDutyDescription); - expect(configs.receiverConfig.severity).toBe(severity); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - }); -}); diff --git a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/receivers.spec.ts b/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/receivers.spec.ts new file mode 100644 index 00000000000..9ef5afd2ec9 --- /dev/null +++ b/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/receivers.spec.ts @@ -0,0 +1,563 @@ +import { test, expect } from '../../../../../fixtures'; +import { + AlertmanagerPage, + getGlobalsAndReceiverConfig, +} from '../../../../../pages/alertmanager-page'; +import KubernetesClient from '../../../../../clients/kubernetes-client'; +import { resetAlertmanagerConfig } from '../alertmanager-test-utils'; + +test.describe('Alertmanager Receiver Forms', { tag: ['@admin'] }, () => { + test.describe.configure({ mode: 'serial' }); + + let alertmanager: AlertmanagerPage; + let k8sClient: KubernetesClient; + + test.beforeEach(async ({ page, k8sClient: client }) => { + alertmanager = new AlertmanagerPage(page); + k8sClient = client; + await resetAlertmanagerConfig(k8sClient); + }); + + test.afterEach(async () => { + await resetAlertmanagerConfig(k8sClient); + }); + + test('creates and edits Webhook Receiver correctly', async ({ page }) => { + const receiverName = `WebhookReceiver-${Date.now()}`; + const configName = 'webhook_configs'; + const label = 'severity = warning'; + const webhookURL = 'http://mywebhookurl'; + const updatedWebhookURL = 'http://myupdatedwebhookurl'; + + await test.step('Create Webhook Receiver', async () => { + await alertmanager.navigateToAlertmanager(); + await alertmanager.createReceiver(receiverName, configName); + + await alertmanager.showAdvancedConfiguration(); + await expect(page.getByTestId('send-resolved-alerts')).toBeChecked(); + + await page.getByTestId('webhook-url').fill(webhookURL); + await page.getByTestId('label-0').fill(label); + + await alertmanager.save(); + }); + + await test.step('Verify Webhook Receiver was created correctly', async () => { + await alertmanager.validateReceiverInList(receiverName); + + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.receiverConfig.url).toBe(webhookURL); + expect(configs.receiverConfig).not.toHaveProperty('send_resolved'); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Edit Webhook Receiver and save advanced fields', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + await expect(page.getByTestId('webhook-url')).toHaveValue(webhookURL); + + await page.getByTestId('webhook-url').clear(); + await page.getByTestId('webhook-url').fill(updatedWebhookURL); + + await alertmanager.showAdvancedConfiguration(); + await page.getByTestId('send-resolved-alerts').uncheck(); + + await alertmanager.save(); + }); + + await test.step('Verify advanced fields were saved correctly', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + await alertmanager.showAdvancedConfiguration(); + + await expect(page.getByTestId('send-resolved-alerts')).not.toBeChecked(); + }); + + await test.step('Verify YAML has correct config', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.receiverConfig.url).toBe(updatedWebhookURL); + expect(configs.receiverConfig.send_resolved).toBe(false); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + }); + + test('creates and edits Email Receiver correctly', async ({ page }) => { + const receiverName = `EmailReceiver-${Date.now()}`; + const configName = 'email_configs'; + const localhost = 'localhost'; + const label = 'severity = warning'; + const emailTo = 'you@there.com'; + const emailFrom = 'me@here.com'; + const emailSmarthost = 'smarthost:8080'; + const username = 'username'; + const password = 'password'; + const identity = 'identity'; + const secret = 'secret'; + const html = 'myhtml'; + + await test.step('Create Email Receiver with basic configuration', async () => { + await alertmanager.navigateToAlertmanager(); + await alertmanager.createReceiver(receiverName, configName); + + // Verify defaults before smtp change + const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); + await expect(saveAsDefaultCheckbox).toBeDisabled(); + + const emailHelloInput = page.getByTestId('email-hello'); + await expect(emailHelloInput).toHaveValue(localhost); + + const requireTlsCheckbox = page.getByTestId('email-require-tls'); + await expect(requireTlsCheckbox).toBeChecked(); + + // Check advanced configuration defaults + await alertmanager.showAdvancedConfiguration(); + const sendResolvedCheckbox = page.getByTestId('send-resolved-alerts'); + await expect(sendResolvedCheckbox).not.toBeChecked(); + + const emailHtmlInput = page.getByTestId('email-html'); + await expect(emailHtmlInput).toHaveValue('{{ template "email.default.html" . }}'); + + // Fill in required fields + await page.getByTestId('email-to').fill(emailTo); + await page.getByTestId('email-from').fill(emailFrom); + + // Save as default should now be enabled + await expect(saveAsDefaultCheckbox).toBeEnabled(); + + await page.getByTestId('email-smarthost').fill(emailSmarthost); + await page.getByTestId('label-0').fill(label); + + await alertmanager.save(); + }); + + await test.step('Verify Email Receiver was created correctly', async () => { + await alertmanager.validateReceiverInList(receiverName); + + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + // Verify values are NOT in globals + expect(configs.globals).not.toHaveProperty('email_to'); + expect(configs.globals).not.toHaveProperty('smtp_from'); + expect(configs.globals).not.toHaveProperty('smtp_smarthost'); + expect(configs.globals).not.toHaveProperty('smtp_require_tls'); + + // Verify values ARE in receiver config + expect(configs.receiverConfig.to).toBe(emailTo); + expect(configs.receiverConfig.from).toBe(emailFrom); + expect(configs.receiverConfig.smarthost).toBe(emailSmarthost); + // require_tls should not be in receiver config (unchanged from global) + expect(configs.receiverConfig).not.toHaveProperty('require_tls'); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Edit receiver with auth and advanced fields', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + // Verify existing values + await expect(page.getByTestId('email-to')).toHaveValue(emailTo); + + const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); + await expect(saveAsDefaultCheckbox).toBeEnabled(); + await expect(saveAsDefaultCheckbox).not.toBeChecked(); + + await expect(page.getByTestId('email-from')).toHaveValue(emailFrom); + await expect(page.getByTestId('email-hello')).toHaveValue(localhost); + + // Add auth fields + await page.getByTestId('email-auth-username').fill(username); + await page.getByTestId('email-auth-password').fill(password); + await page.getByTestId('email-auth-identity').fill(identity); + await page.getByTestId('email-auth-secret').fill(secret); + + // Uncheck require TLS + await page.getByTestId('email-require-tls').uncheck(); + + // Update advanced fields + await alertmanager.showAdvancedConfiguration(); + await page.getByTestId('send-resolved-alerts').check(); + + const htmlInput = page.getByTestId('email-html'); + await htmlInput.clear(); + await htmlInput.fill(html); + + await alertmanager.save(); + }); + + await test.step('Verify auth and advanced fields were saved correctly', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + // Auth username should NOT be in globals + expect(configs.globals).not.toHaveProperty('smtp_auth_username'); + + // Auth fields should be in receiver config + expect(configs.receiverConfig.auth_username).toBe(username); + expect(configs.receiverConfig.auth_password).toBe(password); + expect(configs.receiverConfig.auth_identity).toBe(identity); + expect(configs.receiverConfig.auth_secret).toBe(secret); + + // require_tls should now be explicitly false in receiver config + expect(configs.receiverConfig.require_tls).toBe(false); + + // Advanced fields + expect(configs.receiverConfig.send_resolved).toBe(true); + expect(configs.receiverConfig.html).toBe(html); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Save fields as global defaults', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); + await expect(saveAsDefaultCheckbox).not.toBeChecked(); + await saveAsDefaultCheckbox.check(); + + await alertmanager.save(); + }); + + await test.step('Verify fields were saved as globals', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + // Verify values are now in globals + expect(configs.globals.smtp_from).toBe(emailFrom); + expect(configs.globals.smtp_hello).toBe(localhost); + expect(configs.globals.smtp_smarthost).toBe(emailSmarthost); + expect(configs.globals.smtp_auth_username).toBe(username); + expect(configs.globals.smtp_auth_password).toBe(password); + expect(configs.globals.smtp_auth_identity).toBe(identity); + expect(configs.globals.smtp_auth_secret).toBe(secret); + expect(configs.globals.smtp_require_tls).toBe(false); + + // Non-global field (to) should still be in receiver config + expect(configs.receiverConfig.to).toBe(emailTo); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + }); + + test('creates and edits Slack Receiver correctly', async ({ page }) => { + const receiverName = `SlackReceiver-${Date.now()}`; + const configName = 'slack_configs'; + const label = 'severity = warning'; + const slackAPIURL = 'http://myslackapi'; + const slackChannel = 'myslackchannel'; + const slackIconURL = 'http://slackiconurl'; + const slackUsername = 'slackusername'; + + await test.step('Create Slack Receiver with basic configuration', async () => { + await alertmanager.navigateToAlertmanager(); + await alertmanager.createReceiver(receiverName, configName); + + await expect(page.getByTestId('save-as-default')).toBeDisabled(); + + await alertmanager.showAdvancedConfiguration(); + + // Verify defaults + await expect(page.getByTestId('send-resolved-alerts')).not.toBeChecked(); + await expect(page.getByTestId('slack-icon-url')).toHaveValue( + '{{ template "slack.default.iconurl" .}}', + ); + await expect(page.getByTestId('slack-icon-emoji')).toBeHidden(); + + // Switch to Emoji radio and verify + await page.getByTestId('Emoji-radio-input').click(); + await expect(page.getByTestId('slack-icon-url')).toBeHidden(); + await expect(page.getByTestId('slack-icon-emoji')).toHaveValue( + '{{ template "slack.default.iconemoji" .}}', + ); + + // Switch back to URL for the test + await page.getByTestId('URL-radio-input').click(); + + await expect(page.getByTestId('slack-username')).toHaveValue( + '{{ template "slack.default.username" . }}', + ); + await expect(page.getByTestId('slack-link-names')).not.toBeChecked(); + + // Fill required fields + await page.getByTestId('slack-api-url').fill(slackAPIURL); + await expect(page.getByTestId('save-as-default')).toBeEnabled(); + + await page.getByTestId('slack-channel').fill(slackChannel); + await page.getByTestId('label-0').fill(label); + + await alertmanager.save(); + }); + + await test.step('Verify Slack Receiver was created correctly', async () => { + await alertmanager.validateReceiverInList(receiverName); + + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.globals).not.toHaveProperty('slack_api_url'); + expect(configs.receiverConfig.channel).toBe(slackChannel); + expect(configs.receiverConfig.api_url).toBe(slackAPIURL); + // Advanced fields are not saved since they equal their global values + expect(configs.receiverConfig).not.toHaveProperty('send_resolved'); + expect(configs.receiverConfig).not.toHaveProperty('username'); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Save globals and advanced fields', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + await expect(page.getByTestId('slack-channel')).toHaveValue(slackChannel); + await expect(page.getByTestId('save-as-default')).toBeEnabled(); + await expect(page.getByTestId('slack-api-url')).toHaveValue(slackAPIURL); + + await alertmanager.showAdvancedConfiguration(); + + await page.getByTestId('send-resolved-alerts').check(); + + await page.getByTestId('slack-icon-url').clear(); + await page.getByTestId('slack-icon-url').fill(slackIconURL); + + await page.getByTestId('slack-username').clear(); + await page.getByTestId('slack-username').fill(slackUsername); + + await page.getByTestId('slack-link-names').check(); + + await page.getByTestId('save-as-default').check(); + + await alertmanager.save(); + }); + + await test.step('Verify advanced fields were saved correctly', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + await alertmanager.showAdvancedConfiguration(); + + await expect(page.getByTestId('send-resolved-alerts')).toBeChecked(); + await expect(page.getByTestId('slack-icon-url')).toHaveValue(slackIconURL); + await expect(page.getByTestId('slack-icon-emoji')).toBeHidden(); + await expect(page.getByTestId('slack-username')).toHaveValue(slackUsername); + await expect(page.getByTestId('slack-link-names')).toBeChecked(); + }); + + await test.step('Verify YAML has correct global and receiver config', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.globals.slack_api_url).toBe(slackAPIURL); + expect(configs.receiverConfig).not.toHaveProperty('api_url'); + expect(configs.receiverConfig.channel).toBe('myslackchannel'); + expect(configs.receiverConfig.send_resolved).toBe(true); + expect(configs.receiverConfig.icon_url).toBe(slackIconURL); + expect(configs.receiverConfig.username).toBe(slackUsername); + expect(configs.receiverConfig.link_names).toBe(true); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + }); + + test('creates and edits PagerDuty Receiver correctly', async ({ page }) => { + test.setTimeout(180_000); + + const receiverName = `PagerDutyReceiver-${Date.now()}`; + const configName = 'pagerduty_configs'; + const severity = 'severity'; + const label = `${severity} = warning`; + const pagerDutyClient = '{{ template "pagerduty.default.client" . }}'; + const pagerDutyClientURL = '{{ template "pagerduty.default.clientURL" . }}'; + const pagerDutyURL1 = 'http://pagerduty-url-specific-to-receiver'; + const pagerDutyURL2 = 'http://global-pagerduty-url'; + const pagerDutyURL3 = 'http://pagerduty-url-specific-to-receiver'; + const clientURL = 'http://updated-client-url'; + const pagerDutyDescription = 'new description'; + + await test.step('Create PagerDuty Receiver with basic configuration', async () => { + await alertmanager.navigateToAlertmanager(); + await alertmanager.createReceiver(receiverName, configName); + + await page.getByTestId('integration-key').fill(''); + + // Verify default URL + await expect(page.getByTestId('pagerduty-url')).toHaveValue( + 'https://events.pagerduty.com/v2/enqueue', + ); + + // Check advanced configuration defaults + await alertmanager.showAdvancedConfiguration(); + await expect(page.getByTestId('send-resolved-alerts')).toBeChecked(); + await expect(page.getByTestId('pagerduty-client')).toHaveValue(pagerDutyClient); + await expect(page.getByTestId('pagerduty-client-url')).toHaveValue(pagerDutyClientURL); + await expect(page.getByTestId('pagerduty-description')).toHaveValue( + '{{ template "pagerduty.default.description" .}}', + ); + await expect(page.getByTestId('pagerduty-severity')).toHaveValue('error'); + + await page.getByTestId('label-0').fill(label); + await alertmanager.save(); + }); + + await test.step('Verify PagerDuty Receiver was created correctly', async () => { + await alertmanager.validateReceiverInList(receiverName); + }); + + await test.step('Update pagerduty_url', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + // Save as default checkbox disabled when url equals global url + await expect(page.getByTestId('save-as-default')).toBeDisabled(); + + // Changing url enables Save as default checkbox + const urlInput = page.getByTestId('pagerduty-url'); + await urlInput.clear(); + await urlInput.fill(pagerDutyURL1); + + await expect(page.getByTestId('save-as-default')).toBeEnabled(); + await alertmanager.save(); + }); + + await test.step('Verify pagerduty_url was saved with Receiver and not global', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.globals).not.toHaveProperty('pagerduty_url'); + expect(configs.receiverConfig.url).toBe(pagerDutyURL1); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Save pagerduty_url as global', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + const urlInput = page.getByTestId('pagerduty-url'); + await urlInput.clear(); + await urlInput.fill(pagerDutyURL2); + + const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); + await expect(saveAsDefaultCheckbox).toBeEnabled(); + await saveAsDefaultCheckbox.check(); + + await alertmanager.save(); + }); + + await test.step('Verify pagerduty_url was saved as global', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.globals.pagerduty_url).toBe(pagerDutyURL2); + expect(configs.receiverConfig).not.toHaveProperty('url'); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Add pagerduty_url to receiver with existing global', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + + const urlInput = page.getByTestId('pagerduty-url'); + await urlInput.clear(); + await urlInput.fill(pagerDutyURL3); + + const saveAsDefaultCheckbox = page.getByTestId('save-as-default'); + await expect(saveAsDefaultCheckbox).toBeEnabled(); + await expect(saveAsDefaultCheckbox).not.toBeChecked(); + + await alertmanager.save(); + }); + + await test.step( + 'Verify pagerduty_url saved with Receiver and global still exists', + async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.globals.pagerduty_url).toBe(pagerDutyURL2); + expect(configs.receiverConfig.url).toBe(pagerDutyURL3); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }, + ); + + await test.step('Update advanced configuration fields', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + await alertmanager.showAdvancedConfiguration(); + + const sendResolvedCheckbox = page.getByTestId('send-resolved-alerts'); + await expect(sendResolvedCheckbox).toBeChecked(); + await sendResolvedCheckbox.uncheck(); + await expect(sendResolvedCheckbox).not.toBeChecked(); + + await page.getByTestId('pagerduty-client').clear(); + await page.getByTestId('pagerduty-client').fill('updated-client'); + + await page.getByTestId('pagerduty-client-url').clear(); + await page.getByTestId('pagerduty-client-url').fill(clientURL); + + await alertmanager.save(); + }); + + await test.step('Verify changed fields are saved with Receiver', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.receiverConfig.send_resolved).toBe(false); + expect(configs.receiverConfig.client).toBe('updated-client'); + expect(configs.receiverConfig.client_url).toBe('http://updated-client-url'); + expect(configs.receiverConfig.description).toBeUndefined(); + expect(configs.receiverConfig.severity).toBeUndefined(); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + + await test.step('Restore defaults, change desc and severity', async () => { + await alertmanager.navigateToEditReceiver(receiverName); + await alertmanager.showAdvancedConfiguration(); + + const sendResolvedCheckbox = page.getByTestId('send-resolved-alerts'); + await expect(sendResolvedCheckbox).not.toBeChecked(); + await sendResolvedCheckbox.check(); + await expect(sendResolvedCheckbox).toBeChecked(); + + await page.getByTestId('pagerduty-client').clear(); + await page.getByTestId('pagerduty-client').fill(pagerDutyClient); + + await page.getByTestId('pagerduty-client-url').clear(); + await page.getByTestId('pagerduty-client-url').fill(pagerDutyClientURL); + + await page.getByTestId('pagerduty-description').clear(); + await page.getByTestId('pagerduty-description').fill(pagerDutyDescription); + + await page.getByTestId('pagerduty-severity').clear(); + await page.getByTestId('pagerduty-severity').fill(severity); + + await alertmanager.save(); + }); + + await test.step('Verify defaults removed from config, desc and severity saved', async () => { + await expect(async () => { + await alertmanager.navigateToYAMLPage(); + const yamlContent = await alertmanager.getYAMLContent(); + const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); + + expect(configs.receiverConfig.send_resolved).toBeUndefined(); + expect(configs.receiverConfig.client).toBeUndefined(); + expect(configs.receiverConfig.client_url).toBeUndefined(); + expect(configs.receiverConfig.description).toBe(pagerDutyDescription); + expect(configs.receiverConfig.severity).toBe(severity); + }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); + }); + }); +}); diff --git a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/slack.spec.ts b/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/slack.spec.ts deleted file mode 100644 index 302897114a9..00000000000 --- a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/slack.spec.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { test, expect } from '../../../../../fixtures'; -import { - AlertmanagerPage, - getGlobalsAndReceiverConfig, -} from '../../../../../pages/alertmanager-page'; -import KubernetesClient from '../../../../../clients/kubernetes-client'; -import { resetAlertmanagerConfig } from '../alertmanager-test-utils'; - -test.describe.configure({ mode: 'serial' }); - -test.describe('Alertmanager Slack Receiver Form', { tag: ['@admin'] }, () => { - let alertmanager: AlertmanagerPage; - let k8sClient: KubernetesClient; - - const receiverName = `SlackReceiver-${Date.now()}`; - const receiverType = 'slack'; - const configName = `${receiverType}_configs`; - const label = 'severity = warning'; - const slackAPIURL = 'http://myslackapi'; - const slackChannel = 'myslackchannel'; - const slackIconURL = 'http://slackiconurl'; - const slackUsername = 'slackusername'; - - test.beforeEach(async ({ page, k8sClient: client }) => { - alertmanager = new AlertmanagerPage(page); - k8sClient = client; - }); - - test.afterEach(async () => { - await resetAlertmanagerConfig(k8sClient); - }); - - test('creates and edits Slack Receiver correctly', async ({ page }) => { - await test.step('Create Slack Receiver with basic configuration', async () => { - await alertmanager.navigateToAlertmanager(); - await alertmanager.createReceiver(receiverName, configName); - - await expect(page.getByTestId('save-as-default')).toBeDisabled(); - - await alertmanager.showAdvancedConfiguration(); - - // Verify defaults - await expect(page.getByTestId('send-resolved-alerts')).not.toBeChecked(); - await expect(page.getByTestId('slack-icon-url')).toHaveValue( - '{{ template "slack.default.iconurl" .}}', - ); - await expect(page.getByTestId('slack-icon-emoji')).toBeHidden(); - - // Switch to Emoji radio and verify - await page.getByTestId('Emoji-radio-input').click(); - await expect(page.getByTestId('slack-icon-url')).toBeHidden(); - await expect(page.getByTestId('slack-icon-emoji')).toHaveValue( - '{{ template "slack.default.iconemoji" .}}', - ); - - // Switch back to URL for the test - await page.getByTestId('URL-radio-input').click(); - - await expect(page.getByTestId('slack-username')).toHaveValue( - '{{ template "slack.default.username" . }}', - ); - await expect(page.getByTestId('slack-link-names')).not.toBeChecked(); - - // Fill required fields - await page.getByTestId('slack-api-url').fill(slackAPIURL); - await expect(page.getByTestId('save-as-default')).toBeEnabled(); - - await page.getByTestId('slack-channel').fill(slackChannel); - await page.getByTestId('label-0').fill(label); - - await alertmanager.save(); - }); - - await test.step('Verify Slack Receiver was created correctly', async () => { - await alertmanager.validateReceiverInList(receiverName); - - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.globals).not.toHaveProperty('slack_api_url'); - expect(configs.receiverConfig.channel).toBe(slackChannel); - expect(configs.receiverConfig.api_url).toBe(slackAPIURL); - // Advanced fields are not saved since they equal their global values - expect(configs.receiverConfig).not.toHaveProperty('send_resolved'); - expect(configs.receiverConfig).not.toHaveProperty('username'); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Save globals and advanced fields', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - await expect(page.getByTestId('slack-channel')).toHaveValue(slackChannel); - await expect(page.getByTestId('save-as-default')).toBeEnabled(); - await expect(page.getByTestId('slack-api-url')).toHaveValue(slackAPIURL); - - await alertmanager.showAdvancedConfiguration(); - - await page.getByTestId('send-resolved-alerts').check(); - - await page.getByTestId('slack-icon-url').clear(); - await page.getByTestId('slack-icon-url').fill(slackIconURL); - - await page.getByTestId('slack-username').clear(); - await page.getByTestId('slack-username').fill(slackUsername); - - await page.getByTestId('slack-link-names').check(); - - await page.getByTestId('save-as-default').check(); - - await alertmanager.save(); - }); - - await test.step('Verify advanced fields were saved correctly', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - await alertmanager.showAdvancedConfiguration(); - - await expect(page.getByTestId('send-resolved-alerts')).toBeChecked(); - await expect(page.getByTestId('slack-icon-url')).toHaveValue(slackIconURL); - await expect(page.getByTestId('slack-icon-emoji')).toBeHidden(); - await expect(page.getByTestId('slack-username')).toHaveValue(slackUsername); - await expect(page.getByTestId('slack-link-names')).toBeChecked(); - }); - - await test.step('Verify YAML has correct global and receiver config', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.globals.slack_api_url).toBe(slackAPIURL); - expect(configs.receiverConfig).not.toHaveProperty('api_url'); - expect(configs.receiverConfig.channel).toBe('myslackchannel'); - expect(configs.receiverConfig.send_resolved).toBe(true); - expect(configs.receiverConfig.icon_url).toBe(slackIconURL); - expect(configs.receiverConfig.username).toBe(slackUsername); - expect(configs.receiverConfig.link_names).toBe(true); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - }); -}); diff --git a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/webhook.spec.ts b/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/webhook.spec.ts deleted file mode 100644 index 9cb96b244b2..00000000000 --- a/frontend/e2e/tests/console/cluster-settings/alertmanager/receivers/webhook.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { test, expect } from '../../../../../fixtures'; -import { - AlertmanagerPage, - getGlobalsAndReceiverConfig, -} from '../../../../../pages/alertmanager-page'; -import KubernetesClient from '../../../../../clients/kubernetes-client'; -import { resetAlertmanagerConfig } from '../alertmanager-test-utils'; - -test.describe.configure({ mode: 'serial' }); - -test.describe('Alertmanager Webhook Receiver Form', { tag: ['@admin'] }, () => { - let alertmanager: AlertmanagerPage; - let k8sClient: KubernetesClient; - - const receiverName = `WebhookReceiver-${Date.now()}`; - const receiverType = 'webhook'; - const configName = `${receiverType}_configs`; - const label = 'severity = warning'; - const webhookURL = 'http://mywebhookurl'; - const updatedWebhookURL = 'http://myupdatedwebhookurl'; - - test.beforeEach(async ({ page, k8sClient: client }) => { - alertmanager = new AlertmanagerPage(page); - k8sClient = client; - }); - - test.afterEach(async () => { - await resetAlertmanagerConfig(k8sClient); - }); - - test('creates and edits Webhook Receiver correctly', async ({ page }) => { - await test.step('Create Webhook Receiver', async () => { - await alertmanager.navigateToAlertmanager(); - await alertmanager.createReceiver(receiverName, configName); - - await alertmanager.showAdvancedConfiguration(); - await expect(page.getByTestId('send-resolved-alerts')).toBeChecked(); - - await page.getByTestId('webhook-url').fill(webhookURL); - await page.getByTestId('label-0').fill(label); - - await alertmanager.save(); - }); - - await test.step('Verify Webhook Receiver was created correctly', async () => { - await alertmanager.validateReceiverInList(receiverName); - - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.receiverConfig.url).toBe(webhookURL); - expect(configs.receiverConfig).not.toHaveProperty('send_resolved'); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - - await test.step('Edit Webhook Receiver and save advanced fields', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - - await expect(page.getByTestId('webhook-url')).toHaveValue(webhookURL); - - await page.getByTestId('webhook-url').clear(); - await page.getByTestId('webhook-url').fill(updatedWebhookURL); - - await alertmanager.showAdvancedConfiguration(); - await page.getByTestId('send-resolved-alerts').uncheck(); - - await alertmanager.save(); - }); - - await test.step('Verify advanced fields were saved correctly', async () => { - await alertmanager.navigateToEditReceiver(receiverName); - await alertmanager.showAdvancedConfiguration(); - - await expect(page.getByTestId('send-resolved-alerts')).not.toBeChecked(); - }); - - await test.step('Verify YAML has correct config', async () => { - await expect(async () => { - await alertmanager.navigateToYAMLPage(); - const yamlContent = await alertmanager.getYAMLContent(); - const configs = getGlobalsAndReceiverConfig(receiverName, configName, yamlContent); - - expect(configs.receiverConfig.url).toBe(updatedWebhookURL); - expect(configs.receiverConfig.send_resolved).toBe(false); - }).toPass({ intervals: [2_000, 3_000, 5_000], timeout: 30_000 }); - }); - }); -});