98 lines
5.9 KiB
TypeScript
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") });
|
|
}
|
|
});
|