@uipath/tasks-tool 0.9.0

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.
@@ -0,0 +1,692 @@
1
+ import { Command } from "commander";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { registerTasksCommand } from "./tasks";
4
+
5
+ vi.mock("../utils/sdk-client", () => ({
6
+ createTasksClient: vi.fn(),
7
+ }));
8
+
9
+ vi.mock("@uipath/common", async (importOriginal) => {
10
+ const actual = await importOriginal<typeof import("@uipath/common")>();
11
+ return {
12
+ ...actual,
13
+ OutputFormatter: {
14
+ success: vi.fn(),
15
+ error: vi.fn(),
16
+ },
17
+ };
18
+ });
19
+
20
+ import { OutputFormatter } from "@uipath/common";
21
+ import { createTasksClient } from "../utils/sdk-client";
22
+
23
+ function buildProgram(): Command {
24
+ const program = new Command();
25
+ program.name("test").exitOverride();
26
+ registerTasksCommand(program);
27
+ return program;
28
+ }
29
+
30
+ function mockSdk(overrides: Record<string, unknown> = {}) {
31
+ const sdk = {
32
+ tasks: {
33
+ getAll: vi.fn().mockResolvedValue([]),
34
+ getById: vi.fn().mockResolvedValue({}),
35
+ assign: vi.fn().mockResolvedValue({ success: true, data: [] }),
36
+ reassign: vi.fn().mockResolvedValue({ success: true, data: [] }),
37
+ unassign: vi.fn().mockResolvedValue({ success: true, data: [] }),
38
+ complete: vi.fn().mockResolvedValue({ success: true }),
39
+ ...overrides,
40
+ },
41
+ };
42
+ vi.mocked(createTasksClient).mockResolvedValue(sdk as never);
43
+ return sdk;
44
+ }
45
+
46
+ describe("tasks command registration", () => {
47
+ it("should register all commands on program", () => {
48
+ const program = buildProgram();
49
+ const subcommands = program.commands.map((c) => c.name());
50
+ expect(subcommands).toContain("list");
51
+ expect(subcommands).toContain("get");
52
+ expect(subcommands).toContain("assign");
53
+ expect(subcommands).toContain("reassign");
54
+ expect(subcommands).toContain("unassign");
55
+ expect(subcommands).toContain("complete");
56
+ });
57
+ });
58
+
59
+ describe("tasks list", () => {
60
+ beforeEach(() => {
61
+ vi.resetAllMocks();
62
+ process.exitCode = undefined;
63
+ });
64
+
65
+ it("should list tasks successfully", async () => {
66
+ const taskData = [
67
+ { id: 1, title: "Task 1", status: "Pending" },
68
+ { id: 2, title: "Task 2", status: "Unassigned" },
69
+ ];
70
+ mockSdk({ getAll: vi.fn().mockResolvedValue(taskData) });
71
+
72
+ const program = buildProgram();
73
+ await program.parseAsync(["node", "test", "list"]);
74
+
75
+ expect(createTasksClient).toHaveBeenCalledWith(undefined);
76
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
77
+ expect.objectContaining({
78
+ Result: "Success",
79
+ Code: "TaskList",
80
+ Data: taskData,
81
+ }),
82
+ );
83
+ });
84
+
85
+ it("should pass folder-id option", async () => {
86
+ const sdk = mockSdk();
87
+
88
+ const program = buildProgram();
89
+ await program.parseAsync(["node", "test", "list", "--folder-id", "42"]);
90
+
91
+ expect(sdk.tasks.getAll).toHaveBeenCalledWith(
92
+ expect.objectContaining({ folderId: 42, pageSize: 100 }),
93
+ );
94
+ });
95
+
96
+ it("should pass as-admin option", async () => {
97
+ const sdk = mockSdk();
98
+
99
+ const program = buildProgram();
100
+ await program.parseAsync(["node", "test", "list", "--as-admin"]);
101
+
102
+ expect(sdk.tasks.getAll).toHaveBeenCalledWith(
103
+ expect.objectContaining({ asTaskAdmin: true, pageSize: 100 }),
104
+ );
105
+ });
106
+
107
+ it("should pass tenant option", async () => {
108
+ mockSdk();
109
+
110
+ const program = buildProgram();
111
+ await program.parseAsync(["node", "test", "list", "-t", "MyTenant"]);
112
+
113
+ expect(createTasksClient).toHaveBeenCalledWith("MyTenant");
114
+ });
115
+
116
+ it("should handle client connection error", async () => {
117
+ vi.mocked(createTasksClient).mockRejectedValue(
118
+ new Error("Not logged in"),
119
+ );
120
+
121
+ const program = buildProgram();
122
+ await program.parseAsync(["node", "test", "list"]);
123
+
124
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
125
+ expect.objectContaining({
126
+ Result: "Failure",
127
+ Message: "Error connecting to Action Center",
128
+ }),
129
+ );
130
+ expect(process.exitCode).toBe(1);
131
+ });
132
+
133
+ it("should handle API error", async () => {
134
+ mockSdk({
135
+ getAll: vi.fn().mockRejectedValue(new Error("API error")),
136
+ });
137
+
138
+ const program = buildProgram();
139
+ await program.parseAsync(["node", "test", "list"]);
140
+
141
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
142
+ expect.objectContaining({
143
+ Result: "Failure",
144
+ Message: "Error listing tasks",
145
+ }),
146
+ );
147
+ expect(process.exitCode).toBe(1);
148
+ });
149
+
150
+ it("should paginate through multiple pages", async () => {
151
+ const page1 = {
152
+ items: [{ id: 1, title: "Task 1" }],
153
+ hasNextPage: true,
154
+ nextCursor: { value: "cursor-2" },
155
+ };
156
+ const page2 = {
157
+ items: [{ id: 2, title: "Task 2" }],
158
+ hasNextPage: false,
159
+ };
160
+ const getAllMock = vi
161
+ .fn()
162
+ .mockResolvedValueOnce(page1)
163
+ .mockResolvedValueOnce(page2);
164
+ mockSdk({ getAll: getAllMock });
165
+
166
+ const program = buildProgram();
167
+ await program.parseAsync(["node", "test", "list"]);
168
+
169
+ expect(getAllMock).toHaveBeenCalledTimes(2);
170
+ expect(getAllMock).toHaveBeenLastCalledWith(
171
+ expect.objectContaining({ cursor: { value: "cursor-2" } }),
172
+ );
173
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
174
+ expect.objectContaining({
175
+ Result: "Success",
176
+ Code: "TaskList",
177
+ Data: [
178
+ { id: 1, title: "Task 1" },
179
+ { id: 2, title: "Task 2" },
180
+ ],
181
+ }),
182
+ );
183
+ });
184
+
185
+ it("should respect --limit option", async () => {
186
+ const page1 = {
187
+ items: [
188
+ { id: 1, title: "Task 1" },
189
+ { id: 2, title: "Task 2" },
190
+ { id: 3, title: "Task 3" },
191
+ ],
192
+ hasNextPage: true,
193
+ nextCursor: { value: "cursor-2" },
194
+ };
195
+ mockSdk({ getAll: vi.fn().mockResolvedValue(page1) });
196
+
197
+ const program = buildProgram();
198
+ await program.parseAsync(["node", "test", "list", "--limit", "2"]);
199
+
200
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
201
+ expect.objectContaining({
202
+ Data: [
203
+ { id: 1, title: "Task 1" },
204
+ { id: 2, title: "Task 2" },
205
+ ],
206
+ }),
207
+ );
208
+ });
209
+ });
210
+
211
+ describe("tasks get", () => {
212
+ beforeEach(() => {
213
+ vi.resetAllMocks();
214
+ process.exitCode = undefined;
215
+ });
216
+
217
+ it("should get task by ID", async () => {
218
+ const task = { id: 123, title: "My Task", status: "Pending" };
219
+ mockSdk({ getById: vi.fn().mockResolvedValue(task) });
220
+
221
+ const program = buildProgram();
222
+ await program.parseAsync(["node", "test", "get", "123"]);
223
+
224
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
225
+ expect.objectContaining({
226
+ Result: "Success",
227
+ Code: "TaskDetails",
228
+ Data: task,
229
+ }),
230
+ );
231
+ });
232
+
233
+ it("should pass task-type and folder-id options", async () => {
234
+ const sdk = mockSdk();
235
+
236
+ const program = buildProgram();
237
+ await program.parseAsync([
238
+ "node",
239
+ "test",
240
+ "get",
241
+ "123",
242
+ "--task-type",
243
+ "FormTask",
244
+ "--folder-id",
245
+ "42",
246
+ ]);
247
+
248
+ expect(sdk.tasks.getById).toHaveBeenCalledWith(
249
+ 123,
250
+ expect.objectContaining({ taskType: "FormTask" }),
251
+ 42,
252
+ );
253
+ });
254
+
255
+ it("should handle get error", async () => {
256
+ mockSdk({
257
+ getById: vi.fn().mockRejectedValue(new Error("Not found")),
258
+ });
259
+
260
+ const program = buildProgram();
261
+ await program.parseAsync(["node", "test", "get", "999"]);
262
+
263
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
264
+ expect.objectContaining({
265
+ Result: "Failure",
266
+ Message: "Error getting task '999'",
267
+ }),
268
+ );
269
+ expect(process.exitCode).toBe(1);
270
+ });
271
+
272
+ it("should handle client connection error", async () => {
273
+ vi.mocked(createTasksClient).mockRejectedValue(
274
+ new Error("Not logged in"),
275
+ );
276
+
277
+ const program = buildProgram();
278
+ await program.parseAsync(["node", "test", "get", "123"]);
279
+
280
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
281
+ expect.objectContaining({
282
+ Result: "Failure",
283
+ Message: "Error connecting to Action Center",
284
+ }),
285
+ );
286
+ expect(process.exitCode).toBe(1);
287
+ });
288
+ });
289
+
290
+ describe("tasks assign", () => {
291
+ beforeEach(() => {
292
+ vi.resetAllMocks();
293
+ process.exitCode = undefined;
294
+ });
295
+
296
+ it("should assign task by user email", async () => {
297
+ const result = { success: true, data: [{ taskId: 123 }] };
298
+ const sdk = mockSdk({
299
+ assign: vi.fn().mockResolvedValue(result),
300
+ });
301
+
302
+ const program = buildProgram();
303
+ await program.parseAsync([
304
+ "node",
305
+ "test",
306
+ "assign",
307
+ "123",
308
+ "--user",
309
+ "alice@company.com",
310
+ ]);
311
+
312
+ expect(sdk.tasks.assign).toHaveBeenCalledWith(
313
+ expect.objectContaining({
314
+ taskId: 123,
315
+ userNameOrEmail: "alice@company.com",
316
+ }),
317
+ );
318
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
319
+ expect.objectContaining({
320
+ Result: "Success",
321
+ Code: "TaskAssigned",
322
+ }),
323
+ );
324
+ });
325
+
326
+ it("should assign task by user ID", async () => {
327
+ const sdk = mockSdk();
328
+
329
+ const program = buildProgram();
330
+ await program.parseAsync([
331
+ "node",
332
+ "test",
333
+ "assign",
334
+ "123",
335
+ "--user-id",
336
+ "456",
337
+ ]);
338
+
339
+ expect(sdk.tasks.assign).toHaveBeenCalledWith(
340
+ expect.objectContaining({
341
+ taskId: 123,
342
+ userId: 456,
343
+ }),
344
+ );
345
+ });
346
+
347
+ it("should error when no assignee provided", async () => {
348
+ mockSdk();
349
+
350
+ const program = buildProgram();
351
+ await program.parseAsync(["node", "test", "assign", "123"]);
352
+
353
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
354
+ expect.objectContaining({
355
+ Result: "Failure",
356
+ Message: "Missing assignee",
357
+ }),
358
+ );
359
+ expect(process.exitCode).toBe(1);
360
+ });
361
+
362
+ it("should handle assign API error", async () => {
363
+ mockSdk({
364
+ assign: vi.fn().mockRejectedValue(new Error("Permission denied")),
365
+ });
366
+
367
+ const program = buildProgram();
368
+ await program.parseAsync([
369
+ "node",
370
+ "test",
371
+ "assign",
372
+ "123",
373
+ "--user",
374
+ "alice@company.com",
375
+ ]);
376
+
377
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
378
+ expect.objectContaining({
379
+ Result: "Failure",
380
+ Message: "Error assigning task '123'",
381
+ }),
382
+ );
383
+ expect(process.exitCode).toBe(1);
384
+ });
385
+
386
+ it("should handle client connection error", async () => {
387
+ vi.mocked(createTasksClient).mockRejectedValue(
388
+ new Error("Not logged in"),
389
+ );
390
+
391
+ const program = buildProgram();
392
+ await program.parseAsync([
393
+ "node",
394
+ "test",
395
+ "assign",
396
+ "123",
397
+ "--user",
398
+ "alice@company.com",
399
+ ]);
400
+
401
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
402
+ expect.objectContaining({
403
+ Result: "Failure",
404
+ Message: "Error connecting to Action Center",
405
+ }),
406
+ );
407
+ expect(process.exitCode).toBe(1);
408
+ });
409
+ });
410
+
411
+ describe("tasks reassign", () => {
412
+ beforeEach(() => {
413
+ vi.resetAllMocks();
414
+ process.exitCode = undefined;
415
+ });
416
+
417
+ it("should reassign task by user email", async () => {
418
+ const result = { success: true, data: [{ taskId: 123 }] };
419
+ const sdk = mockSdk({
420
+ reassign: vi.fn().mockResolvedValue(result),
421
+ });
422
+
423
+ const program = buildProgram();
424
+ await program.parseAsync([
425
+ "node",
426
+ "test",
427
+ "reassign",
428
+ "123",
429
+ "--user",
430
+ "bob@company.com",
431
+ ]);
432
+
433
+ expect(sdk.tasks.reassign).toHaveBeenCalledWith(
434
+ expect.objectContaining({
435
+ taskId: 123,
436
+ userNameOrEmail: "bob@company.com",
437
+ }),
438
+ );
439
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
440
+ expect.objectContaining({
441
+ Result: "Success",
442
+ Code: "TaskReassigned",
443
+ }),
444
+ );
445
+ });
446
+
447
+ it("should error when no assignee provided", async () => {
448
+ mockSdk();
449
+
450
+ const program = buildProgram();
451
+ await program.parseAsync(["node", "test", "reassign", "123"]);
452
+
453
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
454
+ expect.objectContaining({
455
+ Result: "Failure",
456
+ Message: "Missing assignee",
457
+ }),
458
+ );
459
+ expect(process.exitCode).toBe(1);
460
+ });
461
+
462
+ it("should handle reassign API error", async () => {
463
+ mockSdk({
464
+ reassign: vi.fn().mockRejectedValue(new Error("Permission denied")),
465
+ });
466
+
467
+ const program = buildProgram();
468
+ await program.parseAsync([
469
+ "node",
470
+ "test",
471
+ "reassign",
472
+ "123",
473
+ "--user",
474
+ "bob@company.com",
475
+ ]);
476
+
477
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
478
+ expect.objectContaining({
479
+ Result: "Failure",
480
+ Message: "Error reassigning task '123'",
481
+ }),
482
+ );
483
+ expect(process.exitCode).toBe(1);
484
+ });
485
+ });
486
+
487
+ describe("tasks unassign", () => {
488
+ beforeEach(() => {
489
+ vi.resetAllMocks();
490
+ process.exitCode = undefined;
491
+ });
492
+
493
+ it("should unassign task", async () => {
494
+ const result = { success: true, data: [{ taskId: 123 }] };
495
+ const sdk = mockSdk({
496
+ unassign: vi.fn().mockResolvedValue(result),
497
+ });
498
+
499
+ const program = buildProgram();
500
+ await program.parseAsync(["node", "test", "unassign", "123"]);
501
+
502
+ expect(sdk.tasks.unassign).toHaveBeenCalledWith(123);
503
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
504
+ expect.objectContaining({
505
+ Result: "Success",
506
+ Code: "TaskUnassigned",
507
+ }),
508
+ );
509
+ });
510
+
511
+ it("should handle unassign API error", async () => {
512
+ mockSdk({
513
+ unassign: vi.fn().mockRejectedValue(new Error("Task not found")),
514
+ });
515
+
516
+ const program = buildProgram();
517
+ await program.parseAsync(["node", "test", "unassign", "999"]);
518
+
519
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
520
+ expect.objectContaining({
521
+ Result: "Failure",
522
+ Message: "Error unassigning task '999'",
523
+ }),
524
+ );
525
+ expect(process.exitCode).toBe(1);
526
+ });
527
+
528
+ it("should handle client connection error", async () => {
529
+ vi.mocked(createTasksClient).mockRejectedValue(
530
+ new Error("Not logged in"),
531
+ );
532
+
533
+ const program = buildProgram();
534
+ await program.parseAsync(["node", "test", "unassign", "123"]);
535
+
536
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
537
+ expect.objectContaining({
538
+ Result: "Failure",
539
+ Message: "Error connecting to Action Center",
540
+ }),
541
+ );
542
+ expect(process.exitCode).toBe(1);
543
+ });
544
+ });
545
+
546
+ describe("tasks complete", () => {
547
+ beforeEach(() => {
548
+ vi.resetAllMocks();
549
+ process.exitCode = undefined;
550
+ });
551
+
552
+ it("should complete task with type and folder-id", async () => {
553
+ const result = { success: true };
554
+ const sdk = mockSdk({
555
+ complete: vi.fn().mockResolvedValue(result),
556
+ });
557
+
558
+ const program = buildProgram();
559
+ await program.parseAsync([
560
+ "node",
561
+ "test",
562
+ "complete",
563
+ "123",
564
+ "--type",
565
+ "ExternalTask",
566
+ "--folder-id",
567
+ "42",
568
+ ]);
569
+
570
+ expect(sdk.tasks.complete).toHaveBeenCalledWith(
571
+ expect.objectContaining({
572
+ taskId: 123,
573
+ type: "ExternalTask",
574
+ }),
575
+ 42,
576
+ );
577
+ expect(OutputFormatter.success).toHaveBeenCalledWith(
578
+ expect.objectContaining({
579
+ Result: "Success",
580
+ Code: "TaskCompleted",
581
+ }),
582
+ );
583
+ });
584
+
585
+ it("should complete FormTask with action and data", async () => {
586
+ const sdk = mockSdk();
587
+
588
+ const program = buildProgram();
589
+ await program.parseAsync([
590
+ "node",
591
+ "test",
592
+ "complete",
593
+ "123",
594
+ "--type",
595
+ "FormTask",
596
+ "--folder-id",
597
+ "42",
598
+ "--action",
599
+ "Approve",
600
+ "--data",
601
+ '{"comments":"Looks good"}',
602
+ ]);
603
+
604
+ expect(sdk.tasks.complete).toHaveBeenCalledWith(
605
+ expect.objectContaining({
606
+ taskId: 123,
607
+ type: "FormTask",
608
+ action: "Approve",
609
+ data: { comments: "Looks good" },
610
+ }),
611
+ 42,
612
+ );
613
+ });
614
+
615
+ it("should error on invalid JSON in --data", async () => {
616
+ mockSdk();
617
+
618
+ const program = buildProgram();
619
+ await program.parseAsync([
620
+ "node",
621
+ "test",
622
+ "complete",
623
+ "123",
624
+ "--type",
625
+ "ExternalTask",
626
+ "--folder-id",
627
+ "42",
628
+ "--data",
629
+ "not-json",
630
+ ]);
631
+
632
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
633
+ expect.objectContaining({
634
+ Result: "Failure",
635
+ Message: "Invalid JSON in --data",
636
+ }),
637
+ );
638
+ expect(process.exitCode).toBe(1);
639
+ });
640
+
641
+ it("should handle complete API error", async () => {
642
+ mockSdk({
643
+ complete: vi.fn().mockRejectedValue(new Error("Server error")),
644
+ });
645
+
646
+ const program = buildProgram();
647
+ await program.parseAsync([
648
+ "node",
649
+ "test",
650
+ "complete",
651
+ "123",
652
+ "--type",
653
+ "ExternalTask",
654
+ "--folder-id",
655
+ "42",
656
+ ]);
657
+
658
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
659
+ expect.objectContaining({
660
+ Result: "Failure",
661
+ Message: "Error completing task '123'",
662
+ }),
663
+ );
664
+ expect(process.exitCode).toBe(1);
665
+ });
666
+
667
+ it("should handle client connection error", async () => {
668
+ vi.mocked(createTasksClient).mockRejectedValue(
669
+ new Error("Not logged in"),
670
+ );
671
+
672
+ const program = buildProgram();
673
+ await program.parseAsync([
674
+ "node",
675
+ "test",
676
+ "complete",
677
+ "123",
678
+ "--type",
679
+ "ExternalTask",
680
+ "--folder-id",
681
+ "42",
682
+ ]);
683
+
684
+ expect(OutputFormatter.error).toHaveBeenCalledWith(
685
+ expect.objectContaining({
686
+ Result: "Failure",
687
+ Message: "Error connecting to Action Center",
688
+ }),
689
+ );
690
+ expect(process.exitCode).toBe(1);
691
+ });
692
+ });