76 lines
5.9 KiB
TypeScript
76 lines
5.9 KiB
TypeScript
import { mkdirSync } from "node:fs";
|
|
import { dirname, resolve } from "node:path";
|
|
|
|
import { expect, test } from "@playwright/test";
|
|
import { createServer, type ViteDevServer } from "vite";
|
|
|
|
let vite: ViteDevServer;
|
|
let webUrl: string;
|
|
const userId = "00000000-0000-4000-8000-000000000701";
|
|
const taskId = "00000000-0000-4000-8000-000000000702";
|
|
const projectId = "00000000-0000-4000-8000-000000000703";
|
|
|
|
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());
|
|
|
|
const session = {
|
|
audience: "user", authenticated: true,
|
|
credits: { available_balance: 9, reserved_balance: 1 },
|
|
csrf_token: "csrf-generation-fixture-000000000000000000000000000000",
|
|
local_data: { capacity_status: "critical", hard_limit_bytes: 5368709120, managed_content_bytes: 4831838208 },
|
|
user: { creator_name: "Generation User", role: "user", social_id: "@generation", status: "active", user_id: userId },
|
|
};
|
|
|
|
test("TDD-WP2-GEN-001-submit-snapshot renders queued truth, frozen credits and critical capacity without fake progress", async ({ page }) => {
|
|
await page.route("**/api/v1/auth/session", (route) => route.fulfill({ contentType: "application/json", json: session }));
|
|
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: "gemini-3.1-flash-image-preview" } }));
|
|
await page.route("**/api/v1/generations/current", (route) => route.fulfill({ contentType: "application/json", json: {
|
|
confirmed_credit_cost: 1, created_at: "2026-08-02T13:00:00.000Z", generation_id: taskId, model_config_version: 1,
|
|
model_id: "gemini-3.1-flash-image-preview", project_id: projectId, prompt: "生成中的海报", ratio: "3:4",
|
|
reference_count: 1, reserved_credits: 1, status: "queued", updated_at: "2026-08-02T13:00:00.000Z",
|
|
} }));
|
|
await page.goto(`${webUrl}/app`);
|
|
await expect(page.getByRole("heading", { name: "当前任务" })).toBeVisible();
|
|
await expect(page.getByText("排队中", { exact: true })).toBeVisible();
|
|
await expect(page.getByText("已冻结 1 点", { exact: true })).toBeVisible();
|
|
await expect(page.getByText(/存储空间已超过 90%/)).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "生成一张图片" })).toBeDisabled();
|
|
await expect(page.locator(".current-task").getByText(/\d+%/)).toHaveCount(0);
|
|
const directory = resolve(process.env.DADA_EVIDENCE_DIR_GENERATION ?? "artifacts/tdd/manual", "TDD-WP2-GEN-001-submit-snapshot", "screenshots");
|
|
mkdirSync(directory, { recursive: true });
|
|
await page.screenshot({ fullPage: true, path: resolve(directory, "queued-workspace.png") });
|
|
await page.setViewportSize({ height: 844, width: 390 });
|
|
await expect(page.getByText("已冻结 1 点", { exact: true })).toBeVisible();
|
|
await page.screenshot({ fullPage: true, path: resolve(directory, "queued-workspace-mobile.png") });
|
|
});
|
|
|
|
test("TDD-WP2-GEN-003-stale-config requires explicit confirmation before a second submit", async ({ page, context }) => {
|
|
const evidenceRoot = process.env.DADA_EVIDENCE_DIR_GENERATION;
|
|
const tracePath = evidenceRoot ? resolve(evidenceRoot, "TDD-WP2-GEN-003-stale-config", "trace.zip") : undefined;
|
|
if (tracePath) { mkdirSync(dirname(tracePath), { recursive: true }); await context.tracing.start({ screenshots: true, snapshots: true }); }
|
|
await page.route("**/api/v1/auth/session", (route) => route.fulfill({ contentType: "application/json", json: { ...session, credits: { available_balance: 10, reserved_balance: 0 }, local_data: { ...session.local_data, capacity_status: "normal", managed_content_bytes: 0 } } }));
|
|
await page.route("**/api/v1/projects?status=active", (route) => route.fulfill({ contentType: "application/json", json: { active_count: 0, active_limit: 20, projects: [] } }));
|
|
await page.route("**/api/v1/generations/current", (route) => route.fulfill({ body: "null", contentType: "application/json", status: 404 }));
|
|
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", recommended_model_id: "gemini-3.1-flash-image-preview",
|
|
models: [{ config_version: 1, contract_validation_status: "verified", credit_cost: 1, enabled: true, is_default: true, model_id: "gemini-3.1-flash-image-preview", prompt_max_length: 1000, recommendation_priority: 1, reference_limits: { max_file_bytes: 1024, max_files: 2, max_total_bytes: 2048 }, runtime_availability: { available_for_new_jobs: true, checked_at: "2026-08-02T13:00:00.000Z", reason: null }, supported_ratios: ["3:4"] }],
|
|
} }));
|
|
await page.route("**/api/v1/generations", (route) => route.fulfill({ contentType: "application/json", status: 412, json: { error: { code: "MODEL_CONFIG_VERSION_CONFLICT", correlation_id: "00000000-0000-4000-8000-000000000704", details: { latest_version: 2 }, message_key: "MODEL_CONFIG_VERSION_CONFLICT" } } }));
|
|
await page.goto(`${webUrl}/app`);
|
|
await page.getByLabel("描述你想生成的画面").fill("旧配置提交");
|
|
await page.getByRole("button", { name: "生成一张图片" }).click();
|
|
await expect(page.getByText(/模型配置已更新/)).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "确认最新配置" })).toBeVisible();
|
|
if (tracePath) await context.tracing.stop({ path: tracePath });
|
|
});
|