Files
openobserve/tests/ui-testing/playwright-tests/utils/global-setup.js
T
Shrinath Rao f61b715a1e test: Cleanup Mechanism and ENV Runs for e2e tests (#8839)
### **PR Type**
Enhancement, Tests


___

### **Description**
- Add robust alerts cleanup utilities

- Improve UI flows and resiliency

- Support deployed env testing delays

- Add CI cleanup and health checks


___

### Diagram Walkthrough


```mermaid
flowchart LR
  UI["Playwright UI flows hardened"]
  API["APICleanup cascade (Alerts→Folders→Destinations→Templates)"]
  CI["CI workflow targets deployed env"]
  Delay["Configurable test step delay"]
  Data["Global ingestion more robust"]

  UI -- "uses" --> API
  CI -- "runs cleanup.spec" --> API
  CI -- "health checks + env vars" --> Data
  Delay -- "slows page ops" --> UI
  Data -- "ingest logs with parsing" --> UI
```



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>7
files</summary><table>
<tr>
<td><strong>alertDestinationsPage.js</strong><dd><code>Add search,
cascade-safe delete, bulk prefix cleanup</code>&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-57e5c53b4ef998b3d8708149620ff94dcc46807927f1a3a9e9f28ed3ebe25b0a">+178/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>alertTemplatesPage.js</strong><dd><code>Add template search,
in-use handling, prefix cleanup</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-f4ebd248b2140051e74dbbbd1aabe344f01c80704ce5857e9c28a04350e018f3">+177/-2</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>alertsPage.js</strong><dd><code>Harden alert actions, bulk
deletion, search across folders</code></dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-79ee01d6d7f145f19207b93b87143b545a1188cfc6a82bc8c45fecfbf68f8a1d">+280/-9</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>apiCleanup.js</strong><dd><code>New API-based cascade
cleanup utility</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-537fe76c0d8e26e81c5f77c83bd0ee35daaa973822105c809bf83171a26b33c5">+399/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>page-manager.js</strong><dd><code>Wire APICleanup into
PageManager</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-565eab8b3c767ae50c9d05e591621d14f66b81af84d6b5ea6efe136c1c3933df">+4/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>baseFixtures.js</strong><dd><code>Add configurable test step
delay hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-c4c5ee7fbb248d1ab14d3d4e33ffb9626f9aab74c9e0207fcfc08051c8c3c7ee">+46/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>global-setup.js</strong><dd><code>Harden login and ingestion
with diagnostics</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-06323dad7214f86b41b62e121e716eeea0054bc4d950c6bcf4a0efe53894acc8">+61/-22</a>&nbsp;
</td>

</tr>
</table></details></td></tr><tr><td><strong>Bug
fix</strong></td><td><details><summary>1 files</summary><table>
<tr>
<td><strong>commonActions.js</strong><dd><code>Fix ingestion URLs to use
org variable</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-4d726dd2bdb34b2c90a31798ff49b6df5bc952bd049b7c38bebb18b6db0076e2">+4/-4</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>4
files</summary><table>
<tr>
<td><strong>alerts-e2e-flow.spec.js</strong><dd><code>Pre-move folder
cleanup and stabilization</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-32972e38c1fe0e8bb887f9f254b3130916a36f96ea924f9987493d44efefeab9">+13/-3</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>alerts-import.spec.js</strong><dd><code>Use dedicated
invalid import test data</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-62b6ec4818685eacc0a934d68aaadfd73b512ef4f4be25fcd5135b97c2844912">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>cleanup.spec.js</strong><dd><code>New pre-test API cleanup
spec</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-c214275a512fa285a7e4f2ab61dfcb42d71a0436b0e4241f94d7b45693587983">+29/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>invalid-alert.json</strong><dd><code>Add invalid alert
sample for import tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-c695f71048e9393a45d3e3e3e415696559fecf3ba85712e7c3f366092f53f424">+24/-0</a>&nbsp;
&nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration
changes</strong></td><td><details><summary>1 files</summary><table>
<tr>
<td><strong>playwright.yml</strong><dd><code>Target deployed env; add
cleanup job and health checks</code>&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/8839/files#diff-7afcd2d8f7b49bda74843f209eefb7b2da45f7e7803bf2e4bd636699b76aa2d3">+123/-65</a></td>

</tr>
</table></details></td></tr></tr></tbody></table>

</details>

___
2025-11-04 23:54:54 +05:30

189 lines
6.3 KiB
JavaScript

const { chromium } = require('@playwright/test');
const path = require('path');
const fs = require('fs');
const logsdata = require("../../../test-data/logs_data.json");
const testLogger = require('./test-logger.js');
/**
* Global setup for all tests - handles authentication and test data ingestion
* This runs once before all tests start
*/
async function globalSetup() {
testLogger.info('Starting global setup for all tests');
// Create auth storage directory
const authDir = path.join(__dirname, 'auth');
if (!fs.existsSync(authDir)) {
fs.mkdirSync(authDir, { recursive: true });
}
const authFile = path.join(authDir, 'user.json');
// Launch browser and create context
const browser = await chromium.launch();
const context = await browser.newContext({
viewport: { width: 1500, height: 1024 }
});
const page = await context.newPage();
try {
testLogger.info('Performing global login authentication');
// Navigate to login page
await page.goto(process.env["ZO_BASE_URL"]);
testLogger.debug('Navigated to base URL', { url: process.env["ZO_BASE_URL"] });
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
// Debug: Log current URL and page title
const currentUrl = page.url();
const pageTitle = await page.title();
testLogger.debug('Page loaded', { currentUrl, pageTitle });
// Handle internal user login if needed
const internalUserButton = page.getByText('Login as internal user');
if (await internalUserButton.isVisible()) {
await internalUserButton.click();
testLogger.debug('Clicked internal user login button');
await page.waitForLoadState('domcontentloaded');
}
// Fill login credentials
const userIdField = page.locator('[data-cy="login-user-id"]');
const passwordField = page.locator('[data-cy="login-password"]');
const signInButton = page.locator('[data-cy="login-sign-in"]');
// Debug: Check if login fields exist
testLogger.debug('Waiting for login fields to appear...');
try {
await userIdField.waitFor({ state: 'visible', timeout: 15000 });
} catch (e) {
// Take screenshot and log page content for debugging
await page.screenshot({ path: path.join(__dirname, 'debug-login-page.png') });
const bodyText = await page.locator('body').textContent();
testLogger.error('Login field not found', {
url: page.url(),
bodyPreview: bodyText?.substring(0, 500)
});
throw new Error(`Login page did not load correctly. Current URL: ${page.url()}`);
}
await userIdField.fill(process.env["ZO_ROOT_USER_EMAIL"]);
await passwordField.waitFor({ state: 'visible' });
await passwordField.fill(process.env["ZO_ROOT_USER_PASSWORD"]);
await signInButton.waitFor({ state: 'visible' });
await signInButton.click();
testLogger.debug('Login credentials submitted');
// Wait for login to complete - look for navigation or success indicators
await page.waitForLoadState('networkidle', { timeout: 15000 });
// Verify login success by checking for a known element
await page.locator('[data-test="menu-link-\\/-item"]').waitFor({
state: 'visible',
timeout: 10000
});
testLogger.info('Global login authentication successful');
// Save authentication state
await context.storageState({ path: authFile });
testLogger.info('Authentication state saved successfully', { authFile });
// Perform global test data ingestion
testLogger.info('Starting global test data ingestion');
await performGlobalIngestion(page);
testLogger.info('Global test data ingestion completed');
} catch (error) {
testLogger.error('Global setup failed', { error: error.message, stack: error.stack });
throw error;
} finally {
await context.close();
await browser.close();
testLogger.debug('Browser closed after global setup');
}
testLogger.info('Global setup completed successfully - ready for test execution');
}
/**
* Performs global test data ingestion
* @param {import('@playwright/test').Page} page
*/
async function performGlobalIngestion(page) {
const orgId = process.env["ORGNAME"];
const streamName = "e2e_automate";
const basicAuthCredentials = Buffer.from(
`${process.env["ZO_ROOT_USER_EMAIL"]}:${process.env["ZO_ROOT_USER_PASSWORD"]}`
).toString('base64');
const headers = {
"Authorization": `Basic ${basicAuthCredentials}`,
"Content-Type": "application/json",
};
const response = await page.evaluate(async ({ url, headers, orgId, streamName, logsdata }) => {
// Remove trailing slash from URL if present
const baseUrl = url.endsWith('/') ? url.slice(0, -1) : url;
const fetchResponse = await fetch(`${baseUrl}/api/${orgId}/${streamName}/_json`, {
method: 'POST',
headers: headers,
body: JSON.stringify(logsdata)
});
// Try to parse JSON, but handle cases where response is empty or not JSON
let data = null;
const contentType = fetchResponse.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
try {
const text = await fetchResponse.text();
data = text ? JSON.parse(text) : null;
} catch (e) {
data = { error: 'Failed to parse JSON response', text: await fetchResponse.text() };
}
} else {
data = { text: await fetchResponse.text() };
}
return {
status: fetchResponse.status,
data: data
};
}, {
url: process.env.INGESTION_URL,
headers: headers,
orgId: orgId,
streamName: streamName,
logsdata: logsdata
});
testLogger.debug('Data ingestion API response received', {
status: response.status,
orgId,
streamName,
data: response.data
});
if (response.status !== 200) {
testLogger.error('Data ingestion failed', {
status: response.status,
response: response.data,
url: `${process.env.INGESTION_URL}/api/${orgId}/${streamName}/_json`,
orgId,
streamName
});
throw new Error(`Data ingestion failed with status: ${response.status}. Response: ${JSON.stringify(response.data)}`);
}
testLogger.info('Global data ingestion successful', {
status: response.status,
orgId,
streamName,
responseData: response.data
});
}
module.exports = globalSetup;