Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

80 řádky
5.4 KiB

  1. import fs from 'node:fs'
  2. import path from 'node:path'
  3. import assert from 'node:assert/strict'
  4. import { createRequire } from 'node:module'
  5. import { fileURLToPath } from 'node:url'
  6. import { login, call } from './role-permissions-live.mjs'
  7. const require = createRequire(new URL('../package.json', import.meta.url))
  8. const { chromium, expect } = require('@playwright/test')
  9. const report = fileURLToPath(new URL('../reports/narration-dialog-layout-20260909/', import.meta.url))
  10. fs.mkdirSync(report, { recursive: true })
  11. const session = await login('root')
  12. const project = (await call('tran/v1/content/projects?type=TRAINING&size=100', session.accessToken)).data.records.find(row => row.name.includes('APE2E-数字人讲解-狐狸'))
  13. assert.ok(project, 'Existing narration test project available')
  14. const browser = await chromium.launch({ channel: 'msedge', headless: true, args: ['--enable-unsafe-swiftshader'] })
  15. const checks = []
  16. let page
  17. try {
  18. const context = await browser.newContext({ viewport: { width: 1920, height: 911 } })
  19. await context.addInitScript(({ accessToken, refreshToken }) => {
  20. sessionStorage.setItem('unreal-tran:web:access-token:v1', accessToken)
  21. sessionStorage.setItem('unreal-tran:web:refresh-token:v1', refreshToken)
  22. }, session)
  23. page = await context.newPage()
  24. await page.goto(`http://127.0.0.1:6180/content/training-projects/${project.id}/legacy`)
  25. await page.locator('[data-action="set-tab"]').filter({ hasText: '讲解' }).click({ timeout: 60000 })
  26. await page.locator('[data-action="set-narration-source"][data-source="digital-library"]').click()
  27. await page.locator('[data-action="manage-digital-human"]').click()
  28. const dialog = page.locator('.digital-human-narration-dialog')
  29. console.log('Narration dialog opened')
  30. await expect(dialog.locator('.connection-state')).toContainText('已连接数字人服务', { timeout: 30000 })
  31. const preview = dialog.getByRole('button', { name: '预览', exact: true }).last()
  32. const previewResponse = page.waitForResponse(response => response.url().includes('/digital-human/videos/') && response.url().endsWith('/preview'), { timeout: 125000 })
  33. await preview.click({ timeout: 30000 })
  34. console.log('Authorized video preview requested')
  35. assert.equal((await previewResponse).status(), 200, 'Authorized video preview response')
  36. await page.waitForFunction(() => document.querySelector('.video-preview video')?.readyState >= 1, null, { timeout: 20000 })
  37. await dialog.locator('video').evaluate(video => { video.currentTime = 0.1 })
  38. async function verify(label, width, height, theme = 'light') {
  39. await page.setViewportSize({ width, height })
  40. await page.evaluate(async value => {
  41. const { usePreferencesStore } = await import('/src/stores/preferences.ts')
  42. usePreferencesStore().setThemeMode(value === 'dark' ? 'dark' : 'military')
  43. }, theme)
  44. await expect.poll(async () => dialog.evaluate(el => {
  45. const box = el.getBoundingClientRect(), footer = el.querySelector('.el-dialog__footer').getBoundingClientRect()
  46. const body = el.querySelector('.el-dialog__body')
  47. return box.top >= 0 && box.bottom <= innerHeight + 1 && box.left >= 0 && box.right <= innerWidth + 1 && footer.bottom <= innerHeight && body.scrollWidth <= body.clientWidth + 1
  48. })).toBe(true)
  49. await dialog.locator('.video-preview').scrollIntoViewIfNeeded()
  50. await expect(dialog.locator('.el-dialog__footer').getByRole('button', { name: '关闭', exact: true })).toBeVisible()
  51. const video = await dialog.locator('video').evaluate(el => ({ width: el.videoWidth, height: el.videoHeight, duration: el.duration }))
  52. assert.ok(video.width > 0 && video.duration > 0)
  53. await page.screenshot({ path: path.join(report, `${label}.png`), animations: 'disabled' })
  54. checks.push({ label, viewport: { width, height }, theme, result: 'PASS', video })
  55. }
  56. await verify('01-desktop-preview', 1920, 911)
  57. await verify('02-short-preview', 1366, 600)
  58. await verify('03-mobile-preview', 390, 844)
  59. await verify('04-dark-preview', 1366, 768, 'dark')
  60. await dialog.getByRole('tab', { name: '现场生成成片' }).click()
  61. await expect(dialog.getByRole('button', { name: '提交生成', exact: true })).toBeAttached()
  62. await dialog.getByRole('button', { name: '提交生成', exact: true }).scrollIntoViewIfNeeded()
  63. await page.screenshot({ path: path.join(report, '05-generation-form.png'), animations: 'disabled' })
  64. await dialog.getByRole('tab', { name: '生成任务', exact: true }).click()
  65. await dialog.locator('.el-dialog__footer').getByRole('button', { name: '关闭', exact: true }).click()
  66. await expect(dialog).toBeHidden()
  67. checks.push({ label: '生成表单、任务页签与底部关闭', result: 'PASS' })
  68. const lan = await context.request.get('http://192.168.31.168:6180/')
  69. assert.equal(lan.status(), 200)
  70. checks.push({ label: '局域网入口', result: 'PASS' })
  71. fs.writeFileSync(path.join(report, 'results.json'), JSON.stringify({ passed: true, projectId: project.id, accountType: '内部维护', checks, dataChanges: '使用已有测试工程和授权视频。打开数字人服务前编辑器按既有逻辑保存测试工程;未提交生成、导入绑定或发布。' }, null, 2))
  72. console.log('Narration dialog layout checks passed')
  73. } catch (error) {
  74. if (page) {
  75. await page.screenshot({ path: path.join(report, 'failure.png'), animations: 'disabled' }).catch(() => {})
  76. console.error(await page.locator('.el-message').allTextContents())
  77. }
  78. throw error
  79. } finally { await browser.close() }