@uipath/tasks-tool 1.201.0-preview.115 → 1.201.0-preview.122
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/browser.json +2 -1
- package/dist/folder-picker-interactive-2kj361mt.js +1850 -0
- package/dist/folder-resolver-htdmzdhy.js +92 -0
- package/dist/index.js +5 -3
- package/dist/tool-0n9mj887.js +2448 -0
- package/dist/tool-rsk1xt86.js +6026 -0
- package/dist/{tool-vw6gha5d.js → tool-xcy3gbe4.js} +7469 -10469
- package/dist/tool.js +3 -2
- package/package.json +2 -2
- package/src/commands/_shared/catalog-options.spec.ts +114 -0
- package/src/commands/_shared/catalog-options.ts +122 -0
- package/src/commands/_shared/connect.spec.ts +52 -0
- package/src/commands/_shared/connect.ts +31 -0
- package/src/commands/_shared/constants.ts +1 -0
- package/src/commands/_shared/folder-picker-interactive.spec.ts +92 -0
- package/src/commands/_shared/folder-picker-interactive.ts +76 -0
- package/src/commands/_shared/folder-picker.spec.ts +159 -0
- package/src/commands/_shared/folder-picker.ts +113 -0
- package/src/commands/_shared/folder-resolver.spec.ts +152 -0
- package/src/commands/_shared/folder-resolver.ts +113 -0
- package/src/commands/_shared/parse.spec.ts +69 -0
- package/src/commands/_shared/parse.ts +43 -0
- package/src/commands/catalogs.ts +277 -0
- package/src/commands/comments.ts +160 -0
- package/src/commands/data.ts +137 -0
- package/src/commands/labels.ts +93 -0
- package/src/commands/metadata.ts +139 -0
- package/src/commands/tasks.spec.ts +1557 -2
- package/src/commands/tasks.ts +191 -280
- package/src/tool.ts +10 -0
- package/src/utils/sdk-client.ts +5 -0
- package/tests/assignable-users.e2e.test.ts +58 -4
- package/tests/catalogs.e2e.test.ts +393 -0
- package/tests/comments.e2e.test.ts +223 -0
- package/tests/complete.e2e.test.ts +9 -9
- package/tests/data.e2e.test.ts +187 -0
- package/tests/get.e2e.test.ts +4 -2
- package/tests/labels.e2e.test.ts +184 -0
- package/tests/list.e2e.test.ts +110 -0
- package/tests/metadata.e2e.test.ts +186 -0
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
EXEC_TIMEOUT,
|
|
9
9
|
localCliRaw,
|
|
10
|
+
localCliRawWithArgs,
|
|
10
11
|
parseOutput,
|
|
11
12
|
readManifest,
|
|
12
13
|
} from "../../../tests/e2e-shared/helpers";
|
|
@@ -49,14 +50,16 @@ describe("tasks users e2e", () => {
|
|
|
49
50
|
}, EXEC_TIMEOUT);
|
|
50
51
|
|
|
51
52
|
it(
|
|
52
|
-
"S1: missing folder
|
|
53
|
+
"S1: missing folder fails loud when not interactive",
|
|
53
54
|
() => {
|
|
54
55
|
const raw = localCliRaw(tempDir, "tasks users --output json");
|
|
55
56
|
expect(raw.exitCode).not.toBe(0);
|
|
56
57
|
const res = parseOutput(raw.stdout, "json") as UsersEnvelope;
|
|
57
|
-
expect(res.Result).toBe(
|
|
58
|
-
expect(res.
|
|
59
|
-
expect(res.
|
|
58
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
59
|
+
expect(res.Message).toBe("A folder is required");
|
|
60
|
+
expect(res.Instructions).toMatch(/folder-id argument/);
|
|
61
|
+
expect(res.Instructions).toMatch(/--folder-path/);
|
|
62
|
+
expect(res.Instructions).not.toMatch(/--folder-id/);
|
|
60
63
|
},
|
|
61
64
|
EXEC_TIMEOUT,
|
|
62
65
|
);
|
|
@@ -110,4 +113,55 @@ describe("tasks users e2e", () => {
|
|
|
110
113
|
},
|
|
111
114
|
EXEC_TIMEOUT,
|
|
112
115
|
);
|
|
116
|
+
|
|
117
|
+
it(
|
|
118
|
+
"S5: lists users by folder path",
|
|
119
|
+
() => {
|
|
120
|
+
const foldersRaw = localCliRaw(
|
|
121
|
+
tempDir,
|
|
122
|
+
"or folders list --output json",
|
|
123
|
+
);
|
|
124
|
+
if (foldersRaw.exitCode !== 0) return;
|
|
125
|
+
const foldersRes = parseOutput(foldersRaw.stdout, "json") as {
|
|
126
|
+
Data?: { Path?: string; Name?: string }[];
|
|
127
|
+
};
|
|
128
|
+
const first = foldersRes.Data?.[0];
|
|
129
|
+
const path = first?.Path || first?.Name;
|
|
130
|
+
if (!path) return;
|
|
131
|
+
|
|
132
|
+
// API-derived value — pass as its own argument, never through a
|
|
133
|
+
// shell-interpolated string.
|
|
134
|
+
const raw = localCliRawWithArgs(tempDir, [
|
|
135
|
+
"tasks",
|
|
136
|
+
"users",
|
|
137
|
+
"--folder-path",
|
|
138
|
+
path,
|
|
139
|
+
"--output",
|
|
140
|
+
"json",
|
|
141
|
+
]);
|
|
142
|
+
expect(raw.exitCode).toBe(0);
|
|
143
|
+
const res = parseOutput(raw.stdout, "json") as UsersEnvelope;
|
|
144
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
145
|
+
expect(res.Code).toBe("TaskUserList");
|
|
146
|
+
expect(Array.isArray(res.Data)).toBe(true);
|
|
147
|
+
},
|
|
148
|
+
EXEC_TIMEOUT,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
it(
|
|
152
|
+
"S6: the folder-id argument and folder options are mutually exclusive",
|
|
153
|
+
() => {
|
|
154
|
+
const raw = localCliRaw(
|
|
155
|
+
tempDir,
|
|
156
|
+
"tasks users 42 --folder-path Shared --output json",
|
|
157
|
+
);
|
|
158
|
+
expect(raw.exitCode).not.toBe(0);
|
|
159
|
+
const res = parseOutput(raw.stdout, "json") as UsersEnvelope;
|
|
160
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
161
|
+
expect(res.Message).toBe(
|
|
162
|
+
"The folder-id argument cannot be used with --folder-path or --folder-key",
|
|
163
|
+
);
|
|
164
|
+
},
|
|
165
|
+
EXEC_TIMEOUT,
|
|
166
|
+
);
|
|
113
167
|
});
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { RESULTS } from "@uipath/common";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
type E2EManifest,
|
|
5
|
+
MANIFEST_PATH,
|
|
6
|
+
} from "../../../tests/e2e-shared/global-setup";
|
|
7
|
+
import {
|
|
8
|
+
EXEC_TIMEOUT,
|
|
9
|
+
localCliRaw,
|
|
10
|
+
localCliRawWithArgs,
|
|
11
|
+
parseOutput,
|
|
12
|
+
readManifest,
|
|
13
|
+
} from "../../../tests/e2e-shared/helpers";
|
|
14
|
+
|
|
15
|
+
interface Envelope {
|
|
16
|
+
Result: string;
|
|
17
|
+
Code?: string;
|
|
18
|
+
Data?: unknown;
|
|
19
|
+
Message?: string;
|
|
20
|
+
Instructions?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Catalog {
|
|
24
|
+
Id?: number;
|
|
25
|
+
Name?: string;
|
|
26
|
+
Encrypted?: boolean;
|
|
27
|
+
RetentionAction?: string;
|
|
28
|
+
RetentionPeriod?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface TaskRow {
|
|
32
|
+
IsDeleted?: boolean;
|
|
33
|
+
Status?: string;
|
|
34
|
+
FolderId?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { tempDir } = readManifest<E2EManifest>(MANIFEST_PATH).sharedInstall;
|
|
38
|
+
|
|
39
|
+
function listTasks(asAdmin: boolean): TaskRow[] {
|
|
40
|
+
const flag = asAdmin ? "--as-admin " : "";
|
|
41
|
+
const raw = localCliRaw(
|
|
42
|
+
tempDir,
|
|
43
|
+
`tasks list ${flag}--limit 200 --output json`,
|
|
44
|
+
);
|
|
45
|
+
if (raw.exitCode !== 0) return [];
|
|
46
|
+
return (parseOutput(raw.stdout, "json") as { Data?: TaskRow[] }).Data ?? [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function discoverFolderId(): string {
|
|
50
|
+
if (process.env.TASKS_TOOL_E2E_FOLDER_ID) {
|
|
51
|
+
return process.env.TASKS_TOOL_E2E_FOLDER_ID;
|
|
52
|
+
}
|
|
53
|
+
for (const asAdmin of [false, true]) {
|
|
54
|
+
const task = listTasks(asAdmin).find(
|
|
55
|
+
(t) => !t.IsDeleted && typeof t.FolderId === "number",
|
|
56
|
+
);
|
|
57
|
+
if (typeof task?.FolderId === "number") {
|
|
58
|
+
return String(task.FolderId);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw new Error(
|
|
62
|
+
"No live task found to derive a folder for catalogs e2e. Set TASKS_TOOL_E2E_FOLDER_ID or seed a task.",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const folderId = discoverFolderId();
|
|
67
|
+
let createdCatalogId: number | undefined;
|
|
68
|
+
|
|
69
|
+
function createCatalog(name: string, extra: string[] = []): Envelope {
|
|
70
|
+
const raw = localCliRawWithArgs(tempDir, [
|
|
71
|
+
"tasks",
|
|
72
|
+
"catalogs",
|
|
73
|
+
"create",
|
|
74
|
+
"--folder-id",
|
|
75
|
+
folderId,
|
|
76
|
+
"--name",
|
|
77
|
+
name,
|
|
78
|
+
...extra,
|
|
79
|
+
"--output",
|
|
80
|
+
"json",
|
|
81
|
+
]);
|
|
82
|
+
return parseOutput(raw.stdout, "json") as Envelope;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getCatalog(id: number): Envelope {
|
|
86
|
+
const raw = localCliRaw(
|
|
87
|
+
tempDir,
|
|
88
|
+
`tasks catalogs get ${id} --folder-id ${folderId} --output json`,
|
|
89
|
+
);
|
|
90
|
+
return parseOutput(raw.stdout, "json") as Envelope;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
describe("tasks catalogs e2e", () => {
|
|
94
|
+
it(
|
|
95
|
+
"S1: list catalogs in a folder",
|
|
96
|
+
() => {
|
|
97
|
+
const raw = localCliRaw(
|
|
98
|
+
tempDir,
|
|
99
|
+
`tasks catalogs list --folder-id ${folderId} --output json`,
|
|
100
|
+
);
|
|
101
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
102
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
103
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
104
|
+
expect(res.Code).toBe("TaskCatalogList");
|
|
105
|
+
expect(Array.isArray(res.Data)).toBe(true);
|
|
106
|
+
},
|
|
107
|
+
EXEC_TIMEOUT,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
it(
|
|
111
|
+
"S2: list caps results with --limit",
|
|
112
|
+
() => {
|
|
113
|
+
const raw = localCliRaw(
|
|
114
|
+
tempDir,
|
|
115
|
+
`tasks catalogs list --folder-id ${folderId} --limit 1 --output json`,
|
|
116
|
+
);
|
|
117
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
118
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
119
|
+
expect(res.Code).toBe("TaskCatalogList");
|
|
120
|
+
expect((res.Data as unknown[]).length).toBeLessThanOrEqual(1);
|
|
121
|
+
},
|
|
122
|
+
EXEC_TIMEOUT,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
it(
|
|
126
|
+
"S3: get a catalog by id",
|
|
127
|
+
() => {
|
|
128
|
+
const listRaw = localCliRaw(
|
|
129
|
+
tempDir,
|
|
130
|
+
`tasks catalogs list --folder-id ${folderId} --limit 1 --output json`,
|
|
131
|
+
);
|
|
132
|
+
const list = parseOutput(listRaw.stdout, "json") as {
|
|
133
|
+
Data?: Catalog[];
|
|
134
|
+
};
|
|
135
|
+
const id = list.Data?.[0]?.Id;
|
|
136
|
+
if (typeof id !== "number") {
|
|
137
|
+
throw new Error(
|
|
138
|
+
"No catalog in the folder for getCatalogById. Seed a catalog first.",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const res = getCatalog(id);
|
|
143
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
144
|
+
expect(res.Code).toBe("TaskCatalogDetails");
|
|
145
|
+
expect((res.Data as Catalog).Id).toBe(id);
|
|
146
|
+
expect(typeof (res.Data as Catalog).Name).toBe("string");
|
|
147
|
+
},
|
|
148
|
+
EXEC_TIMEOUT,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
it(
|
|
152
|
+
"S4: get a non-existent catalog reports a failure",
|
|
153
|
+
() => {
|
|
154
|
+
const raw = localCliRaw(
|
|
155
|
+
tempDir,
|
|
156
|
+
`tasks catalogs get 999999999 --folder-id ${folderId} --output json`,
|
|
157
|
+
);
|
|
158
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
159
|
+
expect(raw.exitCode).not.toBe(0);
|
|
160
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
161
|
+
expect(res.Message).toMatch(/^Error getting task catalog/);
|
|
162
|
+
},
|
|
163
|
+
EXEC_TIMEOUT,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
it(
|
|
167
|
+
"S5: invalid catalog id reports a validation error",
|
|
168
|
+
() => {
|
|
169
|
+
const raw = localCliRaw(
|
|
170
|
+
tempDir,
|
|
171
|
+
`tasks catalogs get not-a-number --folder-id ${folderId} --output json`,
|
|
172
|
+
);
|
|
173
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
174
|
+
expect(raw.exitCode).not.toBe(0);
|
|
175
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
176
|
+
expect(res.Message).toMatch(/^Invalid <id>/);
|
|
177
|
+
expect(res.Instructions).toBe("Must be a positive integer.");
|
|
178
|
+
},
|
|
179
|
+
EXEC_TIMEOUT,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
it(
|
|
183
|
+
"S6: create a minimal catalog (name only)",
|
|
184
|
+
() => {
|
|
185
|
+
const name = `sdk-cli-e2e-${Date.now()}`;
|
|
186
|
+
const res = createCatalog(name);
|
|
187
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
188
|
+
expect(res.Code).toBe("TaskCatalogCreated");
|
|
189
|
+
const created = res.Data as Catalog;
|
|
190
|
+
expect(created.Name).toBe(name);
|
|
191
|
+
expect(typeof created.Id).toBe("number");
|
|
192
|
+
createdCatalogId = created.Id;
|
|
193
|
+
},
|
|
194
|
+
EXEC_TIMEOUT,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
it(
|
|
198
|
+
"S7: create a catalog with description, encryption, and a retention policy",
|
|
199
|
+
() => {
|
|
200
|
+
const name = `sdk-cli-e2e-ret-${Date.now()}`;
|
|
201
|
+
const res = createCatalog(name, [
|
|
202
|
+
"--description",
|
|
203
|
+
"Invoice approvals",
|
|
204
|
+
"--encrypted",
|
|
205
|
+
"--retention-action",
|
|
206
|
+
"Delete",
|
|
207
|
+
"--retention-period",
|
|
208
|
+
"30",
|
|
209
|
+
]);
|
|
210
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
211
|
+
expect(res.Code).toBe("TaskCatalogCreated");
|
|
212
|
+
const created = res.Data as Catalog;
|
|
213
|
+
expect(created.Encrypted).toBe(true);
|
|
214
|
+
expect(created.RetentionAction).toBe("Delete");
|
|
215
|
+
expect(created.RetentionPeriod).toBe(30);
|
|
216
|
+
},
|
|
217
|
+
EXEC_TIMEOUT,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
it(
|
|
221
|
+
"S8: create without --name fails",
|
|
222
|
+
() => {
|
|
223
|
+
const raw = localCliRaw(
|
|
224
|
+
tempDir,
|
|
225
|
+
`tasks catalogs create --folder-id ${folderId} --output json`,
|
|
226
|
+
);
|
|
227
|
+
expect(raw.exitCode).not.toBe(0);
|
|
228
|
+
},
|
|
229
|
+
EXEC_TIMEOUT,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
it(
|
|
233
|
+
"S9: create with an invalid --retention-action is rejected before the API call",
|
|
234
|
+
() => {
|
|
235
|
+
const res = createCatalog(`sdk-cli-e2e-bad-${Date.now()}`, [
|
|
236
|
+
"--retention-action",
|
|
237
|
+
"Purge",
|
|
238
|
+
]);
|
|
239
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
240
|
+
expect(res.Message).toBe("Invalid --retention-action 'Purge'");
|
|
241
|
+
expect(res.Instructions).toContain("Delete");
|
|
242
|
+
},
|
|
243
|
+
EXEC_TIMEOUT,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
it(
|
|
247
|
+
"S10: update a catalog's name and description",
|
|
248
|
+
() => {
|
|
249
|
+
if (typeof createdCatalogId !== "number") {
|
|
250
|
+
throw new Error(
|
|
251
|
+
"S6 must create a catalog before S10 updates it.",
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
const raw = localCliRawWithArgs(tempDir, [
|
|
255
|
+
"tasks",
|
|
256
|
+
"catalogs",
|
|
257
|
+
"update",
|
|
258
|
+
String(createdCatalogId),
|
|
259
|
+
"--folder-id",
|
|
260
|
+
folderId,
|
|
261
|
+
"--name",
|
|
262
|
+
`sdk-cli-e2e-renamed-${Date.now()}`,
|
|
263
|
+
"--description",
|
|
264
|
+
"Updated",
|
|
265
|
+
"--output",
|
|
266
|
+
"json",
|
|
267
|
+
]);
|
|
268
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
269
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
270
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
271
|
+
expect(res.Code).toBe("TaskCatalogUpdated");
|
|
272
|
+
},
|
|
273
|
+
EXEC_TIMEOUT,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
it(
|
|
277
|
+
"S11: retention round-trips through create, update, and get",
|
|
278
|
+
() => {
|
|
279
|
+
const name = `sdk-cli-e2e-rt-${Date.now()}`;
|
|
280
|
+
const created = createCatalog(name, [
|
|
281
|
+
"--retention-action",
|
|
282
|
+
"Delete",
|
|
283
|
+
"--retention-period",
|
|
284
|
+
"30",
|
|
285
|
+
]);
|
|
286
|
+
const id = (created.Data as Catalog).Id;
|
|
287
|
+
expect(typeof id).toBe("number");
|
|
288
|
+
|
|
289
|
+
const afterCreate = getCatalog(id as number);
|
|
290
|
+
expect((afterCreate.Data as Catalog).RetentionPeriod).toBe(30);
|
|
291
|
+
|
|
292
|
+
const upd = localCliRawWithArgs(tempDir, [
|
|
293
|
+
"tasks",
|
|
294
|
+
"catalogs",
|
|
295
|
+
"update",
|
|
296
|
+
String(id),
|
|
297
|
+
"--folder-id",
|
|
298
|
+
folderId,
|
|
299
|
+
"--name",
|
|
300
|
+
name,
|
|
301
|
+
"--retention-action",
|
|
302
|
+
"Delete",
|
|
303
|
+
"--retention-period",
|
|
304
|
+
"60",
|
|
305
|
+
"--output",
|
|
306
|
+
"json",
|
|
307
|
+
]);
|
|
308
|
+
expect(upd.exitCode, upd.stdout).toBe(0);
|
|
309
|
+
|
|
310
|
+
const afterUpdate = getCatalog(id as number);
|
|
311
|
+
expect((afterUpdate.Data as Catalog).RetentionPeriod).toBe(60);
|
|
312
|
+
},
|
|
313
|
+
EXEC_TIMEOUT,
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
it(
|
|
317
|
+
"S12: update a non-existent catalog reports a failure",
|
|
318
|
+
() => {
|
|
319
|
+
const raw = localCliRaw(
|
|
320
|
+
tempDir,
|
|
321
|
+
`tasks catalogs update 999999999 --folder-id ${folderId} --name whatever --output json`,
|
|
322
|
+
);
|
|
323
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
324
|
+
expect(raw.exitCode).not.toBe(0);
|
|
325
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
326
|
+
expect(res.Message).toMatch(/^Error updating task catalog/);
|
|
327
|
+
},
|
|
328
|
+
EXEC_TIMEOUT,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
it(
|
|
332
|
+
"S13: update with only --description preserves the other fields",
|
|
333
|
+
() => {
|
|
334
|
+
const name = `sdk-cli-e2e-merge-${Date.now()}`;
|
|
335
|
+
const created = createCatalog(name, [
|
|
336
|
+
"--retention-action",
|
|
337
|
+
"Delete",
|
|
338
|
+
"--retention-period",
|
|
339
|
+
"30",
|
|
340
|
+
]);
|
|
341
|
+
const id = (created.Data as Catalog).Id;
|
|
342
|
+
expect(typeof id).toBe("number");
|
|
343
|
+
|
|
344
|
+
// Update only the description; leave name and retention unset.
|
|
345
|
+
const upd = localCliRawWithArgs(tempDir, [
|
|
346
|
+
"tasks",
|
|
347
|
+
"catalogs",
|
|
348
|
+
"update",
|
|
349
|
+
String(id),
|
|
350
|
+
"--folder-id",
|
|
351
|
+
folderId,
|
|
352
|
+
"--description",
|
|
353
|
+
"Only this changed",
|
|
354
|
+
"--output",
|
|
355
|
+
"json",
|
|
356
|
+
]);
|
|
357
|
+
expect(upd.exitCode, upd.stdout).toBe(0);
|
|
358
|
+
|
|
359
|
+
// Read-modify-write: name and retention must survive the update.
|
|
360
|
+
const after = getCatalog(id as number).Data as Catalog;
|
|
361
|
+
expect(after.Name).toBe(name);
|
|
362
|
+
expect(after.RetentionPeriod).toBe(30);
|
|
363
|
+
},
|
|
364
|
+
EXEC_TIMEOUT,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
it(
|
|
368
|
+
"S14: list catalogs scoped by folder key",
|
|
369
|
+
() => {
|
|
370
|
+
const foldersRaw = localCliRaw(
|
|
371
|
+
tempDir,
|
|
372
|
+
"or folders list --output json",
|
|
373
|
+
);
|
|
374
|
+
if (foldersRaw.exitCode !== 0) return;
|
|
375
|
+
const foldersRes = parseOutput(foldersRaw.stdout, "json") as {
|
|
376
|
+
Data?: { Key?: string }[];
|
|
377
|
+
};
|
|
378
|
+
const key = foldersRes.Data?.[0]?.Key;
|
|
379
|
+
if (!key) return;
|
|
380
|
+
|
|
381
|
+
const raw = localCliRaw(
|
|
382
|
+
tempDir,
|
|
383
|
+
`tasks catalogs list --folder-key ${key} --output json`,
|
|
384
|
+
);
|
|
385
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
386
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
387
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
388
|
+
expect(res.Code).toBe("TaskCatalogList");
|
|
389
|
+
expect(Array.isArray(res.Data)).toBe(true);
|
|
390
|
+
},
|
|
391
|
+
EXEC_TIMEOUT,
|
|
392
|
+
);
|
|
393
|
+
});
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { RESULTS } from "@uipath/common";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
type E2EManifest,
|
|
5
|
+
MANIFEST_PATH,
|
|
6
|
+
} from "../../../tests/e2e-shared/global-setup";
|
|
7
|
+
import {
|
|
8
|
+
EXEC_TIMEOUT,
|
|
9
|
+
localCliRaw,
|
|
10
|
+
localCliRawWithArgs,
|
|
11
|
+
parseOutput,
|
|
12
|
+
readManifest,
|
|
13
|
+
} from "../../../tests/e2e-shared/helpers";
|
|
14
|
+
|
|
15
|
+
interface Envelope {
|
|
16
|
+
Result: string;
|
|
17
|
+
Code?: string;
|
|
18
|
+
Data?: unknown;
|
|
19
|
+
Message?: string;
|
|
20
|
+
Instructions?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface TaskRow {
|
|
24
|
+
Id?: number;
|
|
25
|
+
IsDeleted?: boolean;
|
|
26
|
+
Status?: string;
|
|
27
|
+
Type?: string;
|
|
28
|
+
FolderId?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { tempDir } = readManifest<E2EManifest>(MANIFEST_PATH).sharedInstall;
|
|
32
|
+
|
|
33
|
+
function listTasks(asAdmin: boolean): TaskRow[] {
|
|
34
|
+
const flag = asAdmin ? "--as-admin " : "";
|
|
35
|
+
const raw = localCliRaw(
|
|
36
|
+
tempDir,
|
|
37
|
+
`tasks list ${flag}--limit 200 --output json`,
|
|
38
|
+
);
|
|
39
|
+
if (raw.exitCode !== 0) return [];
|
|
40
|
+
return (parseOutput(raw.stdout, "json") as { Data?: TaskRow[] }).Data ?? [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function discoverTask(): { taskId: string; folderId: string } | null {
|
|
44
|
+
const envFolder = process.env.TASKS_TOOL_E2E_FOLDER_ID;
|
|
45
|
+
const envTask =
|
|
46
|
+
process.env.TASKS_TOOL_E2E_TASK_ID_COMMENTS ??
|
|
47
|
+
process.env.TASKS_TOOL_E2E_TASK_ID;
|
|
48
|
+
if (envFolder && envTask) {
|
|
49
|
+
return { taskId: envTask, folderId: envFolder };
|
|
50
|
+
}
|
|
51
|
+
for (const asAdmin of [false, true]) {
|
|
52
|
+
const task = listTasks(asAdmin).find(
|
|
53
|
+
(t) =>
|
|
54
|
+
!t.IsDeleted &&
|
|
55
|
+
t.Status !== "Completed" &&
|
|
56
|
+
t.Type === "ExternalTask" &&
|
|
57
|
+
typeof t.Id === "number" &&
|
|
58
|
+
typeof t.FolderId === "number",
|
|
59
|
+
);
|
|
60
|
+
if (task) {
|
|
61
|
+
return {
|
|
62
|
+
taskId: String(task.Id),
|
|
63
|
+
folderId: String(envFolder ?? task.FolderId),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// No actionable ExternalTask on this tenant; skip the live suite (guarded by describe.skipIf below).
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const discovered = discoverTask();
|
|
72
|
+
const taskId = discovered?.taskId ?? "";
|
|
73
|
+
const folderId = discovered?.folderId ?? "";
|
|
74
|
+
|
|
75
|
+
describe.skipIf(!discovered)("tasks comments e2e", () => {
|
|
76
|
+
it(
|
|
77
|
+
"S1: list comments for a task",
|
|
78
|
+
() => {
|
|
79
|
+
const raw = localCliRaw(
|
|
80
|
+
tempDir,
|
|
81
|
+
`tasks comments list ${taskId} --folder-id ${folderId} --output json`,
|
|
82
|
+
);
|
|
83
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
84
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
85
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
86
|
+
expect(res.Code).toBe("TaskCommentList");
|
|
87
|
+
expect(Array.isArray(res.Data)).toBe(true);
|
|
88
|
+
},
|
|
89
|
+
EXEC_TIMEOUT,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
it(
|
|
93
|
+
"S2: list caps results with --limit",
|
|
94
|
+
() => {
|
|
95
|
+
const raw = localCliRaw(
|
|
96
|
+
tempDir,
|
|
97
|
+
`tasks comments list ${taskId} --folder-id ${folderId} --limit 1 --output json`,
|
|
98
|
+
);
|
|
99
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
100
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
101
|
+
expect(res.Code).toBe("TaskCommentList");
|
|
102
|
+
expect((res.Data as unknown[]).length).toBeLessThanOrEqual(1);
|
|
103
|
+
},
|
|
104
|
+
EXEC_TIMEOUT,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
it(
|
|
108
|
+
"S3: invalid task id reports a validation error",
|
|
109
|
+
() => {
|
|
110
|
+
const raw = localCliRaw(
|
|
111
|
+
tempDir,
|
|
112
|
+
`tasks comments list not-a-number --folder-id ${folderId} --output json`,
|
|
113
|
+
);
|
|
114
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
115
|
+
expect(raw.exitCode).not.toBe(0);
|
|
116
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
117
|
+
expect(res.Message).toMatch(/^Invalid <task-id>/);
|
|
118
|
+
expect(res.Instructions).toBe("Must be a positive integer.");
|
|
119
|
+
},
|
|
120
|
+
EXEC_TIMEOUT,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
it(
|
|
124
|
+
"S4: add a comment",
|
|
125
|
+
() => {
|
|
126
|
+
const text = `sdk-cli-e2e comment ${Date.now()}`;
|
|
127
|
+
const raw = localCliRawWithArgs(tempDir, [
|
|
128
|
+
"tasks",
|
|
129
|
+
"comments",
|
|
130
|
+
"add",
|
|
131
|
+
taskId,
|
|
132
|
+
"--folder-id",
|
|
133
|
+
folderId,
|
|
134
|
+
"--text",
|
|
135
|
+
text,
|
|
136
|
+
"--output",
|
|
137
|
+
"json",
|
|
138
|
+
]);
|
|
139
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
140
|
+
expect(raw.exitCode, JSON.stringify(res)).toBe(0);
|
|
141
|
+
expect(res.Result).toBe(RESULTS.Success);
|
|
142
|
+
expect(res.Code).toBe("TaskCommentCreated");
|
|
143
|
+
expect((res.Data as { Text?: string }).Text).toBe(text);
|
|
144
|
+
},
|
|
145
|
+
EXEC_TIMEOUT,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
it(
|
|
149
|
+
"S5: empty text reports a validation error",
|
|
150
|
+
() => {
|
|
151
|
+
const raw = localCliRawWithArgs(tempDir, [
|
|
152
|
+
"tasks",
|
|
153
|
+
"comments",
|
|
154
|
+
"add",
|
|
155
|
+
taskId,
|
|
156
|
+
"--folder-id",
|
|
157
|
+
folderId,
|
|
158
|
+
"--text",
|
|
159
|
+
"",
|
|
160
|
+
"--output",
|
|
161
|
+
"json",
|
|
162
|
+
]);
|
|
163
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
164
|
+
expect(raw.exitCode).not.toBe(0);
|
|
165
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
166
|
+
expect(res.Message).toBe("--text cannot be empty");
|
|
167
|
+
},
|
|
168
|
+
EXEC_TIMEOUT,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
it(
|
|
172
|
+
"S6: add a comment to a non-existent task reports a failure",
|
|
173
|
+
() => {
|
|
174
|
+
const raw = localCliRawWithArgs(tempDir, [
|
|
175
|
+
"tasks",
|
|
176
|
+
"comments",
|
|
177
|
+
"add",
|
|
178
|
+
"999999999",
|
|
179
|
+
"--folder-id",
|
|
180
|
+
folderId,
|
|
181
|
+
"--text",
|
|
182
|
+
"Escalated",
|
|
183
|
+
"--output",
|
|
184
|
+
"json",
|
|
185
|
+
]);
|
|
186
|
+
const res = parseOutput(raw.stdout, "json") as Envelope;
|
|
187
|
+
expect(raw.exitCode).not.toBe(0);
|
|
188
|
+
expect(res.Result).toBe(RESULTS.Failure);
|
|
189
|
+
expect(res.Message).toMatch(/^Error adding comment to task/);
|
|
190
|
+
},
|
|
191
|
+
EXEC_TIMEOUT,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
it(
|
|
195
|
+
"S7: a created comment appears in the list",
|
|
196
|
+
() => {
|
|
197
|
+
const marker = `sdk-cli-e2e-marker-${Date.now()}`;
|
|
198
|
+
const create = localCliRawWithArgs(tempDir, [
|
|
199
|
+
"tasks",
|
|
200
|
+
"comments",
|
|
201
|
+
"add",
|
|
202
|
+
taskId,
|
|
203
|
+
"--folder-id",
|
|
204
|
+
folderId,
|
|
205
|
+
"--text",
|
|
206
|
+
marker,
|
|
207
|
+
"--output",
|
|
208
|
+
"json",
|
|
209
|
+
]);
|
|
210
|
+
expect(create.exitCode, create.stdout).toBe(0);
|
|
211
|
+
|
|
212
|
+
const listRaw = localCliRaw(
|
|
213
|
+
tempDir,
|
|
214
|
+
`tasks comments list ${taskId} --folder-id ${folderId} --limit 100 --output json`,
|
|
215
|
+
);
|
|
216
|
+
const list = parseOutput(listRaw.stdout, "json") as {
|
|
217
|
+
Data?: { Text?: string }[];
|
|
218
|
+
};
|
|
219
|
+
expect((list.Data ?? []).some((n) => n.Text === marker)).toBe(true);
|
|
220
|
+
},
|
|
221
|
+
EXEC_TIMEOUT,
|
|
222
|
+
);
|
|
223
|
+
});
|