test: add dashboard visualize test cases (#8303)

### **PR Type**
Tests, Enhancement


___

### **Description**
- Add visualize error message test

- Add quick mode toggle verification

- Use enhanced base fixtures for tests

- Enable visualize spec in CI workflow


___

### Diagram Walkthrough


```mermaid
flowchart LR
  Tests["Visualize tests updated"] -- "use" --> Fixtures["Enhanced base fixtures"]
  Tests -- "verify" --> ErrorMsg["Aggregation alias error message"]
  Tests -- "verify" --> QuickMode["Quick mode toggle true"]
  CI["Playwright workflow"] -- "enable" --> VisualizeSpec["visualize.spec.js run"]
```



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>visualise.js</strong><dd><code>Add helper to verify
quick mode toggle</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

tests/ui-testing/pages/dashboardPages/visualise.js

<ul><li>Add quick mode toggle verification helper<br> <li> Check
aria-checked and CSS class</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8303/files#diff-c14ac1db3e4aa86190d7525e8e38c410bcbbae7f46b01890e74937cc11be67e6">+37/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>page-manager.js</strong><dd><code>Export PageManager
default for imports</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

tests/ui-testing/pages/page-manager.js

- Export PageManager as default


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8303/files#diff-565eab8b3c767ae50c9d05e591621d14f66b81af84d6b5ea6efe136c1c3933df">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Tests</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>visualize.spec.js</strong><dd><code>Extend visualize
e2e tests and base setup</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></summary>
<hr>

tests/ui-testing/playwright-tests/dashboards/visualize.spec.js

<ul><li>Switch to enhanced base fixtures<br> <li> Add test for
aggregation alias error<br> <li> Add test for quick mode toggle true<br>
<li> Improve navigation and waits</ul>


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8303/files#diff-d144f23552ec150db375c89e52c3a53ede9ff3fa5dcd6092694fbe0a308d22ec">+79/-8</a>&nbsp;
&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>playwright.yml</strong><dd><code>Enable visualize spec
in CI</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></summary>
<hr>

.github/workflows/playwright.yml

- Enable visualize.spec.js in workflow run list


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8303/files#diff-7afcd2d8f7b49bda74843f209eefb7b2da45f7e7803bf2e4bd636699b76aa2d3">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

</details>

___
This commit is contained in:
ktx-akshay
2025-09-06 17:40:07 +05:30
committed by GitHub
parent cabbacf970
commit 89cc402d64
3 changed files with 243 additions and 25 deletions
@@ -301,17 +301,150 @@ export default class LogsVisualise {
const chartRenderer = page.locator(
'[data-test="chart-renderer"], [data-test="dashboard-panel-table"]'
);
const chartExists = await chartRenderer.count();
if (chartExists > 0) {
// Check if the chart is visible without using expect in page object
const isVisible = await chartRenderer.first().isVisible();
if (!isVisible) {
throw new Error("Chart renderer is present but not visible");
try {
// Wait for chart renderer to be present and visible
await chartRenderer.first().waitFor({
state: "visible",
timeout: 15000,
});
// Additional check to ensure the chart is actually rendered
const chartExists = await chartRenderer.count();
if (chartExists > 0) {
// Double-check visibility after waiting
const isVisible = await chartRenderer.first().isVisible();
if (!isVisible) {
// Wait for chart content to be rendered intelligently
const chartContentReady = await this.waitForChartContent(page, 3000);
if (!chartContentReady) {
// Fallback: try waiting for chart renderer to be stable
try {
await chartRenderer.first().waitFor({
state: "visible",
timeout: 2000,
});
} catch (fallbackError) {
throw new Error(
`Chart renderer present but content failed to load after intelligent waiting. Error: ${fallbackError.message}`
);
}
}
// Final visibility check after intelligent waiting
const isVisibleRetry = await chartRenderer.first().isVisible();
if (!isVisibleRetry) {
throw new Error(
"Chart renderer is present but not visible after intelligent retry"
);
}
}
}
return chartExists > 0;
} catch (error) {
// If waiting times out, check if element exists but is not visible
const chartExists = await chartRenderer.count();
if (chartExists > 0) {
const isVisible = await chartRenderer.first().isVisible();
throw new Error(
`Chart renderer found (${chartExists} elements) but visibility check failed. Is visible: ${isVisible}. Original error: ${error.message}`
);
} else {
throw new Error(
`No chart renderer found. Original error: ${error.message}`
);
}
}
}
// RECOMMENDED: Native browser function (fastest & most reliable)
async waitForChartContent(page, timeout = 5000) {
return page
.waitForFunction(
() =>
document.querySelector(
'[data-test="chart-renderer"], [data-test="dashboard-panel-table"]'
)?.offsetParent !== null,
{},
{ timeout }
)
.then(() => true)
.catch(() => false);
}
// Get quick mode toggle state using the most reliable method
async getQuickModeToggleState() {
const quickModeToggle = this.page.locator(
'[data-test="logs-search-bar-quick-mode-toggle-btn"]'
);
// Wait for the toggle to be present
await quickModeToggle.waitFor({ state: "visible", timeout: 10000 });
// Check aria-checked attribute - this is the most reliable indicator
// Falls back to aria-pressed if aria-checked is not available
let ariaChecked = await quickModeToggle.getAttribute("aria-checked");
if (ariaChecked === null) {
ariaChecked = await quickModeToggle.getAttribute("aria-pressed");
}
return ariaChecked === "true";
}
// Verify quick mode toggle state
async verifyQuickModeToggle(expectedState = true) {
const isChecked = await this.getQuickModeToggleState();
// Validate the toggle state matches expectation
if (expectedState !== isChecked) {
throw new Error(
`Expected quick mode to be ${expectedState} but actual state is ${isChecked}`
);
}
return isChecked;
}
// Toggle quick mode to specific state (enable/disable)
async setQuickModeToggle(enableState) {
const quickModeToggle = this.page.locator(
'[data-test="logs-search-bar-quick-mode-toggle-btn"]'
);
await quickModeToggle.waitFor({ state: "visible", timeout: 10000 });
const currentState = await this.getQuickModeToggleState();
// Only click if we need to change the state
if (currentState !== enableState) {
await quickModeToggle.click();
// Wait for state change to complete
await this.page.waitForTimeout(500);
// Verify the state actually changed
const newState = await this.getQuickModeToggleState();
if (newState !== enableState) {
throw new Error(
`Failed to set quick mode to ${enableState}. Current state: ${newState}`
);
}
}
return chartExists > 0;
return enableState;
}
// Enable quick mode if it's currently disabled
async enableQuickModeIfDisabled() {
return await this.setQuickModeToggle(true);
}
// Disable quick mode if it's currently enabled
async disableQuickModeIfEnabled() {
return await this.setQuickModeToggle(false);
}
// Helper function to verify chart type selection
@@ -353,4 +486,10 @@ export default class LogsVisualise {
await this.updateSettingsBtn.waitFor({ state: "visible", timeout: 5000 });
await this.updateSettingsBtn.click();
}
//wait for query inspector to be visible
async waitForQueryInspector(page) {
const queryInspector = page.getByText("Query Inspectorclose");
await expect(queryInspector).toBeVisible({ timeout: 10000 });
}
}
+5 -5
View File
@@ -56,7 +56,7 @@ class PageManager {
*/
constructor(page) {
this.page = page;
// ===== EXISTING DASHBOARD PAGE OBJECTS =====
this.dashboardCreate = new DashboardCreate(page);
this.dashboardList = new DashboardListPage(page);
@@ -75,10 +75,10 @@ class PageManager {
this.dateTimeHelper = new DateTimeHelper(page);
this.logsVisualise = new LogsVisualise(page);
this.dashboardPage = new DashboardPage(page);
// ===== EXISTING ALERTS PAGE OBJECT =====
this.alertsPage = new AlertsPage(page);
// ===== SANITY SPEC ADDITIONAL PAGE OBJECTS =====
this.logsPage = new LogsPage(page);
this.streamsPage = new StreamsPage(page);
@@ -87,7 +87,7 @@ class PageManager {
this.pipelinesPage = new PipelinesPage(page);
this.loginPage = new LoginPage(page);
this.ingestionPage = new IngestionPage(page);
// ===== GENERAL TESTS ADDITIONAL PAGE OBJECTS =====
this.homePage = new HomePage(page);
this.metricsPage = new MetricsPage(page);
@@ -106,7 +106,7 @@ class PageManager {
this.enrichmentPage = new EnrichmentPage(page);
this.schemaPage = new SchemaPage(page);
this.schemaLoadPage = new SchemaLoadPage(page);
// ===== LOGS, REPORTS, STREAMS, PIPELINES ADDITIONAL PAGE OBJECTS =====
this.logsQueryPage = new LogsQueryPage(page);
this.unflattenedPage = new UnflattenedPage(page);
@@ -1,8 +1,12 @@
import { test, expect } from "@playwright/test";
import { login } from "./utils/dashLogin.js";
const {
test,
expect,
navigateToBase,
} = require("../utils/enhanced-baseFixtures.js");
import { ingestion } from "./utils/dashIngestion.js";
import logData from "../../fixtures/log.json";
import PageManager from "../../pages/page-manager";
import { waitForDateTimeButtonToBeEnabled } from "../../pages/dashboardPages/dashboard-time";
import { waitForDashboardPage, deleteDashboard } from "./utils/dashCreation.js";
@@ -46,16 +50,19 @@ const largeDatasetSqlQuery = `SELECT kubernetes_annotations_kubectl_kubernetes_i
const histogramQuery = `SELECT histogram(_timestamp) as "x_axis_1", count(kubernetes_namespace_name) as "y_axis_1" FROM "${STREAM_NAME}" GROUP BY x_axis_1 ORDER BY x_axis_1 ASC `;
// Query without aliases for testing error message
const queryWithoutAliases = `SELECT count(kubernetes_container_hash), count(kubernetes_container_name), count(kubernetes_host) FROM "${STREAM_NAME}" WHERE kubernetes_namespace_name IS NOT NULL GROUP BY kubernetes_annotations_kubectl_kubernetes_io_default_container`;
test.describe("logs testcases", () => {
test.beforeEach(async ({ page }) => {
await login(page);
await page.waitForTimeout(1000);
await navigateToBase(page);
await ingestion(page);
await page.waitForTimeout(2000);
await page.goto(
`${logData.logsUrl}?org_identifier=${process.env["ORGNAME"]}`
);
// Navigate to logs page
const logsUrl = `${logData.logsUrl}?org_identifier=${process.env["ORGNAME"]}`;
await page.goto(logsUrl);
await page.waitForLoadState("networkidle");
// Instantiate PageManager with the current page
const pm = new PageManager(page);
@@ -437,20 +444,26 @@ test.describe("logs testcases", () => {
await pm.logsVisualise.openVisualiseTab();
await pm.logsVisualise.runQueryAndWaitForCompletion();
await pm.logsVisualise.verifyChartRenders(page);
await pm.logsVisualise.addPanelToNewDashboard(
randomDashboardName,
panelName
);
await page.waitForTimeout(2000);
// Wait for and assert the success message
const successMessage = page.getByText("Panel added to dashboard");
await expect(successMessage).toBeVisible({ timeout: 10000 });
await page
.locator('[data-test="dashboard-edit-panel-' + panelName + '-dropdown"]')
.click();
await page.locator('[data-test="dashboard-query-inspector-panel"]').click();
await page.waitForTimeout(2000);
await pm.logsVisualise.waitForQueryInspector(page);
await expect(
page
.getByRole("cell", {
@@ -479,20 +492,24 @@ test.describe("logs testcases", () => {
await pm.logsVisualise.openVisualiseTab();
await page.waitForTimeout(2000);
await pm.logsVisualise.runQueryAndWaitForCompletion();
await pm.logsVisualise.addPanelToNewDashboard(
randomDashboardName,
panelName
);
await page.waitForTimeout(2000);
// Wait for and assert the success message
const successMessage = page.getByText("Panel added to dashboard");
await expect(successMessage).toBeVisible({ timeout: 10000 });
await page
.locator('[data-test="dashboard-edit-panel-' + panelName + '-dropdown"]')
.click();
await page.locator('[data-test="dashboard-query-inspector-panel"]').click();
await page.waitForTimeout(2000);
await pm.logsVisualise.waitForQueryInspector(page);
await expect(
page
.getByRole("cell", {
@@ -505,4 +522,66 @@ test.describe("logs testcases", () => {
await page.locator('[data-test="dashboard-back-btn"]').click();
await deleteDashboard(page, randomDashboardName);
});
test("should show error message when using aggregation functions without aliases in SQL query", async ({
page,
}) => {
const pm = new PageManager(page);
// Step 1: Open logs page
await pm.logsVisualise.openLogs();
// Step 2: Fill the query editor with SQL query without aliases
await pm.logsVisualise.fillLogsQueryEditor(queryWithoutAliases);
// Step 3: Set relative time
await pm.logsVisualise.setRelative("6", "w");
// Step 4: Apply the query
await pm.logsVisualise.logsApplyQueryButton();
// Step 5: Open the visualize tab
await pm.logsVisualise.openVisualiseTab();
// Step 6: Wait for error message to appear
const errorMessage = page.getByText(
"Fields using aggregation functions must have aliases"
);
// Wait for the error message to appear on the page
await errorMessage.waitFor({ state: "visible", timeout: 10000 });
// Step 7: Check for the specific error message using getByText
// Verify the error message is displayed
await expect(errorMessage.first()).toBeVisible();
});
test("should show quick mode as true when toggling to visualize tab", async ({
page,
}) => {
const pm = new PageManager(page);
await pm.logsVisualise.openLogs();
await pm.logsVisualise.logsSelectStream("e2e_automate");
await waitForDateTimeButtonToBeEnabled(page);
await pm.logsVisualise.setRelative("6", "w");
// Step 4: Apply the query
await pm.logsVisualise.logsApplyQueryButton();
// Step 5: Open the visualize tab
await pm.logsVisualise.openVisualiseTab();
// Step 7: Verify quick mode toggle is true
const quickModeState = await pm.logsVisualise.verifyQuickModeToggle(true);
expect(quickModeState).toBe(true);
// Additional assertion using Playwright's expect for the toggle state
const quickModeToggle = page.locator(
'[data-test="logs-search-bar-quick-mode-toggle-btn"]'
);
await expect(quickModeToggle).toHaveAttribute("aria-checked", "true");
});
});