import { expect, test, type Page } from "@playwright/test"; const headers = { "X-WQ-Request": "1" }; async function login(page: Page) { await page.goto("/#backtests"); await page.getByLabel("密码", { exact: true }).fill("browser-test-password"); await page.getByRole("button", { name: "进入工作空间" }).click(); await expect(page.getByRole("button", { name: "新建回测" })).toBeVisible(); await page.request.put("/api/v1/account/credentials", { headers, data: { email: "test@example.com", password: "synthetic-password" }, }); await page.request.post("/api/v1/account/connect", { headers }); await expect .poll( async () => (await (await page.request.get("/api/v1/account")).json()) .connection_status, ) .toBe("connected"); } test("draft, immutable preview, mixed-result persistence and responsive workspace", async ({ page, }) => { const errors: string[] = []; page.on("pageerror", (e) => errors.push(e.message)); await login(page); await page.getByRole("button", { name: "新建回测" }).click(); await page.getByLabel("运行名称", { exact: true }).fill("浏览器回测验收"); await page.getByRole("textbox", { name: "Region", exact: true }).fill("USA"); await page .getByRole("textbox", { name: "Universe", exact: true }) .fill("TOP3000"); await page.getByLabel("回测候选").fill("rank(close)\n-rank(volume)"); await page.getByRole("button", { name: "保存草稿", exact: true }).click(); await expect(page.getByText("草稿已保存", { exact: true })).toBeVisible(); await page.getByRole("button", { name: "预览回测", exact: true }).click(); await expect( page.getByText(/浏览器回测验收 · 2 条候选 · 1 个平台批次/), ).toBeVisible(); await page.screenshot({ path: "../output/playwright/backtest-preview.png" }); await page.getByRole("button", { name: "确认启动回测", exact: true }).click(); await expect(page.getByText("2 / 2 已保存", { exact: true })).toBeVisible({ timeout: 15000, }); await page.getByRole("button", { name: "rank(close)", exact: true }).click(); await expect(page.locator(".backtest-item-detail")).toContainText( '"observed_at"', ); await expect(page.locator(".backtest-item-detail")).toContainText( '"sharpe": null', ); for (const width of [1440, 850, 390]) { await page.setViewportSize({ width, height: 900 }); expect( await page.evaluate( () => document.documentElement.scrollWidth <= innerWidth, ), ).toBe(true); await page.screenshot({ path: `../output/playwright/backtest-results-${width}.png`, }); } await page.keyboard.press("Escape"); await expect(page.getByRole("button", { name: "新建回测" })).toBeVisible(); await page.reload(); await page .getByRole("button", { name: "浏览器回测验收", exact: true }) .click(); await expect(page.getByText("2 / 2 已保存", { exact: true })).toBeVisible(); expect(errors).toEqual([]); }); test("AI prepares one fixed preview, confirms once, and shows live run independently", async ({ page, }) => { await login(page); const config = { base_url: "https://model.test/v1", model: "test-model" }; await page.request.put("/api/v1/ai/settings", { headers, data: { ...config, api_key: "synthetic-key" }, }); await page.request.post("/api/v1/ai/settings/test", { headers }); await page.request.put("/api/v1/ai/settings", { headers, data: { ...config, enabled: true }, }); await page.reload(); await page.getByRole("button", { name: "切换研究助手" }).click(); const chat = page.getByRole("complementary", { name: "AI 研究助手" }); await chat.getByRole("button", { name: "新会话", exact: true }).click(); const before = ( await (await page.request.get("/api/v1/backtests/runs")).json() ).total; await chat.getByLabel("发送给研究助手").fill("为我准备一次回测"); await chat.getByRole("button", { name: "发送", exact: true }).click(); await expect(chat.getByRole("button", { name: "确认执行" })).toBeEnabled(); expect( (await (await page.request.get("/api/v1/backtests/runs")).json()).total, ).toBe(before); await chat.getByRole("button", { name: "确认执行" }).click(); await expect(chat.getByText("已保存 1/1 · 平台失败 0")).toBeVisible({ timeout: 15000, }); await chat.getByRole("button", { name: "打开回测详情", exact: true }).click(); await expect(page.getByText("1 / 1 已保存", { exact: true })).toBeVisible(); }); test("MCP direct batch appears through its run link and source filter", async ({ page, }) => { await login(page); const token = ( await ( await page.request.post("/api/v1/test/mcp-token", { headers }) ).json() ).token; const response = await page.request.post("/api/v1/mcp/", { headers: { Authorization: `Bearer ${token}`, Accept: "application/json, text/event-stream", }, data: { jsonrpc: "2.0", id: "browser-batch", method: "tools/call", params: { name: "submit_backtests", arguments: { name: "MCP 浏览器留痕", idempotency_key: "browser-mcp", duplicate_policy: "rerun", candidates: [ { client_item_id: "browser-1", expression: "rank(close) + 987", settings: { instrumentType: "EQUITY", region: "USA", universe: "TOP3000", delay: 1, decay: 0, neutralization: "INDUSTRY", truncation: 0.08, pasteurization: "ON", unitHandling: "VERIFY", nanHandling: "OFF", language: "FASTEXPR", visualization: false, maxTrade: "OFF", maxPosition: "OFF", }, }, ], source: { reference: "browser-conversation", hypothesis: "验证直接回测留痕", }, }, }, }, }); expect(response.ok()).toBeTruthy(); const result = (await response.json()).result; expect(result.isError).toBeFalsy(); const run = result.structuredContent; await page.goto(run.web_url); await expect(page.getByText("1 / 1 已保存", { exact: true })).toBeVisible({ timeout: 20000, }); await expect( page.getByText("研究来源:MCP 研究", { exact: true }), ).toBeVisible(); await expect( page.getByText("来源引用:browser-conversation", { exact: true }), ).toBeVisible(); await page.reload(); await expect(page.getByText("1 / 1 已保存", { exact: true })).toBeVisible(); await page.keyboard.press("Escape"); await page .getByRole("dialog") .getByRole("heading") .getByRole("button") .click(); await page.getByText("全部研究来源", { exact: true }).click(); await page.getByRole("option", { name: /MCP 研究/ }).click(); await expect( page.getByRole("button", { name: "MCP 浏览器留痕", exact: true }), ).toBeVisible(); });