# What this PR does * Make the filter input say `Filter results...` instead of `Search or filter results...` on the alert group page; disallow custom input there so it's only possible to choose among existing filters * Remove outdated `<page>/filters?search=` functionality from internal API ## Which issue(s) this PR closes Related to https://github.com/grafana/oncall-private/issues/2679 <!-- *Note*: If you want the issue to be auto-closed once the PR is merged, change "Related to" to "Closes" in the line above. If you have more than one GitHub issue that this PR closes, be sure to preface each issue link with a [closing keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue). This ensures that the issue(s) are auto-closed once the PR has been merged. --> ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
import { Locator, Page, expect } from '@playwright/test';
|
|
|
|
import { selectDropdownValue, selectValuePickerValue } from './forms';
|
|
import { goToOnCallPage } from './navigation';
|
|
|
|
const MAX_RETRIES = 5;
|
|
const ALERT_GROUP_REGISTERED_TEXT = 'alert group registered';
|
|
|
|
const getIncidentTimelineList = async (page: Page): Promise<Locator> => {
|
|
const incidentTimelineList = page.getByTestId('incident-timeline-list');
|
|
await incidentTimelineList.waitFor({ state: 'visible' });
|
|
return incidentTimelineList;
|
|
};
|
|
|
|
/**
|
|
* recursively refreshes the page waiting for the background celery workers to have done their job of
|
|
* escalating the alert group
|
|
*/
|
|
const incidentTimelineContainsStep = async (page: Page, triggeredStepText: string, retryNum = 0): Promise<boolean> => {
|
|
if (retryNum > MAX_RETRIES) {
|
|
return Promise.resolve(false);
|
|
}
|
|
|
|
const incidentTimelineList = await getIncidentTimelineList(page);
|
|
|
|
if (!incidentTimelineList.getByText(triggeredStepText)) {
|
|
await page.reload({ waitUntil: 'networkidle' });
|
|
return incidentTimelineContainsStep(page, triggeredStepText, (retryNum += 1));
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* recursively refreshes the page waiting for the background celery workers to have done their job of
|
|
* creating the alert group
|
|
*/
|
|
export const filterAlertGroupsTableByIntegrationAndGoToDetailPage = async (
|
|
page: Page,
|
|
integrationName: string,
|
|
retryNum = 0
|
|
): Promise<void> => {
|
|
if (retryNum > MAX_RETRIES) {
|
|
throw new Error('we were not able to properly filter the alert groups table by integration');
|
|
}
|
|
|
|
await goToOnCallPage(page, 'alert-groups');
|
|
|
|
// filter by integration
|
|
const selectElement = await selectDropdownValue({
|
|
page,
|
|
selectType: 'grafanaSelect',
|
|
placeholderText: 'Filter results...',
|
|
value: 'Integration',
|
|
});
|
|
await selectElement.type(integrationName);
|
|
await selectValuePickerValue(page, integrationName, false);
|
|
|
|
try {
|
|
/**
|
|
* wait for up to 2 seconds for the alert groups to be filtered, if the first row does not correspond
|
|
* to `integrationName` assume that the background workers have not created it yet and lets
|
|
* recursively retry this function
|
|
*/
|
|
|
|
await page.waitForTimeout(2000);
|
|
|
|
expect(await page.locator('table > tbody > tr [data-testid=integration-name]').textContent()).toBe(integrationName);
|
|
await page.locator('table > tbody > tr [data-testid=integration-url]').click();
|
|
} catch (err) {
|
|
return filterAlertGroupsTableByIntegrationAndGoToDetailPage(page, integrationName, (retryNum += 1));
|
|
}
|
|
};
|
|
|
|
export const verifyThatAlertGroupIsRoutedCorrectlyButNotEscalated = async (
|
|
page: Page,
|
|
integrationName: string,
|
|
routedText: string
|
|
): Promise<void> => {
|
|
await filterAlertGroupsTableByIntegrationAndGoToDetailPage(page, integrationName);
|
|
|
|
/**
|
|
* incidentTimelineContainsStep recursively reloads the alert group page until the engine
|
|
* background workers have processed/escalated the alert group
|
|
*/
|
|
expect(await incidentTimelineContainsStep(page, ALERT_GROUP_REGISTERED_TEXT)).toBe(true);
|
|
|
|
const incidentTimelineList = await getIncidentTimelineList(page);
|
|
expect(incidentTimelineList).toContainText(routedText);
|
|
expect(incidentTimelineList).not.toContainText('triggered step');
|
|
};
|
|
|
|
export const verifyThatAlertGroupIsTriggered = async (
|
|
page: Page,
|
|
integrationName: string,
|
|
triggeredStepText: string
|
|
): Promise<void> => {
|
|
await filterAlertGroupsTableByIntegrationAndGoToDetailPage(page, integrationName);
|
|
|
|
expect(await incidentTimelineContainsStep(page, triggeredStepText)).toBe(true);
|
|
};
|
|
|
|
export const resolveFiringAlert = async (page: Page) => {
|
|
await goToOnCallPage(page, 'alert-groups');
|
|
await page.getByText('Firing').nth(2).click({force: true});
|
|
await page.getByLabel('Context menu').getByText('Resolve').click();
|
|
};
|