Files
openobserve/tests/ui-testing/playwright-alpha1.config.js
T
3906bdc296 test: stabilize alpha1 Alerts shard on cloud (fix auto_playwright_stream being-deleted race + concurrency hardening) (#13209)
## What

Stabilizes the **Alerts** shard of the alpha1 cloud E2E suite to green,
running **fully
parallel** (`workers:3`, `fullyParallel:true`) — no serialization.
Test-infra +
page-object changes only; **no product code**. The one product-side
defect surfaced (the
prebuilt destination form unmounts its fields on template auto-load) is
worked around in
the test layer and written up separately for a frontend fix.

## Approach change since the first revision

The first version serialized the contended tests (`@serialAlpha1` + a
dedicated
`chromium-serial` project). Per review feedback ("solve it in
parallel"), that was
**removed** — the shard now runs **fully parallel**, and the flakiness
is fixed at the
source (fixture / registration / render races) instead of by
serializing.

## Root causes & fixes

### Streams / fixtures
- **`auto_playwright_stream` "being deleted" race** — the 3
`alerts-ui-operations` tests
now use the shared, always-protected **`e2e_automate`** stream
(pre-ingested by
global-setup, never deleted by cleanup), eliminating the shared
fixed-name delete race
entirely. `cleanup.spec.js` no longer deletes `auto_playwright_stream`;
`commonActions.ingestTestData` checks `response.ok()`, retries the "is
being deleted"
race, and polls the streams API until the stream is registered before
the wizard opens.

### Alert wizard
- **Dedup alert blocked at save** —
`createScheduledAlertWithDeduplication` typed its SQL
into the Monaco editor without clearing the default `SELECT * FROM
"<stream>"`, producing
malformed SQL → "Please fix the SQL error before saving." Now clears the
editor first.
- **Stream-select ordering + registration poll** — drive the stream
dropdown only after its
list fetch resolves; poll registration; page-reload recovery when the
cached list is stale.

### Prebuilt destinations (the deep one)
- **App defect:** on a *reused* form (2nd+ destination), selecting a
prebuilt type
intermittently **unmounts the credential/name fields ~0.5 s later**
(template auto-load
re-render) and they stay gone. Confirmed via DOM timeline (`count:0` for
25 s+). Since a
wait can't recover an unmounted element, `selectDestinationType` clicks
the card once,
waits for the template network to settle, confirms the fields are
**stable** (not just
present), and — if they unmount — **remounts the form** (cancel+reopen →
full page reload)
  and re-selects. Written up separately for a frontend fix.
- **List verifications raced the OTable loading skeleton** —
`expectDestinationInList` / `verifyDestinationExists` /
`verifyAlertCreated` now wait for
`o2-table-skeleton-body` to clear and poll the **list** endpoint the UI
actually reads
  (not just the single-GET) before asserting the row.

### Form-validation specs
- `alerts-form-validation` + `anomaly-detection-form-validation` flipped
serial→parallel and
made self-sufficient: `beforeAll` seeds a worker-scoped
template+destination via API (the
"Add Alert" button is hard-disabled with zero destinations), `afterAll`
removes them,
  cleanup patterns registered.

### Misc
- Cloud-correct (https) ingestion agent; alert search-input +
`updateAlert` edit-form wait
  hardening for shared-org load.

## Cloud environment gates (legitimate — not coverage drops)

`test.skip(isCloudEnvironment())` (dedup validation), `if
(!isCloudEnvironment())` (import
trigger round-trip), and external destination URLs — all because cloud's
**SSRF guard**
blocks self-referencing destinations by design. All still run fully on
OSS/self-hosted.

## Scope / safety

- 15 files: Alerts page-objects, alpha1 config + global-setup, Alerts
specs, cleanup. **No product code.**
- **No tests deleted, no `.only`, no unconditional skips, no assertions
removed.**
- The temporary Alerts-only playwright matrix (added for faster
iteration) has been
  **reverted** — the full OSS e2e matrix is restored.

## Validation

- Prebuilt P1 validated locally on cloud alpha (repeated clean passes;
every field-unmount
  recovered by the remount).
- alpha1 Alerts CI:
https://github.com/openobserve/o2-enterprise/actions/runs/29391293698


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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Shrinath Rao <shnath@openobserve.ai>
2026-07-17 07:04:48 +00:00

113 lines
4.1 KiB
JavaScript

// @ts-check
const { defineConfig, devices } = require('@playwright/test');
const path = require('path');
const dotenv = require('dotenv');
const testLogger = require('./playwright-tests/utils/test-logger.js');
// Mark this as a cloud environment for page objects and auth helpers
process.env.IS_CLOUD = 'true';
// Load environment variables from .env file
const envResult = dotenv.config();
if (envResult.error) {
testLogger.warn('No .env file found, using system environment variables');
} else {
testLogger.info('Environment variables loaded from .env file');
}
// Check alpha1-specific environment variables
if (!process.env.ZO_BASE_URL) {
testLogger.warn('ZO_BASE_URL not set. Must be provided for alpha1 cloud tests.');
throw new Error('ZO_BASE_URL must be set to run alpha1 tests');
}
if (!process.env.ALPHA1_USER_EMAIL || !process.env.ALPHA1_USER_PASSWORD) {
testLogger.warn('ALPHA1_USER_EMAIL and ALPHA1_USER_PASSWORD must be set for Dex email login');
}
// Set ZO_ROOT_USER_* as fallbacks from ALPHA1_* env vars so that spec files
// and utility modules that reference ZO_ROOT_USER_EMAIL/PASSWORD work on cloud
if (!process.env.ZO_ROOT_USER_EMAIL && process.env.ALPHA1_USER_EMAIL) {
process.env.ZO_ROOT_USER_EMAIL = process.env.ALPHA1_USER_EMAIL;
}
if (!process.env.ZO_ROOT_USER_PASSWORD && process.env.ALPHA1_USER_PASSWORD) {
process.env.ZO_ROOT_USER_PASSWORD = process.env.ALPHA1_USER_PASSWORD;
}
// On cloud, INGESTION_URL must point to the alpha base URL (not localhost from .env)
if (!process.env.INGESTION_URL || process.env.INGESTION_URL.includes('localhost')) {
process.env.INGESTION_URL = process.env.ZO_BASE_URL;
testLogger.info(`INGESTION_URL set to ZO_BASE_URL: ${process.env.ZO_BASE_URL}`);
}
// ORGNAME comes from .env. global-setup-alpha1.js performs a UI org-switch
// post-login so the Pinia store binds API calls to this org, and writes
// cloud-config.json with the matching passcode for ingestion.
testLogger.info(`ORGNAME from .env: ${process.env.ORGNAME}`);
/**
* Alpha1 Cloud Playwright Configuration
* Uses Dex "Continue with Email" login flow
*/
// Shared browser context for the chromium project.
const CHROME_USE = {
...devices['Desktop Chrome'],
viewport: { width: 1500, height: 1024 },
permissions: ['clipboard-read', 'clipboard-write'],
// Reuse auth state from global setup (Dex email login)
storageState: path.join(__dirname, 'playwright-tests/utils/auth/user.json'),
};
module.exports = defineConfig({
testDir: './playwright-tests',
testMatch: ['**/*.spec.js'],
outputDir: './test-results',
testIgnore: ['**/test-archives/**', '**/*_old.js'],
// Custom global setup for Dex email login
globalSetup: './playwright-tests/utils/global-setup-alpha1.js',
globalTeardown: './playwright-tests/utils/global-teardown.js',
fullyParallel: true,
forbidOnly: !!process.env.CI,
// The heavy Alerts specs contend on the shared alpha org's slow list fetches under
// concurrent load. Rather than serialize them, they run fully in parallel and rely on
// (a) the page-object resilience (bounded retry loops + settle waits for slow stream/
// destination/template dropdown fetches) and (b) this suite-wide retry to absorb any
// residual contention flake. Only failing tests retry, so passing runs are unaffected.
retries: process.env.CI ? 2 : 0,
workers: 3,
reporter: process.env.CI
? [
['blob', { outputDir: 'blob-report' }],
]
: [
['html', { outputFolder: 'playwright-results/html-report', open: 'never' }],
['json', { outputFile: 'playwright-results/report.json' }]
],
use: {
baseURL: process.env.ZO_BASE_URL,
trace: 'on-first-retry',
navigationTimeout: process.env.CI ? 90000 : 30000,
actionTimeout: process.env.CI ? 45000 : 15000,
screenshot: process.env.CI ? 'only-on-failure' : 'off',
video: process.env.CI ? 'retain-on-failure' : 'off',
},
timeout: process.env.CI ? 5 * 60 * 1000 : 3 * 60 * 1000,
expect: {
timeout: process.env.CI ? 30000 : 10000,
},
projects: [
{
// Single project — every test runs fully in parallel.
name: 'chromium',
use: CHROME_USE,
},
],
});