import { expect, test } from "bun:test";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { Transcript } from "./transcript";
import type { ChatDestination } from "../conversation-plan/source";
let destination: ChatDestination = {
itemId: "thread",
token: 1,
source: {
messageId: "saved",
author: { kind: "member", handle: "ana" },
role: "option",
quote: "A\u{1F9EA} pilot",
start: 0,
end: 9,
},
};
function markup(sourceDestination?: ChatDestination, text = "A\u{1F9EA} pilot") {
return renderToStaticMarkup(createElement(Transcript, {
active: true,
entries: [{ id: "saved", author: { kind: "member", handle: "ana" }, text, ts: 1 }],
handle: "ana",
onWithdraw: () => {},
queued: [],
sourceDestination,
}));
}
test("saved text and its rendered container retain exact UTF-16 source metadata", () => {
let result = markup();
expect(result).toContain('data-chat-raw="A\u{1F9EA} pilot"');
expect(result).toContain("data-chat-message-text");
expect(result).not.toContain("data-source-exact");
});
test("only the destination message is marked, without a repeated caption", () => {
let selected = markup(destination);
expect(selected).toContain("data-chat-source");
expect(selected).not.toContain("Source:");
let missing = markup({ ...destination, source: { ...destination.source, messageId: "other" } });
expect(missing).not.toContain("data-chat-source");
});
test("source metadata preserves current Markdown rendering instead of flattening text", () => {
let result = markup(destination, "**A\u{1F9EA} pilot**");
expect(result).toContain('data-chat-raw="**A\u{1F9EA} pilot**"');
expect(result).toContain("A\u{1F9EA} pilot");
});
function planner(entries: { id: string; text: string; to?: "planner" }[], queued = []) {
return renderToStaticMarkup(createElement(Transcript, {
active: true,
entries: entries.map(entry => ({
author: { kind: "member" as const, handle: "ana" },
ts: 1,
...entry,
})),
handle: "ana",
onWithdraw: () => {},
queued,
}));
}
test("a message sent to the Planner starts with an @chopin mention; room messages do not", () => {
let result = planner([{ id: "m", text: "tighten it", to: "planner" }]);
expect(result).toContain('@chopin');
expect(result).toContain('data-chat-raw="tighten it"');
expect(result).not.toContain("To Chopin");
expect(planner([{ id: "m", text: "tighten it" }])).not.toContain("@chopin");
});
test("every message in a run and every queued item carries the mention, once", () => {
let entry = (id: string) => ({ id, text: id, to: "planner" as const });
let result = planner(
[entry("a"), entry("b")],
[{ id: "q", handle: "ana", text: "queued" }] as never,
);
expect(result.split('chat-mention">@chopin').length - 1).toBe(3);
});
test("a typed leading @chopin is not doubled", () => {
let result = planner([{ id: "m", text: "@chopin yo", to: "planner" }]);
expect(result.split("@chopin").length - 1).toBe(2); // mention span + raw attribute
expect(result).toContain('data-chat-raw="@chopin yo"');
expect(result).toContain("yo");
});