test(autoe2e): generated E2E tests for #15614 (first-event-status) (#15661)

Co-authored-by: Shrinath-O2 <214651540+Shrinath-O2@users.noreply.github.com>
This commit is contained in:
Shrinath Rao
2026-10-24 05:49:12 +00:00
committed by GitHub
co-authored by Shrinath-O2
parent 9c0995435c
commit df9a274df3
4 changed files with 174 additions and 1 deletions
+2 -1
View File
@@ -534,7 +534,8 @@
"connectDataSourcePopup.spec.js",
"splunk-hec-ingestion-window.spec.js",
"enrichment.spec.js",
"schema.spec.js"
"schema.spec.js",
"first-event-status.spec.js"
],
"disabled": []
},
@@ -10,6 +10,9 @@ export class FirstEventPage {
this.firstRecord = page.locator('[data-test="first-event-status-first-record"]');
this.openBtn = page.locator('[data-test="first-event-status-open-btn"]');
this.troubleshootBtn = page.locator('[data-test="first-event-status-troubleshoot-btn"]');
this.checkNowBtn = page.locator('[data-test="first-event-status-check-now-btn"]');
this.connectSourceBtn = page.locator('[data-test="first-event-status-connect-source-btn"]');
this.setupAnotherBtn = page.locator('[data-test="first-event-status-setup-another-btn"]');
this.diagnosis = page.locator('[data-test="first-event-diagnosis"]');
this.diagnosisFix = page.locator('[data-test="first-event-diagnosis-fix"]');
this.diagnosisCopyBtn = page.locator('[data-test="first-event-diagnosis-copy-btn"]');
@@ -17,6 +20,7 @@ export class FirstEventPage {
this.diagnosisEndpoint = page.locator('[data-test="first-event-diagnosis-endpoint"]');
this.stillStuck = page.locator('[data-test="first-event-diagnosis-still-stuck"]');
this.docsLink = page.locator('[data-test="first-event-diagnosis-docs-link"]');
this.askAiBtn = page.locator('[data-test="first-event-diagnosis-ask-ai-btn"]');
this.contactSupportLink = page.locator('[data-test="first-event-diagnosis-contact-support-link"]');
this.notice = page.locator('[data-test="first-data-notice"]');
this.noticeText = page.locator('[data-test="first-data-notice-text"]');
@@ -48,6 +52,10 @@ export class FirstEventPage {
await this.troubleshootBtn.click();
}
async clickCheckNow() {
await this.checkNowBtn.click();
}
async clickOpen() {
await this.openBtn.click();
}
@@ -0,0 +1,108 @@
const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js');
const testLogger = require('../utils/test-logger.js');
const { FirstEventPage } = require('../../pages/generalPages/firstEventPage.js');
const {
ingest,
withFreshOrg,
targetProbe,
waitForStreamWithRecords,
} = require('../utils/first-event-helpers.js');
test.describe('Ingestion First-Event Status testcases', () => {
test.describe.configure({ mode: 'parallel' });
test.beforeEach(async ({ page }, testInfo) => {
testLogger.testStart(testInfo.title, testInfo.file);
await navigateToBase(page);
testLogger.info('Test setup completed');
});
test('turns green on the curl test guide with test-kind markers and "Connect your real source"', {
tag: ['@first-event-status', '@ingestion', '@all', '@P0'],
}, async ({ page }) => {
testLogger.info('Opening the curl test guide on a fresh org');
await withFreshOrg(page, 'fe_curl', async (orgId) => {
const fe = new FirstEventPage(page);
const baseline = targetProbe(page, 'logs', 'default');
await fe.openGuide('/ingestion/custom/logs/curl', orgId);
await fe.expectState('waiting');
await expect(fe.bar).toHaveAttribute('data-kind', 'test');
await baseline;
await ingest(page, orgId, 'default', [{ level: 'info', log: 'first event e2e curl' }]);
await fe.expectState('received', 15000);
await expect(fe.bar).toHaveAttribute('data-kind', 'test');
await expect(fe.summary).toContainText('default');
await expect(fe.firstRecord).toContainText('first event e2e curl');
await expect(fe.openBtn).toHaveText(/Open in Logs/);
await expect(fe.connectSourceBtn).toBeVisible();
await expect(fe.connectSourceBtn).toContainText('Connect your real source');
await expect(fe.setupAnotherBtn).toHaveCount(0);
testLogger.info('Curl test guide turned green with test-kind markers');
});
});
test.fixme('idle state unreachable in e2e: useFirstEventWatch.ts:241 hasRecords reads stream-list doc_num (no SQL COUNT fallback unlike confirmNewStream), which stays 0 until the 10-min memtable TTL flush', {
tag: ['@first-event-status', '@ingestion', '@all', '@P1'],
}, async ({ page }) => {
testLogger.info('Ingesting before mount so the baseline probe sees an existing stream');
await withFreshOrg(page, 'fe_idle', async (orgId) => {
await ingest(page, orgId, 'default', [{ level: 'info', log: 'pre-existing record' }]);
expect(await waitForStreamWithRecords(page, orgId, 'default')).toBe(true);
const fe = new FirstEventPage(page);
await fe.openGuide('/ingestion/custom/logs/curl', orgId);
await fe.expectState('idle');
await expect(fe.checkNowBtn).toBeVisible();
await expect(fe.troubleshootBtn).toHaveCount(0);
await fe.clickCheckNow();
await fe.expectState('waiting');
await expect(fe.checkNowBtn).toHaveCount(0);
await ingest(page, orgId, 'default', [{ level: 'info', log: 'second record after check now' }]);
await fe.expectState('received', 15000);
await expect(fe.summary).toContainText('default');
testLogger.info('Idle held on pre-existing data, then Check now engaged and received');
});
});
test('Troubleshoot on a fresh tracked org shows no-requests immediately with org id and endpoint', {
tag: ['@first-event-status', '@ingestion', '@all', '@P1'],
}, async ({ page }) => {
testLogger.info('Opening the fluentbit guide on a fresh, data-free org');
await withFreshOrg(page, 'fe_noreq', async (orgId) => {
const fe = new FirstEventPage(page);
await fe.openGuide('/ingestion/custom/logs/fluentbit', orgId);
await fe.expectState('waiting');
await fe.clickTroubleshoot();
await fe.expectState('no-requests');
await fe.expectDiagnosis({ trigger: 'troubleshoot' });
await expect(fe.summary).toContainText('Nothing has arrived');
await expect(fe.diagnosisOrgId).toHaveText(orgId);
await expect(fe.diagnosisEndpoint).not.toHaveText('');
await expect(fe.diagnosisFix).toHaveText('Check that your command uses this org id and endpoint.');
await expect(fe.diagnosisCopyBtn).toBeVisible();
await expect(fe.stillStuck).toContainText('Still stuck?');
await expect(fe.docsLink).toBeVisible();
await expect(fe.docsLink).toHaveAttribute('target', '_blank');
await expect(fe.troubleshootBtn).toHaveCount(0);
testLogger.info('Troubleshoot showed no-requests immediately');
});
});
test.fixme('Ask AI button — not wired in OSS: FirstEventStatus.vue:360 requires enterprise + ai_enabled', {
tag: ['@first-event-status', '@all', '@P2'],
}, async ({ page }) => {
testLogger.info('Ask AI CTA in the Still stuck row (enterprise + ai_enabled only)');
await withFreshOrg(page, 'fe_askai', async (orgId) => {
const fe = new FirstEventPage(page);
await fe.openGuide('/ingestion/custom/logs/fluentbit', orgId);
await fe.expectState('waiting');
await fe.clickTroubleshoot();
await fe.expectState('no-requests');
await expect(fe.askAiBtn).toBeVisible();
testLogger.info('Ask AI button visible after diagnosis');
});
});
});
@@ -0,0 +1,56 @@
const { test, expect } = require('@playwright/test');
const { getAuthHeaders, authedRequest, isCloudEnvironment } = require('./cloud-auth.js');
const base = () => process.env.ZO_BASE_URL.replace(/\/$/, '');
const ingestionBase = () => (process.env.INGESTION_URL || process.env.ZO_BASE_URL).replace(/\/$/, '');
async function ingest(page, orgId, stream, records) {
const res = await page.request.post(`${ingestionBase()}/api/${orgId}/${stream}/_json`, {
headers: getAuthHeaders(),
data: records,
});
expect(res.ok()).toBeTruthy();
}
async function withFreshOrg(page, tag, fn) {
const created = await authedRequest(page, 'post', `${base()}/api/organizations`, {
data: { name: `e2e_${tag}_${Date.now()}` },
});
// Cloud creates orgs only through signup, so a non-200 there means this env cannot stage the scenario.
test.skip(isCloudEnvironment() && !created.ok(), 'Cloud creates orgs only through signup');
expect(created.ok()).toBeTruthy();
const body = await created.json();
const orgId = body.identifier || (body.data && body.data.identifier);
try {
await fn(orgId);
} finally {
await authedRequest(page, 'delete', `${base()}/api/organizations/${orgId}`).catch(() => {});
}
}
// The bar's baseline probe for its own stream, so an ingest never lands before the watch has looked.
const targetProbe = (page, type, stream) =>
page.waitForResponse(
(r) => r.url().includes(`/streams?type=${type}&offset=0&limit=20&keyword=${stream}`),
{ timeout: 20000 },
);
// The watcher's idle gate reads doc_num from the stream list (not search), so wait for it to be non-zero.
async function waitForStreamWithRecords(page, orgId, streamName, type = 'logs', maxWaitMs = 30000) {
const start = Date.now();
while (Date.now() - start < maxWaitMs) {
const res = await page.request.get(`${ingestionBase()}/api/${orgId}/streams?type=${type}`, {
headers: getAuthHeaders(),
});
if (res.ok()) {
const data = await res.json().catch(() => null);
const list = Array.isArray(data?.list) ? data.list : [];
const row = list.find((s) => s?.name === streamName);
if (row && Number(row.stats?.doc_num ?? 0) > 0) return true;
}
await new Promise((resolve) => setTimeout(resolve, 2000));
}
return false;
}
module.exports = { ingest, withFreshOrg, targetProbe, waitForStreamWithRecords };