@smartbear/mcp 0.26.0 → 0.27.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/README.md +10 -2
- package/dist/package.json.js +1 -1
- package/dist/qmetry/client/api/client-api.js +13 -1
- package/dist/qmetry/client/auto-resolve.js +23 -1
- package/dist/qmetry/client/handlers.js +10 -2
- package/dist/qmetry/client/issues.js +113 -1
- package/dist/qmetry/client/testcase.js +82 -1
- package/dist/qmetry/client/testsuite.js +20 -8
- package/dist/qmetry/client/tools/index.js +5 -2
- package/dist/qmetry/client/tools/issue-tools.js +163 -1
- package/dist/qmetry/client/tools/testcase-tools.js +81 -7
- package/dist/qmetry/client/tools/testsuite-tools.js +291 -26
- package/dist/qmetry/client/tools/udf-tools.js +294 -0
- package/dist/qmetry/client/udf.js +376 -0
- package/dist/qmetry/client.js +30 -3
- package/dist/qmetry/config/constants.js +63 -2
- package/dist/qmetry/config/rest-endpoints.js +7 -1
- package/dist/qmetry/types/common.js +85 -47
- package/dist/qmetry/types/issues.js +5 -0
- package/dist/qmetry/types/udf.js +77 -0
- package/dist/swagger/client/api.js +421 -8
- package/dist/swagger/client/configuration.js +9 -0
- package/dist/swagger/client/functional-testing-api.js +38 -2
- package/dist/swagger/client/functional-testing-tools.js +18 -0
- package/dist/swagger/client/functional-testing-types.js +11 -0
- package/dist/swagger/client/portal-types.js +35 -3
- package/dist/swagger/client/portal-utils.js +50 -0
- package/dist/swagger/client/registry-types.js +8 -0
- package/dist/swagger/client/tools.js +25 -4
- package/dist/swagger/client/utils.js +37 -0
- package/dist/swagger/client.js +37 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,7 +110,8 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
110
110
|
"QTM4J_API_KEY": "${input:qtm4j_api_key}",
|
|
111
111
|
"QTM4J_BASE_URL": "${input:qtm4j_base_url}",
|
|
112
112
|
"QTM4J_AUTOMATION_API_KEY": "${input:qtm4j_automation_api_key}",
|
|
113
|
-
"SWAGGER_FUNCTIONAL_TESTING_API_TOKEN": "${input:swagger_functional_testing_api_token}"
|
|
113
|
+
"SWAGGER_FUNCTIONAL_TESTING_API_TOKEN": "${input:swagger_functional_testing_api_token}",
|
|
114
|
+
"SWAGGER_FUNCTIONAL_TESTING_BASE_PATH": "${input:swagger_functional_testing_base_path}"
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
},
|
|
@@ -258,6 +259,12 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
258
259
|
"type": "promptString",
|
|
259
260
|
"description": "Swagger Functional Testing API Token - leave blank to disable Functional Testing tools",
|
|
260
261
|
"password": true
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"id": "swagger_functional_testing_base_path",
|
|
265
|
+
"type": "promptString",
|
|
266
|
+
"description": "Swagger Functional Testing API Base URL - leave blank to use the default (https://api.reflect.run/v1)",
|
|
267
|
+
"password": false
|
|
261
268
|
}
|
|
262
269
|
]
|
|
263
270
|
}
|
|
@@ -300,7 +307,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
|
|
|
300
307
|
"QTM4J_API_KEY": "your_qtm4j_key",
|
|
301
308
|
"QTM4J_BASE_URL": "https://qtmcloud.qmetry.com",
|
|
302
309
|
"QTM4J_AUTOMATION_API_KEY": "your_qtm4j_automation_api_key",
|
|
303
|
-
"SWAGGER_FUNCTIONAL_TESTING_API_TOKEN": "your_swagger_functional_testing_api_token"
|
|
310
|
+
"SWAGGER_FUNCTIONAL_TESTING_API_TOKEN": "your_swagger_functional_testing_api_token",
|
|
311
|
+
"SWAGGER_FUNCTIONAL_TESTING_BASE_PATH": "https://api.reflect.run/v1"
|
|
304
312
|
}
|
|
305
313
|
}
|
|
306
314
|
}
|
package/dist/package.json.js
CHANGED
|
@@ -7,7 +7,10 @@ async function qmetryRequest({
|
|
|
7
7
|
token,
|
|
8
8
|
project,
|
|
9
9
|
baseUrl,
|
|
10
|
-
body
|
|
10
|
+
body,
|
|
11
|
+
scopeId,
|
|
12
|
+
orgCode,
|
|
13
|
+
extraHeaders
|
|
11
14
|
}) {
|
|
12
15
|
const url = `${baseUrl}${path}`;
|
|
13
16
|
const headers = {
|
|
@@ -16,6 +19,15 @@ async function qmetryRequest({
|
|
|
16
19
|
"User-Agent": USER_AGENT,
|
|
17
20
|
"qmetry-source": "smartbear-mcp"
|
|
18
21
|
};
|
|
22
|
+
if (scopeId !== void 0) {
|
|
23
|
+
headers.scope = String(scopeId);
|
|
24
|
+
}
|
|
25
|
+
if (orgCode !== void 0) {
|
|
26
|
+
headers.orgcode = orgCode;
|
|
27
|
+
}
|
|
28
|
+
if (extraHeaders) {
|
|
29
|
+
Object.assign(headers, extraHeaders);
|
|
30
|
+
}
|
|
19
31
|
if (body) {
|
|
20
32
|
headers["Content-Type"] = "application/json";
|
|
21
33
|
}
|
|
@@ -9,6 +9,7 @@ const AUTO_RESOLVE_MODULES = [
|
|
|
9
9
|
handler: QMetryToolsHandlers.CREATE_TEST_CASE,
|
|
10
10
|
folderIdPath: "rootFolders.TC.id",
|
|
11
11
|
folderIdField: "tcFolderID",
|
|
12
|
+
folderIdAsString: true,
|
|
12
13
|
moduleName: "Test Cases"
|
|
13
14
|
},
|
|
14
15
|
{
|
|
@@ -26,6 +27,11 @@ const AUTO_RESOLVE_MODULES = [
|
|
|
26
27
|
viewIdPath: "latestViews.TE.viewId",
|
|
27
28
|
moduleName: "Test Case Run By Test Suite Run"
|
|
28
29
|
},
|
|
30
|
+
{
|
|
31
|
+
handler: QMetryToolsHandlers.FETCH_TEST_RUN_UDF_VALUES,
|
|
32
|
+
viewIdPath: "latestViews.TE.viewId",
|
|
33
|
+
moduleName: "Test Run UDF Values"
|
|
34
|
+
},
|
|
29
35
|
{
|
|
30
36
|
handler: QMetryToolsHandlers.FETCH_EXECUTIONS_BY_TESTSUITE,
|
|
31
37
|
viewIdPath: "latestViews.TEL.viewId",
|
|
@@ -42,8 +48,15 @@ const AUTO_RESOLVE_MODULES = [
|
|
|
42
48
|
handler: QMetryToolsHandlers.CREATE_TEST_SUITE,
|
|
43
49
|
folderIdPath: "rootFolders.TS.id",
|
|
44
50
|
folderIdField: "parentFolderId",
|
|
51
|
+
folderIdAsString: true,
|
|
45
52
|
moduleName: "Test Suites"
|
|
46
53
|
},
|
|
54
|
+
{
|
|
55
|
+
handler: QMetryToolsHandlers.UPDATE_TEST_SUITE,
|
|
56
|
+
folderIdPath: "rootFolders.TS.id",
|
|
57
|
+
folderIdField: "TsFolderID",
|
|
58
|
+
moduleName: "Test Suite"
|
|
59
|
+
},
|
|
47
60
|
{
|
|
48
61
|
handler: QMetryToolsHandlers.FETCH_ISSUES,
|
|
49
62
|
viewIdPath: "latestViews.IS.viewId",
|
|
@@ -69,7 +82,7 @@ function autoResolveViewIdAndFolderPath(args, projectInfo, config) {
|
|
|
69
82
|
if (config.folderIdPath && config.folderIdField && !updatedArgs[config.folderIdField]) {
|
|
70
83
|
const folderId = getNestedProperty(projectInfo, config.folderIdPath);
|
|
71
84
|
if (folderId) {
|
|
72
|
-
updatedArgs[config.folderIdField] = folderId;
|
|
85
|
+
updatedArgs[config.folderIdField] = config.folderIdAsString ? String(folderId) : folderId;
|
|
73
86
|
}
|
|
74
87
|
}
|
|
75
88
|
return updatedArgs;
|
|
@@ -77,9 +90,18 @@ function autoResolveViewIdAndFolderPath(args, projectInfo, config) {
|
|
|
77
90
|
function findAutoResolveConfig(handler) {
|
|
78
91
|
return AUTO_RESOLVE_MODULES.find((module) => module.handler === handler);
|
|
79
92
|
}
|
|
93
|
+
function extractProjectContext(projectInfo) {
|
|
94
|
+
const rawScopeId = projectInfo?.currentProjectId;
|
|
95
|
+
const parsedScopeId = rawScopeId !== void 0 ? Number(rawScopeId) : NaN;
|
|
96
|
+
return {
|
|
97
|
+
scopeId: Number.isFinite(parsedScopeId) ? parsedScopeId : void 0,
|
|
98
|
+
orgCode: projectInfo?.clientCode !== void 0 ? String(projectInfo.clientCode) : void 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
80
101
|
export {
|
|
81
102
|
AUTO_RESOLVE_MODULES,
|
|
82
103
|
autoResolveViewIdAndFolderPath,
|
|
104
|
+
extractProjectContext,
|
|
83
105
|
findAutoResolveConfig,
|
|
84
106
|
getNestedProperty
|
|
85
107
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { QMetryToolsHandlers } from "../config/constants.js";
|
|
2
2
|
import { getAutomationStatus, importAutomationResults } from "./automation.js";
|
|
3
|
-
import { linkIssuesToTestcaseRun, fetchIssues, updateIssue, createIssue, fetchIssuesLinkedToTestCase } from "./issues.js";
|
|
3
|
+
import { fetchIssueExecutions, linkIssuesToTestcaseRun, fetchIssues, updateIssue, createIssue, fetchIssuesLinkedToTestCase } from "./issues.js";
|
|
4
4
|
import { updateCycle, createCycle, createRelease, getPlatforms, getBuilds, getReleasesCycles, getProjectInfo, getProjects } from "./project.js";
|
|
5
5
|
import { fetchRequirementsLinkedToTestCase, fetchRequirementDetails, fetchRequirements } from "./requirement.js";
|
|
6
6
|
import { fetchTestCasesLinkedToRequirement, linkRequirementToTestCase, fetchTestCaseExecutions, fetchTestCaseSteps, fetchTestCaseVersionDetails, fetchTestCaseDetails, fetchTestCases, updateTestCase, createTestCases } from "./testcase.js";
|
|
7
7
|
import { linkPlatformsToTestSuite, fetchLinkedIssuesByTestCaseRun, fetchTestCaseRunsByTestSuiteRun, bulkUpdateExecutionStatus, fetchExecutionsByTestSuite, fetchTestCasesByTestSuite, reqLinkedTestCasesToTestSuite, linkTestCasesToTestSuite, fetchTestSuitesForTestCase, fetchTestSuites, updateTestSuite, createTestSuites } from "./testsuite.js";
|
|
8
|
+
import { fetchCascadeChildValues, fetchTestRunUdfValues, fetchTestRunUdfMetadata, bulkUpdateTestRunUdfs, fetchUdfModules, fetchUdfFieldTypes } from "./udf.js";
|
|
8
9
|
const QMETRY_HANDLER_MAP = {
|
|
9
10
|
[QMetryToolsHandlers.FETCH_PROJECTS]: getProjects,
|
|
10
11
|
[QMetryToolsHandlers.SET_PROJECT_INFO]: getProjectInfo,
|
|
@@ -45,7 +46,14 @@ const QMETRY_HANDLER_MAP = {
|
|
|
45
46
|
[QMetryToolsHandlers.LINK_ISSUES_TO_TESTCASE_RUN]: linkIssuesToTestcaseRun,
|
|
46
47
|
[QMetryToolsHandlers.LINK_PLATFORMS_TO_TESTSUITE]: linkPlatformsToTestSuite,
|
|
47
48
|
[QMetryToolsHandlers.IMPORT_AUTOMATION_RESULTS]: importAutomationResults,
|
|
48
|
-
[QMetryToolsHandlers.FETCH_AUTOMATION_STATUS]: getAutomationStatus
|
|
49
|
+
[QMetryToolsHandlers.FETCH_AUTOMATION_STATUS]: getAutomationStatus,
|
|
50
|
+
[QMetryToolsHandlers.FETCH_UDF_FIELD_TYPES]: fetchUdfFieldTypes,
|
|
51
|
+
[QMetryToolsHandlers.FETCH_UDF_MODULES]: fetchUdfModules,
|
|
52
|
+
[QMetryToolsHandlers.BULK_UPDATE_TEST_RUN_UDFS]: bulkUpdateTestRunUdfs,
|
|
53
|
+
[QMetryToolsHandlers.FETCH_TEST_RUN_UDF_METADATA]: fetchTestRunUdfMetadata,
|
|
54
|
+
[QMetryToolsHandlers.FETCH_TEST_RUN_UDF_VALUES]: fetchTestRunUdfValues,
|
|
55
|
+
[QMetryToolsHandlers.FETCH_ISSUE_EXECUTIONS]: fetchIssueExecutions,
|
|
56
|
+
[QMetryToolsHandlers.FETCH_CASCADE_CHILD_VALUES]: fetchCascadeChildValues
|
|
49
57
|
};
|
|
50
58
|
export {
|
|
51
59
|
QMETRY_HANDLER_MAP
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QMETRY_PATHS } from "../config/rest-endpoints.js";
|
|
2
|
-
import { DEFAULT_LINK_ISSUES_TO_TESTCASE_RUN_PAYLOAD, DEFAULT_FETCH_ISSUES_PAYLOAD, DEFAULT_UPDATE_ISSUE_PAYLOAD, DEFAULT_CREATE_ISSUE_PAYLOAD, DEFAULT_FETCH_ISSUES_LINKED_TO_TESTCASE_PAYLOAD } from "../types/issues.js";
|
|
2
|
+
import { DEFAULT_LINK_ISSUES_TO_TESTCASE_RUN_PAYLOAD, DEFAULT_FETCH_ISSUES_PAYLOAD, DEFAULT_UPDATE_ISSUE_PAYLOAD, DEFAULT_CREATE_ISSUE_PAYLOAD, DEFAULT_FETCH_ISSUES_LINKED_TO_TESTCASE_PAYLOAD, DEFAULT_FETCH_ISSUE_EXECUTIONS_PAYLOAD } from "../types/issues.js";
|
|
3
3
|
import { qmetryRequest } from "./api/client-api.js";
|
|
4
4
|
import { resolveDefaults } from "./utils.js";
|
|
5
5
|
async function createIssue(token, baseUrl, project, payload) {
|
|
@@ -104,6 +104,117 @@ async function fetchIssuesLinkedToTestCase(token, baseUrl, project, payload) {
|
|
|
104
104
|
body
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
|
+
async function fetchIssueExecutions(token, baseUrl, project, payload) {
|
|
108
|
+
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(
|
|
109
|
+
baseUrl,
|
|
110
|
+
project
|
|
111
|
+
);
|
|
112
|
+
const { linkedAssetId, ...rest } = payload;
|
|
113
|
+
const resolvedLinkedAsset = payload.linkedAsset ?? (typeof linkedAssetId === "number" ? { type: "DF", id: linkedAssetId } : void 0);
|
|
114
|
+
if (!resolvedLinkedAsset || typeof resolvedLinkedAsset.id !== "number") {
|
|
115
|
+
throw new Error(
|
|
116
|
+
"[fetchIssueExecutions] Missing or invalid required parameter: 'linkedAssetId'."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const body = {
|
|
120
|
+
...DEFAULT_FETCH_ISSUE_EXECUTIONS_PAYLOAD,
|
|
121
|
+
...rest,
|
|
122
|
+
linkedAsset: resolvedLinkedAsset
|
|
123
|
+
};
|
|
124
|
+
const result = await qmetryRequest({
|
|
125
|
+
method: "POST",
|
|
126
|
+
path: QMETRY_PATHS.ISSUES.GET_ISSUE_EXECUTIONS,
|
|
127
|
+
token,
|
|
128
|
+
project: resolvedProject,
|
|
129
|
+
baseUrl: resolvedBaseUrl,
|
|
130
|
+
body,
|
|
131
|
+
extraHeaders: {
|
|
132
|
+
action: "link-tc-list-view",
|
|
133
|
+
screenname: "ISSUE"
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (result.hasTcRunUdf === false) {
|
|
137
|
+
return {
|
|
138
|
+
...result,
|
|
139
|
+
testRunUdfNote: "No Test Run UDFs are configured for this project. The 'testRunUdfs' field will not be present in execution records. To enable Test Run UDF features, a project administrator must define Test Run UDF fields in the project settings."
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
let fieldDefs = {};
|
|
143
|
+
try {
|
|
144
|
+
const meta = await qmetryRequest({
|
|
145
|
+
method: "POST",
|
|
146
|
+
path: QMETRY_PATHS.UDF.TEST_RUN_UDF_METADATA,
|
|
147
|
+
token,
|
|
148
|
+
project: resolvedProject,
|
|
149
|
+
baseUrl: resolvedBaseUrl,
|
|
150
|
+
body: { entityType: "TCR" },
|
|
151
|
+
extraHeaders: {
|
|
152
|
+
action: "fetch-steps",
|
|
153
|
+
screenname: "EXECUTION RUN"
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
fieldDefs = meta.qmUDF?.TCR ?? {};
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
const hasMetadata = Object.keys(fieldDefs).length > 0;
|
|
160
|
+
const rows = result.data ?? [];
|
|
161
|
+
const enrichedData = rows.map((row) => {
|
|
162
|
+
let rawUdfs = {};
|
|
163
|
+
if (row.udfjson) {
|
|
164
|
+
try {
|
|
165
|
+
rawUdfs = JSON.parse(row.udfjson);
|
|
166
|
+
} catch {
|
|
167
|
+
rawUdfs = {};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
let testRunUdfs;
|
|
171
|
+
if (hasMetadata) {
|
|
172
|
+
testRunUdfs = Object.values(fieldDefs).map((def) => {
|
|
173
|
+
let value = Object.hasOwn(rawUdfs, def.name) ? rawUdfs[def.name] : null;
|
|
174
|
+
if (typeof value === "string" && /<[^>]+>/.test(value)) {
|
|
175
|
+
value = value.replace(/<[^>]*>/g, " ").replace(/&(nbsp|amp|lt|gt|quot);/g, (_, entity) => {
|
|
176
|
+
if (entity === "nbsp") return " ";
|
|
177
|
+
if (entity === "amp") return "&";
|
|
178
|
+
if (entity === "lt") return "<";
|
|
179
|
+
if (entity === "gt") return ">";
|
|
180
|
+
if (entity === "quot") return '"';
|
|
181
|
+
return `&${entity};`;
|
|
182
|
+
}).replace(/\s+/g, " ").trim();
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
name: def.name,
|
|
186
|
+
label: def.fieldLabel,
|
|
187
|
+
fieldID: def.projectUserFieldID,
|
|
188
|
+
fieldType: def.fieldTypeName,
|
|
189
|
+
value
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
} else {
|
|
193
|
+
testRunUdfs = Object.fromEntries(
|
|
194
|
+
Object.entries(rawUdfs).map(([key, val]) => {
|
|
195
|
+
if (typeof val === "string" && /<[^>]+>/.test(val)) {
|
|
196
|
+
const text = val.replace(/<[^>]*>/g, " ").replace(/&(nbsp|amp|lt|gt|quot);/g, (_, entity) => {
|
|
197
|
+
if (entity === "nbsp") return " ";
|
|
198
|
+
if (entity === "amp") return "&";
|
|
199
|
+
if (entity === "lt") return "<";
|
|
200
|
+
if (entity === "gt") return ">";
|
|
201
|
+
if (entity === "quot") return '"';
|
|
202
|
+
return `&${entity};`;
|
|
203
|
+
}).replace(/\s+/g, " ").trim();
|
|
204
|
+
return [key, text];
|
|
205
|
+
}
|
|
206
|
+
return [key, val];
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
const { udfjson: _udfjson, ...rowRest } = row;
|
|
211
|
+
return { ...rowRest, testRunUdfs };
|
|
212
|
+
});
|
|
213
|
+
return {
|
|
214
|
+
...result,
|
|
215
|
+
data: enrichedData
|
|
216
|
+
};
|
|
217
|
+
}
|
|
107
218
|
async function linkIssuesToTestcaseRun(token, baseUrl, project, payload) {
|
|
108
219
|
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(
|
|
109
220
|
baseUrl,
|
|
@@ -134,6 +245,7 @@ async function linkIssuesToTestcaseRun(token, baseUrl, project, payload) {
|
|
|
134
245
|
}
|
|
135
246
|
export {
|
|
136
247
|
createIssue,
|
|
248
|
+
fetchIssueExecutions,
|
|
137
249
|
fetchIssues,
|
|
138
250
|
fetchIssuesLinkedToTestCase,
|
|
139
251
|
linkIssuesToTestcaseRun,
|
|
@@ -197,7 +197,7 @@ async function fetchTestCaseExecutions(token, baseUrl, project, payload) {
|
|
|
197
197
|
"[fetchTestCaseExecutions] Missing or invalid required parameter: 'tcid'."
|
|
198
198
|
);
|
|
199
199
|
}
|
|
200
|
-
|
|
200
|
+
const result = await qmetryRequest({
|
|
201
201
|
method: "POST",
|
|
202
202
|
path: QMETRY_PATHS.TESTCASE.GET_TC_EXECUTIONS,
|
|
203
203
|
token,
|
|
@@ -205,6 +205,87 @@ async function fetchTestCaseExecutions(token, baseUrl, project, payload) {
|
|
|
205
205
|
baseUrl: resolvedBaseUrl,
|
|
206
206
|
body
|
|
207
207
|
});
|
|
208
|
+
if (result.hasTcRunUdf === false) {
|
|
209
|
+
return {
|
|
210
|
+
...result,
|
|
211
|
+
testRunUdfNote: "No Test Run UDFs are configured for this project. The 'testRunUdfs' field will not be present in execution records. To enable Test Run UDF features, a project administrator must define Test Run UDF fields in the project settings."
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
let fieldDefs = {};
|
|
215
|
+
try {
|
|
216
|
+
const meta = await qmetryRequest({
|
|
217
|
+
method: "POST",
|
|
218
|
+
path: QMETRY_PATHS.UDF.TEST_RUN_UDF_METADATA,
|
|
219
|
+
token,
|
|
220
|
+
project: resolvedProject,
|
|
221
|
+
baseUrl: resolvedBaseUrl,
|
|
222
|
+
body: { entityType: "TCR" },
|
|
223
|
+
extraHeaders: {
|
|
224
|
+
action: "fetch-steps",
|
|
225
|
+
screenname: "EXECUTION RUN"
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
fieldDefs = meta.qmUDF?.TCR ?? {};
|
|
229
|
+
} catch {
|
|
230
|
+
}
|
|
231
|
+
const hasMetadata = Object.keys(fieldDefs).length > 0;
|
|
232
|
+
const rows = result.data ?? [];
|
|
233
|
+
const enrichedData = rows.map((row) => {
|
|
234
|
+
let rawUdfs = {};
|
|
235
|
+
if (row.udfjson) {
|
|
236
|
+
try {
|
|
237
|
+
rawUdfs = JSON.parse(row.udfjson);
|
|
238
|
+
} catch {
|
|
239
|
+
rawUdfs = {};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
let testRunUdfs;
|
|
243
|
+
if (hasMetadata) {
|
|
244
|
+
testRunUdfs = Object.values(fieldDefs).map((def) => {
|
|
245
|
+
let value = Object.hasOwn(rawUdfs, def.name) ? rawUdfs[def.name] : null;
|
|
246
|
+
if (typeof value === "string" && /<[^>]+>/.test(value)) {
|
|
247
|
+
value = value.replace(/<[^>]*>/g, " ").replace(/&(nbsp|amp|lt|gt|quot);/g, (_, entity) => {
|
|
248
|
+
if (entity === "nbsp") return " ";
|
|
249
|
+
if (entity === "amp") return "&";
|
|
250
|
+
if (entity === "lt") return "<";
|
|
251
|
+
if (entity === "gt") return ">";
|
|
252
|
+
if (entity === "quot") return '"';
|
|
253
|
+
return `&${entity};`;
|
|
254
|
+
}).replace(/\s+/g, " ").trim();
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
name: def.name,
|
|
258
|
+
label: def.fieldLabel,
|
|
259
|
+
fieldID: def.projectUserFieldID,
|
|
260
|
+
fieldType: def.fieldTypeName,
|
|
261
|
+
value
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
testRunUdfs = Object.fromEntries(
|
|
266
|
+
Object.entries(rawUdfs).map(([key, val]) => {
|
|
267
|
+
if (typeof val === "string" && /<[^>]+>/.test(val)) {
|
|
268
|
+
const text = val.replace(/<[^>]*>/g, " ").replace(/&(nbsp|amp|lt|gt|quot);/g, (_, entity) => {
|
|
269
|
+
if (entity === "nbsp") return " ";
|
|
270
|
+
if (entity === "amp") return "&";
|
|
271
|
+
if (entity === "lt") return "<";
|
|
272
|
+
if (entity === "gt") return ">";
|
|
273
|
+
if (entity === "quot") return '"';
|
|
274
|
+
return `&${entity};`;
|
|
275
|
+
}).replace(/\s+/g, " ").trim();
|
|
276
|
+
return [key, text];
|
|
277
|
+
}
|
|
278
|
+
return [key, val];
|
|
279
|
+
})
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
const { udfjson: _udfjson, ...rest } = row;
|
|
283
|
+
return { ...rest, testRunUdfs };
|
|
284
|
+
});
|
|
285
|
+
return {
|
|
286
|
+
...result,
|
|
287
|
+
data: enrichedData
|
|
288
|
+
};
|
|
208
289
|
}
|
|
209
290
|
async function linkRequirementToTestCase(token, baseUrl, project, payload) {
|
|
210
291
|
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(
|
|
@@ -142,9 +142,10 @@ async function fetchExecutionsByTestSuite(token, baseUrl, project, payload) {
|
|
|
142
142
|
baseUrl,
|
|
143
143
|
project
|
|
144
144
|
);
|
|
145
|
+
const { scopeId, orgCode, ...payloadRest } = payload;
|
|
145
146
|
const body = {
|
|
146
147
|
...DEFAULT_FETCH_EXECUTIONS_BY_TESTSUITE_PAYLOAD,
|
|
147
|
-
...
|
|
148
|
+
...payloadRest
|
|
148
149
|
};
|
|
149
150
|
if (typeof body.tsID !== "number") {
|
|
150
151
|
throw new Error(
|
|
@@ -157,7 +158,9 @@ async function fetchExecutionsByTestSuite(token, baseUrl, project, payload) {
|
|
|
157
158
|
token,
|
|
158
159
|
project: resolvedProject,
|
|
159
160
|
baseUrl: resolvedBaseUrl,
|
|
160
|
-
body
|
|
161
|
+
body,
|
|
162
|
+
scopeId,
|
|
163
|
+
orgCode
|
|
161
164
|
});
|
|
162
165
|
}
|
|
163
166
|
async function fetchTestCaseRunsByTestSuiteRun(token, baseUrl, project, payload) {
|
|
@@ -165,9 +168,10 @@ async function fetchTestCaseRunsByTestSuiteRun(token, baseUrl, project, payload)
|
|
|
165
168
|
baseUrl,
|
|
166
169
|
project
|
|
167
170
|
);
|
|
171
|
+
const { scopeId, orgCode, ...payloadRest } = payload;
|
|
168
172
|
const body = {
|
|
169
173
|
...DEFAULT_FETCH_TESTCASE_RUNS_BY_TESTSUITE_RUN_PAYLOAD,
|
|
170
|
-
...
|
|
174
|
+
...payloadRest
|
|
171
175
|
};
|
|
172
176
|
if (typeof body.tsrunID !== "string" || !body.tsrunID) {
|
|
173
177
|
throw new Error(
|
|
@@ -185,7 +189,9 @@ async function fetchTestCaseRunsByTestSuiteRun(token, baseUrl, project, payload)
|
|
|
185
189
|
token,
|
|
186
190
|
project: resolvedProject,
|
|
187
191
|
baseUrl: resolvedBaseUrl,
|
|
188
|
-
body
|
|
192
|
+
body,
|
|
193
|
+
scopeId,
|
|
194
|
+
orgCode
|
|
189
195
|
});
|
|
190
196
|
}
|
|
191
197
|
async function fetchLinkedIssuesByTestCaseRun(token, baseUrl, project, payload) {
|
|
@@ -216,9 +222,10 @@ async function linkTestCasesToTestSuite(token, baseUrl, project, payload) {
|
|
|
216
222
|
baseUrl,
|
|
217
223
|
project
|
|
218
224
|
);
|
|
225
|
+
const { scopeId, orgCode, ...payloadRest } = payload;
|
|
219
226
|
const body = {
|
|
220
227
|
...DEFAULT_LINKED_TESTCASE_TO_TESTSUITE_PAYLOAD,
|
|
221
|
-
...
|
|
228
|
+
...payloadRest
|
|
222
229
|
};
|
|
223
230
|
if (typeof body.tsID !== "number") {
|
|
224
231
|
throw new Error(
|
|
@@ -234,7 +241,9 @@ async function linkTestCasesToTestSuite(token, baseUrl, project, payload) {
|
|
|
234
241
|
token,
|
|
235
242
|
project: resolvedProject,
|
|
236
243
|
baseUrl: resolvedBaseUrl,
|
|
237
|
-
body
|
|
244
|
+
body,
|
|
245
|
+
scopeId,
|
|
246
|
+
orgCode
|
|
238
247
|
});
|
|
239
248
|
}
|
|
240
249
|
async function reqLinkedTestCasesToTestSuite(token, baseUrl, project, payload) {
|
|
@@ -242,9 +251,10 @@ async function reqLinkedTestCasesToTestSuite(token, baseUrl, project, payload) {
|
|
|
242
251
|
baseUrl,
|
|
243
252
|
project
|
|
244
253
|
);
|
|
254
|
+
const { scopeId, orgCode, ...payloadRest } = payload;
|
|
245
255
|
const body = {
|
|
246
256
|
...DEFAULT_REQLINKED_TESTCASE_TO_TESTSUITE_PAYLOAD,
|
|
247
|
-
...
|
|
257
|
+
...payloadRest
|
|
248
258
|
};
|
|
249
259
|
if (typeof body.tsID !== "number") {
|
|
250
260
|
throw new Error(
|
|
@@ -262,7 +272,9 @@ async function reqLinkedTestCasesToTestSuite(token, baseUrl, project, payload) {
|
|
|
262
272
|
token,
|
|
263
273
|
project: resolvedProject,
|
|
264
274
|
baseUrl: resolvedBaseUrl,
|
|
265
|
-
body
|
|
275
|
+
body,
|
|
276
|
+
scopeId,
|
|
277
|
+
orgCode
|
|
266
278
|
});
|
|
267
279
|
}
|
|
268
280
|
async function linkPlatformsToTestSuite(token, baseUrl, project, payload) {
|
|
@@ -4,13 +4,15 @@ import { PROJECT_TOOLS } from "./project-tools.js";
|
|
|
4
4
|
import { REQUIREMENT_TOOLS } from "./requirement-tools.js";
|
|
5
5
|
import { TESTCASE_TOOLS } from "./testcase-tools.js";
|
|
6
6
|
import { TESTSUITE_TOOLS } from "./testsuite-tools.js";
|
|
7
|
+
import { UDF_TOOLS } from "./udf-tools.js";
|
|
7
8
|
const TOOLS = [
|
|
8
9
|
...PROJECT_TOOLS,
|
|
9
10
|
...TESTCASE_TOOLS,
|
|
10
11
|
...REQUIREMENT_TOOLS,
|
|
11
12
|
...TESTSUITE_TOOLS,
|
|
12
13
|
...ISSUE_TOOLS,
|
|
13
|
-
...AUTOMATION_TOOLS
|
|
14
|
+
...AUTOMATION_TOOLS,
|
|
15
|
+
...UDF_TOOLS
|
|
14
16
|
];
|
|
15
17
|
export {
|
|
16
18
|
AUTOMATION_TOOLS,
|
|
@@ -19,5 +21,6 @@ export {
|
|
|
19
21
|
REQUIREMENT_TOOLS,
|
|
20
22
|
TESTCASE_TOOLS,
|
|
21
23
|
TESTSUITE_TOOLS,
|
|
22
|
-
TOOLS
|
|
24
|
+
TOOLS,
|
|
25
|
+
UDF_TOOLS
|
|
23
26
|
};
|