@aaronshaf/plane 0.1.6 → 0.1.7

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.
@@ -60,6 +60,11 @@ const server = setupServer(
60
60
  http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/states/`, () =>
61
61
  HttpResponse.json({ results: STATES }),
62
62
  ),
63
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/labels/`, () =>
64
+ HttpResponse.json({
65
+ results: [{ id: "l-bug", name: "Bug", color: "#ff0000" }],
66
+ }),
67
+ ),
63
68
  http.get(`${BASE}/api/v1/workspaces/${WS}/members/`, () =>
64
69
  HttpResponse.json(MEMBERS),
65
70
  ),
@@ -102,6 +107,161 @@ describe("issueGet", () => {
102
107
  });
103
108
  });
104
109
 
110
+ describe("issuesList", () => {
111
+ it("filters by state group", async () => {
112
+ server.use(
113
+ http.get(
114
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
115
+ () =>
116
+ HttpResponse.json({
117
+ results: [
118
+ {
119
+ id: "i-state-1",
120
+ sequence_id: 1,
121
+ name: "Done issue",
122
+ priority: "medium",
123
+ state: { id: "s-done", name: "Done", group: "completed" },
124
+ assignees: ["m-alice"],
125
+ },
126
+ {
127
+ id: "i-state-2",
128
+ sequence_id: 2,
129
+ name: "Todo issue",
130
+ priority: "medium",
131
+ state: { id: "s-todo", name: "Todo", group: "unstarted" },
132
+ assignees: ["m-bob"],
133
+ },
134
+ ],
135
+ }),
136
+ ),
137
+ );
138
+
139
+ const { issuesList } = await import("@/commands/issues");
140
+ const logs: string[] = [];
141
+ const orig = console.log;
142
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
143
+
144
+ try {
145
+ await Effect.runPromise(
146
+ (issuesList as any).handler({
147
+ project: "ACME",
148
+ state: { _tag: "Some", value: "completed" },
149
+ assignee: { _tag: "None" },
150
+ priority: { _tag: "None" },
151
+ }),
152
+ );
153
+ } finally {
154
+ console.log = orig;
155
+ }
156
+
157
+ const output = logs.join("\n");
158
+ expect(output).toContain("Done issue");
159
+ expect(output).not.toContain("Todo issue");
160
+ });
161
+
162
+ it("filters by assignee (email)", async () => {
163
+ server.use(
164
+ http.get(
165
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
166
+ () =>
167
+ HttpResponse.json({
168
+ results: [
169
+ {
170
+ id: "i-assignee-1",
171
+ sequence_id: 3,
172
+ name: "Alice issue",
173
+ priority: "medium",
174
+ state: { id: "s-done", name: "Done", group: "completed" },
175
+ assignees: ["m-alice"],
176
+ },
177
+ {
178
+ id: "i-assignee-2",
179
+ sequence_id: 4,
180
+ name: "Bob issue",
181
+ priority: "medium",
182
+ state: { id: "s-todo", name: "Todo", group: "unstarted" },
183
+ assignees: ["m-bob"],
184
+ },
185
+ ],
186
+ }),
187
+ ),
188
+ );
189
+
190
+ const { issuesList } = await import("@/commands/issues");
191
+ const logs: string[] = [];
192
+ const orig = console.log;
193
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
194
+
195
+ try {
196
+ await Effect.runPromise(
197
+ (issuesList as any).handler({
198
+ project: "ACME",
199
+ state: { _tag: "None" },
200
+ assignee: { _tag: "Some", value: "alice@example.com" },
201
+ priority: { _tag: "None" },
202
+ }),
203
+ );
204
+ } finally {
205
+ console.log = orig;
206
+ }
207
+
208
+ const output = logs.join("\n");
209
+ expect(output).toContain("Alice issue");
210
+ expect(output).not.toContain("Bob issue");
211
+ });
212
+
213
+ it("filters by priority", async () => {
214
+ server.use(
215
+ http.get(
216
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
217
+ () =>
218
+ HttpResponse.json({
219
+ results: [
220
+ {
221
+ id: "i-priority-1",
222
+ sequence_id: 5,
223
+ name: "Urgent fix",
224
+ priority: "urgent",
225
+ state: { id: "s-done", name: "Done", group: "completed" },
226
+ assignees: ["m-alice"],
227
+ },
228
+ {
229
+ id: "i-priority-2",
230
+ sequence_id: 6,
231
+ name: "Low cleanup",
232
+ priority: "low",
233
+ state: { id: "s-todo", name: "Todo", group: "unstarted" },
234
+ assignees: ["m-bob"],
235
+ },
236
+ ],
237
+ }),
238
+ ),
239
+ );
240
+
241
+ const { issuesList } = await import("@/commands/issues");
242
+ const logs: string[] = [];
243
+ const orig = console.log;
244
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
245
+
246
+ try {
247
+ await Effect.runPromise(
248
+ (issuesList as any).handler({
249
+ project: "ACME",
250
+ state: { _tag: "None" },
251
+ assignee: { _tag: "None" },
252
+ priority: { _tag: "Some", value: "urgent" },
253
+ }),
254
+ );
255
+ } finally {
256
+ console.log = orig;
257
+ }
258
+
259
+ const output = logs.join("\n");
260
+ expect(output).toContain("Urgent fix");
261
+ expect(output).not.toContain("Low cleanup");
262
+ });
263
+ });
264
+
105
265
  describe("issueUpdate", () => {
106
266
  it("updates state", async () => {
107
267
  server.use(
@@ -131,8 +291,12 @@ describe("issueUpdate", () => {
131
291
  ref: "ACME-29",
132
292
  state: { _tag: "Some", value: "completed" },
133
293
  priority: { _tag: "None" },
294
+ title: { _tag: "None" },
134
295
  description: { _tag: "None" },
135
296
  assignee: { _tag: "None" },
297
+ label: { _tag: "None" },
298
+ estimate: { _tag: "None" },
299
+
136
300
  noAssignee: false,
137
301
  }),
138
302
  );
@@ -171,8 +335,12 @@ describe("issueUpdate", () => {
171
335
  ref: "ACME-29",
172
336
  state: { _tag: "None" },
173
337
  priority: { _tag: "Some", value: "urgent" },
338
+ title: { _tag: "None" },
174
339
  description: { _tag: "None" },
175
340
  assignee: { _tag: "None" },
341
+ label: { _tag: "None" },
342
+ estimate: { _tag: "None" },
343
+
176
344
  noAssignee: false,
177
345
  }),
178
346
  );
@@ -191,8 +359,12 @@ describe("issueUpdate", () => {
191
359
  ref: "ACME-29",
192
360
  state: { _tag: "None" },
193
361
  priority: { _tag: "None" },
362
+ title: { _tag: "None" },
194
363
  description: { _tag: "None" },
195
364
  assignee: { _tag: "None" },
365
+ label: { _tag: "None" },
366
+ estimate: { _tag: "None" },
367
+
196
368
  noAssignee: false,
197
369
  }),
198
370
  ),
@@ -202,6 +374,43 @@ describe("issueUpdate", () => {
202
374
  expect((result.left as Error).message).toContain("Nothing to update");
203
375
  }
204
376
  });
377
+
378
+ it("updates title", async () => {
379
+ let patchedBody: unknown;
380
+ server.use(
381
+ http.patch(
382
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/i1/`,
383
+ async ({ request }) => {
384
+ patchedBody = await request.json();
385
+ return HttpResponse.json({
386
+ id: "i1",
387
+ sequence_id: 29,
388
+ name: "New title",
389
+ priority: "high",
390
+ state: "s1",
391
+ });
392
+ },
393
+ ),
394
+ );
395
+
396
+ const { issueUpdate } = await import("@/commands/issue");
397
+ await Effect.runPromise(
398
+ (issueUpdate as any).handler({
399
+ ref: "ACME-29",
400
+ state: { _tag: "None" },
401
+ priority: { _tag: "None" },
402
+ title: { _tag: "Some", value: "New title" },
403
+ description: { _tag: "None" },
404
+ assignee: { _tag: "None" },
405
+ label: { _tag: "None" },
406
+ estimate: { _tag: "None" },
407
+
408
+ noAssignee: false,
409
+ }),
410
+ );
411
+
412
+ expect((patchedBody as any).name).toBe("New title");
413
+ });
205
414
  });
206
415
 
207
416
  describe("issueComment", () => {
@@ -297,6 +506,8 @@ describe("issueCreate", () => {
297
506
  state: { _tag: "None" },
298
507
  description: { _tag: "None" },
299
508
  assignee: { _tag: "None" },
509
+ estimate: { _tag: "None" },
510
+ label: { _tag: "None" },
300
511
  }),
301
512
  );
302
513
  } finally {
@@ -338,6 +549,8 @@ describe("issueCreate", () => {
338
549
  state: { _tag: "Some", value: "completed" },
339
550
  description: { _tag: "None" },
340
551
  assignee: { _tag: "None" },
552
+ estimate: { _tag: "None" },
553
+ label: { _tag: "None" },
341
554
  }),
342
555
  );
343
556
  } finally {
@@ -376,6 +589,8 @@ describe("issueCreate description", () => {
376
589
  state: { _tag: "None" },
377
590
  description: { _tag: "Some", value: "Some context here" },
378
591
  assignee: { _tag: "None" },
592
+ estimate: { _tag: "None" },
593
+ label: { _tag: "None" },
379
594
  }),
380
595
  );
381
596
 
@@ -411,6 +626,8 @@ describe("issueCreate description", () => {
411
626
  state: { _tag: "None" },
412
627
  description: { _tag: "Some", value: "<script>alert(1)</script>" },
413
628
  assignee: { _tag: "None" },
629
+ estimate: { _tag: "None" },
630
+ label: { _tag: "None" },
414
631
  }),
415
632
  );
416
633
 
@@ -444,8 +661,12 @@ describe("issueUpdate description", () => {
444
661
  ref: "ACME-29",
445
662
  state: { _tag: "None" },
446
663
  priority: { _tag: "None" },
664
+ title: { _tag: "None" },
447
665
  description: { _tag: "Some", value: "Updated description" },
448
666
  assignee: { _tag: "None" },
667
+ label: { _tag: "None" },
668
+ estimate: { _tag: "None" },
669
+
449
670
  noAssignee: false,
450
671
  }),
451
672
  );
@@ -479,8 +700,12 @@ describe("issueUpdate description", () => {
479
700
  ref: "ACME-29",
480
701
  state: { _tag: "None" },
481
702
  priority: { _tag: "None" },
703
+ title: { _tag: "None" },
482
704
  description: { _tag: "Some", value: "<b>bold</b>" },
483
705
  assignee: { _tag: "None" },
706
+ label: { _tag: "None" },
707
+ estimate: { _tag: "None" },
708
+
484
709
  noAssignee: false,
485
710
  }),
486
711
  );
@@ -515,8 +740,12 @@ describe("issueUpdate assignee", () => {
515
740
  ref: "ACME-29",
516
741
  state: { _tag: "None" },
517
742
  priority: { _tag: "None" },
743
+ title: { _tag: "None" },
518
744
  description: { _tag: "None" },
519
745
  assignee: { _tag: "Some", value: "Alice" },
746
+ label: { _tag: "None" },
747
+ estimate: { _tag: "None" },
748
+
520
749
  noAssignee: false,
521
750
  }),
522
751
  );
@@ -548,8 +777,12 @@ describe("issueUpdate assignee", () => {
548
777
  ref: "ACME-29",
549
778
  state: { _tag: "None" },
550
779
  priority: { _tag: "None" },
780
+ title: { _tag: "None" },
551
781
  description: { _tag: "None" },
552
782
  assignee: { _tag: "None" },
783
+ label: { _tag: "None" },
784
+ estimate: { _tag: "None" },
785
+
553
786
  noAssignee: true,
554
787
  }),
555
788
  );
@@ -581,8 +814,12 @@ describe("issueUpdate assignee", () => {
581
814
  ref: "ACME-29",
582
815
  state: { _tag: "None" },
583
816
  priority: { _tag: "None" },
817
+ title: { _tag: "None" },
584
818
  description: { _tag: "None" },
585
819
  assignee: { _tag: "Some", value: "bob@example.com" },
820
+ label: { _tag: "None" },
821
+ estimate: { _tag: "None" },
822
+
586
823
  noAssignee: false,
587
824
  }),
588
825
  );
@@ -619,6 +856,8 @@ describe("issueCreate assignee", () => {
619
856
  state: { _tag: "None" },
620
857
  description: { _tag: "None" },
621
858
  assignee: { _tag: "Some", value: "Alice" },
859
+ estimate: { _tag: "None" },
860
+ label: { _tag: "None" },
622
861
  }),
623
862
  );
624
863
 
@@ -626,6 +865,82 @@ describe("issueCreate assignee", () => {
626
865
  });
627
866
  });
628
867
 
868
+ describe("issueUpdate label", () => {
869
+ it("sets label by name", async () => {
870
+ let patchedBody: unknown;
871
+ server.use(
872
+ http.patch(
873
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/i1/`,
874
+ async ({ request }) => {
875
+ patchedBody = await request.json();
876
+ return HttpResponse.json({
877
+ id: "i1",
878
+ sequence_id: 29,
879
+ name: "Migrate Button",
880
+ priority: "high",
881
+ state: "s1",
882
+ });
883
+ },
884
+ ),
885
+ );
886
+
887
+ const { issueUpdate } = await import("@/commands/issue");
888
+ await Effect.runPromise(
889
+ (issueUpdate as any).handler({
890
+ ref: "ACME-29",
891
+ state: { _tag: "None" },
892
+ priority: { _tag: "None" },
893
+ title: { _tag: "None" },
894
+ description: { _tag: "None" },
895
+ assignee: { _tag: "None" },
896
+ label: { _tag: "Some", value: "bug" },
897
+ estimate: { _tag: "None" },
898
+
899
+ noAssignee: false,
900
+ }),
901
+ );
902
+
903
+ expect((patchedBody as any).label_ids).toEqual(["l-bug"]);
904
+ });
905
+ });
906
+
907
+ describe("issueCreate label", () => {
908
+ it("sets label on create", async () => {
909
+ let postedBody: unknown;
910
+ server.use(
911
+ http.post(
912
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
913
+ async ({ request }) => {
914
+ postedBody = await request.json();
915
+ return HttpResponse.json({
916
+ id: "new-label",
917
+ sequence_id: 301,
918
+ name: (postedBody as any).name,
919
+ priority: "none",
920
+ state: "s1",
921
+ });
922
+ },
923
+ ),
924
+ );
925
+
926
+ const { issueCreate } = await import("@/commands/issue");
927
+ await Effect.runPromise(
928
+ (issueCreate as any).handler({
929
+ project: "ACME",
930
+ title: "Labeled issue",
931
+ priority: { _tag: "None" },
932
+ state: { _tag: "None" },
933
+ description: { _tag: "None" },
934
+ assignee: { _tag: "None" },
935
+ label: { _tag: "Some", value: "Bug" },
936
+ estimate: { _tag: "None" },
937
+ }),
938
+ );
939
+
940
+ expect((postedBody as any).label_ids).toEqual(["l-bug"]);
941
+ });
942
+ });
943
+
629
944
  describe("issueDelete", () => {
630
945
  it("deletes an issue", async () => {
631
946
  let deleted = false;
@@ -655,6 +970,81 @@ describe("issueDelete", () => {
655
970
  });
656
971
  });
657
972
 
973
+ describe("issueUpdate estimate", () => {
974
+ it("sets estimate on update", async () => {
975
+ let patchedBody: unknown;
976
+ server.use(
977
+ http.patch(
978
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/i1/`,
979
+ async ({ request }) => {
980
+ patchedBody = await request.json();
981
+ return HttpResponse.json({
982
+ id: "i1",
983
+ sequence_id: 29,
984
+ name: "Migrate Button",
985
+ priority: "high",
986
+ state: "s1",
987
+ });
988
+ },
989
+ ),
990
+ );
991
+
992
+ const { issueUpdate } = await import("@/commands/issue");
993
+ await Effect.runPromise(
994
+ (issueUpdate as any).handler({
995
+ ref: "ACME-29",
996
+ state: { _tag: "None" },
997
+ priority: { _tag: "None" },
998
+ title: { _tag: "None" },
999
+ description: { _tag: "None" },
1000
+ assignee: { _tag: "None" },
1001
+ label: { _tag: "None" },
1002
+ estimate: { _tag: "Some", value: 3 },
1003
+ noAssignee: false,
1004
+ }),
1005
+ );
1006
+
1007
+ expect((patchedBody as any).estimate_point).toBe(3);
1008
+ });
1009
+ });
1010
+
1011
+ describe("issueCreate estimate", () => {
1012
+ it("sets estimate on create", async () => {
1013
+ let postedBody: unknown;
1014
+ server.use(
1015
+ http.post(
1016
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
1017
+ async ({ request }) => {
1018
+ postedBody = await request.json();
1019
+ return HttpResponse.json({
1020
+ id: "new-est",
1021
+ sequence_id: 400,
1022
+ name: (postedBody as any).name,
1023
+ priority: "none",
1024
+ state: "s1",
1025
+ });
1026
+ },
1027
+ ),
1028
+ );
1029
+
1030
+ const { issueCreate } = await import("@/commands/issue");
1031
+ await Effect.runPromise(
1032
+ (issueCreate as any).handler({
1033
+ project: "ACME",
1034
+ title: "Estimated issue",
1035
+ priority: { _tag: "None" },
1036
+ state: { _tag: "None" },
1037
+ description: { _tag: "None" },
1038
+ assignee: { _tag: "None" },
1039
+ label: { _tag: "None" },
1040
+ estimate: { _tag: "Some", value: 5 },
1041
+ }),
1042
+ );
1043
+
1044
+ expect((postedBody as any).estimate_point).toBe(5);
1045
+ });
1046
+ });
1047
+
658
1048
  describe("--description argv parsing", () => {
659
1049
  async function runCli(argv: string[]): Promise<{ logs: string[] }> {
660
1050
  const { issue } = await import("@/commands/issue");