import { choosePreparation } from "./preparation-helpers"; import { expect, test } from "@playwright/test"; const headers = { "X-WQ-Request": "1" }; const scope = { instrument_type: "EQUITY", region: "USA", universe: "TOP3000", delay: 1, }; test("finite pipeline confirms scope and budget, executes native steps and restores run", async ({ page, }) => { const errors: string[] = []; page.on("pageerror", (e) => errors.push(e.message)); await page.goto("/"); await page.getByLabel("密码", { exact: true }).fill("browser-test-password"); await page.getByRole("button", { name: "进入工作空间" }).click(); await page .getByRole("navigation", { name: "主导航" }) .getByRole("button", { name: "研究流水线", exact: true }) .click(); await expect( page.getByRole("region", { name: "研究流水线", exact: true }), ).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"); 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" }, }); expect( ( await ( await page.request.post("/api/v1/ai/settings/test", { headers }) ).json() ).ready, ).toBe(true); await page.request.put("/api/v1/ai/settings", { headers, data: { ...config, enabled: true }, }); for (const dataset_id of [null, "TEST_FIN"]) { const job = await ( await page.request.post("/api/v1/catalog/sync-jobs", { headers, data: { scope, dataset_id }, }) ).json(); await expect .poll( async () => (await (await page.request.get(`/api/v1/sync-jobs/${job.id}`)).json()) .status, ) .toBe("completed"); } const params = new URLSearchParams( Object.entries(scope).map(([k, v]) => [k, String(v)]), ); const fields = await ( await page.request.get(`/api/v1/catalog/datasets/TEST_FIN/fields?${params}`) ).json(); const fixed = await ( await page.request.post("/api/v1/data-preparations/from-dataset", { headers, data: { scope, dataset_id: "TEST_FIN", collection_version: fields.collection_version, }, }) ).json(); expect( ( await page.request.post("/api/v1/catalog/operators/refresh", { headers }) ).ok(), ).toBe(true); expect( ( await page.request.post("/api/v1/catalog/setting-options/refresh", { headers, }) ).ok(), ).toBe(true); await page.reload(); await page.getByRole("button", { name: "新建自动研究", exact: true }).click(); await page.getByLabel("自动研究名称").fill("浏览器有限研究"); await page.getByLabel("自动研究假设").fill("研究排名稳定性"); await choosePreparation(page, fixed.name, "选择研究数据准备"); await page.getByLabel("自动研究名称").click(); for (const [label, value] of [ ["最大轮数", "1"], ["最大模拟条目数", "2"], ["最大模型调用数", "2"], ["每轮候选上限", "2"], ]) await page.getByLabel(label, { exact: true }).fill(value); await page.getByRole("button", { name: "核对并启动研究" }).click(); await expect( page.getByRole("dialog", { name: "确认自动研究授权" }), ).toContainText("最多 1 轮、2 条模拟、2 次模型调用"); const started = page.waitForResponse( (r) => r.url().endsWith("/research/flows/runs") && r.request().method() === "POST", ); await page .getByRole("dialog", { name: "确认自动研究授权" }) .getByRole("button", { name: "confirm", exact: true }) .click(); const response = await started; expect(response.status()).toBe(201); const run = await response.json(); await expect .poll( async () => ( await ( await page.request.get(`/api/v1/research/flows/runs/${run.id}`) ).json() ).status, { timeout: 40000 }, ) .toBe("completed"); const completed = await ( await page.request.get(`/api/v1/research/flows/runs/${run.id}`) ).json(); expect(completed.simulations_used).toBe(2); expect(completed.model_calls_used).toBe(2); expect(completed.steps).toHaveLength(8); await page.reload(); await page .getByRole("button", { name: "浏览器有限研究", exact: true }) .click(); await expect( page.getByRole("region", { name: "研究运行详情" }), ).toContainText("浏览器有限研究"); await expect( page.getByRole("button", { name: "关联回测", exact: true }), ).toBeVisible(); await page.screenshot({ path: "/tmp/wq-stage-three-run.png", fullPage: true, }); expect(errors).toEqual([]); });