test: multi y-axis stacked charts (#6549)

This commit is contained in:
ktx-akshay
2025-04-17 13:44:32 +05:30
committed by GitHub
parent df1f5eba31
commit 29699ed4cb
6 changed files with 339 additions and 0 deletions
+1
View File
@@ -122,6 +122,7 @@ jobs:
"reportsUpdated2.spec.js",
"dashboards/dashboard-filter.spec.js",
"dashboards/dashboard-folder.spec.js",
"dashboards/dashboard-multi-y-axis.spec.js",
"join.spec.js",
"changeOrg.spec.js",
"serviceAccount.spec.js",
@@ -0,0 +1,141 @@
import { expect } from "@playwright/test";
// pages/chartTypeSelector.js
// Methods : selectChartType, selectStreamType, searchAndAddField, selectStream
export default class ChartTypeSelector {
constructor(page) {
this.page = page;
}
// Chart Type select
async selectChartType(chartType) {
const chartOption = this.page.locator(
`[data-test="selected-chart-${chartType}-item"]`
);
await chartOption.waitFor({ state: "visible", timeout: 5000 });
await chartOption.click();
}
// Stream Type select
async selectStreamType(type) {
// Click the dropdown
await this.page.locator('[data-test="index-dropdown-stream_type"]').click();
await this.page
.getByRole("option", { name: type })
.locator("div")
.nth(2)
.click();
}
// Stream select
async selectStream(streamName) {
const streamInput = this.page.locator(
'[data-test="index-dropdown-stream"]'
);
await streamInput.click();
await streamInput.press("Control+a");
await streamInput.fill(streamName);
const streamOption = this.page
.getByRole("option", { name: streamName, exact: true })
.locator("div")
.nth(2);
await streamOption.waitFor({ state: "visible", timeout: 5000 });
await streamOption.click();
}
// Search field and added for X, Y,Breakdown etc.
async searchAndAddField(fieldName, target) {
const searchInput = this.page.locator(
'[data-test="index-field-search-input"]'
);
await searchInput.click();
await searchInput.fill(fieldName);
const buttonSelectors = {
x: '[data-test="dashboard-add-x-data"]',
y: '[data-test="dashboard-add-y-data"]',
b: '[data-test="dashboard-add-b-data"]',
filter: '[data-test="dashboard-add-filter-data"]',
latitude: '[data-test="dashboard-add-latitude-data"]',
longitude: '[data-test="dashboard-add-longitude-data"]',
weight: '[data-test="dashboard-add-weight-data"]',
z: '[data-test="dashboard-add-z-data"]',
};
const buttonSelector = buttonSelectors[target];
if (!buttonSelector) {
throw new Error(`Invalid target type: ${target}`);
}
await this.page.locator(buttonSelector).click();
}
// Add filter condition
async addFilterCondition(initialFieldName, newFieldName, operator, value) {
// Step 1: Click the existing field if provided
if (initialFieldName) {
await this.page
.locator(
`[data-test="dashboard-add-condition-label-0-${initialFieldName}"]`
)
.click();
}
// Step 2: Change field if newFieldName is provided
if (newFieldName) {
const fieldDropdown = this.page.locator(
'[data-test="dashboard-add-condition-column-0\\}"]'
);
await fieldDropdown.click();
await fieldDropdown.fill(newFieldName);
await this.page
.getByRole("option", { name: newFieldName, exact: true })
.first()
.click();
}
const selectedField = newFieldName || initialFieldName;
// Step 3: Open the condition configuration if operator or value is being handled
if (operator || value) {
await this.page
.locator('[data-test="dashboard-add-condition-list-0"]')
.click();
await this.page
.locator('[data-test="dashboard-add-condition-condition-0"]')
.click();
}
// Step 4: Select operator if provided
if (operator) {
await this.page
.locator('[data-test="dashboard-add-condition-operator"]')
.click();
await this.page.getByRole("option", { name: operator }).click();
}
// Step 5: Enter value if provided
if (value) {
const valueInput = this.page.locator(
'[data-test="common-auto-complete"]'
);
await valueInput.click();
await valueInput.fill(value);
} else if (operator && selectedField) {
// Step 6: Assert dynamic error message for missing value
const expectedError = `Filter: ${selectedField}: Condition value required`;
const errorMessageLocator = this.page
.locator("div")
.filter({ hasText: expectedError })
.first();
await expect(errorMessageLocator).toBeVisible();
}
}
}
@@ -0,0 +1,46 @@
import { expect } from "@playwright/test";
// pages/chartTypeSelector.js
// methods: createDashboard, searchDashboard, AddPanel, applyButton
export default class DashboardCreate {
constructor(page) {
this.page = page;
this.dashCreateBtn = page.locator('[data-test="dashboard-add"]');
this.dashName = page.locator('[data-test="add-dashboard-name"]');
this.submitBtn = page.locator('[data-test="dashboard-add-submit"]');
this.deleteIcon = page.locator('[data-test="dashboard-delete"]');
this.confirmDelete = page.locator('[data-test="confirm-button"]');
this.searchDash = page.locator('[data-test="dashboard-search"]');
this.addPanelIfEmptyBtn = page.locator(
'[data-test="dashboard-if-no-panel-add-panel-btn"]'
);
this.applyQueryBtn = page.locator('[data-test="dashboard-apply"]');
}
async createDashboard(dashboardName) {
await this.dashCreateBtn.click();
await this.dashName.click();
await this.dashName.fill(dashboardName);
await this.submitBtn.click();
}
//Search Folder
async searchDashboard(dashboardName) {
await this.page
.locator('[data-test="dashboard-folder-tab-default"]')
.waitFor({ state: "visible" });
await this.searchDash.click();
await this.searchDash.fill(dashboardName);
}
//Add Panel to dashboard
async addPanel() {
await expect(this.addPanelIfEmptyBtn).toBeVisible({ timeout: 5000 });
await this.addPanelIfEmptyBtn.click();
}
async applyButton() {
await this.applyQueryBtn.click();
}
}
@@ -0,0 +1,38 @@
// Dashboard actions page
// Methods : AddPanelName, SavePanel, ApplyDashboardBtn
export default class DashboardactionPage {
constructor(page) {
this.page = page;
this.panelNameInput = page.locator('[data-test="dashboard-panel-name"]');
this.panelSaveBtn = page.locator('[data-test="dashboard-panel-save"]');
this.applyDashboard = page.locator('[data-test="dashboard-apply"]');
}
// Add panel name
async addPanelName(panelName) {
await this.panelNameInput.click();
await this.panelNameInput.fill(panelName);
}
// Save panel button
async savePanel() {
await this.panelSaveBtn.click();
}
//Apply dashboard button
async applyDashboardBtn() {
await this.applyDashboard.click();
}
// Wait for chart to render
async waitForChartToRender() {
// Wait for it to go back to bg-secondary (render complete)
await this.page.waitForFunction(() => {
const btn = document.querySelector('[data-test="dashboard-apply"]');
return btn && btn.classList.contains("bg-secondary");
});
}
}
@@ -0,0 +1,22 @@
import { expect } from "@playwright/test";
//methods : setRelativeTimeRange
export default class DateTimeHelper {
constructor(page) {
this.page = page;
this.applyQueryBtn = page.locator('[data-test="dashboard-apply"]');
this.timePickerBtn = page.locator('[data-test="date-time-btn"]');
this.applyTimeBtn = page.locator('[data-test="date-time-apply-btn"]');
}
// set relative time range
async setRelativeTimeRange(rangeCode) {
// Minutes= m Hours= h Days= d Weeks= w Months= M
await this.applyQueryBtn.click();
await this.timePickerBtn.click();
await this.page
.locator(`[data-test="date-time-relative-${rangeCode}-btn"]`)
.click();
await this.applyTimeBtn.click();
}
}
@@ -0,0 +1,91 @@
import { test, expect } from "../baseFixtures.js";
import logData from "../../cypress/fixtures/log.json";
import logsdata from "../../../test-data/logs_data.json";
import { login } from "../utils/dashLogin.js";
import { ingestion } from "../utils/dashIngestion.js";
import {
waitForDashboardPage,
deleteDashboard,
} from "../utils/dashCreation.js";
import ChartTypeSelector from "../../pages/dashboardPages/dashboard-chart.js";
import DashboardListPage from "../../pages/dashboardPages/dashboard-list.js";
import DashboardCreate from "../../pages/dashboardPages/dashboard-create.js";
import DateTimeHelper from "../../pages/dashboardPages/dashboard-time.js";
import DashboardactionPage from "../../pages/dashboardPages/dashboard-panel-actions.js";
const randomDashboardName =
"Dashboard_" + Math.random().toString(36).substr(2, 9);
test.describe.configure({ mode: "parallel" });
test.describe("dashboard multi y axis testcases", () => {
test.beforeEach(async ({ page }) => {
console.log("running before each");
await login(page);
await page.waitForTimeout(1000);
await ingestion(page);
await page.waitForTimeout(2000);
const orgNavigation = page.goto(
`${logData.logsUrl}?org_identifier=${process.env["ORGNAME"]}`
);
await orgNavigation;
});
test("Should correctly add multiple Y-axes to the stacked chart type.", async ({
page,
}) => {
const chartTypeSelector = new ChartTypeSelector(page);
const dashboardPage = new DashboardListPage(page);
const dashboardCreate = new DashboardCreate(page);
const dateTimeHelper = new DateTimeHelper(page);
const dashboardPageActions = new DashboardactionPage(page);
await dashboardPage.menuItem("dashboards-item");
await waitForDashboardPage(page);
await dashboardCreate.createDashboard(randomDashboardName);
await dashboardCreate.addPanel();
await chartTypeSelector.selectChartType("stacked");
await chartTypeSelector.selectStreamType("logs");
await chartTypeSelector.selectStream("e2e_automate");
await chartTypeSelector.searchAndAddField("kubernetes_namespace_name", "y");
await chartTypeSelector.searchAndAddField("kubernetes_container_name", "y");
await chartTypeSelector.searchAndAddField("kubernetes_labels_name", "b");
await dateTimeHelper.setRelativeTimeRange("6-w");
await dashboardPageActions.waitForChartToRender();
await page
.locator('[data-test="dashboard-panel-data-view-query-inspector-btn"]')
.click();
await expect(
page
.getByRole("cell", {
name: 'SELECT histogram(_timestamp) as "x_axis_1", count(kubernetes_namespace_name) as "y_axis_1", count(kubernetes_container_name) as "y_axis_2", kubernetes_labels_name as "breakdown_1" FROM "e2e_automate" GROUP BY x_axis_1, breakdown_1 ORDER BY x_axis_1 ASC',
})
.first()
).toBeVisible();
await page.locator('[data-test="query-inspector-close-btn"]').click();
await dashboardPageActions.addPanelName(randomDashboardName);
await dashboardPageActions.savePanel();
await page.locator('[data-test="dashboard-back-btn"]').click();
await deleteDashboard(page, randomDashboardName);
});
});