import {describe, expect, it} from "bun:test"; import {detectTasks} from "../src/pendingTasks"; import type {MarkdownTasksSettings} from "../src/settings"; const baseSettings = { completedSymbol: "x", movedSymbol: ">", countRootTasksOnly: false, } as unknown as MarkdownTasksSettings; const settings = (overrides: Partial = {}): MarkdownTasksSettings => ({ ...baseSettings, ...overrides, }); describe("detectTasks – basic counting", () => { it("counts open tasks as pending", () => { const result = detectTasks("- [ ] one\n- [ ] two\n", settings()); expect(result.totalTasks).toBe(2); expect(result.pendingTasks).toBe(2); expect(result.hasPendingTasks).toBe(true); }); it("treats completed and moved tasks as not pending", () => { const result = detectTasks("- [ ] open\n- [x] done\n- [>] moved\n", settings()); expect(result.totalTasks).toBe(3); expect(result.pendingTasks).toBe(1); }); it("returns no tasks for empty content", () => { const result = detectTasks("", settings()); expect(result.totalTasks).toBe(0); expect(result.pendingTasks).toBe(0); expect(result.hasPendingTasks).toBe(false); }); }); describe("detectTasks – code block exclusion", () => { it("does not count tasks inside a backtick-fenced code block", () => { const input = [ "# Notes", "", "```markdown", "- [ ] not a real task", "- [ ] also not real", "```", "", "- [ ] real task", ].join("\n"); const result = detectTasks(input, settings()); expect(result.totalTasks).toBe(1); expect(result.pendingTasks).toBe(1); }); it("does not count tasks inside a tilde-fenced code block", () => { const input = ["~~~", "- [ ] not real", "~~~", "- [x] done"].join("\n"); const result = detectTasks(input, settings()); expect(result.totalTasks).toBe(1); expect(result.pendingTasks).toBe(0); }); it("returns no pending tasks when only code-block tasks exist", () => { const input = ["```", "- [ ] not real", "```"].join("\n"); const result = detectTasks(input, settings()); expect(result.totalTasks).toBe(0); expect(result.pendingTasks).toBe(0); expect(result.hasPendingTasks).toBe(false); }); it("handles tasks before, inside, and after a code block", () => { const input = [ "- [ ] before", "```", "- [ ] inside (ignored)", "```", "- [x] after done", ].join("\n"); const result = detectTasks(input, settings()); expect(result.totalTasks).toBe(2); expect(result.pendingTasks).toBe(1); }); }); describe("detectTasks – frontmatter exclusion", () => { it("ignores task-like lines inside YAML frontmatter", () => { const input = ["---", "- [ ] not a task", "---", "- [ ] real task"].join("\n"); const result = detectTasks(input, settings()); expect(result.totalTasks).toBe(1); expect(result.pendingTasks).toBe(1); }); }); describe("detectTasks – root-only counting still skips code blocks", () => { it("skips code-block tasks when countRootTasksOnly is enabled", () => { const input = ["```", "- [ ] inside", "```", "- [ ] real"].join("\n"); const result = detectTasks(input, settings({countRootTasksOnly: true})); expect(result.totalTasks).toBe(1); expect(result.pendingTasks).toBe(1); }); });