Submatter/apps/web/components/chat/chat-utils.test.ts

102 lines
3.2 KiB
TypeScript
Raw Permalink Normal View History

import { describe, expect, it } from "vitest";
import {
advanceCatchUpWindow,
chatBadge,
mergeChatMessages,
messageError,
rootReadIsProven,
} from "./chat-utils";
describe("chat client state", () => {
it("retains through across pages, then starts a new interval after exhaustion", () => {
const first = advanceCatchUpWindow(
{ after: "5" },
{ next: "8", through: "10", hasMore: true },
);
expect(first).toEqual({ after: "8", through: "10" });
const second = advanceCatchUpWindow(first, {
next: "9",
through: "10",
hasMore: true,
});
expect(second).toEqual({ after: "9", through: "10" });
expect(
advanceCatchUpWindow(second, {
next: "9",
through: "10",
hasMore: false,
}),
).toEqual({ after: "10" });
});
it("does not lose the cursor on a stalled page or fold concurrent sends into the old interval", () => {
const window = { after: "8", through: "10" };
expect(
advanceCatchUpWindow(window, { next: "8", through: "10", hasMore: true }),
).toBe(window);
expect(
advanceCatchUpWindow(window, {
next: "10",
through: "10",
hasMore: false,
}),
).toEqual({ after: "10" });
expect(
advanceCatchUpWindow(
{ after: "10" },
{ next: "11", through: "12", hasMore: true },
),
).toEqual({ after: "11", through: "12" });
});
it("never replaces a newer edit or tombstone with an older page", () => {
const original = {
id: "m",
sequence: "1",
revision: 1,
body: "old",
status: "active",
};
const edited = { ...original, revision: 2, body: "new" };
const deleted = { ...edited, revision: 3, body: "", status: "deleted" };
expect(mergeChatMessages([edited], [original])).toEqual([edited]);
expect(mergeChatMessages([deleted], [edited])).toEqual([deleted]);
});
it("requires a channel-wide scan and does not claim unseen replies as read", () => {
expect(rootReadIsProven("20", null, null, "0")).toBe(false);
expect(rootReadIsProven("20", "19", null, "0")).toBe(false);
expect(rootReadIsProven("20", "25", "18", "0")).toBe(false);
expect(rootReadIsProven("20", "25", "22", "0")).toBe(true);
});
it("deduplicates history and catch-up in sequence order", () => {
expect(
mergeChatMessages(
[
{ id: "b", sequence: "9" },
{ id: "a", sequence: "2" },
],
[
{ id: "b", sequence: "9" },
{ id: "c", sequence: "10" },
],
).map((m) => m.id),
).toEqual(["a", "b", "c"]);
});
it("distinguishes unread dots from numeric direct and mention activity", () => {
expect(
chatBadge({ unreadCount: 3, mentionCount: 0, directUnreadCount: 0 }),
).toEqual({ dot: true, count: 0 });
expect(
chatBadge({ unreadCount: 3, mentionCount: 2, directUnreadCount: 1 }),
).toEqual({ dot: true, count: 3 });
});
it("maps failed sends to actionable messages", () => {
expect(messageError("NOT_FOUND")).toMatch(/access/i);
expect(messageError("CONFLICT")).toMatch(/different message/i);
expect(messageError("INTERNAL_SERVER_ERROR")).toMatch(/retry/i);
});
});