mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-06 16:09:41 +00:00
# Design System Unification — one visual language, enforced end-to-end
Establishes **one source of truth for every visual decision**, wires the
whole app to it, and **locks it in with CI** so it can't drift back.
### Foundations
- **Design-token system ("one knob per decision").** Every colour, text
size, spacing, corner radius, and shadow is a named token defined in
exactly one place — change the token, every screen updates. Collapsed
hundreds of near-duplicate definitions and removed "fake" duplicates
that pretended to be different but weren't.
- **Shared component library (O2).** Buttons, tables, dialogs, drawers,
form fields, badges, etc. already know the correct colours, spacing, and
dark-mode behaviour; new screens compose them instead of hand-styling.
Added new primitives on this branch (`OContent`, `KpiCard`) and
standardized existing ones.
- **One page structure everywhere** (`PageLayout` / `AppPageHeader` /
`AppPage` / `OContent`) — titles, breadcrumbs, spacing, and content
edges line up identically across every screen.
- **One table implementation** — all tabular data uses the same table
(sorting, density, sticky headers, empty states).
- **Styling consolidated onto Tailwind; old systems removed** —
hand-written CSS driven to the practical minimum, the legacy **Quasar**
framework fully removed, surviving stylesheets required to justify
themselves.
- **Two-tier corner radius** — a tight **4px** for controls and a soft
**12px** for surfaces (dialogs, cards, the app shell), plus a
full-circle option — replacing five radius names that all secretly meant
the same thing.
### Detailed improvements across screens
**Page structure & alignment** — unified page shell rolled across ~40+
screens (IAM, Settings, Alerts, Dashboards, Functions, Pipelines, RUM,
Metrics, Synthetics, Billing, editors, lists); one header height; one
page-edge grid driving headers, tables, rails, and tabs; unified
side-rail widths; inset rail selection/hover pills; every tab strip on
the grid line; tightened title/subtitle spacing.
**Tables (cross-screen)** — action columns consistently centered (incl.
Organizations); unified row-number column with no "#" truncation;
non-standard tables restyled to match; loading skeletons made
geometrically identical to the loaded table; symmetric edge inset; IAM
first-column grid alignment fixed.
**Traces** — flame graph + span-detail correlation panel fixes;
correlated logs/metrics placeholder fit; sticky headers; removed a
double scrollbar + a 1px sliver; aligned table fonts/badges; polished
the service detail drawer; stream-selector + correlation alignment;
aligned loading spinners; floating overlay arrows for the
disabled-services strip.
**Logs** — field-list controls aligned (focus ring no longer clipped);
detail-drawer spacing + metrics-tab overflow fixed; Run Query toolbar
tidied; listing sizing + stray border fixed.
**Metrics** — search added to the "Type" facet rail and aligned with the
other rails; proper empty states for the explorer and facet rail.
**Dashboards** — removed inconsistent card shadows; tightened
borders/resizers/field-list spacing; aligned View Dashboard chrome to
the grid; chart colours via the token helper.
**Home** — redesigned the Usage tab; added reusable KPI stat cards so
summary strips stop drifting.
**Dark mode** — new theme-switch animation; fixed a 1px content shift on
toggle and a white flash on load; fixed AI-chat text and assorted
light/dark mismatches; completed the token-driven dark migration.
**Empty states, controls & small polish** — restored 3 broken
empty-state animations; adopted the standard empty state; added the
sliding indicator to the toggle control; fixed the Quality-page inset +
drawer badge; refreshed Search History / Schedulers; fixed the
alert-history drawer scrollbar + timeline hover; removed a stray
menu-bar border.
### Dead code & cruft removal
- **Deleted the entire legacy `utilities.css`** global stylesheet (well
over a thousand lines) — evacuated in waves, roughly half of it dead
once traced.
- **Removed Quasar entirely**, including dead `.q-*` styles and ~25
phantom `--q-*` variables that were silently breaking bits of the UI.
- Stripped hundreds of **dead CSS classes and tokens that compiled to
nothing** — e.g. 46 uses of a colour class with no backing token, a
no-op shadow class on 51 elements, dead layout/utility classes, a
leftover `card-container` block, a dead table-actions token, and a
mis-registered gradient that rendered nothing.
- Cut **unused keyframe animations (19 → 3)** and removed **unused
components** + a stray build artifact.
- Net effect: a much smaller, leaner styling surface — less to maintain,
fewer dead ends to copy.
## Safety & verification
Large but low-risk: this is overwhelmingly a **styling and structure**
change, not product behaviour or data. Guarded by the
design-token/consistency/type checks above plus the existing test suite,
and reviewed screen-by-screen.
---------
Co-authored-by: Kirtan Thakkar <kirtan@kiara.tech>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: ktx-mihir <mihir.patel@kiara.tech>
485 lines
19 KiB
JavaScript
485 lines
19 KiB
JavaScript
const { test, expect } = require('../utils/enhanced-baseFixtures.js');
|
|
const logData = require("../../fixtures/log.json");
|
|
const PageManager = require('../../pages/page-manager.js');
|
|
const { getHeaders, getIngestionUrl, sendRequest } = require('../../utils/apiUtils.js');
|
|
const testLogger = require('../utils/test-logger.js');
|
|
|
|
// Constants for consistent timeouts and test data
|
|
const MULTISTREAM_CONFIG = {
|
|
TIMEOUTS: {
|
|
STREAM_FILTER: 2000,
|
|
STREAM_SELECTION: 4000,
|
|
MULTISTREAM_SETUP: 5000,
|
|
QUERY_EXECUTION: 5000,
|
|
DATA_INDEXING: 3000,
|
|
UI_INTERACTION: 1000,
|
|
NAVIGATION: 2000,
|
|
TOAST_WAIT: 500
|
|
},
|
|
TEST_DATA: {
|
|
STREAMS: ['e2e_automate', 'e2e_stream1'],
|
|
QUERIES: {
|
|
INVALID_FIELD: 'random_test=2',
|
|
VRL_FUNCTION: '.a=2'
|
|
},
|
|
PAYLOADS: {
|
|
STREAM1: {
|
|
level: "debug",
|
|
job: "test_job_1",
|
|
log: "enhanced test message for stream 1",
|
|
e2e: "enhanced1",
|
|
stream_id: "e2e_automate"
|
|
},
|
|
STREAM2: {
|
|
level: "warning",
|
|
job: "test_job_2",
|
|
log: "enhanced test message for stream 2",
|
|
e2e: "enhanced2",
|
|
stream_id: "e2e_stream1"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Helper functions to reduce code duplication
|
|
async function verifyMultistreamSelection(page, streams = MULTISTREAM_CONFIG.TEST_DATA.STREAMS) {
|
|
const pageManager = new PageManager(page);
|
|
await pageManager.logsPage.expectLogsSearchIndexListContainsText(streams.join(', '));
|
|
}
|
|
|
|
async function setupBasicMultistream(page, streams = MULTISTREAM_CONFIG.TEST_DATA.STREAMS) {
|
|
const pageManager = new PageManager(page);
|
|
|
|
// Add second stream using POM
|
|
await pageManager.logsPage.fillStreamFilter(streams[1]);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.STREAM_FILTER);
|
|
await pageManager.logsPage.toggleStreamSelection(streams[1]);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.STREAM_SELECTION);
|
|
|
|
// Verify both streams are selected
|
|
await verifyMultistreamSelection(page, streams);
|
|
}
|
|
|
|
async function validateCombinedStreamResults(page, payloads) {
|
|
const pageManager = new PageManager(page);
|
|
const tableContent = await pageManager.logsPage.getLogsTableContent();
|
|
|
|
// Extract stream identifiers from payloads for validation
|
|
const stream1Indicators = [payloads.STREAM1.e2e, payloads.STREAM1.job, payloads.STREAM1.level];
|
|
const stream2Indicators = [payloads.STREAM2.e2e, payloads.STREAM2.job, payloads.STREAM2.level];
|
|
|
|
// Check for data from both streams
|
|
const hasStream1Data = stream1Indicators.some(indicator => tableContent.includes(indicator));
|
|
const hasStream2Data = stream2Indicators.some(indicator => tableContent.includes(indicator));
|
|
|
|
if (!hasStream1Data || !hasStream2Data) {
|
|
// Alternative check - verify we have multiple rows of data
|
|
const rowCount = await pageManager.logsPage.getLogsTableRowCount();
|
|
expect(rowCount).toBeGreaterThan(1);
|
|
testLogger.info('Multiple data rows found indicating combined results', { rowCount });
|
|
} else {
|
|
testLogger.info('Data from both streams verified in results');
|
|
}
|
|
}
|
|
|
|
test.describe.configure({ mode: "parallel" });
|
|
|
|
test.describe("Stream multiselect testcases", () => {
|
|
let pm;
|
|
|
|
function removeUTFCharacters(text) {
|
|
return text.replace(/[^\x00-\x7F]/g, " ");
|
|
}
|
|
|
|
async function applyQueryButton(page) {
|
|
const pm = new PageManager(page);
|
|
await pm.logsPage.applyQueryButton(logData.logsUrl);
|
|
}
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
pm = new PageManager(page);
|
|
|
|
const orgId = process.env["ORGNAME"];
|
|
const streamNames = ["e2e_automate", "e2e_stream1"];
|
|
const headers = getHeaders();
|
|
|
|
for (const streamName of streamNames) {
|
|
const ingestionUrl = getIngestionUrl(orgId, streamName);
|
|
const payload = {
|
|
level: "info",
|
|
job: "test",
|
|
log: "test message for openobserve",
|
|
e2e: "1",
|
|
};
|
|
const response = await sendRequest(page, ingestionUrl, payload, headers);
|
|
testLogger.debug('API response received', { streamName, response });
|
|
}
|
|
|
|
await page.goto(`${logData.logsUrl}?org_identifier=${process.env["ORGNAME"]}`);
|
|
await pm.logsPage.selectStream("e2e_automate");
|
|
await applyQueryButton(page);
|
|
// Ensure quick mode is ON (idempotent) so the All Fields toggle is visible
|
|
await pm.logsPage.enableQuickModeIfDisabled();
|
|
await pm.logsPage.clickAllFieldsButton();
|
|
});
|
|
|
|
|
|
async function multistreamselect(page) {
|
|
const pageManager = new PageManager(page);
|
|
|
|
// Add second stream using POM (we're already on logs page from beforeEach with e2e_automate selected and all fields enabled)
|
|
await pageManager.logsPage.fillStreamFilter('e2e_stream1');
|
|
await page.waitForTimeout(2000);
|
|
await pageManager.logsPage.toggleStreamSelection('e2e_stream1');
|
|
await page.waitForTimeout(4000);
|
|
|
|
// Enable function editor using POM (all fields already clicked in beforeEach)
|
|
await pageManager.logsPage.toggleQueryModeEditor();
|
|
// Extra wait for Firefox to render the Monaco editor
|
|
await page.waitForTimeout(3000);
|
|
await pageManager.logsPage.clickMonacoEditor();
|
|
|
|
// Run query to populate results first
|
|
await pageManager.logsPage.selectRunQuery();
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Verify common fields group header is present.
|
|
// When applyFieldGrouping is active (semantic grouping), the header renders as
|
|
// "Common" or "Common (N)"; legacy path renders "Common Group Fields".
|
|
const cell = await pageManager.logsPage.getCellByName(/^Common/);
|
|
const cellText = await cell.textContent();
|
|
expect(cellText).toMatch(/Common/);
|
|
|
|
// Verify both streams are selected in the index list
|
|
await pageManager.logsPage.expectLogsSearchIndexListContainsText('e2e_automate, e2e_stream1');
|
|
|
|
// Execute query and navigate time picker using POM
|
|
await pageManager.logsPage.selectRunQuery();
|
|
await pageManager.logsPage.clickDateTimeButton();
|
|
await pageManager.logsPage.selectRelative6Hours();
|
|
await pageManager.logsPage.clickTimestampColumnMenu();
|
|
}
|
|
|
|
|
|
test("should add a function and display it in streams", {
|
|
tag: ['@vrl', '@function', '@multistream', '@all']
|
|
}, async ({ page }) => {
|
|
const pageManager = new PageManager(page);
|
|
|
|
await multistreamselect(page);
|
|
await pageManager.logsPage.fillMonacoEditor('.a=2');
|
|
await page.waitForTimeout(1000);
|
|
await applyQueryButton(page);
|
|
await pageManager.logsPage.clickTableExpandMenuFirst();
|
|
await pageManager.logsPage.expectVrlFunctionVisible(".a=2");
|
|
await pageManager.logsPage.expectLogsTableVisible();
|
|
await pageManager.logsPage.selectRunQuery();
|
|
});
|
|
|
|
// test("should click on live mode on button and select 5 sec, switch off, and then click run query", async ({
|
|
|
|
// page,
|
|
// }) => {
|
|
// await multistreamselect(page);
|
|
// await page.route("**/logData.ValueQuery", (route) => route.continue());
|
|
// await page.locator('[data-test="date-time-btn"]').click({ force: true });
|
|
|
|
// await page
|
|
// .locator('[data-test="date-time-relative-6-w-btn"] button')
|
|
// .click({
|
|
// force: true,
|
|
// });
|
|
// await page
|
|
// .locator('[data-test="logs-search-bar-refresh-interval-btn-dropdown"]')
|
|
// .click({ force: true });
|
|
// await page.locator('[data-test="logs-search-bar-refresh-time-5"]').click({
|
|
// force: true,
|
|
// });
|
|
// await page.waitForTimeout(1000);
|
|
// await expect(page.locator('[role="alert"]')).toContainText(
|
|
// "Live mode is enabled"
|
|
// );
|
|
// await page.waitForTimeout(5000);
|
|
// await page
|
|
// .locator('[data-test="logs-search-off-refresh-interval"] button')
|
|
// .click({ force: true });
|
|
// await page
|
|
// .locator(
|
|
// '[data-test="logs-search-off-refresh-interval"] button'
|
|
// )
|
|
// .click({ force: true });
|
|
// await applyQueryButton(page);
|
|
// });
|
|
|
|
test("should redirect to logs after clicking on stream explorer via stream page", {
|
|
tag: ['@navigation', '@streamExplorer', '@multistream', '@all']
|
|
}, async ({ page }) => {
|
|
const pageManager = new PageManager(page);
|
|
|
|
await multistreamselect(page);
|
|
await pageManager.logsPage.clickDateTimeButton();
|
|
await pageManager.logsPage.navigateToStreams();
|
|
await page.waitForTimeout(1000);
|
|
await pageManager.logsPage.navigateToStreamsAlternate();
|
|
await pageManager.logsPage.searchStreamByPlaceholder("e2e");
|
|
await page.waitForTimeout(1000);
|
|
await pageManager.logsPage.clickFirstExploreButton();
|
|
await page.waitForTimeout(1000);
|
|
await expect(page.url()).toContain("logs");
|
|
});
|
|
|
|
test("should click on interesting fields icon and display query in editor", {
|
|
tag: ['@interestingFields', '@multistream']
|
|
}, async ({ page }) => {
|
|
const pageManager = new PageManager(page);
|
|
testLogger.info('Testing interesting fields with multistream selection');
|
|
|
|
await multistreamselect(page);
|
|
|
|
// The interesting-field (ⓘ) button only renders when quick mode is on
|
|
// (FieldExpansion.vue v-if="showQuickMode"), and surfaces on row hover.
|
|
await pageManager.logsPage.ensureQuickModeState(true);
|
|
|
|
// Search for job field using POM
|
|
await pageManager.logsPage.searchFieldByName('job');
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Hover the field row so its action buttons render, then click ⓘ
|
|
await pageManager.logsPage.hoverAndClickInterestingFieldLast('job');
|
|
|
|
// Enable SQL mode using POM
|
|
await pageManager.logsPage.enableSQLMode();
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Run query using POM
|
|
await pageManager.logsPage.clickSearchBarRefreshButton();
|
|
|
|
// Click on first result
|
|
await pageManager.logsPage.clickLogTableColumnSource();
|
|
|
|
// Verify table is visible
|
|
await pageManager.logsPage.expectLogsTableVisible();
|
|
testLogger.info('Interesting fields test completed');
|
|
});
|
|
|
|
test("should display results in selected time when multiple stream selected", {
|
|
tag: ['@timePicker', '@dateTime', '@multistream', '@all']
|
|
}, async ({ page }) => {
|
|
const pageManager = new PageManager(page);
|
|
await multistreamselect(page);
|
|
await pageManager.logsPage.setDateTimeToToday();
|
|
await pageManager.logsPage.expectLogsSearchIndexListContainsText('e2e_automate, e2e_stream1');
|
|
|
|
});
|
|
|
|
test("should not show error when histogram toggle is on with multiple streams @multi-histogram @multistream @regression", async ({
|
|
page,
|
|
}) => {
|
|
const pageManager = new PageManager(page);
|
|
|
|
// Navigate to logs page using POM
|
|
await pageManager.logsPage.navigateToHome();
|
|
await pageManager.logsPage.navigateToLogs();
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Select first stream (e2e_automate) using POM
|
|
await pageManager.logsPage.selectStream("e2e_automate");
|
|
await pageManager.logsPage.selectRunQuery();
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Select additional stream using POM
|
|
await pageManager.logsPage.fillStreamFilter("e2e_stream1");
|
|
await page.waitForTimeout(2000);
|
|
await pageManager.logsPage.toggleStreamSelection("e2e_stream1");
|
|
await page.waitForTimeout(4000);
|
|
|
|
// Ensure quick mode is ON so the All Fields toggle is visible
|
|
// (quickMode default depends on whether localStorage has saved streams from prior navigation)
|
|
await pageManager.logsPage.enableQuickModeIfDisabled();
|
|
await pageManager.logsPage.clickAllFieldsButton();
|
|
await pageManager.logsPage.toggleQueryModeEditor();
|
|
await pageManager.logsPage.clickMonacoEditor();
|
|
|
|
// Enable histogram using POM - ensure it's actually enabled
|
|
await pageManager.logsPage.toggleHistogram();
|
|
await pageManager.logsPage.toggleHistogram(); // Double click to ensure ON state
|
|
|
|
await pageManager.logsPage.selectRunQuery();
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Verify no histogram error is displayed
|
|
await pageManager.logsPage.expectErrorWhileFetchingNotVisible();
|
|
});
|
|
|
|
// Enhanced multistream tests
|
|
test("should verify search filter functionality with multiple streams selected", {
|
|
tag: ['@filter', '@invalidfield', '@multistream']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing search filter functionality with multiple streams');
|
|
|
|
const pageManager = new PageManager(page);
|
|
|
|
// Setup multistream selection using helper
|
|
await setupBasicMultistream(page);
|
|
|
|
// Add query in query editor and run
|
|
await pageManager.logsPage.fillQueryEditorWithRole(MULTISTREAM_CONFIG.TEST_DATA.QUERIES.INVALID_FIELD);
|
|
await pageManager.logsPage.clickSearchBarRefreshButton();
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.QUERY_EXECUTION);
|
|
|
|
testLogger.info('Search filter steps executed successfully with multiple streams');
|
|
});
|
|
|
|
test("should add a function and display it in streams with multiple streams", {
|
|
tag: ['@vrl', '@function', '@transformation']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing Monaco editor function with multiple streams');
|
|
|
|
const pageManager = new PageManager(page);
|
|
|
|
// Setup multiple streams using existing multistreamselect function
|
|
await multistreamselect(page);
|
|
|
|
// Fill Monaco editor with VRL function using constant
|
|
await pageManager.logsPage.fillMonacoEditor(MULTISTREAM_CONFIG.TEST_DATA.QUERIES.VRL_FUNCTION);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.UI_INTERACTION);
|
|
await applyQueryButton(page);
|
|
await pageManager.logsPage.clickTableExpandMenuFirst();
|
|
|
|
// Verify VRL function is visible and table is displayed
|
|
await pageManager.logsPage.expectVrlFunctionVisible(MULTISTREAM_CONFIG.TEST_DATA.QUERIES.VRL_FUNCTION);
|
|
await pageManager.logsPage.expectLogsTableVisible();
|
|
await pageManager.logsPage.selectRunQuery();
|
|
|
|
testLogger.info('Monaco editor function test with multiple streams completed successfully');
|
|
});
|
|
|
|
test("should display combined results when both streams have data", {
|
|
tag: ['@multistreamdata', '@datavalidation', '@multistreams']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing combined results display when both streams have data');
|
|
|
|
const pageManager = new PageManager(page);
|
|
|
|
// Ensure both streams have data by ingesting test data
|
|
const orgId = process.env["ORGNAME"];
|
|
const headers = getHeaders();
|
|
const streams = MULTISTREAM_CONFIG.TEST_DATA.STREAMS;
|
|
const payloads = MULTISTREAM_CONFIG.TEST_DATA.PAYLOADS;
|
|
|
|
await sendRequest(page, getIngestionUrl(orgId, streams[0]), payloads.STREAM1, headers);
|
|
await sendRequest(page, getIngestionUrl(orgId, streams[1]), payloads.STREAM2, headers);
|
|
|
|
// Wait for data to be indexed
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.DATA_INDEXING);
|
|
|
|
// Setup multiple streams
|
|
await multistreamselect(page);
|
|
|
|
// Verify both streams are selected
|
|
await verifyMultistreamSelection(page);
|
|
|
|
// Run query to get combined results
|
|
await pageManager.logsPage.selectRunQuery();
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.QUERY_EXECUTION);
|
|
|
|
// Verify results table is visible and contains data
|
|
await pageManager.logsPage.expectLogsTableVisible();
|
|
|
|
// Verify we have results from both streams
|
|
await validateCombinedStreamResults(page, payloads);
|
|
|
|
// Verify no error messages are present
|
|
await pageManager.logsPage.expectNotificationErrorNotVisible();
|
|
|
|
testLogger.info('Combined results from both streams test completed successfully');
|
|
});
|
|
|
|
test("should create and delete saved view with multiple streams selected", {
|
|
tag: ['@savedview', '@viewcrud', '@multiconfig']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing saved view creation and deletion with multiple streams');
|
|
|
|
const pageManager = new PageManager(page);
|
|
const streams = MULTISTREAM_CONFIG.TEST_DATA.STREAMS;
|
|
|
|
// Setup multiple streams selection using existing function
|
|
await multistreamselect(page);
|
|
|
|
// Verify both streams are selected using helper
|
|
await verifyMultistreamSelection(page);
|
|
|
|
// Generate a random saved view name with multistream prefix
|
|
const randomSavedViewName = `multistream_view_${Math.random().toString(36).substring(2, 10)}`;
|
|
testLogger.info(`Creating saved view with name: ${randomSavedViewName}`);
|
|
|
|
// Create saved view with multistream configuration
|
|
await pageManager.logsPage.clickSavedViewsExpand();
|
|
await pageManager.logsPage.clickSaveViewButton();
|
|
await pageManager.logsPage.fillSavedViewName(randomSavedViewName);
|
|
await pageManager.logsPage.clickSavedViewDialogSave();
|
|
|
|
// Wait for success message with timeout constant
|
|
try {
|
|
await page.locator('[data-test-variant="success"]').waitFor({
|
|
state: 'visible',
|
|
timeout: MULTISTREAM_CONFIG.TIMEOUTS.DATA_INDEXING
|
|
});
|
|
testLogger.info('Success toast validated: Multistream view created successfully');
|
|
} catch (error) {
|
|
testLogger.info('View creation toast may have appeared and disappeared quickly - continuing with test');
|
|
}
|
|
|
|
// Wait for saved view creation to complete using constant
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.NAVIGATION);
|
|
|
|
// Navigate away and back to verify saved view persistence
|
|
await pageManager.logsPage.clickStreamsMenuItem();
|
|
await pageManager.logsPage.clickSearchStreamInput();
|
|
await pageManager.logsPage.fillSearchStreamInput('e2e');
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.TOAST_WAIT);
|
|
await pageManager.logsPage.clickExploreButton();
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.NAVIGATION);
|
|
|
|
// Verify saved view exists and can be loaded
|
|
await pageManager.logsPage.waitForSavedViewsButton();
|
|
await pageManager.logsPage.clickSavedViewsExpand();
|
|
await pageManager.logsPage.clickSavedViewSearchInput();
|
|
await pageManager.logsPage.fillSavedViewSearchInput(randomSavedViewName);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.UI_INTERACTION);
|
|
|
|
// Apply the saved view
|
|
await pageManager.logsPage.waitForSavedViewText(randomSavedViewName);
|
|
await pageManager.logsPage.clickSavedViewByText(randomSavedViewName);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.NAVIGATION);
|
|
|
|
// Verify that multistream configuration was saved and restored using helper
|
|
await verifyMultistreamSelection(page);
|
|
testLogger.info('Verified: Saved view correctly restored multistream selection');
|
|
|
|
// Verify query results are displayed
|
|
await pageManager.logsPage.expectLogsTableVisible();
|
|
testLogger.info('Verified: Query results are displayed after loading saved view');
|
|
|
|
// Clean up: Try to delete the saved view (with error handling)
|
|
try {
|
|
await pageManager.logsPage.clickSavedViewsExpand();
|
|
await pageManager.logsPage.clickSavedViewSearchInput();
|
|
await pageManager.logsPage.fillSavedViewSearchInput(randomSavedViewName);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.UI_INTERACTION);
|
|
|
|
// Delete the saved view using POM
|
|
await pageManager.logsPage.clickDeleteSavedViewButton(randomSavedViewName);
|
|
await page.waitForTimeout(MULTISTREAM_CONFIG.TIMEOUTS.TOAST_WAIT);
|
|
await pageManager.logsPage.clickConfirmButton();
|
|
|
|
testLogger.info(`Successfully deleted multistream saved view: ${randomSavedViewName}`);
|
|
} catch (cleanupError) {
|
|
testLogger.warn(`Cleanup failed - saved view may still exist: ${randomSavedViewName}`, { error: cleanupError.message });
|
|
// Test is still successful even if cleanup fails
|
|
}
|
|
|
|
testLogger.info('Multistream saved view creation and deletion test completed successfully');
|
|
});
|
|
})
|