import { expect, test } from "@playwright/test"; const headers = { "X-WQ-Request": "1" }; const scope = { instrument_type: "EQUITY", region: "USA", universe: "TOP3000", delay: 1, }; test("feature conversion, saved views and immutable evaluations", async ({ page, }) => { const errors: string[] = []; page.on("pageerror", (e) => errors.push(e.message)); await page.goto("/#features"); await page.getByLabel("密码", { exact: true }).fill("browser-test-password"); await page.getByRole("button", { name: "进入工作空间" }).click(); await expect( page.getByRole("button", { 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"); for (const dataset_id of [null, "TEST_FIN"]) { const result = await page.request.post("/api/v1/catalog/sync-jobs", { headers, data: { scope, dataset_id }, }); const job = await result.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/catalog/inputs", { headers, data: { scope, dataset_id: "TEST_FIN", collection_version: fields.collection_version, selection: "all", }, }) ).json(); expect(fixed.id).toBeTruthy(); await page.reload(); await page.getByRole("button", { name: "新建特征方案", exact: true }).click(); await page.getByLabel("特征方案名称", { exact: true }).fill("浏览器特征验收"); await page .getByLabel("特征经济假设", { exact: true }) .fill("排名可降低异常值影响"); await page.getByRole("combobox", { name: "特征固定输入" }).click(); await page .getByRole("option") .filter({ hasText: fixed.id.slice(0, 8) }) .click(); await page.getByLabel("特征方案名称", { exact: true }).click(); await page.getByRole("button", { name: "添加处理步骤" }).click(); await page.getByLabel("步骤 1 名称").fill("横截面排名"); await page.getByLabel("步骤 1 理由").fill("减少尺度影响"); await page.getByLabel("步骤 1 表达式").fill("rank(TEST_FIN_001)"); await page .getByRole("textbox", { name: "模板名称", exact: true }) .fill("特征转换模板"); await page.getByLabel("field 候选取值").fill("TEST_FIN_001"); await page.getByRole("button", { name: "保存特征方案", exact: true }).click(); await expect( page.getByText("特征方案版本已保存", { exact: true }), ).toBeVisible(); await page.getByRole("button", { name: "保存并转为模板" }).click(); await expect(page).toHaveURL(/#templates$/); await expect( page.getByRole("textbox", { name: "模板名称", exact: true }), ).toHaveValue("特征转换模板"); const templates = await ( await page.request.get( "/api/v1/research/assets?kind=template&q=特征转换模板", ) ).json(); expect(templates.items[0].provenance.feature.content.input_ids).toEqual([ fixed.id, ]); await page.keyboard.press("Escape"); await page.getByRole("button", { name: "Alpha 管理", exact: true }).click(); await page.getByRole("button", { name: "保存为新视图" }).click(); await page.getByLabel("视图名称", { exact: true }).fill("浏览器常用队列"); await page.getByRole("button", { name: "保存视图", exact: true }).click(); await expect(page.getByText("筛选视图已保存", { exact: true })).toBeVisible(); const views = await ( await page.request.get("/api/v1/research/assets?kind=view&q=浏览器常用队列") ).json(); expect(views.items[0].content.filters.submission).toBe("UNSUBMITTED"); expect(views.items[0].content.columns).toContain("name"); await page .getByRole("button", { name: "导入 Alpha ID", exact: true }) .click(); await page.getByLabel("Alpha IDs", { exact: true }).fill("TEST0001"); const importing = page.waitForResponse( (r) => r.url().endsWith("/api/v1/sync-jobs") && r.request().method() === "POST", ); await page .getByRole("dialog") .getByRole("button", { name: "confirm", exact: true }) .click(); await expect .poll(async () => (await page.request.get("/api/v1/alphas/TEST0001")).status(), ) .toBe(200); const importJob = await (await importing).json(); await expect .poll( async () => ( await ( await page.request.get(`/api/v1/sync-jobs/${importJob.id}`) ).json() ).status, ) .toBe("completed"); await page.reload(); await page.getByRole("button", { name: "筛选 Alpha", exact: true }).click(); await page.getByRole("textbox", { name: "搜索 Alpha" }).fill("TEST0001"); await page.getByRole("button", { name: "查询", exact: true }).click(); await expect(page.locator(".alpha-link")).toHaveCount(1); await page.locator(".alpha-link").click(); await page.getByRole("tab", { name: "研究信息", exact: true }).click(); await page.getByRole("button", { name: "保存规则评估" }).click(); await expect( page.getByText("规则:Sharpe ≥", { exact: false }), ).toBeVisible(); const reports = await ( await page.request.get("/api/v1/research/evaluations?alpha_id=TEST0001") ).json(); expect(reports.items).toHaveLength(1); await page.screenshot({ path: "/tmp/wq-stage-two-evaluation.png", fullPage: true, }); expect(errors).toEqual([]); });