@task-mcp/cli 1.0.15 → 1.0.16
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/README.md +0 -2
- package/package.json +1 -1
- package/src/__tests__/dashboard.test.ts +228 -0
- package/src/__tests__/inbox.test.ts +307 -0
- package/src/__tests__/list.test.ts +352 -0
- package/src/commands/dashboard.ts +11 -6
- package/src/commands/inbox.ts +7 -20
- package/src/commands/list.ts +19 -26
- package/src/constants.ts +56 -0
- package/src/interactive.ts +9 -145
- package/src/storage.ts +37 -424
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { Task, Project, InboxItem } from "../storage.js";
|
|
3
|
+
|
|
4
|
+
// Helper to create test tasks
|
|
5
|
+
function createTask(overrides: Partial<Task> = {}): Task {
|
|
6
|
+
return {
|
|
7
|
+
id: `task_${Math.random().toString(36).slice(2, 10)}`,
|
|
8
|
+
title: "Test Task",
|
|
9
|
+
status: "pending",
|
|
10
|
+
priority: "medium",
|
|
11
|
+
projectId: "proj_test",
|
|
12
|
+
createdAt: new Date().toISOString(),
|
|
13
|
+
updatedAt: new Date().toISOString(),
|
|
14
|
+
...overrides,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Helper to create test projects
|
|
19
|
+
function createProject(overrides: Partial<Project> = {}): Project {
|
|
20
|
+
return {
|
|
21
|
+
id: `proj_${Math.random().toString(36).slice(2, 10)}`,
|
|
22
|
+
name: "Test Project",
|
|
23
|
+
status: "active",
|
|
24
|
+
createdAt: new Date().toISOString(),
|
|
25
|
+
updatedAt: new Date().toISOString(),
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Helper to create inbox items
|
|
31
|
+
function createInboxItem(overrides: Partial<InboxItem> = {}): InboxItem {
|
|
32
|
+
return {
|
|
33
|
+
id: `inbox_${Math.random().toString(36).slice(2, 10)}`,
|
|
34
|
+
content: "Test inbox item",
|
|
35
|
+
status: "pending",
|
|
36
|
+
capturedAt: new Date().toISOString(),
|
|
37
|
+
source: "cli",
|
|
38
|
+
...overrides,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("dashboard command - unit tests", () => {
|
|
43
|
+
describe("project lookup logic", () => {
|
|
44
|
+
test("finds project by exact ID", () => {
|
|
45
|
+
const projects = [
|
|
46
|
+
createProject({ id: "proj_abc123", name: "Project A" }),
|
|
47
|
+
createProject({ id: "proj_xyz789", name: "Project B" }),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const projectId = "proj_abc123";
|
|
51
|
+
const project = projects.find((p) => p.id === projectId || p.id.startsWith(projectId));
|
|
52
|
+
|
|
53
|
+
expect(project).toBeDefined();
|
|
54
|
+
expect(project?.name).toBe("Project A");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("finds project by partial ID prefix", () => {
|
|
58
|
+
const projects = [
|
|
59
|
+
createProject({ id: "proj_abc123456789", name: "Project A" }),
|
|
60
|
+
createProject({ id: "proj_xyz789012345", name: "Project B" }),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const projectId = "proj_abc";
|
|
64
|
+
const project = projects.find((p) => p.id === projectId || p.id.startsWith(projectId));
|
|
65
|
+
|
|
66
|
+
expect(project).toBeDefined();
|
|
67
|
+
expect(project?.name).toBe("Project A");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("returns undefined for non-matching ID", () => {
|
|
71
|
+
const projects = [
|
|
72
|
+
createProject({ id: "proj_abc123", name: "Project A" }),
|
|
73
|
+
createProject({ id: "proj_xyz789", name: "Project B" }),
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const projectId = "proj_nonexistent";
|
|
77
|
+
const project = projects.find((p) => p.id === projectId || p.id.startsWith(projectId));
|
|
78
|
+
|
|
79
|
+
expect(project).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("returns first match when multiple projects share prefix", () => {
|
|
83
|
+
const projects = [
|
|
84
|
+
createProject({ id: "proj_abc111", name: "Project First" }),
|
|
85
|
+
createProject({ id: "proj_abc222", name: "Project Second" }),
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const projectId = "proj_abc";
|
|
89
|
+
const project = projects.find((p) => p.id === projectId || p.id.startsWith(projectId));
|
|
90
|
+
|
|
91
|
+
expect(project).toBeDefined();
|
|
92
|
+
expect(project?.name).toBe("Project First");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("dashboard mode selection", () => {
|
|
97
|
+
test("single project mode when only one project exists", () => {
|
|
98
|
+
const projects = [createProject({ id: "proj_only", name: "Only Project" })];
|
|
99
|
+
|
|
100
|
+
const hasSingleProject = projects.length === 1;
|
|
101
|
+
const shouldShowGlobal = projects.length > 1;
|
|
102
|
+
|
|
103
|
+
expect(hasSingleProject).toBe(true);
|
|
104
|
+
expect(shouldShowGlobal).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("global mode when multiple projects exist", () => {
|
|
108
|
+
const projects = [
|
|
109
|
+
createProject({ id: "proj_1", name: "Project 1" }),
|
|
110
|
+
createProject({ id: "proj_2", name: "Project 2" }),
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const hasSingleProject = projects.length === 1;
|
|
114
|
+
const shouldShowGlobal = projects.length > 1;
|
|
115
|
+
|
|
116
|
+
expect(hasSingleProject).toBe(false);
|
|
117
|
+
expect(shouldShowGlobal).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("project mode when specific projectId provided", () => {
|
|
121
|
+
const projects = [
|
|
122
|
+
createProject({ id: "proj_1", name: "Project 1" }),
|
|
123
|
+
createProject({ id: "proj_2", name: "Project 2" }),
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const projectId = "proj_1";
|
|
127
|
+
const targetProject = projects.find((p) => p.id === projectId || p.id.startsWith(projectId));
|
|
128
|
+
|
|
129
|
+
expect(targetProject).toBeDefined();
|
|
130
|
+
expect(targetProject?.name).toBe("Project 1");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("task grouping by project", () => {
|
|
135
|
+
test("builds task map for each project", () => {
|
|
136
|
+
const projects = [
|
|
137
|
+
createProject({ id: "proj_1" }),
|
|
138
|
+
createProject({ id: "proj_2" }),
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
const allTasks = [
|
|
142
|
+
createTask({ id: "task_1", projectId: "proj_1" }),
|
|
143
|
+
createTask({ id: "task_2", projectId: "proj_1" }),
|
|
144
|
+
createTask({ id: "task_3", projectId: "proj_2" }),
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const tasksByProject = new Map<string, Task[]>();
|
|
148
|
+
for (const project of projects) {
|
|
149
|
+
tasksByProject.set(
|
|
150
|
+
project.id,
|
|
151
|
+
allTasks.filter((t) => t.projectId === project.id)
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
expect(tasksByProject.get("proj_1")?.length).toBe(2);
|
|
156
|
+
expect(tasksByProject.get("proj_2")?.length).toBe(1);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("returns empty array for project with no tasks", () => {
|
|
160
|
+
const tasksByProject = new Map<string, Task[]>();
|
|
161
|
+
tasksByProject.set("proj_1", []);
|
|
162
|
+
|
|
163
|
+
const getProjectTasks = (pid: string) => tasksByProject.get(pid) ?? [];
|
|
164
|
+
|
|
165
|
+
expect(getProjectTasks("proj_1")).toEqual([]);
|
|
166
|
+
expect(getProjectTasks("proj_nonexistent")).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("inbox filtering", () => {
|
|
171
|
+
test("filters inbox items by pending status", () => {
|
|
172
|
+
const items = [
|
|
173
|
+
createInboxItem({ id: "inbox_1", status: "pending" }),
|
|
174
|
+
createInboxItem({ id: "inbox_2", status: "promoted" }),
|
|
175
|
+
createInboxItem({ id: "inbox_3", status: "pending" }),
|
|
176
|
+
createInboxItem({ id: "inbox_4", status: "discarded" }),
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
const pendingItems = items.filter((i) => i.status === "pending");
|
|
180
|
+
|
|
181
|
+
expect(pendingItems.length).toBe(2);
|
|
182
|
+
expect(pendingItems.map((i) => i.id).sort()).toEqual(["inbox_1", "inbox_3"]);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("empty state handling", () => {
|
|
187
|
+
test("detects when no projects exist", () => {
|
|
188
|
+
const projects: Project[] = [];
|
|
189
|
+
|
|
190
|
+
const hasProjects = projects.length > 0;
|
|
191
|
+
|
|
192
|
+
expect(hasProjects).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("detects when tasks are empty", () => {
|
|
196
|
+
const tasks: Task[] = [];
|
|
197
|
+
|
|
198
|
+
const hasTasks = tasks.length > 0;
|
|
199
|
+
|
|
200
|
+
expect(hasTasks).toBe(false);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe("dashboard command - exports", () => {
|
|
206
|
+
test("dashboard function is exported", async () => {
|
|
207
|
+
const { dashboard } = await import("../commands/dashboard.js");
|
|
208
|
+
expect(typeof dashboard).toBe("function");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("dashboard accepts optional projectId parameter", async () => {
|
|
212
|
+
const { dashboard } = await import("../commands/dashboard.js");
|
|
213
|
+
|
|
214
|
+
// Verify function signature allows optional string parameter
|
|
215
|
+
// This is a type-level check, doesn't execute the function
|
|
216
|
+
const fn: (projectId?: string) => Promise<void> = dashboard;
|
|
217
|
+
expect(typeof fn).toBe("function");
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("dashboard rendering options", () => {
|
|
222
|
+
test("version option structure", () => {
|
|
223
|
+
const options = { version: "1.0.0" };
|
|
224
|
+
|
|
225
|
+
expect(options.version).toBe("1.0.0");
|
|
226
|
+
expect(typeof options.version).toBe("string");
|
|
227
|
+
});
|
|
228
|
+
});
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
describe("inbox commands - unit tests", () => {
|
|
4
|
+
describe("inbox item status handling", () => {
|
|
5
|
+
type InboxStatus = "pending" | "promoted" | "discarded";
|
|
6
|
+
|
|
7
|
+
test("pending is the default status", () => {
|
|
8
|
+
const defaultStatus: InboxStatus = "pending";
|
|
9
|
+
expect(defaultStatus).toBe("pending");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("all valid statuses are supported", () => {
|
|
13
|
+
const validStatuses: InboxStatus[] = ["pending", "promoted", "discarded"];
|
|
14
|
+
|
|
15
|
+
expect(validStatuses).toContain("pending");
|
|
16
|
+
expect(validStatuses).toContain("promoted");
|
|
17
|
+
expect(validStatuses).toContain("discarded");
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("content parsing", () => {
|
|
22
|
+
// Test the tag extraction logic from parseInboxInput
|
|
23
|
+
test("extracts hashtags from content", () => {
|
|
24
|
+
const content = "Test idea #tag1 with #tag2 multiple tags";
|
|
25
|
+
const tagRegex = /#(\w+)/g;
|
|
26
|
+
const tags: string[] = [];
|
|
27
|
+
let match;
|
|
28
|
+
while ((match = tagRegex.exec(content)) !== null) {
|
|
29
|
+
tags.push(match[1]!);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(tags).toContain("tag1");
|
|
33
|
+
expect(tags).toContain("tag2");
|
|
34
|
+
expect(tags.length).toBe(2);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("handles content without tags", () => {
|
|
38
|
+
const content = "Simple idea without any tags";
|
|
39
|
+
const tagRegex = /#(\w+)/g;
|
|
40
|
+
const tags: string[] = [];
|
|
41
|
+
let match;
|
|
42
|
+
while ((match = tagRegex.exec(content)) !== null) {
|
|
43
|
+
tags.push(match[1]!);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
expect(tags.length).toBe(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("handles empty content", () => {
|
|
50
|
+
const content = "";
|
|
51
|
+
const tagRegex = /#(\w+)/g;
|
|
52
|
+
const tags: string[] = [];
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = tagRegex.exec(content)) !== null) {
|
|
55
|
+
tags.push(match[1]!);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
expect(tags.length).toBe(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("extracts tags with alphanumeric characters", () => {
|
|
62
|
+
const content = "Idea #feature123 and #bug456";
|
|
63
|
+
const tagRegex = /#(\w+)/g;
|
|
64
|
+
const tags: string[] = [];
|
|
65
|
+
let match;
|
|
66
|
+
while ((match = tagRegex.exec(content)) !== null) {
|
|
67
|
+
tags.push(match[1]!);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
expect(tags).toContain("feature123");
|
|
71
|
+
expect(tags).toContain("bug456");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("title truncation for promote", () => {
|
|
76
|
+
test("truncates long content to 100 characters for title", () => {
|
|
77
|
+
const longContent = "A".repeat(150);
|
|
78
|
+
const title = longContent.slice(0, 100);
|
|
79
|
+
|
|
80
|
+
expect(title.length).toBe(100);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("keeps short content as-is for title", () => {
|
|
84
|
+
const shortContent = "Short inbox idea";
|
|
85
|
+
const title = shortContent.slice(0, 100);
|
|
86
|
+
|
|
87
|
+
expect(title).toBe(shortContent);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("priority validation", () => {
|
|
92
|
+
type Priority = "critical" | "high" | "medium" | "low";
|
|
93
|
+
|
|
94
|
+
test("all valid priorities are recognized", () => {
|
|
95
|
+
const priorities: Priority[] = ["critical", "high", "medium", "low"];
|
|
96
|
+
|
|
97
|
+
expect(priorities.length).toBe(4);
|
|
98
|
+
expect(priorities).toContain("critical");
|
|
99
|
+
expect(priorities).toContain("high");
|
|
100
|
+
expect(priorities).toContain("medium");
|
|
101
|
+
expect(priorities).toContain("low");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("default priority is medium", () => {
|
|
105
|
+
const defaultPriority: Priority = "medium";
|
|
106
|
+
expect(defaultPriority).toBe("medium");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("date formatting", () => {
|
|
111
|
+
test("formats ISO date string for display", () => {
|
|
112
|
+
const isoDate = "2024-12-25T10:30:00Z";
|
|
113
|
+
const date = new Date(isoDate);
|
|
114
|
+
const formatted = date.toLocaleDateString();
|
|
115
|
+
|
|
116
|
+
expect(formatted.length).toBeGreaterThan(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("formats full timestamp for detailed view", () => {
|
|
120
|
+
const isoDate = "2024-12-25T10:30:00Z";
|
|
121
|
+
const date = new Date(isoDate);
|
|
122
|
+
const formatted = date.toLocaleString();
|
|
123
|
+
|
|
124
|
+
expect(formatted.length).toBeGreaterThan(0);
|
|
125
|
+
// Should include both date and time components
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("filtering logic", () => {
|
|
130
|
+
interface InboxItem {
|
|
131
|
+
id: string;
|
|
132
|
+
status: "pending" | "promoted" | "discarded";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
test("filters by pending status", () => {
|
|
136
|
+
const items: InboxItem[] = [
|
|
137
|
+
{ id: "1", status: "pending" },
|
|
138
|
+
{ id: "2", status: "promoted" },
|
|
139
|
+
{ id: "3", status: "pending" },
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const filtered = items.filter((i) => i.status === "pending");
|
|
143
|
+
|
|
144
|
+
expect(filtered.length).toBe(2);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("filters by promoted status", () => {
|
|
148
|
+
const items: InboxItem[] = [
|
|
149
|
+
{ id: "1", status: "pending" },
|
|
150
|
+
{ id: "2", status: "promoted" },
|
|
151
|
+
{ id: "3", status: "promoted" },
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const filtered = items.filter((i) => i.status === "promoted");
|
|
155
|
+
|
|
156
|
+
expect(filtered.length).toBe(2);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("returns all items when no filter applied", () => {
|
|
160
|
+
const items: InboxItem[] = [
|
|
161
|
+
{ id: "1", status: "pending" },
|
|
162
|
+
{ id: "2", status: "promoted" },
|
|
163
|
+
{ id: "3", status: "discarded" },
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
expect(items.length).toBe(3);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("promoted item handling", () => {
|
|
171
|
+
test("tracks promotedToTaskId when promoted", () => {
|
|
172
|
+
const item = {
|
|
173
|
+
id: "inbox_123",
|
|
174
|
+
status: "promoted" as const,
|
|
175
|
+
promotedToTaskId: "task_456",
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
expect(item.promotedToTaskId).toBe("task_456");
|
|
179
|
+
expect(item.status).toBe("promoted");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("prevents double promotion", () => {
|
|
183
|
+
const item = {
|
|
184
|
+
id: "inbox_123",
|
|
185
|
+
status: "promoted" as const,
|
|
186
|
+
promotedToTaskId: "task_456",
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const canPromote = item.status !== "promoted";
|
|
190
|
+
|
|
191
|
+
expect(canPromote).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("allows promotion of pending items", () => {
|
|
195
|
+
const item: {
|
|
196
|
+
id: string;
|
|
197
|
+
status: "pending" | "promoted" | "discarded";
|
|
198
|
+
promotedToTaskId?: string;
|
|
199
|
+
} = {
|
|
200
|
+
id: "inbox_123",
|
|
201
|
+
status: "pending",
|
|
202
|
+
promotedToTaskId: undefined,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const canPromote = item.status !== "promoted";
|
|
206
|
+
|
|
207
|
+
expect(canPromote).toBe(true);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("inbox commands - exports", () => {
|
|
213
|
+
test("inboxAddCmd is exported", async () => {
|
|
214
|
+
const { inboxAddCmd } = await import("../commands/inbox.js");
|
|
215
|
+
expect(typeof inboxAddCmd).toBe("function");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("inboxListCmd is exported", async () => {
|
|
219
|
+
const { inboxListCmd } = await import("../commands/inbox.js");
|
|
220
|
+
expect(typeof inboxListCmd).toBe("function");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("inboxGetCmd is exported", async () => {
|
|
224
|
+
const { inboxGetCmd } = await import("../commands/inbox.js");
|
|
225
|
+
expect(typeof inboxGetCmd).toBe("function");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("inboxPromoteCmd is exported", async () => {
|
|
229
|
+
const { inboxPromoteCmd } = await import("../commands/inbox.js");
|
|
230
|
+
expect(typeof inboxPromoteCmd).toBe("function");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("inboxDiscardCmd is exported", async () => {
|
|
234
|
+
const { inboxDiscardCmd } = await import("../commands/inbox.js");
|
|
235
|
+
expect(typeof inboxDiscardCmd).toBe("function");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("inboxDeleteCmd is exported", async () => {
|
|
239
|
+
const { inboxDeleteCmd } = await import("../commands/inbox.js");
|
|
240
|
+
expect(typeof inboxDeleteCmd).toBe("function");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("inboxCountCmd is exported", async () => {
|
|
244
|
+
const { inboxCountCmd } = await import("../commands/inbox.js");
|
|
245
|
+
expect(typeof inboxCountCmd).toBe("function");
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("inbox options interfaces", () => {
|
|
250
|
+
test("InboxAddOptions structure", () => {
|
|
251
|
+
const options: { content: string; source?: string } = {
|
|
252
|
+
content: "Test content #tag",
|
|
253
|
+
source: "cli",
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
expect(options.content).toBeDefined();
|
|
257
|
+
expect(options.source).toBe("cli");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("InboxListOptions structure", () => {
|
|
261
|
+
const options: {
|
|
262
|
+
all?: boolean;
|
|
263
|
+
status?: "pending" | "promoted" | "discarded";
|
|
264
|
+
} = {
|
|
265
|
+
all: false,
|
|
266
|
+
status: "pending",
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
expect(options.all).toBe(false);
|
|
270
|
+
expect(options.status).toBe("pending");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("InboxPromoteOptions structure", () => {
|
|
274
|
+
const options: {
|
|
275
|
+
itemId: string;
|
|
276
|
+
projectId?: string;
|
|
277
|
+
title?: string;
|
|
278
|
+
priority?: "critical" | "high" | "medium" | "low";
|
|
279
|
+
} = {
|
|
280
|
+
itemId: "inbox_123",
|
|
281
|
+
projectId: "proj_abc",
|
|
282
|
+
title: "Promoted Task",
|
|
283
|
+
priority: "high",
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
expect(options.itemId).toBe("inbox_123");
|
|
287
|
+
expect(options.projectId).toBe("proj_abc");
|
|
288
|
+
expect(options.title).toBe("Promoted Task");
|
|
289
|
+
expect(options.priority).toBe("high");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("InboxPromoteOptions with minimal fields", () => {
|
|
293
|
+
const options: {
|
|
294
|
+
itemId: string;
|
|
295
|
+
projectId?: string;
|
|
296
|
+
title?: string;
|
|
297
|
+
priority?: "critical" | "high" | "medium" | "low";
|
|
298
|
+
} = {
|
|
299
|
+
itemId: "inbox_123",
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
expect(options.itemId).toBe("inbox_123");
|
|
303
|
+
expect(options.projectId).toBeUndefined();
|
|
304
|
+
expect(options.title).toBeUndefined();
|
|
305
|
+
expect(options.priority).toBeUndefined();
|
|
306
|
+
});
|
|
307
|
+
});
|