Skip to content

Commit da2a02d

Browse files
astandrikAnton Standrik
and
Anton Standrik
authoredNov 18, 2024··
fix: tests for templates (#1648)
Co-authored-by: Anton Standrik <astandrik@Antons-MacBook-Air.local>
1 parent 14f3fa4 commit da2a02d

19 files changed

+715
-178
lines changed
 

‎tests/suites/tenant/diagnostics/diagnostics.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {expect, test} from '@playwright/test';
33
import {dsVslotsSchema, tenantName} from '../../../utils/constants';
44
import {NavigationTabs, TenantPage} from '../TenantPage';
55
import {longRunningQuery} from '../constants';
6-
import {QueryEditor} from '../queryEditor/QueryEditor';
6+
import {QueryEditor} from '../queryEditor/models/QueryEditor';
77

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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type {Locator, Page} from '@playwright/test';
2+
3+
import {VISIBILITY_TIMEOUT} from '../../TenantPage';
4+
5+
export enum TemplateCategory {
6+
Tables = 'Tables',
7+
Topics = 'Topics',
8+
AsyncReplication = 'Async replication',
9+
CDC = 'Change data capture',
10+
Users = 'Users',
11+
}
12+
13+
export enum AsyncReplicationTemplates {
14+
Create = 'Create async replication',
15+
Alter = 'Alter async replication',
16+
Drop = 'Drop async replication',
17+
}
18+
19+
export class NewSqlDropdownMenu {
20+
private dropdownButton: Locator;
21+
private menu: Locator;
22+
private subMenu: Locator;
23+
24+
constructor(page: Page) {
25+
this.dropdownButton = page.locator(
26+
'.ydb-query-editor-controls .g-dropdown-menu__switcher-wrapper button',
27+
);
28+
this.menu = page.locator('.g-dropdown-menu__menu');
29+
this.subMenu = page.locator('.g-dropdown-menu__sub-menu');
30+
}
31+
32+
async clickNewSqlButton() {
33+
await this.dropdownButton.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
34+
await this.dropdownButton.click();
35+
}
36+
37+
async hoverCategory(category: TemplateCategory) {
38+
const categoryItem = this.menu.getByRole('menuitem').filter({hasText: category});
39+
await categoryItem.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
40+
await categoryItem.hover();
41+
}
42+
43+
async selectTemplate(template: AsyncReplicationTemplates) {
44+
const templateItem = this.subMenu.getByRole('menuitem').filter({hasText: template});
45+
await templateItem.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
46+
await templateItem.click();
47+
}
48+
49+
async isMenuVisible() {
50+
await this.menu.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
51+
return true;
52+
}
53+
54+
async isSubMenuVisible() {
55+
await this.subMenu.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
56+
return true;
57+
}
58+
}

‎tests/suites/tenant/queryEditor/QueryEditor.ts ‎tests/suites/tenant/queryEditor/models/QueryEditor.ts

+25-162
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import type {Locator, Page} from '@playwright/test';
22

3-
import {VISIBILITY_TIMEOUT} from '../TenantPage';
3+
import {VISIBILITY_TIMEOUT} from '../../TenantPage';
4+
5+
import {QueryTabsNavigation} from './QueryTabsNavigation';
6+
import {PaneWrapper, ResultTable} from './ResultTable';
7+
import {SavedQueriesTable} from './SavedQueriesTable';
8+
import {SettingsDialog} from './SettingsDialog';
49

510
export enum QueryMode {
611
YQLScript = 'YQL Script',
@@ -35,175 +40,16 @@ export enum QueryTabs {
3540
Saved = 'Saved',
3641
}
3742

38-
export class QueryTabsNavigation {
39-
private tabsContainer: Locator;
40-
41-
constructor(page: Page) {
42-
this.tabsContainer = page.locator('.ydb-query__tabs');
43-
}
44-
45-
async selectTab(tabName: QueryTabs) {
46-
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
47-
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
48-
await tab.click();
49-
}
50-
51-
async isTabSelected(tabName: QueryTabs): Promise<boolean> {
52-
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
53-
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
54-
const isSelected = await tab.getAttribute('aria-selected');
55-
return isSelected === 'true';
56-
}
57-
58-
async getTabHref(tabName: QueryTabs): Promise<string | null> {
59-
const link = this.tabsContainer.locator(`a:has(div[role="tab"][title="${tabName}"])`);
60-
await link.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
61-
return link.getAttribute('href');
62-
}
63-
}
64-
65-
export class SettingsDialog {
66-
private dialog: Locator;
67-
private page: Page;
68-
69-
constructor(page: Page) {
70-
this.page = page;
71-
this.dialog = page.locator('.ydb-query-settings-dialog');
72-
}
73-
74-
async changeQueryMode(mode: QueryMode) {
75-
const dropdown = this.dialog.locator(
76-
'.ydb-query-settings-dialog__control-wrapper_queryMode',
77-
);
78-
await dropdown.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
79-
await dropdown.click();
80-
const popup = this.page.locator('.ydb-query-settings-select__popup');
81-
await popup.getByText(mode).first().click();
82-
await this.page.waitForTimeout(1000);
83-
}
84-
85-
async changeTransactionMode(level: string) {
86-
const dropdown = this.dialog.locator(
87-
'.ydb-query-settings-dialog__control-wrapper_transactionMode',
88-
);
89-
await dropdown.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
90-
await dropdown.click();
91-
const popup = this.page.locator('.ydb-query-settings-select__popup');
92-
await popup.getByText(level).first().click();
93-
await this.page.waitForTimeout(1000);
94-
}
95-
96-
async changeStatsLevel(mode: string) {
97-
const dropdown = this.dialog.locator(
98-
'.ydb-query-settings-dialog__control-wrapper_statisticsMode',
99-
);
100-
await dropdown.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
101-
await dropdown.click();
102-
const popup = this.page.locator('.ydb-query-settings-select__popup');
103-
await popup.getByText(mode).first().click();
104-
await this.page.waitForTimeout(1000);
105-
}
106-
107-
async changeLimitRows(limitRows: number) {
108-
const limitRowsInput = this.dialog.locator('.ydb-query-settings-dialog__limit-rows input');
109-
await limitRowsInput.fill(limitRows.toString());
110-
await this.page.waitForTimeout(1000);
111-
}
112-
113-
async clickButton(buttonName: ButtonNames) {
114-
const button = this.dialog.getByRole('button', {name: buttonName});
115-
await button.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
116-
await button.click();
117-
}
118-
119-
async isVisible() {
120-
await this.dialog.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
121-
return true;
122-
}
123-
124-
async isHidden() {
125-
await this.dialog.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
126-
return true;
127-
}
128-
}
129-
130-
class PaneWrapper {
131-
paneWrapper: Locator;
132-
private radioButton: Locator;
133-
134-
constructor(page: Page) {
135-
this.paneWrapper = page.locator('.query-editor__pane-wrapper');
136-
this.radioButton = this.paneWrapper.locator('.g-radio-button');
137-
}
138-
139-
async selectTab(tabName: ResultTabNames) {
140-
const tab = this.radioButton.getByLabel(tabName);
141-
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
142-
await tab.click();
143-
}
144-
}
145-
146-
export class ResultTable {
147-
private table: Locator;
148-
private preview: Locator;
149-
private resultHead: Locator;
150-
151-
constructor(selector: Locator) {
152-
this.table = selector.locator('.ydb-query-execute-result__result');
153-
this.preview = selector.locator('.kv-preview__result');
154-
this.resultHead = selector.locator('.ydb-query-execute-result__result-head');
155-
}
156-
157-
async isVisible() {
158-
await this.table.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
159-
return true;
160-
}
161-
162-
async isHidden() {
163-
await this.table.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
164-
return true;
165-
}
166-
167-
async isPreviewVisible() {
168-
await this.preview.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
169-
return true;
170-
}
171-
172-
async isPreviewHidden() {
173-
await this.preview.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
174-
return true;
175-
}
176-
177-
async getRowCount() {
178-
const rows = this.table.locator('tr');
179-
return rows.count();
180-
}
181-
182-
async getCellValue(row: number, col: number) {
183-
const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`);
184-
return cell.innerText();
185-
}
186-
187-
async isResultHeaderHidden() {
188-
await this.resultHead.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
189-
return true;
190-
}
191-
192-
async getResultHeadText() {
193-
await this.resultHead.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
194-
return this.resultHead.innerText();
195-
}
196-
}
197-
19843
export class QueryEditor {
19944
settingsDialog: SettingsDialog;
20045
paneWrapper: PaneWrapper;
20146
queryTabs: QueryTabsNavigation;
20247
resultTable: ResultTable;
48+
savedQueries: SavedQueriesTable;
49+
editorTextArea: Locator;
20350

20451
private page: Page;
20552
private selector: Locator;
206-
private editorTextArea: Locator;
20753
private runButton: Locator;
20854
private explainButton: Locator;
20955
private stopButton: Locator;
@@ -236,6 +82,7 @@ export class QueryEditor {
23682
this.resultTable = new ResultTable(this.selector);
23783
this.paneWrapper = new PaneWrapper(page);
23884
this.queryTabs = new QueryTabsNavigation(page);
85+
this.savedQueries = new SavedQueriesTable(page);
23986
}
24087

24188
async run(query: string, mode: QueryMode) {
@@ -390,4 +237,20 @@ export class QueryEditor {
390237
await this.indicatorIcon.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
391238
return true;
392239
}
240+
241+
async waitForStatus(expectedStatus: string, timeout = VISIBILITY_TIMEOUT) {
242+
await this.executionStatus.waitFor({state: 'visible', timeout});
243+
244+
// Keep checking status until it matches or times out
245+
const startTime = Date.now();
246+
while (Date.now() - startTime < timeout) {
247+
const status = await this.executionStatus.innerText();
248+
if (status === expectedStatus) {
249+
return true;
250+
}
251+
await this.page.waitForTimeout(100); // Small delay between checks
252+
}
253+
254+
throw new Error(`Status did not change to ${expectedStatus} within ${timeout}ms`);
255+
}
393256
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type {Locator, Page} from '@playwright/test';
2+
3+
import {VISIBILITY_TIMEOUT} from '../../TenantPage';
4+
5+
import type {QueryTabs} from './QueryEditor';
6+
7+
export class QueryTabsNavigation {
8+
private tabsContainer: Locator;
9+
10+
constructor(page: Page) {
11+
this.tabsContainer = page.locator('.ydb-query__tabs');
12+
}
13+
14+
async selectTab(tabName: QueryTabs) {
15+
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
16+
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
17+
await tab.click();
18+
}
19+
20+
async isTabSelected(tabName: QueryTabs): Promise<boolean> {
21+
const tab = this.tabsContainer.locator(`role=tab[name="${tabName}"]`);
22+
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
23+
const isSelected = await tab.getAttribute('aria-selected');
24+
return isSelected === 'true';
25+
}
26+
27+
async getTabHref(tabName: QueryTabs): Promise<string | null> {
28+
const link = this.tabsContainer.locator(`a:has(div[role="tab"][title="${tabName}"])`);
29+
await link.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
30+
return link.getAttribute('href');
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type {Locator, Page} from '@playwright/test';
2+
3+
import {VISIBILITY_TIMEOUT} from '../../TenantPage';
4+
5+
import type {ResultTabNames} from './QueryEditor';
6+
7+
export class PaneWrapper {
8+
paneWrapper: Locator;
9+
private radioButton: Locator;
10+
11+
constructor(page: Page) {
12+
this.paneWrapper = page.locator('.query-editor__pane-wrapper');
13+
this.radioButton = this.paneWrapper.locator('.g-radio-button');
14+
}
15+
16+
async selectTab(tabName: ResultTabNames) {
17+
const tab = this.radioButton.getByLabel(tabName);
18+
await tab.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
19+
await tab.click();
20+
}
21+
}
22+
23+
export class ResultTable {
24+
private table: Locator;
25+
private preview: Locator;
26+
private resultHead: Locator;
27+
28+
constructor(selector: Locator) {
29+
this.table = selector.locator('.ydb-query-execute-result__result');
30+
this.preview = selector.locator('.kv-preview__result');
31+
this.resultHead = selector.locator('.ydb-query-execute-result__result-head');
32+
}
33+
34+
async isVisible() {
35+
await this.table.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
36+
return true;
37+
}
38+
39+
async isHidden() {
40+
await this.table.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
41+
return true;
42+
}
43+
44+
async isPreviewVisible() {
45+
await this.preview.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
46+
return true;
47+
}
48+
49+
async isPreviewHidden() {
50+
await this.preview.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
51+
return true;
52+
}
53+
54+
async getRowCount() {
55+
const rows = this.table.locator('tr');
56+
return rows.count();
57+
}
58+
59+
async getCellValue(row: number, col: number) {
60+
const cell = this.table.locator(`tr:nth-child(${row}) td:nth-child(${col})`);
61+
return cell.innerText();
62+
}
63+
64+
async isResultHeaderHidden() {
65+
await this.resultHead.waitFor({state: 'hidden', timeout: VISIBILITY_TIMEOUT});
66+
return true;
67+
}
68+
69+
async getResultHeadText() {
70+
await this.resultHead.waitFor({state: 'visible', timeout: VISIBILITY_TIMEOUT});
71+
return this.resultHead.innerText();
72+
}
73+
}

0 commit comments

Comments
 (0)
Please sign in to comment.