mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 08:09:27 +00:00
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>
</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>
</td>
</tr>
<tr>
<td>
<details>
<summary><strong>page-manager.js</strong><dd><code>Export PageManager
default for imports</code>
</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>
</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>
</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>
</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>
</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>
</td>
</tr>
</table></td></tr></tr></tbody></table>
</details>
___
This commit is contained in:
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user