diff --git a/tests/ui-testing/pages/alertsPages/alertsPage.js b/tests/ui-testing/pages/alertsPages/alertsPage.js index f421884bac..d5f2aa5ddb 100644 --- a/tests/ui-testing/pages/alertsPages/alertsPage.js +++ b/tests/ui-testing/pages/alertsPages/alertsPage.js @@ -1221,6 +1221,21 @@ export class AlertsPage { * Note: Incidents menu item depends on service_graph_enabled config from the server. * The menu item may take time to render while the config API response is processed. */ + + /** + * Returns true if the incidents sidebar menu item becomes visible within the given timeout. + * Use this in beforeEach to skip enterprise tests on OSS environments. + * @param {number} timeout - ms to wait (default 5000) + */ + async isIncidentsFeatureEnabled(timeout = 5000) { + try { + await this.page.locator(this.locators.incidentsMenuItem).waitFor({ state: 'visible', timeout }); + return true; + } catch { + return false; + } + } + async navigateToIncidentsPage() { testLogger.info('Navigating to Incidents page'); // Wait for the incidents menu item to be available (may take time for config to load) diff --git a/tests/ui-testing/pages/generalPages/logoManagementPage.js b/tests/ui-testing/pages/generalPages/logoManagementPage.js index 3bea23122d..511edd4e33 100644 --- a/tests/ui-testing/pages/generalPages/logoManagementPage.js +++ b/tests/ui-testing/pages/generalPages/logoManagementPage.js @@ -1,6 +1,5 @@ import { expect } from '@playwright/test'; - -export const path = require('path'); +import path from 'path'; export class LogoManagementPage { diff --git a/tests/ui-testing/pages/logsPages/logsPage.js b/tests/ui-testing/pages/logsPages/logsPage.js index e1b3367ee3..aee3f2ef5c 100644 --- a/tests/ui-testing/pages/logsPages/logsPage.js +++ b/tests/ui-testing/pages/logsPages/logsPage.js @@ -6463,6 +6463,18 @@ export class LogsPage { } } + /** + * Wait for actual result rows to appear in the table. + * The table container becomes visible before Vue populates queryResults.hits, which gates + * the analyze button v-if. Waiting for a row ensures hits.length > 0 is true in the + * reactive store before callers check for the analyze button or row-dependent UI. + */ + async waitForSearchResultRows(timeout = 20000) { + await this.page.locator(`${this.logsTable} tbody tr`).first() + .waitFor({ state: 'visible', timeout }); + testLogger.info('waitForSearchResultRows: at least one result row is visible'); + } + /** * Wait for SQL mode to be active after switching */ diff --git a/tests/ui-testing/pages/logsPages/logsPageEP.js b/tests/ui-testing/pages/logsPages/logsPageEP.js index 0c213468a0..89c0f4fb5a 100644 --- a/tests/ui-testing/pages/logsPages/logsPageEP.js +++ b/tests/ui-testing/pages/logsPages/logsPageEP.js @@ -1,6 +1,6 @@ // logsPageEP.js import { expect } from '@playwright/test'; -const { chromium } = require('playwright'); +const testLogger = require('../../playwright-tests/utils/test-logger.js'); export class LogsPageEP { constructor(page) { @@ -96,49 +96,6 @@ async queryJobSearch() { -async clickJobID () { - const { getOrgIdentifier } = require('../../playwright-tests/utils/cloud-auth.js'); - const orgId = getOrgIdentifier(); - - // Intercept the network request and capture the response - await this.page.route( - `${process.env["ZO_BASE_URL"]}/api/${orgId}/search_jobs?type=logs&search_type=UI&use_cache=true`, - async (route) => { - const response = await route.continue(); - const responseBody = await response.body(); - const jsonResponse = JSON.parse(responseBody.toString()); - - // Assuming the ID is in the response JSON - const idJob = jsonResponse.trace_id; // Adjust this according to your response structure - console.log("Job ID Created", idJob); - - // Use the ID to construct the selector - const rowSelector = `tr[data-test="search-scheduler-table-${idJob}-row"]`; - const cancelBtnSelector = `${rowSelector} [data-test="search-scheduler-cancel-btn"]`; - const restartBtnSelector = `${rowSelector} [data-test="search-scheduler-restart-btn"]`; - - // Wait for the row to be visible before clicking - await this.page.waitForSelector(rowSelector); - - // Click the cancel button - await this.page.locator(cancelBtnSelector).click(); - await this.page.waitForTimeout(2000); - // Click the confirm button - await this.page.locator('[data-test="confirm-dialog"] [data-test="o-dialog-primary-btn"]').click(); - await this.page.waitForTimeout(2000); - // Click the restart button - await this.page.locator(restartBtnSelector).click(); - await this.page.waitForTimeout(2000); - // Continue with your automation steps... - } - ) - -} - - - - - async searchSchedulerInvalid() { // OInput: use the inner native input (-field suffix) for fill/press operations @@ -174,12 +131,12 @@ async decryptLogSQL(cipherName) { // Wait for the locator to be visible and enabled const editorLocator = this.page.locator('[data-test="logs-search-bar-query-editor-input"]'); await editorLocator.waitFor({ state: 'visible', timeout: 10000 }); - console.log("SQL Query", sqlQuery); + testLogger.info(`SQL Query: ${sqlQuery}`); // Fill the query editor with the constructed SQL query await editorLocator.fill(sqlQuery); await this.page.waitForTimeout(5000); } catch (error) { - console.error('Error during SQL decryption process:', error); + testLogger.error(`Error during SQL decryption process: ${error}`); throw error; // Re-throw the error if needed for further handling } } diff --git a/tests/ui-testing/pages/pipelinesPages/pipelinesEP.js b/tests/ui-testing/pages/pipelinesPages/pipelinesEP.js index 2e11b7f58a..90897ed834 100644 --- a/tests/ui-testing/pages/pipelinesPages/pipelinesEP.js +++ b/tests/ui-testing/pages/pipelinesPages/pipelinesEP.js @@ -131,42 +131,25 @@ export class PipelinesEP { await this.page.keyboard.type(functionName, { delay: 30 }); } - async createFirstFunction(functionName) { - // Wait for the button to be visible + async createFunction(functionName, vrlCode) { await this.createFunctionButton.waitFor({ state: 'visible' }); - // Click the button await this.createFunctionButton.click(); await this._fillFunctionName(functionName); - // Set VRL function code via Monaco API (more reliable than keyboard typing) - await this.setMonacoEditorValue('.a=41'); - + await this.setMonacoEditorValue(vrlCode); await this.page.locator(this.saveFunctionButton).waitFor({ state: 'visible', timeout: 10000 }); await this.page.locator(this.saveFunctionButton).click(); - // Wait for success notification instead of a fixed timeout await expect( this.page.locator(this.notifyContainer).filter({ hasText: 'Function saved successfully' }).first() ).toBeVisible({ timeout: 15000 }); - // Wait for function list to reload await this.createFunctionButton.waitFor({ state: 'visible', timeout: 10000 }); } - async createSecondFunction(functionName) { - // Wait for the button to be visible - await this.createFunctionButton.waitFor({ state: 'visible' }); - // Click the button - await this.createFunctionButton.click(); - await this._fillFunctionName(functionName); - // Set VRL function code via Monaco API (more reliable than keyboard typing) - await this.setMonacoEditorValue('.test=2'); + async createFirstFunction(functionName) { + await this.createFunction(functionName, '.a=41'); + } - await this.page.locator(this.saveFunctionButton).waitFor({ state: 'visible', timeout: 10000 }); - await this.page.locator(this.saveFunctionButton).click(); - // Wait for success notification instead of a fixed timeout - await expect( - this.page.locator(this.notifyContainer).filter({ hasText: 'Function saved successfully' }).first() - ).toBeVisible({ timeout: 15000 }); - // Wait for function list to reload - await this.createFunctionButton.waitFor({ state: 'visible', timeout: 10000 }); + async createSecondFunction(functionName) { + await this.createFunction(functionName, '.test=2'); } diff --git a/tests/ui-testing/pages/tracesPages/serviceGraphPage.js b/tests/ui-testing/pages/tracesPages/serviceGraphPage.js index 00b68428fe..25a621133d 100644 --- a/tests/ui-testing/pages/tracesPages/serviceGraphPage.js +++ b/tests/ui-testing/pages/tracesPages/serviceGraphPage.js @@ -52,6 +52,14 @@ export class ServiceGraphPage { this.podsPanel = '[data-test="service-graph-side-panel-pods"]'; this.podsTable = '[data-test="service-graph-side-panel-pods-table"]'; + // ===== TELEMETRY CORRELATION (Metrics tab) ===== + this.metricsTab = '[data-test="service-graph-node-panel-tab-metrics"]'; + this.metricsPanel = '[data-test="service-graph-side-panel-metrics"]'; + this.metricsLoadingIndicator = '[data-test="service-graph-side-panel-metrics-loading"]'; + this.metricsDashboard = '[data-test="service-graph-side-panel-metrics-dashboard"]'; + this.metricsError = '[data-test="service-graph-side-panel-metrics-error"]'; + this.metricsEmpty = '[data-test="service-graph-side-panel-metrics-empty"]'; + // ===== TELEMETRY CORRELATION DIALOG ===== this.correlationDashboardClose = '[data-test="correlation-dashboard-close"]'; this.correlationDashboardCard = '[data-test*="correlation-dashboard"]'; @@ -100,6 +108,10 @@ export class ServiceGraphPage { await this.page.locator(this.refreshButton).click(); } + async waitForGraphReload() { + await this.page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => {}); + } + async switchToGraphView() { await this.page.locator(this.graphViewTab).click(); // OToggleGroupItem uses inheritAttrs:false + v-bind="$attrs" on the inner Reka UI