7a88b74016
- Artifacts hub: upload a file from the computer; make a skill by uploading a Markdown file, files, a folder or a .zip/.skill, or by writing it in the dialog; add plugins by picking from the list a marketplace link answers (POST /api/artifacts/plugin/inspect); Settings → AI tools → Artifacts Hub. - Console tabs are combos of up to four split panes (ConsoleTabs, PaneGrid). - Accounts: an own avatar (PUT/DELETE /api/me/avatar, raster only, ≤ 1 MB, <data dir>/avatars/), shown with name and role at the sidebar's foot and on the workspace owner groups (GET /api/users/:id/avatar, owner_avatar_at). - Users page: per-user storage (GET /api/users/storage, measured in a worker) and account-pool spend/tokens per account (server/lib/pool-usage.js). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
/**
|
|
* @file Sidebar.account.test.tsx
|
|
* @description The sidebar's account row (server mode): the signed-in user's
|
|
* avatar — or initials without one — name and role, linking to Settings →
|
|
* Account, beside Sign out; local mode shows no row.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it, expect, vi } from "vitest";
|
|
import { render, screen, within } from "@testing-library/react";
|
|
import { MemoryRouter } from "react-router-dom";
|
|
|
|
const auth = vi.hoisted(() => ({
|
|
state: { mode: "local" } as Record<string, unknown>,
|
|
logout: vi.fn(),
|
|
updateUser: vi.fn(),
|
|
}));
|
|
vi.mock("../AuthGate", () => ({
|
|
useAuth: () => auth,
|
|
usePermissions: () => ({
|
|
isAdmin: auth.state.mode !== "server",
|
|
local: auth.state.mode !== "server",
|
|
seesAll: true,
|
|
canEditLane: () => true,
|
|
}),
|
|
}));
|
|
|
|
import { Sidebar } from "../Sidebar";
|
|
|
|
const renderSidebar = (collapsed = false) =>
|
|
render(
|
|
<MemoryRouter>
|
|
<Sidebar
|
|
wsConnected
|
|
collapsed={collapsed}
|
|
onToggle={() => {}}
|
|
connectionOpen={false}
|
|
onConnectionOpenChange={() => {}}
|
|
/>
|
|
</MemoryRouter>
|
|
);
|
|
|
|
describe("Sidebar account row", () => {
|
|
it("shows the avatar, name and role, linking to Settings → Account", () => {
|
|
auth.state = {
|
|
mode: "server",
|
|
user: { id: 2, username: "mia", role: "developer", displayName: "Mia Tran", avatarAt: "t1" },
|
|
};
|
|
renderSidebar();
|
|
const row = screen.getByTestId("sidebar-account");
|
|
expect(row.textContent).toContain("Mia Tran");
|
|
expect(row.textContent).toContain("Developer");
|
|
expect(within(row).getByTestId("user-avatar-img").getAttribute("src")).toBe(
|
|
"/api/me/avatar?v=t1"
|
|
);
|
|
expect(within(row).getAllByRole("link")[0]?.getAttribute("href")).toBe("/settings/account");
|
|
expect(within(row).getByRole("button", { name: "Sign out" })).toBeTruthy();
|
|
});
|
|
|
|
it("falls back to initials, and keeps only the avatar on the rail", () => {
|
|
auth.state = { mode: "server", user: { id: 3, username: "leo", role: "admin" } };
|
|
renderSidebar(true);
|
|
const row = screen.getByTestId("sidebar-account");
|
|
expect(within(row).getByTestId("user-avatar-initials").textContent).toBe("le");
|
|
expect(row.textContent).not.toContain("Admin");
|
|
});
|
|
|
|
it("has no account row in local mode", () => {
|
|
auth.state = { mode: "local" };
|
|
renderSidebar();
|
|
expect(screen.queryByTestId("sidebar-account")).toBeNull();
|
|
});
|
|
});
|