256 lines
10 KiB
TypeScript
256 lines
10 KiB
TypeScript
|
|
import { test, expect, request as requestFactory, type APIRequestContext } from '@playwright/test'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* UGC 作品状态字段拆分验证测试
|
|||
|
|
*
|
|||
|
|
* 所有 API 请求直接发到后端 localhost:8580,绕过前端代理。
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// ── 配置 ──
|
|||
|
|
const API_BASE = process.env.API_BASE_URL || 'http://localhost:8580/api'
|
|||
|
|
const AUTH = {
|
|||
|
|
username: process.env.TEST_USERNAME || 'demo',
|
|||
|
|
password: process.env.TEST_PASSWORD || 'demo123456',
|
|||
|
|
tenantCode: process.env.TEST_TENANT_CODE || 'gdlib',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const VALID_STATUSES = ['draft', 'unpublished', 'pending_review', 'published', 'rejected']
|
|||
|
|
|
|||
|
|
// ── Helper ──
|
|||
|
|
function url(path: string) {
|
|||
|
|
return `${API_BASE}${path}`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── Fixture ──
|
|||
|
|
type Fixtures = { api: APIRequestContext }
|
|||
|
|
|
|||
|
|
const apiTest = test.extend<Fixtures>({
|
|||
|
|
api: async ({}, use) => {
|
|||
|
|
// 用 requestFactory(顶层 import)创建独立上下文来登录
|
|||
|
|
const loginCtx = await requestFactory.newContext({})
|
|||
|
|
const loginResp = await loginCtx.post(url('/public/auth/login'), {
|
|||
|
|
data: { username: AUTH.username, password: AUTH.password, tenantCode: AUTH.tenantCode },
|
|||
|
|
})
|
|||
|
|
const loginJson = await loginResp.json()
|
|||
|
|
await loginCtx.dispose()
|
|||
|
|
|
|||
|
|
if (loginJson.code !== 200 || !loginJson.data?.token) {
|
|||
|
|
throw new Error(`登录失败: ${JSON.stringify(loginJson)}`)
|
|||
|
|
}
|
|||
|
|
const token = loginJson.data.token
|
|||
|
|
|
|||
|
|
// 创建带 auth header 的 API 上下文
|
|||
|
|
const api = await requestFactory.newContext({
|
|||
|
|
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
|
|||
|
|
})
|
|||
|
|
await use(api)
|
|||
|
|
await api.dispose()
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 1. 我的作品列表 — status 是字符串枚举
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-01 我的作品列表 — status 字段为字符串', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/works?page=1&pageSize=5'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
expect(json.data.list).toBeInstanceOf(Array)
|
|||
|
|
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(
|
|||
|
|
VALID_STATUSES.includes(work.status),
|
|||
|
|
`作品 id=${work.id} 的 status="${work.status}" 不是合法字符串枚举值`,
|
|||
|
|
).toBe(true)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`✓ S-01: 返回 ${json.data.list.length} 条作品,status 全部为字符串`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
apiTest('S-02 我的作品列表 — 按 status=draft 筛选', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/works?page=1&pageSize=50&status=draft'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(work.status).toBe('draft')
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-02: draft 筛选返回 ${json.data.list.length} 条`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
apiTest('S-03 我的作品列表 — 按 status=unpublished 筛选', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/works?page=1&pageSize=50&status=unpublished'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(work.status).toBe('unpublished')
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-03: unpublished 筛选返回 ${json.data.list.length} 条`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
apiTest('S-04 我的作品列表 — 按 status=published 筛选', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/works?page=1&pageSize=50&status=published'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(work.status).toBe('published')
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-04: published 筛选返回 ${json.data.list.length} 条`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
apiTest('S-05 我的作品列表 — 按 status=pending_review 筛选', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/works?page=1&pageSize=50&status=pending_review'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(work.status).toBe('pending_review')
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-05: pending_review 筛选返回 ${json.data.list.length} 条`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
apiTest('S-06 我的作品列表 — 按 status=rejected 筛选', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/works?page=1&pageSize=50&status=rejected'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(work.status).toBe('rejected')
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-06: rejected 筛选返回 ${json.data.list.length} 条`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 2. 创建作品 — 初始 status 为 draft
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-07 创建作品 — 初始 status 为 draft', async ({ api }) => {
|
|||
|
|
const resp = await api.post(url('/public/works'), {
|
|||
|
|
data: {
|
|||
|
|
title: `[E2E] 状态验证_${Date.now()}`,
|
|||
|
|
description: 'Playwright 自动创建',
|
|||
|
|
visibility: 'private',
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
expect(json.data.status).toBe('draft')
|
|||
|
|
expect(typeof json.data.status).toBe('string')
|
|||
|
|
console.log(`✓ S-07: 创建作品 id=${json.data.id}, status="${json.data.status}"`)
|
|||
|
|
|
|||
|
|
// 清理
|
|||
|
|
if (json.data.id) {
|
|||
|
|
await api.delete(url(`/public/works/${json.data.id}`))
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 3. draft 不可发布
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-08 发布作品 — draft 状态不可发布', async ({ api }) => {
|
|||
|
|
const createResp = await api.post(url('/public/works'), {
|
|||
|
|
data: { title: `[E2E] 发布测试_${Date.now()}`, visibility: 'private' },
|
|||
|
|
})
|
|||
|
|
const createJson = await createResp.json()
|
|||
|
|
expect(createJson.code).toBe(200)
|
|||
|
|
const workId = createJson.data.id
|
|||
|
|
|
|||
|
|
const publishResp = await api.post(url(`/public/works/${workId}/publish`))
|
|||
|
|
const publishJson = await publishResp.json()
|
|||
|
|
expect(publishJson.code).toBe(400)
|
|||
|
|
console.log(`✓ S-08: draft 发布被拒绝,code=${publishJson.code}, msg="${publishJson.message}"`)
|
|||
|
|
|
|||
|
|
await api.delete(url(`/public/works/${workId}`))
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 4. 创作历史
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-09 创作历史 — 作品 status 为字符串', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/creation/history?page=1&pageSize=5'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
if (json.data?.list?.length > 0) {
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(
|
|||
|
|
VALID_STATUSES.includes(work.status),
|
|||
|
|
`创作历史 id=${work.id} status="${work.status}" 不合法`,
|
|||
|
|
).toBe(true)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-09: 创作历史 ${json.data?.list?.length ?? 0} 条`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 5. 作品详情
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-10 作品详情 — status + leaiStatus 字段', async ({ api }) => {
|
|||
|
|
const listResp = await api.get(url('/public/works?page=1&pageSize=1'))
|
|||
|
|
const listJson = await listResp.json()
|
|||
|
|
|
|||
|
|
if (listJson.data?.list?.length > 0) {
|
|||
|
|
const workId = listJson.data.list[0].id
|
|||
|
|
const detailResp = await api.get(url(`/public/works/${workId}`))
|
|||
|
|
const detailJson = await detailResp.json()
|
|||
|
|
|
|||
|
|
expect(detailJson.code).toBe(200)
|
|||
|
|
const work = detailJson.data.work
|
|||
|
|
|
|||
|
|
// status 必须是合法字符串
|
|||
|
|
expect(VALID_STATUSES.includes(work.status)).toBe(true)
|
|||
|
|
|
|||
|
|
// leaiStatus 应为数字
|
|||
|
|
if (work.leaiStatus != null) {
|
|||
|
|
expect(typeof work.leaiStatus).toBe('number')
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-10: 作品详情 status="${work.status}", leaiStatus=${work.leaiStatus}`)
|
|||
|
|
} else {
|
|||
|
|
console.log('⚠ S-10: 没有作品可测试详情')
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 6. 作品广场
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-11 作品广场 — 只返回已发布作品', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/gallery?page=1&pageSize=10'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
if (json.data?.list?.length > 0) {
|
|||
|
|
for (const work of json.data.list) {
|
|||
|
|
expect(work.status).toBe('published')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-11: 广场 ${json.data?.list?.length ?? 0} 条已发布作品`)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
// 7. 收藏列表
|
|||
|
|
// ══════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
apiTest('S-12 我的收藏 — status 为字符串', async ({ api }) => {
|
|||
|
|
const resp = await api.get(url('/public/mine/favorites?page=1&pageSize=5'))
|
|||
|
|
const json = await resp.json()
|
|||
|
|
|
|||
|
|
expect(json.code).toBe(200)
|
|||
|
|
if (json.data?.list?.length > 0) {
|
|||
|
|
for (const item of json.data.list) {
|
|||
|
|
if (item.status) {
|
|||
|
|
expect(VALID_STATUSES.includes(item.status)).toBe(true)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
console.log(`✓ S-12: 收藏 ${json.data?.list?.length ?? 0} 条`)
|
|||
|
|
})
|