feat: show all accounts in collapsible overview
This commit is contained in:
@@ -68,6 +68,106 @@ async function openSettings(page: Page, version = "0.3.0-beta.1") {
|
||||
await expect(page.locator(".settings-loading")).toHaveCount(0);
|
||||
}
|
||||
|
||||
test("shows every account as a collapsible summary on the overview", async ({
|
||||
page,
|
||||
}) => {
|
||||
const requestedDashboards = new Set<number>();
|
||||
const dashboards: Record<number, unknown> = {
|
||||
1: {
|
||||
accountId: 1,
|
||||
displayName: "个人账号",
|
||||
account: {
|
||||
email: "personal@example.com",
|
||||
planType: "plus",
|
||||
connected: true,
|
||||
},
|
||||
limits: [
|
||||
{
|
||||
limitId: "primary",
|
||||
limitName: "5 小时限额",
|
||||
windowType: "five_hour",
|
||||
usedPercent: 35,
|
||||
windowDurationMinutes: 300,
|
||||
resetsAt: 1_900_000_000,
|
||||
},
|
||||
],
|
||||
summary: {
|
||||
lifetimeTokens: 12_345,
|
||||
peakDailyTokens: 3_000,
|
||||
currentStreakDays: 4,
|
||||
longestRunningTurnSec: 80,
|
||||
},
|
||||
usage: [{ date: "2026-08-13", totalTokens: 3_000 }],
|
||||
fetchedAt: 1_900_000_000,
|
||||
stale: false,
|
||||
},
|
||||
2: {
|
||||
accountId: 2,
|
||||
displayName: "Team 工作区",
|
||||
account: {
|
||||
email: "team@example.com",
|
||||
planType: "team",
|
||||
connected: true,
|
||||
},
|
||||
limits: [],
|
||||
summary: {},
|
||||
usage: [],
|
||||
fetchedAt: 0,
|
||||
stale: true,
|
||||
},
|
||||
};
|
||||
await page.route("**/api/v1/**", async (route) => {
|
||||
const request = route.request();
|
||||
const key = new URL(request.url()).pathname.replace("/api/v1/", "");
|
||||
if (key === "system/status")
|
||||
return route.fulfill({
|
||||
json: { initialized: true, appServer: true, version: "test" },
|
||||
});
|
||||
if (key === "auth/me")
|
||||
return route.fulfill({ json: { username: "admin" } });
|
||||
if (key === "accounts")
|
||||
return route.fulfill({
|
||||
json: [
|
||||
{ ...responses.accounts[0], displayName: "个人账号" },
|
||||
{
|
||||
...responses.accounts[0],
|
||||
id: 2,
|
||||
displayName: "Team 工作区",
|
||||
email: "team@example.com",
|
||||
planType: "team",
|
||||
expectedKind: "team",
|
||||
},
|
||||
],
|
||||
});
|
||||
if (key === "dashboard") {
|
||||
const id = Number(new URL(request.url()).searchParams.get("accountId"));
|
||||
requestedDashboards.add(id);
|
||||
return route.fulfill({ json: dashboards[id] });
|
||||
}
|
||||
return route.fulfill({ json: responses[key] ?? { ok: true } });
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await expect(page.locator(".account-overview")).toHaveCount(2);
|
||||
await expect.poll(() => requestedDashboards.size).toBe(2);
|
||||
await expect(page.getByText("最低 65% 剩余")).toBeVisible();
|
||||
await expect(page.getByText("限额暂无")).toBeVisible();
|
||||
await expect(page.locator(".account-detail")).toHaveCount(0);
|
||||
await expect(page.locator(".account-select")).toHaveCount(0);
|
||||
|
||||
const first = page.locator('[data-account-id="1"]');
|
||||
await first.getByRole("button", { name: "展开个人账号详情" }).click();
|
||||
await expect(first.locator(".account-detail")).toBeVisible();
|
||||
await expect(first.getByRole("heading", { name: "个人账号" })).toBeVisible();
|
||||
await expect(
|
||||
first.getByRole("heading", { name: "每日 Token 趋势" }),
|
||||
).toBeVisible();
|
||||
await expect(page.locator(".account-detail")).toHaveCount(1);
|
||||
|
||||
await first.getByRole("button", { name: "收起个人账号详情" }).click();
|
||||
await expect(page.locator(".account-detail")).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("shows the build version in the authenticated brand", async ({ page }) => {
|
||||
await openSettings(page);
|
||||
const mobile = (page.viewportSize()?.width ?? 0) <= 800;
|
||||
@@ -233,6 +333,10 @@ test("keeps daily Token axis labels visible on narrow screens", async ({
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await page
|
||||
.locator('[data-account-id="1"]')
|
||||
.getByRole("button", { name: "展开默认账号详情" })
|
||||
.click();
|
||||
const chart = page.locator(".chart");
|
||||
await expect(
|
||||
chart.getByRole("heading", { name: "每日 Token 趋势" }),
|
||||
@@ -505,13 +609,17 @@ test("Codex account emails are masked everywhere they are displayed", async ({
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await expect(page.locator(".account-select")).toContainText(
|
||||
const account = page.locator('[data-account-id="1"]');
|
||||
await expect(account.locator(".account-summary-identity")).toContainText(
|
||||
"t**t@e*****e.com",
|
||||
);
|
||||
await account.getByRole("button", { name: "展开默认账号详情" }).click();
|
||||
await expect(account.locator(".account-info-item").first()).toContainText(
|
||||
"t**t@e*****e.com",
|
||||
);
|
||||
await expect(page.locator(".account b")).toContainText("t**t@e*****e.com");
|
||||
await expect(page.getByText("已使用 25%", { exact: true })).toHaveCount(0);
|
||||
await expect(page.getByText("剩余 75%", { exact: true })).toHaveCount(0);
|
||||
await expect(page.locator(".bar")).toHaveAttribute(
|
||||
await expect(account.locator(".bar")).toHaveAttribute(
|
||||
"aria-label",
|
||||
"5h:已使用 25%,剩余 75%",
|
||||
);
|
||||
@@ -548,12 +656,12 @@ test("returns to login when an authenticated request receives 401", async ({
|
||||
return route.fulfill({ json: responses[key] ?? {} });
|
||||
});
|
||||
await page.goto("/");
|
||||
await expect(page.locator(".account-select")).toBeVisible();
|
||||
await page.getByRole("button", { name: "立即刷新" }).click();
|
||||
await expect(page.locator(".account-overview")).toBeVisible();
|
||||
await page.getByRole("button", { name: "刷新全部" }).click();
|
||||
await expect(page.getByRole("heading", { name: "欢迎回来" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("a slow previous account response cannot replace the selected account", async ({
|
||||
test("keeps each account dashboard in its own card when responses finish out of order", async ({
|
||||
page,
|
||||
}) => {
|
||||
const accounts = [
|
||||
@@ -593,10 +701,15 @@ test("a slow previous account response cannot replace the selected account", asy
|
||||
return route.fulfill({ json: responses[key] ?? {} });
|
||||
});
|
||||
await page.goto("/");
|
||||
const selector = page.locator(".account-select");
|
||||
await selector.selectOption("2");
|
||||
await expect(page.locator(".account b")).toContainText("第二账号");
|
||||
const second = page.locator('[data-account-id="2"]');
|
||||
await expect(second.locator(".account-summary-identity")).toContainText(
|
||||
"第二账号",
|
||||
);
|
||||
await page.waitForTimeout(350);
|
||||
await expect(page.locator(".account b")).toContainText("第二账号");
|
||||
await expect(page.locator(".account b")).not.toContainText("默认账号");
|
||||
await expect(second.locator(".account-summary-identity")).toContainText(
|
||||
"第二账号",
|
||||
);
|
||||
await expect(
|
||||
page.locator('[data-account-id="1"] .account-summary-identity'),
|
||||
).toContainText("默认账号");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user