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