Files

585 lines
24 KiB
JavaScript

/**
* Logs Query Builder — Editor & Mode Tests
*
* Covers: builder tab improvement, edge cases, query mode toggle,
* SQL mode toggle in builder, search bar editor state,
* bare field select defaults, and field list button visibility.
*
* @tags @queryBuilder @logs @all
*/
const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js');
const testLogger = require('../utils/test-logger.js');
const PageManager = require('../../pages/page-manager.js');
const {
ingestForQueryBuilderTest,
setupQueryAndSwitchToBuild,
initQueryBuilderTest,
} = require('../utils/queryBuilder-helpers.js');
// ============================================================================
// Test Suite: P1 - Builder Tab Improvement
// ============================================================================
test.describe("Logs Query Builder - Builder Tab Improvement", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('Builder Tab Improvement test setup completed');
});
test("SQL OFF + empty query → Build tab opens with default fields and bar chart", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Case 1: SQL OFF + empty → default histogram/count');
await pm.logsPage.disableSqlModeIfNeeded();
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.verifyChartTypeSelected('bar');
const sqlModeOn = await pm.logsPage.isSqlModeOn();
expect(sqlModeOn).toBe(false);
testLogger.info('Case 1: SQL OFF + empty → default fields + bar - PASSED');
});
test("SQL OFF + WHERE clause → Build tab parses filter into builder", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Case 2: SQL OFF + WHERE clause → filter parsed');
await pm.logsPage.disableSqlModeIfNeeded();
await page.waitForLoadState('domcontentloaded');
await pm.logsPage.setQueryEditorContent("code = '200'");
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.verifyChartTypeSelected('bar');
const sqlModeOn = await pm.logsPage.isSqlModeOn();
expect(sqlModeOn).toBe(false);
testLogger.info('Case 2: SQL OFF + WHERE → filter parsed + bar - PASSED');
});
test("SQL ON + empty query → Build tab opens with default fields and bar chart", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Case 3: SQL ON + empty → default histogram/count');
await pm.logsPage.enableSqlModeIfNeeded();
await pm.logsPage.setQueryEditorContent('');
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.verifyChartTypeSelected('bar');
testLogger.info('Case 3: SQL ON + empty → default fields + bar - PASSED');
});
test("Query mode toggle: Auto → Custom shows SQL editor", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing query mode toggle: Auto → Custom');
const histogramQuery = 'SELECT histogram(_timestamp) as time, count(*) as count FROM "e2e_automate" GROUP BY time';
await setupQueryAndSwitchToBuild(pm, page, histogramQuery);
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.clickCustomQueryType();
await pm.logsPage.expectCustomModeActive();
testLogger.info('Query mode toggle: Auto → Custom - PASSED');
});
test("SQL ON + multiple aggregations → builder shows multiple Y-axis fields", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing multiple aggregations → multiple Y-axis');
const multiAggQuery = 'SELECT histogram(_timestamp) as "x_axis_1", count(_timestamp) as "y_axis_1", avg(code) as "y_axis_2" FROM "e2e_automate" GROUP BY x_axis_1 ORDER BY x_axis_1 ASC';
await setupQueryAndSwitchToBuild(pm, page, multiAggQuery);
await pm.logsPage.verifyChartTypeSelected('bar');
await pm.logsPage.expectXAxisLayoutVisible();
await pm.logsPage.expectYAxisLayoutVisible();
testLogger.info('Multiple aggregations → multiple Y-axis - PASSED');
});
test("SQL ON + breakdown field → builder shows breakdown section", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing breakdown field populated in builder');
const breakdownQuery = 'SELECT histogram(_timestamp) as "x_axis_1", count(_timestamp) as "y_axis_1", code as "z_axis_1" FROM "e2e_automate" GROUP BY x_axis_1, z_axis_1 ORDER BY x_axis_1 ASC';
await setupQueryAndSwitchToBuild(pm, page, breakdownQuery);
await pm.logsPage.verifyChartTypeSelected('bar');
await pm.logsPage.expectXAxisLayoutVisible();
await pm.logsPage.expectYAxisLayoutVisible();
await pm.logsPage.expectBreakdownLayoutVisible();
testLogger.info('Breakdown field populated - PASSED');
});
});
// ============================================================================
// Test Suite: P2 - Edge Cases
// ============================================================================
test.describe("Logs Query Builder - Edge Cases", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('Edge Cases test setup completed');
});
test("SQL ON mode preserved when toggling to Build tab", {
tag: ['@queryBuilder', '@edge', '@P2', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SQL ON preserved on Build tab switch');
await pm.logsPage.enableSqlModeIfNeeded();
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
const preserved = await pm.logsPage.verifySqlModePreservedOnBuild(true);
expect(preserved).toBeTruthy();
testLogger.info('SQL ON preserved on Build tab - PASSED');
});
test("Window function query selects table chart on Build tab", {
tag: ['@queryBuilder', '@edge', '@P2', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing window function → table chart');
const windowQuery = 'SELECT *, ROW_NUMBER() OVER (ORDER BY _timestamp DESC) as rn FROM "e2e_automate" LIMIT 10';
await setupQueryAndSwitchToBuild(pm, page, windowQuery);
await pm.logsPage.expectDashboardPanelTableVisible();
await pm.logsPage.verifyChartTypeSelected('table');
testLogger.info('Window function → table chart - PASSED');
});
test("Case 7: SQL ON + >2 GROUP BY → table chart", {
tag: ['@queryBuilder', '@edge', '@P2', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Case 7: >2 GROUP BY → table chart');
const multiGroupByQuery = 'SELECT code, method, level, count(*) as "y_axis_1" FROM "e2e_automate" GROUP BY code, method, level';
await setupQueryAndSwitchToBuild(pm, page, multiGroupByQuery);
await pm.logsPage.expectDashboardPanelTableVisible();
await pm.logsPage.verifyChartTypeSelected('table');
testLogger.info('Case 7: >2 GROUP BY → table chart - PASSED');
});
test("Case 9: SQL ON + multi-stream JOIN → builder mode, bar chart", {
tag: ['@queryBuilder', '@edge', '@P2', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Case 9: multi-stream JOIN → builder mode → bar');
const joinQuery = 'SELECT a._timestamp, b._timestamp FROM "e2e_automate" a JOIN "e2e_automate" b ON a._timestamp = b._timestamp LIMIT 10';
await setupQueryAndSwitchToBuild(pm, page, joinQuery);
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.verifyChartTypeSelected('bar');
testLogger.info('Case 9: multi-stream JOIN → builder mode, bar chart - PASSED');
});
});
// ============================================================================
// Test Suite: Query Mode Toggle (Auto ↔ Custom) While in Builder
// ============================================================================
test.describe("Logs Query Builder - Query Mode Toggle", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('Query Mode Toggle test setup completed');
});
test("SQL ON: Auto → Custom shows full generated SQL in editor", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Auto → Custom (SQL ON)');
const histogramQuery = 'SELECT histogram(_timestamp) as "x_axis_1", count(_timestamp) as "y_axis_1" FROM "e2e_automate" GROUP BY x_axis_1';
await setupQueryAndSwitchToBuild(pm, page, histogramQuery);
// Custom mode copies the builder's generated SQL once, so it must exist before the switch.
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.expectQueryEditorPopulated();
await pm.logsPage.clickCustomQueryType();
await pm.logsPage.expectCustomModeActive();
const editorText = await pm.logsPage.getQueryEditorText();
expect(editorText.toLowerCase()).toContain('select');
expect(editorText.toLowerCase()).toContain('from');
testLogger.info('Auto → Custom (SQL ON) - PASSED');
});
test("SQL OFF: Auto → Custom shows WHERE clause in editor", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing Auto → Custom (SQL OFF)');
await pm.logsPage.disableSqlModeIfNeeded();
await pm.logsPage.setQueryEditorContent("code = '200'");
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
await pm.logsPage.clickCustomQueryType();
await pm.logsPage.expectCustomModeActive();
const editorText = await pm.logsPage.getQueryEditorText();
expect(editorText.toLowerCase()).not.toContain('select');
testLogger.info('Auto → Custom (SQL OFF) - PASSED');
});
});
// ============================================================================
// Test Suite: SQL Mode Toggle While in Builder
// ============================================================================
test.describe("Logs Query Builder - SQL Mode Toggle in Builder", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('SQL Mode Toggle in Builder test setup completed');
});
test("SQL OFF → ON in builder: search bar shows full SQL", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SQL OFF → ON in builder');
await pm.logsPage.disableSqlModeIfNeeded();
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
await pm.logsPage.enableSqlModeIfNeeded();
await page.waitForLoadState('domcontentloaded');
const editorText = await pm.logsPage.getQueryEditorTextWhenReady();
expect(editorText.toLowerCase()).toContain('select');
expect(editorText.toLowerCase()).toContain('from');
testLogger.info('SQL OFF → ON: full SQL shown - PASSED');
});
test("SQL ON → OFF in builder: search bar shows WHERE clause only", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SQL ON → OFF in builder');
const filterQuery = 'SELECT histogram(_timestamp) as "x_axis_1", count(_timestamp) as "y_axis_1" FROM "e2e_automate" WHERE code = \'200\' GROUP BY x_axis_1';
await setupQueryAndSwitchToBuild(pm, page, filterQuery);
await pm.logsPage.disableSqlModeIfNeeded();
await page.waitForLoadState('domcontentloaded');
const editorText = await pm.logsPage.getQueryEditorText();
// When SQL mode is turned OFF in the build tab, `onBuildQueryGenerated`
// syncs ONLY the extracted WHERE clause condition (no `WHERE` keyword) into
// the search bar — matching the search bar's filter-only mode. Verify the
// filter content (`code` field) is retained.
expect(editorText.toLowerCase()).toContain('code');
testLogger.info('SQL ON → OFF: WHERE clause filter preserved in builder-generated SQL - PASSED');
});
});
// ============================================================================
// Test Suite: Search Bar Editor State in Builder
// ============================================================================
test.describe("Logs Query Builder - Search Bar Editor State", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('Search Bar Editor State test setup completed');
});
test("SQL OFF + Auto mode: editor disabled with WHERE clause", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SQL OFF + Auto: editor disabled');
await pm.logsPage.disableSqlModeIfNeeded();
await pm.logsPage.setQueryEditorContent("code = '200'");
await pm.logsPage.clickBuildToggle();
await pm.logsPage.waitForBuildTabLoaded();
await pm.logsPage.expectMonacoEditorAreaVisible();
await pm.logsPage.expectBuilderModeActive();
testLogger.info('SQL OFF + Auto: editor disabled - PASSED');
});
test("SQL ON + Auto mode: editor disabled with full generated SQL", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SQL ON + Auto: editor disabled');
const histogramQuery = 'SELECT histogram(_timestamp) as "x_axis_1", count(_timestamp) as "y_axis_1" FROM "e2e_automate" GROUP BY x_axis_1';
await setupQueryAndSwitchToBuild(pm, page, histogramQuery);
// Wait for builder toggle to be active AND for the editor to actually contain SQL.
// expectBuilderModeActive() passes as soon as the toggle state is set (from the
// PanelEditor immediate watcher), but makeAutoSQLQuery() needs the updateGroupedFields
// API call to finish first — that takes longer in CI.
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.expectQueryEditorPopulated();
const editorText = await pm.logsPage.getQueryEditorText();
expect(editorText.toLowerCase()).toContain('select');
expect(editorText.toLowerCase()).toContain('from');
testLogger.info('SQL ON + Auto: editor disabled - PASSED');
});
test("SQL ON + Custom mode: editor editable with full SQL", {
tag: ['@queryBuilder', '@functional', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SQL ON + Custom: editor editable');
const histogramQuery = 'SELECT histogram(_timestamp) as "x_axis_1", count(_timestamp) as "y_axis_1" FROM "e2e_automate" GROUP BY x_axis_1';
await setupQueryAndSwitchToBuild(pm, page, histogramQuery);
// Wait for builder toggle AND editor content before switching to Custom mode.
// onBuildModeToggle reads queries[0].query to populate the editor; that value
// is set by makeAutoSQLQuery() which needs the updateGroupedFields API call first.
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.expectQueryEditorPopulated();
await pm.logsPage.clickCustomQueryType();
await pm.logsPage.expectCustomModeActive();
const editorText = await pm.logsPage.getQueryEditorText();
expect(editorText.toLowerCase()).toContain('select');
await pm.logsPage.setQueryEditorContent('SELECT count(*) FROM "e2e_automate"');
const newText = await pm.logsPage.getQueryEditorText();
expect(newText.toLowerCase()).toContain('count');
testLogger.info('SQL ON + Custom: editor editable - PASSED');
});
});
// ============================================================================
// Test Suite: Bare Field Select → Default histogram/count (Case 3a)
// ============================================================================
test.describe("Logs Query Builder — Bare Field Select defaults (Case 3a)", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('Bare field select test setup completed');
});
test("Single bare field gets default histogram/count on build toggle", {
tag: ['@queryBuilder', '@bareFieldSelect', '@P0', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SELECT single_field → default histogram/count');
await setupQueryAndSwitchToBuild(pm, page, 'SELECT kubernetes_container_name FROM "e2e_automate"');
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.expectXAxisHasItems();
await pm.logsPage.expectYAxisHasItems();
await pm.logsPage.verifyChartTypeSelected('bar');
testLogger.info('Single bare field → default histogram/count - PASSED');
});
test("Multiple bare fields get default histogram/count on build toggle", {
tag: ['@queryBuilder', '@bareFieldSelect', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SELECT field1, field2 → default histogram/count');
await setupQueryAndSwitchToBuild(pm, page, 'SELECT kubernetes_container_name, kubernetes_host FROM "e2e_automate"');
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.expectXAxisHasItems();
await pm.logsPage.expectYAxisHasItems();
await pm.logsPage.verifyChartTypeSelected('bar');
testLogger.info('Multiple bare fields → default histogram/count - PASSED');
});
test("Bare field with WHERE clause preserves filter and uses defaults", {
tag: ['@queryBuilder', '@bareFieldSelect', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing SELECT field WHERE condition → defaults + filter preserved');
await setupQueryAndSwitchToBuild(pm, page, "SELECT kubernetes_container_name FROM \"e2e_automate\" WHERE kubernetes_host = 'test'");
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.expectXAxisHasItems();
await pm.logsPage.expectYAxisHasItems();
await pm.logsPage.verifyChartTypeSelected('bar');
testLogger.info('Bare field with WHERE → defaults + filter - PASSED');
});
test("Query with aggregation does NOT use defaults (parsed normally)", {
tag: ['@queryBuilder', '@bareFieldSelect', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Testing query with aggregation → parsed normally');
const aggQuery = 'SELECT kubernetes_container_name, count(_timestamp) as "y_axis_1" FROM "e2e_automate" GROUP BY kubernetes_container_name';
await setupQueryAndSwitchToBuild(pm, page, aggQuery);
await pm.logsPage.expectBuilderModeActive();
const xCount = await pm.logsPage.expectXAxisHasItems();
expect(xCount).toBeGreaterThanOrEqual(1);
const yCount = await pm.logsPage.expectYAxisHasItems();
expect(yCount).toBeGreaterThanOrEqual(1);
testLogger.info('Query with aggregation → parsed normally - PASSED');
});
});
// ============================================================================
// Test Suite: FieldList Button Visibility
// ============================================================================
test.describe("Logs Query Builder — FieldList button visibility", () => {
test.describe.configure({ mode: 'parallel' });
let pm;
test.beforeAll(async ({ request }) => {
await ingestForQueryBuilderTest(request);
});
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await initQueryBuilderTest(page, pm);
testLogger.info('FieldList button test setup completed');
});
test("Search filter does not break button visibility in builder mode", {
tag: ['@queryBuilder', '@fieldListButtons', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Verifying search filter does not break buttons');
await setupQueryAndSwitchToBuild(pm, page, 'SELECT * FROM "e2e_automate"');
await pm.logsPage.expectBuilderModeActive();
await pm.logsPage.searchFieldInBuilder('kubernetes');
const visibleXCount = await pm.logsPage.getAddXButtonCount();
expect(visibleXCount).toBeGreaterThan(0);
testLogger.info(`After filter: ${visibleXCount} +X buttons visible`);
await pm.logsPage.searchFieldInBuilder('');
const afterClearCount = await pm.logsPage.getAddXButtonCount();
expect(afterClearCount).toBeGreaterThan(0);
testLogger.info(`After clear: ${afterClearCount} +X buttons visible - PASSED`);
});
test("Custom mode: buttons visible on custom/VRL fields after search", {
tag: ['@queryBuilder', '@fieldListButtons', '@P1', '@all', '@logs']
}, async ({ page }) => {
testLogger.info('Verifying buttons in custom mode with search');
await setupQueryAndSwitchToBuild(pm, page, 'SELECT kubernetes_container_name, kubernetes_host FROM "e2e_automate"');
await pm.logsPage.clickCustomQueryType();
await pm.logsPage.searchFieldInBuilder('axis');
const buttonCount = await pm.logsPage.getAddXButtonCount();
testLogger.info(`Custom mode + search: ${buttonCount} +X buttons`);
expect(buttonCount).toBeGreaterThan(0);
testLogger.info('Custom mode buttons with search - PASSED');
});
});