library-picturebook-activity/lesingle-creation-frontend/e2e/leai/creation-iframe.spec.ts
En 98e9ad1d28 feat(前端): 测试环境登录框支持自动填充测试账号
通过 VITE_AUTO_FILL_TEST 环境变量控制,在 .env.test 中启用,
使测试环境构建后登录框也能自动填充测试账号,方便测试人员使用。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 17:03:22 +08:00

245 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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',
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.120: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',
phone: '13800001111',
},
}),
})
})
await loggedInPage.route('http://192.168.1.120: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',
phone: '13800001111',
},
}),
})
}
})
await loggedInPage.route('http://192.168.1.120: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',
phone: '13800001111',
},
}),
})
})
await loggedInPage.route('http://192.168.1.120: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)
})
})
})