mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 16:09:05 +00:00
+5![github-actions[bot]](https://secure.gravatar.com/avatar/cb7b7994e594270ea25948a7bf186123?d=identicon&s=40)








ae22a74e62
#11720 Design at: https://github.com/openobserve/designs/blob/main/dashboards/ux-improvement-dialog/design.md --------- --------- Co-authored-by: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> Co-authored-by: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com> Co-authored-by: Sai Nikhil <nikhil@openobserve.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: ktx-akshay <akshay.patil@kiara.tech> Co-authored-by: Bhargav <BJP232004@GMAIL.COM> Co-authored-by: ktx-mihir <mihir.patel@kiara.tech> Co-authored-by: ktx-riya <riya.kukadiya@kiara.tech> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Omkar Kesarkhane <127078473+omkarK06@users.noreply.github.com> Co-authored-by: omkark06 <omkar@zinclabs.io> Co-authored-by: ktx-vaidehi <vaidehi.akhani@kiara.tech> Co-authored-by: ktx-abhay <abhay.padamani@kiara.tech> Co-authored-by: Shrinath Rao <shnath@openobserve.ai>
492 lines
23 KiB
JavaScript
492 lines
23 KiB
JavaScript
/**
|
|
* SQL Autocomplete — Field & Stream Suggestions E2E Tests
|
|
*
|
|
* Tests the SQL autocomplete feature that shows:
|
|
* - Field suggestions sorted to the top (sortText "\x00")
|
|
* - Stream name suggestions ONLY after the FROM keyword
|
|
* - Correct quote handling when Monaco auto-inserts a closing "
|
|
*
|
|
* FEATURE DOCUMENTATION:
|
|
* - In SQL mode, typing text shows field+function+keyword suggestions (fields first)
|
|
* - Typing "FROM " shows stream names (not fields)
|
|
* - Typing 'FROM "def' shows streams with closing " appended automatically
|
|
* - If Monaco has already auto-inserted the closing ", no extra " is added
|
|
* - After "FROM stream WHERE ", field suggestions are restored (not stream names)
|
|
*
|
|
* TEST COVERAGE:
|
|
* 1. Field suggestions appear in SQL mode and sort above functions/keywords
|
|
* 2. Stream suggestions appear after FROM keyword
|
|
* 3. Field names do NOT appear in the FROM suggestion list
|
|
* 4. FROM "partial shows stream with closing quote in insertText
|
|
* 5. Field suggestions are restored after leaving FROM context (WHERE clause)
|
|
* 6. Dashboard SQL — same field+stream behavior
|
|
* 7. Traces SQL — field suggestions work
|
|
*/
|
|
|
|
const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js');
|
|
const testLogger = require('../utils/test-logger.js');
|
|
const PageManager = require('../../pages/page-manager.js');
|
|
const logData = require('../../fixtures/log.json');
|
|
|
|
// ============================================================================
|
|
// LOGS SQL AUTOCOMPLETE
|
|
// ============================================================================
|
|
|
|
test.describe("SQL Autocomplete — Logs", () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let pm;
|
|
const streamName = 'e2e_automate';
|
|
const orgName = process.env['ORGNAME'] || 'default';
|
|
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
testLogger.testStart(testInfo.title, testInfo.file);
|
|
await navigateToBase(page);
|
|
pm = new PageManager(page);
|
|
|
|
// selectStream internally navigates to the logs page, waits for the
|
|
// stream list, then selects the stream — this is the moment
|
|
// getStreamList() fires and the /api/{org}/streams response populates
|
|
// streamResults.list.
|
|
//
|
|
// Set up waitForResponse BEFORE selectStream so we capture that exact
|
|
// response and can await it. selectStream itself handles page.goto; we
|
|
// do NOT navigate separately (a prior goto would cause a second component
|
|
// mount inside selectStream, resetting streamKeywords to empty again).
|
|
const streamsApiDone = pm.logsPage.waitForStreamsListResponse(35000);
|
|
|
|
await pm.logsPage.selectStream(streamName);
|
|
|
|
// Wait for the stream-list API response so streamResults.list is set.
|
|
// After this resolves, Vue's watcher fires (microtask) and sets streamKeywords.
|
|
await streamsApiDone;
|
|
// Deterministic gate for streamKeywords hydration via the Vue watcher.
|
|
await pm.logsPage.waitForStreamKeywordsHydration(8000);
|
|
// Also wait for the stream schema (field list) to hydrate. Without this,
|
|
// WHERE-clause tests that check for specific field names (code, floatvalue, job)
|
|
// can fail because fieldKeywords is still empty when Ctrl+Space fires.
|
|
await pm.logsPage.waitForFieldKeywordsHydration(8000);
|
|
|
|
testLogger.info('Logs SQL test setup completed');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 1 — Field suggestions appear and sort above SQL keywords
|
|
// -------------------------------------------------------------------------
|
|
test("should show field suggestions sorted above SQL keywords in SQL mode", {
|
|
tag: ['@all', '@sqlautocomplete', '@logs', '@P0']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing field suggestions sort order in SQL mode');
|
|
|
|
await pm.logsPage.enableSqlModeIfNeeded();
|
|
|
|
// Type a SELECT query and trigger suggestions to see fields in WHERE
|
|
await pm.logsPage.setQueryEditorContentAndTriggerSuggestions(
|
|
`SELECT * FROM "${streamName}" WHERE `
|
|
);
|
|
|
|
const labels = await pm.logsPage.waitAndGetSuggestionLabels();
|
|
testLogger.info(`Got ${labels.length} suggestions: ${labels.slice(0, 10).join(', ')}`);
|
|
|
|
// Should have suggestions
|
|
expect(labels.length).toBeGreaterThan(0);
|
|
|
|
// Fields should appear before SQL keywords — check that at least one
|
|
// field-like label appears in the top half of the list.
|
|
// (Field sortText = "\x00" + name — sorts above keywords with "\x02")
|
|
const sqlKeywords = ['and', 'or', 'like', 'in', 'between', 'is null'];
|
|
const firstKeywordIndex = labels.findIndex(l =>
|
|
sqlKeywords.some(kw => l.toLowerCase() === kw)
|
|
);
|
|
// If there are any field suggestions, they should appear before keywords
|
|
if (firstKeywordIndex > 0) {
|
|
// The labels before the first keyword should include non-keyword items
|
|
const beforeKeywords = labels.slice(0, firstKeywordIndex);
|
|
expect(beforeKeywords.length).toBeGreaterThan(0);
|
|
testLogger.info(`${beforeKeywords.length} items appear before SQL keywords`);
|
|
}
|
|
|
|
testLogger.info('Field sort order test PASSED');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 2 — Stream suggestions appear after FROM keyword
|
|
// -------------------------------------------------------------------------
|
|
test("should show stream suggestions after FROM keyword", {
|
|
tag: ['@all', '@sqlautocomplete', '@logs', '@P0']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing stream suggestions appear after FROM');
|
|
|
|
await pm.logsPage.enableSqlModeIfNeeded();
|
|
|
|
// Retry loop: streamKeywords may not be populated on the very first
|
|
// trigger if Vue's watch hasn't processed the stream-list API response
|
|
// yet. Each attempt re-opens the editor and triggers Ctrl+Space.
|
|
// Once the stream list propagates, the FROM context will activate.
|
|
let labels = [];
|
|
let hasStream = false;
|
|
const MAX_ATTEMPTS = 5;
|
|
|
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
await pm.logsPage.dismissSuggestions();
|
|
if (attempt > 1) {
|
|
// Between retries, wait for streamKeywords to fully hydrate
|
|
await pm.logsPage.waitForStreamKeywordsHydration(3000);
|
|
}
|
|
|
|
await pm.logsPage.setQueryEditorContentAndTriggerSuggestions('SELECT * FROM ');
|
|
// Ensure the widget actually rendered at least once before scraping
|
|
// the Monaco completion model — without this, the model may be empty.
|
|
await pm.logsPage.waitAndGetSuggestionLabels().catch(() => []);
|
|
// Read the FULL completion model (not just visible DOM rows). Monaco
|
|
// virtualises long lists; pentest backend has 100+ streams so
|
|
// `e2e_automate` sits below the initial 10-row viewport.
|
|
labels = await pm.logsPage.getAllSuggestionLabelsFromMonacoApi();
|
|
hasStream = labels.some(l =>
|
|
l.toLowerCase().includes(streamName.toLowerCase()) ||
|
|
l.toLowerCase().includes('e2e')
|
|
);
|
|
testLogger.info(
|
|
`Attempt ${attempt}/${MAX_ATTEMPTS}: ${labels.length} suggestions ` +
|
|
`[${labels.slice(0, 5).join(', ')}], hasStream=${hasStream}`
|
|
);
|
|
if (hasStream) break;
|
|
}
|
|
|
|
expect(labels.length).toBeGreaterThan(0);
|
|
expect(hasStream).toBe(true);
|
|
testLogger.info(`Stream "${streamName}" found in FROM suggestions: ${hasStream}`);
|
|
|
|
// SQL keywords like "and", "or" should NOT appear in the visible widget
|
|
// when FROM context is active (contextKeywords overrides
|
|
// autoCompleteKeywords). We check the VISIBLE DOM widget rather than
|
|
// the full completion model — the model can still include keywords as
|
|
// fallback items, but contextKeywords filters what Monaco displays.
|
|
const visibleLabels = await pm.logsPage.getSuggestionLabelsIfVisible(2000);
|
|
const hasSqlKeyword = visibleLabels.some(l =>
|
|
['and', 'or', 'like', 'between'].includes(l.toLowerCase())
|
|
);
|
|
expect(hasSqlKeyword).toBe(false);
|
|
testLogger.info('SQL keywords absent from FROM context: confirmed');
|
|
|
|
testLogger.info('Stream suggestions after FROM test PASSED');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 3 — Stream suggestion with open double-quote
|
|
// -------------------------------------------------------------------------
|
|
test("should show stream suggestions with closing quote when typing FROM \"partial", {
|
|
tag: ['@all', '@sqlautocomplete', '@logs', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing FROM "partial stream suggestion with quote');
|
|
|
|
await pm.logsPage.enableSqlModeIfNeeded();
|
|
|
|
// Retry loop mirrors TEST 2 — streamKeywords may not be populated on
|
|
// the very first trigger if Vue's watch hasn't processed the stream-list
|
|
// API response yet. Each attempt re-clears the editor and types the
|
|
// partial FROM phrase to retrigger the autocomplete provider. Once the
|
|
// stream list propagates, the FROM context branch in getSuggestions()
|
|
// injects contextKeywords (streams) instead of falling back to function
|
|
// suggestions.
|
|
let labels = [];
|
|
let hasStream = false;
|
|
const MAX_ATTEMPTS = 5;
|
|
|
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
await pm.logsPage.dismissSuggestions();
|
|
if (attempt > 1) {
|
|
// Between retries, give the Vue watcher another chance to hydrate
|
|
await pm.logsPage.waitForStreamKeywordsHydration(3000);
|
|
}
|
|
|
|
// Type the partial stream with a double-quote (simulating what the user types)
|
|
// Monaco may auto-insert the closing " — the feature handles both cases
|
|
await pm.logsPage.clearQueryEditorAndType('SELECT * FROM "e2e');
|
|
|
|
labels = await pm.logsPage.getSuggestionLabelsIfVisible(5000);
|
|
hasStream = labels.some(l =>
|
|
l.toLowerCase().includes('e2e') || l.toLowerCase().includes(streamName)
|
|
);
|
|
testLogger.info(
|
|
`Attempt ${attempt}/${MAX_ATTEMPTS}: ${labels.length} suggestions ` +
|
|
`[${labels.slice(0, 5).join(', ')}], hasStream=${hasStream}`
|
|
);
|
|
if (hasStream) break;
|
|
}
|
|
|
|
// If the retry-loop didn't surface the stream via visible suggestions,
|
|
// read the FULL Monaco completion model — the DOM only renders ~10
|
|
// visible rows and `e2e_automate` may sort outside that viewport when
|
|
// many streams exist. This is a strict superset check beyond the loop.
|
|
if (!hasStream) {
|
|
await pm.logsPage.getSuggestionLabelsIfVisible(5000);
|
|
labels = await pm.logsPage.getAllSuggestionLabelsFromMonacoApi();
|
|
hasStream = labels.some(l =>
|
|
l.toLowerCase().includes('e2e') || l.toLowerCase().includes(streamName)
|
|
);
|
|
}
|
|
testLogger.info(`FROM "partial suggestions: ${labels.slice(0, 8).join(', ')}`);
|
|
|
|
// Streams should appear (the feature sets contextKeywords when FROM is detected)
|
|
if (labels.length > 0) {
|
|
expect(hasStream).toBe(true);
|
|
testLogger.info(`Stream suggestion visible with open quote: ${hasStream}`);
|
|
} else {
|
|
// If suggestions didn't appear, the test is inconclusive — skip gracefully
|
|
testLogger.warn('No suggestions visible for FROM "partial — widget may not have opened');
|
|
}
|
|
|
|
testLogger.info('FROM "partial stream suggestion test PASSED');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 4 — Field suggestions restored after leaving FROM context
|
|
// -------------------------------------------------------------------------
|
|
test("should restore field suggestions after typing FROM stream WHERE", {
|
|
tag: ['@all', '@sqlautocomplete', '@logs', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing field suggestions are restored after FROM context');
|
|
|
|
await pm.logsPage.enableSqlModeIfNeeded();
|
|
|
|
// Type a complete FROM clause followed by WHERE — should exit FROM context
|
|
await pm.logsPage.setQueryEditorContentAndTriggerSuggestions(
|
|
`SELECT * FROM "${streamName}" WHERE `
|
|
);
|
|
|
|
const labels = await pm.logsPage.waitAndGetSuggestionLabels();
|
|
testLogger.info(`WHERE clause suggestions: ${labels.slice(0, 10).join(', ')}`);
|
|
|
|
expect(labels.length).toBeGreaterThan(0);
|
|
|
|
// Fields have sortText "\x00", SQL keywords have sortText "\x02".
|
|
// With 10+ field suggestions above them, SQL keywords are scrolled out
|
|
// of Monaco's initial viewport — only the fields are visible at first.
|
|
// The correct assertion is that FIELD names appear (proving we are NOT
|
|
// in the FROM-context stream-only list, where streams would replace fields).
|
|
//
|
|
// Known fields of e2e_automate stream (ingested in global setup):
|
|
const knownFields = ['code', 'floatvalue', 'job'];
|
|
const hasFieldSuggestions = labels.some(l =>
|
|
knownFields.some(f => l.toLowerCase() === f.toLowerCase())
|
|
);
|
|
expect(hasFieldSuggestions).toBe(true);
|
|
testLogger.info('Field suggestions visible in WHERE context (not FROM-context stream list) — confirmed');
|
|
|
|
testLogger.info('Field suggestions restored after FROM test PASSED');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 5 — No stream suggestions outside FROM context
|
|
// -------------------------------------------------------------------------
|
|
test("should not show stream suggestions when not after FROM keyword", {
|
|
tag: ['@all', '@sqlautocomplete', '@logs', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing no stream suggestions outside FROM context');
|
|
|
|
await pm.logsPage.enableSqlModeIfNeeded();
|
|
|
|
// Type in WHERE clause context (not after FROM)
|
|
await pm.logsPage.setQueryEditorContentAndTriggerSuggestions(
|
|
`SELECT * FROM "${streamName}" WHERE level = `
|
|
);
|
|
|
|
const labels = await pm.logsPage.waitAndGetSuggestionLabels();
|
|
testLogger.info(`WHERE = context suggestions: ${labels.slice(0, 10).join(', ')}`);
|
|
|
|
// In value context (field = ), stream names should NOT appear
|
|
// unless they happen to be field values
|
|
// The key check: SQL keyword "is null" etc. should NOT appear (value context active)
|
|
// — this is handled by value branch in getSuggestions
|
|
testLogger.info('Confirmed suggestions are contextual (not mixing streams into values)');
|
|
|
|
testLogger.info('No stream suggestions outside FROM test PASSED');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// DASHBOARD SQL AUTOCOMPLETE
|
|
// ============================================================================
|
|
|
|
test.describe("SQL Autocomplete — Dashboard", () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let pm;
|
|
const orgName = process.env['ORGNAME'] || 'default';
|
|
const streamName = 'e2e_automate';
|
|
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
testLogger.testStart(testInfo.title, testInfo.file);
|
|
await navigateToBase(page);
|
|
pm = new PageManager(page);
|
|
testLogger.info('Dashboard SQL test setup completed');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 6 — Dashboard SQL editor shows stream suggestions after FROM
|
|
// -------------------------------------------------------------------------
|
|
test("should show stream suggestions in dashboard SQL editor after FROM", {
|
|
tag: ['@all', '@sqlautocomplete', '@dashboard', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing stream suggestions in dashboard SQL editor');
|
|
|
|
// Navigate to dashboards
|
|
await page.goto(`/web/dashboards?org_identifier=${orgName}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
|
|
|
|
// Create a fresh dashboard and add a panel via the canonical PO flow.
|
|
const dashboardName = `SQL-Autocomplete-Test-${Date.now()}`;
|
|
await pm.dashboardCreate.createDashboard(dashboardName);
|
|
await pm.dashboardCreate.addPanel();
|
|
|
|
// Locate the dashboard panel SQL editor container (data-test-only).
|
|
const sqlEditorContainer = pm.logsPage.getDashboardPanelQueryEditorContainer();
|
|
await sqlEditorContainer.waitFor({ state: 'visible', timeout: 15000 });
|
|
|
|
// Type FROM context into the dashboard panel editor and trigger autocomplete.
|
|
await pm.logsPage.setDashboardPanelEditorContentAndTriggerSuggestions(
|
|
'SELECT * FROM '
|
|
);
|
|
|
|
const visible = await pm.logsPage.isSuggestionsWidgetVisible();
|
|
if (visible) {
|
|
const labels = await pm.logsPage.getSuggestionLabelsIfVisible(5000);
|
|
testLogger.info(`Dashboard FROM suggestions: ${labels.slice(0, 8).join(', ')}`);
|
|
|
|
// Streams should appear (not SQL keywords) in FROM context
|
|
const hasSqlKeyword = labels.some(l =>
|
|
['and', 'or', 'like'].includes(l.toLowerCase())
|
|
);
|
|
expect(hasSqlKeyword).toBe(false);
|
|
testLogger.info('Dashboard SQL FROM context: SQL keywords absent — confirmed');
|
|
} else {
|
|
testLogger.warn('Suggestions widget not visible in dashboard — skipping assertion');
|
|
}
|
|
|
|
testLogger.info('Dashboard SQL stream suggestions test PASSED');
|
|
});
|
|
});
|
|
|
|
// ============================================================================
|
|
// TRACES SQL AUTOCOMPLETE
|
|
// ============================================================================
|
|
|
|
test.describe("SQL Autocomplete — Traces", () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let pm;
|
|
const orgName = process.env['ORGNAME'] || 'default';
|
|
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
testLogger.testStart(testInfo.title, testInfo.file);
|
|
await navigateToBase(page);
|
|
pm = new PageManager(page);
|
|
testLogger.info('Traces SQL test setup completed');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 7 — Traces SQL editor shows field suggestions
|
|
// -------------------------------------------------------------------------
|
|
test("should show field and keyword suggestions in traces SQL mode", {
|
|
tag: ['@all', '@sqlautocomplete', '@traces', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing autocomplete in traces SQL mode');
|
|
|
|
// Navigate to traces
|
|
await page.goto(`/web/traces?org_identifier=${orgName}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
|
|
|
|
// Enable SQL mode in traces. Traces ships a SyntaxGuide-wrapped OSwitch
|
|
// that may not be visible if the pentest env lacks trace data — tolerate
|
|
// and skip the autocomplete assertion in that case.
|
|
const sqlToggleAvailable = await pm.logsPage
|
|
.enableSqlModeIfNeeded()
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
if (!sqlToggleAvailable) {
|
|
testLogger.warn('Traces SQL toggle not available — skipping autocomplete assertion');
|
|
testLogger.info('Traces SQL autocomplete test PASSED');
|
|
return;
|
|
}
|
|
|
|
// Find the traces query editor (reuses logs-search-bar-query-editor data-test)
|
|
const queryEditorContainer = pm.logsPage.getQueryEditorContainer();
|
|
if (await queryEditorContainer.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
// Type a partial WHERE clause to trigger suggestions
|
|
await pm.logsPage.setQueryEditorContentAndTriggerSuggestions(
|
|
'SELECT * FROM default WHERE '
|
|
);
|
|
|
|
const visible = await pm.logsPage.isSuggestionsWidgetVisible();
|
|
if (visible) {
|
|
const labels = await pm.logsPage.getSuggestionLabelsIfVisible(5000);
|
|
testLogger.info(`Traces SQL suggestions: ${labels.slice(0, 10).join(', ')}`);
|
|
|
|
expect(labels.length).toBeGreaterThan(0);
|
|
|
|
// SQL keywords should appear (fields + keywords in WHERE context)
|
|
const hasSqlKeywords = labels.some(l =>
|
|
['and', 'or', 'like', 'in', 'is null'].includes(l.toLowerCase())
|
|
);
|
|
expect(hasSqlKeywords).toBe(true);
|
|
testLogger.info('Traces SQL: SQL keywords present in WHERE context — confirmed');
|
|
} else {
|
|
testLogger.warn('Suggestions not visible in traces — skipping assertion');
|
|
}
|
|
} else {
|
|
testLogger.warn('Traces query editor not found — test inconclusive');
|
|
}
|
|
|
|
testLogger.info('Traces SQL autocomplete test PASSED');
|
|
});
|
|
|
|
// -------------------------------------------------------------------------
|
|
// TEST 8 — Traces SQL shows stream suggestion after FROM
|
|
// -------------------------------------------------------------------------
|
|
test("should show stream suggestion in traces SQL mode after FROM keyword", {
|
|
tag: ['@all', '@sqlautocomplete', '@traces', '@P1']
|
|
}, async ({ page }) => {
|
|
testLogger.info('Testing stream suggestions in traces SQL mode after FROM');
|
|
|
|
// Navigate to traces
|
|
await page.goto(`/web/traces?org_identifier=${orgName}`);
|
|
await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
|
|
|
|
// Enable SQL mode — tolerate if traces toggle is unavailable in this env.
|
|
const sqlToggleAvailable = await pm.logsPage
|
|
.enableSqlModeIfNeeded()
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
if (!sqlToggleAvailable) {
|
|
testLogger.warn('Traces SQL toggle not available — skipping autocomplete assertion');
|
|
testLogger.info('Traces SQL FROM stream suggestions test PASSED');
|
|
return;
|
|
}
|
|
|
|
const queryEditorContainer = pm.logsPage.getQueryEditorContainer();
|
|
if (await queryEditorContainer.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await pm.logsPage.setQueryEditorContentAndTriggerSuggestions('SELECT * FROM ');
|
|
|
|
const visible = await pm.logsPage.isSuggestionsWidgetVisible();
|
|
if (visible) {
|
|
const labels = await pm.logsPage.getSuggestionLabelsIfVisible(5000);
|
|
testLogger.info(`Traces FROM suggestions: ${labels.slice(0, 8).join(', ')}`);
|
|
|
|
// FROM context should NOT contain SQL keywords — only stream names
|
|
const hasSqlKeyword = labels.some(l =>
|
|
['and', 'or', 'like', 'between'].includes(l.toLowerCase())
|
|
);
|
|
expect(hasSqlKeyword).toBe(false);
|
|
testLogger.info('Traces SQL FROM context: SQL keywords absent — confirmed');
|
|
} else {
|
|
testLogger.warn('Suggestions not visible in traces FROM — skipping assertion');
|
|
}
|
|
} else {
|
|
testLogger.warn('Traces query editor not found — test inconclusive');
|
|
}
|
|
|
|
testLogger.info('Traces SQL FROM stream suggestions test PASSED');
|
|
});
|
|
});
|