From b6093cb996f8b18c8efac707fc9b53bdae37e0a5 Mon Sep 17 00:00:00 2001 From: Shrinath Rao Date: Tue, 2 Jun 2026 10:19:40 +0530 Subject: [PATCH] test: repair two failing dashboard E2E tests (#12395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - **camelCase zero-value line chart** (`dashboard.spec.js:826`): SQL referenced `kubernetes_namespace_name` / `kubernetes_container_name` / `kubernetes_pod_id` with underscores, but `e2e_automate` stores those fields with dot notation (`kubernetes.namespace_name`). DataFusion returned "column not found", `errorDetail.message` was set, `ChartRenderer` was unmounted (behind a `v-if`), and the test timed out waiting for `[data-test="chart-renderer"]`. Fixed by switching to `level` / `method` / `stream` — fields guaranteed to exist — with impossible conditions so all series values are zero. - **Sankey no-data test** (`dashboard-sankey.spec.js:400`): After `waitForChartToRender()` there is an async gap between the apply button re-enabling and Vue committing the "No Data" text to the DOM. The old `waitForFunction` used `offsetParent !== null`, which fires immediately for the empty zero-height no-data div (`position: absolute`, no content → 0px height). `isVisible()` then returned `false`, producing `Expected true, Received false`. Fixed by replacing the custom logic with `expect.poll()` that reads `textContent` — retrying until the text is actually committed to the DOM. ## Test plan - [x] Both tests verified passing locally against a hosted enterprise OO instance (localhost:5080) - [ ] CI run on `o2-enterprise` PR to confirm `Dashboards-Core` and `Dashboards-Charts` jobs go green --------- Co-authored-by: Claude Sonnet 4.6 --- .../pages/dashboardPages/dashboard-chart.js | 8 +++-- .../dashboardPages/dashboard-panel-actions.js | 4 ++- .../Dashboards/dashboard-sankey.spec.js | 32 +++++++------------ .../Dashboards/dashboard.spec.js | 13 +++++--- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/tests/ui-testing/pages/dashboardPages/dashboard-chart.js b/tests/ui-testing/pages/dashboardPages/dashboard-chart.js index b0dae01897..49efdcbd93 100644 --- a/tests/ui-testing/pages/dashboardPages/dashboard-chart.js +++ b/tests/ui-testing/pages/dashboardPages/dashboard-chart.js @@ -482,10 +482,14 @@ export default class ChartTypeSelector { async enterCustomSQL(query) { await this.queryEditor.waitFor({ state: "visible", timeout: 10000 }); await this.queryEditor.click(); - // Clear any placeholder/default content, then type the new query + await this.page.keyboard.press("Escape"); // dismiss any open Monaco suggestion await this.page.keyboard.press("Control+a"); await this.page.keyboard.press("Delete"); - await this.page.keyboard.type(query); + // Use insertText (single input event) instead of type (per-character keydown events) + // to prevent Monaco's autocomplete from accepting suggestions mid-input and + // mangling the SQL (e.g. replacing table names with function calls). + await this.page.keyboard.insertText(query); + await this.page.keyboard.press("Escape"); // dismiss any suggestion triggered at end testLogger.debug('Entered custom SQL query', { query }); } diff --git a/tests/ui-testing/pages/dashboardPages/dashboard-panel-actions.js b/tests/ui-testing/pages/dashboardPages/dashboard-panel-actions.js index a4b40a8e25..e70ff20d5d 100644 --- a/tests/ui-testing/pages/dashboardPages/dashboard-panel-actions.js +++ b/tests/ui-testing/pages/dashboardPages/dashboard-panel-actions.js @@ -60,8 +60,10 @@ export default class DashboardactionPage { } // Get dashboard-error locator + // DashboardErrors.vue has two nested elements with the same data-test attribute; + // use .first() to avoid Playwright strict-mode throws on multi-element locators. getDashboardErrorLocator() { - return this.page.locator('[data-test="dashboard-error"]'); + return this.page.locator('[data-test="dashboard-error"]').first(); } // Generate a unique panel name diff --git a/tests/ui-testing/playwright-tests/Dashboards/dashboard-sankey.spec.js b/tests/ui-testing/playwright-tests/Dashboards/dashboard-sankey.spec.js index 3f49036edc..312520dcfd 100644 --- a/tests/ui-testing/playwright-tests/Dashboards/dashboard-sankey.spec.js +++ b/tests/ui-testing/playwright-tests/Dashboards/dashboard-sankey.spec.js @@ -421,29 +421,21 @@ test.describe("Sankey chart testcases", () => { await pm.dashboardPanelActions.waitForChartToRender(); // Verify no data or error is shown (incomplete Sankey config). - // Both elements may be visible simultaneously on CI, so check each - // independently rather than using .or() which throws strict mode - // violation when both match. + // Use expect.poll to handle the async gap between the apply button + // re-enabling and Vue committing the no-data text to the DOM. + // Checking textContent (not just isVisible) avoids false-positives + // from the empty zero-height no-data div that exists when noData === "". const noDataLocator = pm.dashboardPanelActions.getNoDataLocator(); const dashErrorLocator = pm.dashboardPanelActions.getDashboardErrorLocator(); - const noDataVisible = await noDataLocator.isVisible().catch(() => false); - const dashErrorVisible = await dashErrorLocator.isVisible().catch(() => false); - - if (!noDataVisible && !dashErrorVisible) { - // Neither visible yet — wait for either one to appear - await page.waitForFunction(() => { - const noData = document.querySelector('[data-test="no-data"]'); - const dashErr = document.querySelector('[data-test="dashboard-error"]'); - return (noData && noData.offsetParent !== null) || - (dashErr && dashErr.offsetParent !== null); - }, { timeout: 10000 }); - } - - expect(noDataVisible || dashErrorVisible || - await noDataLocator.isVisible().catch(() => false) || - await dashErrorLocator.isVisible().catch(() => false) - ).toBe(true); + await expect.poll(async () => { + const noDataText = (await noDataLocator.textContent().catch(() => '')).trim(); + const dashErrorVisible = await dashErrorLocator.isVisible().catch(() => false); + return noDataText.length > 0 || dashErrorVisible; + }, { + timeout: 12000, + message: 'Expected "No Data" or dashboard error when only Source field is configured', + }).toBeTruthy(); testLogger.info("Sankey no data state verified"); diff --git a/tests/ui-testing/playwright-tests/Dashboards/dashboard.spec.js b/tests/ui-testing/playwright-tests/Dashboards/dashboard.spec.js index d2e7e1ce9e..f4c5e0bcbf 100644 --- a/tests/ui-testing/playwright-tests/Dashboards/dashboard.spec.js +++ b/tests/ui-testing/playwright-tests/Dashboards/dashboard.spec.js @@ -841,13 +841,16 @@ test.describe("dashboard UI testcases", () => { await pm.chartTypeSelector.removeField("x_axis_1", "x"); await pm.chartTypeSelector.selectChartType("line"); - // Set custom SQL — fields are populated client-side by the SQL parser + // Set custom SQL — fields are populated client-side by the SQL parser. + // Use `level` and `method` (guaranteed fields in e2e_automate) with + // impossible conditions so all counts are zero. This validates that the + // line chart renders zero-valued camelCase-aliased series without errors. await pm.chartTypeSelector.setCustomSQL( `SELECT histogram(_timestamp, '5 minute') AS "_time", - COUNT(CASE WHEN kubernetes_namespace_name = 'ziox' AND kubernetes_container_name LIKE '4%' THEN 1 END) AS "4xxErrorCount", - COUNT(CASE WHEN kubernetes_namespace_name = 'ziox' AND kubernetes_container_name LIKE 'tes%' THEN 1 END) AS "5xxErrorCount", - COUNT(CASE WHEN kubernetes_namespace_name = 'ziox' AND kubernetes_pod_id IS NULL THEN 1 END) AS "NullErrorCount", - COUNT(CASE WHEN kubernetes_container_name = 'prometheus' THEN 1 END) AS "pageViewCount" + COUNT(CASE WHEN level = 'nonexistentLevel_abc' THEN 1 END) AS "4xxErrorCount", + COUNT(CASE WHEN level = 'nonexistentLevel_def' THEN 1 END) AS "5xxErrorCount", + COUNT(CASE WHEN method = 'NONEXISTENT_METHOD_xyz' THEN 1 END) AS "NullErrorCount", + COUNT(CASE WHEN stream = 'nonexistentStream_xyz' THEN 1 END) AS "pageViewCount" FROM e2e_automate GROUP BY _time ORDER BY _time ASC`