Files
openobserve/tests/ui-testing/playwright-tests/utils/MonacoEditorHelper.js
T
+5 ae22a74e62 feat: ux improvement main (#11838)
#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>
2026-05-28 11:00:49 +00:00

176 lines
5.7 KiB
JavaScript

/**
* MonacoEditorHelper - Utility class for Monaco editor interactions
*
* Encapsulates Monaco-specific selectors and interactions to:
* 1. Keep spec files clean (POM compliance)
* 2. Centralize Monaco selector changes if Monaco version updates
* 3. Provide consistent interaction patterns across tests
*
* Note: Monaco editor is a third-party component. These selectors
* may need updates when Monaco version changes.
*/
class MonacoEditorHelper {
/**
* @param {import('@playwright/test').Page} page - Playwright page object
*/
constructor(page) {
this.page = page;
// Monaco editor selectors (third-party component)
this.selectors = {
editor: '.monaco-editor',
viewLines: '.monaco-editor .view-lines',
inputArea: '.monaco-editor textarea.inputarea',
suggestWidget: '.monaco-editor .suggest-widget',
suggestionRows: '.monaco-editor .suggest-widget .monaco-list-row',
suggestionLabel: '.label-name, .monaco-icon-label-container',
};
}
/**
* Get the Monaco editor within a container
* @param {import('@playwright/test').Locator} container - Container locator with data-test attribute
* @returns {import('@playwright/test').Locator}
*/
getEditor(container) {
return container.locator(this.selectors.viewLines).first();
}
/**
* Get the input area (textarea) within a container
* @param {import('@playwright/test').Locator} container - Container locator
* @returns {import('@playwright/test').Locator}
*/
getInputArea(container) {
return container.locator(this.selectors.inputArea);
}
/**
* Click into the Monaco editor to focus it
* @param {import('@playwright/test').Locator} container - Container locator
*/
async focus(container) {
const inputArea = this.getInputArea(container);
if (await inputArea.count() > 0) {
await inputArea.focus();
} else {
const editor = this.getEditor(container);
await editor.click({ force: true });
}
}
/**
* Clear all content from the Monaco editor
* @param {import('@playwright/test').Locator} container - Container locator
*/
async clear(container) {
await this.focus(container);
await this.page.keyboard.press('Control+a');
await this.page.keyboard.press('Backspace');
}
/**
* Type text into the Monaco editor
* @param {import('@playwright/test').Locator} container - Container locator
* @param {string} text - Text to type
*/
async type(container, text) {
await this.focus(container);
await this.page.keyboard.type(text);
}
/**
* Set content in the Monaco editor (clears first, then types)
* @param {import('@playwright/test').Locator} container - Container locator
* @param {string} content - Content to set
*/
async setContent(container, content) {
await this.clear(container);
await this.page.keyboard.type(content);
}
/**
* Get the text content of the Monaco editor
* @param {import('@playwright/test').Locator} container - Container locator
* @returns {Promise<string>}
*/
async getContent(container) {
const viewLines = container.locator(this.selectors.viewLines);
return await viewLines.textContent();
}
/**
* Trigger autocomplete suggestions (Ctrl+Space)
*/
async triggerSuggestions() {
await this.page.keyboard.press('Control+Space');
}
/**
* Wait for the suggestions widget to be visible
* @param {number} timeout - Timeout in milliseconds (default 5000)
* @returns {Promise<import('@playwright/test').Locator>}
*/
async waitForSuggestions(timeout = 5000) {
const suggestWidget = this.page.locator(this.selectors.suggestWidget);
await suggestWidget.waitFor({ state: 'visible', timeout });
return suggestWidget;
}
/**
* Check if suggestions widget is visible
* @returns {Promise<boolean>}
*/
async isSuggestionsVisible() {
const suggestWidget = this.page.locator(this.selectors.suggestWidget);
return await suggestWidget.isVisible();
}
/**
* Get all suggestion labels from the autocomplete widget
* @param {number} timeout - Timeout to wait for suggestions (default 5000)
* @returns {Promise<string[]>}
*/
async getSuggestionLabels(timeout = 5000) {
const suggestionRows = this.page.locator(this.selectors.suggestionRows);
await suggestionRows.first().waitFor({ state: 'visible', timeout });
const labels = await suggestionRows
.locator(this.selectors.suggestionLabel)
.allTextContents();
return labels.map(l => l.trim()).filter(l => l.length > 0);
}
/**
* Check if suggestions contain a specific value
* @param {string} value - Value to search for
* @returns {Promise<boolean>}
*/
async suggestionsContain(value) {
const labels = await this.getSuggestionLabels();
return labels.some(label => label.includes(value));
}
/**
* Select a suggestion by text
* @param {string} text - Text of the suggestion to select
*/
async selectSuggestion(text) {
const suggestionRow = this.page.locator(
`${this.selectors.suggestionRows}:has-text("${text}")`
).first();
await suggestionRow.click();
}
/**
* Dismiss the suggestions widget by clicking outside the editor
*/
async dismissSuggestions() {
await this.page.locator('body').click({ position: { x: 10, y: 10 } });
}
}
module.exports = MonacoEditorHelper;