Skip to content

Commit ddce99c

Browse files
committed
add additional tests
1 parent 6a29389 commit ddce99c

16 files changed

+1486
-91
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
* Unless required by applicable law or agreed to in writing, software
7+
* distributed under the License is distributed on an "AS IS" BASIS,
8+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9+
* See the License for the specific language governing permissions and
10+
* limitations under the License.
11+
*/
12+
13+
import { Locator, Page } from '@playwright/test';
14+
import { BasePage } from './base-page';
15+
16+
export class FolderRenamePage extends BasePage {
17+
readonly folderList: Locator;
18+
readonly contextMenu: Locator;
19+
readonly renameMenuItem: Locator;
20+
readonly deleteMenuItem: Locator;
21+
readonly moveToTrashMenuItem: Locator;
22+
readonly renameModal: Locator;
23+
readonly renameInput: Locator;
24+
readonly confirmButton: Locator;
25+
readonly cancelButton: Locator;
26+
readonly validationError: Locator;
27+
readonly deleteIcon: Locator;
28+
readonly deleteConfirmation: Locator;
29+
readonly deleteConfirmButton: Locator;
30+
readonly deleteCancelButton: Locator;
31+
32+
constructor(page: Page) {
33+
super(page);
34+
this.folderList = page.locator('zeppelin-node-list');
35+
this.contextMenu = page.locator('.operation'); // Operation buttons area instead of dropdown
36+
this.renameMenuItem = page.locator('a[nz-tooltip][nztooltiptitle="Rename folder"]').first();
37+
this.deleteMenuItem = page.locator('a[nz-tooltip][nztooltiptitle="Move folder to Trash"]').first();
38+
this.moveToTrashMenuItem = page.locator('a[nz-tooltip][nztooltiptitle="Move folder to Trash"]').first();
39+
this.renameModal = page.locator('.ant-modal');
40+
this.renameInput = page.locator('input[placeholder="Insert New Name"]');
41+
this.confirmButton = page.getByRole('button', { name: 'Rename' });
42+
this.cancelButton = page.locator('.ant-modal-close-x'); // Modal close button
43+
this.validationError = page.locator(
44+
'.ant-form-item-explain, .error-message, .validation-error, .ant-form-item-explain-error'
45+
);
46+
this.deleteIcon = page.locator('i[nz-icon][nztype="delete"]');
47+
this.deleteConfirmation = page.locator('.ant-popover').filter({ hasText: 'This folder will be moved to trash.' });
48+
this.deleteConfirmButton = page.getByRole('button', { name: 'OK' }).last();
49+
this.deleteCancelButton = page.getByRole('button', { name: 'Cancel' }).last();
50+
}
51+
52+
async navigate(): Promise<void> {
53+
await this.page.goto('/#/');
54+
await this.waitForPageLoad();
55+
}
56+
57+
async hoverOverFolder(folderName: string): Promise<void> {
58+
// Wait for the folder list to be loaded
59+
await this.folderList.waitFor({ state: 'visible' });
60+
61+
// Find the folder node by locating the .node that contains the specific folder name
62+
// Use a more reliable selector that targets the folder name exactly
63+
const folderNode = this.page
64+
.locator('.node')
65+
.filter({
66+
has: this.page.locator('.folder .name', { hasText: folderName })
67+
})
68+
.first();
69+
70+
// Wait for the folder to be visible and hover over the entire .node container
71+
await folderNode.waitFor({ state: 'visible' });
72+
await folderNode.hover();
73+
74+
// Wait for hover effects to take place by checking for interactive elements
75+
await folderNode
76+
.locator('a[nz-tooltip], i[nztype], button')
77+
.first()
78+
.waitFor({
79+
state: 'visible',
80+
timeout: 2000
81+
})
82+
.catch(() => {
83+
console.log('No interactive elements found after hover, continuing...');
84+
});
85+
}
86+
87+
async clickDeleteIcon(folderName: string): Promise<void> {
88+
// First hover over the folder to reveal the delete icon
89+
await this.hoverOverFolder(folderName);
90+
91+
// Find the specific folder node and its delete button
92+
const folderNode = this.page
93+
.locator('.node')
94+
.filter({
95+
has: this.page.locator('.folder .name', { hasText: folderName })
96+
})
97+
.first();
98+
99+
const deleteIcon = folderNode.locator('a[nz-tooltip][nztooltiptitle="Move folder to Trash"]');
100+
await deleteIcon.click();
101+
}
102+
103+
async clickRenameMenuItem(folderName?: string): Promise<void> {
104+
if (folderName) {
105+
// Ensure the specific folder is hovered first
106+
await this.hoverOverFolder(folderName);
107+
108+
// Find the specific folder node and its rename button
109+
const folderNode = this.page
110+
.locator('.node')
111+
.filter({
112+
has: this.page.locator('.folder .name', { hasText: folderName })
113+
})
114+
.first();
115+
116+
const renameIcon = folderNode.locator('a[nz-tooltip][nztooltiptitle="Rename folder"]');
117+
await renameIcon.click();
118+
119+
// Wait for modal to appear by checking for its presence
120+
await this.renameModal.waitFor({ state: 'visible', timeout: 3000 });
121+
} else {
122+
// Fallback to generic rename button (now using .first() to avoid strict mode)
123+
await this.renameMenuItem.click();
124+
await this.renameModal.waitFor({ state: 'visible', timeout: 3000 });
125+
}
126+
}
127+
128+
async enterNewName(name: string): Promise<void> {
129+
await this.renameInput.fill(name);
130+
}
131+
132+
async clearNewName(): Promise<void> {
133+
await this.renameInput.clear();
134+
}
135+
136+
async clickConfirm(): Promise<void> {
137+
await this.confirmButton.click();
138+
139+
// Wait for validation or submission to process by monitoring modal state
140+
await this.page
141+
.waitForFunction(
142+
() => {
143+
// Check if modal is still open or if validation errors appeared
144+
const modal = document.querySelector('.ant-modal-wrap');
145+
const validationErrors = document.querySelectorAll('.ant-form-item-explain-error, .has-error');
146+
147+
// If modal closed or validation errors appeared, processing is complete
148+
return !modal || validationErrors.length > 0 || (modal && getComputedStyle(modal).display === 'none');
149+
},
150+
{ timeout: 2000 }
151+
)
152+
.catch(() => {
153+
console.log('Modal state check timeout, continuing...');
154+
});
155+
}
156+
157+
async clickCancel(): Promise<void> {
158+
await this.cancelButton.click();
159+
}
160+
161+
async isRenameModalVisible(): Promise<boolean> {
162+
return this.renameModal.isVisible();
163+
}
164+
165+
async getRenameInputValue(): Promise<string> {
166+
return (await this.renameInput.inputValue()) || '';
167+
}
168+
169+
async isValidationErrorVisible(): Promise<boolean> {
170+
return this.validationError.isVisible();
171+
}
172+
173+
async isConfirmButtonDisabled(): Promise<boolean> {
174+
return !(await this.confirmButton.isEnabled());
175+
}
176+
177+
async isFolderVisible(folderName: string): Promise<boolean> {
178+
return this.page
179+
.locator('.node')
180+
.filter({
181+
has: this.page.locator('.folder .name', { hasText: folderName })
182+
})
183+
.first()
184+
.isVisible();
185+
}
186+
}

0 commit comments

Comments
 (0)