Files
openobserve/tests/ui-testing/playwright-tests/SDR/regexPatternManagement.spec.js
T
Shrinath RaoandClaude Sonnet 4.6 0929fa5678 test: Sentinel audit fixes + parallelize Metrics E2E shard (~22 min → ~5-7 min) (#12367)
## Summary

Two independent improvements to the Playwright E2E test suite.

---

### Part 1 — Sentinel audit fixes (original PR scope)

Fixes from the Sentinel audit review of PR #12358 (selector drift
stabilization).

- **Dead code removal**: delete `clickJobID()` from `logsPageEP.js`
(unreachable code containing `console.log`); remove unused `chromium`
import
- **Critical fix**: replace `console.log`/`console.error` in
`decryptLogSQL()` with `testLogger` equivalents
- **POM compliance**: replace raw `page.locator()` call in
`alerts-ui-operations.spec.js` `beforeEach` with
`pm.alertsPage.isIncidentsFeatureEnabled()`; add that method to
`alertsPage.js`
- **POM compliance**: move per-test `const pm = new PageManager(page)`
into `beforeEach` in `service-graph.spec.js`; replace raw
`page.waitForLoadState()` with
`pm.serviceGraphPage.waitForGraphReload()`; add `waitForGraphReload()`
to `serviceGraphPage.js`
- **Inline selector cleanup**: move metrics tab inline string literals
in `serviceGraphPage.js` into constructor properties
- **Tagging**: add
`@cipherKeys/@enterprise/@P1/@P2/@negative/@validation` to all 6 tests
in `cipherKeys.spec.js` (were untagged); add `@P1/@P2` to all 5 tests in
`regexPatternManagement.spec.js`
- **ESM fix**: correct `export const path = require('path')` → `import
path from 'path'` in `logoManagementPage.js`
- **Deduplication**: consolidate
`createFirstFunction`/`createSecondFunction` into shared
`createFunction(name, vrlCode)` in `pipelinesEP.js`

---

### Part 2 — Parallelize Metrics E2E shard

**Problem**: The `e2e / Metrics` CI shard was consistently the slowest
job at ~22 minutes per PR run, while all other shards complete in ~5-7
minutes.

**Root cause**: 9 of 11 Metrics spec files used
`test.describe.configure({ mode: 'serial' })` because they shared a
describe-scope `let pm` variable reassigned in `beforeEach`. This forced
every test in those files onto a single Playwright worker, eliminating
all parallelism despite `fullyParallel: true` and 5 workers being
configured.

**Fix applied to all 9 affected files** (`metrics.spec.js`,
`metrics-config.spec.js`, `metrics-config-tabs.spec.js`,
`metrics-visualizations.spec.js`,
`metrics-promql-query-persistence.spec.js`,
`metrics-table-column-order.spec.js`, `metrics-queries.spec.js`,
`metrics-aggregations.spec.js`, `metrics-advanced.spec.js`):
- Remove `test.describe.configure({ mode: 'serial' })`
- Replace shared `let pm` + `beforeEach` pattern with a `setupTest(page,
testInfo)` helper that creates and returns a fresh `PageManager` per
test — the same pattern already used in `metrics-promql-builder.spec.js`
(the one file that was already parallel)
- `metrics-table-column-order.spec.js`: pass `pm` explicitly into
`setupTableChart(pm, page, mode)` since it can no longer close over a
describe-scope variable
- Split consolidated mega-tests (for-loops over multiple independent
scenarios) in `metrics-queries`, `metrics-aggregations`, and
`metrics-advanced` into individual `test()` blocks so Playwright can
distribute them across workers

**No test assertions, selectors, query logic, or validation changed.**

| File | Tests before | Tests after |
|---|---|---|
| `metrics.spec.js` | 10 (serial) | 10 (parallel) |
| `metrics-config.spec.js` | 2 (serial) | 2 (parallel) |
| `metrics-config-tabs.spec.js` | 3 (serial) | 3 (parallel) |
| `metrics-visualizations.spec.js` | 4 (serial) | 4 (parallel) |
| `metrics-promql-query-persistence.spec.js` | 6 (serial) | 6 (parallel)
|
| `metrics-table-column-order.spec.js` | 20 (serial) | 20 (parallel) |
| `metrics-queries.spec.js` | 4 mega-tests (serial) | 12 individual
(parallel) |
| `metrics-aggregations.spec.js` | 3 mega-tests (serial) | 10 individual
(parallel) |
| `metrics-advanced.spec.js` | 4 mega-tests (serial) | 12 individual
(parallel) |

**Expected impact**: ~1300s (~22 min) → ~300-400s (~5-7 min) wall-clock
time.

---

## Test plan
- [ ] All 40 tests in the 4 Sentinel-audit spec files enumerate cleanly
via `npx playwright test --list`
- [ ] Tag filters `@cipherKeys`, `@P2`, `@sdr` match expected tests
- [ ] All Metrics spec files enumerate cleanly with no serial mode
warnings
- [ ] Metrics shard CI time drops to ~5-7 min on next PR run

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 02:31:44 +00:00

151 lines
5.7 KiB
JavaScript

