@dichovsky/testrail-api-client 1.0.0 → 2.1.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.
Files changed (137) hide show
  1. package/README.md +22 -0
  2. package/dist/cli/auth.d.ts +21 -0
  3. package/dist/cli/auth.js +16 -0
  4. package/dist/cli/body.d.ts +42 -0
  5. package/dist/cli/body.js +89 -0
  6. package/dist/cli/dispatch.d.ts +16 -0
  7. package/dist/cli/dispatch.js +87 -0
  8. package/dist/cli/handler-context.d.ts +43 -0
  9. package/dist/cli/handler-context.js +2 -0
  10. package/dist/cli/handlers/case-write.d.ts +4 -0
  11. package/dist/cli/handlers/case-write.js +26 -0
  12. package/dist/cli/handlers/case.d.ts +4 -0
  13. package/dist/cli/handlers/case.js +11 -0
  14. package/dist/cli/handlers/milestone.d.ts +4 -0
  15. package/dist/cli/handlers/milestone.js +15 -0
  16. package/dist/cli/handlers/project.d.ts +4 -0
  17. package/dist/cli/handlers/project.js +11 -0
  18. package/dist/cli/handlers/result-write.d.ts +4 -0
  19. package/dist/cli/handlers/result-write.js +40 -0
  20. package/dist/cli/handlers/result.d.ts +3 -0
  21. package/dist/cli/handlers/result.js +11 -0
  22. package/dist/cli/handlers/run-write.d.ts +10 -0
  23. package/dist/cli/handlers/run-write.js +29 -0
  24. package/dist/cli/handlers/run.d.ts +4 -0
  25. package/dist/cli/handlers/run.js +15 -0
  26. package/dist/cli/handlers/suite.d.ts +4 -0
  27. package/dist/cli/handlers/suite.js +10 -0
  28. package/dist/cli/handlers/user.d.ts +4 -0
  29. package/dist/cli/handlers/user.js +11 -0
  30. package/dist/cli/ids.d.ts +6 -0
  31. package/dist/cli/ids.js +20 -0
  32. package/dist/cli/index.d.ts +3 -0
  33. package/dist/cli/index.js +198 -0
  34. package/dist/cli/install-skill.d.ts +35 -0
  35. package/dist/cli/install-skill.js +71 -0
  36. package/dist/cli/metadata.d.ts +37 -0
  37. package/dist/cli/metadata.js +151 -0
  38. package/dist/cli/output.d.ts +28 -0
  39. package/dist/cli/output.js +84 -0
  40. package/dist/cli.d.ts +1 -1
  41. package/dist/cli.js +1 -266
  42. package/dist/client-core.d.ts +16 -7
  43. package/dist/client-core.js +153 -27
  44. package/dist/client.d.ts +274 -118
  45. package/dist/client.js +404 -463
  46. package/dist/constants.d.ts +1 -0
  47. package/dist/constants.js +1 -0
  48. package/dist/errors.d.ts +11 -9
  49. package/dist/errors.js +12 -8
  50. package/dist/index.d.ts +4 -2
  51. package/dist/index.js +2 -1
  52. package/dist/modules/attachments.d.ts +19 -0
  53. package/dist/modules/attachments.js +64 -0
  54. package/dist/modules/cases.d.ts +13 -0
  55. package/dist/modules/cases.js +58 -0
  56. package/dist/modules/configurations.d.ts +14 -0
  57. package/dist/modules/configurations.js +37 -0
  58. package/dist/modules/datasets.d.ts +12 -0
  59. package/dist/modules/datasets.js +28 -0
  60. package/dist/modules/metadata.d.ts +14 -0
  61. package/dist/modules/metadata.js +31 -0
  62. package/dist/modules/milestones.d.ts +12 -0
  63. package/dist/modules/milestones.js +36 -0
  64. package/dist/modules/plans.d.ts +16 -0
  65. package/dist/modules/plans.js +59 -0
  66. package/dist/modules/projects.d.ts +36 -0
  67. package/dist/modules/projects.js +55 -0
  68. package/dist/modules/reports.d.ts +9 -0
  69. package/dist/modules/reports.js +16 -0
  70. package/dist/modules/results.d.ts +14 -0
  71. package/dist/modules/results.js +69 -0
  72. package/dist/modules/runs.d.ts +14 -0
  73. package/dist/modules/runs.js +57 -0
  74. package/dist/modules/sections.d.ts +16 -0
  75. package/dist/modules/sections.js +37 -0
  76. package/dist/modules/sharedSteps.d.ts +12 -0
  77. package/dist/modules/sharedSteps.js +28 -0
  78. package/dist/modules/suites.d.ts +37 -0
  79. package/dist/modules/suites.js +54 -0
  80. package/dist/modules/tests.d.ts +9 -0
  81. package/dist/modules/tests.js +25 -0
  82. package/dist/modules/users.d.ts +18 -0
  83. package/dist/modules/users.js +62 -0
  84. package/dist/modules/variables.d.ts +11 -0
  85. package/dist/modules/variables.js +24 -0
  86. package/dist/schemas.d.ts +544 -0
  87. package/dist/schemas.js +419 -0
  88. package/dist/types.d.ts +1 -55
  89. package/dist/utils.d.ts +2 -0
  90. package/dist/utils.js +4 -0
  91. package/package.json +23 -15
  92. package/skill/SKILL.md +395 -0
  93. package/src/cli/auth.ts +37 -0
  94. package/src/cli/body.ts +100 -0
  95. package/src/cli/dispatch.ts +91 -0
  96. package/src/cli/handler-context.ts +46 -0
  97. package/src/cli/handlers/case-write.ts +26 -0
  98. package/src/cli/handlers/case.ts +13 -0
  99. package/src/cli/handlers/milestone.ts +19 -0
  100. package/src/cli/handlers/project.ts +13 -0
  101. package/src/cli/handlers/result-write.ts +40 -0
  102. package/src/cli/handlers/result.ts +14 -0
  103. package/src/cli/handlers/run-write.ts +30 -0
  104. package/src/cli/handlers/run.ts +19 -0
  105. package/src/cli/handlers/suite.ts +12 -0
  106. package/src/cli/handlers/user.ts +13 -0
  107. package/src/cli/ids.ts +20 -0
  108. package/src/cli/index.ts +224 -0
  109. package/src/cli/install-skill.ts +89 -0
  110. package/src/cli/metadata.ts +194 -0
  111. package/src/cli/output.ts +96 -0
  112. package/src/cli.ts +1 -286
  113. package/src/client-core.ts +183 -67
  114. package/src/client.ts +414 -483
  115. package/src/constants.ts +1 -0
  116. package/src/errors.ts +18 -11
  117. package/src/index.ts +50 -8
  118. package/src/modules/attachments.ts +125 -0
  119. package/src/modules/cases.ts +78 -0
  120. package/src/modules/configurations.ts +68 -0
  121. package/src/modules/datasets.ts +44 -0
  122. package/src/modules/metadata.ts +63 -0
  123. package/src/modules/milestones.ts +54 -0
  124. package/src/modules/plans.ts +89 -0
  125. package/src/modules/projects.ts +67 -0
  126. package/src/modules/reports.ts +23 -0
  127. package/src/modules/results.ts +90 -0
  128. package/src/modules/runs.ts +70 -0
  129. package/src/modules/sections.ts +55 -0
  130. package/src/modules/sharedSteps.ts +44 -0
  131. package/src/modules/suites.ts +67 -0
  132. package/src/modules/tests.ts +28 -0
  133. package/src/modules/users.ts +87 -0
  134. package/src/modules/variables.ts +36 -0
  135. package/src/schemas.ts +551 -0
  136. package/src/types.ts +11 -60
  137. package/src/utils.ts +5 -0
