Files
worldquant-alpha-system/frontend/tests/alpha-management.spec.ts
T
2026-09-10 11:45:05 +08:00

235 lines
8.6 KiB
TypeScript

import { expect, test } from "@playwright/test";
test("submission tabs, day selection, local correlation and reload", async ({
page,
}) => {
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/#alphas");
await page.getByLabel("密码", { exact: true }).fill("browser-test-password");
await page.getByRole("button", { name: "进入工作空间" }).click();
await expect(
page.getByRole("tab", { name: "待提交", exact: true }),
).toBeVisible();
const headers = { "X-WQ-Request": "1" };
const credentials = await page.request.put("/api/v1/account/credentials", {
headers,
data: { email: "test@example.com", password: "synthetic-password" },
});
expect(credentials.ok()).toBe(true);
expect(
(await page.request.post("/api/v1/account/connect", { headers })).ok(),
).toBe(true);
await expect
.poll(
async () =>
(await (await page.request.get("/api/v1/account")).json())
.connection_status,
)
.toBe("connected");
await page.reload();
await page.getByRole("button", { name: "Alpha 管理", exact: true }).click();
await page.getByRole("tab", { name: "已提交", exact: true }).click();
await page.getByRole("button", { name: "按天同步", exact: true }).click();
await expect(page.getByRole("dialog")).toContainText("提交日期");
await page.getByRole("button", { name: "开始同步", exact: true }).click();
await expect(page.getByRole("alert")).toContainText("请选择有效的起止日期");
// Separate validation from submission; Semi ignores repeated OK clicks within 100 ms.
await page
.getByRole("dialog")
.getByRole("button", { name: "cancel", exact: true })
.click();
await page.getByRole("button", { name: "按天同步", exact: true }).click();
await page.getByLabel("同步开始日期").fill("2025-02-02");
await page.getByLabel("同步结束日期").fill("2025-02-02");
const request = page.waitForResponse(
(response) =>
response.url().endsWith("/api/v1/sync-jobs") &&
response.request().method() === "POST",
);
await page.getByRole("button", { name: "开始同步", exact: true }).click();
const job = await (await request).json();
expect(job.payload).toMatchObject({
submission: "SUBMITTED",
date_from: "2025-02-02",
date_to: "2025-02-02",
});
await expect
.poll(
async () =>
(await (await page.request.get(`/api/v1/sync-jobs/${job.id}`)).json())
.status,
)
.toBe("completed");
const complete = await (
await page.request.get(`/api/v1/sync-jobs/${job.id}`)
).json();
expect(complete.processed).toBe(103);
await expect(page.locator(".job-panel")).toContainText("2025-02-02");
await page.keyboard.press("Escape");
await page.getByRole("textbox", { name: "搜索 Alpha" }).fill("TEST0003");
await page.getByRole("button", { name: "查询", exact: true }).click();
await expect(page.locator(".alpha-link")).toHaveCount(1);
await page.getByRole("tab", { name: "待提交", exact: true }).click();
await expect(page.getByRole("textbox", { name: "搜索 Alpha" })).toHaveValue(
"",
);
await page.getByRole("button", { name: "按天同步", exact: true }).click();
await expect(page.getByRole("dialog")).toContainText("创建日期");
await page.getByLabel("同步开始日期").fill("2025-01-01");
await page.getByLabel("同步结束日期").fill("2025-01-01");
await page.getByRole("button", { name: "开始同步", exact: true }).click();
await expect
.poll(
async () =>
(
await (
await page.request.get("/api/v1/alphas?submission=UNSUBMITTED")
).json()
).total,
)
.toBe(413);
await page.keyboard.press("Escape");
await page.getByRole("textbox", { name: "搜索 Alpha" }).fill("TEST0004");
await page.getByRole("button", { name: "查询", exact: true }).click();
await page.route("**/api/v1/alphas/TEST0004", async (route) => {
const response = await route.fetch();
const detail = await response.json();
detail.is_metrics.nested = {
yearly: { sharpe: 1.25, status: "PASS" },
samples: 120,
enabled: false,
};
await route.fulfill({ response, json: detail });
});
await page.locator(".alpha-link").click();
const sheet = page.locator(".alpha-detail");
await expect(sheet.getByRole("tab")).toHaveText([
"概览与表达式",
"指标",
"相关性检查",
"基线比较",
"变体关系",
"研究信息",
]);
const width = await sheet
.locator(".semi-sidesheet-inner")
.evaluate((element) => element.getBoundingClientRect().width);
expect(width).toBeCloseTo(1440 * 0.8, 0);
await expect(
sheet.getByRole("button", { name: "研究此 Alpha 的变体" }),
).not.toBeVisible();
await sheet.getByRole("tab", { name: "指标", exact: true }).click();
const metrics = sheet.locator(".detail-section").filter({
has: page.getByRole("heading", { name: "IS 指标", exact: true }),
});
await expect(metrics.locator(".nested-descriptions").first()).toContainText(
"1.25",
);
await expect(
metrics
.locator(".semi-descriptions-horizontal > table > tbody > tr")
.first()
.locator(":scope > th"),
).toHaveCount(3);
await expect(
metrics.locator(".semi-tag").filter({ hasText: "PASS" }),
).toBeVisible();
await expect(
metrics.locator(".semi-tag").filter({ hasText: "否" }),
).toBeVisible();
expect(
await metrics
.locator("th, td")
.evaluateAll((cells) =>
cells.every((cell) => getComputedStyle(cell).textAlign === "left"),
),
).toBe(true);
await page.screenshot({
path: "../output/playwright/alpha-detail-metrics.png",
});
await sheet.getByRole("tab", { name: "变体关系", exact: true }).click();
await expect(
sheet.getByRole("button", { name: "研究此 Alpha 的变体" }),
).toBeVisible();
await page.getByRole("tab", { name: "相关性检查", exact: true }).click();
await page.getByRole("button", { name: "检测自相关", exact: true }).click();
await expect
.poll(
async () =>
(
await (
await page.request.get("/api/v1/alphas/TEST0004/self-correlation")
).json()
).result?.status,
{ timeout: 20000 },
)
.toBe("high");
await page.keyboard.press("Escape");
await expect(page.locator(".job-panel")).not.toBeVisible();
await expect(page.locator(".correlation-panel")).toContainText("相关性偏高");
await expect(page.locator(".correlation-panel")).toContainText("1.0000");
const header = await sheet.locator(".detail-title").boundingBox();
const tabs = await sheet
.getByRole("tab", { name: "相关性检查", exact: true })
.boundingBox();
const content = sheet.locator(".alpha-detail-tabs > .semi-tabs-content");
await content.evaluate((element) => {
element.scrollTop = element.scrollHeight;
});
expect(
await content.evaluate((element) => element.scrollTop),
).toBeGreaterThan(0);
expect((await sheet.locator(".detail-title").boundingBox())!.y).toBe(
header!.y,
);
expect(
(await sheet
.getByRole("tab", { name: "相关性检查", exact: true })
.boundingBox())!.y,
).toBe(tabs!.y);
await expect(
sheet.getByRole("textbox", { name: "regular Description", exact: true }),
).toBeVisible();
await page.screenshot({
path: "../output/playwright/alpha-detail-scrolled.png",
});
await content.evaluate((element) => {
element.scrollTop = 0;
});
await page.screenshot({
path: "../output/playwright/local-correlation.png",
animations: "disabled",
});
await page.keyboard.press("Escape");
await page.reload();
await page.getByRole("textbox", { name: "搜索 Alpha" }).fill("TEST0004");
await page.getByRole("button", { name: "查询", exact: true }).click();
await expect(page.locator(".alpha-table:visible")).toContainText(
"相关性偏高",
);
await page.getByRole("tab", { name: "已提交", exact: true }).click();
await expect(
page.getByRole("button", { name: "全量同步已提交" }),
).toBeVisible();
for (const width of [1440, 390]) {
await page.setViewportSize({ width, height: 900 });
await expect(
page.getByRole("tab", { name: "已提交", exact: true }),
).toBeVisible();
await expect(
page.getByRole("button", { name: "按天同步", exact: true }),
).toBeVisible();
expect(
await page.evaluate(
() => document.documentElement.scrollWidth <= window.innerWidth,
),
).toBe(true);
await page.screenshot({
path: `../output/playwright/alpha-tabs-${width}.png`,
animations: "disabled",
});
}
expect(errors).toEqual([]);
});