@aaronshaf/plane 0.1.2 → 0.1.5
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/package.json +1 -1
- package/scripts/check-coverage.ts +2 -2
- package/src/api.ts +67 -59
- package/src/app.ts +78 -0
- package/src/bin.ts +6 -70
- package/src/commands/cycles.ts +104 -80
- package/src/commands/init.ts +57 -55
- package/src/commands/intake.ts +82 -62
- package/src/commands/issue.ts +418 -305
- package/src/commands/issues.ts +51 -40
- package/src/commands/labels.ts +52 -40
- package/src/commands/members.ts +25 -16
- package/src/commands/modules.ts +136 -94
- package/src/commands/pages.ts +58 -46
- package/src/commands/projects.ts +28 -19
- package/src/commands/states.ts +31 -22
- package/src/config.ts +152 -154
- package/src/format.ts +15 -8
- package/src/output.ts +39 -0
- package/src/resolve.ts +66 -53
- package/tests/api.test.ts +178 -155
- package/tests/cycles-extended.test.ts +205 -162
- package/tests/format.test.ts +72 -54
- package/tests/helpers/mock-api.ts +16 -14
- package/tests/intake.test.ts +173 -139
- package/tests/issue-activity.test.ts +191 -158
- package/tests/issue-commands.test.ts +587 -304
- package/tests/issue-comments-worklogs.test.ts +337 -265
- package/tests/issue-links.test.ts +229 -193
- package/tests/modules.test.ts +283 -239
- package/tests/new-schemas.test.ts +203 -183
- package/tests/new-schemas2.test.ts +195 -183
- package/tests/output.test.ts +80 -0
- package/tests/pages.test.ts +122 -108
- package/tests/resolve.test.ts +186 -156
- package/tests/schemas.test.ts +215 -177
package/tests/schemas.test.ts
CHANGED
|
@@ -1,203 +1,241 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test"
|
|
2
|
-
import { Effect, Schema } from "effect"
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from "@/config"
|
|
17
|
-
|
|
18
|
-
async function decode<A, I>(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
StateSchema,
|
|
5
|
+
IssueSchema,
|
|
6
|
+
IssuesResponseSchema,
|
|
7
|
+
StatesResponseSchema,
|
|
8
|
+
ProjectSchema,
|
|
9
|
+
ProjectsResponseSchema,
|
|
10
|
+
LabelSchema,
|
|
11
|
+
LabelsResponseSchema,
|
|
12
|
+
MemberSchema,
|
|
13
|
+
MembersResponseSchema,
|
|
14
|
+
CycleSchema,
|
|
15
|
+
CyclesResponseSchema,
|
|
16
|
+
} from "@/config";
|
|
17
|
+
|
|
18
|
+
async function decode<A, I>(
|
|
19
|
+
schema: Schema.Schema<A, I>,
|
|
20
|
+
data: unknown,
|
|
21
|
+
): Promise<A> {
|
|
22
|
+
return Effect.runPromise(
|
|
23
|
+
Schema.decodeUnknown(schema)(data).pipe(
|
|
24
|
+
Effect.mapError((e) => new Error(String(e))),
|
|
25
|
+
),
|
|
26
|
+
);
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
describe("StateSchema", () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
it("decodes a valid state", async () => {
|
|
31
|
+
const state = await decode(StateSchema, {
|
|
32
|
+
id: "s1",
|
|
33
|
+
name: "In Progress",
|
|
34
|
+
group: "started",
|
|
35
|
+
});
|
|
36
|
+
expect(state.id).toBe("s1");
|
|
37
|
+
expect(state.group).toBe("started");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("accepts optional color", async () => {
|
|
41
|
+
const state = await decode(StateSchema, {
|
|
42
|
+
id: "s1",
|
|
43
|
+
name: "Done",
|
|
44
|
+
group: "completed",
|
|
45
|
+
color: "#00ff00",
|
|
46
|
+
});
|
|
47
|
+
expect(state.color).toBe("#00ff00");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("rejects missing required fields", async () => {
|
|
51
|
+
await expect(decode(StateSchema, { id: "s1" })).rejects.toThrow();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
45
54
|
|
|
46
55
|
describe("IssueSchema", () => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
const base = {
|
|
57
|
+
id: "i1",
|
|
58
|
+
sequence_id: 42,
|
|
59
|
+
name: "Fix bug",
|
|
60
|
+
priority: "high",
|
|
61
|
+
state: "uuid-state",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
it("decodes with string state", async () => {
|
|
65
|
+
const issue = await decode(IssueSchema, base);
|
|
66
|
+
expect(issue.sequence_id).toBe(42);
|
|
67
|
+
expect(issue.state).toBe("uuid-state");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("decodes with object state", async () => {
|
|
71
|
+
const issue = await decode(IssueSchema, {
|
|
72
|
+
...base,
|
|
73
|
+
state: { id: "s1", name: "In Progress", group: "started" },
|
|
74
|
+
});
|
|
75
|
+
expect(typeof issue.state).toBe("object");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("accepts null description_html", async () => {
|
|
79
|
+
const issue = await decode(IssueSchema, {
|
|
80
|
+
...base,
|
|
81
|
+
description_html: null,
|
|
82
|
+
});
|
|
83
|
+
expect(issue.description_html).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("rejects missing name", async () => {
|
|
87
|
+
await expect(
|
|
88
|
+
decode(IssueSchema, { ...base, name: undefined }),
|
|
89
|
+
).rejects.toThrow();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
78
92
|
|
|
79
93
|
describe("IssuesResponseSchema", () => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
it("decodes results array", async () => {
|
|
95
|
+
const data = {
|
|
96
|
+
results: [
|
|
97
|
+
{
|
|
98
|
+
id: "i1",
|
|
99
|
+
sequence_id: 1,
|
|
100
|
+
name: "Issue 1",
|
|
101
|
+
priority: "low",
|
|
102
|
+
state: "s1",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "i2",
|
|
106
|
+
sequence_id: 2,
|
|
107
|
+
name: "Issue 2",
|
|
108
|
+
priority: "high",
|
|
109
|
+
state: "s2",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
const resp = await decode(IssuesResponseSchema, data);
|
|
114
|
+
expect(resp.results).toHaveLength(2);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("decodes empty results", async () => {
|
|
118
|
+
const resp = await decode(IssuesResponseSchema, { results: [] });
|
|
119
|
+
expect(resp.results).toHaveLength(0);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
96
122
|
|
|
97
123
|
describe("StatesResponseSchema", () => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
})
|
|
124
|
+
it("decodes results", async () => {
|
|
125
|
+
const resp = await decode(StatesResponseSchema, {
|
|
126
|
+
results: [{ id: "s1", name: "Backlog", group: "backlog" }],
|
|
127
|
+
});
|
|
128
|
+
expect(resp.results[0].name).toBe("Backlog");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
105
131
|
|
|
106
132
|
describe("ProjectSchema", () => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
it("decodes a project", async () => {
|
|
134
|
+
const p = await decode(ProjectSchema, {
|
|
135
|
+
id: "p1",
|
|
136
|
+
identifier: "ACME",
|
|
137
|
+
name: "Acme Project",
|
|
138
|
+
});
|
|
139
|
+
expect(p.identifier).toBe("ACME");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("accepts optional description", async () => {
|
|
143
|
+
const p = await decode(ProjectSchema, {
|
|
144
|
+
id: "p1",
|
|
145
|
+
identifier: "ACME",
|
|
146
|
+
name: "InstUI",
|
|
147
|
+
description: "desc",
|
|
148
|
+
});
|
|
149
|
+
expect(p.description).toBe("desc");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
122
152
|
|
|
123
153
|
describe("ProjectsResponseSchema", () => {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
})
|
|
154
|
+
it("decodes results", async () => {
|
|
155
|
+
const resp = await decode(ProjectsResponseSchema, {
|
|
156
|
+
results: [{ id: "p1", identifier: "WEB", name: "Web Project" }],
|
|
157
|
+
});
|
|
158
|
+
expect(resp.results[0].identifier).toBe("WEB");
|
|
159
|
+
});
|
|
160
|
+
});
|
|
131
161
|
|
|
132
162
|
describe("LabelSchema", () => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
it("decodes a label", async () => {
|
|
164
|
+
const label = await decode(LabelSchema, { id: "l1", name: "bug" });
|
|
165
|
+
expect(label.name).toBe("bug");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("accepts null color", async () => {
|
|
169
|
+
const label = await decode(LabelSchema, {
|
|
170
|
+
id: "l1",
|
|
171
|
+
name: "bug",
|
|
172
|
+
color: null,
|
|
173
|
+
});
|
|
174
|
+
expect(label.color).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
143
177
|
|
|
144
178
|
describe("LabelsResponseSchema", () => {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
})
|
|
179
|
+
it("decodes results", async () => {
|
|
180
|
+
const resp = await decode(LabelsResponseSchema, {
|
|
181
|
+
results: [{ id: "l1", name: "bug" }],
|
|
182
|
+
});
|
|
183
|
+
expect(resp.results).toHaveLength(1);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
152
186
|
|
|
153
187
|
describe("MemberSchema", () => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
188
|
+
it("decodes a member", async () => {
|
|
189
|
+
const m = await decode(MemberSchema, {
|
|
190
|
+
id: "u1",
|
|
191
|
+
display_name: "Aaron",
|
|
192
|
+
email: "aaron@example.com",
|
|
193
|
+
});
|
|
194
|
+
expect(m.display_name).toBe("Aaron");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("accepts null email", async () => {
|
|
198
|
+
const m = await decode(MemberSchema, {
|
|
199
|
+
id: "u1",
|
|
200
|
+
display_name: "Aaron",
|
|
201
|
+
email: null,
|
|
202
|
+
});
|
|
203
|
+
expect(m.email).toBeNull();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
168
206
|
|
|
169
207
|
describe("MembersResponseSchema (flat array)", () => {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
})
|
|
208
|
+
it("decodes a flat array", async () => {
|
|
209
|
+
const members = await decode(MembersResponseSchema, [
|
|
210
|
+
{ id: "u1", display_name: "Aaron" },
|
|
211
|
+
{ id: "u2", display_name: "Bea" },
|
|
212
|
+
]);
|
|
213
|
+
expect(members).toHaveLength(2);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
178
216
|
|
|
179
217
|
describe("CycleSchema", () => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
})
|
|
218
|
+
it("decodes a cycle", async () => {
|
|
219
|
+
const c = await decode(CycleSchema, { id: "c1", name: "Sprint 1" });
|
|
220
|
+
expect(c.name).toBe("Sprint 1");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("accepts optional dates", async () => {
|
|
224
|
+
const c = await decode(CycleSchema, {
|
|
225
|
+
id: "c1",
|
|
226
|
+
name: "Sprint 1",
|
|
227
|
+
start_date: "2025-01-01",
|
|
228
|
+
end_date: "2025-01-14",
|
|
229
|
+
});
|
|
230
|
+
expect(c.start_date).toBe("2025-01-01");
|
|
231
|
+
});
|
|
232
|
+
});
|
|
195
233
|
|
|
196
234
|
describe("CyclesResponseSchema", () => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
})
|
|
235
|
+
it("decodes results", async () => {
|
|
236
|
+
const resp = await decode(CyclesResponseSchema, {
|
|
237
|
+
results: [{ id: "c1", name: "Sprint 1" }],
|
|
238
|
+
});
|
|
239
|
+
expect(resp.results[0].id).toBe("c1");
|
|
240
|
+
});
|
|
241
|
+
});
|