import {describe, expect, it} from "bun:test"; import type {Editor} from "obsidian"; import {createTask, cycleTaskStatus} from "../src/taskActions"; import type {MarkdownTasksSettings} from "../src/settings"; const baseSettings = { completedSymbol: "x", inProgressSymbol: "/", movedSymbol: ">", cycleStatuses: {open: true, inProgress: true, completed: true, moved: false}, } as unknown as MarkdownTasksSettings; const settings = (overrides: Partial = {}): MarkdownTasksSettings => ({ ...baseSettings, ...overrides, }); interface FakeEditor { editor: Editor; getLines(): string[]; } function makeEditor(initial: string[], from = 0, to = initial.length - 1): FakeEditor { const lines = [...initial]; const editor = { getCursor: (which: "from" | "to") => ({line: which === "from" ? from : to, ch: 0}), getLine: (n: number) => lines[n], setLine: (n: number, text: string) => { lines[n] = text; }, } as unknown as Editor; return {editor, getLines: () => lines}; } describe("cycleTaskStatus – existing tasks", () => { it("cycles open → in-progress with default settings", () => { const fake = makeEditor(["- [ ] thing"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["- [/] thing"]); }); it("wraps the last enabled status back to the first", () => { const fake = makeEditor(["- [x] done"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["- [ ] done"]); }); it("treats unknown markers as the start of the cycle", () => { const fake = makeEditor(["- [?] mystery"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["- [ ] mystery"]); }); it("does nothing when no statuses are enabled in the cycle", () => { const fake = makeEditor(["- [ ] thing"]); cycleTaskStatus( fake.editor, settings({ cycleStatuses: {open: false, inProgress: false, completed: false, moved: false}, }), ); expect(fake.getLines()).toEqual(["- [ ] thing"]); }); }); describe("cycleTaskStatus – non-task lines", () => { it("converts a plain text line into an open task", () => { const fake = makeEditor(["just some text"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["- [ ] just some text"]); }); it("preserves indentation when converting plain text", () => { const fake = makeEditor([" hello"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual([" - [ ] hello"]); }); it("turns a bullet list item into a task without changing its marker", () => { const fake = makeEditor(["* shopping"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["* [ ] shopping"]); }); it("turns an ordered list item into a task without changing its marker", () => { const fake = makeEditor(["1. first"]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["1. [ ] first"]); }); it("leaves an empty line as a bare task", () => { const fake = makeEditor([""]); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["- [ ] "]); }); it("converts non-task lines and cycles existing tasks within the same selection", () => { const fake = makeEditor(["plain", "- [ ] task", "- next"], 0, 2); cycleTaskStatus(fake.editor, settings()); expect(fake.getLines()).toEqual(["- [ ] plain", "- [/] task", "- [ ] next"]); }); }); describe("createTask – unchanged behavior", () => { it("does not modify a line that is already a task", () => { const fake = makeEditor(["- [/] in progress"]); createTask(fake.editor); expect(fake.getLines()).toEqual(["- [/] in progress"]); }); it("converts plain text into an open task", () => { const fake = makeEditor(["plain"]); createTask(fake.editor); expect(fake.getLines()).toEqual(["- [ ] plain"]); }); });