Files
openobserve/tests/ui-testing/playwright-tests/Logs/join.spec.js
T
Shrinath RaoandClaude Opus 4.7 e2813291ce test: stabilise alpha1 e2e tests via UI org-switch (#11528)
## Summary

Stabilises alpha1 cloud E2E tests across the Logs-Core shard. Fixes a
UI/API org mismatch root cause, adds env-aware query execution helpers,
and brings all 10 changed spec files into POM compliance.

## Root cause (original)

After Dex login, the Pinia store binds all API calls to the user's
*default* org. The URL `?org_identifier=...` alone does not update the
store. Ingestion went to the `.env` org while UI searches went to the
user's default org → empty results → cascading test failures.

## Changes

### Alpha1 environment / authentication
- **`global-setup-alpha1.js`** — switches active org via the navbar
dropdown after Dex login so the Pinia store adopts the correct org;
`context.storageState` captures the result for all test contexts
- **`playwright-alpha1.config.js`** — `ORGNAME` sourced purely from
`.env`; cloud-config override removed
- **`loginPage.login()` / `unflattened.spec.js`** — include
`?org_identifier=${ORGNAME}` in SPA navigation to prevent defaulting to
`_meta`
- **`unflattened.js openStreamDetail()`** — replaced broken
stream-name-text click with `Stream Detail` action button + Schema-tab
visibility wait

### Auto-run query handling (`auto_query_enabled` flag)
- **`logsPage.js`** — added `runQueryAfterModeChange()`: on alpha1
(`auto_query_enabled=true`) waits for the in-flight auto-query to
complete; on local triggers a fresh query. Fixes
`@interestingFieldsCRUD` and `@quickModeResults` tests that were passing
on alpha1 but failing locally because the old fix clicked Refresh
mid-auto-run (hitting Cancel)
- **`logsQueryPage.js`** — null-guard `textContent()` return in
`_toggleAutoRun()` (`|| ''`) to satisfy Playwright's `string | null`
type contract

### Merge conflict resolution
- **`logsPage.js enableQuickModeIfDisabled()`** — kept HEAD's version
that defines `quickModeIndicator` at the top of the function (used in
the retry loop); discarded main's version that opened the utilities menu
before the variable was defined

### POM compliance (Sentinel audit)
Added three new POM methods to **`logsPage.js`**:
- `watchForNotification(timeout)` — returns a promise for pre-action
race-condition-safe notification watching
- `expectNotificationContainsText(text)` — notification text assertion
- `waitForNotificationWithText(text, timeout)` — notification waiter
with text filter

Removed raw locators from spec files:
- **`logspage.spec.js`** — notification race pattern →
`watchForNotification()` + `expectNotificationContainsText()`; 5-line
raw histogram toggle block → `ensureHistogramState(true)` (POM method
already existed)
- **`logsqueries.spec.js`** —
`pm.logsPage.page.waitForSelector('.q-notification__message:has-text(...)')`
→ `waitForNotificationWithText()`
- **`jobSearch.spec.js`** — added `testLogger` import; replaced 14×
`console.log` with `testLogger.info`; raw Monaco editor locators (lines
158–168) → `logsPage.waitForQueryEditorVisible()` +
`logsPage.getQueryEditorText()` (both already in POM)

### Tags
- **`jobSearch.spec.js`** — all 8 tests now tagged: `@jobSearch,
@P0/@P1` with functional sub-tags (`@smoke`, `@crud`, `@validation`)
- **`logshistogram.spec.js`** — all 5 tests now include `@P1`

## Results

- All `logsquickmode.spec.js` tests pass on both local and alpha1
- Zero Sentinel critical issues remaining across all 10 changed spec
files
- Remaining warnings (old-style auth in `jobSearch.spec.js` /
`pagination.spec.js`, `pm.logsPage.page.reload()` in
`logsquickmode.spec.js`) are pre-existing tech debt deferred to a
separate PR

## Test plan

- [ ] Run `logsquickmode.spec.js` locally and on alpha1 — both should
pass
- [ ] Run full Logs-Core shard on alpha1: `npx playwright test --config
playwright-alpha1.config.js
playwright-tests/Logs/{join,logspage,logsquickmode,pagination,unflattened,logshistogram,jobSearch,logsqueries,logsAnalyzeDimensions}.spec.js`
- [ ] Verify global setup logs: `Switching active org to: <ORGNAME>` →
`Org switch complete`
- [ ] Confirm no `console.log` output in `jobSearch.spec.js` test run
(all replaced by `testLogger`)

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 16:19:54 +00:00

187 lines
7.7 KiB
JavaScript

const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js');
const PageManager = require('../../pages/page-manager.js');
const testLogger = require('../utils/test-logger.js');
// Utility Functions
// Legacy login function replaced by global authentication via navigateToBase
test.describe("Join for logs", () => {
test.describe.configure({ mode: 'parallel' });
let pm; // Page Manager instance
test.beforeEach(async ({ page }, testInfo) => {
// Initialize test setup
testLogger.testStart(testInfo.title, testInfo.file);
// Navigate to base URL with authentication
await navigateToBase(page);
pm = new PageManager(page);
// Strategic 500ms wait for post-authentication stabilization - this is functionally necessary
await page.waitForTimeout(500);
// Data ingestion for join testing (preserve exact logic)
await pm.ingestionPage.ingestion();
await pm.ingestionPage.ingestionJoin();
testLogger.info('Join test setup completed');
});
test("Run query after selecting two streams", { tag: ['@join', '@smoke', '@P0'] }, async ({ page }) => {
testLogger.info('Testing run query with two streams selection');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.displayTwoStreams();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('Two streams query execution completed');
});
test("Run query after selecting two streams and SQL Mode On", { tag: ['@join', '@sqlMode', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing two streams query with SQL Mode enabled');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.displayTwoStreams();
await pm.logsPage.validateResult();
testLogger.info('Two streams SQL Mode query completed');
});
test("Run query after selecting two streams, selecting field and SQL Mode On", { tag: ['@join', '@union', '@functional', '@P1'], timeout: 300000 }, async ({ page }) => {
testLogger.info('Testing two streams with field selection and SQL Mode');
// Generate unique testRunId to avoid "stream being deleted" conflicts (SDR pattern)
const testRunId = Date.now().toString(36);
testLogger.info(`Using testRunId: ${testRunId}`);
// Use dedicated UNION test ingestion and streams to ensure schema compatibility
// Returns unique stream names that we pass to the selection method
const { streamA, streamB } = await pm.ingestionPage.ingestionJoinUnion(testRunId);
testLogger.info(`Created streams: ${streamA}, ${streamB}`);
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoinUnion(streamA, streamB);
await pm.logsPage.kubernetesContainerName();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('Field selection with SQL Mode query validated successfully');
});
test("Run query after selecting two streams, SQL Mode On and entering join queries", { tag: ['@join', '@innerJoin', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing join queries with SQL Mode enabled');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.kubernetesContainerNameJoin();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.displayCountQuery();
await pm.logsPage.validateResult();
testLogger.info('Join queries execution and validation completed');
});
test("Run query after selecting two streams, SQL Mode On and entering join limit", { tag: ['@join', '@limit', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing join queries with limit clause');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.kubernetesContainerNameJoinLimit();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('Join limit query validation completed');
});
test("Run query after selecting two streams, SQL Mode On and entering join like", { tag: ['@join', '@like', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing join queries with LIKE clause');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.kubernetesContainerNameJoinLike();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('Join LIKE query execution completed');
});
test("Run query after selecting two streams, SQL Mode On and entering Left join queries", { tag: ['@join', '@leftJoin', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing LEFT JOIN queries');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.kubernetesContainerNameLeftJoin();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('LEFT JOIN query validation completed');
});
test("Run query after selecting two streams, SQL Mode On and entering Right join queries", { tag: ['@join', '@rightJoin', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing RIGHT JOIN queries');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.kubernetesContainerNameRightJoin();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('RIGHT JOIN query validation completed');
});
test("Run query after selecting two streams, SQL Mode On and entering Full join queries", { tag: ['@join', '@fullJoin', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing FULL JOIN queries');
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoin();
await pm.logsPage.kubernetesContainerNameFullJoin();
await pm.logsPage.enableSQLMode();
await pm.logsPage.selectRunQuery();
await pm.logsPage.validateResult();
testLogger.info('FULL JOIN query validation completed');
});
test("Add/remove interesting field in editor and results", { tag: ['@join', '@interestingFields', '@functional', '@P1'] }, async ({ page }) => {
testLogger.info('Testing add/remove interesting field functionality');
// Use unique streams to avoid contention with other join tests
const testRunId = Date.now().toString(36);
const { streamA, streamB } = await pm.ingestionPage.ingestionJoinUnion(testRunId);
testLogger.info(`Created streams: ${streamA}, ${streamB}`);
await pm.logsPage.navigateToLogs();
await pm.logsPage.selectIndexAndStreamJoinUnion(streamA, streamB);
await pm.logsPage.enableSQLMode();
await pm.logsPage.ensureQuickModeState(true);
await pm.logsPage.clickAllFieldsButton();
await pm.logsPage.selectRunQuery();
// Step 1: Add field via interesting field star icon
await pm.logsPage.clickInterestingFields();
await pm.logsPage.expectQueryEditorContainsText('kubernetes_pod_name');
testLogger.info('Verified kubernetes_pod_name was added to query');
// Step 2: Remove field by clicking star again
await pm.logsPage.addRemoveInteresting();
await pm.logsPage.expectQueryEditorNotContainsText('kubernetes_pod_name');
testLogger.info('Verified kubernetes_pod_name was removed from query');
testLogger.info('Add/remove interesting field functionality completed');
});
});