rebuild(stage-10): deliver mentor and unified llm streaming
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const { expect, test } = require("@playwright/test");
|
||||
|
||||
const evidence = path.resolve(__dirname, "../../docs/evidence/stage-10");
|
||||
test.beforeAll(() => fs.mkdirSync(evidence, { recursive: true }));
|
||||
|
||||
async function authenticate(page, username, password) {
|
||||
await page.goto("/");
|
||||
await page.getByLabel("账号名").fill(username);
|
||||
await page.getByLabel("密码").fill(password);
|
||||
await page.getByRole("button", { name: "登录", exact: true }).click();
|
||||
await expect(page.locator(".sidebar, .field-error")).toBeVisible();
|
||||
if (!(await page.locator(".sidebar").isVisible())) {
|
||||
await page.getByRole("tab", { name: "注册" }).click();
|
||||
await page.getByRole("button", { name: "注册并登录" }).click();
|
||||
}
|
||||
}
|
||||
|
||||
const mentors = [
|
||||
{
|
||||
id: "emotion-mentor",
|
||||
name: "情绪模型",
|
||||
description: "情绪周期与短线节奏",
|
||||
tagline: "先看环境,再谈机会。",
|
||||
focus: ["情绪周期", "风险控制"],
|
||||
grade: "A",
|
||||
evidence_label: "公开资料",
|
||||
evidence_note: "已核验",
|
||||
private: false,
|
||||
pinned: false,
|
||||
sort_order: 0,
|
||||
},
|
||||
{
|
||||
id: "macro-mentor",
|
||||
name: "宏观模型",
|
||||
description: "指数、ETF与资金方向",
|
||||
tagline: "把复杂行情回归常识。",
|
||||
focus: ["宏观", "指数"],
|
||||
grade: "B",
|
||||
evidence_label: "公开资料",
|
||||
evidence_note: "已核验",
|
||||
private: false,
|
||||
pinned: false,
|
||||
sort_order: 1,
|
||||
},
|
||||
];
|
||||
|
||||
async function mockMentor(page) {
|
||||
await page.route("**/api/mentors/setup?*", (route) => route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ trade_date: "2026-07-30", mentors }),
|
||||
}));
|
||||
await page.route("**/api/mentors/messages?*", (route) => route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(route.request().method() === "DELETE" ? { deleted: 2 } : []),
|
||||
}));
|
||||
await page.route("**/api/mentors/preferences", (route) => route.fulfill({
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ order: mentors.map((item) => item.id), pinned: [] }),
|
||||
}));
|
||||
await page.route("**/api/mentors/chat", (route) => route.fulfill({
|
||||
contentType: "application/x-ndjson",
|
||||
body: [
|
||||
JSON.stringify({ type: "delta", content: "第一段", request_id: "request-1" }),
|
||||
JSON.stringify({ type: "delta", content: "第二段", request_id: "request-1" }),
|
||||
JSON.stringify({ type: "done", request_id: "request-1" }),
|
||||
"",
|
||||
].join("\n"),
|
||||
}));
|
||||
}
|
||||
|
||||
test("stage 10 mentor library, streaming answer and responsive workspace", async ({ page }) => {
|
||||
const consoleErrors = [];
|
||||
page.on("console", (message) => {
|
||||
if (message.type() === "error" && !message.text().includes("401 (Unauthorized)")) {
|
||||
consoleErrors.push(message.text());
|
||||
}
|
||||
});
|
||||
await mockMentor(page);
|
||||
await authenticate(page, "stage4admin", "Stage4-pass-123!");
|
||||
await page.goto("/workspace/mentor");
|
||||
await page.getByRole("button", { name: "夜间" }).click();
|
||||
|
||||
await expect(page.getByRole("heading", { name: "思维模型" })).toBeVisible();
|
||||
await expect(page.getByText("2 位", { exact: true })).toBeVisible();
|
||||
await page.getByLabel("搜索思维模型").fill("宏观");
|
||||
await expect(page.getByText("1 位", { exact: true })).toBeVisible();
|
||||
await expect(page.locator(".mentor-list").getByText("情绪模型", { exact: true })).toHaveCount(0);
|
||||
await page.getByLabel("搜索思维模型").fill("");
|
||||
await page.getByRole("button", { name: "A级" }).click();
|
||||
await expect(page.locator(".mentor-list").getByText("情绪模型", { exact: true })).toBeVisible();
|
||||
await expect(page.locator(".mentor-list").getByText("宏观模型", { exact: true })).toHaveCount(0);
|
||||
await page.getByRole("button", { name: "全部" }).click();
|
||||
const macro = page.locator(".mentor-item").filter({ hasText: "宏观模型" });
|
||||
await expect(macro).toBeVisible();
|
||||
await macro.getByRole("button", { name: "置顶" }).click();
|
||||
await expect(macro.getByRole("button", { name: "取消置顶" })).toBeVisible();
|
||||
|
||||
await page.getByPlaceholder("输入你的复盘问题").fill("请复盘今天的市场");
|
||||
await page.getByRole("button", { name: "发送", exact: true }).click();
|
||||
await expect(page.getByText("第一段第二段", { exact: true })).toBeVisible();
|
||||
await expect(page.getByText("第一段第二段第一段第二段", { exact: true })).toHaveCount(0);
|
||||
await expect(page.locator(".mentor-composer")).toBeInViewport();
|
||||
await page.screenshot({ path: path.join(evidence, "mentor-dark-1920x1080.jpg"), type: "jpeg", quality: 82 });
|
||||
|
||||
await page.locator(".mentor-chat-header").getByRole("button", { name: "清空" }).click();
|
||||
await expect(page.getByRole("dialog", { name: "清空当前对话" })).toBeVisible();
|
||||
await page.locator(".dialog .btn-primary").click();
|
||||
await expect(page.getByText("先看环境,再谈机会。")).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "日间" }).click();
|
||||
await page.setViewportSize({ width: 3840, height: 2160 });
|
||||
await expect(page.locator(".mentor-composer")).toBeInViewport();
|
||||
await page.screenshot({ path: path.join(evidence, "mentor-light-3840x2160.jpg"), type: "jpeg", quality: 82 });
|
||||
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await expect(page.getByRole("button", { name: "模型库" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "模型库" }).click();
|
||||
await expect(page.locator(".mentor-library")).toBeVisible();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(0);
|
||||
await page.screenshot({ path: path.join(evidence, "mentor-light-390x844.jpg"), type: "jpeg", quality: 82 });
|
||||
expect(consoleErrors).toEqual([]);
|
||||
});
|
||||
|
||||
test("nonmembers retain the complete grey mentor structure", async ({ page }) => {
|
||||
await mockMentor(page);
|
||||
await authenticate(page, "stage4user", "Stage4-user-123!");
|
||||
await page.goto("/workspace/mentor");
|
||||
|
||||
await expect(page.getByText("问师仅对会员开放")).toBeVisible();
|
||||
await expect(page.getByRole("heading", { name: "思维模型" })).toBeVisible();
|
||||
await expect(page.locator(".mentor-workspace")).toHaveClass(/locked-content/);
|
||||
await expect(page.getByPlaceholder("输入你的复盘问题")).toBeDisabled();
|
||||
});
|
||||
Reference in New Issue
Block a user