Files
tyx_AI_xhs/tests/e2e/project-latest-exports.spec.ts

103 lines
6.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 projectId = "00000000-0000-4000-8000-000000000701";
const imageId = "00000000-0000-4000-8000-000000000702";
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("TDD-WP2-EXP-002 shows the no-export state and only its two allowed actions", async ({ page }) => {
await page.route("**/api/v1/auth/session", (route) => route.fulfill({ body: JSON.stringify({ csrf_token: "csrf-latest-export-fixture-000000000000000000000000000000", user: { creator_name: "Export User" } }), contentType: "application/json", status: 200 }));
await page.route(`**/api/v1/projects/${projectId}`, (route) => route.fulfill({
body: JSON.stringify({
canvas_state: { background: { adjustments: { brightness: 0, contrast: 0, crop: null, filter: "none", fit: "fill", saturation: 0, sharpness: 0, temperature: 0 }, asset_id: imageId }, elements: [], pixel_height: 1440, pixel_width: 1080, ratio: "3:4", schema_version: 1 },
created_at: "2026-08-03T08:00:00.000Z", current_image_id: imageId, deleted_at: null, draft_prompt: "导出空状态",
generations: [], images: [{ created_at: "2026-08-03T08:00:00.000Z", generation_id: "00000000-0000-4000-8000-000000000703", image_id: imageId }],
latest_exports: [], name: "导出空状态", pixel_height: 1440, pixel_width: 1080, project_id: projectId, purge_at: null,
ratio: "3:4", save_status: "saved", state_version: 2, status: "active", successful_image_count: 1, updated_at: "2026-08-03T08:00:00.000Z",
}), contentType: "application/json", status: 200,
}));
await page.goto(`${webUrl}/app/projects/${projectId}`);
const region = page.getByRole("region", { name: "最新成品" });
await expect(region.getByText("暂无导出成品", { exact: true })).toBeVisible();
await expect(region.getByRole("link", { name: "进入编辑并导出" })).toHaveAttribute("href", `/app/projects/${projectId}/editor`);
await expect(region.getByRole("link", { name: "下载原始生成图" })).toHaveAttribute("href", `/api/v1/private-assets/projects/${projectId}/images/${imageId}`);
await expect(region.getByRole("link")).toHaveCount(2);
expect(await region.getByRole("button").count()).toBe(0);
const evidenceRoot = process.env.DADA_EVIDENCE_DIR_LATEST_EXPORTS;
if (evidenceRoot) {
const directory = resolve(evidenceRoot, "TDD-WP2-EXP-002-empty-latest", "screenshots");
mkdirSync(directory, { recursive: true });
await page.screenshot({ fullPage: true, path: resolve(directory, "no-export.png") });
}
});
test("TDD-WP2-EXP-001 exposes the frozen browser result matrix without conflating download and persistence", async ({ page }) => {
await page.goto(webUrl);
const matrix = await page.evaluate(async () => {
const { exportResultCopy, runExportFlow } = await import("/src/export-flow.ts");
const run = async (downloadFails: boolean, persistFails: boolean) => runExportFlow({
compose: async () => new Blob(["browser-export"], { type: "image/png" }),
download: async () => { if (downloadFails) throw new Error("download_failed"); },
persist: async () => { if (persistFails) throw new Error("persist_failed"); },
});
const results = [await run(false, false), await run(false, true), await run(true, false)];
return results.map((result) => ({ copy: exportResultCopy[result.status], status: result.status }));
});
expect(matrix).toEqual([
{ copy: "已下载并保存为最新成品", status: "downloaded_and_saved" },
{ copy: "文件已下载,但未保存为最新成品", status: "downloaded_not_saved" },
{ copy: "浏览器下载失败,可重新下载同一次合成结果", status: "download_failed" },
]);
await page.evaluate((items) => {
document.body.innerHTML = `<main style="font-family:system-ui;padding:32px"><h1>导出结果矩阵</h1>${items.map((item) => `<p data-status="${item.status}" style="padding:16px;border:1px solid #111">${item.copy}</p>`).join("")}</main>`;
}, matrix);
const evidenceRoot = process.env.DADA_EVIDENCE_DIR_LATEST_EXPORTS;
if (evidenceRoot) {
const directory = resolve(evidenceRoot, "TDD-WP2-EXP-001-result-matrix", "screenshots");
mkdirSync(directory, { recursive: true });
await page.screenshot({ fullPage: true, path: resolve(directory, "result.png") });
}
});
test("TDD-WP2-EXP-001 lists only the newest saved artifact for each format", async ({ page }) => {
await page.route("**/api/v1/auth/session", (route) => route.fulfill({ body: JSON.stringify({ csrf_token: "csrf-latest-list-fixture-00000000000000000000000000000000", user: { creator_name: "Export User" } }), contentType: "application/json", status: 200 }));
await page.route(`**/api/v1/projects/${projectId}`, (route) => route.fulfill({
body: JSON.stringify({
canvas_state: { background: { adjustments: { brightness: 0, contrast: 0, crop: null, filter: "none", fit: "fill", saturation: 0, sharpness: 0, temperature: 0 }, asset_id: imageId }, elements: [], pixel_height: 1440, pixel_width: 1080, ratio: "3:4", schema_version: 1 },
created_at: "2026-08-03T08:00:00.000Z", current_image_id: imageId, deleted_at: null, draft_prompt: "最新成品",
generations: [], images: [{ created_at: "2026-08-03T08:00:00.000Z", generation_id: "00000000-0000-4000-8000-000000000703", image_id: imageId }],
latest_exports: ["jpg", "png"].map((format, index) => ({
byte_size: 100 + index, created_at: "2026-08-03T08:00:00.000Z",
download_url: `/api/v1/projects/${projectId}/latest-exports/${format}`,
export_id: `00000000-0000-4000-8000-00000000071${index}`, format, pixel_height: 1440, pixel_width: 1080,
sha256: String(index).repeat(64), state_version: 2,
})),
name: "最新成品", pixel_height: 1440, pixel_width: 1080, project_id: projectId, purge_at: null,
ratio: "3:4", save_status: "saved", state_version: 2, status: "active", successful_image_count: 1, updated_at: "2026-08-03T08:00:00.000Z",
}), contentType: "application/json", status: 200,
}));
await page.goto(`${webUrl}/app/projects/${projectId}`);
const region = page.getByRole("region", { name: "最新成品" });
await expect(region.getByRole("link", { name: "重新下载" })).toHaveCount(2);
await expect(region.getByText("JPG", { exact: true })).toBeVisible();
await expect(region.getByText("PNG", { exact: true })).toBeVisible();
await expect(region.getByText("暂无导出成品", { exact: true })).toHaveCount(0);
});