Alerting: Add E2E test configuration and fix saved searches tests (#116203)
Some checks are pending
Actionlint / Lint GitHub Actions files (push) Waiting to run
Backend Code Checks / Detect whether code changed (push) Waiting to run
Backend Code Checks / Validate Backend Configs (push) Blocked by required conditions
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Backend Unit Tests / Grafana (1/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (2/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (3/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (4/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (5/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (6/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (7/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (8/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (1/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (2/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (3/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (4/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (5/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (6/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (7/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (8/8) (push) Blocked by required conditions
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
CodeQL checks / Detect whether code changed (push) Waiting to run
CodeQL checks / Analyze (push) Blocked by required conditions
Lint Frontend / Detect whether code changed (push) Waiting to run
Lint Frontend / Lint (push) Blocked by required conditions
Lint Frontend / Typecheck (push) Blocked by required conditions
Lint Frontend / Verify API clients (push) Waiting to run
Lint Frontend / Verify API clients (enterprise) (push) Waiting to run
Lint Frontend / Verify packed frontend packages (push) Blocked by required conditions
golangci-lint / Detect whether code changed (push) Waiting to run
golangci-lint / go-fmt (push) Blocked by required conditions
golangci-lint / lint-go (push) Blocked by required conditions
Crowdin Upload Action / upload-sources-to-crowdin (push) Waiting to run
Verify i18n / verify-i18n (push) Waiting to run
Documentation / Build & Verify Docs (push) Waiting to run
End-to-end tests / Detect whether code changed (push) Waiting to run
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
End-to-end tests / push-docker-image (push) Blocked by required conditions
End-to-end tests / dashboards-suite (old arch) (push) Blocked by required conditions
End-to-end tests / panels-suite (old arch) (push) Blocked by required conditions
End-to-end tests / smoke-tests-suite (old arch) (push) Blocked by required conditions
End-to-end tests / various-suite (old arch) (push) Blocked by required conditions
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (1/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (2/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (3/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (4/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (5/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (6/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (7/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (8/8) (push) Blocked by required conditions
End-to-end tests / run-azure-monitor-e2e (push) Blocked by required conditions
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
End-to-end tests / A11y test (push) Blocked by required conditions
End-to-end tests / Publish metrics (push) Blocked by required conditions
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Frontend tests / Detect whether code changed (push) Waiting to run
Frontend tests / Unit tests (1 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (10 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (11 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (12 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (13 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (14 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (15 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (16 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (2 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (3 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (4 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (5 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (6 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (7 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (8 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (9 / 16) (push) Blocked by required conditions
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Frontend tests / Packages unit tests (push) Blocked by required conditions
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Frontend tests / Devenv frontend-service build (push) Blocked by required conditions
Integration Tests / Detect whether code changed (push) Waiting to run
Integration Tests / Sqlite (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (profiled) (push) Blocked by required conditions
Integration Tests / MySQL (1/16) (push) Blocked by required conditions
Integration Tests / MySQL (10/16) (push) Blocked by required conditions
Integration Tests / MySQL (11/16) (push) Blocked by required conditions
Integration Tests / MySQL (12/16) (push) Blocked by required conditions
Integration Tests / MySQL (13/16) (push) Blocked by required conditions
Integration Tests / MySQL (14/16) (push) Blocked by required conditions
Integration Tests / MySQL (15/16) (push) Blocked by required conditions
Integration Tests / MySQL (16/16) (push) Blocked by required conditions
Integration Tests / MySQL (2/16) (push) Blocked by required conditions
Integration Tests / MySQL (3/16) (push) Blocked by required conditions
Integration Tests / MySQL (4/16) (push) Blocked by required conditions
Integration Tests / MySQL (5/16) (push) Blocked by required conditions
Integration Tests / MySQL (6/16) (push) Blocked by required conditions
Integration Tests / MySQL (7/16) (push) Blocked by required conditions
Integration Tests / MySQL (8/16) (push) Blocked by required conditions
Integration Tests / MySQL (9/16) (push) Blocked by required conditions
Integration Tests / Postgres (1/16) (push) Blocked by required conditions
Integration Tests / Postgres (10/16) (push) Blocked by required conditions
Integration Tests / Postgres (11/16) (push) Blocked by required conditions
Integration Tests / Postgres (12/16) (push) Blocked by required conditions
Integration Tests / Postgres (13/16) (push) Blocked by required conditions
Integration Tests / Postgres (14/16) (push) Blocked by required conditions
Integration Tests / Postgres (15/16) (push) Blocked by required conditions
Integration Tests / Postgres (16/16) (push) Blocked by required conditions
Integration Tests / Postgres (2/16) (push) Blocked by required conditions
Integration Tests / Postgres (3/16) (push) Blocked by required conditions
Integration Tests / Postgres (4/16) (push) Blocked by required conditions
Integration Tests / Postgres (5/16) (push) Blocked by required conditions
Integration Tests / Postgres (6/16) (push) Blocked by required conditions
Integration Tests / Postgres (7/16) (push) Blocked by required conditions
Integration Tests / Postgres (8/16) (push) Blocked by required conditions
Integration Tests / Postgres (9/16) (push) Blocked by required conditions
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
publish-kinds-next / main (push) Waiting to run
publish-technical-documentation-next / sync (push) Waiting to run
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Build Release Packages / setup (push) Waiting to run
Build Release Packages / Dispatch grafana-enterprise build (push) Blocked by required conditions
Build Release Packages / / darwin-amd64 (push) Blocked by required conditions
Build Release Packages / / darwin-arm64 (push) Blocked by required conditions
Build Release Packages / / linux-amd64 (push) Blocked by required conditions
Build Release Packages / / linux-armv6 (push) Blocked by required conditions
Build Release Packages / / linux-armv7 (push) Blocked by required conditions
Build Release Packages / / linux-arm64 (push) Blocked by required conditions
Build Release Packages / / linux-s390x (push) Blocked by required conditions
Build Release Packages / / windows-amd64 (push) Blocked by required conditions
Build Release Packages / / windows-arm64 (push) Blocked by required conditions
Build Release Packages / Upload artifacts (push) Blocked by required conditions
Build Release Packages / publish-dockerhub (push) Blocked by required conditions
Build Release Packages / Dispatch publish NPM canaries (push) Blocked by required conditions
Build Release Packages / notify-pr (push) Blocked by required conditions
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Shellcheck / Shellcheck scripts (push) Waiting to run
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Run Storybook a11y tests / Run Storybook a11y tests (light theme) (push) Blocked by required conditions
Run Storybook a11y tests / Run Storybook a11y tests (dark theme) (push) Blocked by required conditions
Swagger generated code / Detect whether code changed (push) Waiting to run
Swagger generated code / Verify committed API specs match (push) Blocked by required conditions
Dispatch sync to mirror / dispatch-job (push) Waiting to run
trigger-dashboard-search-e2e / trigger-search-e2e (push) Waiting to run
Trivy Scan / trivy-scan (push) Waiting to run

* Alerting: Fix and stabilize saved searches E2E tests

Stabilizes the saved searches E2E tests by ensuring correct feature
toggles are enabled and improving the data cleanup logic.

Previously, `clearSavedSearches` relied on clearing localStorage, which
was insufficient as saved searches are persisted server-side via the
UserStorage API. The cleanup now correctly invokes the UserStorage API.

Changes:
- Add `alerting` project to Playwright configuration with authentication
- Enable `alertingListViewV2`, `alertingFilterV2`, and `alertingSavedSearches` toggles in tests
- Update `clearSavedSearches` helper to use UserStorage API for reliable cleanup
- Improve test selectors to use more robust `getByRole` and `getByText` queries
- Update `SavedSearchItem` to merge `aria-label` into `tooltip` for consistency

* Fix saved searchers unit tests
This commit is contained in:
Konrad Lalik 2026-01-14 16:41:53 +01:00 committed by GitHub
parent 19deffee40
commit 0faab257b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 60 additions and 25 deletions

View file

@ -2,6 +2,15 @@ import { Page } from '@playwright/test';
import { test, expect } from '@grafana/plugin-e2e';
// Enable required feature toggles for Saved Searches (part of RuleList.v2)
test.use({
featureToggles: {
alertingListViewV2: true,
alertingFilterV2: true,
alertingSavedSearches: true,
},
});
/**
* UI selectors for Saved Searches e2e tests.
* Each selector is a function that takes the page and returns a locator.
@ -26,26 +35,50 @@ const ui = {
// Indicators
emptyState: (page: Page) => page.getByText(/no saved searches/i),
defaultIcon: (page: Page) => page.locator('[title="Default search"]'),
defaultIcon: (page: Page) => page.getByRole('img', { name: /default search/i }),
duplicateError: (page: Page) => page.getByText(/already exists/i),
};
/**
* Helper to clear saved searches storage.
* UserStorage uses localStorage as fallback, so we clear both potential keys.
* Helper to clear saved searches from UserStorage.
* UserStorage persists data server-side via k8s API, so we need to delete via API.
*/
async function clearSavedSearches(page: Page) {
await page.evaluate(() => {
// Clear localStorage keys that might contain saved searches
// UserStorage stores under 'grafana.userstorage.alerting' pattern
const keysToRemove = Object.keys(localStorage).filter(
(key) => key.includes('alerting') && (key.includes('savedSearches') || key.includes('userstorage'))
);
keysToRemove.forEach((key) => localStorage.removeItem(key));
// Get namespace and user info from Grafana config
const storageInfo = await page.evaluate(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const bootData = (window as any).grafanaBootData;
const user = bootData?.user;
const userUID = user?.uid === '' || !user?.uid ? String(user?.id ?? 'anonymous') : user.uid;
const resourceName = `alerting:${userUID}`;
const namespace = bootData?.settings?.namespace || 'default';
// Also clear session storage visited flag
const sessionKeysToRemove = Object.keys(sessionStorage).filter((key) => key.includes('alerting'));
sessionKeysToRemove.forEach((key) => sessionStorage.removeItem(key));
return { namespace, resourceName };
});
// Delete the UserStorage resource
try {
await page.request.delete(
`/apis/userstorage.grafana.app/v0alpha1/namespaces/${storageInfo.namespace}/user-storage/${storageInfo.resourceName}`
);
} catch (error) {
// Ignore 404 errors (resource doesn't exist)
if (!(error && typeof error === 'object' && 'status' in error && error.status === 404)) {
console.warn('Failed to clear saved searches:', error);
}
}
// Also clear localStorage as fallback storage
await page.evaluate(({ resourceName }) => {
// The UserStorage key pattern is always `{resourceName}:{key}`
// For saved searches, the key is 'savedSearches'
const key = `${resourceName}:savedSearches`;
window.localStorage.removeItem(key);
}, storageInfo);
// Clear session storage visited flag
await page.evaluate(() => {
window.sessionStorage.removeItem('grafana.alerting.ruleList.visited');
});
}
@ -150,7 +183,7 @@ test.describe(
await ui.saveButton(page).click();
await ui.saveNameInput(page).fill('Apply Test');
await ui.saveNameInput(page).fill('Firing Rules');
await ui.saveConfirmButton(page).click();
// Clear the search
@ -159,7 +192,7 @@ test.describe(
// Apply the saved search
await ui.savedSearchesButton(page).click();
await page.getByRole('button', { name: /apply search.*apply test/i }).click();
await page.getByRole('button', { name: /apply.*search.*firing rules/i }).click();
// Verify the search input is updated
await expect(ui.searchInput(page)).toHaveValue('state:firing');
@ -182,7 +215,7 @@ test.describe(
await ui.renameMenuItem(page).click();
// Enter new name
const renameInput = page.getByDisplayValue('Original Name');
const renameInput = page.getByRole('textbox', { name: /enter a name/i });
await renameInput.clear();
await renameInput.fill('Renamed Search');
await page.keyboard.press('Enter');
@ -260,12 +293,12 @@ test.describe(
await expect(ui.saveNameInput(page)).toBeVisible();
// Press Escape to cancel
// Press Escape to cancel - this closes the entire dropdown
await page.keyboard.press('Escape');
// Verify we're back to list mode
await expect(ui.saveNameInput(page)).not.toBeVisible();
await expect(ui.saveButton(page)).toBeVisible();
// Verify the entire dialog is closed
await expect(ui.dropdown(page)).not.toBeVisible();
await expect(ui.saveButton(page)).not.toBeVisible();
});
}
);

View file

@ -179,6 +179,10 @@ export default defineConfig<PluginOptions>({
name: 'cloud-plugins',
testDir: path.join(testDirRoot, '/cloud-plugins-suite'),
}),
withAuth({
name: 'alerting',
testDir: path.join(testDirRoot, '/alerting-suite'),
}),
withAuth({
name: 'dashboard-new-layouts',
testDir: path.join(testDirRoot, '/dashboard-new-layouts'),

View file

@ -121,11 +121,10 @@ export function SavedSearchItem({
{/* Apply button (magnifying glass) */}
<IconButton
name="search"
aria-label={t('alerting.saved-searches.apply-aria-label', 'Apply search "{{name}}"', {
tooltip={t('alerting.saved-searches.apply-tooltip', 'Apply search "{{name}}"', {
name: search.name,
})}
onClick={onApply}
tooltip={t('alerting.saved-searches.apply-tooltip', 'Apply this search')}
size="md"
variant="secondary"
disabled={isDisabled}

View file

@ -19,7 +19,7 @@ const ui = {
saveInput: byPlaceholderText(/enter a name/i),
// Action buttons
cancelButton: byRole('button', { name: /cancel/i }),
applyButtons: byRole('button', { name: /apply this search/i }),
applyButtons: byRole('button', { name: /apply search/i }),
actionMenuButtons: byRole('button', { name: /actions/i }),
deleteButton: byRole('button', { name: /delete/i }),
// Menu items (using byRole for proper accessibility testing)

View file

@ -2681,8 +2681,7 @@
},
"saved-searches": {
"actions-aria-label": "Actions",
"apply-aria-label": "Apply search \"{{name}}\"",
"apply-tooltip": "Apply this search",
"apply-tooltip": "Apply search \"{{name}}\"",
"button-label": "Saved searches",
"cancel": "Cancel",
"default-indicator": "Default search",