@uipath/tasks-tool 1.202.0-preview.141 → 1.202.0-preview.142
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/dist/index.js +1 -1
- package/dist/{tool-23m8gpxb.js → tool-315h4wtj.js} +5 -2
- package/dist/tool.js +1 -1
- package/package.json +5 -2
- package/browser.json +0 -4
- package/src/commands/_shared/catalog-options.spec.ts +0 -114
- package/src/commands/_shared/catalog-options.ts +0 -122
- package/src/commands/_shared/connect.spec.ts +0 -52
- package/src/commands/_shared/connect.ts +0 -31
- package/src/commands/_shared/constants.ts +0 -1
- package/src/commands/_shared/folder-picker-interactive.spec.ts +0 -92
- package/src/commands/_shared/folder-picker-interactive.ts +0 -76
- package/src/commands/_shared/folder-picker.spec.ts +0 -159
- package/src/commands/_shared/folder-picker.ts +0 -113
- package/src/commands/_shared/folder-resolver.spec.ts +0 -152
- package/src/commands/_shared/folder-resolver.ts +0 -113
- package/src/commands/_shared/parse.spec.ts +0 -69
- package/src/commands/_shared/parse.ts +0 -43
- package/src/commands/catalogs.ts +0 -281
- package/src/commands/comments.ts +0 -160
- package/src/commands/data.ts +0 -137
- package/src/commands/labels.ts +0 -93
- package/src/commands/metadata.ts +0 -139
- package/src/commands/tasks.spec.ts +0 -2449
- package/src/commands/tasks.ts +0 -631
- package/src/index.ts +0 -12
- package/src/tool.ts +0 -24
- package/src/utils/sdk-client.ts +0 -30
- package/tests/assign.e2e.test.ts +0 -166
- package/tests/assignable-users.e2e.test.ts +0 -167
- package/tests/catalogs.e2e.test.ts +0 -418
- package/tests/comments.e2e.test.ts +0 -223
- package/tests/complete.e2e.test.ts +0 -207
- package/tests/data.e2e.test.ts +0 -187
- package/tests/get.e2e.test.ts +0 -103
- package/tests/labels.e2e.test.ts +0 -184
- package/tests/list.e2e.test.ts +0 -234
- package/tests/metadata.e2e.test.ts +0 -186
- package/tests/performance.e2e.test.ts +0 -32
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -1
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { RESULTS } from "@uipath/common";
|
|
2
|
-
import { beforeAll, 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
|
-
parseOutput,
|
|
11
|
-
readManifest,
|
|
12
|
-
} from "../../../tests/e2e-shared/helpers";
|
|
13
|
-
|
|
14
|
-
// Scenario: scenarios/tasks-tool/complete.md
|
|
15
|
-
// `uip tasks complete <task-id>` — completion with action and data.
|
|
16
|
-
|
|
17
|
-
interface CompleteEnvelope {
|
|
18
|
-
Result: string;
|
|
19
|
-
Code?: string;
|
|
20
|
-
Data?: unknown;
|
|
21
|
-
Message?: string;
|
|
22
|
-
Instructions?: string;
|
|
23
|
-
ErrorCode?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// `tasks list` passes through the raw SDK task payload, which is
|
|
27
|
-
// PascalCase (Id/FolderId/Type/Status/...) — confirmed against a live tenant.
|
|
28
|
-
interface TaskSummary {
|
|
29
|
-
Id?: number;
|
|
30
|
-
FolderId?: number;
|
|
31
|
-
Type?: string;
|
|
32
|
-
Status?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const { tempDir } = readManifest<E2EManifest>(MANIFEST_PATH).sharedInstall;
|
|
36
|
-
|
|
37
|
-
describe("tasks complete e2e", () => {
|
|
38
|
-
let formTaskId: string | undefined;
|
|
39
|
-
let formTaskFolderId: string | undefined;
|
|
40
|
-
let quickFormTaskId: string | undefined;
|
|
41
|
-
let quickFormTaskFolderId: string | undefined;
|
|
42
|
-
|
|
43
|
-
beforeAll(() => {
|
|
44
|
-
const listRaw = localCliRaw(tempDir, "tasks list --output json");
|
|
45
|
-
if (listRaw.exitCode !== 0) return;
|
|
46
|
-
const listRes = parseOutput(listRaw.stdout, "json") as {
|
|
47
|
-
Data?: TaskSummary[];
|
|
48
|
-
};
|
|
49
|
-
const tasks = listRes.Data ?? [];
|
|
50
|
-
|
|
51
|
-
const formTask = tasks.find(
|
|
52
|
-
(t) => t.Type === "FormTask" && t.Status !== "Completed",
|
|
53
|
-
);
|
|
54
|
-
if (formTask?.Id !== undefined) {
|
|
55
|
-
formTaskId = String(formTask.Id);
|
|
56
|
-
formTaskFolderId =
|
|
57
|
-
formTask.FolderId !== undefined
|
|
58
|
-
? String(formTask.FolderId)
|
|
59
|
-
: undefined;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const quickFormTask = tasks.find(
|
|
63
|
-
(t) => t.Type === "QuickFormTask" && t.Status === "Pending",
|
|
64
|
-
);
|
|
65
|
-
if (quickFormTask?.Id !== undefined) {
|
|
66
|
-
quickFormTaskId = String(quickFormTask.Id);
|
|
67
|
-
quickFormTaskFolderId =
|
|
68
|
-
quickFormTask.FolderId !== undefined
|
|
69
|
-
? String(quickFormTask.FolderId)
|
|
70
|
-
: undefined;
|
|
71
|
-
}
|
|
72
|
-
}, EXEC_TIMEOUT);
|
|
73
|
-
|
|
74
|
-
// ── Deterministic (no tenant) ────────────────────────────────────────────
|
|
75
|
-
|
|
76
|
-
it(
|
|
77
|
-
"S1: missing --type fails fast",
|
|
78
|
-
() => {
|
|
79
|
-
const raw = localCliRaw(
|
|
80
|
-
tempDir,
|
|
81
|
-
"tasks complete 5 --folder-id 1 --output json",
|
|
82
|
-
);
|
|
83
|
-
expect(raw.exitCode).toBe(3);
|
|
84
|
-
const res = parseOutput(raw.stdout, "json") as CompleteEnvelope;
|
|
85
|
-
expect(res.Result).toBe("ValidationError");
|
|
86
|
-
expect(res.ErrorCode).toBe("invalid_argument");
|
|
87
|
-
expect(res.Message).toBe(
|
|
88
|
-
"error: required option '--type <type>' not specified",
|
|
89
|
-
);
|
|
90
|
-
},
|
|
91
|
-
EXEC_TIMEOUT,
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
// ── Tenant read (requires login) ─────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
it(
|
|
97
|
-
"S2: missing folder fails loud when not interactive",
|
|
98
|
-
() => {
|
|
99
|
-
const raw = localCliRaw(
|
|
100
|
-
tempDir,
|
|
101
|
-
"tasks complete 5 --type FormTask --output json",
|
|
102
|
-
);
|
|
103
|
-
expect(raw.exitCode).not.toBe(0);
|
|
104
|
-
const res = parseOutput(raw.stdout, "json") as CompleteEnvelope;
|
|
105
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
106
|
-
expect(res.Message).toBe("A folder is required");
|
|
107
|
-
expect(res.Instructions).toMatch(/--folder-id/);
|
|
108
|
-
expect(res.Instructions).toMatch(/--folder-path/);
|
|
109
|
-
expect(res.Instructions).toMatch(/--folder-key/);
|
|
110
|
-
},
|
|
111
|
-
EXEC_TIMEOUT,
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
it(
|
|
115
|
-
"S3: invalid task type value is rejected",
|
|
116
|
-
() => {
|
|
117
|
-
const raw = localCliRaw(
|
|
118
|
-
tempDir,
|
|
119
|
-
"tasks complete 5 --type NotARealType --folder-id 1 --output json",
|
|
120
|
-
);
|
|
121
|
-
expect(raw.exitCode).not.toBe(0);
|
|
122
|
-
const res = parseOutput(raw.stdout, "json") as CompleteEnvelope;
|
|
123
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
124
|
-
expect(res.Message).toBe("Invalid task type 'NotARealType'");
|
|
125
|
-
expect(res.Instructions).toMatch(/FormTask/);
|
|
126
|
-
},
|
|
127
|
-
EXEC_TIMEOUT,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
it(
|
|
131
|
-
"S4: invalid JSON in --data is rejected",
|
|
132
|
-
() => {
|
|
133
|
-
const raw = localCliRaw(
|
|
134
|
-
tempDir,
|
|
135
|
-
`tasks complete 5 --type FormTask --folder-id 1 --data "{not json" --output json`,
|
|
136
|
-
);
|
|
137
|
-
expect(raw.exitCode).not.toBe(0);
|
|
138
|
-
const res = parseOutput(raw.stdout, "json") as CompleteEnvelope;
|
|
139
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
140
|
-
expect(res.Message).toBe("Invalid JSON in --data");
|
|
141
|
-
},
|
|
142
|
-
EXEC_TIMEOUT,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// ── Tenant read/write (guarded — skip if no completable FormTask exists) ──
|
|
146
|
-
//
|
|
147
|
-
// The action/button name a FormTask actually accepts comes from its form
|
|
148
|
-
// layout definition, which `uip tasks` has no way to discover — `--action`
|
|
149
|
-
// is validated purely server-side against buttons this CLI can't inspect.
|
|
150
|
-
// Confirmed live: a task with a real FormLayoutId still rejected both
|
|
151
|
-
// "Approve" and "Submit" with "Provided form action does not match any
|
|
152
|
-
// button". So this scenario can only assert that a *wrong* action fails
|
|
153
|
-
// cleanly (already covered structurally) plus, opportunistically, that a
|
|
154
|
-
// guessed common action name either completes the task or fails with
|
|
155
|
-
// that specific button-mismatch message — never a connection/auth error.
|
|
156
|
-
it(
|
|
157
|
-
"S5: completes a task with an action and data",
|
|
158
|
-
() => {
|
|
159
|
-
if (!formTaskId || !formTaskFolderId) return;
|
|
160
|
-
const raw = localCliRaw(
|
|
161
|
-
tempDir,
|
|
162
|
-
`tasks complete ${formTaskId} --type FormTask --folder-id ${formTaskFolderId} --action Submit --data "{}" --output json`,
|
|
163
|
-
);
|
|
164
|
-
const res = parseOutput(raw.stdout, "json") as CompleteEnvelope;
|
|
165
|
-
if (res.Result === RESULTS.Success) {
|
|
166
|
-
expect(raw.exitCode).toBe(0);
|
|
167
|
-
expect(res.Code).toBe("TaskCompleted");
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
// No way to know this fixture's real button name — accept the
|
|
171
|
-
// known button-mismatch failure rather than asserting success.
|
|
172
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
173
|
-
expect(res.Instructions).toBe(
|
|
174
|
-
"Provided form action does not match any button",
|
|
175
|
-
);
|
|
176
|
-
},
|
|
177
|
-
EXEC_TIMEOUT,
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// `QuickFormTask` (Maestro HITL QuickForm nodes) was previously missing
|
|
181
|
-
// from VALID_TASK_TYPES — completion was impossible for this type before
|
|
182
|
-
// this PR. "Submit" is the default single-outcome QuickForm action name;
|
|
183
|
-
// a QuickForm with custom outcomes would need a different name this CLI
|
|
184
|
-
// has no way to discover, so this scenario applies the same defensive
|
|
185
|
-
// accept-known-button-mismatch pattern as S5.
|
|
186
|
-
it(
|
|
187
|
-
"S6: completes a QuickFormTask",
|
|
188
|
-
() => {
|
|
189
|
-
if (!quickFormTaskId || !quickFormTaskFolderId) return;
|
|
190
|
-
const raw = localCliRaw(
|
|
191
|
-
tempDir,
|
|
192
|
-
`tasks complete ${quickFormTaskId} --type QuickFormTask --folder-id ${quickFormTaskFolderId} --action Submit --data "{}" --output json`,
|
|
193
|
-
);
|
|
194
|
-
const res = parseOutput(raw.stdout, "json") as CompleteEnvelope;
|
|
195
|
-
if (res.Result === RESULTS.Success) {
|
|
196
|
-
expect(raw.exitCode).toBe(0);
|
|
197
|
-
expect(res.Code).toBe("TaskCompleted");
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
201
|
-
expect(res.Instructions).toBe(
|
|
202
|
-
"Provided form action does not match any button",
|
|
203
|
-
);
|
|
204
|
-
},
|
|
205
|
-
EXEC_TIMEOUT,
|
|
206
|
-
);
|
|
207
|
-
});
|
package/tests/data.e2e.test.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
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_DATA ??
|
|
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
|
-
function getData(id: string): Envelope {
|
|
76
|
-
const raw = localCliRaw(
|
|
77
|
-
tempDir,
|
|
78
|
-
`tasks data get ${id} --folder-id ${folderId} --output json`,
|
|
79
|
-
);
|
|
80
|
-
return parseOutput(raw.stdout, "json") as Envelope;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function saveData(id: string, dataJson: string): Envelope {
|
|
84
|
-
const raw = localCliRawWithArgs(tempDir, [
|
|
85
|
-
"tasks",
|
|
86
|
-
"data",
|
|
87
|
-
"save",
|
|
88
|
-
id,
|
|
89
|
-
"--folder-id",
|
|
90
|
-
folderId,
|
|
91
|
-
"--data",
|
|
92
|
-
dataJson,
|
|
93
|
-
"--output",
|
|
94
|
-
"json",
|
|
95
|
-
]);
|
|
96
|
-
return parseOutput(raw.stdout, "json") as Envelope;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
describe.skipIf(!discovered)("tasks data e2e", () => {
|
|
100
|
-
it(
|
|
101
|
-
"S1: get task data",
|
|
102
|
-
() => {
|
|
103
|
-
const res = getData(taskId);
|
|
104
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
105
|
-
expect(res.Code).toBe("TaskData");
|
|
106
|
-
expect((res.Data as { Id?: number }).Id).toBe(Number(taskId));
|
|
107
|
-
},
|
|
108
|
-
EXEC_TIMEOUT,
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
it(
|
|
112
|
-
"S2: get a non-existent task reports a failure",
|
|
113
|
-
() => {
|
|
114
|
-
const res = getData("999999999");
|
|
115
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
116
|
-
expect(res.Message).toMatch(/^Error getting data for task/);
|
|
117
|
-
},
|
|
118
|
-
EXEC_TIMEOUT,
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
it(
|
|
122
|
-
"S3: invalid task id reports a validation error",
|
|
123
|
-
() => {
|
|
124
|
-
const res = getData("not-a-number");
|
|
125
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
126
|
-
expect(res.Message).toMatch(/^Invalid <task-id>/);
|
|
127
|
-
expect(res.Instructions).toBe("Must be a positive integer.");
|
|
128
|
-
},
|
|
129
|
-
EXEC_TIMEOUT,
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
it(
|
|
133
|
-
"S4: save task data",
|
|
134
|
-
() => {
|
|
135
|
-
const res = saveData(taskId, '{"amount":1200}');
|
|
136
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
137
|
-
expect(res.Code).toBe("TaskDataSaved");
|
|
138
|
-
},
|
|
139
|
-
EXEC_TIMEOUT,
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
it(
|
|
143
|
-
"S5: invalid data JSON reports a validation error",
|
|
144
|
-
() => {
|
|
145
|
-
const res = saveData(taskId, "not-json");
|
|
146
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
147
|
-
expect(res.Message).toBe("Invalid JSON in --data");
|
|
148
|
-
},
|
|
149
|
-
EXEC_TIMEOUT,
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
it(
|
|
153
|
-
"S6: save on a non-existent task reports a failure",
|
|
154
|
-
() => {
|
|
155
|
-
const res = saveData("999999999", '{"amount":1}');
|
|
156
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
157
|
-
expect(res.Message).toMatch(/^Error saving data for task/);
|
|
158
|
-
},
|
|
159
|
-
EXEC_TIMEOUT,
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
it(
|
|
163
|
-
"S7: non-object data is rejected before the API call",
|
|
164
|
-
() => {
|
|
165
|
-
const res = saveData(taskId, "[1,2,3]");
|
|
166
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
167
|
-
expect(res.Message).toBe("--data must be a JSON object");
|
|
168
|
-
},
|
|
169
|
-
EXEC_TIMEOUT,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
it(
|
|
173
|
-
"S8: data round-trips through save and get",
|
|
174
|
-
() => {
|
|
175
|
-
const marker = `sdk-cli-e2e-${Date.now()}`;
|
|
176
|
-
const save = saveData(taskId, `{"Marker":"${marker}"}`);
|
|
177
|
-
expect(save.Result).toBe(RESULTS.Success);
|
|
178
|
-
|
|
179
|
-
const res = getData(taskId);
|
|
180
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
181
|
-
const payload = (res.Data as { Data?: Record<string, unknown> })
|
|
182
|
-
.Data;
|
|
183
|
-
expect(payload?.Marker).toBe(marker);
|
|
184
|
-
},
|
|
185
|
-
EXEC_TIMEOUT,
|
|
186
|
-
);
|
|
187
|
-
});
|
package/tests/get.e2e.test.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
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
|
-
parseOutput,
|
|
11
|
-
readManifest,
|
|
12
|
-
} from "../../../tests/e2e-shared/helpers";
|
|
13
|
-
|
|
14
|
-
// Scenario: scenarios/tasks-tool/get.md
|
|
15
|
-
// `uip tasks get <id>` — single task detail retrieval.
|
|
16
|
-
|
|
17
|
-
interface GetEnvelope {
|
|
18
|
-
Result: string;
|
|
19
|
-
Code?: string;
|
|
20
|
-
Data?: unknown;
|
|
21
|
-
Message?: string;
|
|
22
|
-
Instructions?: string;
|
|
23
|
-
ErrorCode?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const { tempDir } = readManifest<E2EManifest>(MANIFEST_PATH).sharedInstall;
|
|
27
|
-
|
|
28
|
-
/** Discover a real task ID via `tasks list`, or undefined if the tenant has none. */
|
|
29
|
-
function discoverTaskId(): string | undefined {
|
|
30
|
-
const raw = localCliRaw(tempDir, "tasks list --limit 1 --output json");
|
|
31
|
-
if (raw.exitCode !== 0) return undefined;
|
|
32
|
-
const res = parseOutput(raw.stdout, "json") as GetEnvelope;
|
|
33
|
-
// `tasks list` passes through the raw SDK task payload, which is
|
|
34
|
-
// PascalCase (Id/...) — confirmed against a live tenant.
|
|
35
|
-
const items = res.Data as Array<{ Id?: number }> | undefined;
|
|
36
|
-
const id = items?.[0]?.Id;
|
|
37
|
-
return typeof id === "number" ? String(id) : undefined;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
describe("tasks get e2e", () => {
|
|
41
|
-
it(
|
|
42
|
-
"S1: missing id argument fails fast",
|
|
43
|
-
() => {
|
|
44
|
-
const raw = localCliRaw(tempDir, "tasks get --output json");
|
|
45
|
-
expect(raw.exitCode).toBe(3);
|
|
46
|
-
const res = parseOutput(raw.stdout, "json") as GetEnvelope;
|
|
47
|
-
expect(res.Result).toBe("ValidationError");
|
|
48
|
-
expect(res.ErrorCode).toBe("invalid_argument");
|
|
49
|
-
expect(res.Message).toBe("error: missing required argument 'id'");
|
|
50
|
-
},
|
|
51
|
-
EXEC_TIMEOUT,
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
it(
|
|
55
|
-
"S2: gets a task's details when one exists",
|
|
56
|
-
() => {
|
|
57
|
-
const id = discoverTaskId();
|
|
58
|
-
if (!id) return; // tenant has no tasks to fetch
|
|
59
|
-
const raw = localCliRaw(tempDir, `tasks get ${id} --output json`);
|
|
60
|
-
expect(raw.exitCode).toBe(0);
|
|
61
|
-
const res = parseOutput(raw.stdout, "json") as GetEnvelope;
|
|
62
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
63
|
-
expect(res.Code).toBe("TaskDetails");
|
|
64
|
-
expect(res.Data).not.toBeNull();
|
|
65
|
-
},
|
|
66
|
-
EXEC_TIMEOUT,
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
it(
|
|
70
|
-
"S3: unknown task id reports a clean not-found failure",
|
|
71
|
-
() => {
|
|
72
|
-
const raw = localCliRaw(
|
|
73
|
-
tempDir,
|
|
74
|
-
"tasks get 999999999 --output json",
|
|
75
|
-
);
|
|
76
|
-
expect(raw.exitCode).not.toBe(0);
|
|
77
|
-
const res = parseOutput(raw.stdout, "json") as GetEnvelope;
|
|
78
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
79
|
-
expect(res.Message).toBe("Error getting task '999999999'");
|
|
80
|
-
},
|
|
81
|
-
EXEC_TIMEOUT,
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
it(
|
|
85
|
-
"S4: --task-type without a folder option is rejected",
|
|
86
|
-
() => {
|
|
87
|
-
const raw = localCliRaw(
|
|
88
|
-
tempDir,
|
|
89
|
-
"tasks get 1 --task-type FormTask --output json",
|
|
90
|
-
);
|
|
91
|
-
expect(raw.exitCode).not.toBe(0);
|
|
92
|
-
const res = parseOutput(raw.stdout, "json") as GetEnvelope;
|
|
93
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
94
|
-
expect(res.Message).toBe(
|
|
95
|
-
"A folder is required when --task-type is specified",
|
|
96
|
-
);
|
|
97
|
-
expect(res.Instructions).toMatch(/--folder-id/);
|
|
98
|
-
expect(res.Instructions).toMatch(/--folder-path/);
|
|
99
|
-
expect(res.Instructions).toMatch(/--folder-key/);
|
|
100
|
-
},
|
|
101
|
-
EXEC_TIMEOUT,
|
|
102
|
-
);
|
|
103
|
-
});
|
package/tests/labels.e2e.test.ts
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
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
|
-
Message?: string;
|
|
19
|
-
Instructions?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface TaskRow {
|
|
23
|
-
Id?: number;
|
|
24
|
-
IsDeleted?: boolean;
|
|
25
|
-
Status?: string;
|
|
26
|
-
Type?: string;
|
|
27
|
-
FolderId?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const { tempDir } = readManifest<E2EManifest>(MANIFEST_PATH).sharedInstall;
|
|
31
|
-
|
|
32
|
-
function listTasks(asAdmin: boolean): TaskRow[] {
|
|
33
|
-
const flag = asAdmin ? "--as-admin " : "";
|
|
34
|
-
const raw = localCliRaw(
|
|
35
|
-
tempDir,
|
|
36
|
-
`tasks list ${flag}--limit 200 --output json`,
|
|
37
|
-
);
|
|
38
|
-
if (raw.exitCode !== 0) return [];
|
|
39
|
-
return (parseOutput(raw.stdout, "json") as { Data?: TaskRow[] }).Data ?? [];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function discoverTask(): { taskId: string; folderId: string } | null {
|
|
43
|
-
const envFolder = process.env.TASKS_TOOL_E2E_FOLDER_ID;
|
|
44
|
-
const envTask =
|
|
45
|
-
process.env.TASKS_TOOL_E2E_TASK_ID_LABELS ??
|
|
46
|
-
process.env.TASKS_TOOL_E2E_TASK_ID;
|
|
47
|
-
if (envFolder && envTask) {
|
|
48
|
-
return { taskId: envTask, folderId: envFolder };
|
|
49
|
-
}
|
|
50
|
-
for (const asAdmin of [false, true]) {
|
|
51
|
-
const task = listTasks(asAdmin).find(
|
|
52
|
-
(t) =>
|
|
53
|
-
!t.IsDeleted &&
|
|
54
|
-
t.Status !== "Completed" &&
|
|
55
|
-
t.Type === "ExternalTask" &&
|
|
56
|
-
typeof t.Id === "number" &&
|
|
57
|
-
typeof t.FolderId === "number",
|
|
58
|
-
);
|
|
59
|
-
if (task) {
|
|
60
|
-
return {
|
|
61
|
-
taskId: String(task.Id),
|
|
62
|
-
folderId: String(envFolder ?? task.FolderId),
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// No actionable ExternalTask on this tenant; skip the live suite (guarded by describe.skipIf below).
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const discovered = discoverTask();
|
|
71
|
-
const taskId = discovered?.taskId ?? "";
|
|
72
|
-
const folderId = discovered?.folderId ?? "";
|
|
73
|
-
|
|
74
|
-
function setTags(id: string, tagsJson: string): Envelope {
|
|
75
|
-
const raw = localCliRawWithArgs(tempDir, [
|
|
76
|
-
"tasks",
|
|
77
|
-
"labels",
|
|
78
|
-
id,
|
|
79
|
-
"--folder-id",
|
|
80
|
-
folderId,
|
|
81
|
-
"--labels",
|
|
82
|
-
tagsJson,
|
|
83
|
-
"--output",
|
|
84
|
-
"json",
|
|
85
|
-
]);
|
|
86
|
-
return parseOutput(raw.stdout, "json") as Envelope;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
describe.skipIf(!discovered)("tasks labels e2e", () => {
|
|
90
|
-
it(
|
|
91
|
-
"S1: set labels on a task",
|
|
92
|
-
() => {
|
|
93
|
-
const res = setTags(taskId, '[{"name":"urgent"}]');
|
|
94
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
95
|
-
expect(res.Code).toBe("TaskLabelsSaved");
|
|
96
|
-
},
|
|
97
|
-
EXEC_TIMEOUT,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
it(
|
|
101
|
-
"S2: clear labels with an empty array",
|
|
102
|
-
() => {
|
|
103
|
-
const res = setTags(taskId, "[]");
|
|
104
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
105
|
-
expect(res.Code).toBe("TaskLabelsSaved");
|
|
106
|
-
},
|
|
107
|
-
EXEC_TIMEOUT,
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
it(
|
|
111
|
-
"S3: invalid task id reports a validation error",
|
|
112
|
-
() => {
|
|
113
|
-
const res = setTags("not-a-number", "[]");
|
|
114
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
115
|
-
expect(res.Message).toMatch(/^Invalid <task-id>/);
|
|
116
|
-
expect(res.Instructions).toBe("Must be a positive integer.");
|
|
117
|
-
},
|
|
118
|
-
EXEC_TIMEOUT,
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
it(
|
|
122
|
-
"S4: set labels on a non-existent task reports a failure",
|
|
123
|
-
() => {
|
|
124
|
-
const res = setTags("999999999", '[{"name":"urgent"}]');
|
|
125
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
126
|
-
expect(res.Message).toMatch(/^Error saving labels on task/);
|
|
127
|
-
},
|
|
128
|
-
EXEC_TIMEOUT,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
it(
|
|
132
|
-
"S5: invalid JSON in --labels is rejected before the API call",
|
|
133
|
-
() => {
|
|
134
|
-
const res = setTags(taskId, "not-json");
|
|
135
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
136
|
-
expect(res.Message).toBe("Invalid JSON in --labels");
|
|
137
|
-
},
|
|
138
|
-
EXEC_TIMEOUT,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
it(
|
|
142
|
-
"S6: non-array --labels is rejected before the API call",
|
|
143
|
-
() => {
|
|
144
|
-
const res = setTags(taskId, '{"name":"urgent"}');
|
|
145
|
-
expect(res.Result).toBe(RESULTS.Failure);
|
|
146
|
-
expect(res.Message).toBe("--labels must be a JSON array");
|
|
147
|
-
},
|
|
148
|
-
EXEC_TIMEOUT,
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
it(
|
|
152
|
-
"S7: set a label with a display name and value",
|
|
153
|
-
() => {
|
|
154
|
-
const res = setTags(
|
|
155
|
-
taskId,
|
|
156
|
-
'[{"name":"region","displayName":"Region","displayValue":"EMEA"}]',
|
|
157
|
-
);
|
|
158
|
-
expect(res.Result).toBe(RESULTS.Success);
|
|
159
|
-
expect(res.Code).toBe("TaskLabelsSaved");
|
|
160
|
-
},
|
|
161
|
-
EXEC_TIMEOUT,
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
it(
|
|
165
|
-
"S8: labels round-trip through set and read-back",
|
|
166
|
-
() => {
|
|
167
|
-
const marker = `sdkclie2e${Date.now()}`;
|
|
168
|
-
const set = setTags(taskId, `[{"name":"${marker}"}]`);
|
|
169
|
-
expect(set.Result).toBe(RESULTS.Success);
|
|
170
|
-
|
|
171
|
-
const getRaw = localCliRaw(
|
|
172
|
-
tempDir,
|
|
173
|
-
`tasks get ${taskId} --output json`,
|
|
174
|
-
);
|
|
175
|
-
const task = parseOutput(getRaw.stdout, "json") as {
|
|
176
|
-
Data?: { Tags?: { Name?: string }[] };
|
|
177
|
-
};
|
|
178
|
-
expect((task.Data?.Tags ?? []).some((t) => t.Name === marker)).toBe(
|
|
179
|
-
true,
|
|
180
|
-
);
|
|
181
|
-
},
|
|
182
|
-
EXEC_TIMEOUT,
|
|
183
|
-
);
|
|
184
|
-
});
|