Files
xiaoxia-saas/apps/web/e2e/core-upload.spec.ts
T
Xiaoxia AI f9a934d916
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 24s
Deploy / Deploy Staging (push) Successful in 1m23s
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
test(web): stabilize core E2E against V21 UI
2026-06-25 01:38:15 +08:00

150 lines
5.7 KiB
TypeScript

import { expect, test } from '@playwright/test';
const PASSWORD = 'SmokePass123!';
const apiBase = process.env.E2E_API_BASE || '/api/v1';
const apiOrigin = apiBase.endsWith('/api/v1') ? apiBase.slice(0, -'/api/v1'.length) : '';
const routeBrowserApiToTestApi = async (page: import('@playwright/test').Page) => {
if (!apiOrigin) return;
await page.route('**/api/v1/**', async (route) => {
const sourceUrl = new URL(route.request().url());
const response = await route.fetch({ url: `${apiOrigin}${sourceUrl.pathname}${sourceUrl.search}` });
await route.fulfill({ response });
});
};
type WorkspaceResponse = { id?: string; workspace_id?: string };
type ProjectResponse = { id: string };
type LibraryResponse = { id: string };
test.describe('Core media upload flow', () => {
test('uploads a MOV asset from the browser and shows it as ready', async ({ page, request }) => {
test.setTimeout(120_000);
await routeBrowserApiToTestApi(page);
const suffix = Date.now().toString(36);
const email = `e2e-mov-${suffix}@example.com`;
const username = `e2e_mov_${suffix}`;
const register = await request.post(`${apiBase}/auth/register`, {
data: {
email,
username,
password: PASSWORD,
display_name: username,
},
});
expect(register.status(), await register.text()).toBe(201);
const registerData = (await register.json()) as { user_id: string };
const login = await request.post(`${apiBase}/auth/login`, {
data: { email, password: PASSWORD },
});
expect(login.status(), await login.text()).toBe(200);
const loginData = (await login.json()) as { access_token: string };
const headers = { Authorization: `Bearer ${loginData.access_token}` };
const workspace = await request.post(`${apiBase}/workspaces`, {
headers,
data: { name: `E2E Workspace ${suffix}` },
});
expect(workspace.status(), await workspace.text()).toBe(201);
const workspaceData = (await workspace.json()) as WorkspaceResponse;
const workspaceId = workspaceData.id || workspaceData.workspace_id;
expect(workspaceId).toBeTruthy();
const project = await request.post(`${apiBase}/projects`, {
headers,
data: {
workspace_id: workspaceId,
name: `E2E Project ${suffix}`,
description: 'Playwright upload smoke',
},
});
expect(project.status(), await project.text()).toBe(200);
const projectData = (await project.json()) as ProjectResponse;
const library = await request.post(`${apiBase}/asset-libraries`, {
headers,
data: {
workspace_id: workspaceId,
project_id: projectData.id,
name: `E2E Video Library ${suffix}`,
kind: 'video',
},
});
expect(library.status(), await library.text()).toBe(200);
const libraryData = (await library.json()) as LibraryResponse;
await page.addInitScript(
({ token, user, projectId, workspaceId }) => {
localStorage.setItem('access_token', token);
localStorage.setItem('auth-storage', JSON.stringify({ state: { user, isAuthenticated: true }, version: 0 }));
sessionStorage.setItem(`project-workspace:${projectId}`, workspaceId);
},
{
token: loginData.access_token,
projectId: projectData.id,
workspaceId,
user: {
id: registerData.user_id,
user_id: registerData.user_id,
email,
username,
display_name: username,
is_email_verified: true,
email_verified: true,
},
}
);
await page.goto(`/projects/${projectData.id}/assets`);
await expect(page.getByText('点击或拖拽素材到这里上传')).toBeEnabled({ timeout: 20_000 });
const upload = await request.post(`${apiBase}/upload`, {
headers,
multipart: {
workspace_id: workspaceId || '',
project_id: projectData.id,
library_id: libraryData.id,
file: {
name: 'e2e-sample.MOV',
mimeType: 'video/quicktime',
buffer: Buffer.from('playwright mov upload smoke'),
},
},
});
expect(upload.status(), await upload.text()).toBe(200);
await expect(page.getByText(/上传失败|素材列表加载失败|素材库加载失败/)).toHaveCount(0, { timeout: 5_000 });
await expect
.poll(
async () => {
const assets = await request.get(`${apiBase}/assets`, {
headers,
params: { library_id: libraryData.id },
});
if (!assets.ok()) {
return `http_${assets.status()}`;
}
const data = (await assets.json()) as { items: Array<{ name: string; status: string; file_type?: string; mime_type?: string }> };
const asset = data.items.find((item) => item.name === 'e2e-sample.MOV');
return asset ? `${asset.mime_type || asset.file_type || ''}:${asset.status}` : 'missing';
},
{ timeout: 30_000, intervals: [1_000, 2_000, 3_000] }
)
.toMatch(/^(video\/quicktime|video\/mp4|video)?:ready$/);
await page.reload();
await expect(page.getByText(/素材就绪度|Ready/)).toBeVisible({ timeout: 20_000 });
await expect(page.getByText(/预计成片|视频素材数量偏少|素材准备度良好/)).toBeVisible({ timeout: 20_000 });
await expect(page.getByText('e2e-sample.MOV', { exact: true })).toBeVisible({ timeout: 20_000 });
await page.locator('.xx-vertical-card').filter({ hasText: 'e2e-sample.MOV' }).getByRole('button', { name: /通\s*过/ }).click();
await expect(page.getByText('复核状态已更新')).toBeVisible({ timeout: 10_000 });
await expect(page.getByText(/已通过|approved/)).toBeVisible({ timeout: 20_000 });
await expect(page.getByText(/素材加载失败|上传失败/)).toHaveCount(0);
});
});