317 lines
12 KiB
Markdown
317 lines
12 KiB
Markdown
|
|
# Instructions
|
|||
|
|
|
|||
|
|
- Following Playwright test failed.
|
|||
|
|
- Explain why, be concise, respect Playwright best practices.
|
|||
|
|
- Provide a snippet of code with the fix, if possible.
|
|||
|
|
|
|||
|
|
# Test info
|
|||
|
|
|
|||
|
|
- Name: upload\oss-upload.spec.ts >> OSS 直传上传 >> 登录 -> 赛事创建页 -> 上传封面图片到 OSS
|
|||
|
|
- Location: e2e\upload\oss-upload.spec.ts:33:3
|
|||
|
|
|
|||
|
|
# Error details
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Error: expect(received).not.toContain(expected) // indexOf
|
|||
|
|
|
|||
|
|
Expected substring: not "/login"
|
|||
|
|
Received string: "http://localhost:3000/super/login"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
# Page snapshot
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
- generic [ref=e3]:
|
|||
|
|
- complementary [ref=e4]:
|
|||
|
|
- generic [ref=e6]:
|
|||
|
|
- generic [ref=e7]:
|
|||
|
|
- generic [ref=e8]:
|
|||
|
|
- img "乐绘世界" [ref=e9]
|
|||
|
|
- generic [ref=e10]:
|
|||
|
|
- generic [ref=e11]: 乐绘世界
|
|||
|
|
- generic [ref=e12]: 创想活动乐园
|
|||
|
|
- menu [ref=e13]:
|
|||
|
|
- generic [ref=e14] [cursor=pointer]:
|
|||
|
|
- img "fund-view" [ref=e15]:
|
|||
|
|
- img [ref=e16]
|
|||
|
|
- generic [ref=e20]: 活动监管
|
|||
|
|
- list [ref=e21]:
|
|||
|
|
- menuitem "unordered-list 全部活动" [ref=e22] [cursor=pointer]:
|
|||
|
|
- img "unordered-list" [ref=e23]:
|
|||
|
|
- img [ref=e24]
|
|||
|
|
- generic [ref=e26]: 全部活动
|
|||
|
|
- menuitem "user-add 报名数据" [ref=e27] [cursor=pointer]:
|
|||
|
|
- img "user-add" [ref=e28]:
|
|||
|
|
- img [ref=e29]
|
|||
|
|
- generic [ref=e31]: 报名数据
|
|||
|
|
- menuitem "file-text 作品数据" [ref=e32] [cursor=pointer]:
|
|||
|
|
- img "file-text" [ref=e33]:
|
|||
|
|
- img [ref=e34]
|
|||
|
|
- generic [ref=e36]: 作品数据
|
|||
|
|
- menuitem "dashboard 评审进度" [ref=e37] [cursor=pointer]:
|
|||
|
|
- img "dashboard" [ref=e38]:
|
|||
|
|
- img [ref=e39]
|
|||
|
|
- generic [ref=e41]: 评审进度
|
|||
|
|
- menuitem "trophy 活动成果" [ref=e42] [cursor=pointer]:
|
|||
|
|
- img "trophy" [ref=e43]:
|
|||
|
|
- img [ref=e44]
|
|||
|
|
- generic [ref=e46]: 活动成果
|
|||
|
|
- generic [ref=e47] [cursor=pointer]:
|
|||
|
|
- img "picture" [ref=e48]:
|
|||
|
|
- img [ref=e49]
|
|||
|
|
- generic [ref=e51]: 内容管理
|
|||
|
|
- menuitem "bank 机构管理" [ref=e52] [cursor=pointer]:
|
|||
|
|
- img "bank" [ref=e53]:
|
|||
|
|
- img [ref=e54]
|
|||
|
|
- generic [ref=e56]: 机构管理
|
|||
|
|
- generic [ref=e57] [cursor=pointer]:
|
|||
|
|
- img "team" [ref=e58]:
|
|||
|
|
- img [ref=e59]
|
|||
|
|
- generic [ref=e61]: 用户中心
|
|||
|
|
- generic [ref=e62] [cursor=pointer]:
|
|||
|
|
- img "setting" [ref=e63]:
|
|||
|
|
- img [ref=e64]
|
|||
|
|
- generic [ref=e66]: 系统设置
|
|||
|
|
- generic [ref=e67]:
|
|||
|
|
- generic [ref=e68] [cursor=pointer]:
|
|||
|
|
- img [ref=e70]
|
|||
|
|
- generic [ref=e71]: 超级管理员
|
|||
|
|
- img "menu-fold" [ref=e73] [cursor=pointer]:
|
|||
|
|
- img [ref=e74]
|
|||
|
|
- main [ref=e77]:
|
|||
|
|
- generic [ref=e78]:
|
|||
|
|
- generic [ref=e82]: 活动列表
|
|||
|
|
- generic [ref=e83]:
|
|||
|
|
- generic [ref=e84] [cursor=pointer]:
|
|||
|
|
- img "appstore" [ref=e86]:
|
|||
|
|
- img [ref=e87]
|
|||
|
|
- generic [ref=e89]:
|
|||
|
|
- generic [ref=e90]: "0"
|
|||
|
|
- generic [ref=e91]: 全部
|
|||
|
|
- generic [ref=e92] [cursor=pointer]:
|
|||
|
|
- img "form" [ref=e94]:
|
|||
|
|
- img [ref=e95]
|
|||
|
|
- generic [ref=e98]:
|
|||
|
|
- generic [ref=e99]: "0"
|
|||
|
|
- generic [ref=e100]: 报名中
|
|||
|
|
- generic [ref=e101] [cursor=pointer]:
|
|||
|
|
- img "edit" [ref=e103]:
|
|||
|
|
- img [ref=e104]
|
|||
|
|
- generic [ref=e106]:
|
|||
|
|
- generic [ref=e107]: "0"
|
|||
|
|
- generic [ref=e108]: 征稿中
|
|||
|
|
- generic [ref=e109] [cursor=pointer]:
|
|||
|
|
- img "eye" [ref=e111]:
|
|||
|
|
- img [ref=e112]
|
|||
|
|
- generic [ref=e114]:
|
|||
|
|
- generic [ref=e115]: "0"
|
|||
|
|
- generic [ref=e116]: 评审中
|
|||
|
|
- generic [ref=e117] [cursor=pointer]:
|
|||
|
|
- img "check-circle" [ref=e119]:
|
|||
|
|
- img [ref=e120]
|
|||
|
|
- generic [ref=e123]:
|
|||
|
|
- generic [ref=e124]: "0"
|
|||
|
|
- generic [ref=e125]: 已结束
|
|||
|
|
- generic [ref=e126] [cursor=pointer]:
|
|||
|
|
- img "close-circle" [ref=e128]:
|
|||
|
|
- img [ref=e129]
|
|||
|
|
- generic [ref=e131]:
|
|||
|
|
- generic [ref=e132]: "0"
|
|||
|
|
- generic [ref=e133]: 未发布
|
|||
|
|
- generic [ref=e134]:
|
|||
|
|
- generic [ref=e136]:
|
|||
|
|
- generic "活动名称" [ref=e138]: "活动名称 :"
|
|||
|
|
- textbox "请输入活动名称" [ref=e143]
|
|||
|
|
- generic [ref=e146]:
|
|||
|
|
- generic "活动阶段" [ref=e148]: "活动阶段 :"
|
|||
|
|
- generic [ref=e152] [cursor=pointer]:
|
|||
|
|
- generic [ref=e153]:
|
|||
|
|
- combobox [ref=e155]
|
|||
|
|
- generic: 全部阶段
|
|||
|
|
- generic:
|
|||
|
|
- img:
|
|||
|
|
- img
|
|||
|
|
- generic [ref=e157]:
|
|||
|
|
- generic "活动类型" [ref=e159]: "活动类型 :"
|
|||
|
|
- generic [ref=e163] [cursor=pointer]:
|
|||
|
|
- generic [ref=e164]:
|
|||
|
|
- combobox [ref=e166]
|
|||
|
|
- generic: 全部
|
|||
|
|
- generic:
|
|||
|
|
- img:
|
|||
|
|
- img
|
|||
|
|
- generic [ref=e168]:
|
|||
|
|
- generic "主办机构" [ref=e170]: "主办机构 :"
|
|||
|
|
- generic [ref=e174] [cursor=pointer]:
|
|||
|
|
- generic [ref=e175]:
|
|||
|
|
- combobox [ref=e177]
|
|||
|
|
- generic: 全部机构
|
|||
|
|
- generic:
|
|||
|
|
- img:
|
|||
|
|
- img
|
|||
|
|
- generic [ref=e182]:
|
|||
|
|
- button "search 搜索" [ref=e183] [cursor=pointer]:
|
|||
|
|
- img "search" [ref=e184]:
|
|||
|
|
- img [ref=e185]
|
|||
|
|
- generic [ref=e187]: 搜索
|
|||
|
|
- button "reload 重置" [ref=e188] [cursor=pointer]:
|
|||
|
|
- img "reload" [ref=e189]:
|
|||
|
|
- img [ref=e190]
|
|||
|
|
- generic [ref=e192]: 重置
|
|||
|
|
- table [ref=e199]:
|
|||
|
|
- rowgroup [ref=e212]:
|
|||
|
|
- row "序号 活动名称 主办机构 类型 阶段 可见范围 报名 作品 评审 活动时间 操作" [ref=e213]:
|
|||
|
|
- columnheader "序号" [ref=e214]
|
|||
|
|
- columnheader "活动名称" [ref=e215]
|
|||
|
|
- columnheader "主办机构" [ref=e216]
|
|||
|
|
- columnheader "类型" [ref=e217]
|
|||
|
|
- columnheader "阶段" [ref=e218]
|
|||
|
|
- columnheader "可见范围" [ref=e219]
|
|||
|
|
- columnheader "报名" [ref=e220]
|
|||
|
|
- columnheader "作品" [ref=e221]
|
|||
|
|
- columnheader "评审" [ref=e222]
|
|||
|
|
- columnheader "活动时间" [ref=e223]
|
|||
|
|
- columnheader "操作" [ref=e224]
|
|||
|
|
- rowgroup [ref=e225]:
|
|||
|
|
- row "暂无数据" [ref=e226]:
|
|||
|
|
- cell "暂无数据" [ref=e227]:
|
|||
|
|
- generic [ref=e228]:
|
|||
|
|
- img [ref=e230]
|
|||
|
|
- paragraph [ref=e236]: 暂无数据
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
# Test source
|
|||
|
|
|
|||
|
|
```ts
|
|||
|
|
1 | import { test, expect } from '@playwright/test'
|
|||
|
|
2 | import path from 'path'
|
|||
|
|
3 | import { fileURLToPath } from 'url'
|
|||
|
|
4 | import fs from 'fs'
|
|||
|
|
5 |
|
|||
|
|
6 | const __filename = fileURLToPath(import.meta.url)
|
|||
|
|
7 | const __dirname = path.dirname(__filename)
|
|||
|
|
8 |
|
|||
|
|
9 | // 测试配置
|
|||
|
|
10 | const TENANT_CODE = 'super'
|
|||
|
|
11 | const USERNAME = 'admin'
|
|||
|
|
12 | const PASSWORD = 'admin123'
|
|||
|
|
13 |
|
|||
|
|
14 | // 确保测试图片存在
|
|||
|
|
15 | const FIXTURES_DIR = path.join(__dirname, 'fixtures')
|
|||
|
|
16 | const TEST_IMAGE_PATH = path.join(FIXTURES_DIR, 'test-upload.png')
|
|||
|
|
17 | if (!fs.existsSync(FIXTURES_DIR)) {
|
|||
|
|
18 | fs.mkdirSync(FIXTURES_DIR, { recursive: true })
|
|||
|
|
19 | }
|
|||
|
|
20 | if (!fs.existsSync(TEST_IMAGE_PATH)) {
|
|||
|
|
21 | const pngData = Buffer.from(
|
|||
|
|
22 | 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
|
|||
|
|
23 | 'base64'
|
|||
|
|
24 | )
|
|||
|
|
25 | fs.writeFileSync(TEST_IMAGE_PATH, pngData)
|
|||
|
|
26 | }
|
|||
|
|
27 |
|
|||
|
|
28 | test.describe('OSS 直传上传', () => {
|
|||
|
|
29 |
|
|||
|
|
30 | // 单独给这个测试更长的超时
|
|||
|
|
31 | test.setTimeout(60000)
|
|||
|
|
32 |
|
|||
|
|
33 | test('登录 -> 赛事创建页 -> 上传封面图片到 OSS', async ({ page }) => {
|
|||
|
|
34 | // 监听网络请求,捕获 OSS 相关请求
|
|||
|
|
35 | const ossTokenRequests: string[] = []
|
|||
|
|
36 | const ossUploadRequests: string[] = []
|
|||
|
|
37 |
|
|||
|
|
38 | page.on('request', (req) => {
|
|||
|
|
39 | const url = req.url()
|
|||
|
|
40 | if (url.includes('/upload/oss/token')) {
|
|||
|
|
41 | ossTokenRequests.push(url)
|
|||
|
|
42 | }
|
|||
|
|
43 | if (url.includes('aliyuncs.com')) {
|
|||
|
|
44 | ossUploadRequests.push(url)
|
|||
|
|
45 | }
|
|||
|
|
46 | })
|
|||
|
|
47 |
|
|||
|
|
48 | // ========== 1. 登录 ==========
|
|||
|
|
49 | await page.goto(`/${TENANT_CODE}/login`)
|
|||
|
|
50 | await page.waitForLoadState('domcontentloaded')
|
|||
|
|
51 |
|
|||
|
|
52 | // 填写 Ant Design 表单
|
|||
|
|
53 | await page.locator('input[placeholder="请输入用户名"]').fill(USERNAME)
|
|||
|
|
54 | await page.locator('input[placeholder="请输入密码"]').fill(PASSWORD)
|
|||
|
|
55 |
|
|||
|
|
56 | // 点击登录按钮(Ant Design a-button html-type="submit")
|
|||
|
|
57 | await page.locator('button.login-btn, button:has-text("登录"):visible').first().click()
|
|||
|
|
58 |
|
|||
|
|
59 | // 等待登录成功跳转
|
|||
|
|
60 | await page.waitForURL(`**/${TENANT_CODE}/**`, { timeout: 15000 })
|
|||
|
|
61 | await page.waitForLoadState('domcontentloaded')
|
|||
|
|
62 |
|
|||
|
|
63 | // 确认不在登录页了
|
|||
|
|
64 | const currentUrl = page.url()
|
|||
|
|
> 65 | expect(currentUrl).not.toContain('/login')
|
|||
|
|
| ^ Error: expect(received).not.toContain(expected) // indexOf
|
|||
|
|
66 | console.log('[1] 登录成功, 当前页面:', currentUrl)
|
|||
|
|
67 |
|
|||
|
|
68 | // ========== 2. 进入赛事创建页 ==========
|
|||
|
|
69 | await page.goto(`/${TENANT_CODE}/contests/create`)
|
|||
|
|
70 | await page.waitForLoadState('domcontentloaded')
|
|||
|
|
71 |
|
|||
|
|
72 | // 等待表单页面加载
|
|||
|
|
73 | await page.locator('input[placeholder*="活动名称"], input[placeholder*="名称"]').first().waitFor({ timeout: 10000 })
|
|||
|
|
74 | console.log('[2] 赛事创建页加载成功')
|
|||
|
|
75 |
|
|||
|
|
76 | // ========== 3. 上传封面图片 ==========
|
|||
|
|
77 | // 直接用全局的 file input(Ant Design Upload 的隐藏 input)
|
|||
|
|
78 | const fileInputs = page.locator('input[type="file"]')
|
|||
|
|
79 | const fileCount = await fileInputs.count()
|
|||
|
|
80 | console.log('[3] 发现 file input 数量:', fileCount)
|
|||
|
|
81 |
|
|||
|
|
82 | // 第一个 file input 对应封面上传
|
|||
|
|
83 | await fileInputs.first().setInputFiles(TEST_IMAGE_PATH)
|
|||
|
|
84 |
|
|||
|
|
85 | console.log('[3] 已选择封面文件,等待 OSS 上传...')
|
|||
|
|
86 |
|
|||
|
|
87 | // 等待网络请求完成
|
|||
|
|
88 | await page.waitForTimeout(5000)
|
|||
|
|
89 |
|
|||
|
|
90 | // ========== 4. 验证 ==========
|
|||
|
|
91 | console.log('[4] OSS Token 请求数:', ossTokenRequests.length)
|
|||
|
|
92 | console.log('[4] OSS 上传请求数:', ossUploadRequests.length)
|
|||
|
|
93 |
|
|||
|
|
94 | // 验证:发出了 OSS Token 请求
|
|||
|
|
95 | if (ossTokenRequests.length > 0) {
|
|||
|
|
96 | console.log('[4] Token 请求 URL:', ossTokenRequests[0])
|
|||
|
|
97 | }
|
|||
|
|
98 |
|
|||
|
|
99 | // 验证:发出了 OSS 上传请求
|
|||
|
|
100 | if (ossUploadRequests.length > 0) {
|
|||
|
|
101 | console.log('[4] 上传目标 URL:', ossUploadRequests[0])
|
|||
|
|
102 | expect(ossUploadRequests[0]).toContain('aliyuncs.com')
|
|||
|
|
103 | }
|
|||
|
|
104 |
|
|||
|
|
105 | // 验证:检查页面上是否有上传成功的 UI 指示
|
|||
|
|
106 | const successItems = page.locator('.ant-upload-list-item-done')
|
|||
|
|
107 | const errorItems = page.locator('.ant-upload-list-item-error')
|
|||
|
|
108 | const successCount = await successItems.count()
|
|||
|
|
109 | const errorCount = await errorItems.count()
|
|||
|
|
110 |
|
|||
|
|
111 | console.log('[4] 上传成功项:', successCount, '上传失败项:', errorCount)
|
|||
|
|
112 |
|
|||
|
|
113 | // 检查是否有错误提示消息
|
|||
|
|
114 | const errorMsg = await page.locator('.ant-message-error').textContent().catch(() => '')
|
|||
|
|
115 | if (errorMsg) {
|
|||
|
|
116 | console.log('[4] 错误消息:', errorMsg)
|
|||
|
|
117 | }
|
|||
|
|
118 |
|
|||
|
|
119 | // 核心断言
|
|||
|
|
120 | expect(ossTokenRequests.length).toBeGreaterThanOrEqual(1)
|
|||
|
|
121 | expect(ossUploadRequests.length).toBeGreaterThanOrEqual(1)
|
|||
|
|
122 | expect(errorCount).toBe(0)
|
|||
|
|
123 |
|
|||
|
|
124 | console.log('\n===== OSS 直传上传测试通过 =====')
|
|||
|
|
125 | console.log('OSS Token 请求:', ossTokenRequests.length)
|
|||
|
|
126 | console.log('OSS 上传请求:', ossUploadRequests.length)
|
|||
|
|
127 | console.log('上传目标:', ossUploadRequests[0] || 'N/A')
|
|||
|
|
128 | })
|
|||
|
|
129 | })
|
|||
|
|
130 |
|
|||
|
|
```
|