582c4e07e9
Phase 4.5 (priorities 1, 2, 5, 6). - Content tab now reads GET /api/cv/outline: each entry-based section expands to its entries with per-item hide, title/subtitle override, and rich-text bullet editing (RichTextField markdown toolbar). - Native HTML5 drag-and-drop reorder for sections AND entries (useDragReorder, zero deps), with drop-line + dim animations; arrow buttons remain for keyboard. - Preview: zoom presets (+/- , slider, Fit), measured page count with page navigation and dashed page-break indicators, an "updating…" chip, 300ms debounce. - Save indicator now distinguishes Unsaved / Saving / Saved / failed (aria-live). - Loading skeletons, better empty states, ATS-friendly theme badge, relative times in History; a11y: ARIA labels, keyboard-selectable theme cards, focus rings. - Pure helpers moveItem/wrapSelection extracted + unit-tested; tsc + build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
import { moveItem, wrapSelection } from "./cvBuilder";
|
|
|
|
describe("moveItem", () => {
|
|
test("moves an item forward", () => {
|
|
expect(moveItem(["a", "b", "c"], 0, 2)).toEqual(["b", "c", "a"]);
|
|
});
|
|
test("moves an item backward", () => {
|
|
expect(moveItem(["a", "b", "c"], 2, 0)).toEqual(["c", "a", "b"]);
|
|
});
|
|
test("no-op for equal or out-of-range indices", () => {
|
|
const a = ["a", "b"];
|
|
expect(moveItem(a, 1, 1)).toBe(a);
|
|
expect(moveItem(a, 5, 0)).toBe(a);
|
|
expect(moveItem(a, 0, -1)).toBe(a);
|
|
});
|
|
test("does not mutate the input", () => {
|
|
const a = ["a", "b", "c"];
|
|
moveItem(a, 0, 2);
|
|
expect(a).toEqual(["a", "b", "c"]);
|
|
});
|
|
});
|
|
|
|
describe("wrapSelection", () => {
|
|
test("wraps a selection and keeps the selection over the inner text", () => {
|
|
const r = wrapSelection("hello world", 6, 11, "**", "**");
|
|
expect(r.text).toBe("hello **world**");
|
|
expect(r.text.slice(r.selStart, r.selEnd)).toBe("world");
|
|
});
|
|
test("inserts a placeholder when nothing is selected", () => {
|
|
const r = wrapSelection("", 0, 0, "**", "**", "bold text");
|
|
expect(r.text).toBe("**bold text**");
|
|
expect(r.text.slice(r.selStart, r.selEnd)).toBe("bold text");
|
|
});
|
|
test("wraps a link with a url suffix", () => {
|
|
const r = wrapSelection("see docs", 4, 8, "[", "](https://)");
|
|
expect(r.text).toBe("see [docs](https://)");
|
|
expect(r.text.slice(r.selStart, r.selEnd)).toBe("docs");
|
|
});
|
|
});
|