feat: authorAt + wordDiffByAuthor pure helpers (author-colored word diff w/ adjacency del)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, test, expect } from "vitest";
|
||||
import { splitBlocks, splitBlocksWithRanges, diffBlocks, diffToHunks, diffToBlockHunks, renderTrackChanges, colorByAuthor, type AuthorSpan } from "../src/trackChangesModel";
|
||||
import { splitBlocks, splitBlocksWithRanges, diffBlocks, diffToHunks, diffToBlockHunks, renderTrackChanges, colorByAuthor, authorAt, wordDiffByAuthor, type AuthorSpan } from "../src/trackChangesModel";
|
||||
|
||||
describe("splitBlocks", () => {
|
||||
it("splits prose paragraphs on blank lines, dropping empties", () => {
|
||||
@@ -209,6 +209,46 @@ describe("colorByAuthor", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("authorAt", () => {
|
||||
const spans: AuthorSpan[] = [
|
||||
{ start: 0, end: 5, author: "human" },
|
||||
{ start: 5, end: 10, author: "claude" },
|
||||
];
|
||||
test("returns the covering span's author (half-open)", () => {
|
||||
expect(authorAt(0, spans)).toBe("human");
|
||||
expect(authorAt(4, spans)).toBe("human");
|
||||
expect(authorAt(5, spans)).toBe("claude");
|
||||
expect(authorAt(9, spans)).toBe("claude");
|
||||
});
|
||||
test("returns null outside any span", () => {
|
||||
expect(authorAt(10, spans)).toBeNull();
|
||||
expect(authorAt(-1, spans)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("wordDiffByAuthor", () => {
|
||||
test("an inserted run is colored by the author covering its landed offset", () => {
|
||||
// before "hello " ; after "hello brave " ; "brave " inserted at landed offset 6..12
|
||||
const spans: AuthorSpan[] = [{ start: 6, end: 12, author: "claude" }];
|
||||
const html = wordDiffByAuthor("hello ", "hello brave ", 0, spans);
|
||||
expect(html).toContain('<ins class="cw-ins-claude">brave </ins>');
|
||||
expect(html).toContain("hello ");
|
||||
});
|
||||
test("a paired deletion inherits the paired insertion's author (adjacency)", () => {
|
||||
// "light" -> "dark", both in one cluster; "dark" is claude-inserted
|
||||
const spans: AuthorSpan[] = [{ start: 0, end: 4, author: "claude" }];
|
||||
const html = wordDiffByAuthor("light", "dark", 0, spans);
|
||||
expect(html).toContain('<del class="cw-del-claude">light</del>');
|
||||
expect(html).toContain('<ins class="cw-ins-claude">dark</ins>');
|
||||
});
|
||||
test("a standalone deletion (no paired insertion) is neutral", () => {
|
||||
const html = wordDiffByAuthor("keep this word", "keep word", 0, []);
|
||||
expect(html).toContain('<del class="cw-del-none">this </del>');
|
||||
expect(html).not.toContain("cw-del-human");
|
||||
expect(html).not.toContain("cw-del-claude");
|
||||
});
|
||||
});
|
||||
|
||||
import { renderPlain } from "../src/trackChangesModel";
|
||||
|
||||
describe("renderPlain", () => {
|
||||
|
||||
Reference in New Issue
Block a user