@@ -0,0 +1,67 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Project, AddProjectPayload, UpdateProjectPayload } from '../types.js';
3
+ import { ProjectSchema } from '../schemas.js';
4
+ import { z } from 'zod';
5
+
6
+ export class ProjectModule {
7
+ constructor(private readonly client: TestRailClientCore) {}
8
+
9
+ /**
10
+ * Get a project by ID.
11
+ * @throws {TestRailValidationError} When projectId is invalid
12
+ * @throws {TestRailApiError} When the API request fails
13
+ */
14
+ async getProject(projectId: number): Promise<Project> {
15
+ this.client.validateId(projectId, 'projectId');
16
+ return this.client.parse(ProjectSchema, await this.client.request<unknown>('GET', `get_project/${projectId}`));
17
+ }
18
+
19
+ /**
20
+ * Get all projects.
21
+ * @throws {TestRailValidationError} When limit or offset is invalid
22
+ * @throws {TestRailApiError} When the API request fails
23
+ */
24
+ async getProjects(limit?: number, offset?: number): Promise<Project[]> {
25
+ this.client.validatePaginationParams(limit, offset);
26
+ const endpoint = this.client.buildEndpoint('get_projects', { limit, offset });
27
+ const raw = await this.client.request<unknown>('GET', endpoint);
28
+ return (
29
+ this.client.parse<{ projects?: Project[] }>(z.object({ projects: z.array(ProjectSchema).optional() }), raw)
30
+ .projects ?? []
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Add a new project.
36
+ * @throws {TestRailApiError} When the API request fails
37
+ */
38
+ async addProject(payload: AddProjectPayload): Promise<Project> {
39
+ return this.client.parse<Project>(
40
+ ProjectSchema,
41
+ await this.client.request<unknown>('POST', 'add_project', payload),
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Update an existing project.
47
+ * @throws {TestRailValidationError} When projectId is invalid
48
+ * @throws {TestRailApiError} When the API request fails
49
+ */
50
+ async updateProject(projectId: number, payload: UpdateProjectPayload): Promise<Project> {
51
+ this.client.validateId(projectId, 'projectId');
52
+ return this.client.parse<Project>(
53
+ ProjectSchema,
54
+ await this.client.request<unknown>('POST', `update_project/${projectId}`, payload),
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Delete a project.
60
+ * @throws {TestRailValidationError} When projectId is invalid
61
+ * @throws {TestRailApiError} When the API request fails
62
+ */
63
+ async deleteProject(projectId: number): Promise<void> {
64
+ this.client.validateId(projectId, 'projectId');
65
+ await this.client.request<void>('POST', `delete_project/${projectId}`);
66
+ }
67
+ }
@@ -0,0 +1,23 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Report, ReportResult } from '../types.js';
3
+ import { ReportSchema, ReportResultSchema } from '../schemas.js';
4
+
5
+ export class ReportModule {
6
+ constructor(private readonly client: TestRailClientCore) {}
7
+
8
+ async getReports(projectId: number): Promise<Report[]> {
9
+ this.client.validateId(projectId, 'projectId');
10
+ return this.client.parse<Report[]>(
11
+ ReportSchema.array(),
12
+ await this.client.request<unknown>('GET', `get_reports/${projectId}`),
13
+ );
14
+ }
15
+
16
+ async runReport(reportTemplateId: number): Promise<ReportResult> {
17
+ this.client.validateId(reportTemplateId, 'reportTemplateId');
18
+ return this.client.parse<ReportResult>(
19
+ ReportResultSchema,
20
+ await this.client.request<unknown>('GET', `run_report/${reportTemplateId}`),
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,90 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Result, GetResultsOptions } from '../types.js';
3
+ import type { AddResultPayload, AddResultsForCasesPayload } from '../schemas.js';
4
+ import { ResultSchema } from '../schemas.js';
5
+ import { serializeIdList } from '../utils.js';
6
+ import { z } from 'zod';
7
+
8
+ export class ResultModule {
9
+ constructor(private readonly client: TestRailClientCore) {}
10
+
11
+ async getResults(testId: number, options?: GetResultsOptions): Promise<Result[]> {
12
+ this.client.validateId(testId, 'testId');
13
+ this.client.validatePaginationParams(options?.limit, options?.offset);
14
+ const endpoint = this.client.buildEndpoint(`get_results/${testId}`, {
15
+ created_after: options?.created_after,
16
+ created_before: options?.created_before,
17
+ created_by: serializeIdList(options?.created_by),
18
+ status_id: serializeIdList(options?.status_id),
19
+ limit: options?.limit,
20
+ offset: options?.offset,
21
+ });
22
+ const raw = await this.client.request<unknown>('GET', endpoint);
23
+ return (
24
+ this.client.parse<{ results?: Result[] }>(z.object({ results: z.array(ResultSchema).optional() }), raw)
25
+ .results ?? []
26
+ );
27
+ }
28
+
29
+ async getResultsForCase(runId: number, caseId: number, options?: GetResultsOptions): Promise<Result[]> {
30
+ this.client.validateId(runId, 'runId');
31
+ this.client.validateId(caseId, 'caseId');
32
+ this.client.validatePaginationParams(options?.limit, options?.offset);
33
+ const endpoint = this.client.buildEndpoint(`get_results_for_case/${runId}/${caseId}`, {
34
+ created_after: options?.created_after,
35
+ created_before: options?.created_before,
36
+ created_by: serializeIdList(options?.created_by),
37
+ status_id: serializeIdList(options?.status_id),
38
+ limit: options?.limit,
39
+ offset: options?.offset,
40
+ });
41
+ const raw = await this.client.request<unknown>('GET', endpoint);
42
+ return (
43
+ this.client.parse<{ results?: Result[] }>(z.object({ results: z.array(ResultSchema).optional() }), raw)
44
+ .results ?? []
45
+ );
46
+ }
47
+
48
+ async getResultsForRun(runId: number, options?: GetResultsOptions): Promise<Result[]> {
49
+ this.client.validateId(runId, 'runId');
50
+ this.client.validatePaginationParams(options?.limit, options?.offset);
51
+ const endpoint = this.client.buildEndpoint(`get_results_for_run/${runId}`, {
52
+ created_after: options?.created_after,
53
+ created_before: options?.created_before,
54
+ created_by: serializeIdList(options?.created_by),
55
+ status_id: serializeIdList(options?.status_id),
56
+ limit: options?.limit,
57
+ offset: options?.offset,
58
+ });
59
+ const raw = await this.client.request<unknown>('GET', endpoint);
60
+ return (
61
+ this.client.parse<{ results?: Result[] }>(z.object({ results: z.array(ResultSchema).optional() }), raw)
62
+ .results ?? []
63
+ );
64
+ }
65
+
66
+ async addResult(testId: number, payload: AddResultPayload): Promise<Result> {
67
+ this.client.validateId(testId, 'testId');
68
+ return this.client.parse<Result>(
69
+ ResultSchema,
70
+ await this.client.request<unknown>('POST', `add_result/${testId}`, payload),
71
+ );
72
+ }
73
+
74
+ async addResultForCase(runId: number, caseId: number, payload: AddResultPayload): Promise<Result> {
75
+ this.client.validateId(runId, 'runId');
76
+ this.client.validateId(caseId, 'caseId');
77
+ return this.client.parse<Result>(
78
+ ResultSchema,
79
+ await this.client.request<unknown>('POST', `add_result_for_case/${runId}/${caseId}`, payload),
80
+ );
81
+ }
82
+
83
+ async addResultsForCases(runId: number, payload: AddResultsForCasesPayload): Promise<Result[]> {
84
+ this.client.validateId(runId, 'runId');
85
+ return this.client.parse<Result[]>(
86
+ z.array(ResultSchema),
87
+ await this.client.request<unknown>('POST', `add_results_for_cases/${runId}`, payload),
88
+ );
89
+ }
90
+ }
@@ -0,0 +1,70 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Run, GetRunsOptions } from '../types.js';
3
+ import type { AddRunPayload, UpdateRunPayload } from '../schemas.js';
4
+ import { RunSchema } from '../schemas.js';
5
+ import { z } from 'zod';
6
+
7
+ export class RunModule {
8
+ constructor(private readonly client: TestRailClientCore) {}
9
+
10
+ async getRun(runId: number): Promise<Run> {
11
+ this.client.validateId(runId, 'runId');
12
+ return this.client.parse<Run>(RunSchema, await this.client.request<unknown>('GET', `get_run/${runId}`));
13
+ }
14
+
15
+ async getRuns(projectId: number, options?: GetRunsOptions): Promise<Run[]> {
16
+ this.client.validateId(projectId, 'projectId');
17
+ const { createdAfter, createdBefore, createdBy, isCompleted, milestoneId, refsFilter, suiteId, limit, offset } =
18
+ options ?? {};
19
+ this.client.validatePaginationParams(limit, offset);
20
+ if (milestoneId !== undefined) {
21
+ this.client.validateId(milestoneId, 'milestoneId');
22
+ }
23
+ if (suiteId !== undefined) {
24
+ this.client.validateId(suiteId, 'suiteId');
25
+ }
26
+ if (createdBy !== undefined) {
27
+ createdBy.forEach((userId) => this.client.validateId(userId, 'createdBy'));
28
+ }
29
+ const createdByFilter = createdBy && createdBy.length > 0 ? createdBy.join(',') : undefined;
30
+ const endpoint = this.client.buildEndpoint(`get_runs/${projectId}`, {
31
+ created_after: createdAfter,
32
+ created_before: createdBefore,
33
+ created_by: createdByFilter,
34
+ is_completed: isCompleted !== undefined ? (isCompleted ? 1 : 0) : undefined,
35
+ milestone_id: milestoneId,
36
+ refs_filter: refsFilter,
37
+ suite_id: suiteId,
38
+ limit,
39
+ offset,
40
+ });
41
+ const raw = await this.client.request<unknown>('GET', endpoint);
42
+ return this.client.parse<{ runs?: Run[] }>(z.object({ runs: z.array(RunSchema).optional() }), raw).runs ?? [];
43
+ }
44
+
45
+ async addRun(projectId: number, payload: AddRunPayload): Promise<Run> {
46
+ this.client.validateId(projectId, 'projectId');
47
+ return this.client.parse<Run>(
48
+ RunSchema,
49
+ await this.client.request<unknown>('POST', `add_run/${projectId}`, payload),
50
+ );
51
+ }
52
+
53
+ async updateRun(runId: number, payload: UpdateRunPayload): Promise<Run> {
54
+ this.client.validateId(runId, 'runId');
55
+ return this.client.parse<Run>(
56
+ RunSchema,
57
+ await this.client.request<unknown>('POST', `update_run/${runId}`, payload),
58
+ );
59
+ }
60
+
61
+ async closeRun(runId: number): Promise<Run> {
62
+ this.client.validateId(runId, 'runId');
63
+ return this.client.parse<Run>(RunSchema, await this.client.request<unknown>('POST', `close_run/${runId}`));
64
+ }
65
+
66
+ async deleteRun(runId: number): Promise<void> {
67
+ this.client.validateId(runId, 'runId');
68
+ await this.client.request<void>('POST', `delete_run/${runId}`);
69
+ }
70
+ }
@@ -0,0 +1,55 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Section, AddSectionPayload, UpdateSectionPayload } from '../types.js';
3
+ import { SectionSchema } from '../schemas.js';
4
+ import { z } from 'zod';
5
+
6
+ export class SectionModule {
7
+ constructor(private readonly client: TestRailClientCore) {}
8
+
9
+ async getSection(sectionId: number): Promise<Section> {
10
+ this.client.validateId(sectionId, 'sectionId');
11
+ return this.client.parse<Section>(
12
+ SectionSchema,
13
+ await this.client.request<unknown>('GET', `get_section/${sectionId}`),
14
+ );
15
+ }
16
+
17
+ async getSections(
18
+ projectId: number,
19
+ options?: { suiteId?: number; limit?: number; offset?: number },
20
+ ): Promise<Section[]> {
21
+ this.client.validateId(projectId, 'projectId');
22
+ const { suiteId, limit, offset } = options ?? {};
23
+ if (suiteId !== undefined) {
24
+ this.client.validateId(suiteId, 'suiteId');
25
+ }
26
+ this.client.validatePaginationParams(limit, offset);
27
+ const endpoint = this.client.buildEndpoint(`get_sections/${projectId}`, { suite_id: suiteId, limit, offset });
28
+ const raw = await this.client.request<unknown>('GET', endpoint);
29
+ return (
30
+ this.client.parse<{ sections?: Section[] }>(z.object({ sections: z.array(SectionSchema).optional() }), raw)
31
+ .sections ?? []
32
+ );
33
+ }
34
+
35
+ async addSection(projectId: number, payload: AddSectionPayload): Promise<Section> {
36
+ this.client.validateId(projectId, 'projectId');
37
+ return this.client.parse<Section>(
38
+ SectionSchema,
39
+ await this.client.request<unknown>('POST', `add_section/${projectId}`, payload),
40
+ );
41
+ }
42
+
43
+ async updateSection(sectionId: number, payload: UpdateSectionPayload): Promise<Section> {
44
+ this.client.validateId(sectionId, 'sectionId');
45
+ return this.client.parse<Section>(
46
+ SectionSchema,
47
+ await this.client.request<unknown>('POST', `update_section/${sectionId}`, payload),
48
+ );
49
+ }
50
+
51
+ async deleteSection(sectionId: number): Promise<void> {
52
+ this.client.validateId(sectionId, 'sectionId');
53
+ await this.client.request<void>('POST', `delete_section/${sectionId}`);
54
+ }
55
+ }
@@ -0,0 +1,44 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { SharedStep, AddSharedStepPayload, UpdateSharedStepPayload } from '../types.js';
3
+ import { SharedStepSchema } from '../schemas.js';
4
+
5
+ export class SharedStepModule {
6
+ constructor(private readonly client: TestRailClientCore) {}
7
+
8
+ async getSharedStep(sharedStepId: number): Promise<SharedStep> {
9
+ this.client.validateId(sharedStepId, 'sharedStepId');
10
+ return this.client.parse<SharedStep>(
11
+ SharedStepSchema,
12
+ await this.client.request<unknown>('GET', `get_shared_step/${sharedStepId}`),
13
+ );
14
+ }
15
+
16
+ async getSharedSteps(projectId: number): Promise<SharedStep[]> {
17
+ this.client.validateId(projectId, 'projectId');
18
+ return this.client.parse<SharedStep[]>(
19
+ SharedStepSchema.array(),
20
+ await this.client.request<unknown>('GET', `get_shared_steps/${projectId}`),
21
+ );
22
+ }
23
+
24
+ async addSharedStep(projectId: number, payload: AddSharedStepPayload): Promise<SharedStep> {
25
+ this.client.validateId(projectId, 'projectId');
26
+ return this.client.parse<SharedStep>(
27
+ SharedStepSchema,
28
+ await this.client.request<unknown>('POST', `add_shared_step/${projectId}`, payload),
29
+ );
30
+ }
31
+
32
+ async updateSharedStep(sharedStepId: number, payload: UpdateSharedStepPayload): Promise<SharedStep> {
33
+ this.client.validateId(sharedStepId, 'sharedStepId');
34
+ return this.client.parse<SharedStep>(
35
+ SharedStepSchema,
36
+ await this.client.request<unknown>('POST', `update_shared_step/${sharedStepId}`, payload),
37
+ );
38
+ }
39
+
40
+ async deleteSharedStep(sharedStepId: number): Promise<void> {
41
+ this.client.validateId(sharedStepId, 'sharedStepId');
42
+ await this.client.request<void>('POST', `delete_shared_step/${sharedStepId}`);
43
+ }
44
+ }
@@ -0,0 +1,67 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Suite, AddSuitePayload, UpdateSuitePayload } from '../types.js';
3
+ import { SuiteSchema } from '../schemas.js';
4
+ import { z } from 'zod';
5
+
6
+ export class SuiteModule {
7
+ constructor(private readonly client: TestRailClientCore) {}
8
+
9
+ /**
10
+ * Get a suite by ID.
11
+ * @throws {TestRailValidationError} When suiteId is invalid
12
+ * @throws {TestRailApiError} When the API request fails
13
+ */
14
+ async getSuite(suiteId: number): Promise<Suite> {
15
+ this.client.validateId(suiteId, 'suiteId');
16
+ return this.client.parse<Suite>(SuiteSchema, await this.client.request<unknown>('GET', `get_suite/${suiteId}`));
17
+ }
18
+
19
+ /**
20
+ * Get all suites for a project.
21
+ * @throws {TestRailValidationError} When projectId is invalid
22
+ * @throws {TestRailApiError} When the API request fails
23
+ */
24
+ async getSuites(projectId: number): Promise<Suite[]> {
25
+ this.client.validateId(projectId, 'projectId');
26
+ return this.client.parse<Suite[]>(
27
+ z.array(SuiteSchema),
28
+ await this.client.request<unknown>('GET', `get_suites/${projectId}`),
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Add a suite to a project.
34
+ * @throws {TestRailValidationError} When projectId is invalid
35
+ * @throws {TestRailApiError} When the API request fails
36
+ */
37
+ async addSuite(projectId: number, payload: AddSuitePayload): Promise<Suite> {
38
+ this.client.validateId(projectId, 'projectId');
39
+ return this.client.parse<Suite>(
40
+ SuiteSchema,
41
+ await this.client.request<unknown>('POST', `add_suite/${projectId}`, payload),
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Update a suite.
47
+ * @throws {TestRailValidationError} When suiteId is invalid
48
+ * @throws {TestRailApiError} When the API request fails
49
+ */
50
+ async updateSuite(suiteId: number, payload: UpdateSuitePayload): Promise<Suite> {
51
+ this.client.validateId(suiteId, 'suiteId');
52
+ return this.client.parse<Suite>(
53
+ SuiteSchema,
54
+ await this.client.request<unknown>('POST', `update_suite/${suiteId}`, payload),
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Delete a suite.
60
+ * @throws {TestRailValidationError} When suiteId is invalid
61
+ * @throws {TestRailApiError} When the API request fails
62
+ */
63
+ async deleteSuite(suiteId: number): Promise<void> {
64
+ this.client.validateId(suiteId, 'suiteId');
65
+ await this.client.request<void>('POST', `delete_suite/${suiteId}`);
66
+ }
67
+ }
@@ -0,0 +1,28 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Test, GetTestsOptions } from '../types.js';
3
+ import { TestSchema } from '../schemas.js';
4
+ import { serializeIdList } from '../utils.js';
5
+ import { z } from 'zod';
6
+
7
+ export class TestModule {
8
+ constructor(private readonly client: TestRailClientCore) {}
9
+
10
+ async getTest(testId: number): Promise<Test> {
11
+ this.client.validateId(testId, 'testId');
12
+ return this.client.parse<Test>(TestSchema, await this.client.request<unknown>('GET', `get_test/${testId}`));
13
+ }
14
+
15
+ async getTests(runId: number, options?: GetTestsOptions): Promise<Test[]> {
16
+ this.client.validateId(runId, 'runId');
17
+ this.client.validatePaginationParams(options?.limit, options?.offset);
18
+ const endpoint = this.client.buildEndpoint(`get_tests/${runId}`, {
19
+ status_id: serializeIdList(options?.status_id),
20
+ limit: options?.limit,
21
+ offset: options?.offset,
22
+ });
23
+ const raw = await this.client.request<unknown>('GET', endpoint);
24
+ return (
25
+ this.client.parse<{ tests?: Test[] }>(z.object({ tests: z.array(TestSchema).optional() }), raw).tests ?? []
26
+ );
27
+ }
28
+ }
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import { TestRailValidationError } from '../errors.js';
3
+ import { UserSchema, GroupSchema } from '../schemas.js';
4
+ import { TestRailClientCore } from '../client-core.js';
5
+ import type { AddGroupPayload, AddUserPayload, Group, UpdateGroupPayload, UpdateUserPayload, User } from '../types.js';
6
+
7
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8
+
9
+ export class UsersModule {
10
+ constructor(private readonly client: TestRailClientCore) {}
11
+
12
+ async getUser(userId: number): Promise<User> {
13
+ this.client.validateId(userId, 'userId');
14
+ return this.client.parse<User>(UserSchema, await this.client.request<unknown>('GET', `get_user/${userId}`));
15
+ }
16
+
17
+ async getUserByEmail(email: string): Promise<User> {
18
+ if (!EMAIL_REGEX.test(email)) {
19
+ throw new TestRailValidationError('Invalid email format');
20
+ }
21
+
22
+ const endpoint = this.client.buildEndpoint('get_user_by_email', { email });
23
+ return this.client.parse<User>(UserSchema, await this.client.request<unknown>('GET', endpoint));
24
+ }
25
+
26
+ async getUsers(limit?: number, offset?: number, projectId?: number): Promise<User[]> {
27
+ this.client.validatePaginationParams(limit, offset);
28
+ if (projectId !== undefined) {
29
+ this.client.validateId(projectId, 'projectId');
30
+ }
31
+
32
+ const endpoint = this.client.buildEndpoint(projectId !== undefined ? `get_users/${projectId}` : 'get_users', {
33
+ limit,
34
+ offset,
35
+ });
36
+ const raw = await this.client.request<unknown>('GET', endpoint);
37
+
38
+ return (
39
+ this.client.parse<{ users?: User[] }>(z.object({ users: z.array(UserSchema).optional() }), raw).users ?? []
40
+ );
41
+ }
42
+
43
+ async getCurrentUser(): Promise<User> {
44
+ return this.client.parse<User>(UserSchema, await this.client.request<unknown>('GET', 'get_current_user'));
45
+ }
46
+
47
+ async addUser(payload: AddUserPayload): Promise<User> {
48
+ return this.client.parse<User>(UserSchema, await this.client.request<unknown>('POST', 'add_user', payload));
49
+ }
50
+
51
+ async updateUser(userId: number, payload: UpdateUserPayload): Promise<User> {
52
+ this.client.validateId(userId, 'userId');
53
+ return this.client.parse<User>(
54
+ UserSchema,
55
+ await this.client.request<unknown>('POST', `update_user/${userId}`, payload),
56
+ );
57
+ }
58
+
59
+ async getGroup(groupId: number): Promise<Group> {
60
+ this.client.validateId(groupId, 'groupId');
61
+ return this.client.parse<Group>(GroupSchema, await this.client.request<unknown>('GET', `get_group/${groupId}`));
62
+ }
63
+
64
+ async getGroups(): Promise<Group[]> {
65
+ return this.client.parse<Group[]>(
66
+ z.array(GroupSchema),
67
+ await this.client.request<unknown>('GET', 'get_groups'),
68
+ );
69
+ }
70
+
71
+ async addGroup(payload: AddGroupPayload): Promise<Group> {
72
+ return this.client.parse<Group>(GroupSchema, await this.client.request<unknown>('POST', 'add_group', payload));
73
+ }
74
+
75
+ async updateGroup(groupId: number, payload: UpdateGroupPayload): Promise<Group> {
76
+ this.client.validateId(groupId, 'groupId');
77
+ return this.client.parse<Group>(
78
+ GroupSchema,
79
+ await this.client.request<unknown>('POST', `update_group/${groupId}`, payload),
80
+ );
81
+ }
82
+
83
+ async deleteGroup(groupId: number): Promise<void> {
84
+ this.client.validateId(groupId, 'groupId');
85
+ await this.client.request<void>('POST', `delete_group/${groupId}`);
86
+ }
87
+ }
@@ -0,0 +1,36 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Variable, AddVariablePayload, UpdateVariablePayload } from '../types.js';
3
+ import { VariableSchema } from '../schemas.js';
4
+
5
+ export class VariableModule {
6
+ constructor(private readonly client: TestRailClientCore) {}
7
+
8
+ async getVariables(projectId: number): Promise<Variable[]> {
9
+ this.client.validateId(projectId, 'projectId');
10
+ return this.client.parse<Variable[]>(
11
+ VariableSchema.array(),
12
+ await this.client.request<unknown>('GET', `get_variables/${projectId}`),
13
+ );
14
+ }
15
+
16
+ async addVariable(projectId: number, payload: AddVariablePayload): Promise<Variable> {
17
+ this.client.validateId(projectId, 'projectId');
18
+ return this.client.parse<Variable>(
19
+ VariableSchema,
20
+ await this.client.request<unknown>('POST', `add_variable/${projectId}`, payload),
21
+ );
22
+ }
23
+
24
+ async updateVariable(variableId: number, payload: UpdateVariablePayload): Promise<Variable> {
25
+ this.client.validateId(variableId, 'variableId');
26
+ return this.client.parse<Variable>(
27
+ VariableSchema,
28
+ await this.client.request<unknown>('POST', `update_variable/${variableId}`, payload),
29
+ );
30
+ }
31
+
32
+ async deleteVariable(variableId: number): Promise<void> {
33
+ this.client.validateId(variableId, 'variableId');
34
+ await this.client.request<void>('POST', `delete_variable/${variableId}`);
35
+ }
36
+ }