diff --git a/apps/web/e2e/core-generation.spec.ts b/apps/web/e2e/core-generation.spec.ts index 4ff0750ba..5c006569a 100755 --- a/apps/web/e2e/core-generation.spec.ts +++ b/apps/web/e2e/core-generation.spec.ts @@ -19,7 +19,6 @@ const routeBrowserApiToTestApi = async ( }); }; -/** 登录操作,遇到 429 限流自动等待重试 */ async function loginWithRetry( request: any, email: string, @@ -31,7 +30,7 @@ async function loginWithRetry( data: { email, password }, }); if (response.status() !== 429) return response; - console.log(`[login] 触发限流,等待 65s 后重试 (${i + 1}/${maxRetries})`); + console.log(`[login] rate limited, waiting 65s (${i + 1}/${maxRetries})`); await new Promise((r) => setTimeout(r, 65000)); } return request.post(`${apiBase}/auth/login`, { @@ -46,8 +45,6 @@ type AssetListResponse = { id: string; name: string; status: string; - mime_type?: string; - file_type?: string; }>; }; type GenerationTaskResponse = { @@ -56,7 +53,6 @@ type GenerationTaskResponse = { progress: number; result_count: number; error_message?: string | null; - edit_plan_id?: string | null; }; test.describe("Core generation flow", () => { @@ -69,29 +65,29 @@ test.describe("Core generation flow", () => { await routeBrowserApiToTestApi(page); const suffix = Date.now().toString(36); - const email = `e2e-generation-${suffix}@example.com`; - const username = `e2e_generation_${suffix}`; - const libraryName = `E2E Gen Library ${suffix}`; + const email = `e2e-gen-${suffix}@example.com`; + const username = `e2e_gen_${suffix}`; + const libraryName = `E2E Gen Lib ${suffix}`; // Register const register = await request.post(`${apiBase}/auth/register`, { data: { email, username, password: PASSWORD, display_name: username }, }); - expect(register.status(), await register.text()).toBe(201); + expect(register.status()).toBe(201); const registerData = (await register.json()) as { user_id: string }; // Login const login = await loginWithRetry(request, email, PASSWORD); - expect(login.status(), await login.text()).toBe(200); + expect(login.status()).toBe(200); const loginData = (await login.json()) as { access_token: string }; const headers = { Authorization: `Bearer ${loginData.access_token}` }; // Create project const project = await request.post(`${apiBase}/projects`, { headers, - data: { name: `E2E Gen Project ${suffix}` }, + data: { name: `E2E Gen Proj ${suffix}` }, }); - expect(project.status(), await project.text()).toBe(200); + expect(project.status()).toBe(200); const projectData = (await project.json()) as ProjectResponse; // Create asset library @@ -99,11 +95,11 @@ test.describe("Core generation flow", () => { headers, data: { project_id: projectData.id, name: libraryName, kind: "video" }, }); - expect(library.status(), await library.text()).toBe(200); + expect(library.status()).toBe(200); const libraryData = (await library.json()) as LibraryResponse; // Upload source video - const sourceFileName = "e2e-generation-source.mp4"; + const sourceFileName = "e2e-gen-source.mp4"; const upload = await request.post(`${apiBase}/upload`, { headers, multipart: { @@ -112,11 +108,11 @@ test.describe("Core generation flow", () => { file: { name: sourceFileName, mimeType: "video/mp4", - buffer: Buffer.from("e2e generation source video data"), + buffer: Buffer.from("e2e source data"), }, }, }); - expect(upload.status(), await upload.text()).toBe(200); + expect(upload.status()).toBe(200); // Wait for asset to be ready let sourceAssetId = ""; @@ -166,74 +162,70 @@ test.describe("Core generation flow", () => { // Navigate to generate page await page.goto("/app/generate"); - await expect( - page.getByRole("heading", { name: "一键生成" }), - ).toBeVisible({ timeout: 20_000 }); + await expect(page.getByRole("heading", { name: "一键生成" })).toBeVisible({ timeout: 20_000 }); - // Fill in title - const titleText = `E2E 生成测试 ${suffix}`; - await page.getByPlaceholder("请输入视频标题").fill(titleText); + // Step 1: template - default selected, click next + await expect(page.locator(".xx-choice-item.selected")).toBeVisible(); + await page.getByRole("button", { name: "下一步" }).click(); - // Select the uploaded material - await expect(page.locator(".xx-material-card").first()).toBeVisible({ - timeout: 15_000, - }); - const materialCard = page - .locator(".xx-material-card") - .filter({ hasText: sourceFileName }); - await materialCard.click({ force: true }); - await expect(materialCard).toHaveClass(/xx-material-card-selected/, { - timeout: 10_000, - }); + // Step 2: select material + await expect(page.getByText("选择素材")).toBeVisible(); + const librarySelect = page.locator("select").first(); + await librarySelect.selectOption({ label: libraryName }); + const materialLabel = page.getByText(sourceFileName).locator(".."); + await expect(materialLabel.locator("input[type='checkbox']")).toBeVisible({ timeout: 10_000 }); + await materialLabel.locator("input[type='checkbox']").check(); + await page.getByRole("button", { name: "下一步" }).click(); - // Click generate - const generateButton = page.getByRole("button", { - name: "开始生成视频", - }); - await expect(generateButton).toBeEnabled(); + // Step 3: title + await expect(page.getByText("选择标题")).toBeVisible(); + const titleText = `E2E Test ${suffix}`; + await page.getByPlaceholder("输入自定义标题…").fill(titleText); + await page.getByRole("button", { name: "下一步" }).click(); - // Wait for the generation task to be created - const createTaskResponsePromise = page.waitForResponse( + // Step 4: voice + await expect(page.getByText("选择配音")).toBeVisible(); + const firstVoiceCard = page.locator(".xx-voice-choice-item").first(); + await firstVoiceCard.click(); + await page.getByRole("button", { name: "下一步" }).click(); + + // Step 5: confirm and generate + await expect(page.getByText("确认生成")).toBeVisible(); + + const createTaskPromise = page.waitForResponse( (response) => response.url().includes("/edit-plans") && response.request().method() === "POST" && !response.url().includes("/generate"), { timeout: 30_000 }, ); - await generateButton.click({ force: true }); - // Verify plan creation - try { - const planResponse = await createTaskResponsePromise; - expect(planResponse.ok()).toBe(true); - const planData = (await planResponse.json()) as { id: string }; - expect(planData.id).toBeTruthy(); + await page.getByRole("button", { name: "确认生成" }).click(); - // Wait for generation to show progress or completion - await expect(page.getByText(/正在生成视频|视频生成完成/)).toBeVisible({ - timeout: 30_000, - }); - } catch (e) { - // If plan creation response not caught, just check that generation started - const hasError = await page.getByText(/生成失败/).isVisible({ - timeout: 5_000, - }); - if (hasError) { - // It's OK if generation fails quickly (e.g., no worker), test the flow - } - } + const createTaskResp = await createTaskPromise; + expect(createTaskResp.ok()).toBeTruthy(); + const taskData = (await createTaskResp.json()) as { id: string; status: string }; - // Navigate to products page (verify page renders, not necessarily with products) - // Generation is async and may not complete in test environment; just verify the page loads + // Wait for generation to complete + await expect + .poll( + async () => { + const resp = await request.get( + `${apiBase}/generation-tasks/${taskData.id}`, + { headers }, + ); + if (!resp.ok()) return `http_${resp.status()}`; + const data = (await resp.json()) as GenerationTaskResponse; + if (data.error_message) return `error: ${data.error_message}`; + return data.status; + }, + { timeout: 120_000, intervals: [2_000, 3_000, 5_000] }, + ) + .toBe("completed"); + + // Verify result in product library await page.goto("/app/products"); - await expect(page.locator(".xx-products-page")).toBeVisible({ - timeout: 20_000, - }); - - // Navigate to history page - await page.goto("/app/history"); - await expect(page.getByRole("heading", { name: "任务历史" })).toBeVisible({ timeout: 20_000 }); - await expect(page.getByText("全部")).toBeVisible(); + await expect(page.getByText(titleText)).toBeVisible({ timeout: 15_000 }); }); test("generation task API creates and lists tasks", async ({ request }) => { @@ -251,19 +243,20 @@ test.describe("Core generation flow", () => { const loginData = (await login.json()) as { access_token: string }; const headers = { Authorization: `Bearer ${loginData.access_token}` }; - // Create project const project = await request.post(`${apiBase}/projects`, { headers, - data: { name: `API Gen Test ${suffix}` }, + data: { name: `E2E API Proj ${suffix}` }, }); expect(project.status()).toBe(200); + const projectData = (await project.json()) as ProjectResponse; - // Get user tasks - const tasks = await request.get(`${apiBase}/tasks`, { headers }); + const tasks = await request.get(`${apiBase}/generation-tasks`, { + headers, + params: { project_id: projectData.id }, + }); expect(tasks.status()).toBe(200); - const tasksData = (await tasks.json()) as { - items: Array<{ id: string; task_type: string }>; - }; + const tasksData = await tasks.json(); + expect(tasksData.items).toBeDefined(); expect(Array.isArray(tasksData.items)).toBe(true); }); });