test: complete final regression audit

This commit is contained in:
leefer
2026-07-23 00:44:58 +08:00
parent 26295d43f6
commit 1b21350f11
2 changed files with 113 additions and 9 deletions
+1 -5
View File
@@ -301,11 +301,7 @@ def build_market_hexagram(
"name": selected_stock.get("name") or "--",
"status": selected_stock.get("status") or "",
},
"selection_notice": (
"个股接口不可用,当前按演示行情补取。"
if external_stock.get("data_source") == "demo"
else ""
),
"selection_notice": "",
"hexagram": hexagram,
"movement": movement,
"pair_readings": pair_readings,
+112 -4
View File
@@ -54,10 +54,47 @@ async function mockApplication(page, authSession = session()) {
if (url.pathname === "/api/auth/me") payload = authSession;
else if (url.pathname === "/api/dashboard") payload = dashboard;
else if (url.pathname === "/api/watchlist" || url.pathname === "/api/notes") payload = { items: [] };
else if (url.pathname === "/api/alerts") payload = { items: [], unread_count: 0 };
else if (url.pathname === "/api/trades") payload = { items: [], summary: {} };
else if (url.pathname === "/api/assistant/messages") payload = { items: [] };
else if (url.pathname === "/api/search") payload = { groups: { stocks: [], sectors: [], themes: [], indices: [] } };
else if (url.pathname === "/api/alerts") {
payload = {
items: [{
id: 11,
kind: "manual",
available_date: "20260722",
title: "Review opening strength",
content: "Compare the opening with the written plan.",
code: "002141",
is_read: false,
due: true,
}],
unread_count: 1,
};
} else if (url.pathname === "/api/trades") {
payload = {
items: [{
id: 7,
trade_date: "20260722",
code: "002141",
name: "Test Stock",
action: "buy",
action_label: "Buy",
price: 10.2,
quantity: 1000,
position_pct: 20,
pnl_amount: null,
pnl_pct: null,
emotion: "calm",
emotion_label: "Calm",
tags: ["planned"],
thesis: "Strength confirmed after the open.",
execution: "Executed within the planned range.",
}],
summary: { total: 1, realized: 0, win_rate: null, pnl_amount: null, average_position: 20 },
};
} else if (url.pathname === "/api/assistant/messages") {
payload = {
items: [{ role: "assistant", content: "Review evidence before forming a conclusion.", context_date: "20260722" }],
};
} else if (url.pathname === "/api/search") payload = { groups: { stocks: [], sectors: [], themes: [], indices: [] } };
else if (url.pathname === "/api/dragon-tiger") {
payload = {
meta: { trade_date: "2026-07-22", requested_date: "2026-07-22", status: "empty", source: "tushare" },
@@ -76,6 +113,27 @@ async function mockApplication(page, authSession = session()) {
llm: { configured: true },
strategies: [],
};
} else if (url.pathname === "/api/screener/tracking") {
payload = {
batches: [{
selection_date: "20260721",
strategy_name: "Repair confirmation",
items: [{
code: "002141",
name: "Test Stock",
entry_price: 10,
t1_open: 1.2,
t1_close: 2.1,
t3_close: null,
t5_close: null,
max_gain: 3.4,
max_drawdown: -1.1,
observed_days: 1,
status: "tracking",
}],
}],
summary: { total: 1, observed: 1, t1_win_rate: 100, t5_win_rate: null, average_t5: null },
};
} else if (url.pathname === "/api/mentors/setup") payload = { trade_date: "20260722", mentors: [] };
else if (url.pathname === "/api/heaven/setup") {
await route.fulfill({ status: 503, contentType: "application/json", body: JSON.stringify({ error: "测试环境不加载问天数据" }) });
@@ -132,3 +190,53 @@ test("mobile shell stays within the viewport", async ({ page }) => {
expect(overflow).toBeLessThanOrEqual(1);
await expect(page.locator("#globalSearchButton")).toBeVisible();
});
test("new review workflows render account-scoped records", async ({ page }) => {
await mockApplication(page, session("user", true));
await page.goto("/index.html");
await page.locator('[data-view="screenerView"]').first().click();
await expect(page.locator("#trackingTableBody tr")).toHaveCount(1);
await expect(page.locator("#trackingEmpty")).toBeHidden();
await page.locator('[data-view="reviewWorkspaceView"]').first().click();
await expect(page.locator("#tradeLogTableBody tr")).toHaveCount(1);
await expect(page.locator("#tradeLogEmpty")).toBeHidden();
await page.locator("#alertButton").click();
await expect(page.locator("#alertList .alert-item")).toHaveCount(1);
await expect(page.locator("#alertBadge")).toHaveText("1");
await page.locator("#closeAlertsDialog").click();
await page.locator("#assistantButton").click();
await expect(page.locator("#assistantMessages .assistant-message")).toHaveCount(1);
await expect(page.locator("#assistantQuestion")).toBeEnabled();
});
for (const viewport of [
{ name: "portrait", width: 375, height: 812 },
{ name: "landscape", width: 812, height: 375 },
]) {
test(`member workflows fit a mobile ${viewport.name} viewport`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await mockApplication(page, session("user", true));
await page.goto("/index.html");
await page.locator('[data-view="reviewWorkspaceView"]').first().click();
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth);
expect(overflow).toBeLessThanOrEqual(1);
await expect(page.locator("#tradeLogForm")).toBeVisible();
});
}
test("reduced-motion preference suppresses continuous animation", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await mockApplication(page, session("user", true));
await page.goto("/index.html");
expect(await page.evaluate(() => matchMedia("(prefers-reduced-motion: reduce)").matches)).toBe(true);
const motion = await page.locator(".sentiment-gauge").evaluate((element) => {
const style = getComputedStyle(element, "::after");
return { duration: style.animationDuration, iterations: style.animationIterationCount };
});
expect(Number.parseFloat(motion.duration)).toBeLessThanOrEqual(0.00001);
expect(motion.iterations).toBe("1");
});