diff --git a/tests/ui-testing/pages/alertsPages/alertsPage.js b/tests/ui-testing/pages/alertsPages/alertsPage.js index 17c1c28e24..56586bb081 100644 --- a/tests/ui-testing/pages/alertsPages/alertsPage.js +++ b/tests/ui-testing/pages/alertsPages/alertsPage.js @@ -1118,15 +1118,16 @@ export class AlertsPage { await this.page.locator(this.locators.alertSubmitButton).click(); await this.page.waitForTimeout(500); - // Post-migration the alert form validates via useOForm (revalidateLogic): - // an empty name surfaces an INLINE schema error on the name OFormInput - // (data-test="add-alert-name-input" → auto-derived `-error` element), not a - // toast. Message is `alerts.nameRequired` = "Alert name is required." — the - // same copy the old toast used; only the delivery (toast → inline) changed. - const nameError = this.page.locator('[data-test="add-alert-name-input-error"]').first(); - await expect(nameError).toBeVisible({ timeout: 10000 }); - await expect(nameError).toContainText('Alert name is required'); - testLogger.info('Invalid alert name validation working'); + // An empty-name Save is rejected with a toast reading "Alert name is required." + // (alerts.nameRequired); the name OFormInput may additionally surface an inline field + // error (add-alert-name-input-error). Assert whichever signal is present — both + // confirm the empty-name save was blocked, which is what this validation verifies. + const nameError = this.page.locator('[data-test="add-alert-name-input-error"]'); + const nameRequiredToast = this.page + .locator('[data-test="o-toast-message"]') + .filter({ hasText: 'Alert name is required.' }); + await expect(nameError.or(nameRequiredToast).first()).toBeVisible({ timeout: 10000 }); + testLogger.info('Invalid alert name validation working (empty-name save blocked)'); // Close with robust handling for dialog backdrops await this._closeAlertWizard(); diff --git a/tests/ui-testing/pages/commonActions.js b/tests/ui-testing/pages/commonActions.js index a64f7ceb74..8168595969 100644 --- a/tests/ui-testing/pages/commonActions.js +++ b/tests/ui-testing/pages/commonActions.js @@ -284,8 +284,11 @@ export class CommonActions { // concurrent CI load the wizard's stream-list fetch can run before registration // completes, so the freshly-ingested stream never renders as an option (the exact // failure seen for auto_playwright_stream). Mirrors initializeAlertTestStream. + // 90s budget to match initializeAlertTestStream — cloud stream registration under + // concurrent full-suite load can exceed 60s; a late-but-present stream avoids the + // downstream "wizard stream dropdown empty" flake. Returns immediately once registered. let registered = false; - for (let i = 0; i < 20 && !registered; i++) { + for (let i = 0; i < 90 && !registered; i++) { const listResp = await this.page.request.get(listUrl, { headers }).catch(() => null); if (listResp && listResp.ok()) { const body = await listResp.json().catch(() => null); @@ -377,9 +380,24 @@ export class CommonActions { // page immediately after this call; if the SPA fetches its stream list before // registration completes, the stale list is cached and the alert wizard's // stream dropdown never shows the stream. + // Registration MUST complete before the caller's page load — the alert wizard's + // stream dropdown is populated from the SPA's stream-list fetch at page load, so a + // stream that registers after that never renders as an option. Budget = 90s (was 15s): + // on the shared cloud alpha org the streams-list registration routinely exceeds 60s + // while the full multi-shard suite ingests concurrently (global-setup budgets 90s for + // the same indexing). Empirically ~40% of the self-seeded alert_e2e_* / alert_import_* + // streams missed a 60s window under full load; 90s matches the proven global-setup cap. + // Returns the instant the stream registers, so the healthy case stays fast. + // + // NON-FATAL on timeout: don't throw. The stream IS ingested and will register shortly; + // the caller does template/folder setup (tens of seconds) then reloads before the + // wizard opens, giving registration extra real time. Throwing here turned a slow-but- + // recoverable registration into a hard test failure in beforeEach. Surface it as a + // warning and let the point-of-use stream selection be the real gate. const listUrl = `${baseUrl}/api/${orgName}/streams?type=logs`; + const maxWaitSeconds = 90; let registered = false; - for (let i = 0; i < 15 && !registered; i++) { + for (let i = 0; i < maxWaitSeconds && !registered; i++) { const listResp = await this.page.request.get(listUrl, { headers }).catch(() => null); if (listResp && listResp.ok()) { const body = await listResp.json().catch(() => null); @@ -388,12 +406,13 @@ export class CommonActions { if (!registered) await this.page.waitForTimeout(1000); } if (!registered) { - throw new Error(`Stream ${streamName} did not appear in streams list within 15s of ingestion`); + testLogger.warn(`Stream ${streamName} not confirmed in streams list within ${maxWaitSeconds}s of ingestion — proceeding; point-of-use selection will retry`, { streamName }); } - testLogger.info(`Successfully initialized stream: ${streamName}`); + testLogger.info(`Initialized stream: ${streamName}`, { registered }); return { streamName, + registered, columns: ['city', 'country', 'status', 'age', 'test_run_id', 'test_timestamp', 'message'] }; }