Skip to content

Commit d08dbc6

Browse files
authored
PMM-14596: Refactor autowaiting for panel loadings for UI tests (#838)
* PMM-14596: Refactor dashboard tests to utilize loadAllPanels method for improved readability and maintainability * fix to autowait * fix panel flakiness * updated Expects * remove redundancies * fix dashboard * fix panels
1 parent 5eba3a3 commit d08dbc6

5 files changed

Lines changed: 50 additions & 84 deletions

File tree

e2e_tests/components/dashboards/panels/panel.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ export default class PanelComponent {
66
constructor() {}
77

88
protected verifyData = async (locator: Locator, panelName: string) => {
9-
await locator.first().scrollIntoViewIfNeeded({ timeout: Timeouts.ONE_SECOND });
9+
const target = locator.first();
10+
await target.waitFor({ state: 'visible', timeout: Timeouts.ONE_MINUTE });
11+
await target.scrollIntoViewIfNeeded();
1012
const barGaugeTexts = await locator.allTextContents();
1113
for (const barGaugeText of barGaugeTexts) {
1214
expect.soft(barGaugeText.length, `Panel: ${panelName} has empty values!`).toBeGreaterThan(0);

e2e_tests/pages/dashboards/dashboards.page.ts

Lines changed: 41 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Page, expect } from '@playwright/test';
1+
import { Page, expect, test } from '@playwright/test';
22
import { GrafanaPanel } from '@interfaces/grafanaPanel';
33
import { GetService } from '@interfaces/inventory';
44
import { replaceWildcards } from '@helpers/metrics.helper';
@@ -58,11 +58,7 @@ export default class Dashboards {
5858
}
5959

6060
private elements = {
61-
expandRow: () => this.page.locator('//*[@aria-label="Expand row"]'),
62-
row: () =>
63-
this.page.locator(
64-
'//button[contains(@data-testid, "dashboard-row-title")]//ancestor::div[contains(@class, "react-grid-item")]',
65-
),
61+
expandRow: () => this.page.getByLabel('Expand row'),
6662
panelName: () => this.page.locator('//section[contains(@data-testid, "Panel header")]//h2'),
6763
noDataPanel: () =>
6864
this.page.locator(
@@ -72,102 +68,66 @@ export default class Dashboards {
7268
this.page.locator(
7369
'//*[(text()="No data") or (text()="NO DATA") or (text()="N/A") or (text()="-") or (text() = "No Data") or (@data-testid="data-testid Panel data error message")]//ancestor::section//h2',
7470
),
75-
rowByName: (rowName: string) =>
76-
this.page.locator(
77-
`//button[contains(@data-testid, "dashboard-row-title") and contains(@data-testid, "${rowName}")]`,
78-
),
71+
refreshButton: () => this.page.getByLabel('Refresh', { exact: true }),
72+
loadingIndicator: () => this.page.getByLabel('data-testid Loading indicator', { exact: true }),
73+
loadingText: () => this.page.getByText('Loading plugin panel...', { exact: true }),
7974
loadingBar: () => this.page.getByLabel('Panel loading bar'),
8075
gridItems: () => this.page.locator('.react-grid-item'),
8176
};
8277

83-
public async loadAllPanels() {
84-
await this.expandAllRows();
85-
86-
const items = this.elements.gridItems();
87-
const totalItems = await items.count();
88-
89-
// Open every item and wait until the item has at least one child, indicating content started to load.
90-
for (let i = 0; i < totalItems; i++) {
91-
const item = items.nth(i);
92-
await item.scrollIntoViewIfNeeded();
93-
await expect
94-
.poll(async () => await item.locator(':scope > *').count(), { timeout: Timeouts.ONE_MINUTE })
95-
.toBeGreaterThan(0);
96-
}
78+
private async loadAllPanels() {
79+
const expectPanel = expect.configure({ timeout: Timeouts.ONE_MINUTE });
80+
// Wait for the dashboard to be visible before proceeding.
81+
await test.step('Wait for initial loading to finish', async () => {
82+
await expectPanel(this.elements.refreshButton()).toBeVisible();
83+
await expectPanel(this.elements.loadingIndicator()).toHaveCount(0);
84+
await expectPanel(this.elements.loadingText()).toHaveCount(0);
85+
});
86+
87+
// Expand rows if present and wait for content in each item.
88+
await test.step('Expand rows and load panel content', async () => {
89+
for (let i = 0; i < (await this.elements.gridItems().count()); i++) {
90+
const item = this.elements.gridItems().nth(i);
91+
await item.scrollIntoViewIfNeeded();
92+
93+
const expandButton = item.getByLabel('Expand row');
94+
if (await expandButton.isVisible()) {
95+
await expandButton.click();
96+
}
97+
98+
await expectPanel(item.locator(':scope > *')).not.toHaveCount(0);
99+
}
100+
});
97101

98-
// Confirm no loading bars remain.
99-
await expect
100-
.poll(async () => await this.elements.loadingBar().count(), {
101-
timeout: Timeouts.ONE_MINUTE,
102-
})
103-
.toBe(0);
102+
// Confirms that there are no remaining loading bars.
103+
await test.step('Wait for loading to finish', async () => {
104+
await expectPanel(this.elements.loadingBar()).toHaveCount(0);
105+
});
104106
}
105107

106-
public async verifyAllPanelsHaveData(noDataMetrics: string[]) {
107-
const noDataPanels = new Set<string>();
108-
109-
for (let i = 0; i < 10; i++) {
110-
const noDataPanelNames = await this.elements.noDataPanelName().allTextContents();
111-
noDataPanelNames.forEach((panelName: string) => noDataPanels.add(panelName));
112-
113-
await this.page.keyboard.press('PageDown');
114-
await this.page.waitForTimeout(Timeouts.HALF_SECOND);
115-
}
116-
117-
await this.page.keyboard.press('Home');
118-
await this.page.waitForTimeout(Timeouts.HALF_SECOND);
119-
108+
async verifyAllPanelsHaveData(noDataMetrics: string[]) {
109+
await this.loadAllPanels();
110+
const noDataPanels = await this.elements.noDataPanelName().allTextContents();
120111
const missingMetrics = Array.from(noDataPanels).filter((e) => !noDataMetrics.includes(e));
121-
const extraMetrics = noDataMetrics.filter((e) => !noDataPanels.has(e));
112+
const extraMetrics = noDataMetrics.filter((e) => !noDataPanels.includes(e));
122113

123114
expect.soft(missingMetrics, `Metrics without data are: ${missingMetrics}`).toHaveLength(0);
124115
expect
125116
.soft(extraMetrics, `Metrics with data that are expected to be empty are: ${extraMetrics}`)
126117
.toHaveLength(0);
127118
}
128119

129-
public async verifyMetricsPresent(expectedMetrics: GrafanaPanel[], serviceList?: GetService[]) {
120+
async verifyMetricsPresent(expectedMetrics: GrafanaPanel[], serviceList?: GetService[]) {
130121
expectedMetrics = serviceList ? replaceWildcards(expectedMetrics, serviceList) : expectedMetrics;
131122
const expectedMetricsNames = expectedMetrics.map((e) => e.name);
132-
await this.page.keyboard.press('Home');
133-
const availableMetrics = await this.getAllAvailablePanels();
123+
await this.loadAllPanels();
124+
const availableMetrics = await this.elements.panelName().allTextContents();
134125

135126
expect(availableMetrics.sort()).toEqual(expectedMetricsNames.sort());
136-
137-
await this.page.keyboard.press('Home');
138-
await this.page.waitForTimeout(Timeouts.HALF_SECOND);
139-
}
140-
141-
expandAllRows = async () => {
142-
await this.elements.row().first().waitFor({ state: 'visible' });
143-
await this.page.keyboard.press('End');
144-
await this.page.waitForTimeout(Timeouts.ONE_SECOND);
145-
const rowsName = await this.elements.expandRow().allTextContents();
146-
147-
for (const rowName of rowsName) {
148-
await this.elements.rowByName(rowName).click();
149-
await this.page.waitForTimeout(Timeouts.ONE_SECOND);
150-
}
151-
152-
await this.page.keyboard.press('Home');
153-
await this.page.waitForTimeout(Timeouts.ONE_SECOND);
154-
};
155-
156-
private async getAllAvailablePanels(): Promise<string[]> {
157-
const availableMetrics = new Set<string>();
158-
159-
for (let i = 0; i < 10; i++) {
160-
await this.page.keyboard.press('PageDown');
161-
await this.page.waitForTimeout(Timeouts.ONE_SECOND);
162-
(await this.elements.panelName().allTextContents()).forEach((availableMetric) =>
163-
availableMetrics.add(availableMetric),
164-
);
165-
}
166-
167-
return Array.from(availableMetrics.values());
168127
}
169128

170-
public verifyPanelValues = async (panels: GrafanaPanel[], serviceList?: GetService[]) => {
129+
async verifyPanelValues(panels: GrafanaPanel[], serviceList?: GetService[]) {
130+
await this.loadAllPanels();
171131
const panelList = serviceList ? replaceWildcards(panels, serviceList) : panels;
172132
for (const panel of panelList) {
173133
switch (panel.type) {

e2e_tests/pages/dashboards/valkey/valkeyCommandDetailDashboard.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ export default class ValkeyCommandDetailDashboard {
2828
{ name: 'valkey-replica-4-svc-* - Total Time Spent by Command/Sec', type: 'unknown' },
2929
{ name: 'valkey-replica-5-svc-* - Total Time Spent by Command/Sec', type: 'unknown' },
3030
{ name: 'valkey-replica-6-svc-* - Total Time Spent by Command/Sec', type: 'unknown' },
31+
// For some reason these panels have identical names even though they are different
32+
{ name: 'Commands Totals', type: 'unknown' },
33+
{ name: 'Commands Totals', type: 'unknown' },
34+
{ name: 'Commands Totals', type: 'unknown' },
35+
{ name: 'Commands Totals', type: 'unknown' },
36+
{ name: 'Commands Totals', type: 'unknown' },
3137
{ name: 'Commands Totals', type: 'unknown' },
3238
{ name: 'valkey-primary-1-svc-* - Command Percentiles', type: 'unknown' },
3339
{ name: 'valkey-primary-2-svc-* - Command Percentiles', type: 'unknown' },

e2e_tests/tests/dashboards/mysql/mysqlDashboards.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ pmmTest(
1616
}),
1717
);
1818

19-
await dashboard.expandAllRows();
2019
await dashboard.verifyMetricsPresent(dashboard.mysqlInstanceOverview.metrics);
2120
await dashboard.verifyAllPanelsHaveData(dashboard.mysqlInstanceOverview.noDataMetrics);
2221
await dashboard.verifyPanelValues(dashboard.mysqlInstanceOverview.metrics);

e2e_tests/tests/dashboards/valkey/valkeyDashboards.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ for (const dashboardName of Object.keys(valkeyDashboards)) {
2121
}),
2222
);
2323

24-
await dashboard.loadAllPanels();
2524
await dashboard.verifyMetricsPresent(dashboardPage.metrics, serviceList);
2625
await dashboard.verifyAllPanelsHaveData([]);
2726
await dashboard.verifyPanelValues(dashboardPage.metrics, serviceList);

0 commit comments

Comments
 (0)