import { Page, expect } from '@playwright/test'; export class TestStabilityHelper { private readonly page: Page; private readonly maxRetries: number = 3; private readonly retryDelay: number = 1000; constructor(page: Page) { this.page = page; } async waitForNetworkIdle(timeout: number = 30000): Promise { try { await this.page.waitForLoadState('networkidle', { timeout }); } catch (error) { console.log('Network idle timeout, continuing anyway'); } } async waitForElementVisible(selector: string, timeout: number = 10000): Promise { await this.retry(async () => { const element = this.page.locator(selector); await expect(element).toBeVisible({ timeout }); }); } async safeClick(selector: string): Promise { await this.retry(async () => { const element = this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: 10000 }); await element.click({ timeout: 5000 }); }); } async safeFill(selector: string, value: string): Promise { await this.retry(async () => { const element = this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: 10000 }); await element.clear(); await element.fill(value); }); } async safeSelect(selector: string, value: string): Promise { await this.retry(async () => { const element = this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: 10000 }); await element.selectOption(value); }); } async waitForURL(urlPattern: RegExp | string, timeout: number = 30000): Promise { await this.retry(async () => { await this.page.waitForURL(urlPattern, { timeout }); }); } async handleModal(): Promise { try { const modal = this.page.locator('.el-dialog, .el-message-box'); const isVisible = await modal.isVisible({ timeout: 2000 }); if (isVisible) { const confirmButton = modal.locator('.el-button--primary').first(); const cancelButton = modal.locator('.el-button--default').first(); if (await confirmButton.isVisible({ timeout: 1000 })) { await confirmButton.click(); } else if (await cancelButton.isVisible({ timeout: 1000 })) { await cancelButton.click(); } } } catch (error) { console.log('No modal found or modal handling failed'); } } async waitForLoadingComplete(): Promise { try { const loading = this.page.locator('.el-loading-mask, .loading'); await loading.waitFor({ state: 'hidden', timeout: 10000 }); } catch (error) { console.log('Loading element not found or timeout'); } } async safeNavigate(url: string): Promise { await this.retry(async () => { await this.page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); }); } async waitForTableData(tableSelector: string, minRows: number = 1): Promise { await this.retry(async () => { const table = this.page.locator(tableSelector); await expect(table).toBeVisible({ timeout: 10000 }); const rows = table.locator('.el-table__row'); const rowCount = await rows.count(); expect(rowCount).toBeGreaterThanOrEqual(minRows); }); } async safeScrollIntoView(selector: string): Promise { const element = this.page.locator(selector); await element.scrollIntoViewIfNeeded(); await this.page.waitForTimeout(500); } async clearLocalStorage(): Promise { await this.page.evaluate(() => { localStorage.clear(); }); } async clearSessionStorage(): Promise { await this.page.evaluate(() => { sessionStorage.clear(); }); } async takeScreenshot(name: string): Promise { await this.page.screenshot({ path: `test-results/screenshots/${name}.png`, fullPage: true }); } async getErrorMessage(): Promise { try { const errorElement = this.page.locator('.el-message--error, .error-message'); const isVisible = await errorElement.isVisible({ timeout: 2000 }); if (isVisible) { return await errorElement.textContent(); } return null; } catch (error) { return null; } } async hasErrorMessage(): Promise { const errorMessage = await this.getErrorMessage(); return errorMessage !== null; } private async retry(fn: () => Promise): Promise { let lastError: Error | undefined; for (let attempt = 1; attempt <= this.maxRetries; attempt++) { try { return await fn(); } catch (error) { lastError = error as Error; console.log(`Attempt ${attempt} failed, retrying...`, error); if (attempt < this.maxRetries) { await this.page.waitForTimeout(this.retryDelay); } } } throw lastError || new Error('All retry attempts failed'); } async waitForElementNotVisible(selector: string, timeout: number = 10000): Promise { await this.retry(async () => { const element = this.page.locator(selector); await expect(element).not.toBeVisible({ timeout }); }); } async safeHover(selector: string): Promise { await this.retry(async () => { const element = this.page.locator(selector); await element.waitFor({ state: 'visible', timeout: 10000 }); await element.hover({ timeout: 5000 }); }); } async waitForText(selector: string, text: string, timeout: number = 10000): Promise { await this.retry(async () => { const element = this.page.locator(selector); await expect(element).toContainText(text, { timeout }); }); } async waitForTextNotPresent(selector: string, text: string, timeout: number = 10000): Promise { await this.retry(async () => { const element = this.page.locator(selector); await expect(element).not.toContainText(text, { timeout }); }); } }