library-picturebook-activity/frontend/e2e/upload/oss-upload.spec.ts
En b9ed5e17c6 feat: OSS 客户端直传改造(STS Token 签发 + 前端直传 + CORS 自动配置)
后端新增 OssUtils/OssTokenVo/OssCorsInitRunner,通过 STS 临时凭证实现客户端直传 OSS;
前端 upload API 适配直传流程,赛事创建/作品提交/作业/富文本编辑器均已切换;
多环境(dev/test/prod) OSS 配置补全;新增 oss-direct-upload-demo 示例项目及 E2E 测试。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 15:19:43 +08:00

131 lines
4.7 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 '@playwright/test'
import path from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 测试配置
const TENANT_CODE = 'super'
const USERNAME = 'admin'
const PASSWORD = 'admin123'
// 确保测试图片存在
const FIXTURES_DIR = path.join(__dirname, 'fixtures')
const TEST_IMAGE_PATH = path.join(FIXTURES_DIR, 'test-upload.png')
if (!fs.existsSync(FIXTURES_DIR)) {
fs.mkdirSync(FIXTURES_DIR, { recursive: true })
}
if (!fs.existsSync(TEST_IMAGE_PATH)) {
const pngData = Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
'base64'
)
fs.writeFileSync(TEST_IMAGE_PATH, pngData)
}
test.describe('OSS 直传上传', () => {
// 单独给这个测试更长的超时
test.setTimeout(60000)
test('登录 -> 赛事创建页 -> 上传封面图片到 OSS', async ({ page }) => {
// 监听网络请求,捕获 OSS 相关请求
const ossTokenRequests: string[] = []
const ossUploadRequests: string[] = []
page.on('request', (req) => {
const url = req.url()
if (url.includes('/upload/oss/token')) {
ossTokenRequests.push(url)
}
if (url.includes('aliyuncs.com')) {
ossUploadRequests.push(url)
}
})
// ========== 1. 登录 ==========
await page.goto(`/${TENANT_CODE}/login`)
await page.waitForLoadState('domcontentloaded')
// 等待登录表单可见
await page.locator('input[placeholder="请输入用户名"]').waitFor({ state: 'visible', timeout: 10000 })
// 填写 Ant Design 表单
await page.locator('input[placeholder="请输入用户名"]').click()
await page.locator('input[placeholder="请输入用户名"]').fill(USERNAME)
await page.locator('input[placeholder="请输入密码"]').click()
await page.locator('input[placeholder="请输入密码"]').fill(PASSWORD)
// 点击登录按钮
const loginBtn = page.locator('button[type="submit"]').first()
await loginBtn.click()
// 等待登录成功URL 不再包含 /login
await page.waitForFunction(() => !window.location.pathname.includes('/login'), { timeout: 15000 })
console.log('[1] 登录成功, 当前页面:', page.url())
// ========== 2. 进入赛事创建页 ==========
await page.goto(`/${TENANT_CODE}/contests/create`)
await page.waitForLoadState('domcontentloaded')
// 等待表单页面加载
await page.locator('input[placeholder*="活动名称"], input[placeholder*="名称"]').first().waitFor({ timeout: 10000 })
console.log('[2] 赛事创建页加载成功')
// ========== 3. 上传封面图片 ==========
// 直接用全局的 file inputAnt Design Upload 的隐藏 input
const fileInputs = page.locator('input[type="file"]')
const fileCount = await fileInputs.count()
console.log('[3] 发现 file input 数量:', fileCount)
// 第一个 file input 对应封面上传
await fileInputs.first().setInputFiles(TEST_IMAGE_PATH)
console.log('[3] 已选择封面文件,等待 OSS 上传...')
// 等待网络请求完成
await page.waitForTimeout(5000)
// ========== 4. 验证 ==========
console.log('[4] OSS Token 请求数:', ossTokenRequests.length)
console.log('[4] OSS 上传请求数:', ossUploadRequests.length)
// 验证:发出了 OSS Token 请求
if (ossTokenRequests.length > 0) {
console.log('[4] Token 请求 URL:', ossTokenRequests[0])
}
// 验证:发出了 OSS 上传请求
if (ossUploadRequests.length > 0) {
console.log('[4] 上传目标 URL:', ossUploadRequests[0])
expect(ossUploadRequests[0]).toContain('aliyuncs.com')
}
// 验证:检查页面上是否有上传成功的 UI 指示
const successItems = page.locator('.ant-upload-list-item-done')
const errorItems = page.locator('.ant-upload-list-item-error')
const successCount = await successItems.count()
const errorCount = await errorItems.count()
console.log('[4] 上传成功项:', successCount, '上传失败项:', errorCount)
// 检查是否有错误提示消息
const errorMsg = await page.locator('.ant-message-error').textContent().catch(() => '')
if (errorMsg) {
console.log('[4] 错误消息:', errorMsg)
}
// 核心断言
expect(ossTokenRequests.length).toBeGreaterThanOrEqual(1)
expect(ossUploadRequests.length).toBeGreaterThanOrEqual(1)
expect(errorCount).toBe(0)
console.log('\n===== OSS 直传上传测试通过 =====')
console.log('OSS Token 请求:', ossTokenRequests.length)
console.log('OSS 上传请求:', ossUploadRequests.length)
console.log('上传目标:', ossUploadRequests[0] || 'N/A')
})
})