backlog-md/src/test/checklist.test.ts

274 lines
7.8 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import {
alignAcceptanceCriteria,
CHECKBOX_PATTERNS,
type ChecklistItem,
extractAndFormatAcceptanceCriteria,
formatChecklist,
formatChecklistItem,
parseCheckboxLine,
parseCheckboxLines,
} from "../ui/checklist.ts";
describe("Checklist utilities", () => {
describe("CHECKBOX_PATTERNS", () => {
test("should match checked checkbox lines", () => {
const testCases = [
"- [x] Checked item",
" - [x] Indented checked item",
"- [x] Item with extra spaces",
"-[x] No space after dash",
];
for (const testCase of testCases) {
expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(true);
}
});
test("should match unchecked checkbox lines", () => {
const testCases = [
"- [ ] Unchecked item",
" - [ ] Indented unchecked item",
"- [ ] Item with extra spaces",
"-[ ] No space after dash",
];
for (const testCase of testCases) {
expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(true);
}
});
test("should not match non-checkbox lines", () => {
const testCases = [
"Regular text",
"- Regular bullet point",
"- [ Missing closing bracket",
"- [y] Invalid checkbox state",
"[x] Missing dash prefix",
"## Header",
];
for (const testCase of testCases) {
expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(false);
}
});
test("should match checkbox prefix pattern", () => {
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [x] ")).toBe(true);
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [ ] ")).toBe(true);
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("-[x] ")).toBe(true);
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [x]")).toBe(true);
});
});
describe("parseCheckboxLine", () => {
test("should parse checked checkbox lines", () => {
const result = parseCheckboxLine("- [x] This is checked");
expect(result).toEqual({
text: "This is checked",
checked: true,
});
});
test("should parse unchecked checkbox lines", () => {
const result = parseCheckboxLine("- [ ] This is unchecked");
expect(result).toEqual({
text: "This is unchecked",
checked: false,
});
});
test("should handle indented checkboxes", () => {
const result = parseCheckboxLine(" - [x] Indented checkbox");
expect(result).toEqual({
text: "Indented checkbox",
checked: true,
});
});
test("should handle extra whitespace", () => {
const result = parseCheckboxLine("- [x] Extra spaces around text ");
expect(result).toEqual({
text: "Extra spaces around text",
checked: true,
});
});
test("should return null for non-checkbox lines", () => {
expect(parseCheckboxLine("Regular text")).toBe(null);
expect(parseCheckboxLine("- Regular bullet")).toBe(null);
expect(parseCheckboxLine("## Header")).toBe(null);
});
});
describe("formatChecklistItem", () => {
test("should format checked item with default options", () => {
const item: ChecklistItem = { text: "Test item", checked: true };
const result = formatChecklistItem(item);
expect(result).toBe(" [x] Test item");
});
test("should format unchecked item with default options", () => {
const item: ChecklistItem = { text: "Test item", checked: false };
const result = formatChecklistItem(item);
expect(result).toBe(" [ ] Test item");
});
test("should use custom symbols", () => {
const item: ChecklistItem = { text: "Test item", checked: true };
const result = formatChecklistItem(item, {
checkedSymbol: "☑",
uncheckedSymbol: "☐",
});
expect(result).toBe(" ☑ Test item");
});
test("should use custom padding", () => {
const item: ChecklistItem = { text: "Test item", checked: false };
const result = formatChecklistItem(item, {
padding: " ",
});
expect(result).toBe(" [ ] Test item");
});
});
describe("parseCheckboxLines", () => {
test("should parse multiple checkbox lines", () => {
const text = `- [x] First item
- [ ] Second item
- [x] Third item`;
const result = parseCheckboxLines(text);
expect(result).toEqual([
{ text: "First item", checked: true },
{ text: "Second item", checked: false },
{ text: "Third item", checked: true },
]);
});
test("should ignore non-checkbox lines", () => {
const text = `- [x] Checkbox item
Regular text
- [ ] Another checkbox
## Header`;
const result = parseCheckboxLines(text);
expect(result).toEqual([
{ text: "Checkbox item", checked: true },
{ text: "Another checkbox", checked: false },
]);
});
});
describe("formatChecklist", () => {
test("should format multiple items consistently", () => {
const items: ChecklistItem[] = [
{ text: "First item", checked: true },
{ text: "Second item", checked: false },
{ text: "Third item", checked: true },
];
const result = formatChecklist(items);
expect(result).toEqual([" [x] First item", " [ ] Second item", " [x] Third item"]);
});
});
describe("alignAcceptanceCriteria", () => {
test("should align checkbox items consistently", () => {
const criteriaSection = `- [x] First criterion
- [ ] Second criterion
- [x] Third criterion`;
const result = alignAcceptanceCriteria(criteriaSection);
expect(result).toEqual([" [x] First criterion", " [ ] Second criterion", " [x] Third criterion"]);
});
test("should handle mixed content with consistent padding", () => {
const criteriaSection = `- [x] Checkbox item
Regular note
- [ ] Another checkbox`;
const result = alignAcceptanceCriteria(criteriaSection);
expect(result).toEqual([" [x] Checkbox item", " Regular note", " [ ] Another checkbox"]);
});
test("should handle empty or whitespace-only lines", () => {
const criteriaSection = `- [x] First item
- [ ] Second item
- [x] Third item`;
const result = alignAcceptanceCriteria(criteriaSection);
expect(result).toEqual([" [x] First item", " [ ] Second item", " [x] Third item"]);
});
});
describe("extractAndFormatAcceptanceCriteria", () => {
test("should extract and format acceptance criteria from markdown", () => {
const content = `## Description
Some description here.
## Acceptance Criteria
- [x] First criterion is done
- [ ] Second criterion pending
- [x] Third criterion complete
## Implementation Notes
Some notes here.`;
const result = extractAndFormatAcceptanceCriteria(content);
expect(result).toEqual([
" [x] First criterion is done",
" [ ] Second criterion pending",
" [x] Third criterion complete",
]);
});
test("should return empty array when no acceptance criteria section exists", () => {
const content = `## Description
Some description here.
## Implementation Notes
Some notes here.`;
const result = extractAndFormatAcceptanceCriteria(content);
expect(result).toEqual([]);
});
test("should handle case-insensitive section headers", () => {
const content = `## acceptance criteria
- [x] Test item
- [ ] Another test`;
const result = extractAndFormatAcceptanceCriteria(content);
expect(result).toEqual([" [x] Test item", " [ ] Another test"]);
});
});
describe("alignment consistency", () => {
test("all formatted items should start at the same column", () => {
const items: ChecklistItem[] = [
{ text: "Short", checked: true },
{ text: "Much longer item text here", checked: false },
{ text: "Medium length item", checked: true },
];
const formatted = formatChecklist(items);
// All items should start with the same padding
for (const line of formatted) {
expect(line.startsWith(" ")).toBe(true);
expect(line.charAt(0)).toBe(" ");
}
// All checkbox positions should be the same
const checkboxPositions = formatted.map((line) => line.indexOf("["));
const firstPosition = checkboxPositions[0] ?? -1;
for (const position of checkboxPositions) {
expect(position).toBe(firstPosition);
}
});
});
});