// traceQueryEditor.spec.js // Tests for OpenObserve Traces feature - Query Editor and filtering functionality // Following the modular pattern from Logs tests const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js'); const testLogger = require('../utils/test-logger.js'); const PageManager = require('../../pages/page-manager.js'); test.describe("Trace Query Editor testcases", () => { test.describe.configure({ mode: 'serial' }); let pm; // Page Manager instance test.beforeEach(async ({ page }, testInfo) => { // Initialize test setup testLogger.testStart(testInfo.title, testInfo.file); // Navigate to base URL with authentication await navigateToBase(page); pm = new PageManager(page); // Post-authentication stabilization wait await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {}); // Navigate to traces page await pm.tracesPage.navigateToTraces(); // Select the default stream as data is ingested for it only await pm.tracesPage.isStreamSelectVisible() await pm.tracesPage.selectTraceStream('default'); await page.waitForTimeout(2000); testLogger.info('Test setup completed for trace query editor'); }); test.afterEach(async ({ }, testInfo) => { testLogger.testEnd(testInfo.title, testInfo.status); }); // P0 - Critical Path Tests test("P0: Use query editor to run basic trace search", { tag: ['@traceQueryEditor', '@traces', '@query', '@smoke', '@P0', '@all'] }, async ({ page }) => { testLogger.info('Testing query editor for basic trace search'); // Enter search query await pm.tracesPage.enterTraceQuery("service_name='api-gateway'"); // Verify query appears in editor using page object const editorContent = await pm.tracesPage.getQueryEditorContent(); expect(editorContent).toContain("service_name='api-gateway'"); testLogger.info('Query entered successfully in editor'); // Set time range and run search await pm.tracesPage.setTimeRange('15m'); await pm.tracesPage.runSearch(); // Wait for search to complete with retry logic let searchCompleted = false; for (let i = 0; i < 3; i++) { await page.waitForTimeout(2000); const hasResults = await pm.tracesPage.hasTraceResults(); const hasNoResults = await pm.tracesPage.isNoResultsVisible(); if (hasResults || hasNoResults) { searchCompleted = true; testLogger.info(`Search completed on attempt ${i + 1}: Results=${hasResults}, NoResults=${hasNoResults}`); break; } } // Verify search completed expect(searchCompleted).toBeTruthy(); // If results exist, verify they match the filter using page object if (await pm.tracesPage.hasTraceResults()) { const serviceNameVisible = await pm.tracesPage.isTextVisibleInResults('api-gateway'); if (serviceNameVisible) { testLogger.info('Results correctly filtered by service name'); } } testLogger.info('Basic trace search completed successfully'); }); // P1 - Core Functionality Tests test("P1: Filter traces by expanding and selecting field values", { tag: ['@traceQueryEditor', '@traces', '@query', '@functional', '@P1', '@all'] }, async ({ page }) => { testLogger.info('Testing field expansion and filtering by clicking field values'); // Initial setup and search await pm.tracesPage.setupTraceSearch(); // Expand status_code field const expanded = await pm.tracesPage.expandTraceField('status_code'); if (expanded) { // Select status_code='2' value const selected = await pm.tracesPage.selectFieldValue('status_code', '2'); if (selected) { // Verify filter appears in query editor using page object const editorContent = await pm.tracesPage.getQueryEditorContent(); if (editorContent.includes('status_code=')) { testLogger.info('Status code filter successfully applied'); } // Run query with filter await pm.tracesPage.runSearch(); await page.waitForTimeout(2000); // Verify query was applied expect(editorContent.includes('status_code') || selected).toBeTruthy(); } else { testLogger.info('Could not select status_code value - field may not have value 2'); // Verify expansion worked expect(expanded).toBeTruthy(); } } else { // Try alternative field - service_name const serviceExpanded = await pm.tracesPage.expandTraceField('service_name'); if (serviceExpanded) { await pm.tracesPage.selectFieldValue('service_name', 'api-gateway'); await pm.tracesPage.runSearch(); await page.waitForTimeout(2000); expect(serviceExpanded).toBeTruthy(); } else { // Verify search still works const hasResults = await pm.tracesPage.hasTraceResults(); const noResults = await pm.tracesPage.isNoResultsVisible(); expect(hasResults || noResults).toBeTruthy(); } } }); // NOTE: "P1: Click on trace result to view span details" test removed per Rule 4 (No duplicates) // This functionality is already covered by: // - "P0: View trace details from search results" in tracesSearch.spec.js // - "P1: Verify trace span attributes and values" test below // - Multiple tests in traceDetails.spec.js // NOTE: "P1: Filter traces by service name from span details" test removed per Rule 4 (No duplicates) // This functionality is already covered by: // - traceDetails.spec.js tests which verify trace detail interactions // - The trace details navigation is validated in other passing tests // P2 - Edge Cases test("P2: View error spans and their details", { tag: ['@traceQueryEditor', '@traces', '@query', '@edge', '@P2', '@all'] }, async ({ page }) => { testLogger.info('Testing error span viewing'); // Setup and run initial search await pm.tracesPage.setupTraceSearch(); // Look for traces with errors using page object const hasErrorTraces = await pm.tracesPage.hasErrorTracesWithPattern(); if (hasErrorTraces) { await pm.tracesPage.clickFirstErrorTrace(); testLogger.info('Clicked on trace with errors'); await page.waitForTimeout(2000); // All three checks below read the waterfall span tree, which only renders // on the 'waterfall' tab. The view opens on the flame graph by default and // the last active tab is persisted, so select waterfall explicitly. await pm.tracesPage.openTraceDetailsTab('waterfall'); // Look for ERROR status in the span details using page object const errorCellVisible = await pm.tracesPage.isErrorStatusVisible(); if (errorCellVisible) { await pm.tracesPage.clickCell('ERROR'); testLogger.info('Found and clicked ERROR status cell'); } // Check for status code 2 (error) using page object const statusCode2Visible = await pm.tracesPage.isStatusCode2Visible(); if (statusCode2Visible) { testLogger.info('Found error status code 2'); } // Verify error trace was opened expect(errorCellVisible || statusCode2Visible || await pm.tracesPage.isTraceDetailsTreeVisible()).toBeTruthy(); } else { testLogger.info('No traces with errors found'); // Verify search completed properly const hasResults = await pm.tracesPage.hasTraceResults(); const noResults = await pm.tracesPage.isNoResultsVisible(); expect(hasResults || noResults).toBeTruthy(); } }); test("P1: Verify trace span attributes and values", { tag: ['@traceQueryEditor', '@traces', '@query', '@functional', '@P1', '@all'] }, async ({ page }) => { testLogger.info('Testing trace span attributes'); // Setup and run search await pm.tracesPage.setupTraceSearch(); // Check if we have results const hasResults = await pm.tracesPage.hasTraceResults(); if (!hasResults) { throw new Error('Precondition failed: No trace results available. Ensure trace data is ingested.'); } // Click on first trace result await pm.tracesPage.clickFirstTraceResult(); // Wait with retry for trace details to render (UI may be slow) let detailsTreeVisible = false; let anyDetailsVisible = false; let clickSuccessful = false; for (let attempt = 0; attempt < 3; attempt++) { await page.waitForTimeout(2000); detailsTreeVisible = await pm.tracesPage.isTraceDetailsTreeVisible(); anyDetailsVisible = await pm.tracesPage.isAnyTraceDetailVisible(); clickSuccessful = await pm.tracesPage.isTraceClickSuccessful(); if (detailsTreeVisible || anyDetailsVisible) { testLogger.info(`Trace details visible on attempt ${attempt + 1}`); break; } // If click was successful, UI may show trace details inline if (clickSuccessful) { testLogger.info(`Trace click successful on attempt ${attempt + 1} - UI may show details inline`); break; } testLogger.info(`Waiting for trace details, attempt ${attempt + 1}`); } // Look for common trace attributes using page object const attributes = ['status_code', 'service_name', 'span_name', 'duration']; let foundAttributes = 0; for (const attr of attributes) { const attrVisible = await pm.tracesPage.isAttributeCellVisible(attr); if (attrVisible) { testLogger.info(`Found attribute: ${attr}`); foundAttributes++; // Try to click on the attribute to see its value await pm.tracesPage.clickAttributeCell(attr); } } // Verify at least some attributes were found, trace details are visible, or click was handled expect(foundAttributes > 0 || detailsTreeVisible || anyDetailsVisible || clickSuccessful).toBeTruthy(); testLogger.info(`Found ${foundAttributes} of ${attributes.length} expected attributes`); }); });