Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

140 wiersze
5.1 KiB

  1. import type { Locator, Page } from '@playwright/test'
  2. import { expect } from './fixtures'
  3. export interface Credentials {
  4. username: string
  5. password: string
  6. roleCode?: 'admin' | 'administrative' | 'teacher' | 'student'
  7. }
  8. const roleLabels = {
  9. admin: '管理员',
  10. administrative: '行政',
  11. teacher: '教员',
  12. student: '学员',
  13. } as const
  14. const readRequired = (name: string) => {
  15. const value = process.env[name]?.trim()
  16. if (!value) throw new Error(`缺少环境变量 ${name};请参考 ute2e/.env.example 配置测试凭据。`)
  17. return value
  18. }
  19. const readOptional = (name: string) => process.env[name]?.trim() || ''
  20. export const missingLoginOrganizationEnvironment = (roleCode: Credentials['roleCode']) => {
  21. if (roleCode === 'administrative') {
  22. return readOptional('E2E_LOGIN_DEPARTMENT_NAME') ? [] : ['E2E_LOGIN_DEPARTMENT_NAME']
  23. }
  24. if (roleCode === 'teacher') {
  25. return [
  26. !readOptional('E2E_LOGIN_PARENT_DEPARTMENT_NAME') && 'E2E_LOGIN_PARENT_DEPARTMENT_NAME',
  27. !readOptional('E2E_LOGIN_DEPARTMENT_NAME') && 'E2E_LOGIN_DEPARTMENT_NAME',
  28. ].filter((name): name is string => Boolean(name))
  29. }
  30. return []
  31. }
  32. const selectVisibleOption = async (select: Locator, requestedLabel: string, aliases: string[] = []) => {
  33. await expect(select).toBeVisible()
  34. await expect(select).toBeEnabled()
  35. const availableLabels = (await select.locator('option:not([disabled])').allTextContents()).map((label) => label.trim())
  36. const selectedLabel = [requestedLabel, ...aliases].find((label) => availableLabels.includes(label))
  37. if (!selectedLabel) {
  38. throw new Error(`登录组织选项中找不到“${requestedLabel}”;当前可选项:${availableLabels.join('、') || '无'}。`)
  39. }
  40. await select.selectOption({ label: selectedLabel })
  41. }
  42. export async function selectLoginOrganization(page: Page, roleCode: Credentials['roleCode']) {
  43. if (roleCode !== 'administrative' && roleCode !== 'teacher') return
  44. const missingEnvironment = missingLoginOrganizationEnvironment(roleCode)
  45. if (missingEnvironment.length) {
  46. throw new Error(`角色“${roleLabels[roleCode]}”登录需要配置 ${missingEnvironment.join('、')}。`)
  47. }
  48. const departmentName = readOptional('E2E_LOGIN_DEPARTMENT_NAME')
  49. if (roleCode === 'administrative') {
  50. await selectVisibleOption(page.getByTestId('login-department'), departmentName)
  51. return
  52. }
  53. const parentDepartmentName = readOptional('E2E_LOGIN_PARENT_DEPARTMENT_NAME')
  54. await selectVisibleOption(page.getByTestId('login-organization'), parentDepartmentName)
  55. await selectVisibleOption(
  56. page.getByTestId('login-department'),
  57. departmentName,
  58. departmentName === parentDepartmentName ? [`${departmentName}(本级)`] : [],
  59. )
  60. }
  61. export const adminCredentials = (): Credentials => ({
  62. username: readRequired('E2E_ADMIN_USERNAME'),
  63. password: readRequired('E2E_ADMIN_PASSWORD'),
  64. roleCode: 'admin',
  65. })
  66. export async function login(page: Page, credentials: Credentials) {
  67. await page.goto('/login')
  68. const roleCode = credentials.roleCode ?? 'admin'
  69. await page.locator(`[data-role-code="${roleCode}"]`).click()
  70. await selectLoginOrganization(page, roleCode)
  71. await page.locator('input[name="username"]').fill(credentials.username)
  72. await page.locator('input[name="password"]').fill(credentials.password)
  73. await page.getByRole('button', { name: /登录系统|正在验证身份/ }).click()
  74. await expect(page).not.toHaveURL(/\/login(?:\?|$)/)
  75. await expect(page.locator('.platform-shell')).toBeVisible()
  76. }
  77. export async function loginAsAdmin(page: Page) {
  78. await login(page, adminCredentials())
  79. }
  80. export const visibleDialog = (page: Page) => page.locator('.el-dialog:visible').last()
  81. export const formItem = (scope: Locator, label: string | RegExp) => scope
  82. .locator('.el-form-item')
  83. .filter({ hasText: label })
  84. .first()
  85. export async function fillFormItem(scope: Locator, label: string | RegExp, value: string) {
  86. const control = formItem(scope, label).locator('input:not([type="hidden"]), textarea').first()
  87. await expect(control).toBeVisible()
  88. await control.fill(value)
  89. }
  90. export async function chooseSelectOption(
  91. page: Page,
  92. scope: Locator,
  93. label: string | RegExp,
  94. option: string | RegExp,
  95. ) {
  96. const item = formItem(scope, label)
  97. await item.locator('.el-select').click()
  98. const dropdown = page.locator('.el-select-dropdown:visible').last()
  99. await expect(dropdown).toBeVisible()
  100. await dropdown.locator('.el-select-dropdown__item').filter({ hasText: option }).first().click()
  101. }
  102. export async function confirmMessageBox(page: Page, buttonName: string | RegExp) {
  103. const box = page.locator('.el-message-box:visible').last()
  104. await expect(box).toBeVisible()
  105. await box.getByRole('button', { name: buttonName }).click()
  106. }
  107. export async function dismissOverlays(page: Page) {
  108. for (let index = 0; index < 3; index += 1) await page.keyboard.press('Escape')
  109. }
  110. export const tableRowByText = (page: Page, text: string) => page
  111. .locator('.el-table__body-wrapper .el-table__row')
  112. .filter({ hasText: text })
  113. .first()
  114. export const roleCardByText = (page: Page, text: string) => page
  115. .locator('.roles-table .el-table__body-wrapper .el-table__row')
  116. .filter({ hasText: text })
  117. .first()