feat(frontend): slice 6b — Users compose dialog + 9 icons (finish Users)
Web UI rework. Completes the Users migration (6a directory + 6b compose): - Compose dialog off @mui: shadcn Dialog family + Input (subject) + Textarea (html body) + Separator + Label; IconButton -> Button ghost - 9 @mui/icons-material -> lucide-react: Close->X, AttachFile->Paperclip, FormatBold->Bold, FormatItalic->Italic, Link->Link, FormatListBulleted->List, MailOutlined->Mail, Send->Send, DeleteOutlined->Trash2 - Rich-text compose parity: markup insertion, attachments (FormData), queue-status polling, send via useSendUserMessage - UsersPage.impl.tsx now has ZERO @mui imports Gate: build + lint + test green (20 files / 46 tests).
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { UsersPage } from "../UsersPage.impl";
|
||||
import { TooltipProvider } from "../../components/ui/tooltip";
|
||||
import type {
|
||||
NowPlayingSession,
|
||||
UserDirectoryItem,
|
||||
@@ -23,6 +24,14 @@ beforeEach(() => {
|
||||
dispatchEvent: () => false,
|
||||
})) as unknown as typeof window.matchMedia;
|
||||
}
|
||||
// The compose formatting actions defer a focus/selection restore via
|
||||
// requestAnimationFrame (see insertMarkup). jsdom may not flush rAF
|
||||
// synchronously, so make it synchronous so the slice-6b compose test can
|
||||
// observe the html-body value update.
|
||||
window.requestAnimationFrame = ((cb: FrameRequestCallback) => {
|
||||
cb(0);
|
||||
return 0;
|
||||
}) as typeof window.requestAnimationFrame;
|
||||
});
|
||||
|
||||
// Keep the drawer's nested session panel out of the DOM under test.
|
||||
@@ -239,3 +248,38 @@ describe("UsersPage (slice 6a — directory surface + drawer)", () => {
|
||||
expect(screen.getByTestId("session-panel-stub")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("UsersPage (slice 6b — compose dialog formatting actions)", () => {
|
||||
it("opens compose and inserts bold markup into the html body", async () => {
|
||||
users = [userFixture({ jellyfin_id: "u1", display_name: "Alice" })];
|
||||
// The formatting toolbar renders <Tooltip> (shadcn), which in the app is
|
||||
// wrapped by a global <TooltipProvider> in App.tsx; supply it here.
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<UsersPage />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
// Select a deliverable user so the "Message selected" button enables.
|
||||
await userEvent.click(
|
||||
screen.getByRole("checkbox", { name: "Select Alice" }),
|
||||
);
|
||||
await userEvent.click(
|
||||
screen.getByRole("button", { name: "Message selected" }),
|
||||
);
|
||||
|
||||
// Compose dialog opens (shadcn Dialog family).
|
||||
expect(
|
||||
screen.getByRole("heading", { name: "Message selected users" }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Bold action wraps the cursor selection in <strong></strong> via the
|
||||
// preserved insertMarkup helper (markup insertion actions parity).
|
||||
await userEvent.click(screen.getByRole("button", { name: "Bold" }));
|
||||
|
||||
const body = screen.getByRole("textbox", {
|
||||
name: "HTML message body",
|
||||
}) as HTMLTextAreaElement;
|
||||
expect(body.value).toContain("<strong>");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user