const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js');
const testLogger = require('../utils/test-logger.js');
const PageManager = require('../../pages/page-manager.js');
const testData = require("../../../test-data/sdr_test_data.json");
const path = require('path');
test.describe("Regex Pattern Management Tests", { tag: '@enterprise' }, () => {
test.describe.configure({ mode: 'parallel' });
let pm;
// Generate unique test run ID for isolation
const testRunId = Date.now().toString(36);
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
pm = new PageManager(page);
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {});
testLogger.info('Regex Pattern Management test setup completed');
});
// ===== VALIDATION TESTS =====
test("should disable save button when required fields are empty", {
tag: ['@sdr', '@regexPatterns', '@negative', '@validation', '@P2']
}, async ({ page }) => {
testLogger.info('Testing that save button is disabled when required fields are empty');
await pm.sdrPatternsPage.navigateToRegexPatterns();
// Test 1: Empty pattern name
testLogger.info('Testing empty pattern name');
await pm.sdrPatternsPage.clickAddPattern();
await pm.sdrPatternsPage.patternDescriptionInput.fill('Test description');
await pm.sdrPatternsPage.patternInput.fill('^[A-Z]{5}[0-9]{4}[A-Z]$');
let saveButton = pm.sdrPatternsPage.saveButton;
let isDisabled = await saveButton.isDisabled();
testLogger.info(`Save button disabled (empty name): ${isDisabled}`);
expect(isDisabled).toBeTruthy();
await pm.sdrPatternsPage.cancelButton.click();
await page.waitForTimeout(500);
// Test 2: Empty regex pattern
testLogger.info('Testing empty regex pattern');
await pm.sdrPatternsPage.clickAddPattern();
await pm.sdrPatternsPage.patternNameInput.fill('empty_pattern');
await pm.sdrPatternsPage.patternDescriptionInput.fill('Test description');
saveButton = pm.sdrPatternsPage.saveButton;
isDisabled = await saveButton.isDisabled();
testLogger.info(`Save button disabled (empty pattern): ${isDisabled}`);
expect(isDisabled).toBeTruthy();
});
// ===== ERROR HANDLING TESTS =====
test("should show error when pattern contains invalid regex syntax", {
tag: ['@sdr', '@regexPatterns', '@negative', '@P2']
}, async ({ page }) => {
testLogger.info('Testing invalid regex syntax');
await pm.sdrPatternsPage.navigateToRegexPatterns();
await pm.sdrPatternsPage.clickAddPattern();
await pm.sdrPatternsPage.fillPatternDetails(
'invalid_regex',
'Invalid regex pattern',
'[invalid(regex'
);
await pm.sdrPatternsPage.clickSavePattern();
// Verify save failed: drawer stays open (invalid regex rejected by server)
await pm.sdrPatternsPage.verifyPatternCreationFailed();
testLogger.info('Invalid regex rejected — drawer remained open as expected');
});
test("should handle duplicate pattern name and allow fixing", {
tag: ['@sdr', '@regexPatterns', '@negative', '@P2']
}, async ({ page }) => {
const patternName = `duplicate_test_${testRunId}`;
const fixedPatternName = `duplicate_test_fixed_${testRunId}`;
testLogger.info(`Testing duplicate pattern error and fixing by changing name (pattern: ${patternName})`);
await pm.sdrPatternsPage.navigateToRegexPatterns();
// Create initial pattern
testLogger.info('Creating initial pattern');
await pm.sdrPatternsPage.createPatternWithDeleteIfExists(
patternName,
'First pattern',
'^test$',
pm.streamAssociationPage
);
await pm.sdrPatternsPage.verifyPatternCreatedSuccess();
// Try to create duplicate - should fail
testLogger.info('Attempting to create duplicate pattern');
await pm.sdrPatternsPage.clickAddPattern();
await pm.sdrPatternsPage.fillPatternDetails(
patternName,
'Duplicate pattern',
'^test2$'
);
await pm.sdrPatternsPage.clickSavePattern();
await pm.sdrPatternsPage.verifyDuplicatePatternError();
// Fix by changing name - should succeed
testLogger.info('Fixing duplicate by changing name');
await pm.sdrPatternsPage.patternNameInput.click();
await pm.sdrPatternsPage.patternNameInput.fill(fixedPatternName);
await pm.sdrPatternsPage.clickSavePattern();
await pm.sdrPatternsPage.verifyPatternCreatedSuccess();
});
test("should cancel pattern creation", {
tag: ['@sdr', '@regexPatterns', '@negative', '@P2']
}, async ({ page }) => {
testLogger.info('Testing cancel pattern creation');
await pm.sdrPatternsPage.navigateToRegexPatterns();
await pm.sdrPatternsPage.clickAddPattern();
await pm.sdrPatternsPage.fillPatternDetails(
'cancelled_pattern',
'This will be cancelled',
'^test$'
);
await pm.sdrPatternsPage.cancelButton.click();
await page.waitForTimeout(500);
const patternVisible = await pm.sdrPatternsPage.checkPatternExists('cancelled_pattern');
expect(patternVisible).toBeFalsy();
});
// ===== IMPORT FUNCTIONALITY =====
test("should test import functionality with valid JSON file", {
tag: ['@sdr', '@import', '@all', '@P1']
}, async ({ page }) => {
testLogger.info('Testing regex pattern import');
await pm.sdrPatternsPage.navigateToRegexPatterns();
const importFilePath = path.resolve(__dirname, '../../../test-data/regex_patterns_import.json');
const imported = await pm.sdrPatternsPage.importPatternsFromFile(importFilePath);
expect(imported, 'Import should complete successfully (dialog should close)').toBeTruthy();
testLogger.info('Import completed, verifying imported patterns');
});
});