@aaronshaf/plane 0.1.7 → 0.1.10
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/src/commands/cycles.ts +86 -63
- package/src/commands/intake.ts +64 -44
- package/src/commands/issue.ts +386 -301
- package/src/commands/issues.ts +58 -45
- package/src/commands/labels.ts +6 -2
- package/src/commands/modules.ts +108 -74
- package/src/commands/pages.ts +42 -30
- package/src/config.ts +1 -1
- package/src/output.ts +12 -9
- package/src/resolve.ts +17 -5
- package/tests/api.test.ts +12 -8
- package/tests/cycles-extended.test.ts +15 -15
- package/tests/intake.test.ts +12 -12
- package/tests/issue-activity.test.ts +8 -16
- package/tests/issue-commands.test.ts +212 -278
- package/tests/issue-comments-worklogs.test.ts +31 -38
- package/tests/issue-links.test.ts +20 -23
- package/tests/modules.test.ts +17 -23
- package/tests/output.test.ts +11 -0
- package/tests/pages.test.ts +8 -8
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "bun:test";
|
|
10
10
|
import { Command } from "@effect/cli";
|
|
11
11
|
import { NodeContext } from "@effect/platform-node";
|
|
12
|
-
import { Effect, Layer } from "effect";
|
|
12
|
+
import { Effect, Layer, Option } from "effect";
|
|
13
13
|
import { http, HttpResponse } from "msw";
|
|
14
14
|
import { setupServer } from "msw/node";
|
|
15
15
|
import { _clearProjectCache } from "@/resolve";
|
|
@@ -65,6 +65,7 @@ const server = setupServer(
|
|
|
65
65
|
results: [{ id: "l-bug", name: "Bug", color: "#ff0000" }],
|
|
66
66
|
}),
|
|
67
67
|
),
|
|
68
|
+
|
|
68
69
|
http.get(`${BASE}/api/v1/workspaces/${WS}/members/`, () =>
|
|
69
70
|
HttpResponse.json(MEMBERS),
|
|
70
71
|
),
|
|
@@ -89,13 +90,13 @@ afterEach(() => {
|
|
|
89
90
|
|
|
90
91
|
describe("issueGet", () => {
|
|
91
92
|
it("prints full JSON for an issue", async () => {
|
|
92
|
-
const {
|
|
93
|
+
const { issueGetHandler } = await import("@/commands/issue");
|
|
93
94
|
const logs: string[] = [];
|
|
94
95
|
const orig = console.log;
|
|
95
96
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
96
97
|
|
|
97
98
|
try {
|
|
98
|
-
await Effect.runPromise((
|
|
99
|
+
await Effect.runPromise(issueGetHandler({ ref: "ACME-29" }));
|
|
99
100
|
} finally {
|
|
100
101
|
console.log = orig;
|
|
101
102
|
}
|
|
@@ -136,18 +137,18 @@ describe("issuesList", () => {
|
|
|
136
137
|
),
|
|
137
138
|
);
|
|
138
139
|
|
|
139
|
-
const {
|
|
140
|
+
const { issuesListHandler } = await import("@/commands/issues");
|
|
140
141
|
const logs: string[] = [];
|
|
141
142
|
const orig = console.log;
|
|
142
143
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
143
144
|
|
|
144
145
|
try {
|
|
145
146
|
await Effect.runPromise(
|
|
146
|
-
(
|
|
147
|
+
issuesListHandler({
|
|
147
148
|
project: "ACME",
|
|
148
|
-
state:
|
|
149
|
-
assignee:
|
|
150
|
-
priority:
|
|
149
|
+
state: Option.some("completed"),
|
|
150
|
+
assignee: Option.none(),
|
|
151
|
+
priority: Option.none(),
|
|
151
152
|
}),
|
|
152
153
|
);
|
|
153
154
|
} finally {
|
|
@@ -187,18 +188,18 @@ describe("issuesList", () => {
|
|
|
187
188
|
),
|
|
188
189
|
);
|
|
189
190
|
|
|
190
|
-
const {
|
|
191
|
+
const { issuesListHandler } = await import("@/commands/issues");
|
|
191
192
|
const logs: string[] = [];
|
|
192
193
|
const orig = console.log;
|
|
193
194
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
194
195
|
|
|
195
196
|
try {
|
|
196
197
|
await Effect.runPromise(
|
|
197
|
-
(
|
|
198
|
+
issuesListHandler({
|
|
198
199
|
project: "ACME",
|
|
199
|
-
state:
|
|
200
|
-
assignee:
|
|
201
|
-
priority:
|
|
200
|
+
state: Option.none(),
|
|
201
|
+
assignee: Option.some("alice@example.com"),
|
|
202
|
+
priority: Option.none(),
|
|
202
203
|
}),
|
|
203
204
|
);
|
|
204
205
|
} finally {
|
|
@@ -238,18 +239,18 @@ describe("issuesList", () => {
|
|
|
238
239
|
),
|
|
239
240
|
);
|
|
240
241
|
|
|
241
|
-
const {
|
|
242
|
+
const { issuesListHandler } = await import("@/commands/issues");
|
|
242
243
|
const logs: string[] = [];
|
|
243
244
|
const orig = console.log;
|
|
244
245
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
245
246
|
|
|
246
247
|
try {
|
|
247
248
|
await Effect.runPromise(
|
|
248
|
-
(
|
|
249
|
+
issuesListHandler({
|
|
249
250
|
project: "ACME",
|
|
250
|
-
state:
|
|
251
|
-
assignee:
|
|
252
|
-
priority:
|
|
251
|
+
state: Option.none(),
|
|
252
|
+
assignee: Option.none(),
|
|
253
|
+
priority: Option.some("urgent"),
|
|
253
254
|
}),
|
|
254
255
|
);
|
|
255
256
|
} finally {
|
|
@@ -268,7 +269,7 @@ describe("issueUpdate", () => {
|
|
|
268
269
|
http.patch(
|
|
269
270
|
`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/i1/`,
|
|
270
271
|
async ({ request }) => {
|
|
271
|
-
const body = (await request.json()) as
|
|
272
|
+
const body = (await request.json()) as { state?: string };
|
|
272
273
|
return HttpResponse.json({
|
|
273
274
|
id: "i1",
|
|
274
275
|
sequence_id: 29,
|
|
@@ -280,23 +281,21 @@ describe("issueUpdate", () => {
|
|
|
280
281
|
),
|
|
281
282
|
);
|
|
282
283
|
|
|
283
|
-
const {
|
|
284
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
284
285
|
const logs: string[] = [];
|
|
285
286
|
const orig = console.log;
|
|
286
287
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
287
288
|
|
|
288
289
|
try {
|
|
289
290
|
await Effect.runPromise(
|
|
290
|
-
(
|
|
291
|
+
issueUpdateHandler({
|
|
291
292
|
ref: "ACME-29",
|
|
292
|
-
state:
|
|
293
|
-
priority:
|
|
294
|
-
title:
|
|
295
|
-
description:
|
|
296
|
-
assignee:
|
|
297
|
-
label:
|
|
298
|
-
estimate: { _tag: "None" },
|
|
299
|
-
|
|
293
|
+
state: Option.some("completed"),
|
|
294
|
+
priority: Option.none(),
|
|
295
|
+
title: Option.none(),
|
|
296
|
+
description: Option.none(),
|
|
297
|
+
assignee: Option.none(),
|
|
298
|
+
label: Option.none(),
|
|
300
299
|
noAssignee: false,
|
|
301
300
|
}),
|
|
302
301
|
);
|
|
@@ -312,7 +311,7 @@ describe("issueUpdate", () => {
|
|
|
312
311
|
http.patch(
|
|
313
312
|
`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/i1/`,
|
|
314
313
|
async ({ request }) => {
|
|
315
|
-
const body = (await request.json()) as
|
|
314
|
+
const body = (await request.json()) as { priority?: string };
|
|
316
315
|
return HttpResponse.json({
|
|
317
316
|
id: "i1",
|
|
318
317
|
sequence_id: 29,
|
|
@@ -324,23 +323,21 @@ describe("issueUpdate", () => {
|
|
|
324
323
|
),
|
|
325
324
|
);
|
|
326
325
|
|
|
327
|
-
const {
|
|
326
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
328
327
|
const logs: string[] = [];
|
|
329
328
|
const orig = console.log;
|
|
330
329
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
331
330
|
|
|
332
331
|
try {
|
|
333
332
|
await Effect.runPromise(
|
|
334
|
-
(
|
|
333
|
+
issueUpdateHandler({
|
|
335
334
|
ref: "ACME-29",
|
|
336
|
-
state:
|
|
337
|
-
priority:
|
|
338
|
-
title:
|
|
339
|
-
description:
|
|
340
|
-
assignee:
|
|
341
|
-
label:
|
|
342
|
-
estimate: { _tag: "None" },
|
|
343
|
-
|
|
335
|
+
state: Option.none(),
|
|
336
|
+
priority: Option.some("urgent"),
|
|
337
|
+
title: Option.none(),
|
|
338
|
+
description: Option.none(),
|
|
339
|
+
assignee: Option.none(),
|
|
340
|
+
label: Option.none(),
|
|
344
341
|
noAssignee: false,
|
|
345
342
|
}),
|
|
346
343
|
);
|
|
@@ -352,19 +349,17 @@ describe("issueUpdate", () => {
|
|
|
352
349
|
});
|
|
353
350
|
|
|
354
351
|
it("fails when nothing to update", async () => {
|
|
355
|
-
const {
|
|
352
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
356
353
|
const result = await Effect.runPromise(
|
|
357
354
|
Effect.either(
|
|
358
|
-
(
|
|
355
|
+
issueUpdateHandler({
|
|
359
356
|
ref: "ACME-29",
|
|
360
|
-
state:
|
|
361
|
-
priority:
|
|
362
|
-
title:
|
|
363
|
-
description:
|
|
364
|
-
assignee:
|
|
365
|
-
label:
|
|
366
|
-
estimate: { _tag: "None" },
|
|
367
|
-
|
|
357
|
+
state: Option.none(),
|
|
358
|
+
priority: Option.none(),
|
|
359
|
+
title: Option.none(),
|
|
360
|
+
description: Option.none(),
|
|
361
|
+
assignee: Option.none(),
|
|
362
|
+
label: Option.none(),
|
|
368
363
|
noAssignee: false,
|
|
369
364
|
}),
|
|
370
365
|
),
|
|
@@ -393,23 +388,21 @@ describe("issueUpdate", () => {
|
|
|
393
388
|
),
|
|
394
389
|
);
|
|
395
390
|
|
|
396
|
-
const {
|
|
391
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
397
392
|
await Effect.runPromise(
|
|
398
|
-
(
|
|
393
|
+
issueUpdateHandler({
|
|
399
394
|
ref: "ACME-29",
|
|
400
|
-
state:
|
|
401
|
-
priority:
|
|
402
|
-
title:
|
|
403
|
-
description:
|
|
404
|
-
assignee:
|
|
405
|
-
label:
|
|
406
|
-
estimate: { _tag: "None" },
|
|
407
|
-
|
|
395
|
+
state: Option.none(),
|
|
396
|
+
priority: Option.none(),
|
|
397
|
+
title: Option.some("New title"),
|
|
398
|
+
description: Option.none(),
|
|
399
|
+
assignee: Option.none(),
|
|
400
|
+
label: Option.none(),
|
|
408
401
|
noAssignee: false,
|
|
409
402
|
}),
|
|
410
403
|
);
|
|
411
404
|
|
|
412
|
-
expect((patchedBody as
|
|
405
|
+
expect((patchedBody as { name?: string }).name).toBe("New title");
|
|
413
406
|
});
|
|
414
407
|
});
|
|
415
408
|
|
|
@@ -426,14 +419,14 @@ describe("issueComment", () => {
|
|
|
426
419
|
),
|
|
427
420
|
);
|
|
428
421
|
|
|
429
|
-
const {
|
|
422
|
+
const { issueCommentHandler } = await import("@/commands/issue");
|
|
430
423
|
const logs: string[] = [];
|
|
431
424
|
const orig = console.log;
|
|
432
425
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
433
426
|
|
|
434
427
|
try {
|
|
435
428
|
await Effect.runPromise(
|
|
436
|
-
(
|
|
429
|
+
issueCommentHandler({
|
|
437
430
|
ref: "ACME-29",
|
|
438
431
|
text: "Fixed in latest build",
|
|
439
432
|
}),
|
|
@@ -442,7 +435,9 @@ describe("issueComment", () => {
|
|
|
442
435
|
console.log = orig;
|
|
443
436
|
}
|
|
444
437
|
|
|
445
|
-
expect((postedBody as
|
|
438
|
+
expect((postedBody as { comment_html?: string }).comment_html).toContain(
|
|
439
|
+
"Fixed in latest build",
|
|
440
|
+
);
|
|
446
441
|
expect(logs.join("\n")).toContain("Comment added to ACME-29");
|
|
447
442
|
});
|
|
448
443
|
|
|
@@ -458,10 +453,10 @@ describe("issueComment", () => {
|
|
|
458
453
|
),
|
|
459
454
|
);
|
|
460
455
|
|
|
461
|
-
const {
|
|
456
|
+
const { issueCommentHandler } = await import("@/commands/issue");
|
|
462
457
|
try {
|
|
463
458
|
await Effect.runPromise(
|
|
464
|
-
(
|
|
459
|
+
issueCommentHandler({
|
|
465
460
|
ref: "ACME-29",
|
|
466
461
|
text: "<script>alert(1)</script>",
|
|
467
462
|
}),
|
|
@@ -469,8 +464,12 @@ describe("issueComment", () => {
|
|
|
469
464
|
} finally {
|
|
470
465
|
}
|
|
471
466
|
|
|
472
|
-
expect((postedBody as
|
|
473
|
-
|
|
467
|
+
expect((postedBody as { comment_html?: string }).comment_html).toContain(
|
|
468
|
+
"<script>",
|
|
469
|
+
);
|
|
470
|
+
expect(
|
|
471
|
+
(postedBody as { comment_html?: string }).comment_html,
|
|
472
|
+
).not.toContain("<script>");
|
|
474
473
|
});
|
|
475
474
|
});
|
|
476
475
|
|
|
@@ -480,7 +479,7 @@ describe("issueCreate", () => {
|
|
|
480
479
|
http.post(
|
|
481
480
|
`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
|
|
482
481
|
async ({ request }) => {
|
|
483
|
-
const body = (await request.json()) as
|
|
482
|
+
const body = (await request.json()) as { name?: string };
|
|
484
483
|
return HttpResponse.json({
|
|
485
484
|
id: "new-i",
|
|
486
485
|
sequence_id: 99,
|
|
@@ -492,22 +491,21 @@ describe("issueCreate", () => {
|
|
|
492
491
|
),
|
|
493
492
|
);
|
|
494
493
|
|
|
495
|
-
const {
|
|
494
|
+
const { issueCreateHandler } = await import("@/commands/issue");
|
|
496
495
|
const logs: string[] = [];
|
|
497
496
|
const orig = console.log;
|
|
498
497
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
499
498
|
|
|
500
499
|
try {
|
|
501
500
|
await Effect.runPromise(
|
|
502
|
-
(
|
|
501
|
+
issueCreateHandler({
|
|
503
502
|
project: "ACME",
|
|
504
503
|
title: "New issue",
|
|
505
|
-
priority:
|
|
506
|
-
state:
|
|
507
|
-
description:
|
|
508
|
-
assignee:
|
|
509
|
-
|
|
510
|
-
label: { _tag: "None" },
|
|
504
|
+
priority: Option.none(),
|
|
505
|
+
state: Option.none(),
|
|
506
|
+
description: Option.none(),
|
|
507
|
+
assignee: Option.none(),
|
|
508
|
+
label: Option.none(),
|
|
511
509
|
}),
|
|
512
510
|
);
|
|
513
511
|
} finally {
|
|
@@ -523,7 +521,11 @@ describe("issueCreate", () => {
|
|
|
523
521
|
http.post(
|
|
524
522
|
`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`,
|
|
525
523
|
async ({ request }) => {
|
|
526
|
-
const body = (await request.json()) as
|
|
524
|
+
const body = (await request.json()) as {
|
|
525
|
+
name?: string;
|
|
526
|
+
priority?: string;
|
|
527
|
+
state?: string;
|
|
528
|
+
};
|
|
527
529
|
return HttpResponse.json({
|
|
528
530
|
id: "new-i2",
|
|
529
531
|
sequence_id: 100,
|
|
@@ -535,22 +537,21 @@ describe("issueCreate", () => {
|
|
|
535
537
|
),
|
|
536
538
|
);
|
|
537
539
|
|
|
538
|
-
const {
|
|
540
|
+
const { issueCreateHandler } = await import("@/commands/issue");
|
|
539
541
|
const logs: string[] = [];
|
|
540
542
|
const orig = console.log;
|
|
541
543
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
542
544
|
|
|
543
545
|
try {
|
|
544
546
|
await Effect.runPromise(
|
|
545
|
-
(
|
|
547
|
+
issueCreateHandler({
|
|
546
548
|
project: "ACME",
|
|
547
549
|
title: "High priority issue",
|
|
548
|
-
priority:
|
|
549
|
-
state:
|
|
550
|
-
description:
|
|
551
|
-
assignee:
|
|
552
|
-
|
|
553
|
-
label: { _tag: "None" },
|
|
550
|
+
priority: Option.some("high"),
|
|
551
|
+
state: Option.some("completed"),
|
|
552
|
+
description: Option.none(),
|
|
553
|
+
assignee: Option.none(),
|
|
554
|
+
label: Option.none(),
|
|
554
555
|
}),
|
|
555
556
|
);
|
|
556
557
|
} finally {
|
|
@@ -572,7 +573,7 @@ describe("issueCreate description", () => {
|
|
|
572
573
|
return HttpResponse.json({
|
|
573
574
|
id: "new-i3",
|
|
574
575
|
sequence_id: 101,
|
|
575
|
-
name: (postedBody as
|
|
576
|
+
name: (postedBody as { name?: string }).name,
|
|
576
577
|
priority: "none",
|
|
577
578
|
state: "s1",
|
|
578
579
|
});
|
|
@@ -580,21 +581,20 @@ describe("issueCreate description", () => {
|
|
|
580
581
|
),
|
|
581
582
|
);
|
|
582
583
|
|
|
583
|
-
const {
|
|
584
|
+
const { issueCreateHandler } = await import("@/commands/issue");
|
|
584
585
|
await Effect.runPromise(
|
|
585
|
-
(
|
|
586
|
+
issueCreateHandler({
|
|
586
587
|
project: "ACME",
|
|
587
588
|
title: "Issue with description",
|
|
588
|
-
priority:
|
|
589
|
-
state:
|
|
590
|
-
description:
|
|
591
|
-
assignee:
|
|
592
|
-
|
|
593
|
-
label: { _tag: "None" },
|
|
589
|
+
priority: Option.none(),
|
|
590
|
+
state: Option.none(),
|
|
591
|
+
description: Option.some("Some context here"),
|
|
592
|
+
assignee: Option.none(),
|
|
593
|
+
label: Option.none(),
|
|
594
594
|
}),
|
|
595
595
|
);
|
|
596
596
|
|
|
597
|
-
expect((postedBody as
|
|
597
|
+
expect((postedBody as { description_html?: string }).description_html).toBe(
|
|
598
598
|
"<p>Some context here</p>",
|
|
599
599
|
);
|
|
600
600
|
});
|
|
@@ -609,7 +609,7 @@ describe("issueCreate description", () => {
|
|
|
609
609
|
return HttpResponse.json({
|
|
610
610
|
id: "new-i4",
|
|
611
611
|
sequence_id: 102,
|
|
612
|
-
name: (postedBody as
|
|
612
|
+
name: (postedBody as { name?: string }).name,
|
|
613
613
|
priority: "none",
|
|
614
614
|
state: "s1",
|
|
615
615
|
});
|
|
@@ -617,22 +617,25 @@ describe("issueCreate description", () => {
|
|
|
617
617
|
),
|
|
618
618
|
);
|
|
619
619
|
|
|
620
|
-
const {
|
|
620
|
+
const { issueCreateHandler } = await import("@/commands/issue");
|
|
621
621
|
await Effect.runPromise(
|
|
622
|
-
(
|
|
622
|
+
issueCreateHandler({
|
|
623
623
|
project: "ACME",
|
|
624
624
|
title: "XSS test",
|
|
625
|
-
priority:
|
|
626
|
-
state:
|
|
627
|
-
description:
|
|
628
|
-
assignee:
|
|
629
|
-
|
|
630
|
-
label: { _tag: "None" },
|
|
625
|
+
priority: Option.none(),
|
|
626
|
+
state: Option.none(),
|
|
627
|
+
description: Option.some("<script>alert(1)</script>"),
|
|
628
|
+
assignee: Option.none(),
|
|
629
|
+
label: Option.none(),
|
|
631
630
|
}),
|
|
632
631
|
);
|
|
633
632
|
|
|
634
|
-
expect(
|
|
635
|
-
|
|
633
|
+
expect(
|
|
634
|
+
(postedBody as { description_html?: string }).description_html,
|
|
635
|
+
).toContain("<script>");
|
|
636
|
+
expect(
|
|
637
|
+
(postedBody as { description_html?: string }).description_html,
|
|
638
|
+
).not.toContain("<script>");
|
|
636
639
|
});
|
|
637
640
|
});
|
|
638
641
|
|
|
@@ -655,25 +658,23 @@ describe("issueUpdate description", () => {
|
|
|
655
658
|
),
|
|
656
659
|
);
|
|
657
660
|
|
|
658
|
-
const {
|
|
661
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
659
662
|
await Effect.runPromise(
|
|
660
|
-
(
|
|
663
|
+
issueUpdateHandler({
|
|
661
664
|
ref: "ACME-29",
|
|
662
|
-
state:
|
|
663
|
-
priority:
|
|
664
|
-
title:
|
|
665
|
-
description:
|
|
666
|
-
assignee:
|
|
667
|
-
label:
|
|
668
|
-
estimate: { _tag: "None" },
|
|
669
|
-
|
|
665
|
+
state: Option.none(),
|
|
666
|
+
priority: Option.none(),
|
|
667
|
+
title: Option.none(),
|
|
668
|
+
description: Option.some("Updated description"),
|
|
669
|
+
assignee: Option.none(),
|
|
670
|
+
label: Option.none(),
|
|
670
671
|
noAssignee: false,
|
|
671
672
|
}),
|
|
672
673
|
);
|
|
673
674
|
|
|
674
|
-
expect(
|
|
675
|
-
|
|
676
|
-
);
|
|
675
|
+
expect(
|
|
676
|
+
(patchedBody as { description_html?: string }).description_html,
|
|
677
|
+
).toBe("<p>Updated description</p>");
|
|
677
678
|
});
|
|
678
679
|
|
|
679
680
|
it("HTML-escapes angle brackets in update description", async () => {
|
|
@@ -694,24 +695,26 @@ describe("issueUpdate description", () => {
|
|
|
694
695
|
),
|
|
695
696
|
);
|
|
696
697
|
|
|
697
|
-
const {
|
|
698
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
698
699
|
await Effect.runPromise(
|
|
699
|
-
(
|
|
700
|
+
issueUpdateHandler({
|
|
700
701
|
ref: "ACME-29",
|
|
701
|
-
state:
|
|
702
|
-
priority:
|
|
703
|
-
title:
|
|
704
|
-
description:
|
|
705
|
-
assignee:
|
|
706
|
-
label:
|
|
707
|
-
estimate: { _tag: "None" },
|
|
708
|
-
|
|
702
|
+
state: Option.none(),
|
|
703
|
+
priority: Option.none(),
|
|
704
|
+
title: Option.none(),
|
|
705
|
+
description: Option.some("<b>bold</b>"),
|
|
706
|
+
assignee: Option.none(),
|
|
707
|
+
label: Option.none(),
|
|
709
708
|
noAssignee: false,
|
|
710
709
|
}),
|
|
711
710
|
);
|
|
712
711
|
|
|
713
|
-
expect(
|
|
714
|
-
|
|
712
|
+
expect(
|
|
713
|
+
(patchedBody as { description_html?: string }).description_html,
|
|
714
|
+
).toContain("<b>");
|
|
715
|
+
expect(
|
|
716
|
+
(patchedBody as { description_html?: string }).description_html,
|
|
717
|
+
).not.toContain("<b>");
|
|
715
718
|
});
|
|
716
719
|
});
|
|
717
720
|
|
|
@@ -734,23 +737,23 @@ describe("issueUpdate assignee", () => {
|
|
|
734
737
|
),
|
|
735
738
|
);
|
|
736
739
|
|
|
737
|
-
const {
|
|
740
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
738
741
|
await Effect.runPromise(
|
|
739
|
-
(
|
|
742
|
+
issueUpdateHandler({
|
|
740
743
|
ref: "ACME-29",
|
|
741
|
-
state:
|
|
742
|
-
priority:
|
|
743
|
-
title:
|
|
744
|
-
description:
|
|
745
|
-
assignee:
|
|
746
|
-
label:
|
|
747
|
-
estimate: { _tag: "None" },
|
|
748
|
-
|
|
744
|
+
state: Option.none(),
|
|
745
|
+
priority: Option.none(),
|
|
746
|
+
title: Option.none(),
|
|
747
|
+
description: Option.none(),
|
|
748
|
+
assignee: Option.some("Alice"),
|
|
749
|
+
label: Option.none(),
|
|
749
750
|
noAssignee: false,
|
|
750
751
|
}),
|
|
751
752
|
);
|
|
752
753
|
|
|
753
|
-
expect((patchedBody as
|
|
754
|
+
expect((patchedBody as { assignees?: string[] }).assignees).toEqual([
|
|
755
|
+
"m-alice",
|
|
756
|
+
]);
|
|
754
757
|
});
|
|
755
758
|
|
|
756
759
|
it("clears assignees with --no-assignee", async () => {
|
|
@@ -771,23 +774,21 @@ describe("issueUpdate assignee", () => {
|
|
|
771
774
|
),
|
|
772
775
|
);
|
|
773
776
|
|
|
774
|
-
const {
|
|
777
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
775
778
|
await Effect.runPromise(
|
|
776
|
-
(
|
|
779
|
+
issueUpdateHandler({
|
|
777
780
|
ref: "ACME-29",
|
|
778
|
-
state:
|
|
779
|
-
priority:
|
|
780
|
-
title:
|
|
781
|
-
description:
|
|
782
|
-
assignee:
|
|
783
|
-
label:
|
|
784
|
-
estimate: { _tag: "None" },
|
|
785
|
-
|
|
781
|
+
state: Option.none(),
|
|
782
|
+
priority: Option.none(),
|
|
783
|
+
title: Option.none(),
|
|
784
|
+
description: Option.none(),
|
|
785
|
+
assignee: Option.none(),
|
|
786
|
+
label: Option.none(),
|
|
786
787
|
noAssignee: true,
|
|
787
788
|
}),
|
|
788
789
|
);
|
|
789
790
|
|
|
790
|
-
expect((patchedBody as
|
|
791
|
+
expect((patchedBody as { assignees?: string[] }).assignees).toEqual([]);
|
|
791
792
|
});
|
|
792
793
|
|
|
793
794
|
it("resolves assignee by email", async () => {
|
|
@@ -808,23 +809,23 @@ describe("issueUpdate assignee", () => {
|
|
|
808
809
|
),
|
|
809
810
|
);
|
|
810
811
|
|
|
811
|
-
const {
|
|
812
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
812
813
|
await Effect.runPromise(
|
|
813
|
-
(
|
|
814
|
+
issueUpdateHandler({
|
|
814
815
|
ref: "ACME-29",
|
|
815
|
-
state:
|
|
816
|
-
priority:
|
|
817
|
-
title:
|
|
818
|
-
description:
|
|
819
|
-
assignee:
|
|
820
|
-
label:
|
|
821
|
-
estimate: { _tag: "None" },
|
|
822
|
-
|
|
816
|
+
state: Option.none(),
|
|
817
|
+
priority: Option.none(),
|
|
818
|
+
title: Option.none(),
|
|
819
|
+
description: Option.none(),
|
|
820
|
+
assignee: Option.some("bob@example.com"),
|
|
821
|
+
label: Option.none(),
|
|
823
822
|
noAssignee: false,
|
|
824
823
|
}),
|
|
825
824
|
);
|
|
826
825
|
|
|
827
|
-
expect((patchedBody as
|
|
826
|
+
expect((patchedBody as { assignees?: string[] }).assignees).toEqual([
|
|
827
|
+
"m-bob",
|
|
828
|
+
]);
|
|
828
829
|
});
|
|
829
830
|
});
|
|
830
831
|
|
|
@@ -839,7 +840,7 @@ describe("issueCreate assignee", () => {
|
|
|
839
840
|
return HttpResponse.json({
|
|
840
841
|
id: "new-assignee",
|
|
841
842
|
sequence_id: 300,
|
|
842
|
-
name: (postedBody as
|
|
843
|
+
name: (postedBody as { name?: string }).name,
|
|
843
844
|
priority: "none",
|
|
844
845
|
state: "s1",
|
|
845
846
|
});
|
|
@@ -847,21 +848,22 @@ describe("issueCreate assignee", () => {
|
|
|
847
848
|
),
|
|
848
849
|
);
|
|
849
850
|
|
|
850
|
-
const {
|
|
851
|
+
const { issueCreateHandler } = await import("@/commands/issue");
|
|
851
852
|
await Effect.runPromise(
|
|
852
|
-
(
|
|
853
|
+
issueCreateHandler({
|
|
853
854
|
project: "ACME",
|
|
854
855
|
title: "Assigned issue",
|
|
855
|
-
priority:
|
|
856
|
-
state:
|
|
857
|
-
description:
|
|
858
|
-
assignee:
|
|
859
|
-
|
|
860
|
-
label: { _tag: "None" },
|
|
856
|
+
priority: Option.none(),
|
|
857
|
+
state: Option.none(),
|
|
858
|
+
description: Option.none(),
|
|
859
|
+
assignee: Option.some("Alice"),
|
|
860
|
+
label: Option.none(),
|
|
861
861
|
}),
|
|
862
862
|
);
|
|
863
863
|
|
|
864
|
-
expect((postedBody as
|
|
864
|
+
expect((postedBody as { assignees?: string[] }).assignees).toEqual([
|
|
865
|
+
"m-alice",
|
|
866
|
+
]);
|
|
865
867
|
});
|
|
866
868
|
});
|
|
867
869
|
|
|
@@ -884,23 +886,23 @@ describe("issueUpdate label", () => {
|
|
|
884
886
|
),
|
|
885
887
|
);
|
|
886
888
|
|
|
887
|
-
const {
|
|
889
|
+
const { issueUpdateHandler } = await import("@/commands/issue");
|
|
888
890
|
await Effect.runPromise(
|
|
889
|
-
(
|
|
891
|
+
issueUpdateHandler({
|
|
890
892
|
ref: "ACME-29",
|
|
891
|
-
state:
|
|
892
|
-
priority:
|
|
893
|
-
title:
|
|
894
|
-
description:
|
|
895
|
-
assignee:
|
|
896
|
-
label:
|
|
897
|
-
estimate: { _tag: "None" },
|
|
898
|
-
|
|
893
|
+
state: Option.none(),
|
|
894
|
+
priority: Option.none(),
|
|
895
|
+
title: Option.none(),
|
|
896
|
+
description: Option.none(),
|
|
897
|
+
assignee: Option.none(),
|
|
898
|
+
label: Option.some("bug"),
|
|
899
899
|
noAssignee: false,
|
|
900
900
|
}),
|
|
901
901
|
);
|
|
902
902
|
|
|
903
|
-
expect((patchedBody as
|
|
903
|
+
expect((patchedBody as { label_ids?: string[] }).label_ids).toEqual([
|
|
904
|
+
"l-bug",
|
|
905
|
+
]);
|
|
904
906
|
});
|
|
905
907
|
});
|
|
906
908
|
|
|
@@ -915,7 +917,7 @@ describe("issueCreate label", () => {
|
|
|
915
917
|
return HttpResponse.json({
|
|
916
918
|
id: "new-label",
|
|
917
919
|
sequence_id: 301,
|
|
918
|
-
name: (postedBody as
|
|
920
|
+
name: (postedBody as { name?: string }).name,
|
|
919
921
|
priority: "none",
|
|
920
922
|
state: "s1",
|
|
921
923
|
});
|
|
@@ -923,21 +925,22 @@ describe("issueCreate label", () => {
|
|
|
923
925
|
),
|
|
924
926
|
);
|
|
925
927
|
|
|
926
|
-
const {
|
|
928
|
+
const { issueCreateHandler } = await import("@/commands/issue");
|
|
927
929
|
await Effect.runPromise(
|
|
928
|
-
(
|
|
930
|
+
issueCreateHandler({
|
|
929
931
|
project: "ACME",
|
|
930
932
|
title: "Labeled issue",
|
|
931
|
-
priority:
|
|
932
|
-
state:
|
|
933
|
-
description:
|
|
934
|
-
assignee:
|
|
935
|
-
label:
|
|
936
|
-
estimate: { _tag: "None" },
|
|
933
|
+
priority: Option.none(),
|
|
934
|
+
state: Option.none(),
|
|
935
|
+
description: Option.none(),
|
|
936
|
+
assignee: Option.none(),
|
|
937
|
+
label: Option.some("Bug"),
|
|
937
938
|
}),
|
|
938
939
|
);
|
|
939
940
|
|
|
940
|
-
expect((postedBody as
|
|
941
|
+
expect((postedBody as { label_ids?: string[] }).label_ids).toEqual([
|
|
942
|
+
"l-bug",
|
|
943
|
+
]);
|
|
941
944
|
});
|
|
942
945
|
});
|
|
943
946
|
|
|
@@ -954,13 +957,13 @@ describe("issueDelete", () => {
|
|
|
954
957
|
),
|
|
955
958
|
);
|
|
956
959
|
|
|
957
|
-
const {
|
|
960
|
+
const { issueDeleteHandler } = await import("@/commands/issue");
|
|
958
961
|
const logs: string[] = [];
|
|
959
962
|
const orig = console.log;
|
|
960
963
|
console.log = (...args: unknown[]) => logs.push(args.join(" "));
|
|
961
964
|
|
|
962
965
|
try {
|
|
963
|
-
await Effect.runPromise((
|
|
966
|
+
await Effect.runPromise(issueDeleteHandler({ ref: "ACME-29" }));
|
|
964
967
|
} finally {
|
|
965
968
|
console.log = orig;
|
|
966
969
|
}
|
|
@@ -970,81 +973,6 @@ describe("issueDelete", () => {
|
|
|
970
973
|
});
|
|
971
974
|
});
|
|
972
975
|
|
|
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
|
-
|
|
1048
976
|
describe("--description argv parsing", () => {
|
|
1049
977
|
async function runCli(argv: string[]): Promise<{ logs: string[] }> {
|
|
1050
978
|
const { issue } = await import("@/commands/issue");
|
|
@@ -1084,7 +1012,7 @@ describe("--description argv parsing", () => {
|
|
|
1084
1012
|
return HttpResponse.json({
|
|
1085
1013
|
id: "argv-i1",
|
|
1086
1014
|
sequence_id: 200,
|
|
1087
|
-
name: (postedBody as
|
|
1015
|
+
name: (postedBody as { name?: string }).name,
|
|
1088
1016
|
priority: "none",
|
|
1089
1017
|
state: "s1",
|
|
1090
1018
|
});
|
|
@@ -1101,7 +1029,9 @@ describe("--description argv parsing", () => {
|
|
|
1101
1029
|
"Argv test issue",
|
|
1102
1030
|
]);
|
|
1103
1031
|
expect(logs.join("\n")).toContain("Created");
|
|
1104
|
-
expect((postedBody as
|
|
1032
|
+
expect((postedBody as { description_html?: string }).description_html).toBe(
|
|
1033
|
+
"<p>Hello world</p>",
|
|
1034
|
+
);
|
|
1105
1035
|
});
|
|
1106
1036
|
|
|
1107
1037
|
it("issue create HTML-escapes & in description via argv", async () => {
|
|
@@ -1114,7 +1044,7 @@ describe("--description argv parsing", () => {
|
|
|
1114
1044
|
return HttpResponse.json({
|
|
1115
1045
|
id: "argv-i2",
|
|
1116
1046
|
sequence_id: 201,
|
|
1117
|
-
name: (postedBody as
|
|
1047
|
+
name: (postedBody as { name?: string }).name,
|
|
1118
1048
|
priority: "none",
|
|
1119
1049
|
state: "s1",
|
|
1120
1050
|
});
|
|
@@ -1130,7 +1060,9 @@ describe("--description argv parsing", () => {
|
|
|
1130
1060
|
"ACME",
|
|
1131
1061
|
"Ampersand test",
|
|
1132
1062
|
]);
|
|
1133
|
-
expect((postedBody as
|
|
1063
|
+
expect((postedBody as { description_html?: string }).description_html).toBe(
|
|
1064
|
+
"<p>a & b</p>",
|
|
1065
|
+
);
|
|
1134
1066
|
});
|
|
1135
1067
|
|
|
1136
1068
|
it("issue update passes --description to API via argv", async () => {
|
|
@@ -1152,6 +1084,8 @@ describe("--description argv parsing", () => {
|
|
|
1152
1084
|
);
|
|
1153
1085
|
|
|
1154
1086
|
await runCli(["issue", "update", "--description", "New desc", "ACME-29"]);
|
|
1155
|
-
expect(
|
|
1087
|
+
expect(
|
|
1088
|
+
(patchedBody as { description_html?: string }).description_html,
|
|
1089
|
+
).toBe("<p>New desc</p>");
|
|
1156
1090
|
});
|
|
1157
1091
|
});
|