test(e2e): gate generation download flow
Deploy / Build Production Runtime Images (push) Successful in 8m53s
Deploy / Deploy Production (push) Successful in 58s
Deploy / Production Browser E2E (push) Successful in 36s
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
Deploy / Deploy Staging (push) Has been skipped
Deploy / Build Production Runtime Images (push) Successful in 8m53s
Deploy / Deploy Production (push) Successful in 58s
Deploy / Production Browser E2E (push) Successful in 36s
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
Deploy / Deploy Staging (push) Has been skipped
This commit is contained in:
@@ -226,4 +226,4 @@ jobs:
|
||||
-v "$PWD:/workspace" \
|
||||
-w /workspace/apps/web \
|
||||
mcr.microsoft.com/playwright:v1.45.0-jammy \
|
||||
sh -lc 'npm ci && npx playwright test --project=chromium e2e/core-upload.spec.ts'
|
||||
sh -lc 'npm ci && npx playwright test --project=chromium e2e/core-upload.spec.ts e2e/core-generation.spec.ts'
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
const PASSWORD = 'SmokePass123!';
|
||||
|
||||
type WorkspaceResponse = { id?: string; workspace_id?: string };
|
||||
type ProjectResponse = { id: string };
|
||||
type LibraryResponse = { id: string };
|
||||
type GenerationTaskResponse = { id: string; status: string; progress: number; result_count: number; error_message?: string | null };
|
||||
type GeneratedVideoResponse = { id: string; name: string; file_url: string; file_size: number };
|
||||
|
||||
test.describe('Core generation and download flow', () => {
|
||||
test('generates an MP4 from the browser and exposes a playable download', async ({ page, request }) => {
|
||||
test.setTimeout(180_000);
|
||||
|
||||
const suffix = Date.now().toString(36);
|
||||
const email = `e2e-generation-${suffix}@example.com`;
|
||||
const username = `e2e_generation_${suffix}`;
|
||||
const libraryName = `E2E Generation Library ${suffix}`;
|
||||
const apiBase = '/api/v1';
|
||||
|
||||
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 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 Generation 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 Generation Project ${suffix}` },
|
||||
});
|
||||
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: libraryName, kind: 'video' },
|
||||
});
|
||||
expect(library.status(), await library.text()).toBe(200);
|
||||
const libraryData = (await library.json()) as LibraryResponse;
|
||||
|
||||
await page.goto('/login');
|
||||
await page.getByPlaceholder('邮箱').fill(email);
|
||||
await page.getByPlaceholder('密码').fill(PASSWORD);
|
||||
await page.getByRole('button', { name: /登\s*录/ }).click();
|
||||
await expect(page).toHaveURL(/\/workspaces/, { timeout: 20_000 });
|
||||
|
||||
await page.goto(`/projects/${projectData.id}/generation`);
|
||||
await expect(page.getByText('项目生成任务')).toBeVisible({ timeout: 20_000 });
|
||||
await page.locator('.ant-select-selector').first().click();
|
||||
await page.getByText(`${libraryName} (video)`).click();
|
||||
|
||||
const createTaskResponsePromise = page.waitForResponse(
|
||||
(response) => response.url().includes('/api/v1/generation/tasks') && response.request().method() === 'POST',
|
||||
{ timeout: 30_000 }
|
||||
);
|
||||
await page.getByRole('button', { name: '发起生成' }).click();
|
||||
const createTaskResponse = await createTaskResponsePromise;
|
||||
expect(createTaskResponse.status(), await createTaskResponse.text()).toBe(200);
|
||||
const createdTask = (await createTaskResponse.json()) as GenerationTaskResponse;
|
||||
|
||||
await expect(page.getByText(/任务状态:生成完成/)).toBeVisible({ timeout: 90_000 });
|
||||
await expect(page.getByText(/生成失败|生成任务查询失败|生成结果查询失败/)).toHaveCount(0);
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const task = await request.get(`${apiBase}/generation/tasks/${createdTask.id}`, { headers });
|
||||
if (!task.ok()) {
|
||||
return `http_${task.status()}`;
|
||||
}
|
||||
const data = (await task.json()) as GenerationTaskResponse;
|
||||
return `${data.status}:${data.result_count}:${data.error_message || ''}`;
|
||||
},
|
||||
{ timeout: 90_000, intervals: [1_000, 2_000, 5_000] }
|
||||
)
|
||||
.toMatch(/^completed:[1-9]\d*:/);
|
||||
|
||||
const results = await request.get(`${apiBase}/generation/tasks/${createdTask.id}/results`, { headers });
|
||||
expect(results.status(), await results.text()).toBe(200);
|
||||
const resultsData = (await results.json()) as { items: GeneratedVideoResponse[] };
|
||||
expect(resultsData.items.length).toBeGreaterThan(0);
|
||||
const generatedVideo = resultsData.items[0];
|
||||
expect(generatedVideo.name).toMatch(/\.mp4$/);
|
||||
expect(generatedVideo.file_size).toBeGreaterThan(0);
|
||||
|
||||
await page.goto(`/projects/${projectData.id}/results`);
|
||||
await expect(page.getByText(generatedVideo.name, { exact: true })).toBeVisible({ timeout: 20_000 });
|
||||
const downloadUrlResponse = await request.get(`${apiBase}/generated-videos/${generatedVideo.id}/download-url`, { headers });
|
||||
expect(downloadUrlResponse.status(), await downloadUrlResponse.text()).toBe(200);
|
||||
const downloadData = (await downloadUrlResponse.json()) as { download_url: string };
|
||||
const videoResponse = await request.get(downloadData.download_url, { timeout: 30_000 });
|
||||
expect(videoResponse.status(), await videoResponse.text()).toBe(200);
|
||||
expect(videoResponse.headers()['content-type'] || '').toContain('video/mp4');
|
||||
const videoBody = await videoResponse.body();
|
||||
expect(videoBody.length).toBeGreaterThan(1024);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user