Skip to content

fix: tests for templates #1648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tests/suites/tenant/diagnostics/diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {expect, test} from '@playwright/test';
import {dsVslotsSchema, tenantName} from '../../../utils/constants';
import {NavigationTabs, TenantPage} from '../TenantPage';
import {longRunningQuery} from '../constants';
import {QueryEditor} from '../queryEditor/QueryEditor';
import {QueryEditor} from '../queryEditor/models/QueryEditor';

import {Diagnostics, DiagnosticsTab, QueriesSwitch} from './Diagnostics';

Expand Down
58 changes: 58 additions & 0 deletions tests/suites/tenant/queryEditor/models/NewSqlDropdownMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type {Locator, Page} from '@playwright/test';

import {VISIBILITY_TIMEOUT} from '../../TenantPage';

export enum TemplateCategory {
Tables = 'Tables',
Topics = 'Topics',
AsyncReplication = 'Async replication',
CDC = 'Change data capture',
Users = 'Users',
}

export enum AsyncReplicationTemplates {
Create = 'Create async replication',
Alter = 'Alter async replication',
Drop = 'Drop async replication',
}

export class NewSqlDropdownMenu {
private dropdownButton: Locator;
private menu: Locator;
private subMenu: Locator;

constructor(page: Page) {
this.dropdownButton = page.locator(
'.ydb-query-editor-controls .g-dropdown-menu__switcher-wrapper button',
);
this.menu = page.locator('.g-dropdown-menu__menu');
this.subMenu = page.locator('.g-dropdown-menu__sub-menu');
}

async clickNewSqlButton() {
await this.dropdownButton.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await this.dropdownButton.click();
}

async hoverCategory(category: TemplateCategory) {
const categoryItem = this.menu.getByRole('menuitem').filter({hasText: category});
await categoryItem.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await categoryItem.hover();
}

async selectTemplate(template: AsyncReplicationTemplates) {
const templateItem = this.subMenu.getByRole('menuitem').filter({hasText: template});
await templateItem.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await templateItem.click();
}

async isMenuVisible() {
await this.menu.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isSubMenuVisible() {
await this.subMenu.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type {Locator, Page} from '@playwright/test';

import {VISIBILITY_TIMEOUT} from '../TenantPage';
import {VISIBILITY_TIMEOUT} from '../../TenantPage';

import {QueryTabsNavigation} from './QueryTabsNavigation';
import {PaneWrapper, ResultTable} from './ResultTable';
import {SavedQueriesTable} from './SavedQueriesTable';
import {SettingsDialog} from './SettingsDialog';

export enum QueryMode {
YQLScript = 'YQL Script',
Expand Down Expand Up @@ -35,175 +40,16 @@ export enum QueryTabs {
Saved = 'Saved',
}

export class QueryTabsNavigation {
private tabsContainer: Locator;

constructor(page: Page) {
this.tabsContainer = page.locator('.ydb-query__tabs');
}

async selectTab(tabName: QueryTabs) {
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await tab.click();
}

async isTabSelected(tabName: QueryTabs): Promise<boolean> {
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
const isSelected = await tab.getAttribute('aria-selected');
return isSelected === 'true';
}

async getTabHref(tabName: QueryTabs): Promise<string | null> {
const link = this.tabsContainer.locator(`a:has(div[role="tab"][title="${tabName}"])`);
await link.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return link.getAttribute('href');
}
}

export class SettingsDialog {
private dialog: Locator;
private page: Page;

constructor(page: Page) {
this.page = page;
this.dialog = page.locator('.ydb-query-settings-dialog');
}

async changeQueryMode(mode: QueryMode) {
const dropdown = this.dialog.locator(
'.ydb-query-settings-dialog__control-wrapper_queryMode',
);
await dropdown.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await dropdown.click();
const popup = this.page.locator('.ydb-query-settings-select__popup');
await popup.getByText(mode).first().click();
await this.page.waitForTimeout(1000);
}

async changeTransactionMode(level: string) {
const dropdown = this.dialog.locator(
'.ydb-query-settings-dialog__control-wrapper_transactionMode',
);
await dropdown.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await dropdown.click();
const popup = this.page.locator('.ydb-query-settings-select__popup');
await popup.getByText(level).first().click();
await this.page.waitForTimeout(1000);
}

async changeStatsLevel(mode: string) {
const dropdown = this.dialog.locator(
'.ydb-query-settings-dialog__control-wrapper_statisticsMode',
);
await dropdown.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await dropdown.click();
const popup = this.page.locator('.ydb-query-settings-select__popup');
await popup.getByText(mode).first().click();
await this.page.waitForTimeout(1000);
}

async changeLimitRows(limitRows: number) {
const limitRowsInput = this.dialog.locator('.ydb-query-settings-dialog__limit-rows input');
await limitRowsInput.fill(limitRows.toString());
await this.page.waitForTimeout(1000);
}

async clickButton(buttonName: ButtonNames) {
const button = this.dialog.getByRole('button', {name: buttonName});
await button.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await button.click();
}

async isVisible() {
await this.dialog.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isHidden() {
await this.dialog.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}
}

class PaneWrapper {
paneWrapper: Locator;
private radioButton: Locator;

constructor(page: Page) {
this.paneWrapper = page.locator('.query-editor__pane-wrapper');
this.radioButton = this.paneWrapper.locator('.g-radio-button');
}

async selectTab(tabName: ResultTabNames) {
const tab = this.radioButton.getByLabel(tabName);
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await tab.click();
}
}

export class ResultTable {
private table: Locator;
private preview: Locator;
private resultHead: Locator;

constructor(selector: Locator) {
this.table = selector.locator('.ydb-query-execute-result__result');
this.preview = selector.locator('.kv-preview__result');
this.resultHead = selector.locator('.ydb-query-execute-result__result-head');
}

async isVisible() {
await this.table.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isHidden() {
await this.table.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isPreviewVisible() {
await this.preview.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isPreviewHidden() {
await this.preview.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async getRowCount() {
const rows = this.table.locator('tr');
return rows.count();
}

async getCellValue(row: number, col: number) {
const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`);
return cell.innerText();
}

async isResultHeaderHidden() {
await this.resultHead.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async getResultHeadText() {
await this.resultHead.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return this.resultHead.innerText();
}
}

export class QueryEditor {
settingsDialog: SettingsDialog;
paneWrapper: PaneWrapper;
queryTabs: QueryTabsNavigation;
resultTable: ResultTable;
savedQueries: SavedQueriesTable;
editorTextArea: Locator;

private page: Page;
private selector: Locator;
private editorTextArea: Locator;
private runButton: Locator;
private explainButton: Locator;
private stopButton: Locator;
Expand Down Expand Up @@ -236,6 +82,7 @@ export class QueryEditor {
this.resultTable = new ResultTable(this.selector);
this.paneWrapper = new PaneWrapper(page);
this.queryTabs = new QueryTabsNavigation(page);
this.savedQueries = new SavedQueriesTable(page);
}

async run(query: string, mode: QueryMode) {
Expand Down Expand Up @@ -390,4 +237,20 @@ export class QueryEditor {
await this.indicatorIcon.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async waitForStatus(expectedStatus: string, timeout = VISIBILITY_TIMEOUT) {
await this.executionStatus.waitFor({state: 'visible', timeout});

// Keep checking status until it matches or times out
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const status = await this.executionStatus.innerText();
if (status === expectedStatus) {
return true;
}
await this.page.waitForTimeout(100); // Small delay between checks
}

throw new Error(`Status did not change to ${expectedStatus} within ${timeout}ms`);
}
}
32 changes: 32 additions & 0 deletions tests/suites/tenant/queryEditor/models/QueryTabsNavigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type {Locator, Page} from '@playwright/test';

import {VISIBILITY_TIMEOUT} from '../../TenantPage';

import type {QueryTabs} from './QueryEditor';

export class QueryTabsNavigation {
private tabsContainer: Locator;

constructor(page: Page) {
this.tabsContainer = page.locator('.ydb-query__tabs');
}

async selectTab(tabName: QueryTabs) {
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await tab.click();
}

async isTabSelected(tabName: QueryTabs): Promise<boolean> {
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
const isSelected = await tab.getAttribute('aria-selected');
return isSelected === 'true';
}

async getTabHref(tabName: QueryTabs): Promise<string | null> {
const link = this.tabsContainer.locator(`a:has(div[role="tab"][title="${tabName}"])`);
await link.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return link.getAttribute('href');
}
}
73 changes: 73 additions & 0 deletions tests/suites/tenant/queryEditor/models/ResultTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type {Locator, Page} from '@playwright/test';

import {VISIBILITY_TIMEOUT} from '../../TenantPage';

import type {ResultTabNames} from './QueryEditor';

export class PaneWrapper {
paneWrapper: Locator;
private radioButton: Locator;

constructor(page: Page) {
this.paneWrapper = page.locator('.query-editor__pane-wrapper');
this.radioButton = this.paneWrapper.locator('.g-radio-button');
}

async selectTab(tabName: ResultTabNames) {
const tab = this.radioButton.getByLabel(tabName);
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
await tab.click();
}
}

export class ResultTable {
private table: Locator;
private preview: Locator;
private resultHead: Locator;

constructor(selector: Locator) {
this.table = selector.locator('.ydb-query-execute-result__result');
this.preview = selector.locator('.kv-preview__result');
this.resultHead = selector.locator('.ydb-query-execute-result__result-head');
}

async isVisible() {
await this.table.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isHidden() {
await this.table.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isPreviewVisible() {
await this.preview.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return true;
}

async isPreviewHidden() {
await this.preview.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async getRowCount() {
const rows = this.table.locator('tr');
return rows.count();
}

async getCellValue(row: number, col: number) {
const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`);
return cell.innerText();
}

async isResultHeaderHidden() {
await this.resultHead.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
return true;
}

async getResultHeadText() {
await this.resultHead.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
return this.resultHead.innerText();
}
}
Loading
Loading