import { test, expect } from '../fixtures/auth.fixture' import { fileURLToPath } from 'url' import path from 'path' /** * P1: 创作页 iframe 嵌入测试 * * 测试 frontend/src/views/public/create/Index.vue * - iframe 正确加载 * - 加载状态 * - 错误处理 * - iframe 属性(allow、尺寸) */ const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) /** Mock H5 页面的文件路径 */ const MOCK_H5_PATH = path.resolve(__dirname, '../utils/mock-h5.html') test.describe('创作页 iframe 嵌入', () => { test.describe('未登录状态', () => { test('访问 /p/create — 重定向到登录页', async ({ page }) => { await page.goto('/p/create') // 应该重定向到登录页或显示登录提示 await page.waitForTimeout(2000) const url = page.url() expect(url).toMatch(/\/(login|auth)/) }) }) test.describe('已登录状态', () => { test('显示加载中提示', async ({ loggedInPage }) => { // 拦截 token API,让它延迟 await loggedInPage.route('**/leai-auth/token', async (route) => { await new Promise((r) => setTimeout(r, 3000)) await route.continue() }) await loggedInPage.goto('/p/create') // 应该显示加载状态 const loadingText = loggedInPage.locator('text=正在加载创作工坊') await expect(loadingText).toBeVisible({ timeout: 5000 }) }) test('iframe 正确渲染', async ({ loggedInPage }) => { // 拦截 token API 返回 mock 数据 await loggedInPage.route('**/leai-auth/token', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 200, data: { token: 'mock_session_token_xxx', orgId: 'gdlib', h5Url: 'http://localhost:3001', phone: '13800001111', }, }), }) }) // 拦截 iframe 加载,返回 mock H5 页面 await loggedInPage.route('**/*leai*/**', async (route) => { await route.fulfill({ status: 200, contentType: 'text/html', path: MOCK_H5_PATH, }) }) // 也拦截通配形式的 H5 URL await loggedInPage.route('http://192.168.1.72:3001/**', async (route) => { await route.fulfill({ status: 200, contentType: 'text/html', path: MOCK_H5_PATH, }) }) await loggedInPage.goto('/p/create') // 等待 iframe 出现 const iframe = loggedInPage.locator('iframe.creation-iframe, iframe[allow*="camera"]') await expect(iframe).toBeVisible({ timeout: 10_000 }) // 验证 iframe src 包含必要参数 const src = await iframe.getAttribute('src') expect(src).toBeTruthy() expect(src).toContain('token=') expect(src).toContain('orgId=') expect(src).toContain('phone=') expect(src).toContain('embed=1') }) test('iframe 有 camera 和 microphone 权限', async ({ loggedInPage }) => { await loggedInPage.route('**/leai-auth/token', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 200, data: { token: 'mock_token', orgId: 'gdlib', h5Url: 'http://localhost:3001', phone: '13800001111', }, }), }) }) await loggedInPage.route('http://192.168.1.72:3001/**', async (route) => { await route.fulfill({ status: 200, contentType: 'text/html', path: MOCK_H5_PATH, }) }) await loggedInPage.goto('/p/create') const iframe = loggedInPage.locator('iframe').first() await expect(iframe).toBeVisible({ timeout: 10_000 }) const allow = await iframe.getAttribute('allow') expect(allow).toContain('camera') expect(allow).toContain('microphone') }) test('Token 获取失败 — 显示错误和重试按钮', async ({ loggedInPage }) => { // 拦截 token API 返回 HTTP 500 错误 await loggedInPage.route('**/leai-auth/token', async (route) => { await route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ code: 500, message: '获取创作Token失败: 连接超时', }), }) }) await loggedInPage.goto('/p/create') // 应该显示错误信息(.load-error 在 v-if="loading" 容器内) const errorText = loggedInPage.locator('.load-error') await expect(errorText).toBeVisible({ timeout: 10_000 }) // 应该有重新加载按钮 const retryBtn = loggedInPage.locator('button:has-text("重新加载")') await expect(retryBtn).toBeVisible() }) test('重新加载按钮 — 可重新获取 Token', async ({ loggedInPage }) => { let callCount = 0 await loggedInPage.route('**/leai-auth/token', async (route) => { callCount++ if (callCount === 1) { // 第一次失败(HTTP 500) await route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ code: 500, message: '网络错误' }), }) } else { // 第二次成功 await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 200, data: { token: 'retry_token_success', orgId: 'gdlib', h5Url: 'http://localhost:3001', phone: '13800001111', }, }), }) } }) await loggedInPage.route('http://192.168.1.72:3001/**', async (route) => { await route.fulfill({ status: 200, contentType: 'text/html', path: MOCK_H5_PATH, }) }) await loggedInPage.goto('/p/create') // 等待错误出现 const retryBtn = loggedInPage.locator('button:has-text("重新加载")') await expect(retryBtn).toBeVisible({ timeout: 10_000 }) // 点击重试 await retryBtn.click() // 第二次应成功,iframe 应出现 const iframe = loggedInPage.locator('iframe') await expect(iframe).toBeVisible({ timeout: 10_000 }) expect(callCount).toBe(2) }) test('iframe 占满内容区域', async ({ loggedInPage }) => { await loggedInPage.route('**/leai-auth/token', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ code: 200, data: { token: 'mock_token', orgId: 'gdlib', h5Url: 'http://localhost:3001', phone: '13800001111', }, }), }) }) await loggedInPage.route('http://192.168.1.72:3001/**', async (route) => { await route.fulfill({ status: 200, contentType: 'text/html', path: MOCK_H5_PATH, }) }) await loggedInPage.goto('/p/create') const iframe = loggedInPage.locator('iframe').first() await expect(iframe).toBeVisible({ timeout: 10_000 }) // iframe 应该没有边框 const frameBorder = await iframe.getAttribute('frameborder') expect(frameBorder).toBe('0') // iframe 高度应接近视口高度(至少 400px) const box = await iframe.boundingBox() expect(box).toBeTruthy() expect(box!.height).toBeGreaterThan(400) }) }) })