@dichovsky/testrail-api-client 1.0.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.
- package/LICENSE +21 -0
- package/README.md +537 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +268 -0
- package/dist/client-core.d.ts +103 -0
- package/dist/client-core.js +601 -0
- package/dist/client.d.ts +652 -0
- package/dist/client.js +1106 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +14 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.js +29 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +762 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +13 -0
- package/package.json +76 -0
- package/src/cli.ts +287 -0
- package/src/client-core.ts +761 -0
- package/src/client.ts +1338 -0
- package/src/constants.ts +15 -0
- package/src/errors.ts +28 -0
- package/src/index.ts +74 -0
- package/src/types.ts +853 -0
- package/src/utils.ts +15 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,1338 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Case,
|
|
3
|
+
Suite,
|
|
4
|
+
Section,
|
|
5
|
+
Project,
|
|
6
|
+
Plan,
|
|
7
|
+
Run,
|
|
8
|
+
Test,
|
|
9
|
+
Result,
|
|
10
|
+
Milestone,
|
|
11
|
+
User,
|
|
12
|
+
Status,
|
|
13
|
+
Priority,
|
|
14
|
+
AddCasePayload,
|
|
15
|
+
UpdateCasePayload,
|
|
16
|
+
AddSuitePayload,
|
|
17
|
+
UpdateSuitePayload,
|
|
18
|
+
AddPlanPayload,
|
|
19
|
+
UpdatePlanPayload,
|
|
20
|
+
AddPlanEntryPayload,
|
|
21
|
+
UpdatePlanEntryPayload,
|
|
22
|
+
PlanEntry,
|
|
23
|
+
AddRunPayload,
|
|
24
|
+
UpdateRunPayload,
|
|
25
|
+
AddResultPayload,
|
|
26
|
+
AddResultsForCasesPayload,
|
|
27
|
+
AddSectionPayload,
|
|
28
|
+
UpdateSectionPayload,
|
|
29
|
+
AddMilestonePayload,
|
|
30
|
+
UpdateMilestonePayload,
|
|
31
|
+
AddProjectPayload,
|
|
32
|
+
UpdateProjectPayload,
|
|
33
|
+
GetPlansOptions,
|
|
34
|
+
GetTestsOptions,
|
|
35
|
+
GetResultsOptions,
|
|
36
|
+
GetMilestonesOptions,
|
|
37
|
+
GetRunsOptions,
|
|
38
|
+
GetCasesOptions,
|
|
39
|
+
ResultField,
|
|
40
|
+
CaseField,
|
|
41
|
+
CaseType,
|
|
42
|
+
Template,
|
|
43
|
+
ConfigurationGroup,
|
|
44
|
+
Configuration,
|
|
45
|
+
AddConfigurationGroupPayload,
|
|
46
|
+
UpdateConfigurationGroupPayload,
|
|
47
|
+
AddConfigurationPayload,
|
|
48
|
+
UpdateConfigurationPayload,
|
|
49
|
+
AddUserPayload,
|
|
50
|
+
UpdateUserPayload,
|
|
51
|
+
Role,
|
|
52
|
+
Group,
|
|
53
|
+
AddGroupPayload,
|
|
54
|
+
UpdateGroupPayload,
|
|
55
|
+
Attachment,
|
|
56
|
+
SharedStep,
|
|
57
|
+
AddSharedStepPayload,
|
|
58
|
+
UpdateSharedStepPayload,
|
|
59
|
+
Variable,
|
|
60
|
+
AddVariablePayload,
|
|
61
|
+
UpdateVariablePayload,
|
|
62
|
+
Dataset,
|
|
63
|
+
AddDatasetPayload,
|
|
64
|
+
UpdateDatasetPayload,
|
|
65
|
+
Report,
|
|
66
|
+
ReportResult,
|
|
67
|
+
} from './types.js';
|
|
68
|
+
import { TestRailClientCore } from './client-core.js';
|
|
69
|
+
import { TestRailValidationError } from './errors.js';
|
|
70
|
+
|
|
71
|
+
export { TestRailApiError, TestRailValidationError } from './errors.js';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* TestRail API Client
|
|
75
|
+
*
|
|
76
|
+
* Type-safe client covering Projects, Suites, Sections, Cases, Plans, Runs,
|
|
77
|
+
* Tests, Results, Milestones, Users, Statuses, and Priorities.
|
|
78
|
+
* Extends {@link TestRailClientCore} for HTTP pipeline, caching, rate limiting, and retry.
|
|
79
|
+
*/
|
|
80
|
+
export class TestRailClient extends TestRailClientCore {
|
|
81
|
+
// ── Projects ──────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get a project by ID.
|
|
85
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
86
|
+
* @throws {TestRailApiError} When the API request fails
|
|
87
|
+
*/
|
|
88
|
+
async getProject(projectId: number): Promise<Project> {
|
|
89
|
+
this.validateId(projectId, 'projectId');
|
|
90
|
+
return this.request<Project>('GET', `get_project/${projectId}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get all projects.
|
|
95
|
+
* @throws {TestRailValidationError} When limit or offset is invalid
|
|
96
|
+
* @throws {TestRailApiError} When the API request fails
|
|
97
|
+
*/
|
|
98
|
+
async getProjects(limit?: number, offset?: number): Promise<Project[]> {
|
|
99
|
+
this.validatePaginationParams(limit, offset);
|
|
100
|
+
const endpoint = this.buildEndpoint('get_projects', { limit, offset });
|
|
101
|
+
const response = await this.request<{ projects: Project[] }>('GET', endpoint);
|
|
102
|
+
return response.projects ?? [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Add a new project.
|
|
107
|
+
* @throws {TestRailApiError} When the API request fails
|
|
108
|
+
*/
|
|
109
|
+
async addProject(payload: AddProjectPayload): Promise<Project> {
|
|
110
|
+
return this.request<Project>('POST', 'add_project', payload);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Update an existing project.
|
|
115
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
116
|
+
* @throws {TestRailApiError} When the API request fails
|
|
117
|
+
*/
|
|
118
|
+
async updateProject(projectId: number, payload: UpdateProjectPayload): Promise<Project> {
|
|
119
|
+
this.validateId(projectId, 'projectId');
|
|
120
|
+
return this.request<Project>('POST', `update_project/${projectId}`, payload);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Delete a project.
|
|
125
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
126
|
+
* @throws {TestRailApiError} When the API request fails
|
|
127
|
+
*/
|
|
128
|
+
async deleteProject(projectId: number): Promise<void> {
|
|
129
|
+
this.validateId(projectId, 'projectId');
|
|
130
|
+
await this.request<void>('POST', `delete_project/${projectId}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ── Suites ────────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get a suite by ID.
|
|
137
|
+
* @throws {TestRailValidationError} When suiteId is invalid
|
|
138
|
+
* @throws {TestRailApiError} When the API request fails
|
|
139
|
+
*/
|
|
140
|
+
async getSuite(suiteId: number): Promise<Suite> {
|
|
141
|
+
this.validateId(suiteId, 'suiteId');
|
|
142
|
+
return this.request<Suite>('GET', `get_suite/${suiteId}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get all suites for a project.
|
|
147
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
148
|
+
* @throws {TestRailApiError} When the API request fails
|
|
149
|
+
*/
|
|
150
|
+
async getSuites(projectId: number): Promise<Suite[]> {
|
|
151
|
+
this.validateId(projectId, 'projectId');
|
|
152
|
+
return this.request<Suite[]>('GET', `get_suites/${projectId}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Add a suite to a project.
|
|
157
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
158
|
+
* @throws {TestRailApiError} When the API request fails
|
|
159
|
+
*/
|
|
160
|
+
async addSuite(projectId: number, payload: AddSuitePayload): Promise<Suite> {
|
|
161
|
+
this.validateId(projectId, 'projectId');
|
|
162
|
+
return this.request<Suite>('POST', `add_suite/${projectId}`, payload);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Update a suite.
|
|
167
|
+
* @throws {TestRailValidationError} When suiteId is invalid
|
|
168
|
+
* @throws {TestRailApiError} When the API request fails
|
|
169
|
+
*/
|
|
170
|
+
async updateSuite(suiteId: number, payload: UpdateSuitePayload): Promise<Suite> {
|
|
171
|
+
this.validateId(suiteId, 'suiteId');
|
|
172
|
+
return this.request<Suite>('POST', `update_suite/${suiteId}`, payload);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Delete a suite.
|
|
177
|
+
* @throws {TestRailValidationError} When suiteId is invalid
|
|
178
|
+
* @throws {TestRailApiError} When the API request fails
|
|
179
|
+
*/
|
|
180
|
+
async deleteSuite(suiteId: number): Promise<void> {
|
|
181
|
+
this.validateId(suiteId, 'suiteId');
|
|
182
|
+
await this.request<void>('POST', `delete_suite/${suiteId}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── Sections ──────────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get a section by ID.
|
|
189
|
+
* @throws {TestRailValidationError} When sectionId is invalid
|
|
190
|
+
* @throws {TestRailApiError} When the API request fails
|
|
191
|
+
*/
|
|
192
|
+
async getSection(sectionId: number): Promise<Section> {
|
|
193
|
+
this.validateId(sectionId, 'sectionId');
|
|
194
|
+
return this.request<Section>('GET', `get_section/${sectionId}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get all sections for a project, optionally filtered by suite.
|
|
199
|
+
* @param options.suiteId - Optional suite filter
|
|
200
|
+
* @param options.limit - Optional maximum number of results to return
|
|
201
|
+
* @param options.offset - Optional number of results to skip
|
|
202
|
+
* @throws {TestRailValidationError} When projectId or suiteId is invalid
|
|
203
|
+
* @throws {TestRailApiError} When the API request fails
|
|
204
|
+
*/
|
|
205
|
+
async getSections(
|
|
206
|
+
projectId: number,
|
|
207
|
+
options?: { suiteId?: number; limit?: number; offset?: number },
|
|
208
|
+
): Promise<Section[]> {
|
|
209
|
+
this.validateId(projectId, 'projectId');
|
|
210
|
+
const { suiteId, limit, offset } = options ?? {};
|
|
211
|
+
if (suiteId !== undefined) {
|
|
212
|
+
this.validateId(suiteId, 'suiteId');
|
|
213
|
+
}
|
|
214
|
+
this.validatePaginationParams(limit, offset);
|
|
215
|
+
const endpoint = this.buildEndpoint(`get_sections/${projectId}`, { suite_id: suiteId, limit, offset });
|
|
216
|
+
const response = await this.request<{ sections: Section[] }>('GET', endpoint);
|
|
217
|
+
return response.sections ?? [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Add a new section to a project.
|
|
222
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
223
|
+
* @throws {TestRailApiError} When the API request fails
|
|
224
|
+
*/
|
|
225
|
+
async addSection(projectId: number, payload: AddSectionPayload): Promise<Section> {
|
|
226
|
+
this.validateId(projectId, 'projectId');
|
|
227
|
+
return this.request<Section>('POST', `add_section/${projectId}`, payload);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Update an existing section.
|
|
232
|
+
* @throws {TestRailValidationError} When sectionId is invalid
|
|
233
|
+
* @throws {TestRailApiError} When the API request fails
|
|
234
|
+
*/
|
|
235
|
+
async updateSection(sectionId: number, payload: UpdateSectionPayload): Promise<Section> {
|
|
236
|
+
this.validateId(sectionId, 'sectionId');
|
|
237
|
+
return this.request<Section>('POST', `update_section/${sectionId}`, payload);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Delete a section.
|
|
242
|
+
* @throws {TestRailValidationError} When sectionId is invalid
|
|
243
|
+
* @throws {TestRailApiError} When the API request fails
|
|
244
|
+
*/
|
|
245
|
+
async deleteSection(sectionId: number): Promise<void> {
|
|
246
|
+
this.validateId(sectionId, 'sectionId');
|
|
247
|
+
await this.request<void>('POST', `delete_section/${sectionId}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Cases ─────────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get a case by ID.
|
|
254
|
+
* @throws {TestRailValidationError} When caseId is invalid
|
|
255
|
+
* @throws {TestRailApiError} When the API request fails
|
|
256
|
+
*/
|
|
257
|
+
async getCase(caseId: number): Promise<Case> {
|
|
258
|
+
this.validateId(caseId, 'caseId');
|
|
259
|
+
return this.request<Case>('GET', `get_case/${caseId}`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get all cases for a project with optional filters.
|
|
264
|
+
* @param options.suiteId - Return only cases in this suite
|
|
265
|
+
* @param options.sectionId - Return only cases in this section
|
|
266
|
+
* @param options.typeId - Return only cases of this type
|
|
267
|
+
* @param options.priorityId - Return only cases with this priority
|
|
268
|
+
* @param options.templateId - Return only cases using this template
|
|
269
|
+
* @param options.milestoneId - Return only cases linked to this milestone
|
|
270
|
+
* @param options.createdAfter - Return only cases created after this Unix timestamp
|
|
271
|
+
* @param options.createdBefore - Return only cases created before this Unix timestamp
|
|
272
|
+
* @param options.updatedAfter - Return only cases updated after this Unix timestamp
|
|
273
|
+
* @param options.updatedBefore - Return only cases updated before this Unix timestamp
|
|
274
|
+
* @param options.limit - Maximum number of cases to return
|
|
275
|
+
* @param options.offset - Pagination offset
|
|
276
|
+
* @throws {TestRailValidationError} When any provided ID is invalid
|
|
277
|
+
* @throws {TestRailApiError} When the API request fails
|
|
278
|
+
*/
|
|
279
|
+
async getCases(projectId: number, options?: GetCasesOptions): Promise<Case[]> {
|
|
280
|
+
this.validateId(projectId, 'projectId');
|
|
281
|
+
const {
|
|
282
|
+
suiteId,
|
|
283
|
+
sectionId,
|
|
284
|
+
typeId,
|
|
285
|
+
priorityId,
|
|
286
|
+
templateId,
|
|
287
|
+
milestoneId,
|
|
288
|
+
createdAfter,
|
|
289
|
+
createdBefore,
|
|
290
|
+
updatedAfter,
|
|
291
|
+
updatedBefore,
|
|
292
|
+
limit,
|
|
293
|
+
offset,
|
|
294
|
+
} = options ?? {};
|
|
295
|
+
if (suiteId !== undefined) this.validateId(suiteId, 'suiteId');
|
|
296
|
+
if (sectionId !== undefined) this.validateId(sectionId, 'sectionId');
|
|
297
|
+
if (typeId !== undefined) this.validateId(typeId, 'typeId');
|
|
298
|
+
if (priorityId !== undefined) this.validateId(priorityId, 'priorityId');
|
|
299
|
+
if (templateId !== undefined) this.validateId(templateId, 'templateId');
|
|
300
|
+
if (milestoneId !== undefined) this.validateId(milestoneId, 'milestoneId');
|
|
301
|
+
this.validatePaginationParams(limit, offset);
|
|
302
|
+
const endpoint = this.buildEndpoint(`get_cases/${projectId}`, {
|
|
303
|
+
suite_id: suiteId,
|
|
304
|
+
section_id: sectionId,
|
|
305
|
+
type_id: typeId,
|
|
306
|
+
priority_id: priorityId,
|
|
307
|
+
template_id: templateId,
|
|
308
|
+
milestone_id: milestoneId,
|
|
309
|
+
created_after: createdAfter,
|
|
310
|
+
created_before: createdBefore,
|
|
311
|
+
updated_after: updatedAfter,
|
|
312
|
+
updated_before: updatedBefore,
|
|
313
|
+
limit,
|
|
314
|
+
offset,
|
|
315
|
+
});
|
|
316
|
+
const response = await this.request<{ cases: Case[] }>('GET', endpoint);
|
|
317
|
+
return response.cases ?? [];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Add a new case to a section.
|
|
322
|
+
* @throws {TestRailValidationError} When sectionId is invalid
|
|
323
|
+
* @throws {TestRailApiError} When the API request fails
|
|
324
|
+
*/
|
|
325
|
+
async addCase(sectionId: number, payload: AddCasePayload): Promise<Case> {
|
|
326
|
+
this.validateId(sectionId, 'sectionId');
|
|
327
|
+
return this.request<Case>('POST', `add_case/${sectionId}`, payload);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Update an existing case.
|
|
332
|
+
* @throws {TestRailValidationError} When caseId is invalid
|
|
333
|
+
* @throws {TestRailApiError} When the API request fails
|
|
334
|
+
*/
|
|
335
|
+
async updateCase(caseId: number, payload: UpdateCasePayload): Promise<Case> {
|
|
336
|
+
this.validateId(caseId, 'caseId');
|
|
337
|
+
return this.request<Case>('POST', `update_case/${caseId}`, payload);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Delete a case.
|
|
342
|
+
* @throws {TestRailValidationError} When caseId is invalid
|
|
343
|
+
* @throws {TestRailApiError} When the API request fails
|
|
344
|
+
*/
|
|
345
|
+
async deleteCase(caseId: number): Promise<void> {
|
|
346
|
+
this.validateId(caseId, 'caseId');
|
|
347
|
+
await this.request<void>('POST', `delete_case/${caseId}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ── Plans ─────────────────────────────────────────────────────────────────
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Get a plan by ID.
|
|
354
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
355
|
+
* @throws {TestRailApiError} When the API request fails
|
|
356
|
+
*/
|
|
357
|
+
async getPlan(planId: number): Promise<Plan> {
|
|
358
|
+
this.validateId(planId, 'planId');
|
|
359
|
+
return this.request<Plan>('GET', `get_plan/${planId}`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get all plans for a project with optional filters.
|
|
364
|
+
* @param projectId - The project ID
|
|
365
|
+
* @param options - Optional filter parameters (created_after, created_before, created_by,
|
|
366
|
+
* is_completed, milestone_id, limit, offset)
|
|
367
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
368
|
+
* @throws {TestRailApiError} When the API request fails
|
|
369
|
+
*/
|
|
370
|
+
async getPlans(projectId: number, options?: GetPlansOptions): Promise<Plan[]> {
|
|
371
|
+
this.validateId(projectId, 'projectId');
|
|
372
|
+
this.validatePaginationParams(options?.limit, options?.offset);
|
|
373
|
+
const endpoint = this.buildEndpoint(`get_plans/${projectId}`, {
|
|
374
|
+
created_after: options?.created_after,
|
|
375
|
+
created_before: options?.created_before,
|
|
376
|
+
created_by: this.serializeIdList(options?.created_by),
|
|
377
|
+
is_completed: options?.is_completed,
|
|
378
|
+
milestone_id: this.serializeIdList(options?.milestone_id),
|
|
379
|
+
limit: options?.limit,
|
|
380
|
+
offset: options?.offset,
|
|
381
|
+
});
|
|
382
|
+
const response = await this.request<{ plans: Plan[] }>('GET', endpoint);
|
|
383
|
+
return response.plans ?? [];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Add a new plan to a project.
|
|
388
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
389
|
+
* @throws {TestRailApiError} When the API request fails
|
|
390
|
+
*/
|
|
391
|
+
async addPlan(projectId: number, payload: AddPlanPayload): Promise<Plan> {
|
|
392
|
+
this.validateId(projectId, 'projectId');
|
|
393
|
+
return this.request<Plan>('POST', `add_plan/${projectId}`, payload);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Update a plan.
|
|
398
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
399
|
+
* @throws {TestRailApiError} When the API request fails
|
|
400
|
+
*/
|
|
401
|
+
async updatePlan(planId: number, payload: UpdatePlanPayload): Promise<Plan> {
|
|
402
|
+
this.validateId(planId, 'planId');
|
|
403
|
+
return this.request<Plan>('POST', `update_plan/${planId}`, payload);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Close a plan.
|
|
408
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
409
|
+
* @throws {TestRailApiError} When the API request fails
|
|
410
|
+
*/
|
|
411
|
+
async closePlan(planId: number): Promise<Plan> {
|
|
412
|
+
this.validateId(planId, 'planId');
|
|
413
|
+
return this.request<Plan>('POST', `close_plan/${planId}`);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Delete a plan.
|
|
418
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
419
|
+
* @throws {TestRailApiError} When the API request fails
|
|
420
|
+
*/
|
|
421
|
+
async deletePlan(planId: number): Promise<void> {
|
|
422
|
+
this.validateId(planId, 'planId');
|
|
423
|
+
await this.request<void>('POST', `delete_plan/${planId}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Add a plan entry (run) to a plan.
|
|
428
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
429
|
+
* @throws {TestRailApiError} When the API request fails
|
|
430
|
+
*/
|
|
431
|
+
async addPlanEntry(planId: number, payload: AddPlanEntryPayload): Promise<PlanEntry> {
|
|
432
|
+
this.validateId(planId, 'planId');
|
|
433
|
+
return this.request<PlanEntry>('POST', `add_plan_entry/${planId}`, payload);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Update an existing plan entry.
|
|
438
|
+
* @throws {TestRailValidationError} When planId is invalid or entryId is not a non-empty string
|
|
439
|
+
* @throws {TestRailApiError} When the API request fails
|
|
440
|
+
*/
|
|
441
|
+
async updatePlanEntry(planId: number, entryId: string, payload: UpdatePlanEntryPayload): Promise<PlanEntry> {
|
|
442
|
+
this.validateId(planId, 'planId');
|
|
443
|
+
this.validateEntryId(entryId);
|
|
444
|
+
return this.request<PlanEntry>('POST', `update_plan_entry/${planId}/${entryId}`, payload);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Delete a plan entry.
|
|
449
|
+
* @throws {TestRailValidationError} When planId is invalid or entryId is not a non-empty string
|
|
450
|
+
* @throws {TestRailApiError} When the API request fails
|
|
451
|
+
*/
|
|
452
|
+
async deletePlanEntry(planId: number, entryId: string): Promise<void> {
|
|
453
|
+
this.validateId(planId, 'planId');
|
|
454
|
+
this.validateEntryId(entryId);
|
|
455
|
+
await this.request<void>('POST', `delete_plan_entry/${planId}/${entryId}`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// ── Runs ──────────────────────────────────────────────────────────────────
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Get a run by ID.
|
|
462
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
463
|
+
* @throws {TestRailApiError} When the API request fails
|
|
464
|
+
*/
|
|
465
|
+
async getRun(runId: number): Promise<Run> {
|
|
466
|
+
this.validateId(runId, 'runId');
|
|
467
|
+
return this.request<Run>('GET', `get_run/${runId}`);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Get all runs for a project, with optional filters.
|
|
472
|
+
* @param projectId - The project ID
|
|
473
|
+
* @param options - Optional filters: createdAfter, createdBefore, createdBy, isCompleted,
|
|
474
|
+
* milestoneId, refsFilter, suiteId, limit, offset
|
|
475
|
+
* @throws {TestRailValidationError} When projectId or pagination params are invalid
|
|
476
|
+
* @throws {TestRailApiError} When the API request fails
|
|
477
|
+
*/
|
|
478
|
+
async getRuns(projectId: number, options?: GetRunsOptions): Promise<Run[]> {
|
|
479
|
+
this.validateId(projectId, 'projectId');
|
|
480
|
+
const { createdAfter, createdBefore, createdBy, isCompleted, milestoneId, refsFilter, suiteId, limit, offset } =
|
|
481
|
+
options ?? {};
|
|
482
|
+
this.validatePaginationParams(limit, offset);
|
|
483
|
+
if (milestoneId !== undefined) {
|
|
484
|
+
this.validateId(milestoneId, 'milestoneId');
|
|
485
|
+
}
|
|
486
|
+
if (suiteId !== undefined) {
|
|
487
|
+
this.validateId(suiteId, 'suiteId');
|
|
488
|
+
}
|
|
489
|
+
if (createdBy !== undefined) {
|
|
490
|
+
createdBy.forEach((userId) => this.validateId(userId, 'createdBy'));
|
|
491
|
+
}
|
|
492
|
+
const createdByFilter = createdBy && createdBy.length > 0 ? createdBy.join(',') : undefined;
|
|
493
|
+
const endpoint = this.buildEndpoint(`get_runs/${projectId}`, {
|
|
494
|
+
created_after: createdAfter,
|
|
495
|
+
created_before: createdBefore,
|
|
496
|
+
created_by: createdByFilter,
|
|
497
|
+
is_completed: isCompleted !== undefined ? (isCompleted ? 1 : 0) : undefined,
|
|
498
|
+
milestone_id: milestoneId,
|
|
499
|
+
refs_filter: refsFilter,
|
|
500
|
+
suite_id: suiteId,
|
|
501
|
+
limit,
|
|
502
|
+
offset,
|
|
503
|
+
});
|
|
504
|
+
const response = await this.request<{ runs: Run[] }>('GET', endpoint);
|
|
505
|
+
return response.runs ?? [];
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Add a new run to a project.
|
|
510
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
511
|
+
* @throws {TestRailApiError} When the API request fails
|
|
512
|
+
*/
|
|
513
|
+
async addRun(projectId: number, payload: AddRunPayload): Promise<Run> {
|
|
514
|
+
this.validateId(projectId, 'projectId');
|
|
515
|
+
return this.request<Run>('POST', `add_run/${projectId}`, payload);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Update a run.
|
|
520
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
521
|
+
* @throws {TestRailApiError} When the API request fails
|
|
522
|
+
*/
|
|
523
|
+
async updateRun(runId: number, payload: UpdateRunPayload): Promise<Run> {
|
|
524
|
+
this.validateId(runId, 'runId');
|
|
525
|
+
return this.request<Run>('POST', `update_run/${runId}`, payload);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Close a run.
|
|
530
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
531
|
+
* @throws {TestRailApiError} When the API request fails
|
|
532
|
+
*/
|
|
533
|
+
async closeRun(runId: number): Promise<Run> {
|
|
534
|
+
this.validateId(runId, 'runId');
|
|
535
|
+
return this.request<Run>('POST', `close_run/${runId}`);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Delete a run.
|
|
540
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
541
|
+
* @throws {TestRailApiError} When the API request fails
|
|
542
|
+
*/
|
|
543
|
+
async deleteRun(runId: number): Promise<void> {
|
|
544
|
+
this.validateId(runId, 'runId');
|
|
545
|
+
await this.request<void>('POST', `delete_run/${runId}`);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// ── Tests ─────────────────────────────────────────────────────────────────
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Get a test by ID.
|
|
552
|
+
* @throws {TestRailValidationError} When testId is invalid
|
|
553
|
+
* @throws {TestRailApiError} When the API request fails
|
|
554
|
+
*/
|
|
555
|
+
async getTest(testId: number): Promise<Test> {
|
|
556
|
+
this.validateId(testId, 'testId');
|
|
557
|
+
return this.request<Test>('GET', `get_test/${testId}`);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get all tests for a run with optional filters.
|
|
562
|
+
* @param runId - The run ID
|
|
563
|
+
* @param options - Optional filter parameters (status_id, limit, offset)
|
|
564
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
565
|
+
* @throws {TestRailApiError} When the API request fails
|
|
566
|
+
*/
|
|
567
|
+
async getTests(runId: number, options?: GetTestsOptions): Promise<Test[]> {
|
|
568
|
+
this.validateId(runId, 'runId');
|
|
569
|
+
this.validatePaginationParams(options?.limit, options?.offset);
|
|
570
|
+
const endpoint = this.buildEndpoint(`get_tests/${runId}`, {
|
|
571
|
+
status_id: this.serializeIdList(options?.status_id),
|
|
572
|
+
limit: options?.limit,
|
|
573
|
+
offset: options?.offset,
|
|
574
|
+
});
|
|
575
|
+
const response = await this.request<{ tests: Test[] }>('GET', endpoint);
|
|
576
|
+
return response.tests ?? [];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// ── Results ───────────────────────────────────────────────────────────────
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Get results for a test with optional filters.
|
|
583
|
+
* @param testId - The test ID
|
|
584
|
+
* @param options - Optional filter parameters (created_after, created_before, created_by,
|
|
585
|
+
* status_id, limit, offset)
|
|
586
|
+
* @throws {TestRailValidationError} When testId is invalid
|
|
587
|
+
* @throws {TestRailApiError} When the API request fails
|
|
588
|
+
*/
|
|
589
|
+
async getResults(testId: number, options?: GetResultsOptions): Promise<Result[]> {
|
|
590
|
+
this.validateId(testId, 'testId');
|
|
591
|
+
this.validatePaginationParams(options?.limit, options?.offset);
|
|
592
|
+
const endpoint = this.buildEndpoint(`get_results/${testId}`, {
|
|
593
|
+
created_after: options?.created_after,
|
|
594
|
+
created_before: options?.created_before,
|
|
595
|
+
created_by: this.serializeIdList(options?.created_by),
|
|
596
|
+
status_id: this.serializeIdList(options?.status_id),
|
|
597
|
+
limit: options?.limit,
|
|
598
|
+
offset: options?.offset,
|
|
599
|
+
});
|
|
600
|
+
const response = await this.request<{ results: Result[] }>('GET', endpoint);
|
|
601
|
+
return response.results ?? [];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Get results for a specific case within a run with optional filters.
|
|
606
|
+
* @param runId - The run ID
|
|
607
|
+
* @param caseId - The case ID
|
|
608
|
+
* @param options - Optional filter parameters (created_after, created_before, created_by,
|
|
609
|
+
* status_id, limit, offset)
|
|
610
|
+
* @throws {TestRailValidationError} When runId or caseId is invalid
|
|
611
|
+
* @throws {TestRailApiError} When the API request fails
|
|
612
|
+
*/
|
|
613
|
+
async getResultsForCase(runId: number, caseId: number, options?: GetResultsOptions): Promise<Result[]> {
|
|
614
|
+
this.validateId(runId, 'runId');
|
|
615
|
+
this.validateId(caseId, 'caseId');
|
|
616
|
+
this.validatePaginationParams(options?.limit, options?.offset);
|
|
617
|
+
const endpoint = this.buildEndpoint(`get_results_for_case/${runId}/${caseId}`, {
|
|
618
|
+
created_after: options?.created_after,
|
|
619
|
+
created_before: options?.created_before,
|
|
620
|
+
created_by: this.serializeIdList(options?.created_by),
|
|
621
|
+
status_id: this.serializeIdList(options?.status_id),
|
|
622
|
+
limit: options?.limit,
|
|
623
|
+
offset: options?.offset,
|
|
624
|
+
});
|
|
625
|
+
const response = await this.request<{ results: Result[] }>('GET', endpoint);
|
|
626
|
+
return response.results ?? [];
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Get all results for a run with optional filters.
|
|
631
|
+
* @param runId - The run ID
|
|
632
|
+
* @param options - Optional filter parameters (created_after, created_before, created_by,
|
|
633
|
+
* status_id, limit, offset)
|
|
634
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
635
|
+
* @throws {TestRailApiError} When the API request fails
|
|
636
|
+
*/
|
|
637
|
+
async getResultsForRun(runId: number, options?: GetResultsOptions): Promise<Result[]> {
|
|
638
|
+
this.validateId(runId, 'runId');
|
|
639
|
+
this.validatePaginationParams(options?.limit, options?.offset);
|
|
640
|
+
const endpoint = this.buildEndpoint(`get_results_for_run/${runId}`, {
|
|
641
|
+
created_after: options?.created_after,
|
|
642
|
+
created_before: options?.created_before,
|
|
643
|
+
created_by: this.serializeIdList(options?.created_by),
|
|
644
|
+
status_id: this.serializeIdList(options?.status_id),
|
|
645
|
+
limit: options?.limit,
|
|
646
|
+
offset: options?.offset,
|
|
647
|
+
});
|
|
648
|
+
const response = await this.request<{ results: Result[] }>('GET', endpoint);
|
|
649
|
+
return response.results ?? [];
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Add a result for a test.
|
|
654
|
+
* @throws {TestRailValidationError} When testId is invalid
|
|
655
|
+
* @throws {TestRailApiError} When the API request fails
|
|
656
|
+
*/
|
|
657
|
+
async addResult(testId: number, payload: AddResultPayload): Promise<Result> {
|
|
658
|
+
this.validateId(testId, 'testId');
|
|
659
|
+
return this.request<Result>('POST', `add_result/${testId}`, payload);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Add a result for a specific case within a run.
|
|
664
|
+
* @throws {TestRailValidationError} When runId or caseId is invalid
|
|
665
|
+
* @throws {TestRailApiError} When the API request fails
|
|
666
|
+
*/
|
|
667
|
+
async addResultForCase(runId: number, caseId: number, payload: AddResultPayload): Promise<Result> {
|
|
668
|
+
this.validateId(runId, 'runId');
|
|
669
|
+
this.validateId(caseId, 'caseId');
|
|
670
|
+
return this.request<Result>('POST', `add_result_for_case/${runId}/${caseId}`, payload);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Add multiple results for cases in a run.
|
|
675
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
676
|
+
* @throws {TestRailApiError} When the API request fails
|
|
677
|
+
*/
|
|
678
|
+
async addResultsForCases(runId: number, payload: AddResultsForCasesPayload): Promise<Result[]> {
|
|
679
|
+
this.validateId(runId, 'runId');
|
|
680
|
+
return this.request<Result[]>('POST', `add_results_for_cases/${runId}`, payload);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// ── Milestones ────────────────────────────────────────────────────────────
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Get a milestone by ID.
|
|
687
|
+
* @throws {TestRailValidationError} When milestoneId is invalid
|
|
688
|
+
* @throws {TestRailApiError} When the API request fails
|
|
689
|
+
*/
|
|
690
|
+
async getMilestone(milestoneId: number): Promise<Milestone> {
|
|
691
|
+
this.validateId(milestoneId, 'milestoneId');
|
|
692
|
+
return this.request<Milestone>('GET', `get_milestone/${milestoneId}`);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Get all milestones for a project with optional filters.
|
|
697
|
+
* @param projectId - The project ID
|
|
698
|
+
* @param options - Optional filter parameters (is_completed, limit, offset)
|
|
699
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
700
|
+
* @throws {TestRailApiError} When the API request fails
|
|
701
|
+
*/
|
|
702
|
+
async getMilestones(projectId: number, options?: GetMilestonesOptions): Promise<Milestone[]> {
|
|
703
|
+
this.validateId(projectId, 'projectId');
|
|
704
|
+
this.validatePaginationParams(options?.limit, options?.offset);
|
|
705
|
+
const endpoint = this.buildEndpoint(`get_milestones/${projectId}`, {
|
|
706
|
+
is_completed: options?.is_completed,
|
|
707
|
+
limit: options?.limit,
|
|
708
|
+
offset: options?.offset,
|
|
709
|
+
});
|
|
710
|
+
const response = await this.request<{ milestones: Milestone[] }>('GET', endpoint);
|
|
711
|
+
return response.milestones ?? [];
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Add a new milestone to a project.
|
|
716
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
717
|
+
* @throws {TestRailApiError} When the API request fails
|
|
718
|
+
*/
|
|
719
|
+
async addMilestone(projectId: number, payload: AddMilestonePayload): Promise<Milestone> {
|
|
720
|
+
this.validateId(projectId, 'projectId');
|
|
721
|
+
return this.request<Milestone>('POST', `add_milestone/${projectId}`, payload);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Update an existing milestone.
|
|
726
|
+
* @throws {TestRailValidationError} When milestoneId is invalid
|
|
727
|
+
* @throws {TestRailApiError} When the API request fails
|
|
728
|
+
*/
|
|
729
|
+
async updateMilestone(milestoneId: number, payload: UpdateMilestonePayload): Promise<Milestone> {
|
|
730
|
+
this.validateId(milestoneId, 'milestoneId');
|
|
731
|
+
return this.request<Milestone>('POST', `update_milestone/${milestoneId}`, payload);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Delete a milestone.
|
|
736
|
+
* @throws {TestRailValidationError} When milestoneId is invalid
|
|
737
|
+
* @throws {TestRailApiError} When the API request fails
|
|
738
|
+
*/
|
|
739
|
+
async deleteMilestone(milestoneId: number): Promise<void> {
|
|
740
|
+
this.validateId(milestoneId, 'milestoneId');
|
|
741
|
+
await this.request<void>('POST', `delete_milestone/${milestoneId}`);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// ── Users ─────────────────────────────────────────────────────────────────
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Get a user by ID.
|
|
748
|
+
* @throws {TestRailValidationError} When userId is invalid
|
|
749
|
+
* @throws {TestRailApiError} When the API request fails
|
|
750
|
+
*/
|
|
751
|
+
async getUser(userId: number): Promise<User> {
|
|
752
|
+
this.validateId(userId, 'userId');
|
|
753
|
+
return this.request<User>('GET', `get_user/${userId}`);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Get a user by email address.
|
|
758
|
+
* @throws {TestRailValidationError} When email format is invalid
|
|
759
|
+
* @throws {TestRailApiError} When the API request fails
|
|
760
|
+
*/
|
|
761
|
+
async getUserByEmail(email: string): Promise<User> {
|
|
762
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
763
|
+
if (!emailRegex.test(email)) {
|
|
764
|
+
throw new TestRailValidationError('Invalid email format');
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// buildEndpoint now encodes all values via encodeURIComponent internally.
|
|
768
|
+
return this.request<User>('GET', this.buildEndpoint('get_user_by_email', { email }));
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Get all users, optionally scoped to a project.
|
|
773
|
+
* @param limit - Maximum number of users to return
|
|
774
|
+
* @param offset - Number of users to skip
|
|
775
|
+
* @param projectId - When provided, returns only users with access to the specified project
|
|
776
|
+
* @throws {TestRailValidationError} When pagination or projectId is invalid
|
|
777
|
+
* @throws {TestRailApiError} When the API request fails
|
|
778
|
+
*/
|
|
779
|
+
async getUsers(limit?: number, offset?: number, projectId?: number): Promise<User[]> {
|
|
780
|
+
this.validatePaginationParams(limit, offset);
|
|
781
|
+
if (projectId !== undefined) {
|
|
782
|
+
this.validateId(projectId, 'projectId');
|
|
783
|
+
}
|
|
784
|
+
const endpoint = this.buildEndpoint(projectId !== undefined ? `get_users/${projectId}` : 'get_users', {
|
|
785
|
+
limit,
|
|
786
|
+
offset,
|
|
787
|
+
});
|
|
788
|
+
const response = await this.request<{ users: User[] }>('GET', endpoint);
|
|
789
|
+
return response.users ?? [];
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Get the currently authenticated user.
|
|
794
|
+
* @throws {TestRailApiError} When the API request fails
|
|
795
|
+
*/
|
|
796
|
+
async getCurrentUser(): Promise<User> {
|
|
797
|
+
return this.request<User>('GET', 'get_current_user');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// ── Statuses ──────────────────────────────────────────────────────────────
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Get all test statuses.
|
|
804
|
+
* @throws {TestRailApiError} When the API request fails
|
|
805
|
+
*/
|
|
806
|
+
async getStatuses(): Promise<Status[]> {
|
|
807
|
+
return this.request<Status[]>('GET', 'get_statuses');
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// ── Priorities ────────────────────────────────────────────────────────────
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Get all case priorities.
|
|
814
|
+
* @throws {TestRailApiError} When the API request fails
|
|
815
|
+
*/
|
|
816
|
+
async getPriorities(): Promise<Priority[]> {
|
|
817
|
+
return this.request<Priority[]>('GET', 'get_priorities');
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// ── Result Fields ─────────────────────────────────────────────────────────
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Get all available custom result fields.
|
|
824
|
+
* @throws {TestRailApiError} When the API request fails
|
|
825
|
+
*/
|
|
826
|
+
async getResultFields(): Promise<ResultField[]> {
|
|
827
|
+
return this.request<ResultField[]>('GET', 'get_result_fields');
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// ── Case Fields & Types ───────────────────────────────────────────────────
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Get all available custom case fields.
|
|
834
|
+
* @throws {TestRailApiError} When the API request fails
|
|
835
|
+
*/
|
|
836
|
+
async getCaseFields(): Promise<CaseField[]> {
|
|
837
|
+
return this.request<CaseField[]>('GET', 'get_case_fields');
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Get all available case types.
|
|
842
|
+
* @throws {TestRailApiError} When the API request fails
|
|
843
|
+
*/
|
|
844
|
+
async getCaseTypes(): Promise<CaseType[]> {
|
|
845
|
+
return this.request<CaseType[]>('GET', 'get_case_types');
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// ── Templates ─────────────────────────────────────────────────────────────
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Get all available case templates for a project (requires TestRail 5.2+).
|
|
852
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
853
|
+
* @throws {TestRailApiError} When the API request fails
|
|
854
|
+
*/
|
|
855
|
+
async getTemplates(projectId: number): Promise<Template[]> {
|
|
856
|
+
this.validateId(projectId, 'projectId');
|
|
857
|
+
return this.request<Template[]>('GET', `get_templates/${projectId}`);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// ── Configurations ────────────────────────────────────────────────────────
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Get all configuration groups and their configurations for a project.
|
|
864
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
865
|
+
* @throws {TestRailApiError} When the API request fails
|
|
866
|
+
*/
|
|
867
|
+
async getConfigurations(projectId: number): Promise<ConfigurationGroup[]> {
|
|
868
|
+
this.validateId(projectId, 'projectId');
|
|
869
|
+
return this.request<ConfigurationGroup[]>('GET', `get_configs/${projectId}`);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Add a new configuration group to a project.
|
|
874
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
875
|
+
* @throws {TestRailApiError} When the API request fails
|
|
876
|
+
*/
|
|
877
|
+
async addConfigurationGroup(projectId: number, payload: AddConfigurationGroupPayload): Promise<ConfigurationGroup> {
|
|
878
|
+
this.validateId(projectId, 'projectId');
|
|
879
|
+
return this.request<ConfigurationGroup>('POST', `add_config_group/${projectId}`, payload);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Update an existing configuration group.
|
|
884
|
+
* @throws {TestRailValidationError} When configGroupId is invalid
|
|
885
|
+
* @throws {TestRailApiError} When the API request fails
|
|
886
|
+
*/
|
|
887
|
+
async updateConfigurationGroup(
|
|
888
|
+
configGroupId: number,
|
|
889
|
+
payload: UpdateConfigurationGroupPayload,
|
|
890
|
+
): Promise<ConfigurationGroup> {
|
|
891
|
+
this.validateId(configGroupId, 'configGroupId');
|
|
892
|
+
return this.request<ConfigurationGroup>('POST', `update_config_group/${configGroupId}`, payload);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Delete an existing configuration group and all its configurations.
|
|
897
|
+
* @throws {TestRailValidationError} When configGroupId is invalid
|
|
898
|
+
* @throws {TestRailApiError} When the API request fails
|
|
899
|
+
*/
|
|
900
|
+
async deleteConfigurationGroup(configGroupId: number): Promise<void> {
|
|
901
|
+
this.validateId(configGroupId, 'configGroupId');
|
|
902
|
+
await this.request<void>('POST', `delete_config_group/${configGroupId}`);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Add a new configuration to a configuration group.
|
|
907
|
+
* @throws {TestRailValidationError} When configGroupId is invalid
|
|
908
|
+
* @throws {TestRailApiError} When the API request fails
|
|
909
|
+
*/
|
|
910
|
+
async addConfiguration(configGroupId: number, payload: AddConfigurationPayload): Promise<Configuration> {
|
|
911
|
+
this.validateId(configGroupId, 'configGroupId');
|
|
912
|
+
return this.request<Configuration>('POST', `add_config/${configGroupId}`, payload);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Update an existing configuration.
|
|
917
|
+
* @throws {TestRailValidationError} When configId is invalid
|
|
918
|
+
* @throws {TestRailApiError} When the API request fails
|
|
919
|
+
*/
|
|
920
|
+
async updateConfiguration(configId: number, payload: UpdateConfigurationPayload): Promise<Configuration> {
|
|
921
|
+
this.validateId(configId, 'configId');
|
|
922
|
+
return this.request<Configuration>('POST', `update_config/${configId}`, payload);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Delete an existing configuration.
|
|
927
|
+
* @throws {TestRailValidationError} When configId is invalid
|
|
928
|
+
* @throws {TestRailApiError} When the API request fails
|
|
929
|
+
*/
|
|
930
|
+
async deleteConfiguration(configId: number): Promise<void> {
|
|
931
|
+
this.validateId(configId, 'configId');
|
|
932
|
+
await this.request<void>('POST', `delete_config/${configId}`);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// ── User Management (TASK-024, requires TestRail 7.3+) ────────────────────
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Create a new TestRail user (requires TestRail 7.3+).
|
|
939
|
+
* @throws {TestRailApiError} When the API request fails
|
|
940
|
+
*/
|
|
941
|
+
async addUser(payload: AddUserPayload): Promise<User> {
|
|
942
|
+
return this.request<User>('POST', 'add_user', payload);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Update an existing TestRail user (requires TestRail 7.3+).
|
|
947
|
+
* @throws {TestRailValidationError} When userId is invalid
|
|
948
|
+
* @throws {TestRailApiError} When the API request fails
|
|
949
|
+
*/
|
|
950
|
+
async updateUser(userId: number, payload: UpdateUserPayload): Promise<User> {
|
|
951
|
+
this.validateId(userId, 'userId');
|
|
952
|
+
return this.request<User>('POST', `update_user/${userId}`, payload);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// ── Roles (TASK-025, requires TestRail 7.3+) ──────────────────────────────
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Get all available user roles (requires TestRail 7.3+).
|
|
959
|
+
* @throws {TestRailApiError} When the API request fails
|
|
960
|
+
*/
|
|
961
|
+
async getRoles(): Promise<Role[]> {
|
|
962
|
+
return this.request<Role[]>('GET', 'get_roles');
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// ── Groups (TASK-026, requires TestRail 7.5+) ─────────────────────────────
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Get a single group by ID (requires TestRail 7.5+).
|
|
969
|
+
* @throws {TestRailValidationError} When groupId is invalid
|
|
970
|
+
* @throws {TestRailApiError} When the API request fails
|
|
971
|
+
*/
|
|
972
|
+
async getGroup(groupId: number): Promise<Group> {
|
|
973
|
+
this.validateId(groupId, 'groupId');
|
|
974
|
+
return this.request<Group>('GET', `get_group/${groupId}`);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Get all groups (requires TestRail 7.5+).
|
|
979
|
+
* @throws {TestRailApiError} When the API request fails
|
|
980
|
+
*/
|
|
981
|
+
async getGroups(): Promise<Group[]> {
|
|
982
|
+
return this.request<Group[]>('GET', 'get_groups');
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* Create a new group (requires TestRail 7.5+).
|
|
987
|
+
* @throws {TestRailApiError} When the API request fails
|
|
988
|
+
*/
|
|
989
|
+
async addGroup(payload: AddGroupPayload): Promise<Group> {
|
|
990
|
+
return this.request<Group>('POST', 'add_group', payload);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Update an existing group (requires TestRail 7.5+).
|
|
995
|
+
* @throws {TestRailValidationError} When groupId is invalid
|
|
996
|
+
* @throws {TestRailApiError} When the API request fails
|
|
997
|
+
*/
|
|
998
|
+
async updateGroup(groupId: number, payload: UpdateGroupPayload): Promise<Group> {
|
|
999
|
+
this.validateId(groupId, 'groupId');
|
|
1000
|
+
return this.request<Group>('POST', `update_group/${groupId}`, payload);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Delete a group (requires TestRail 7.5+).
|
|
1005
|
+
* @throws {TestRailValidationError} When groupId is invalid
|
|
1006
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1007
|
+
*/
|
|
1008
|
+
async deleteGroup(groupId: number): Promise<void> {
|
|
1009
|
+
this.validateId(groupId, 'groupId');
|
|
1010
|
+
await this.request<void>('POST', `delete_group/${groupId}`);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// ── Attachments (TASK-027) ────────────────────────────────────────────────
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Get all attachments for a test case.
|
|
1017
|
+
* @throws {TestRailValidationError} When caseId is invalid
|
|
1018
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1019
|
+
*/
|
|
1020
|
+
async getAttachmentsForCase(caseId: number): Promise<Attachment[]> {
|
|
1021
|
+
this.validateId(caseId, 'caseId');
|
|
1022
|
+
const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_case/${caseId}`);
|
|
1023
|
+
return response.attachments ?? [];
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Get all attachments for a test run.
|
|
1028
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
1029
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1030
|
+
*/
|
|
1031
|
+
async getAttachmentsForRun(runId: number): Promise<Attachment[]> {
|
|
1032
|
+
this.validateId(runId, 'runId');
|
|
1033
|
+
const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_run/${runId}`);
|
|
1034
|
+
return response.attachments ?? [];
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Get all attachments for a test.
|
|
1039
|
+
* @throws {TestRailValidationError} When testId is invalid
|
|
1040
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1041
|
+
*/
|
|
1042
|
+
async getAttachmentsForTest(testId: number): Promise<Attachment[]> {
|
|
1043
|
+
this.validateId(testId, 'testId');
|
|
1044
|
+
const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_test/${testId}`);
|
|
1045
|
+
return response.attachments ?? [];
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Get all attachments for a test plan.
|
|
1050
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
1051
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1052
|
+
*/
|
|
1053
|
+
async getAttachmentsForPlan(planId: number): Promise<Attachment[]> {
|
|
1054
|
+
this.validateId(planId, 'planId');
|
|
1055
|
+
const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_plan/${planId}`);
|
|
1056
|
+
return response.attachments ?? [];
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Get all attachments for a specific plan entry.
|
|
1061
|
+
* @throws {TestRailValidationError} When planId or entryId is invalid
|
|
1062
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1063
|
+
*/
|
|
1064
|
+
async getAttachmentsForPlanEntry(planId: number, entryId: number): Promise<Attachment[]> {
|
|
1065
|
+
this.validateId(planId, 'planId');
|
|
1066
|
+
this.validateId(entryId, 'entryId');
|
|
1067
|
+
const response = await this.request<{ attachments: Attachment[] }>(
|
|
1068
|
+
'GET',
|
|
1069
|
+
`get_attachments_for_plan_entry/${planId}/${entryId}`,
|
|
1070
|
+
);
|
|
1071
|
+
return response.attachments ?? [];
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* Download the raw binary content of an attachment.
|
|
1076
|
+
* @param attachmentId - The attachment ID (numeric)
|
|
1077
|
+
* @throws {TestRailValidationError} When attachmentId is invalid
|
|
1078
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1079
|
+
*/
|
|
1080
|
+
async getAttachment(attachmentId: number): Promise<ArrayBuffer> {
|
|
1081
|
+
this.validateId(attachmentId, 'attachmentId');
|
|
1082
|
+
return this.requestBinary(`get_attachment/${attachmentId}`);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Upload a file attachment to a test case.
|
|
1087
|
+
* @throws {TestRailValidationError} When caseId is invalid
|
|
1088
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1089
|
+
*/
|
|
1090
|
+
async addAttachmentToCase(
|
|
1091
|
+
caseId: number,
|
|
1092
|
+
file: globalThis.Blob | Uint8Array | globalThis.File,
|
|
1093
|
+
filename: string,
|
|
1094
|
+
): Promise<Attachment> {
|
|
1095
|
+
this.validateId(caseId, 'caseId');
|
|
1096
|
+
return this.requestMultipart<Attachment>(`add_attachment_to_case/${caseId}`, file, filename);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Upload a file attachment to a test result.
|
|
1101
|
+
* @throws {TestRailValidationError} When resultId is invalid
|
|
1102
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1103
|
+
*/
|
|
1104
|
+
async addAttachmentToResult(
|
|
1105
|
+
resultId: number,
|
|
1106
|
+
file: globalThis.Blob | Uint8Array | globalThis.File,
|
|
1107
|
+
filename: string,
|
|
1108
|
+
): Promise<Attachment> {
|
|
1109
|
+
this.validateId(resultId, 'resultId');
|
|
1110
|
+
return this.requestMultipart<Attachment>(`add_attachment_to_result/${resultId}`, file, filename);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Upload a file attachment to a test run.
|
|
1115
|
+
* @throws {TestRailValidationError} When runId is invalid
|
|
1116
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1117
|
+
*/
|
|
1118
|
+
async addAttachmentToRun(
|
|
1119
|
+
runId: number,
|
|
1120
|
+
file: globalThis.Blob | Uint8Array | globalThis.File,
|
|
1121
|
+
filename: string,
|
|
1122
|
+
): Promise<Attachment> {
|
|
1123
|
+
this.validateId(runId, 'runId');
|
|
1124
|
+
return this.requestMultipart<Attachment>(`add_attachment_to_run/${runId}`, file, filename);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* Upload a file attachment to a test plan (requires TestRail 6.5.2+).
|
|
1129
|
+
* @throws {TestRailValidationError} When planId is invalid
|
|
1130
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1131
|
+
*/
|
|
1132
|
+
async addAttachmentToPlan(
|
|
1133
|
+
planId: number,
|
|
1134
|
+
file: globalThis.Blob | Uint8Array | globalThis.File,
|
|
1135
|
+
filename: string,
|
|
1136
|
+
): Promise<Attachment> {
|
|
1137
|
+
this.validateId(planId, 'planId');
|
|
1138
|
+
return this.requestMultipart<Attachment>(`add_attachment_to_plan/${planId}`, file, filename);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* Upload a file attachment to a specific plan entry (requires TestRail 6.5.2+).
|
|
1143
|
+
* @throws {TestRailValidationError} When planId or entryId is invalid
|
|
1144
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1145
|
+
*/
|
|
1146
|
+
async addAttachmentToPlanEntry(
|
|
1147
|
+
planId: number,
|
|
1148
|
+
entryId: number,
|
|
1149
|
+
file: globalThis.Blob | Uint8Array | globalThis.File,
|
|
1150
|
+
filename: string,
|
|
1151
|
+
): Promise<Attachment> {
|
|
1152
|
+
this.validateId(planId, 'planId');
|
|
1153
|
+
this.validateId(entryId, 'entryId');
|
|
1154
|
+
return this.requestMultipart<Attachment>(`add_attachment_to_plan_entry/${planId}/${entryId}`, file, filename);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Delete an attachment by ID.
|
|
1159
|
+
* @throws {TestRailValidationError} When attachmentId is invalid
|
|
1160
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1161
|
+
*/
|
|
1162
|
+
async deleteAttachment(attachmentId: number): Promise<void> {
|
|
1163
|
+
this.validateId(attachmentId, 'attachmentId');
|
|
1164
|
+
await this.request<void>('POST', `delete_attachment/${attachmentId}`);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
// ── Shared Steps (TASK-028, requires TestRail 7.0+) ───────────────────────
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Get a single shared step by ID (requires TestRail 7.0+).
|
|
1171
|
+
* @throws {TestRailValidationError} When sharedStepId is invalid
|
|
1172
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1173
|
+
*/
|
|
1174
|
+
async getSharedStep(sharedStepId: number): Promise<SharedStep> {
|
|
1175
|
+
this.validateId(sharedStepId, 'sharedStepId');
|
|
1176
|
+
return this.request<SharedStep>('GET', `get_shared_step/${sharedStepId}`);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Get all shared steps for a project (requires TestRail 7.0+).
|
|
1181
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1182
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1183
|
+
*/
|
|
1184
|
+
async getSharedSteps(projectId: number): Promise<SharedStep[]> {
|
|
1185
|
+
this.validateId(projectId, 'projectId');
|
|
1186
|
+
return this.request<SharedStep[]>('GET', `get_shared_steps/${projectId}`);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Create a new shared step in a project (requires TestRail 7.0+).
|
|
1191
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1192
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1193
|
+
*/
|
|
1194
|
+
async addSharedStep(projectId: number, payload: AddSharedStepPayload): Promise<SharedStep> {
|
|
1195
|
+
this.validateId(projectId, 'projectId');
|
|
1196
|
+
return this.request<SharedStep>('POST', `add_shared_step/${projectId}`, payload);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Update an existing shared step (requires TestRail 7.0+).
|
|
1201
|
+
* @throws {TestRailValidationError} When sharedStepId is invalid
|
|
1202
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1203
|
+
*/
|
|
1204
|
+
async updateSharedStep(sharedStepId: number, payload: UpdateSharedStepPayload): Promise<SharedStep> {
|
|
1205
|
+
this.validateId(sharedStepId, 'sharedStepId');
|
|
1206
|
+
return this.request<SharedStep>('POST', `update_shared_step/${sharedStepId}`, payload);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Delete a shared step (requires TestRail 7.0+).
|
|
1211
|
+
* @throws {TestRailValidationError} When sharedStepId is invalid
|
|
1212
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1213
|
+
*/
|
|
1214
|
+
async deleteSharedStep(sharedStepId: number): Promise<void> {
|
|
1215
|
+
this.validateId(sharedStepId, 'sharedStepId');
|
|
1216
|
+
await this.request<void>('POST', `delete_shared_step/${sharedStepId}`);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// ── Variables (TASK-029) ──────────────────────────────────────────────────
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Get all variables for a project.
|
|
1223
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1224
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1225
|
+
*/
|
|
1226
|
+
async getVariables(projectId: number): Promise<Variable[]> {
|
|
1227
|
+
this.validateId(projectId, 'projectId');
|
|
1228
|
+
return this.request<Variable[]>('GET', `get_variables/${projectId}`);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* Create a new variable in a project.
|
|
1233
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1234
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1235
|
+
*/
|
|
1236
|
+
async addVariable(projectId: number, payload: AddVariablePayload): Promise<Variable> {
|
|
1237
|
+
this.validateId(projectId, 'projectId');
|
|
1238
|
+
return this.request<Variable>('POST', `add_variable/${projectId}`, payload);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/**
|
|
1242
|
+
* Update an existing variable.
|
|
1243
|
+
* @throws {TestRailValidationError} When variableId is invalid
|
|
1244
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1245
|
+
*/
|
|
1246
|
+
async updateVariable(variableId: number, payload: UpdateVariablePayload): Promise<Variable> {
|
|
1247
|
+
this.validateId(variableId, 'variableId');
|
|
1248
|
+
return this.request<Variable>('POST', `update_variable/${variableId}`, payload);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Delete a variable.
|
|
1253
|
+
* @throws {TestRailValidationError} When variableId is invalid
|
|
1254
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1255
|
+
*/
|
|
1256
|
+
async deleteVariable(variableId: number): Promise<void> {
|
|
1257
|
+
this.validateId(variableId, 'variableId');
|
|
1258
|
+
await this.request<void>('POST', `delete_variable/${variableId}`);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// ── Datasets (TASK-030) ───────────────────────────────────────────────────
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* Get a single dataset by ID.
|
|
1265
|
+
* @throws {TestRailValidationError} When datasetId is invalid
|
|
1266
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1267
|
+
*/
|
|
1268
|
+
async getDataset(datasetId: number): Promise<Dataset> {
|
|
1269
|
+
this.validateId(datasetId, 'datasetId');
|
|
1270
|
+
return this.request<Dataset>('GET', `get_dataset/${datasetId}`);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Get all datasets for a project.
|
|
1275
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1276
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1277
|
+
*/
|
|
1278
|
+
async getDatasets(projectId: number): Promise<Dataset[]> {
|
|
1279
|
+
this.validateId(projectId, 'projectId');
|
|
1280
|
+
return this.request<Dataset[]>('GET', `get_datasets/${projectId}`);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Create a new dataset in a project.
|
|
1285
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1286
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1287
|
+
*/
|
|
1288
|
+
async addDataset(projectId: number, payload: AddDatasetPayload): Promise<Dataset> {
|
|
1289
|
+
this.validateId(projectId, 'projectId');
|
|
1290
|
+
return this.request<Dataset>('POST', `add_dataset/${projectId}`, payload);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* Update an existing dataset.
|
|
1295
|
+
* @throws {TestRailValidationError} When datasetId is invalid
|
|
1296
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1297
|
+
*/
|
|
1298
|
+
async updateDataset(datasetId: number, payload: UpdateDatasetPayload): Promise<Dataset> {
|
|
1299
|
+
this.validateId(datasetId, 'datasetId');
|
|
1300
|
+
return this.request<Dataset>('POST', `update_dataset/${datasetId}`, payload);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Delete a dataset.
|
|
1305
|
+
* @throws {TestRailValidationError} When datasetId is invalid
|
|
1306
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1307
|
+
*/
|
|
1308
|
+
async deleteDataset(datasetId: number): Promise<void> {
|
|
1309
|
+
this.validateId(datasetId, 'datasetId');
|
|
1310
|
+
await this.request<void>('POST', `delete_dataset/${datasetId}`);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// ── Reports (TASK-031) ────────────────────────────────────────────────────
|
|
1314
|
+
|
|
1315
|
+
/**
|
|
1316
|
+
* Get all available report templates for a project.
|
|
1317
|
+
* @throws {TestRailValidationError} When projectId is invalid
|
|
1318
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1319
|
+
*/
|
|
1320
|
+
async getReports(projectId: number): Promise<Report[]> {
|
|
1321
|
+
this.validateId(projectId, 'projectId');
|
|
1322
|
+
return this.request<Report[]>('GET', `get_reports/${projectId}`);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* Execute a report template and return URLs to the generated output.
|
|
1327
|
+
* @throws {TestRailValidationError} When reportTemplateId is invalid
|
|
1328
|
+
* @throws {TestRailApiError} When the API request fails
|
|
1329
|
+
*/
|
|
1330
|
+
async runReport(reportTemplateId: number): Promise<ReportResult> {
|
|
1331
|
+
this.validateId(reportTemplateId, 'reportTemplateId');
|
|
1332
|
+
return this.request<ReportResult>('GET', `run_report/${reportTemplateId}`);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
private serializeIdList(ids?: number[]): string | undefined {
|
|
1336
|
+
return ids !== undefined && ids.length > 0 ? ids.join(',') : undefined;
|
|
1337
|
+
}
|
|
1338
|
+
}
|