Files
CherryHQ-cherry-studio/tests/e2e/utils/wait-helpers.ts
亢奋猫 9ed91491c1 refactor(styling): remove styled-components usage (#16370)
Co-authored-by: gujiaming <52187003+AtomsH4@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: kangfenmao <kangfenmao@qq.com>
Signed-off-by: gujiaming <52187003+AtomsH4@users.noreply.github.com>
2026-06-26 18:40:12 +08:00

104 lines
3.2 KiB
TypeScript

import type { Page } from '@playwright/test'
/**
* Wait for the application to be fully ready.
* The app uses PersistGate which may delay initial render.
* Layout can be either Sidebar-based or TabsContainer-based depending on settings.
*/
export async function waitForAppReady(page: Page, timeout: number = 60000): Promise<void> {
// First, wait for React root to be attached
await page.waitForSelector('#root', { state: 'attached', timeout })
// Wait for main app content to render
// The app may show either:
// 1. Sidebar layout (navbarPosition === 'left')
// 2. TabsContainer layout (default)
// 3. Home page content
await page.waitForSelector(
[
'#home-page', // Home page container
'[class*="Sidebar"]', // Sidebar component
'[class*="TabsContainer"]', // Tabs container
'[class*="home-navbar"]', // Home navbar
'[class*="Container"]' // Generic generated container class
].join(', '),
{
state: 'visible',
timeout
}
)
// Additional wait for React to fully hydrate
await page.waitForLoadState('domcontentloaded')
}
/**
* Wait for navigation to a specific path.
* The app uses HashRouter, so paths are prefixed with #.
*/
export async function waitForNavigation(page: Page, path: string, timeout: number = 15000): Promise<void> {
await page.waitForURL(`**/#${path}**`, { timeout })
}
/**
* Wait for the chat interface to be ready.
*/
export async function waitForChatReady(page: Page, timeout: number = 30000): Promise<void> {
await page.waitForSelector(
['#home-page', '[class*="Chat"]', '[class*="Inputbar"]', '[class*="home-tabs"]'].join(', '),
{ state: 'visible', timeout }
)
}
/**
* Wait for the settings page to load.
*/
export async function waitForSettingsLoad(page: Page, timeout: number = 30000): Promise<void> {
await page.waitForSelector(['[class*="SettingsPage"]', '[class*="Settings"]', 'a[href*="/settings/"]'].join(', '), {
state: 'visible',
timeout
})
}
/**
* Wait for a modal/dialog to appear.
*/
export async function waitForModal(page: Page, timeout: number = 10000): Promise<void> {
await page.waitForSelector('.ant-modal, [role="dialog"], .ant-drawer', { state: 'visible', timeout })
}
/**
* Wait for a modal/dialog to close.
*/
export async function waitForModalClose(page: Page, timeout: number = 10000): Promise<void> {
await page.waitForSelector('.ant-modal, [role="dialog"], .ant-drawer', { state: 'hidden', timeout })
}
/**
* Wait for loading state to complete.
*/
export async function waitForLoadingComplete(page: Page, timeout: number = 30000): Promise<void> {
const spinner = page.locator('.ant-spin, [class*="Loading"], [class*="Spinner"]')
if ((await spinner.count()) > 0) {
await spinner.first().waitFor({ state: 'hidden', timeout })
}
}
/**
* Wait for a notification/toast to appear.
*/
export async function waitForNotification(page: Page, timeout: number = 10000): Promise<void> {
await page.waitForSelector('.ant-notification, .ant-message, [class*="Notification"]', {
state: 'visible',
timeout
})
}
/**
* Sleep for a specified duration.
* Use sparingly - prefer explicit waits when possible.
*/
export async function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
}