backlog.md 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/backlog-darwin-arm64/backlog +0 -0
- package/bin/backlog-darwin-x64/backlog +0 -0
- package/bin/backlog-linux-arm64/backlog +0 -0
- package/{cli → bin/backlog-linux-x64}/backlog +0 -0
- package/bin/backlog-win32-x64/backlog.exe +0 -0
- package/cli.js +62 -0
- package/package.json +57 -46
- package/.backlog/archive/drafts/readme.md +0 -3
- package/.backlog/archive/drafts/task-41 - temporary-test-task.md +0 -13
- package/.backlog/archive/readme.md +0 -6
- package/.backlog/archive/tasks/readme.md +0 -3
- package/.backlog/archive/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -14
- package/.backlog/config.yml +0 -7
- package/.backlog/decisions/readme.md +0 -7
- package/.backlog/docs/readme.md +0 -20
- package/.backlog/drafts/readme.md +0 -3
- package/.backlog/drafts/task-26 - docs-add-board-export-step-to-agent-dod.md +0 -21
- package/.backlog/drafts/task-28 - add-code-of-conduct.md +0 -20
- package/.backlog/drafts/task-30 - create-changelog.md +0 -19
- package/.backlog/milestones/m-0 - project-setup.md +0 -8
- package/.backlog/milestones/m-1 - cli.md +0 -8
- package/.backlog/milestones/m-2 - cli-kanban.md +0 -8
- package/.backlog/milestones/m-3 - gui.md +0 -8
- package/.backlog/milestones/m-4 - gui-kanban.md +0 -8
- package/.backlog/milestones/m-5 - gui-advanced.md +0 -12
- package/.backlog/milestones/readme.md +0 -3
- package/.backlog/readme.md +0 -5
- package/.backlog/tasks/readme.md +0 -37
- package/.backlog/tasks/task-1 - cli-setup-core-project.md +0 -23
- package/.backlog/tasks/task-10 - gui-init-packaging.md +0 -23
- package/.backlog/tasks/task-11 - gui-kanban-board.md +0 -26
- package/.backlog/tasks/task-12 - gui-advanced.md +0 -25
- package/.backlog/tasks/task-13 - cli-add-agent-instruction-prompt.md +0 -53
- package/.backlog/tasks/task-13.1 - cli-agent-instruction-file-selection.md +0 -40
- package/.backlog/tasks/task-14 - gui-introduction-screens.md +0 -21
- package/.backlog/tasks/task-15 - improve-tasks-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-16 - improve-docs-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-17 - improve-drafts-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-18 - improve-decisions-readme-with-generic-example-and-cli-reference.md +0 -20
- package/.backlog/tasks/task-19 - cli-fix-default-task-status-and-remove-draft-from-statuses.md +0 -55
- package/.backlog/tasks/task-2 - cli-core-logic-library.md +0 -28
- package/.backlog/tasks/task-20 - add-agent-guideline-to-mark-tasks-in-progress-on-start.md +0 -32
- package/.backlog/tasks/task-21 - kanban-board-vertical-layout.md +0 -31
- package/.backlog/tasks/task-22 - cli-prevent-double-dash-in-task-filenames.md +0 -24
- package/.backlog/tasks/task-23 - cli-kanban-board-order-tasks-by-id-asc.md +0 -30
- package/.backlog/tasks/task-24 - handle-subtasks-in-the-kanban-view.md +0 -38
- package/.backlog/tasks/task-24.1 - cli-kanban-board-milestone-view.md +0 -19
- package/.backlog/tasks/task-25 - cli-export-kanban-board-to-readme.md +0 -28
- package/.backlog/tasks/task-27 - add-contributing-guidelines.md +0 -27
- package/.backlog/tasks/task-29 - add-github-templates.md +0 -28
- package/.backlog/tasks/task-3 - cli-implement-backlog-init.md +0 -63
- package/.backlog/tasks/task-31 - update-readme-for-open-source.md +0 -26
- package/.backlog/tasks/task-32 - cli-hide-empty-'no-status'-column.md +0 -31
- package/.backlog/tasks/task-33 - cli-export-milestones-board-as-roadmap.md +0 -20
- package/.backlog/tasks/task-34 - split-readme.md-for-users-and-contributors.md +0 -26
- package/.backlog/tasks/task-35 - finalize-package.json-metadata-for-publishing.md +0 -24
- package/.backlog/tasks/task-36 - cli-prompt-for-project-name-in-init.md +0 -24
- package/.backlog/tasks/task-37 - cli-board-view-open-tasks-in-ide.md +0 -19
- package/.backlog/tasks/task-38 - cli-improved-agent-selection-for-init.md +0 -25
- package/.backlog/tasks/task-39 - cli-fix-empty-agent-instruction-files-on-init.md +0 -31
- package/.backlog/tasks/task-4 - cli-task-management-commands.md +0 -28
- package/.backlog/tasks/task-4.1 - cli-task-create.md +0 -27
- package/.backlog/tasks/task-4.10 - use-cli-to-mark-tasks-done.md +0 -51
- package/.backlog/tasks/task-4.11 - docs-add-definition-of-done-to-agent-guidelines.md +0 -23
- package/.backlog/tasks/task-4.12 - cli-handle-task-id-conflicts-across-branches.md +0 -53
- package/.backlog/tasks/task-4.13 - cli-fix-config-command-local-global-logic.md +0 -58
- package/.backlog/tasks/task-4.2 - cli-task-list-view.md +0 -25
- package/.backlog/tasks/task-4.3 - cli-task-edit.md +0 -24
- package/.backlog/tasks/task-4.4 - cli-task-archive-transition.md +0 -27
- package/.backlog/tasks/task-4.5 - cli-init-prompts-for-reporter-name-and-global-local-config.md +0 -28
- package/.backlog/tasks/task-4.6 - cli-add-empty-assignee-array-field-for-new-tasks.md +0 -35
- package/.backlog/tasks/task-4.7 - cli-parse-unquoted-created_date.md +0 -40
- package/.backlog/tasks/task-4.8 - cli-enforce-description-header.md +0 -48
- package/.backlog/tasks/task-4.9 - cli-normalize-task-id-inputs.md +0 -66
- package/.backlog/tasks/task-40 - cli-board-command-defaults-to-view.md +0 -38
- package/.backlog/tasks/task-41 - cli-migrate-terminal-ui-to-bblessed.md +0 -93
- package/.backlog/tasks/task-41.1 - cli-bblessed-init-wizard.md +0 -42
- package/.backlog/tasks/task-41.2 - cli-bblessed-task-view.md +0 -44
- package/.backlog/tasks/task-41.3 - cli-bblessed-doc-view.md +0 -45
- package/.backlog/tasks/task-41.4 - cli-bblessed-board-view.md +0 -49
- package/.backlog/tasks/task-41.5 - cli-audit-remaining-ui-for-bblessed.md +0 -55
- package/.backlog/tasks/task-42 - visual-hierarchy.md +0 -54
- package/.backlog/tasks/task-43 - remove-duplicate-acceptance-criteria-and-style-metadata.md +0 -56
- package/.backlog/tasks/task-44 - checklist-alignment.md +0 -24
- package/.backlog/tasks/task-45 - safe-line-wrapping.md +0 -23
- package/.backlog/tasks/task-46 - split-pane-layout.md +0 -24
- package/.backlog/tasks/task-47 - sticky-header-in-detail-view.md +0 -43
- package/.backlog/tasks/task-48 - footer-hint-line.md +0 -21
- package/.backlog/tasks/task-49 - status-styling.md +0 -53
- package/.backlog/tasks/task-5 - cli-docs-decisions.md +0 -57
- package/.backlog/tasks/task-50 - borders-&-padding.md +0 -22
- package/.backlog/tasks/task-51 - code-path-styling.md +0 -23
- package/.backlog/tasks/task-52 - cli-filter-tasks-list-by-status-or-assignee.md +0 -29
- package/.backlog/tasks/task-6 - cli-packaging.md +0 -65
- package/.backlog/tasks/task-6.1 - cli-local-installation-support-for-bunx-npx.md +0 -49
- package/.backlog/tasks/task-6.2 - cli-github-actions-for-build-&-publish.md +0 -64
- package/.backlog/tasks/task-7 - cli-kanban-view.md +0 -60
- package/.backlog/tasks/task-7.1 - cli-kanban-board-detect-remote-task-status.md +0 -62
- package/.backlog/tasks/task-8 - gui-project-setup.md +0 -21
- package/.backlog/tasks/task-9 - gui-task-crud.md +0 -24
- package/.cursorrules +0 -223
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -25
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -8
- package/.github/workflows/ci.yml +0 -36
- package/.husky/pre-commit +0 -1
- package/AGENTS.md +0 -65
- package/CLAUDE.md +0 -87
- package/CONTRIBUTING.md +0 -19
- package/DEVELOPMENT.md +0 -37
- package/biome.json +0 -31
- package/bun.lock +0 -152
- package/cli/.cursorrules-xh86jabm.md +0 -82
- package/cli/AGENTS-xh86jabm.md +0 -82
- package/cli/CLAUDE-xh86jabm.md +0 -82
- package/cli/cli.js +0 -19622
- package/cli/index.js +0 -2
- package/docs/npm-publishing.md +0 -69
- package/scripts/build.js +0 -73
- package/src/agent-instructions.ts +0 -54
- package/src/board.ts +0 -263
- package/src/cli.ts +0 -806
- package/src/constants/index.ts +0 -48
- package/src/core/backlog.ts +0 -183
- package/src/core/remote-tasks.ts +0 -168
- package/src/file-system/operations.ts +0 -515
- package/src/git/operations.ts +0 -189
- package/src/guidelines/.cursorrules.md +0 -82
- package/src/guidelines/AGENTS.md +0 -82
- package/src/guidelines/CLAUDE.md +0 -82
- package/src/guidelines/index.ts +0 -7
- package/src/index.ts +0 -30
- package/src/markdown/parser.ts +0 -145
- package/src/markdown/serializer.ts +0 -71
- package/src/test/agent-instructions.test.ts +0 -62
- package/src/test/board.test.ts +0 -291
- package/src/test/build.test.ts +0 -28
- package/src/test/checklist.test.ts +0 -273
- package/src/test/cli.test.ts +0 -1300
- package/src/test/code-path.test.ts +0 -204
- package/src/test/core.test.ts +0 -330
- package/src/test/filesystem.test.ts +0 -435
- package/src/test/git.test.ts +0 -26
- package/src/test/heading.test.ts +0 -102
- package/src/test/line-wrapping.test.ts +0 -252
- package/src/test/local-install.test.ts +0 -34
- package/src/test/markdown.test.ts +0 -526
- package/src/test/parallel-loading.test.ts +0 -160
- package/src/test/parent-id-normalization.test.ts +0 -48
- package/src/test/remote-id-conflict.test.ts +0 -60
- package/src/test/status-icon.test.ts +0 -93
- package/src/types/blessed.d.ts +0 -14
- package/src/types/index.ts +0 -55
- package/src/types/raw.d.ts +0 -4
- package/src/ui/board.ts +0 -322
- package/src/ui/checklist.ts +0 -103
- package/src/ui/code-path.ts +0 -113
- package/src/ui/heading.ts +0 -121
- package/src/ui/loading.ts +0 -216
- package/src/ui/status-icon.ts +0 -53
- package/src/ui/task-list.ts +0 -168
- package/src/ui/task-viewer.ts +0 -640
- package/src/ui/tui.ts +0 -301
- package/tsconfig.json +0 -26
package/src/test/board.test.ts
DELETED
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { exportKanbanBoardToFile, generateKanbanBoard } from "../board.ts";
|
|
6
|
-
import type { Task } from "../types/index.ts";
|
|
7
|
-
|
|
8
|
-
describe("generateKanbanBoard", () => {
|
|
9
|
-
it("creates board layout with statuses", () => {
|
|
10
|
-
const tasks: Task[] = [
|
|
11
|
-
{
|
|
12
|
-
id: "task-1",
|
|
13
|
-
title: "First",
|
|
14
|
-
status: "To Do",
|
|
15
|
-
assignee: [],
|
|
16
|
-
createdDate: "",
|
|
17
|
-
labels: [],
|
|
18
|
-
dependencies: [],
|
|
19
|
-
description: "",
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: "task-2",
|
|
23
|
-
title: "Second",
|
|
24
|
-
status: "In Progress",
|
|
25
|
-
assignee: [],
|
|
26
|
-
createdDate: "",
|
|
27
|
-
labels: [],
|
|
28
|
-
dependencies: [],
|
|
29
|
-
description: "",
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: "task-3",
|
|
33
|
-
title: "Third",
|
|
34
|
-
status: "Done",
|
|
35
|
-
assignee: [],
|
|
36
|
-
createdDate: "",
|
|
37
|
-
labels: [],
|
|
38
|
-
dependencies: [],
|
|
39
|
-
description: "",
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
|
|
44
|
-
const lines = board.split("\n");
|
|
45
|
-
expect(lines[0]).toContain("To Do");
|
|
46
|
-
expect(lines[0]).toContain("In Progress");
|
|
47
|
-
expect(lines[0]).toContain("Done");
|
|
48
|
-
// Tasks are now on separate lines: ID first, then title
|
|
49
|
-
expect(board).toContain("task-1");
|
|
50
|
-
expect(board).toContain("First");
|
|
51
|
-
expect(board).toContain("task-2");
|
|
52
|
-
expect(board).toContain("Second");
|
|
53
|
-
expect(board).toContain("task-3");
|
|
54
|
-
expect(board).toContain("Third");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("handles tasks with no status", () => {
|
|
58
|
-
const tasks: Task[] = [
|
|
59
|
-
{
|
|
60
|
-
id: "task-1",
|
|
61
|
-
title: "No Status Task",
|
|
62
|
-
status: "",
|
|
63
|
-
assignee: [],
|
|
64
|
-
createdDate: "",
|
|
65
|
-
labels: [],
|
|
66
|
-
dependencies: [],
|
|
67
|
-
description: "",
|
|
68
|
-
},
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
|
|
72
|
-
expect(board).toContain("No Status");
|
|
73
|
-
expect(board).toContain("task-1");
|
|
74
|
-
expect(board).toContain("No Status Task");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("omits 'No Status' column when all tasks have status", () => {
|
|
78
|
-
const tasks: Task[] = [
|
|
79
|
-
{
|
|
80
|
-
id: "task-1",
|
|
81
|
-
title: "Status Task",
|
|
82
|
-
status: "To Do",
|
|
83
|
-
assignee: [],
|
|
84
|
-
createdDate: "",
|
|
85
|
-
labels: [],
|
|
86
|
-
dependencies: [],
|
|
87
|
-
description: "",
|
|
88
|
-
},
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
|
|
92
|
-
expect(board).not.toContain("No Status");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("handles empty task list", () => {
|
|
96
|
-
const board = generateKanbanBoard([], ["To Do", "In Progress", "Done"]);
|
|
97
|
-
expect(board).toBe(""); // No columns when no tasks
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("respects status order from config", () => {
|
|
101
|
-
const tasks: Task[] = [
|
|
102
|
-
{
|
|
103
|
-
id: "task-1",
|
|
104
|
-
title: "First",
|
|
105
|
-
status: "Done",
|
|
106
|
-
assignee: [],
|
|
107
|
-
createdDate: "",
|
|
108
|
-
labels: [],
|
|
109
|
-
dependencies: [],
|
|
110
|
-
description: "",
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
id: "task-2",
|
|
114
|
-
title: "Second",
|
|
115
|
-
status: "To Do",
|
|
116
|
-
assignee: [],
|
|
117
|
-
createdDate: "",
|
|
118
|
-
labels: [],
|
|
119
|
-
dependencies: [],
|
|
120
|
-
description: "",
|
|
121
|
-
},
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"]);
|
|
125
|
-
const lines = board.split("\n");
|
|
126
|
-
// Status order should be preserved even if tasks exist in different order
|
|
127
|
-
const header = lines[0];
|
|
128
|
-
const todoIndex = header.indexOf("To Do");
|
|
129
|
-
const doneIndex = header.indexOf("Done");
|
|
130
|
-
expect(todoIndex).toBeLessThan(doneIndex);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("handles long task titles by wrapping text within max column width", () => {
|
|
134
|
-
const tasks: Task[] = [
|
|
135
|
-
{
|
|
136
|
-
id: "task-1",
|
|
137
|
-
title: "This is a very long task title that should expand the column width significantly",
|
|
138
|
-
status: "To Do",
|
|
139
|
-
assignee: [],
|
|
140
|
-
createdDate: "",
|
|
141
|
-
labels: [],
|
|
142
|
-
dependencies: [],
|
|
143
|
-
description: "",
|
|
144
|
-
},
|
|
145
|
-
];
|
|
146
|
-
|
|
147
|
-
const board = generateKanbanBoard(tasks, ["To Do"]);
|
|
148
|
-
const lines = board.split("\n");
|
|
149
|
-
const header = lines[0];
|
|
150
|
-
const taskIdLine = lines[2]; // First task line (ID)
|
|
151
|
-
const firstTitleLine = lines[3]; // First wrapped title line
|
|
152
|
-
// Column width should be constrained by default maxColumnWidth (20)
|
|
153
|
-
expect(header.length).toBe(20);
|
|
154
|
-
expect(taskIdLine).toContain("task-1");
|
|
155
|
-
expect(firstTitleLine).toContain("This is a very long");
|
|
156
|
-
// Should have multiple lines for the wrapped title
|
|
157
|
-
expect(lines.length).toBeGreaterThan(4);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it("nests subtasks under their parent when statuses match", () => {
|
|
161
|
-
const tasks: Task[] = [
|
|
162
|
-
{
|
|
163
|
-
id: "task-1",
|
|
164
|
-
title: "Parent",
|
|
165
|
-
status: "To Do",
|
|
166
|
-
assignee: [],
|
|
167
|
-
createdDate: "",
|
|
168
|
-
labels: [],
|
|
169
|
-
dependencies: [],
|
|
170
|
-
description: "",
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
id: "task-1.1",
|
|
174
|
-
title: "Child",
|
|
175
|
-
status: "To Do",
|
|
176
|
-
assignee: [],
|
|
177
|
-
createdDate: "",
|
|
178
|
-
labels: [],
|
|
179
|
-
dependencies: [],
|
|
180
|
-
description: "",
|
|
181
|
-
parentTaskId: "task-1",
|
|
182
|
-
},
|
|
183
|
-
];
|
|
184
|
-
|
|
185
|
-
const board = generateKanbanBoard(tasks, ["To Do"]);
|
|
186
|
-
expect(board).toContain(" └─ task-1.1");
|
|
187
|
-
expect(board).toContain(" Child");
|
|
188
|
-
|
|
189
|
-
const lines = board.split("\n");
|
|
190
|
-
const parentIdx = lines.findIndex((l) => l.includes("task-1") && !l.includes("task-1.1"));
|
|
191
|
-
const childIdx = lines.findIndex((l) => l.includes(" └─ task-1.1"));
|
|
192
|
-
expect(parentIdx).toBeGreaterThan(-1);
|
|
193
|
-
expect(childIdx).toBeGreaterThan(parentIdx);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it("sorts tasks by numeric id within each status", () => {
|
|
197
|
-
const tasks: Task[] = [
|
|
198
|
-
{
|
|
199
|
-
id: "task-10",
|
|
200
|
-
title: "T10",
|
|
201
|
-
status: "To Do",
|
|
202
|
-
assignee: [],
|
|
203
|
-
createdDate: "",
|
|
204
|
-
labels: [],
|
|
205
|
-
dependencies: [],
|
|
206
|
-
description: "",
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
id: "task-2",
|
|
210
|
-
title: "T2",
|
|
211
|
-
status: "To Do",
|
|
212
|
-
assignee: [],
|
|
213
|
-
createdDate: "",
|
|
214
|
-
labels: [],
|
|
215
|
-
dependencies: [],
|
|
216
|
-
description: "",
|
|
217
|
-
},
|
|
218
|
-
];
|
|
219
|
-
|
|
220
|
-
const board = generateKanbanBoard(tasks, ["To Do"]);
|
|
221
|
-
const lines = board.split("\n");
|
|
222
|
-
const firstIdLine = lines[2];
|
|
223
|
-
const secondIdLine = lines[5];
|
|
224
|
-
expect(firstIdLine.trim().startsWith("task-2")).toBe(true);
|
|
225
|
-
expect(secondIdLine.trim().startsWith("task-10")).toBe(true);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
it("creates vertical board layout", () => {
|
|
229
|
-
const tasks: Task[] = [
|
|
230
|
-
{
|
|
231
|
-
id: "task-1",
|
|
232
|
-
title: "First",
|
|
233
|
-
status: "To Do",
|
|
234
|
-
assignee: [],
|
|
235
|
-
createdDate: "",
|
|
236
|
-
labels: [],
|
|
237
|
-
dependencies: [],
|
|
238
|
-
description: "",
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
id: "task-2",
|
|
242
|
-
title: "Second",
|
|
243
|
-
status: "In Progress",
|
|
244
|
-
assignee: [],
|
|
245
|
-
createdDate: "",
|
|
246
|
-
labels: [],
|
|
247
|
-
dependencies: [],
|
|
248
|
-
description: "",
|
|
249
|
-
},
|
|
250
|
-
];
|
|
251
|
-
|
|
252
|
-
const board = generateKanbanBoard(tasks, ["To Do", "In Progress", "Done"], "vertical");
|
|
253
|
-
const lines = board.split("\n");
|
|
254
|
-
expect(lines[0]).toBe("To Do");
|
|
255
|
-
expect(lines).toContain("In Progress");
|
|
256
|
-
expect(board).toContain("task-1");
|
|
257
|
-
expect(board).toContain("First");
|
|
258
|
-
expect(board).toContain("task-2");
|
|
259
|
-
expect(board).toContain("Second");
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
describe("exportKanbanBoardToFile", () => {
|
|
264
|
-
it("creates file and appends board content", async () => {
|
|
265
|
-
const dir = await mkdtemp(join(tmpdir(), "board-export-"));
|
|
266
|
-
const file = join(dir, "readme.md");
|
|
267
|
-
const tasks: Task[] = [
|
|
268
|
-
{
|
|
269
|
-
id: "task-1",
|
|
270
|
-
title: "First",
|
|
271
|
-
status: "To Do",
|
|
272
|
-
assignee: [],
|
|
273
|
-
createdDate: "",
|
|
274
|
-
labels: [],
|
|
275
|
-
dependencies: [],
|
|
276
|
-
description: "",
|
|
277
|
-
},
|
|
278
|
-
];
|
|
279
|
-
|
|
280
|
-
await exportKanbanBoardToFile(tasks, ["To Do"], file);
|
|
281
|
-
const initial = await Bun.file(file).text();
|
|
282
|
-
expect(initial).toContain("task-1");
|
|
283
|
-
|
|
284
|
-
await exportKanbanBoardToFile(tasks, ["To Do"], file);
|
|
285
|
-
const second = await Bun.file(file).text();
|
|
286
|
-
const occurrences = second.split("task-1").length - 1;
|
|
287
|
-
expect(occurrences).toBe(2);
|
|
288
|
-
|
|
289
|
-
await rm(dir, { recursive: true, force: true });
|
|
290
|
-
});
|
|
291
|
-
});
|
package/src/test/build.test.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
-
import { mkdir, rm } from "node:fs/promises";
|
|
3
|
-
import { platform } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
const TEST_DIR = join(process.cwd(), "test-build");
|
|
7
|
-
const isWindows = platform() === "win32";
|
|
8
|
-
const executableName = isWindows ? "backlog.exe" : "backlog";
|
|
9
|
-
const OUTFILE = join(TEST_DIR, executableName);
|
|
10
|
-
|
|
11
|
-
describe("CLI packaging", () => {
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
await rm(TEST_DIR, { recursive: true, force: true }).catch(() => {});
|
|
14
|
-
await mkdir(TEST_DIR, { recursive: true });
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterEach(async () => {
|
|
18
|
-
await rm(TEST_DIR, { recursive: true, force: true }).catch(() => {});
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("should build and run compiled executable", async () => {
|
|
22
|
-
await Bun.spawn(["bun", "build", "src/cli.ts", "--compile", "--outfile", OUTFILE, "--loader:.md=text"]).exited;
|
|
23
|
-
|
|
24
|
-
const result = Bun.spawnSync({ cmd: [OUTFILE, "--help"] });
|
|
25
|
-
const output = result.stdout.toString();
|
|
26
|
-
expect(output).toContain("Backlog project management CLI");
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
CHECKBOX_PATTERNS,
|
|
4
|
-
type ChecklistItem,
|
|
5
|
-
alignAcceptanceCriteria,
|
|
6
|
-
extractAndFormatAcceptanceCriteria,
|
|
7
|
-
formatChecklist,
|
|
8
|
-
formatChecklistItem,
|
|
9
|
-
parseCheckboxLine,
|
|
10
|
-
parseCheckboxLines,
|
|
11
|
-
} from "../ui/checklist.ts";
|
|
12
|
-
|
|
13
|
-
describe("Checklist utilities", () => {
|
|
14
|
-
describe("CHECKBOX_PATTERNS", () => {
|
|
15
|
-
test("should match checked checkbox lines", () => {
|
|
16
|
-
const testCases = [
|
|
17
|
-
"- [x] Checked item",
|
|
18
|
-
" - [x] Indented checked item",
|
|
19
|
-
"- [x] Item with extra spaces",
|
|
20
|
-
"-[x] No space after dash",
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
for (const testCase of testCases) {
|
|
24
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(true);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("should match unchecked checkbox lines", () => {
|
|
29
|
-
const testCases = [
|
|
30
|
-
"- [ ] Unchecked item",
|
|
31
|
-
" - [ ] Indented unchecked item",
|
|
32
|
-
"- [ ] Item with extra spaces",
|
|
33
|
-
"-[ ] No space after dash",
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
for (const testCase of testCases) {
|
|
37
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(true);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("should not match non-checkbox lines", () => {
|
|
42
|
-
const testCases = [
|
|
43
|
-
"Regular text",
|
|
44
|
-
"- Regular bullet point",
|
|
45
|
-
"- [ Missing closing bracket",
|
|
46
|
-
"- [y] Invalid checkbox state",
|
|
47
|
-
"[x] Missing dash prefix",
|
|
48
|
-
"## Header",
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
for (const testCase of testCases) {
|
|
52
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_LINE.test(testCase)).toBe(false);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("should match checkbox prefix pattern", () => {
|
|
57
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [x] ")).toBe(true);
|
|
58
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [ ] ")).toBe(true);
|
|
59
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("-[x] ")).toBe(true);
|
|
60
|
-
expect(CHECKBOX_PATTERNS.CHECKBOX_PREFIX.test("- [x]")).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
describe("parseCheckboxLine", () => {
|
|
65
|
-
test("should parse checked checkbox lines", () => {
|
|
66
|
-
const result = parseCheckboxLine("- [x] This is checked");
|
|
67
|
-
expect(result).toEqual({
|
|
68
|
-
text: "This is checked",
|
|
69
|
-
checked: true,
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("should parse unchecked checkbox lines", () => {
|
|
74
|
-
const result = parseCheckboxLine("- [ ] This is unchecked");
|
|
75
|
-
expect(result).toEqual({
|
|
76
|
-
text: "This is unchecked",
|
|
77
|
-
checked: false,
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("should handle indented checkboxes", () => {
|
|
82
|
-
const result = parseCheckboxLine(" - [x] Indented checkbox");
|
|
83
|
-
expect(result).toEqual({
|
|
84
|
-
text: "Indented checkbox",
|
|
85
|
-
checked: true,
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("should handle extra whitespace", () => {
|
|
90
|
-
const result = parseCheckboxLine("- [x] Extra spaces around text ");
|
|
91
|
-
expect(result).toEqual({
|
|
92
|
-
text: "Extra spaces around text",
|
|
93
|
-
checked: true,
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("should return null for non-checkbox lines", () => {
|
|
98
|
-
expect(parseCheckboxLine("Regular text")).toBe(null);
|
|
99
|
-
expect(parseCheckboxLine("- Regular bullet")).toBe(null);
|
|
100
|
-
expect(parseCheckboxLine("## Header")).toBe(null);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
describe("formatChecklistItem", () => {
|
|
105
|
-
test("should format checked item with default options", () => {
|
|
106
|
-
const item: ChecklistItem = { text: "Test item", checked: true };
|
|
107
|
-
const result = formatChecklistItem(item);
|
|
108
|
-
expect(result).toBe(" [x] Test item");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("should format unchecked item with default options", () => {
|
|
112
|
-
const item: ChecklistItem = { text: "Test item", checked: false };
|
|
113
|
-
const result = formatChecklistItem(item);
|
|
114
|
-
expect(result).toBe(" [ ] Test item");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("should use custom symbols", () => {
|
|
118
|
-
const item: ChecklistItem = { text: "Test item", checked: true };
|
|
119
|
-
const result = formatChecklistItem(item, {
|
|
120
|
-
checkedSymbol: "☑",
|
|
121
|
-
uncheckedSymbol: "☐",
|
|
122
|
-
});
|
|
123
|
-
expect(result).toBe(" ☑ Test item");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("should use custom padding", () => {
|
|
127
|
-
const item: ChecklistItem = { text: "Test item", checked: false };
|
|
128
|
-
const result = formatChecklistItem(item, {
|
|
129
|
-
padding: " ",
|
|
130
|
-
});
|
|
131
|
-
expect(result).toBe(" [ ] Test item");
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe("parseCheckboxLines", () => {
|
|
136
|
-
test("should parse multiple checkbox lines", () => {
|
|
137
|
-
const text = `- [x] First item
|
|
138
|
-
- [ ] Second item
|
|
139
|
-
- [x] Third item`;
|
|
140
|
-
|
|
141
|
-
const result = parseCheckboxLines(text);
|
|
142
|
-
expect(result).toEqual([
|
|
143
|
-
{ text: "First item", checked: true },
|
|
144
|
-
{ text: "Second item", checked: false },
|
|
145
|
-
{ text: "Third item", checked: true },
|
|
146
|
-
]);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test("should ignore non-checkbox lines", () => {
|
|
150
|
-
const text = `- [x] Checkbox item
|
|
151
|
-
Regular text
|
|
152
|
-
- [ ] Another checkbox
|
|
153
|
-
## Header`;
|
|
154
|
-
|
|
155
|
-
const result = parseCheckboxLines(text);
|
|
156
|
-
expect(result).toEqual([
|
|
157
|
-
{ text: "Checkbox item", checked: true },
|
|
158
|
-
{ text: "Another checkbox", checked: false },
|
|
159
|
-
]);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
describe("formatChecklist", () => {
|
|
164
|
-
test("should format multiple items consistently", () => {
|
|
165
|
-
const items: ChecklistItem[] = [
|
|
166
|
-
{ text: "First item", checked: true },
|
|
167
|
-
{ text: "Second item", checked: false },
|
|
168
|
-
{ text: "Third item", checked: true },
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
const result = formatChecklist(items);
|
|
172
|
-
expect(result).toEqual([" [x] First item", " [ ] Second item", " [x] Third item"]);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe("alignAcceptanceCriteria", () => {
|
|
177
|
-
test("should align checkbox items consistently", () => {
|
|
178
|
-
const criteriaSection = `- [x] First criterion
|
|
179
|
-
- [ ] Second criterion
|
|
180
|
-
- [x] Third criterion`;
|
|
181
|
-
|
|
182
|
-
const result = alignAcceptanceCriteria(criteriaSection);
|
|
183
|
-
expect(result).toEqual([" [x] First criterion", " [ ] Second criterion", " [x] Third criterion"]);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test("should handle mixed content with consistent padding", () => {
|
|
187
|
-
const criteriaSection = `- [x] Checkbox item
|
|
188
|
-
Regular note
|
|
189
|
-
- [ ] Another checkbox`;
|
|
190
|
-
|
|
191
|
-
const result = alignAcceptanceCriteria(criteriaSection);
|
|
192
|
-
expect(result).toEqual([" [x] Checkbox item", " Regular note", " [ ] Another checkbox"]);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("should handle empty or whitespace-only lines", () => {
|
|
196
|
-
const criteriaSection = `- [x] First item
|
|
197
|
-
|
|
198
|
-
- [ ] Second item
|
|
199
|
-
|
|
200
|
-
- [x] Third item`;
|
|
201
|
-
|
|
202
|
-
const result = alignAcceptanceCriteria(criteriaSection);
|
|
203
|
-
expect(result).toEqual([" [x] First item", " [ ] Second item", " [x] Third item"]);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe("extractAndFormatAcceptanceCriteria", () => {
|
|
208
|
-
test("should extract and format acceptance criteria from markdown", () => {
|
|
209
|
-
const content = `## Description
|
|
210
|
-
Some description here.
|
|
211
|
-
|
|
212
|
-
## Acceptance Criteria
|
|
213
|
-
- [x] First criterion is done
|
|
214
|
-
- [ ] Second criterion pending
|
|
215
|
-
- [x] Third criterion complete
|
|
216
|
-
|
|
217
|
-
## Implementation Notes
|
|
218
|
-
Some notes here.`;
|
|
219
|
-
|
|
220
|
-
const result = extractAndFormatAcceptanceCriteria(content);
|
|
221
|
-
expect(result).toEqual([
|
|
222
|
-
" [x] First criterion is done",
|
|
223
|
-
" [ ] Second criterion pending",
|
|
224
|
-
" [x] Third criterion complete",
|
|
225
|
-
]);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test("should return empty array when no acceptance criteria section exists", () => {
|
|
229
|
-
const content = `## Description
|
|
230
|
-
Some description here.
|
|
231
|
-
|
|
232
|
-
## Implementation Notes
|
|
233
|
-
Some notes here.`;
|
|
234
|
-
|
|
235
|
-
const result = extractAndFormatAcceptanceCriteria(content);
|
|
236
|
-
expect(result).toEqual([]);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
test("should handle case-insensitive section headers", () => {
|
|
240
|
-
const content = `## acceptance criteria
|
|
241
|
-
- [x] Test item
|
|
242
|
-
- [ ] Another test`;
|
|
243
|
-
|
|
244
|
-
const result = extractAndFormatAcceptanceCriteria(content);
|
|
245
|
-
expect(result).toEqual([" [x] Test item", " [ ] Another test"]);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
describe("alignment consistency", () => {
|
|
250
|
-
test("all formatted items should start at the same column", () => {
|
|
251
|
-
const items: ChecklistItem[] = [
|
|
252
|
-
{ text: "Short", checked: true },
|
|
253
|
-
{ text: "Much longer item text here", checked: false },
|
|
254
|
-
{ text: "Medium length item", checked: true },
|
|
255
|
-
];
|
|
256
|
-
|
|
257
|
-
const formatted = formatChecklist(items);
|
|
258
|
-
|
|
259
|
-
// All items should start with the same padding
|
|
260
|
-
for (const line of formatted) {
|
|
261
|
-
expect(line.startsWith(" ")).toBe(true);
|
|
262
|
-
expect(line.charAt(0)).toBe(" ");
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// All checkbox positions should be the same
|
|
266
|
-
const checkboxPositions = formatted.map((line) => line.indexOf("["));
|
|
267
|
-
const firstPosition = checkboxPositions[0];
|
|
268
|
-
for (const position of checkboxPositions) {
|
|
269
|
-
expect(position).toBe(firstPosition);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
});
|