Files
xiaoxia-saas/apps/web/e2e/test_asset.spec.ts
T
xiaoxia a5e5ba4426
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
Deploy / Deploy Staging (push) Failing after 142h46m12s
CI/CD Pipeline / Frontend Lint (push) Failing after 142h46m42s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 142h46m50s
test: 添加核心流程 E2E 测试
- 认证流程(注册/登录/登出/获取用户信息,正向+反向共 10 个用例)
- 工作空间流程(创建/列出/详情/成员,共 6 个用例)
- 项目流程(创建/列出/详情/未授权,共 6 个用例)
- 素材库流程(创建库/列出库/创建素材/列出素材,共 8 个用例)

共 30 个回归测试用例,覆盖视频生成 SaaS 核心业务路径。
2026-07-03 14:07:29 +08:00

242 lines
8.1 KiB
TypeScript
Executable File
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.
/**
* 素材库流程 E2E 测试
*
* 覆盖:创建素材库、列出素材库、创建素材记录
* 每个测试独立,先注册登录获取 auth token。
*/
import { expect, test } from '@playwright/test';
const PASSWORD = 'Test123456!';
const apiBase = process.env.E2E_API_BASE || '/api/v1';
function uniqueEmail(prefix: string): string {
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}@example.com`;
}
function uniqueUsername(prefix: string): string {
return `${prefix}_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
}
/** 注册并登录,返回 { headers, email, username, userId } */
async function createAuthedUser(request: any, label: string) {
const email = uniqueEmail(label);
const username = uniqueUsername(label);
const reg = await request.post(`${apiBase}/auth/register`, {
data: { email, password: PASSWORD, username, display_name: `E2E ${label}` },
});
expect(reg.ok(), `注册应成功: ${await reg.text()}`).toBeTruthy();
const regData = await reg.json();
const login = await request.post(`${apiBase}/auth/login`, {
data: { email, password: PASSWORD },
});
expect(login.ok(), `登录应成功: ${await login.text()}`).toBeTruthy();
const loginData = await login.json();
return {
headers: { Authorization: `Bearer ${loginData.access_token}` },
email,
username,
userId: regData.user_id,
};
}
/** 创建一个项目并返回 project id */
async function createProject(request: any, headers: Record<string, string>, suffix: string): Promise<string> {
const resp = await request.post(`${apiBase}/projects`, {
headers,
data: { name: `Asset Test Proj ${suffix}`, description: 'E2E asset test' },
});
expect(resp.ok(), `创建项目应成功: ${await resp.text()}`).toBeTruthy();
const data = await resp.json();
return data.id;
}
test.describe('素材库流程', () => {
test('创建素材库', async ({ request }) => {
const { headers } = await createAuthedUser(request, 'lib-create');
const projectId = await createProject(request, headers, Date.now().toString());
const response = await request.post(`${apiBase}/asset-libraries`, {
headers,
data: {
project_id: projectId,
name: `视频素材库 ${Date.now()}`,
kind: 'video',
},
});
expect(response.ok(), `创建素材库应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
const data = await response.json();
expect(data.id, '应返回素材库 ID').toBeTruthy();
expect(data.name).toContain('视频素材库');
expect(data.kind).toBe('video');
expect(data.project_id).toBe(projectId);
});
test('创建素材库 - 无效 kind 反向', async ({ request }) => {
const { headers } = await createAuthedUser(request, 'lib-badkind');
const projectId = await createProject(request, headers, Date.now().toString());
const response = await request.post(`${apiBase}/asset-libraries`, {
headers,
data: {
project_id: projectId,
name: 'Bad Kind Library',
kind: 'invalid_kind',
},
});
// kind 有 pattern 校验 ^video|voice|image)$,应返回 422
expect([400, 422]).toContain(response.status());
});
test('创建素材库 - 不存在的项目反向', async ({ request }) => {
const { headers } = await createAuthedUser(request, 'lib-nopj');
const response = await request.post(`${apiBase}/asset-libraries`, {
headers,
data: {
project_id: 'nonexistent-project-999',
name: 'Orphan Library',
kind: 'video',
},
});
expect(response.status(), '不存在的项目应返回 404').toBe(404);
});
test('列出素材库', async ({ request }) => {
const { headers } = await createAuthedUser(request, 'lib-list');
const projectId = await createProject(request, headers, Date.now().toString());
// 创建 2 个不同类型的素材库
await request.post(`${apiBase}/asset-libraries`, {
headers,
data: { project_id: projectId, name: `Video Lib ${Date.now()}`, kind: 'video' },
});
await request.post(`${apiBase}/asset-libraries`, {
headers,
data: { project_id: projectId, name: `Image Lib ${Date.now()}`, kind: 'image' },
});
// 列出(按 project_id 过滤)
const response = await request.get(`${apiBase}/asset-libraries`, {
headers,
params: { project_id: projectId },
});
expect(response.ok(), `列出素材库应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
const data = await response.json();
const items = data.items || [];
expect(items.length, '应至少有 2 个素材库').toBeGreaterThanOrEqual(2);
const kinds = items.map((i: any) => i.kind);
expect(kinds).toContain('video');
expect(kinds).toContain('image');
});
test('创建素材记录', async ({ request }) => {
const { headers, userId } = await createAuthedUser(request, 'asset-create');
const projectId = await createProject(request, headers, Date.now().toString());
// 创建素材库
const lib = await request.post(`${apiBase}/asset-libraries`, {
headers,
data: { project_id: projectId, name: `Asset Lib ${Date.now()}`, kind: 'video' },
});
expect(lib.ok()).toBeTruthy();
const libData = await lib.json();
// 创建素材记录
const response = await request.post(`${apiBase}/assets`, {
headers,
data: {
project_id: projectId,
library_id: libData.id,
name: `test_video_${Date.now()}.mp4`,
storage_key: `uploads/e2e/test_${Date.now()}.mp4`,
mime_type: 'video/mp4',
metadata: { duration: 15.5, resolution: '1080p' },
file_size: 1024000,
status: 'ready',
uploaded_by_user_id: userId,
},
});
expect(response.ok(), `创建素材应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
const data = await response.json();
expect(data.id, '应返回素材 ID').toBeTruthy();
expect(data.name).toContain('test_video');
expect(data.mime_type).toBe('video/mp4');
expect(data.library_id).toBe(libData.id);
});
test('列出素材', async ({ request }) => {
const { headers, userId } = await createAuthedUser(request, 'asset-list');
const projectId = await createProject(request, headers, Date.now().toString());
// 创建素材库
const lib = await request.post(`${apiBase}/asset-libraries`, {
headers,
data: { project_id: projectId, name: `List Lib ${Date.now()}`, kind: 'video' },
});
expect(lib.ok(), `创建素材库应成功: ${await lib.text()}`).toBeTruthy();
const libData = await lib.json();
// 创建 2 个素材
await request.post(`${apiBase}/assets`, {
headers,
data: {
project_id: projectId,
library_id: libData.id,
name: `clip_a_${Date.now()}.mp4`,
storage_key: `uploads/e2e/clip_a.mp4`,
mime_type: 'video/mp4',
status: 'ready',
uploaded_by_user_id: userId,
},
});
await request.post(`${apiBase}/assets`, {
headers,
data: {
project_id: projectId,
library_id: libData.id,
name: `clip_b_${Date.now()}.mp4`,
storage_key: `uploads/e2e/clip_b.mp4`,
mime_type: 'video/mp4',
status: 'ready',
uploaded_by_user_id: userId,
},
});
// 列出素材
const response = await request.get(`${apiBase}/assets`, {
headers,
params: { library_id: libData.id },
});
expect(response.ok(), `列出素材应返回 2xx,实际: ${response.status()} ${await response.text()}`).toBeTruthy();
const data = await response.json();
const items = data.items || [];
expect(items.length, '应至少有 2 个素材').toBeGreaterThanOrEqual(2);
});
test('未登录创建素材库 - 反向', async ({ request }) => {
const response = await request.post(`${apiBase}/asset-libraries`, {
data: {
project_id: 'some-project',
name: 'Unauthorized Library',
kind: 'video',
},
});
expect([401, 403]).toContain(response.status());
});
});