111 lines
3.2 KiB
TypeScript
111 lines
3.2 KiB
TypeScript
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
||
|
|
import { describe, expect, it, vi } from "vitest";
|
||
|
|
|
||
|
|
import { ChatMessageList } from "./chat-message-list";
|
||
|
|
|
||
|
|
const message = {
|
||
|
|
id: "server",
|
||
|
|
sequence: "2",
|
||
|
|
body: "Confirmed",
|
||
|
|
authorUserId: "user",
|
||
|
|
createdAt: "2026-01-01",
|
||
|
|
};
|
||
|
|
|
||
|
|
describe("rendered message states", () => {
|
||
|
|
it("shows a visible sending state and a retry only on failure", () => {
|
||
|
|
const pending = {
|
||
|
|
key: "same-key",
|
||
|
|
body: "Hello",
|
||
|
|
failed: false,
|
||
|
|
retry: vi.fn(),
|
||
|
|
};
|
||
|
|
const sending = renderToStaticMarkup(
|
||
|
|
<ChatMessageList messages={[]} pending={[pending]} />,
|
||
|
|
);
|
||
|
|
expect(sending).toContain("Sending…");
|
||
|
|
expect(sending).not.toContain("Retry");
|
||
|
|
const failed = renderToStaticMarkup(
|
||
|
|
<ChatMessageList
|
||
|
|
messages={[]}
|
||
|
|
pending={[
|
||
|
|
{
|
||
|
|
...pending,
|
||
|
|
failed: true,
|
||
|
|
error: "Could not send message. Please retry.",
|
||
|
|
},
|
||
|
|
]}
|
||
|
|
/>,
|
||
|
|
);
|
||
|
|
expect(failed).toContain('role="alert"');
|
||
|
|
expect(failed).toContain("Retry");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("renders only the messages supplied by the parent", () => {
|
||
|
|
const html = renderToStaticMarkup(<ChatMessageList messages={[message]} />);
|
||
|
|
expect(html).toContain("Confirmed");
|
||
|
|
expect(html).not.toContain("Sending…");
|
||
|
|
expect(html).not.toContain("Retry");
|
||
|
|
|
||
|
|
const empty = renderToStaticMarkup(<ChatMessageList messages={[]} />);
|
||
|
|
expect(empty).not.toContain("Confirmed");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("shows an edited indicator for edited messages", () => {
|
||
|
|
const html = renderToStaticMarkup(
|
||
|
|
<ChatMessageList messages={[{ ...message, editedAt: "2026-01-02" }]} />,
|
||
|
|
);
|
||
|
|
expect(html).toContain("(edited)");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("only offers owner edit, owner or moderator delete, and never archived or deleted controls", () => {
|
||
|
|
const own = renderToStaticMarkup(
|
||
|
|
<ChatMessageList
|
||
|
|
messages={[message]}
|
||
|
|
currentUserId="user"
|
||
|
|
onEdit={vi.fn()}
|
||
|
|
onDelete={vi.fn()}
|
||
|
|
/>,
|
||
|
|
);
|
||
|
|
expect(own).toContain("Edit message server");
|
||
|
|
expect(own).toContain("Delete message server");
|
||
|
|
const moderator = renderToStaticMarkup(
|
||
|
|
<ChatMessageList
|
||
|
|
messages={[message]}
|
||
|
|
currentUserId="moderator"
|
||
|
|
canModerate
|
||
|
|
onEdit={vi.fn()}
|
||
|
|
onDelete={vi.fn()}
|
||
|
|
/>,
|
||
|
|
);
|
||
|
|
expect(moderator).not.toContain("Edit message server");
|
||
|
|
expect(moderator).toContain("Delete message server");
|
||
|
|
const archived = renderToStaticMarkup(
|
||
|
|
<ChatMessageList
|
||
|
|
messages={[message]}
|
||
|
|
currentUserId="user"
|
||
|
|
readOnly
|
||
|
|
onEdit={vi.fn()}
|
||
|
|
onDelete={vi.fn()}
|
||
|
|
/>,
|
||
|
|
);
|
||
|
|
expect(archived).not.toContain("Edit message server");
|
||
|
|
const deleted = renderToStaticMarkup(
|
||
|
|
<ChatMessageList
|
||
|
|
messages={[{ ...message, status: "deleted" }]}
|
||
|
|
currentUserId="user"
|
||
|
|
onEdit={vi.fn()}
|
||
|
|
onDelete={vi.fn()}
|
||
|
|
/>,
|
||
|
|
);
|
||
|
|
expect(deleted).not.toContain("Delete message server");
|
||
|
|
});
|
||
|
|
|
||
|
|
it("renders deleted messages as a placeholder instead of their body", () => {
|
||
|
|
const html = renderToStaticMarkup(
|
||
|
|
<ChatMessageList messages={[{ ...message, deletedAt: "2026-01-02" }]} />,
|
||
|
|
);
|
||
|
|
expect(html).toContain("Message deleted");
|
||
|
|
expect(html).not.toContain("Confirmed");
|
||
|
|
});
|
||
|
|
});
|