Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

90 rindas
4.0 KiB

  1. import fs from 'node:fs'
  2. import path from 'node:path'
  3. import type { APIRequestContext, Locator, Page } from '@playwright/test'
  4. import { expect } from './fixtures'
  5. export interface AdminCredentials { username: string; password: string }
  6. export function readAdminCredentials(): AdminCredentials {
  7. const configured = process.env.E2E_ACCOUNT_FILE?.trim()
  8. const filename = path.resolve(configured || '../账号.MD')
  9. if (!fs.existsSync(filename)) throw new Error(`测试账号文件不存在:${filename}`)
  10. const values = new Map<string, string>()
  11. for (const line of fs.readFileSync(filename, 'utf8').split(/\r?\n/)) {
  12. const match = line.match(/^\s*([^::]+)\s*[::]\s*(.*?)\s*$/)
  13. if (match?.[1] && match[2]) values.set(match[1].trim(), match[2].trim())
  14. }
  15. const username = values.get('账号') || values.get('用户名') || ''
  16. const password = values.get('密码') || ''
  17. if (!username || !password) throw new Error('账号.MD 必须包含“账号”和“密码”两项')
  18. return { username, password }
  19. }
  20. type RecordValue = Record<string, unknown>
  21. const asRecord = (value: unknown): RecordValue => value && typeof value === 'object' ? value as RecordValue : {}
  22. export const envelopeData = <T = unknown>(value: unknown): T => {
  23. const body = asRecord(value)
  24. return ('data' in body ? body.data : value) as T
  25. }
  26. export async function adminAccessToken(request: APIRequestContext) {
  27. const credentials = readAdminCredentials()
  28. const response = await request.post('/api/auth/v1/auth/login', {
  29. data: { roleCode: 'admin', username: credentials.username, password: credentials.password, rememberMe: false },
  30. })
  31. expect(response.ok(), `管理员 API 登录失败:HTTP ${response.status()}`).toBeTruthy()
  32. const data = asRecord(envelopeData(await response.json()))
  33. const tokens = asRecord(data.tokens)
  34. const accessToken = String(data.accessToken ?? tokens.accessToken ?? '')
  35. if (!accessToken) throw new Error('管理员登录响应缺少 accessToken')
  36. return accessToken
  37. }
  38. export async function authHeaders(request: APIRequestContext) {
  39. const accessToken = await adminAccessToken(request)
  40. return { Authorization: `Bearer ${accessToken}` }
  41. }
  42. export async function loginAsAdmin(page: Page) {
  43. const credentials = readAdminCredentials()
  44. await page.goto('/login')
  45. await page.locator('[data-role-code="admin"]').click()
  46. const username = page.locator('input[name="username"]')
  47. const password = page.locator('input[name="password"]')
  48. await username.fill(credentials.username)
  49. await password.fill(credentials.password)
  50. await page.getByRole('button', { name: /登录系统|正在验证身份/ }).click()
  51. await expect(page).not.toHaveURL(/\/login(?:\?|$)/)
  52. await expect(page.locator('.app-shell, .main-layout, .platform-shell').first()).toBeVisible()
  53. }
  54. export const visibleDialog = (page: Page) => page.locator('.el-dialog:visible').last()
  55. export const formItem = (scope: Locator, label: string | RegExp) => scope.locator('.el-form-item').filter({ hasText: label }).first()
  56. export async function fillFormItem(scope: Locator, label: string | RegExp, value: string) {
  57. const control = formItem(scope, label).locator('input:not([type="hidden"]), textarea').first()
  58. await expect(control).toBeVisible()
  59. await control.fill(value)
  60. }
  61. export async function chooseSelectOption(page: Page, scope: Locator, label: string | RegExp, option: string | RegExp) {
  62. const item = formItem(scope, label)
  63. await item.locator('.el-select').click()
  64. const dropdown = page.locator('.el-select-dropdown:visible').last()
  65. await expect(dropdown).toBeVisible()
  66. await dropdown.locator('.el-select-dropdown__item').filter({ hasText: option }).first().click()
  67. }
  68. export async function confirmMessageBox(page: Page, buttonName: string | RegExp) {
  69. const box = page.locator('.el-message-box:visible').last()
  70. await expect(box).toBeVisible()
  71. await box.getByRole('button', { name: buttonName }).click()
  72. }
  73. export const tableRowByText = (page: Page, text: string) => page
  74. .locator('.el-table__body-wrapper .el-table__row, tbody tr')
  75. .filter({ hasText: text })
  76. .first()