Files
tyx_AI_xhs/tests/e2e/generation-terminal-actions.spec.ts
T

98 lines
5.9 KiB
TypeScript

import { mkdirSync } from "node:fs";
import { resolve } from "node:path";
import { expect, test } from "@playwright/test";
import { createServer, type ViteDevServer } from "vite";
let vite: ViteDevServer;
let webUrl: string;
const categories = [
["upstream_timeout", "使用原输入重试"],
["upstream_failed", "稍后重试"],
["safety_rejected", "修改提示词或参考图"],
["model_disabled", "选择其他模型或等待"],
["gateway_balance_insufficient", "选择未受影响模型或联系管理员"],
["gateway_contract_invalid", "选择其他模型或联系管理员"],
["reference_invalid", "更换或移除参考图"],
["unknown_retryable", "稍后重试"],
["unknown_non_retryable", "联系管理员"],
] as const;
test.use({ trace: "off" });
test.beforeAll(async () => {
vite = await createServer({ configFile: resolve("apps/web/vite.config.ts"), root: resolve("apps/web"), server: { host: "127.0.0.1", port: 0 } });
await vite.listen();
const address = vite.httpServer?.address();
if (!address || typeof address === "string") throw new Error("Vite did not expose a test port.");
webUrl = `http://127.0.0.1:${address.port}`;
});
test.afterAll(async () => vite.close());
test("TASK-WP2-06 renders the single frozen action for every generation category", async ({ page }) => {
let activeCategory: typeof categories[number][0] = "upstream_timeout";
await page.route("**/api/v1/auth/session", (route) => route.fulfill({ contentType: "application/json", json: {
audience: "user", authenticated: true, credits: { available_balance: 10, reserved_balance: 0 },
csrf_token: "csrf-terminal-fixture-00000000000000000000000000000000",
local_data: { capacity_status: "normal", hard_limit_bytes: 5_368_709_120, managed_content_bytes: 0 },
user: { creator_name: "Terminal User", role: "user", social_id: "@terminal", status: "active", user_id: "00000000-0000-4000-8000-000000000801" },
} }));
await page.route("**/api/v1/projects?status=active", (route) => route.fulfill({ contentType: "application/json", json: { active_count: 1, active_limit: 20, projects: [] } }));
await page.route("**/api/v1/models", (route) => route.fulfill({ contentType: "application/json", json: {
config_set_version: 1, configured_default_model_id: "gemini-3.1-flash-image-preview", models: [], recommended_model_id: null,
} }));
await page.route("**/api/v1/generations/current", (route) => route.fulfill({ contentType: "application/json", json: {
confirmed_credit_cost: 1, created_at: "2026-08-02T14:00:00.000Z", error_category: activeCategory,
generation_id: "00000000-0000-4000-8000-000000000802", model_config_version: 1,
model_id: "gemini-3.1-flash-image-preview", project_id: "00000000-0000-4000-8000-000000000803",
prompt: "失败任务", ratio: "3:4", reference_count: 0, reserved_credits: 0,
status: activeCategory === "safety_rejected" ? "rejected" : "failed", updated_at: "2026-08-02T14:01:00.000Z",
} }));
const evidenceRoot = process.env.DADA_EVIDENCE_DIR_GENERATION_RUNTIME;
for (const [category, action] of categories) {
activeCategory = category;
await page.goto(`${webUrl}/app`);
await expect(page.getByRole("button", { name: action })).toBeVisible();
await expect(page.locator(".current-task").getByRole("button")).toHaveCount(1);
await expect(page.getByText(/UPSTREAM_SECRET|internal\.invalid|stack trace/i)).toHaveCount(0);
if (evidenceRoot) {
const suffix = category === "gateway_balance_insufficient"
? "gateway-balance"
: category === "gateway_contract_invalid"
? "gateway-contract"
: category.replaceAll("_", "-");
const directory = resolve(evidenceRoot, `TDD-WP2-ERR-001-${suffix}`, "screenshots");
mkdirSync(directory, { recursive: true });
await page.screenshot({ fullPage: true, path: resolve(directory, `${category}.png`) });
}
}
});
test("TASK-WP2-06 succeeded task exposes its result without an error action", async ({ page }) => {
await page.route("**/api/v1/auth/session", (route) => route.fulfill({ contentType: "application/json", json: {
audience: "user", authenticated: true, credits: { available_balance: 9, reserved_balance: 0 },
csrf_token: "csrf-success-fixture-00000000000000000000000000000000",
local_data: { capacity_status: "normal", hard_limit_bytes: 5_368_709_120, managed_content_bytes: 104 },
user: { creator_name: "Success User", role: "user", social_id: "@success", status: "active", user_id: "00000000-0000-4000-8000-000000000811" },
} }));
await page.route("**/api/v1/projects?status=active", (route) => route.fulfill({ contentType: "application/json", json: { active_count: 1, active_limit: 20, projects: [] } }));
await page.route("**/api/v1/models", (route) => route.fulfill({ contentType: "application/json", json: { config_set_version: 1, configured_default_model_id: "gemini-3.1-flash-image-preview", models: [], recommended_model_id: null } }));
await page.route("**/api/v1/generations/current", (route) => route.fulfill({ contentType: "application/json", json: {
confirmed_credit_cost: 1, created_at: "2026-08-02T14:00:00.000Z", error_category: null,
generation_id: "00000000-0000-4000-8000-000000000812", model_config_version: 1,
model_id: "gemini-3.1-flash-image-preview", project_id: "00000000-0000-4000-8000-000000000813",
prompt: "成功任务", ratio: "3:4", reference_count: 0, reserved_credits: 0,
status: "succeeded", updated_at: "2026-08-02T14:01:00.000Z",
} }));
await page.goto(`${webUrl}/app`);
await expect(page.getByText("生成成功", { exact: true })).toBeVisible();
await expect(page.getByRole("link", { name: "进入编辑" })).toBeVisible();
const evidenceRoot = process.env.DADA_EVIDENCE_DIR_GENERATION_RUNTIME;
if (evidenceRoot) {
const directory = resolve(evidenceRoot, "TDD-WP2-GEN-002-success", "screenshots");
mkdirSync(directory, { recursive: true });
await page.screenshot({ fullPage: true, path: resolve(directory, "succeeded.png") });
}
});