mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 16:09:05 +00:00
## Summary - Add comprehensive E2E test coverage for RUM sourcemap functionality (26 total tests) - API tests: 20 pytest tests (upload/list/delete/resolve operations + RBAC) - UI tests: 6 Playwright tests for error tracking UI - Helper scripts for RUM data ingestion ## Implementation Details ### API Tests (pytest) - **test_sourcemap_api.py**: 15 tests covering: - Sourcemap upload (multipart/form-data) - List sourcemaps - Delete sourcemaps - Stacktrace resolution with sourcemaps - **test_sourcemap_rbac.py**: 5 tests for role-based access control - **ingest_rum_errors.py**: Helper to inject synthetic RUM error data for UI tests - **ingest_rum_performance.py**: Helper to enable RUM feature in UI ### UI Tests (Playwright) - **sourcemap-ui.spec.js**: 6 tests validating: - Error tracking page loads - Query execution - Error list display - Error detail navigation - Metadata display - Stack trace display - **rumPage.js**: Updated page object with API response interception - Workaround for Vue 3 @click handlers not responding to Playwright clicks - Navigate via URL using error IDs extracted from API responses - Tests run in **parallel mode** with proper assertions (no graceful skips) ## Test Execution ```bash # API tests (pytest) cd tests/api-testing pytest tests/test_sourcemap_api.py -v # 15 tests pytest tests/test_sourcemap_rbac.py -v # 5 tests # UI tests (Playwright) cd tests/ui-testing npx playwright test playwright-tests/RUM/sourcemap-ui.spec.js --workers=3 # 6 tests ``` ## Technical Notes - Vue 3 production builds don't respond to Playwright's synthetic click events on @click handlers - Solution: Intercept API responses to extract error IDs, then navigate directly via URL - All tests use hard assertions - tests fail if data doesn't exist (no graceful skips) --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Shrinath Rao <undefined@users.noreply.github.com>
254 lines
9.4 KiB
JavaScript
254 lines
9.4 KiB
JavaScript
/**
|
|
* Sourcemap UI E2E Tests
|
|
*
|
|
* Tests for RUM Error Tracking UI with sourcemap display.
|
|
*
|
|
* Test Coverage:
|
|
* - Error Tracking page navigation
|
|
* - Date range selection and query execution
|
|
* - Error list display
|
|
* - Error detail view navigation
|
|
* - Error metadata display
|
|
* - Stack trace display
|
|
*
|
|
* Prerequisites:
|
|
* - OpenObserve ENTERPRISE build running on localhost:5080
|
|
* - RUM enabled
|
|
* - RUM errors auto-ingested via global setup (rum-error-ingestion.js)
|
|
*
|
|
* Note:
|
|
* - Tests tagged with @enterprise (enterprise-only feature)
|
|
* - Pretty tab with resolved source code not yet implemented in UI
|
|
* - These tests validate basic error display functionality
|
|
*/
|
|
|
|
const { test, expect } = require('../utils/enhanced-baseFixtures.js');
|
|
const testLogger = require('../utils/test-logger.js');
|
|
const PageManager = require('../../pages/page-manager.js');
|
|
|
|
test.describe("Sourcemap UI Tests", { tag: '@enterprise' }, () => {
|
|
// Run tests in parallel for faster execution
|
|
test.describe.configure({ mode: 'parallel' });
|
|
let pm;
|
|
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
testLogger.testStart(testInfo.title, testInfo.file);
|
|
|
|
// Navigate to base and initialize page manager
|
|
await page.goto(`${process.env.ZO_BASE_URL}?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
|
|
|
|
pm = new PageManager(page);
|
|
|
|
testLogger.info('Test setup completed');
|
|
});
|
|
|
|
// ==========================================================================
|
|
// P0 - BASIC NAVIGATION
|
|
// ==========================================================================
|
|
|
|
test("P0: Error Tracking page loads successfully", {
|
|
tag: ['@sourcemap', '@ui', '@smoke', '@P0']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Navigating to RUM Error Tracking page');
|
|
|
|
// Navigate directly to RUM Errors page
|
|
await page.goto(`${process.env.ZO_BASE_URL}/web/rum/errors?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
|
|
await page.waitForTimeout(2000); // Allow page to stabilize
|
|
|
|
// Verify key elements are visible
|
|
await pm.rumPage.expectErrorTrackingPageLoaded();
|
|
|
|
testLogger.info('✅ Error Tracking page loaded successfully');
|
|
});
|
|
|
|
test("P0: Set date range and run query", {
|
|
tag: ['@sourcemap', '@ui', '@smoke', '@P0']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing query execution with default time range');
|
|
|
|
// Navigate to Error Tracking page
|
|
await page.goto(`${process.env.ZO_BASE_URL}/web/rum/errors?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
|
|
await page.waitForTimeout(2000); // Allow page to stabilize
|
|
|
|
// Use default time range (Past 15 Minutes) which should capture recent errors
|
|
testLogger.info('Clicking Run Query button with default time range');
|
|
await pm.rumPage.clickRunQuery();
|
|
|
|
// Wait for query to execute
|
|
await pm.rumPage.waitForQueryExecution();
|
|
|
|
// Wait for results table
|
|
await pm.rumPage.expectErrorTableVisible();
|
|
|
|
testLogger.info('✅ Query executed successfully');
|
|
});
|
|
|
|
// ==========================================================================
|
|
// P1 - ERROR DISPLAY
|
|
// ==========================================================================
|
|
|
|
test("P1: Error list displays errors", {
|
|
tag: ['@sourcemap', '@ui', '@functional', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Verifying error list displays errors');
|
|
|
|
// Navigate to Error Tracking page (uses default 15 min time range)
|
|
await page.goto(`${process.env.ZO_BASE_URL}/web/rum/errors?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
|
|
await page.waitForTimeout(2000); // Allow page to stabilize
|
|
|
|
// Run query with default time range
|
|
await pm.rumPage.clickRunQuery();
|
|
await pm.rumPage.waitForQueryExecution();
|
|
|
|
// Wait for table to load and verify errors exist
|
|
const hasErrors = await pm.rumPage.hasErrorRows();
|
|
expect(hasErrors).toBe(true); // Fail if no errors found
|
|
|
|
// Count rows
|
|
const rowCount = await pm.rumPage.getErrorRowCount();
|
|
testLogger.info(`Found ${rowCount} error rows`);
|
|
expect(rowCount).toBeGreaterThan(0); // Fail if no rows
|
|
|
|
if (rowCount > 0) {
|
|
// Verify first row has expected content
|
|
const rowText = await pm.rumPage.getFirstErrorRowText();
|
|
testLogger.info(`First row text: ${rowText}`);
|
|
|
|
// Should contain error-related text
|
|
const hasErrorText = rowText.includes('Error') ||
|
|
rowText.includes('TypeError') ||
|
|
rowText.includes('ReferenceError') ||
|
|
rowText.includes('o2-sourcemap-test-app');
|
|
|
|
if (hasErrorText) {
|
|
testLogger.info('✅ Error list displays errors with expected content');
|
|
} else {
|
|
testLogger.warn('⚠️ Error list displayed but content may not be from test app');
|
|
}
|
|
}
|
|
});
|
|
|
|
test("P1: Click error opens detail view", {
|
|
tag: ['@sourcemap', '@ui', '@functional', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing error detail view navigation');
|
|
|
|
// Navigate and load errors (uses default 15 min time range)
|
|
await page.goto(`${process.env.ZO_BASE_URL}/web/rum/errors?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
|
|
await page.waitForTimeout(2000); // Allow page to stabilize
|
|
|
|
await pm.rumPage.clickRunQuery();
|
|
await pm.rumPage.waitForQueryExecution();
|
|
|
|
// Wait for table and verify errors exist
|
|
const hasErrors = await pm.rumPage.hasErrorRows();
|
|
expect(hasErrors).toBe(true); // Fail if no errors found
|
|
|
|
// Click first error row
|
|
testLogger.info('Clicking first error row');
|
|
await pm.rumPage.clickFirstErrorRow();
|
|
|
|
// Wait for navigation
|
|
await page.waitForURL('**/rum/errors/view/**', { timeout: 10000 }).catch(() => {
|
|
testLogger.warn('URL did not change to error detail view');
|
|
});
|
|
|
|
// Wait for detail view to load
|
|
await pm.rumPage.expectErrorDetailViewLoaded();
|
|
|
|
testLogger.info('✅ Error detail view opened successfully');
|
|
});
|
|
|
|
test("P1: Error detail shows metadata", {
|
|
tag: ['@sourcemap', '@ui', '@functional', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Verifying error detail metadata display');
|
|
|
|
// Navigate to error detail (uses default 15 min time range)
|
|
await page.goto(`${process.env.ZO_BASE_URL}/web/rum/errors?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
|
|
await page.waitForTimeout(2000); // Allow page to stabilize
|
|
|
|
await pm.rumPage.clickRunQuery();
|
|
await pm.rumPage.waitForQueryExecution();
|
|
|
|
const hasErrors = await pm.rumPage.hasErrorRows();
|
|
expect(hasErrors).toBe(true); // Fail if no errors found
|
|
|
|
await pm.rumPage.clickFirstErrorRow();
|
|
await pm.rumPage.expectErrorDetailViewLoaded();
|
|
|
|
// Verify metadata sections
|
|
testLogger.info('Checking for metadata sections');
|
|
|
|
// Tags section
|
|
await pm.rumPage.expectTagsSectionVisible();
|
|
|
|
// Get container text
|
|
const containerText = await pm.rumPage.getErrorViewerContainerText();
|
|
|
|
// Verify common metadata fields
|
|
const hasMetadata = containerText.includes('browser') ||
|
|
containerText.includes('Browser') ||
|
|
containerText.includes('ip') ||
|
|
containerText.includes('IP') ||
|
|
containerText.includes('service');
|
|
|
|
if (hasMetadata) {
|
|
testLogger.info('✅ Error metadata displayed correctly');
|
|
} else {
|
|
testLogger.warn('⚠️ Expected metadata fields not found');
|
|
}
|
|
});
|
|
|
|
test("P1: Stack trace displays frames", {
|
|
tag: ['@sourcemap', '@ui', '@functional', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Verifying stack trace display');
|
|
|
|
// Navigate to error detail (uses default 15 min time range)
|
|
await page.goto(`${process.env.ZO_BASE_URL}/web/rum/errors?org_identifier=${process.env.ORGNAME}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
|
|
await page.waitForTimeout(2000); // Allow page to stabilize
|
|
|
|
await pm.rumPage.clickRunQuery();
|
|
await pm.rumPage.waitForQueryExecution();
|
|
|
|
const hasErrors = await pm.rumPage.hasErrorRows();
|
|
expect(hasErrors).toBe(true); // Fail if no errors found
|
|
|
|
await pm.rumPage.clickFirstErrorRow();
|
|
await pm.rumPage.expectErrorDetailViewLoaded();
|
|
|
|
// Look for stack trace section
|
|
testLogger.info('Looking for stack trace section');
|
|
|
|
await pm.rumPage.expectStackTraceVisible();
|
|
|
|
// Get stack trace text
|
|
const frameText = await pm.rumPage.getStackTraceText();
|
|
|
|
if (frameText) {
|
|
testLogger.info(`Stack trace text: ${frameText.substring(0, 200)}...`);
|
|
|
|
// Verify stack frame format
|
|
const hasStackFrames = frameText.includes('at ') &&
|
|
(frameText.includes('.js') || frameText.includes('http')) &&
|
|
frameText.match(/:\d+:\d+/); // line:column format
|
|
|
|
if (hasStackFrames) {
|
|
testLogger.info('✅ Stack trace displays frames correctly');
|
|
} else {
|
|
testLogger.warn('⚠️ Stack trace present but format unexpected');
|
|
}
|
|
} else {
|
|
testLogger.warn('⚠️ Stack trace section not visible');
|
|
}
|
|
});
|
|
});
|