2026-04-07 21:52:32 +08:00
|
|
|
|
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
|
2026-04-08 13:37:14 +08:00
|
|
|
|
await loggedInPage.route('http://192.168.1.120:3001/**', async (route) => {
|
2026-04-07 21:52:32 +08:00
|
|
|
|
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',
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2026-04-08 13:37:14 +08:00
|
|
|
|
await loggedInPage.route('http://192.168.1.120:3001/**', async (route) => {
|
2026-04-07 21:52:32 +08:00
|
|
|
|
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',
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-04-08 13:37:14 +08:00
|
|
|
|
await loggedInPage.route('http://192.168.1.120:3001/**', async (route) => {
|
2026-04-07 21:52:32 +08:00
|
|
|
|
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',
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2026-04-08 13:37:14 +08:00
|
|
|
|
await loggedInPage.route('http://192.168.1.120:3001/**', async (route) => {
|
2026-04-07 21:52:32 +08:00
|
|
|
|
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)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|