[8.x] [kbn/response-ops-alerts-table] set data-test-subj for EuiDataGrid based on loading status (#217230) (#218243)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[kbn/response-ops-alerts-table] set data-test-subj for EuiDataGrid
based on loading status
(#217230)](https://github.com/elastic/kibana/pull/217230)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Dzmitry
Lemechko","email":"dzmitry.lemechko@elastic.co"},"sourceCommit":{"committedDate":"2025-04-15T11:05:49Z","message":"[kbn/response-ops-alerts-table]
set data-test-subj for EuiDataGrid based on loading status
(#217230)\n\n## Summary\n\nFollow-up to #217153\n\n### Problem
Description\nIn UI tests, there was no reliable way to determine when
the Alerts\ntable content had fully loaded before interacting with it.
This could\nlead to flaky tests where interactions occurred before the
data was\navailable (rows are not present yet), causing failures or
inconsistent\nresults (checking for row with specific content to
exist)\n\n\n![image](https://github.com/user-attachments/assets/6580f134-0bf2-48b8-8cc9-b6d476f4e932)\n\nQuite
often we see tests waiting for global indicator (spinner in the\ntop
left corner) to be hidden as a condition for page loading is\ncomplete.
This is quite unreliable approach and testing tools have no\nconsistent
built-in solution: FTR, Cypress or even Playwright - network\nidle wait
is officially marked
as\n[discouraged](https://playwright.dev/docs/api/class-page)).\nWe need
to help testing tool to interact with UI components in ready\nstate
only.\n\n\n### Solution\nTo address this issue, I modified a
`data-test-subj` property in the\n`<EuiDataGrid>` component. The
property dynamically switches between\n`alertsTableIsLoading` when data
is still loading and\n`alertsTableIsLoaded `once the content is
available. This allows UI\ntests to wait for precisely
`alertsTableIsLoaded` to be in in the DOM\nbefore interacting with the
table, ensuring more reliable and stable\ntest execution.\n\n\nPassed
10/10\n<img width=\"538\"
alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/e44bae5f-4094-4ed2-89f3-74a52cb2be53\"\n/>","sha":"edf8d6d975f7ae2bd4df8a469bc391ca602ddc1e","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:version","v9.1.0","v8.19.0","v9.0.1"],"title":"[kbn/response-ops-alerts-table]
set data-test-subj for EuiDataGrid based on loading
status","number":217230,"url":"https://github.com/elastic/kibana/pull/217230","mergeCommit":{"message":"[kbn/response-ops-alerts-table]
set data-test-subj for EuiDataGrid based on loading status
(#217230)\n\n## Summary\n\nFollow-up to #217153\n\n### Problem
Description\nIn UI tests, there was no reliable way to determine when
the Alerts\ntable content had fully loaded before interacting with it.
This could\nlead to flaky tests where interactions occurred before the
data was\navailable (rows are not present yet), causing failures or
inconsistent\nresults (checking for row with specific content to
exist)\n\n\n![image](https://github.com/user-attachments/assets/6580f134-0bf2-48b8-8cc9-b6d476f4e932)\n\nQuite
often we see tests waiting for global indicator (spinner in the\ntop
left corner) to be hidden as a condition for page loading is\ncomplete.
This is quite unreliable approach and testing tools have no\nconsistent
built-in solution: FTR, Cypress or even Playwright - network\nidle wait
is officially marked
as\n[discouraged](https://playwright.dev/docs/api/class-page)).\nWe need
to help testing tool to interact with UI components in ready\nstate
only.\n\n\n### Solution\nTo address this issue, I modified a
`data-test-subj` property in the\n`<EuiDataGrid>` component. The
property dynamically switches between\n`alertsTableIsLoading` when data
is still loading and\n`alertsTableIsLoaded `once the content is
available. This allows UI\ntests to wait for precisely
`alertsTableIsLoaded` to be in in the DOM\nbefore interacting with the
table, ensuring more reliable and stable\ntest execution.\n\n\nPassed
10/10\n<img width=\"538\"
alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/e44bae5f-4094-4ed2-89f3-74a52cb2be53\"\n/>","sha":"edf8d6d975f7ae2bd4df8a469bc391ca602ddc1e"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","9.0"],"targetPullRequestStates":[{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/217230","number":217230,"mergeCommit":{"message":"[kbn/response-ops-alerts-table]
set data-test-subj for EuiDataGrid based on loading status
(#217230)\n\n## Summary\n\nFollow-up to #217153\n\n### Problem
Description\nIn UI tests, there was no reliable way to determine when
the Alerts\ntable content had fully loaded before interacting with it.
This could\nlead to flaky tests where interactions occurred before the
data was\navailable (rows are not present yet), causing failures or
inconsistent\nresults (checking for row with specific content to
exist)\n\n\n![image](https://github.com/user-attachments/assets/6580f134-0bf2-48b8-8cc9-b6d476f4e932)\n\nQuite
often we see tests waiting for global indicator (spinner in the\ntop
left corner) to be hidden as a condition for page loading is\ncomplete.
This is quite unreliable approach and testing tools have no\nconsistent
built-in solution: FTR, Cypress or even Playwright - network\nidle wait
is officially marked
as\n[discouraged](https://playwright.dev/docs/api/class-page)).\nWe need
to help testing tool to interact with UI components in ready\nstate
only.\n\n\n### Solution\nTo address this issue, I modified a
`data-test-subj` property in the\n`<EuiDataGrid>` component. The
property dynamically switches between\n`alertsTableIsLoading` when data
is still loading and\n`alertsTableIsLoaded `once the content is
available. This allows UI\ntests to wait for precisely
`alertsTableIsLoaded` to be in in the DOM\nbefore interacting with the
table, ensuring more reliable and stable\ntest execution.\n\n\nPassed
10/10\n<img width=\"538\"
alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/e44bae5f-4094-4ed2-89f3-74a52cb2be53\"\n/>","sha":"edf8d6d975f7ae2bd4df8a469bc391ca602ddc1e"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Dzmitry Lemechko 2025-04-15 17:18:21 +02:00 committed by GitHub
parent 93b2fd62d9
commit 2e306a27f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 23 additions and 20 deletions

View file

@ -358,7 +358,7 @@ export const AlertsDataGrid = typedMemo(
ref={dataGridRef}
css={rowStyles}
aria-label="Alerts table"
data-test-subj="alertsTable"
data-test-subj={isLoading ? `alertsTableIsLoading` : `alertsTableIsLoaded`}
height={height}
columns={columnsWithCellActions}
columnVisibility={columnVisibility}

View file

@ -10,16 +10,14 @@ import { ScoutPage, Locator, expect } from '@kbn/scout';
const PAGE_URL = 'security/alerts';
export class AlertsTablePage {
public detectionsAlertsContainer: Locator;
public detectionsAlertsWrapper: Locator;
public alertRow: Locator;
public alertsTableBody: Locator;
public alertsTable: Locator;
constructor(private readonly page: ScoutPage) {
this.detectionsAlertsContainer = this.page.testSubj.locator('detectionsAlertsPage');
this.detectionsAlertsWrapper = this.page.testSubj.locator('detectionsAlertsPage');
this.alertRow = this.page.locator('div.euiDataGridRow');
this.alertsTableBody = this.page.testSubj
.locator('alertsTable')
.locator(`[data-test-subj='euiDataGridBody']`);
this.alertsTable = this.page.testSubj.locator('alertsTableIsLoaded'); // Search for loaded Alerts table
}
async navigate() {
@ -27,7 +25,7 @@ export class AlertsTablePage {
}
async expandAlertDetailsFlyout(ruleName: string) {
await this.alertsTableBody.waitFor({ state: 'visible' });
await this.alertsTable.waitFor({ state: 'visible' });
// Filter alert by unique rule name
const row = this.alertRow.filter({ hasText: ruleName });
await expect(
@ -37,4 +35,9 @@ export class AlertsTablePage {
return row.locator(`[data-test-subj='expand-event']`).click();
}
async waitForDetectionsAlertsWrapper() {
// Increased timeout to 20 seconds because this page sometimes takes longer to load
return this.detectionsAlertsWrapper.waitFor({ state: 'visible', timeout: 20_000 });
}
}

View file

@ -139,7 +139,7 @@ describe('Isolate command', { tags: ['@ess', '@serverless', '@brokenInServerless
loadPage(APP_ALERTS_PATH);
closeAllToasts();
cy.getByTestSubj('alertsTable').within(() => {
cy.getByTestSubj('alertsTableIsLoaded').within(() => {
cy.getByTestSubj('expand-event')
.first()
.within(() => {

View file

@ -30,7 +30,7 @@ export const getAlertsTableRows = (timeout?: number): Cypress.Chainable<JQuery<H
clickAlertListRefreshButton();
return cy
.getByTestSubj('alertsTable')
.getByTestSubj('alertsTableIsLoaded')
.find<HTMLDivElement>('.euiDataGridRow')
.then(($rowsFound) => {
$rows = $rowsFound;

View file

@ -28,7 +28,7 @@ spaceTest.describe('Expandable flyout state sync', { tag: ['@ess', '@svlSecurity
const urlBeforeAlertDetails = page.url();
expect(urlBeforeAlertDetails).not.toContain(RIGHT);
await pageObjects.alertsTablePage.detectionsAlertsContainer.waitFor({ state: 'visible' });
await pageObjects.alertsTablePage.waitForDetectionsAlertsWrapper();
await pageObjects.alertsTablePage.expandAlertDetailsFlyout(ruleName);
const urlAfterAlertDetails = page.url();
@ -38,7 +38,7 @@ spaceTest.describe('Expandable flyout state sync', { tag: ['@ess', '@svlSecurity
await expect(headerTitle).toHaveText(ruleName);
await page.reload();
await pageObjects.alertsTablePage.detectionsAlertsContainer.waitFor({ state: 'visible' });
await pageObjects.alertsTablePage.waitForDetectionsAlertsWrapper();
const urlAfterReload = page.url();
expect(urlAfterReload).toContain(RIGHT);

View file

@ -64,7 +64,7 @@ export const testSubjectIds = {
GRAPH_ACTIONS_TOGGLE_SEARCH_ID: 'cloudSecurityGraphGraphInvestigationToggleSearch',
GRAPH_ACTIONS_INVESTIGATE_IN_TIMELINE_ID:
'cloudSecurityGraphGraphInvestigationInvestigateInTimeline',
ALERT_TABLE_ROW_CSS_SELECTOR: '[data-test-subj="alertsTable"] .euiDataGridRow',
ALERT_TABLE_ROW_CSS_SELECTOR: '[data-test-subj="alertsTableIsLoaded"] .euiDataGridRow',
SETUP_TECHNOLOGY_SELECTOR: 'setup-technology-selector',
DIRECT_ACCESS_KEYS: 'direct_access_keys',
SETUP_TECHNOLOGY_SELECTOR_AGENTLESS_RADIO: 'setup-technology-agentless-radio',

View file

@ -21,7 +21,7 @@ const DATE_WITH_DATA = {
const ALERTS_FLYOUT_SELECTOR = 'alertsFlyout';
const FILTER_FOR_VALUE_BUTTON_SELECTOR = 'filterForValue';
const ALERTS_TABLE_CONTAINER_SELECTOR = 'alertsTable';
const ALERTS_TABLE_CONTAINER_SELECTOR = 'alertsTableIsLoaded';
const ALERTS_TABLE_ERROR_PROMPT_SELECTOR = 'alertsTableErrorPrompt';
const ALERTS_TABLE_ACTIONS_MENU_SELECTOR = 'alertsTableActionsMenu';
const VIEW_RULE_DETAILS_SELECTOR = 'viewRuleDetails';

View file

@ -22,7 +22,7 @@ const ALERTS_TITLE = 'Alerts';
const ALERTS_ACCORDION_SELECTOR = `accordion-${ALERTS_TITLE}`;
const ALERTS_SECTION_BUTTON_CSS_SELECTOR = `[data-test-subj=${ALERTS_ACCORDION_SELECTOR}] button.euiAccordion__button`;
const ALERTS_TABLE_NO_DATA_SELECTOR = 'alertsTableEmptyState';
const ALERTS_TABLE_WITH_DATA_SELECTOR = 'alertsTable';
const ALERTS_TABLE_WITH_DATA_SELECTOR = 'alertsTableIsLoaded';
const ALERTS_TABLE_LOADING_SELECTOR = 'internalAlertsPageLoading';
export function ObservabilityOverviewCommonProvider({

View file

@ -41,7 +41,7 @@ describe('Alert Table API calls', { tags: ['@ess', '@serverless'] }, () => {
});
it('should call `api/lists/index` only once', () => {
cy.get('[data-test-subj="alertsTable"]').then(() => {
cy.get('[data-test-subj="alertsTableIsLoaded"]').then(() => {
expect(callCount, 'number of times lists index api is called').to.equal(1);
});
});

View file

@ -38,7 +38,7 @@ export const GET_DATA_GRID_HEADER_CELL_ACTION_GROUP = (fieldName: string) => {
};
export const DATA_GRID_FULL_SCREEN =
'[data-test-subj="alertsTable"] [data-test-subj="dataGridFullScreenButton"]';
'[data-test-subj="alertsTableIsLoaded"] [data-test-subj="dataGridFullScreenButton"]';
export const DATA_GRID_FIELD_SORT_BTN = '[data-test-subj="dataGridColumnSortingButton"]';

View file

@ -87,7 +87,7 @@ export const NEW_TERMS_FIELDS_DETAILS = 'Fields';
export const NEW_TERMS_HISTORY_WINDOW_DETAILS = 'History Window Size';
export const FIELDS_BROWSER_BTN =
'[data-test-subj="alertsTable"] [data-test-subj="show-field-browser"]';
'[data-test-subj="alertsTableIsLoaded"] [data-test-subj="show-field-browser"]';
export const LAST_EXECUTION_STATUS_REFRESH_BUTTON =
'[data-test-subj="ruleLastExecutionStatusRefreshButton"]';

View file

@ -489,7 +489,7 @@ export const sumAlertCountFromAlertCountTable = (callback?: (sumOfEachRow: numbe
};
export const selectFirstPageAlerts = () => {
const ALERTS_DATA_GRID = '[data-test-subj="alertsTable"]';
const ALERTS_DATA_GRID = '[data-test-subj="alertsTableIsLoaded"]';
cy.get(ALERTS_DATA_GRID).find(SELECT_ALL_VISIBLE_ALERTS).scrollIntoView();
cy.get(ALERTS_DATA_GRID).find(SELECT_ALL_VISIBLE_ALERTS).click({ force: true });
};

View file

@ -8,7 +8,7 @@
import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
import { FtrService } from '../../../functional/ftr_provider_context';
const ALERT_TABLE_ROW_CSS_SELECTOR = '[data-test-subj="alertsTable"] .euiDataGridRow';
const ALERT_TABLE_ROW_CSS_SELECTOR = '[data-test-subj="alertsTableIsLoaded"] .euiDataGridRow';
export class DetectionsPageObject extends FtrService {
private readonly find = this.ctx.getService('find');