import type { Locator, Page } from '@playwright/test' import { expect } from './fixtures' export interface Credentials { username: string password: string roleCode?: 'admin' | 'administrative' | 'teacher' | 'student' } const roleLabels = { admin: '管理员', administrative: '行政', teacher: '教员', student: '学员', } as const const readRequired = (name: string) => { const value = process.env[name]?.trim() if (!value) throw new Error(`缺少环境变量 ${name};请参考 ute2e/.env.example 配置测试凭据。`) return value } const readOptional = (name: string) => process.env[name]?.trim() || '' export const missingLoginOrganizationEnvironment = (roleCode: Credentials['roleCode']) => { if (roleCode === 'administrative') { return readOptional('E2E_LOGIN_DEPARTMENT_NAME') ? [] : ['E2E_LOGIN_DEPARTMENT_NAME'] } if (roleCode === 'teacher') { return [ !readOptional('E2E_LOGIN_PARENT_DEPARTMENT_NAME') && 'E2E_LOGIN_PARENT_DEPARTMENT_NAME', !readOptional('E2E_LOGIN_DEPARTMENT_NAME') && 'E2E_LOGIN_DEPARTMENT_NAME', ].filter((name): name is string => Boolean(name)) } return [] } const selectVisibleOption = async (select: Locator, requestedLabel: string, aliases: string[] = []) => { await expect(select).toBeVisible() await expect(select).toBeEnabled() const availableLabels = (await select.locator('option:not([disabled])').allTextContents()).map((label) => label.trim()) const selectedLabel = [requestedLabel, ...aliases].find((label) => availableLabels.includes(label)) if (!selectedLabel) { throw new Error(`登录组织选项中找不到“${requestedLabel}”;当前可选项:${availableLabels.join('、') || '无'}。`) } await select.selectOption({ label: selectedLabel }) } export async function selectLoginOrganization(page: Page, roleCode: Credentials['roleCode']) { if (roleCode !== 'administrative' && roleCode !== 'teacher') return const missingEnvironment = missingLoginOrganizationEnvironment(roleCode) if (missingEnvironment.length) { throw new Error(`角色“${roleLabels[roleCode]}”登录需要配置 ${missingEnvironment.join('、')}。`) } const departmentName = readOptional('E2E_LOGIN_DEPARTMENT_NAME') if (roleCode === 'administrative') { await selectVisibleOption(page.getByTestId('login-department'), departmentName) return } const parentDepartmentName = readOptional('E2E_LOGIN_PARENT_DEPARTMENT_NAME') await selectVisibleOption(page.getByTestId('login-organization'), parentDepartmentName) await selectVisibleOption( page.getByTestId('login-department'), departmentName, departmentName === parentDepartmentName ? [`${departmentName}(本级)`] : [], ) } export const adminCredentials = (): Credentials => ({ username: readRequired('E2E_ADMIN_USERNAME'), password: readRequired('E2E_ADMIN_PASSWORD'), roleCode: 'admin', }) export async function login(page: Page, credentials: Credentials) { await page.goto('/login') const roleCode = credentials.roleCode ?? 'admin' await page.locator(`[data-role-code="${roleCode}"]`).click() await selectLoginOrganization(page, roleCode) await page.locator('input[name="username"]').fill(credentials.username) await page.locator('input[name="password"]').fill(credentials.password) await page.getByRole('button', { name: /登录系统|正在验证身份/ }).click() await expect(page).not.toHaveURL(/\/login(?:\?|$)/) await expect(page.locator('.platform-shell')).toBeVisible() } export async function loginAsAdmin(page: Page) { await login(page, adminCredentials()) } export const visibleDialog = (page: Page) => page.locator('.el-dialog:visible').last() export const formItem = (scope: Locator, label: string | RegExp) => scope .locator('.el-form-item') .filter({ hasText: label }) .first() export async function fillFormItem(scope: Locator, label: string | RegExp, value: string) { const control = formItem(scope, label).locator('input:not([type="hidden"]), textarea').first() await expect(control).toBeVisible() await control.fill(value) } export async function chooseSelectOption( page: Page, scope: Locator, label: string | RegExp, option: string | RegExp, ) { const item = formItem(scope, label) await item.locator('.el-select').click() const dropdown = page.locator('.el-select-dropdown:visible').last() await expect(dropdown).toBeVisible() await dropdown.locator('.el-select-dropdown__item').filter({ hasText: option }).first().click() } export async function confirmMessageBox(page: Page, buttonName: string | RegExp) { const box = page.locator('.el-message-box:visible').last() await expect(box).toBeVisible() await box.getByRole('button', { name: buttonName }).click() } export async function dismissOverlays(page: Page) { for (let index = 0; index < 3; index += 1) await page.keyboard.press('Escape') } export const tableRowByText = (page: Page, text: string) => page .locator('.el-table__body-wrapper .el-table__row') .filter({ hasText: text }) .first() export const roleCardByText = (page: Page, text: string) => page .locator('.roles-table .el-table__body-wrapper .el-table__row') .filter({ hasText: text }) .first()