79 lines
3.5 KiB
TypeScript
79 lines
3.5 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { joinAssistantText } from "../src/ui-model"
|
|
|
|
describe("joinAssistantText", () => {
|
|
const cases: Array<[name: string, left: string, right: string, expected: string]> = [
|
|
[
|
|
"inserts a space between sentences",
|
|
"Okay, I'll check that.",
|
|
"The build passed.",
|
|
"Okay, I'll check that. The build passed.",
|
|
],
|
|
["inserts a space mid-sentence", "Let me look at", "the config file.", "Let me look at the config file."],
|
|
[
|
|
"keeps a single trailing space",
|
|
"Okay, I'll check that. ",
|
|
"The build passed.",
|
|
"Okay, I'll check that. The build passed.",
|
|
],
|
|
[
|
|
"keeps a single leading space",
|
|
"Okay, I'll check that.",
|
|
" The build passed.",
|
|
"Okay, I'll check that. The build passed.",
|
|
],
|
|
["collapses spaces on both sides", "Done. ", " Next.", "Done. Next."],
|
|
["collapses runs of spaces", "Done. ", "\t \tNext.", "Done. Next."],
|
|
[
|
|
"spaces after a question mark",
|
|
"Want me to run tests?",
|
|
"I can do that now.",
|
|
"Want me to run tests? I can do that now.",
|
|
],
|
|
["spaces after an ellipsis", "Thinking…", "done.", "Thinking… done."],
|
|
["preserves a trailing newline", "Done:\n", "Next up, tests.", "Done:\nNext up, tests."],
|
|
["preserves a leading newline", "Done:", "\nNext up, tests.", "Done:\nNext up, tests."],
|
|
[
|
|
"preserves a paragraph break",
|
|
"First paragraph.\n\n",
|
|
"Second paragraph.",
|
|
"First paragraph.\n\nSecond paragraph.",
|
|
],
|
|
["preserves a newline mixed with spaces", "Done: ", " \n Next.", "Done: \n Next."],
|
|
["never spaces before closing punctuation", "Done", ".", "Done."],
|
|
["never spaces before a comma", "one", ", two", "one, two"],
|
|
["never spaces before a closing paren", "(aside", ")", "(aside)"],
|
|
["never spaces after an opening paren", "an (", "aside)", "an (aside)"],
|
|
["drops an empty left side", "", "The build passed.", "The build passed."],
|
|
["drops an empty right side", "The build passed.", "", "The build passed."],
|
|
["drops a whitespace-only left side", " ", "The build passed.", "The build passed."],
|
|
["drops a whitespace-only right side", "The build passed.", " \n ", "The build passed."],
|
|
["handles two empty sides", "", "", ""],
|
|
["does not trim the outer edges", " Leading kept.", "Trailing kept. ", " Leading kept. Trailing kept. "],
|
|
]
|
|
|
|
for (const [name, left, right, expected] of cases) {
|
|
test(name, () => expect(joinAssistantText(left, right).text).toBe(expected))
|
|
}
|
|
|
|
test("splits the result at the boundary so reveals cover only the appended text", () => {
|
|
for (const [name, left, right] of cases) {
|
|
const joined = joinAssistantText(left, right)
|
|
expect(`${name}: ${joined.previous}${joined.appended}`).toBe(`${name}: ${joined.text}`)
|
|
expect(`${name}: ${joined.text.endsWith(joined.appended)}`).toBe(`${name}: true`)
|
|
}
|
|
})
|
|
|
|
test("is idempotent once a boundary is normalised", () => {
|
|
const once = joinAssistantText("Okay.", "Next.").text
|
|
expect(joinAssistantText(once, "").text).toBe(once)
|
|
expect(joinAssistantText("Okay. ", "Next.").text).toBe(once)
|
|
})
|
|
|
|
test("folds consecutive messages with exactly one space per boundary", () => {
|
|
expect(["First.", "Second.", "Third."].reduce((left, right) => joinAssistantText(left, right).text)).toBe(
|
|
"First. Second. Third.",
|
|
)
|
|
expect(["First.", "", "Third."].reduce((left, right) => joinAssistantText(left, right).text)).toBe("First. Third.")
|
|
})
|
|
})
|