mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 08:09:22 +00:00
test: repair two failing dashboard E2E tests (#12395)
## 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3e865a3e6a
commit
b6093cb996
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user