283 lines
8.0 KiB
TypeScript
283 lines
8.0 KiB
TypeScript
import { Page, Locator } from '@playwright/test';
|
|
|
|
export class TestHelpers {
|
|
static async waitForElementVisible(locator: Locator, timeout: number = 5000): Promise<boolean> {
|
|
try {
|
|
await locator.waitFor({ state: 'visible', timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async waitForElementHidden(locator: Locator, timeout: number = 5000): Promise<boolean> {
|
|
try {
|
|
await locator.waitFor({ state: 'hidden', timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async safeClick(locator: Locator, timeout: number = 5000): Promise<boolean> {
|
|
try {
|
|
await locator.waitFor({ state: 'visible', timeout });
|
|
await locator.click();
|
|
return true;
|
|
} catch (error) {
|
|
console.warn('Safe click failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async safeFill(locator: Locator, value: string, timeout: number = 5000): Promise<boolean> {
|
|
try {
|
|
await locator.waitFor({ state: 'visible', timeout });
|
|
await locator.clear();
|
|
await locator.fill(value);
|
|
return true;
|
|
} catch (error) {
|
|
console.warn('Safe fill failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async safeSelect(locator: Locator, value: string, timeout: number = 5000): Promise<boolean> {
|
|
try {
|
|
await locator.waitFor({ state: 'visible', timeout });
|
|
await locator.selectOption(value);
|
|
return true;
|
|
} catch (error) {
|
|
console.warn('Safe select failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async retryOperation<T>(
|
|
operation: () => Promise<T>,
|
|
maxRetries: number = 3,
|
|
delayMs: number = 1000
|
|
): Promise<T | null> {
|
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
try {
|
|
return await operation();
|
|
} catch (error) {
|
|
if (attempt === maxRetries) {
|
|
console.error(`Operation failed after ${maxRetries} attempts:`, error);
|
|
return null;
|
|
}
|
|
console.log(`Attempt ${attempt} failed, retrying in ${delayMs}ms...`);
|
|
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static async waitForNetworkIdle(page: Page, timeout: number = 10000): Promise<void> {
|
|
try {
|
|
await page.waitForLoadState('networkidle', { timeout });
|
|
} catch (error) {
|
|
console.warn('Network idle timeout, continuing...');
|
|
}
|
|
}
|
|
|
|
static async waitForNavigation(page: Page, urlPattern: RegExp, timeout: number = 10000): Promise<boolean> {
|
|
try {
|
|
await page.waitForURL(urlPattern, { timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async handleDialog(page: Page, action: 'accept' | 'dismiss' = 'accept'): Promise<void> {
|
|
page.on('dialog', async dialog => {
|
|
if (action === 'accept') {
|
|
await dialog.accept();
|
|
} else {
|
|
await dialog.dismiss();
|
|
}
|
|
});
|
|
}
|
|
|
|
static async getTableData(table: Locator): Promise<string[][]> {
|
|
const rows = await table.locator('tbody tr').all();
|
|
const data: string[][] = [];
|
|
|
|
for (const row of rows) {
|
|
const cells = await row.locator('td').allTextContents();
|
|
data.push(cells);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static async findTableRowByContent(table: Locator, content: string): Promise<Locator | null> {
|
|
const rows = await table.locator('tbody tr').all();
|
|
|
|
for (const row of rows) {
|
|
const textContent = await row.textContent();
|
|
if (textContent && textContent.includes(content)) {
|
|
return row;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static async scrollToElement(page: Page, locator: Locator): Promise<void> {
|
|
await locator.scrollIntoViewIfNeeded();
|
|
await page.waitForTimeout(300);
|
|
}
|
|
|
|
static async waitForAnimation(locator: Locator): Promise<void> {
|
|
await locator.waitFor({ state: 'attached' });
|
|
await locator.evaluate(el => {
|
|
return new Promise(resolve => {
|
|
requestAnimationFrame(() => {
|
|
setTimeout(resolve, 300);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
static async takeScreenshot(page: Page, name: string): Promise<void> {
|
|
await page.screenshot({ path: `test-results/screenshots/${name}.png`, fullPage: true });
|
|
}
|
|
|
|
static async waitForPageLoad(page: Page, timeout: number = 10000): Promise<void> {
|
|
try {
|
|
await page.waitForLoadState('load', { timeout });
|
|
} catch (error) {
|
|
console.warn('Page load timeout, continuing...');
|
|
}
|
|
}
|
|
|
|
static async waitForDOMContent(page: Page, timeout: number = 10000): Promise<void> {
|
|
try {
|
|
await page.waitForLoadState('domcontentloaded', { timeout });
|
|
} catch (error) {
|
|
console.warn('DOM content load timeout, continuing...');
|
|
}
|
|
}
|
|
|
|
static async isElementVisible(locator: Locator): Promise<boolean> {
|
|
try {
|
|
return await locator.isVisible({ timeout: 1000 });
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async isElementEnabled(locator: Locator): Promise<boolean> {
|
|
try {
|
|
return await locator.isEnabled({ timeout: 1000 });
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async getElementText(locator: Locator): Promise<string | null> {
|
|
try {
|
|
return await locator.textContent({ timeout: 5000 });
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static async getElementCount(locator: Locator): Promise<number> {
|
|
try {
|
|
return await locator.count();
|
|
} catch {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static async waitForTextContent(locator: Locator, expectedText: string, timeout: number = 5000): Promise<boolean> {
|
|
try {
|
|
await locator.waitFor({ state: 'visible', timeout });
|
|
const text = await locator.textContent();
|
|
return text !== null && text.includes(expectedText);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async clearInput(locator: Locator): Promise<void> {
|
|
await locator.click();
|
|
await locator.fill('');
|
|
await locator.press('Control+A');
|
|
await locator.press('Backspace');
|
|
}
|
|
|
|
static async waitForSuccessMessage(page: Page, timeout: number = 5000): Promise<boolean> {
|
|
const successMessage = page.locator('.el-message--success, .success-message, [class*="success"]');
|
|
try {
|
|
await successMessage.waitFor({ state: 'visible', timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async waitForErrorMessage(page: Page, timeout: number = 5000): Promise<boolean> {
|
|
const errorMessage = page.locator('.el-message--error, .error-message, [class*="error"]');
|
|
try {
|
|
await errorMessage.waitFor({ state: 'visible', timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async waitForLoadingComplete(page: Page, timeout: number = 10000): Promise<void> {
|
|
const loadingSpinner = page.locator('.el-loading-mask, .loading, [class*="loading"]');
|
|
|
|
try {
|
|
await loadingSpinner.waitFor({ state: 'visible', timeout: 2000 });
|
|
await loadingSpinner.waitFor({ state: 'hidden', timeout });
|
|
} catch {
|
|
console.log('No loading spinner found or already hidden');
|
|
}
|
|
}
|
|
|
|
static async waitForModal(page: Page, timeout: number = 5000): Promise<boolean> {
|
|
const modal = page.locator('.el-dialog, .modal, [role="dialog"]');
|
|
try {
|
|
await modal.waitFor({ state: 'visible', timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async closeModal(page: Page): Promise<boolean> {
|
|
const closeButton = page.locator('.el-dialog__close, .modal-close, button[aria-label="Close"]');
|
|
try {
|
|
await closeButton.click();
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async waitForSelectDropdown(page: Page, timeout: number = 5000): Promise<boolean> {
|
|
const dropdown = page.locator('.el-select-dropdown, .select-dropdown');
|
|
try {
|
|
await dropdown.waitFor({ state: 'visible', timeout });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static async selectFromDropdown(page: Page, value: string): Promise<boolean> {
|
|
const option = page.locator('.el-select-dropdown__item, .select-option').filter({ hasText: value });
|
|
try {
|
|
await option.click();
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
} |