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
27 changes: 27 additions & 0 deletions web/e2e/alert-secondary-tabs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expect, test } from '@playwright/test'
import { setupDemoMode } from './helpers/setup'

const ALERTS_PAGE_TIMEOUT_MS = 30_000

test('Alert drilldown switches between Labels, Source, and AI panels', async ({ page }) => {
await setupDemoMode(page)
// Vite serves source modules below /src/lib/api; let them bypass the API mock.
await page.route('**/src/lib/api/**', route => route.continue())
await page.goto('/alerts', { waitUntil: 'domcontentloaded' })

const alertCard = page.locator('[data-card-type="active_alerts"]')
await expect(alertCard).toBeVisible({ timeout: ALERTS_PAGE_TIMEOUT_MS })
await alertCard.locator('[data-keynav-item="alert"] [role="button"]').first().click()

const modal = page.getByTestId('drilldown-modal')
await expect(modal).toBeVisible()
await modal.locator('[role="tab"][data-tab-id="labels"]').click()
await expect(modal.locator('[role="tabpanel"]').last()).toContainText('All Labels')

await modal.locator('[role="tab"][data-tab-id="source"]').click()
await expect(modal.locator('[role="tabpanel"]').last()).toContainText('Alert Rule Source')

await modal.locator('[role="tab"][data-tab-id="ai"]').click()
await expect(modal.locator('[role="tabpanel"]').last()).toContainText('AI agent not connected')
await expect(modal.locator('button').filter({ hasText: 'Diagnose Alert' })).toBeDisabled()
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { AlertAITab, AlertLabelsTab, AlertSourceTab } from '../AlertSecondaryTabs.parts'

const panelProps = { role: 'tabpanel', id: 'alert-panel', 'aria-labelledby': 'alert-tab' }

describe('Alert secondary tab panels', () => {
it('renders every label and passes the exact label pair to copy', () => {
const onCopy = vi.fn()
const { rerender } = render(
<AlertLabelsTab tabPanelProps={panelProps} labelEntries={[
['namespace', 'monitoring'], ['severity', 'critical'],
]} copiedField={null} onCopy={onCopy} />,
)

expect(screen.getByRole('tabpanel')).toHaveAttribute('aria-labelledby', 'alert-tab')
expect(screen.getByText('monitoring')).toBeVisible()
expect(screen.getByText('critical')).toBeVisible()
fireEvent.click(screen.getAllByRole('button', { name: 'drilldown.alertDetail.copyLabel' })[1])
expect(onCopy).toHaveBeenCalledWith('severity', 'severity=critical')

rerender(<AlertLabelsTab tabPanelProps={panelProps} labelEntries={[
['namespace', 'monitoring'], ['severity', 'critical'],
]} copiedField="severity" onCopy={onCopy} />)
expect(screen.getAllByRole('button', { name: 'drilldown.alertDetail.copyLabel' })[1]
.querySelector('svg.lucide-check')).toBeTruthy()
})

it('shows source loading, rule content, and unavailable states', () => {
const { rerender, container } = render(
<AlertSourceTab tabPanelProps={panelProps} sourceLoading sourceRule={null} />,
)
expect(container.querySelector('.animate-spin')).toBeTruthy()

rerender(<AlertSourceTab tabPanelProps={panelProps} sourceLoading={false} sourceRule="alert: HighCPU" />)
expect(screen.getByText('alert: HighCPU')).toBeVisible()

rerender(<AlertSourceTab tabPanelProps={panelProps} sourceLoading={false} sourceRule={null} />)
expect(screen.getByText('drilldown.alertDetail.ruleNotAvailable')).toBeVisible()
})

it('disables diagnosis without an agent and shows connected states', () => {
const onDiagnose = vi.fn()
const { rerender, container } = render(
<AlertAITab tabPanelProps={panelProps} isAgentConnected={false}
aiAnalysisLoading={false} aiAnalysis={null} onDiagnose={onDiagnose} />,
)
const diagnose = screen.getByRole('button', { name: 'drilldown.alertDetail.diagnoseAlert' })
expect(diagnose).toBeDisabled()
expect(screen.getByText('drilldown.ai.notConnected')).toBeVisible()

rerender(<AlertAITab tabPanelProps={panelProps} isAgentConnected
aiAnalysisLoading aiAnalysis={null} onDiagnose={onDiagnose} />)
expect(container.querySelector('.animate-spin')).toBeTruthy()

rerender(<AlertAITab tabPanelProps={panelProps} isAgentConnected
aiAnalysisLoading={false} aiAnalysis="CPU is saturated" onDiagnose={onDiagnose} />)
expect(screen.getByText('CPU is saturated')).toBeVisible()
fireEvent.click(diagnose)
expect(onDiagnose).toHaveBeenCalledOnce()

rerender(<AlertAITab tabPanelProps={panelProps} isAgentConnected
aiAnalysisLoading={false} aiAnalysis={null} onDiagnose={onDiagnose} />)
expect(screen.getByText('drilldown.alertDetail.clickDiagnose')).toBeVisible()
})
})
Loading