common-qa-mcp 0.7.0 → 0.8.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/dist/adapters/remoteMcp/adapter.js +1 -1
- package/dist/adapters/simplifyqa/adapter.d.ts +16 -28
- package/dist/adapters/simplifyqa/adapter.d.ts.map +1 -1
- package/dist/adapters/simplifyqa/adapter.js +236 -70
- package/dist/adapters/simplifyqa/adapter.js.map +1 -1
- package/dist/adapters/simplifyqa/mapper.d.ts.map +1 -1
- package/dist/adapters/simplifyqa/mapper.js +12 -1
- package/dist/adapters/simplifyqa/mapper.js.map +1 -1
- package/dist/adapters/types.d.ts +10 -2
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/domain/types.d.ts +19 -3
- package/dist/domain/types.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/tools/discovery.tools.d.ts.map +1 -1
- package/dist/tools/discovery.tools.js +36 -9
- package/dist/tools/discovery.tools.js.map +1 -1
- package/dist/tools/markdown.d.ts.map +1 -1
- package/dist/tools/markdown.js +6 -1
- package/dist/tools/markdown.js.map +1 -1
- package/dist/tools/objects.tools.d.ts.map +1 -1
- package/dist/tools/objects.tools.js +46 -11
- package/dist/tools/objects.tools.js.map +1 -1
- package/dist/tools/recording.tools.d.ts.map +1 -1
- package/dist/tools/recording.tools.js +12 -1
- package/dist/tools/recording.tools.js.map +1 -1
- package/dist/tools/run.tools.d.ts.map +1 -1
- package/dist/tools/run.tools.js +66 -13
- package/dist/tools/run.tools.js.map +1 -1
- package/dist/tools/testcase.tools.d.ts.map +1 -1
- package/dist/tools/testcase.tools.js +15 -9
- package/dist/tools/testcase.tools.js.map +1 -1
- package/dist/tools/testdata.tools.d.ts.map +1 -1
- package/dist/tools/testdata.tools.js +43 -4
- package/dist/tools/testdata.tools.js.map +1 -1
- package/package.json +1 -1
|
@@ -38,7 +38,7 @@ export class RemoteMcpAdapter {
|
|
|
38
38
|
async client() {
|
|
39
39
|
if (!this.clientPromise) {
|
|
40
40
|
this.clientPromise = (async () => {
|
|
41
|
-
const client = new Client({ name: 'common-qa-mcp', version: '0.
|
|
41
|
+
const client = new Client({ name: 'common-qa-mcp', version: '0.8.0' });
|
|
42
42
|
// The `as Transport` cast bridges exactOptionalPropertyTypes: StreamableHTTPClientTransport
|
|
43
43
|
// implements Transport, but the SDK's own .d.ts (built without EOPT) leaves its optional
|
|
44
44
|
// callbacks (onclose?/onerror?/onmessage?) without `| undefined`, which this project's EOPT
|
|
@@ -69,10 +69,6 @@ export declare class SimplifyQaAdapter {
|
|
|
69
69
|
getTestCase(id: string, versionId?: string): Promise<TestCase>;
|
|
70
70
|
private fetchRawTestcase;
|
|
71
71
|
private fetchStepsCollection;
|
|
72
|
-
/** For every FUNCTION step, resolve the referenced function's code + name via /tm/function search
|
|
73
|
-
* (batched by distinct functionId). Mutates `tc.steps[i].functionCall` in place with
|
|
74
|
-
* `testCaseCode`/`testCaseName`. Non-fatal on failure — the raw id still renders. Mirrors the
|
|
75
|
-
* Excel export builder in tcfn.service.js:2696-2703. */
|
|
76
72
|
private hydrateFunctionNames;
|
|
77
73
|
/** For every step with an objectRef, resolve the object's display name (`disname`) via
|
|
78
74
|
* /tm/object/search (batched by distinct objectId). Mutates `objectRef.name` in place. Mirrors the
|
|
@@ -138,15 +134,13 @@ export declare class SimplifyQaAdapter {
|
|
|
138
134
|
* Backend has no tx endpoint: on mid-walk failure the returned tree carries `failedAt`, and
|
|
139
135
|
* every node BEFORE that point is already persisted. */
|
|
140
136
|
createModule(input: ModuleCreateInput): Promise<ModuleSaved>;
|
|
137
|
+
private checkChildLinked;
|
|
141
138
|
/** Depth-first walk of a subtree. Stops at the first failure — everything already POSTed stays
|
|
142
139
|
* (no rollback endpoint upstream) and the failure is bubbled back on the root via `failedAt`. */
|
|
143
140
|
private createSubtree;
|
|
144
141
|
private postOneModule;
|
|
145
|
-
/** PUT /pm/module/:id — partial edit. Controller reads `projectId` off the body for its
|
|
146
|
-
* `add_module` privilege check (module.controller.js:64), so it MUST be sent even when unchanged;
|
|
147
|
-
* we default to the connected project. Only `name` / `description` are exposed — the controller
|
|
148
|
-
* ignores everything else the validator accepts (isRoot, parentId, childIds). */
|
|
149
142
|
updateModule(id: string, patch: ModuleUpdateInput): Promise<SavedRef>;
|
|
143
|
+
private reparentModule;
|
|
150
144
|
/** PATCH /pm/module — two-phase per module.controller.js:94. `confirm:false` returns the per-id
|
|
151
145
|
* dependency footprint (test cases, functions, params, objects, defects, user stories). `confirm:true`
|
|
152
146
|
* triggers the actual delete: server drops only ids with EMPTY dependencies; ids with references are
|
|
@@ -173,7 +167,11 @@ export declare class SimplifyQaAdapter {
|
|
|
173
167
|
* their assigned projects. Optional `projectId` narrows to that project's members; optional
|
|
174
168
|
* `search` is a case-insensitive regex over `name`. */
|
|
175
169
|
listUsers(q: UserQuery): Promise<ListResult<UserSummary>>;
|
|
176
|
-
getObject(id: string
|
|
170
|
+
getObject(id: string, opts?: {
|
|
171
|
+
enrichNames?: boolean;
|
|
172
|
+
}): Promise<QaObject>;
|
|
173
|
+
private enrichObjectNames;
|
|
174
|
+
private resolveParameterNames;
|
|
177
175
|
productUrlFor(entity: EntityRef): string;
|
|
178
176
|
private hop;
|
|
179
177
|
private requireHop;
|
|
@@ -323,25 +321,6 @@ export declare class SimplifyQaAdapter {
|
|
|
323
321
|
* (e.g. "click" for WEB + ANDROID) resolves to the right numeric id. */
|
|
324
322
|
private actionCatalogCache;
|
|
325
323
|
private actionCatalog;
|
|
326
|
-
/** POST /tm/object/createObjectSeq — get-or-create the shared object row by locator match. The
|
|
327
|
-
* backend does the dedup (`object.controller.js:268-370`) — same-locators reuses, same-disname-
|
|
328
|
-
* different-locators creates a suffixed one. Returns the numeric object id.
|
|
329
|
-
*
|
|
330
|
-
* ponytail: minimum body — no thumbnail_url / pageScreenShot_url / parameterId. The recording
|
|
331
|
-
* path enriches these; the tc_save path doesn't have screenshots to attach, and the object
|
|
332
|
-
* works for execution without them. */
|
|
333
|
-
/** GET-OR-CREATE by exact `disname`, scoped to the project — despite its name and the old comment
|
|
334
|
-
* here, `createObjectSeq` is a plain CREATE with no dedup of its own, so calling it unconditionally
|
|
335
|
-
* on every save minted a fresh duplicate object for the same recorded element every time (root
|
|
336
|
-
* cause of the "duplicate objects in the Object module" reports). Mirrors the SAME
|
|
337
|
-
* search-then-reuse pattern already trusted for parameters (findParameterId/getOrCreateParameter):
|
|
338
|
-
* 1. Exact case-insensitive name match found + SAME locator set → reuse the existing object as-is.
|
|
339
|
-
* 2. Exact name match found + DIFFERENT locators (the element's attributes drifted) → UPDATE the
|
|
340
|
-
* existing object in place (via updateObject's own locator-upsert) instead of creating a
|
|
341
|
-
* sibling — one entry per element, not a growing pile of near-identical copies.
|
|
342
|
-
* 3. No name match → create a new object, same as before.
|
|
343
|
-
* Does NOT catch a different name resolving to the same physical element (no cross-project
|
|
344
|
-
* locator-value search) — that would need a much broader, more expensive scan; out of scope here. */
|
|
345
324
|
private resolveObjectId;
|
|
346
325
|
/** For every automation step, resolve `actionId` (from action name + technology) and
|
|
347
326
|
* `objectId` (via createObjectSeq get-or-create) so the mapper can emit a valid step. Runs
|
|
@@ -434,6 +413,8 @@ export declare class SimplifyQaAdapter {
|
|
|
434
413
|
parameterNames?: string[];
|
|
435
414
|
/** Resolve to moduleId via listModules — the caller supplies a name, never an id. */
|
|
436
415
|
moduleName?: string;
|
|
416
|
+
}, opts?: {
|
|
417
|
+
enrichNames?: boolean;
|
|
437
418
|
}): Promise<QaObject>;
|
|
438
419
|
actionsCatalog(platform?: string): Promise<ActionDef[]>;
|
|
439
420
|
/** Fetch the default (template) iteration row for a TC. Fills gaps from the raw TC:
|
|
@@ -470,6 +451,13 @@ export declare class SimplifyQaAdapter {
|
|
|
470
451
|
* (`children[]`) instead of step details — a suite doc's `type` field flags it. start/stop still
|
|
471
452
|
* hop for v1. */
|
|
472
453
|
getRun(runId: string): Promise<RunStatus>;
|
|
454
|
+
private fetchExecDoc;
|
|
455
|
+
private fetchStepResults;
|
|
456
|
+
getSuiteChildRun(suiteRunId: string, child: {
|
|
457
|
+
testcaseId: string;
|
|
458
|
+
versionId?: string | undefined;
|
|
459
|
+
stepId?: string | undefined;
|
|
460
|
+
}): Promise<RunStatus>;
|
|
473
461
|
/** Most recent execution runId for a SUITE — same `/exec/execution/search` the /reports UI's
|
|
474
462
|
* current-executions view drives, filtered to `type=SUITE` + this suite's numeric id, sorted by
|
|
475
463
|
* id desc, limit 1. Returns the numeric run id (what `getRun` accepts), or null if the suite has
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/simplifyqa/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAU,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EACV,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAC5F,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAC5D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAClE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EACxF,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAClF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAmB,iBAAiB,EAAE,WAAW,EACzF,cAAc,EAAE,QAAQ,EAAE,YAAY,EACtC,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,uBAAuB,EACjF,6BAA6B,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EACnE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EACxD,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EACpF,WAAW,EAAE,kBAAkB,EAAE,mBAAmB,EACrD,MAAM,uBAAuB,CAAC;AAY/B,kFAAkF;AAClF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,cAAc,GAAG,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/simplifyqa/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,12 +7,15 @@ function mapRunState(s) {
|
|
|
7
7
|
switch (s.toUpperCase()) {
|
|
8
8
|
case 'PASSED': return 'passed';
|
|
9
9
|
case 'FAILED': return 'failed';
|
|
10
|
+
case 'SKIPPED': return 'skipped';
|
|
11
|
+
case 'NOTEXECUTED': return 'notExecuted';
|
|
10
12
|
case 'STOPPED': return 'stopped';
|
|
13
|
+
case 'ERROR': return 'error';
|
|
11
14
|
case 'RUNNING':
|
|
12
15
|
case 'INPROGRESS': return 'running';
|
|
13
|
-
case 'QUEUED':
|
|
14
|
-
case '
|
|
15
|
-
default: return '
|
|
16
|
+
case 'QUEUED': return 'queued';
|
|
17
|
+
case '': return 'queued'; // no status yet assigned — genuinely not started
|
|
18
|
+
default: return 'error';
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
/** Formula names round-trip with parens on ("FIRST_NAME()") from the backend and sometimes with a full
|
|
@@ -514,10 +517,6 @@ export class SimplifyQaAdapter {
|
|
|
514
517
|
const rows = Array.isArray(body) ? body : Array.isArray(body?.data) ? body.data : [];
|
|
515
518
|
return rows;
|
|
516
519
|
}
|
|
517
|
-
/** For every FUNCTION step, resolve the referenced function's code + name via /tm/function search
|
|
518
|
-
* (batched by distinct functionId). Mutates `tc.steps[i].functionCall` in place with
|
|
519
|
-
* `testCaseCode`/`testCaseName`. Non-fatal on failure — the raw id still renders. Mirrors the
|
|
520
|
-
* Excel export builder in tcfn.service.js:2696-2703. */
|
|
521
520
|
async hydrateFunctionNames(tc) {
|
|
522
521
|
const ids = new Set();
|
|
523
522
|
for (const s of tc.steps) {
|
|
@@ -543,16 +542,33 @@ export class SimplifyQaAdapter {
|
|
|
543
542
|
continue;
|
|
544
543
|
byId.set(rid, { code: str(r, 'code'), name: str(r, 'name', 'title') ?? '' });
|
|
545
544
|
}
|
|
545
|
+
const versionNamesById = new Map();
|
|
546
|
+
await Promise.all([...ids].map(async (fnId) => {
|
|
547
|
+
try {
|
|
548
|
+
const versionRows = await this.fetchFunctionVersionRows(fnId);
|
|
549
|
+
const versions = toVersionEntries(versionRows);
|
|
550
|
+
versionNamesById.set(fnId, new Map(versions.map((v) => [v.id, v.name])));
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
// Non-fatal per function: that step's version falls back to the bare uuid.
|
|
554
|
+
}
|
|
555
|
+
}));
|
|
546
556
|
for (const s of tc.steps) {
|
|
547
557
|
if (s.stepType !== 'function' || !s.functionCall)
|
|
548
558
|
continue;
|
|
549
559
|
const hit = byId.get(s.functionCall.testCaseId);
|
|
550
|
-
if (
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
560
|
+
if (hit) {
|
|
561
|
+
if (hit.code)
|
|
562
|
+
s.functionCall.testCaseCode = hit.code;
|
|
563
|
+
if (hit.name)
|
|
564
|
+
s.functionCall.testCaseName = hit.name;
|
|
565
|
+
}
|
|
566
|
+
const versionId = s.functionCall.versionId;
|
|
567
|
+
if (versionId) {
|
|
568
|
+
const versionName = versionNamesById.get(s.functionCall.testCaseId)?.get(versionId);
|
|
569
|
+
if (versionName)
|
|
570
|
+
s.functionCall.versionName = versionName;
|
|
571
|
+
}
|
|
556
572
|
}
|
|
557
573
|
}
|
|
558
574
|
catch {
|
|
@@ -1023,11 +1039,27 @@ export class SimplifyQaAdapter {
|
|
|
1023
1039
|
* every node BEFORE that point is already persisted. */
|
|
1024
1040
|
async createModule(input) {
|
|
1025
1041
|
const root = await this.postOneModule(input.name, input.isRoot, input.projectId, input.description, input.parentId);
|
|
1042
|
+
if (!input.isRoot && input.parentId) {
|
|
1043
|
+
root.linkWarning = await this.checkChildLinked(input.parentId, root.id);
|
|
1044
|
+
}
|
|
1026
1045
|
if (input.children?.length) {
|
|
1027
1046
|
root.children = await this.createSubtree(input.children, root.id, input.projectId, input.name, root);
|
|
1028
1047
|
}
|
|
1029
1048
|
return root;
|
|
1030
1049
|
}
|
|
1050
|
+
async checkChildLinked(parentId, childId) {
|
|
1051
|
+
try {
|
|
1052
|
+
const parent = await this.getModule(parentId);
|
|
1053
|
+
const numChildId = Number.parseInt(childId, 10);
|
|
1054
|
+
if (parent.childIds?.includes(numChildId))
|
|
1055
|
+
return undefined;
|
|
1056
|
+
return `Created with parentId=${parentId}, but a fresh read of module ${parentId} does not yet list ` +
|
|
1057
|
+
`${childId} in its childIds — it may not appear under its parent in the Module screen tree until that catches up.`;
|
|
1058
|
+
}
|
|
1059
|
+
catch {
|
|
1060
|
+
return undefined;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1031
1063
|
/** Depth-first walk of a subtree. Stops at the first failure — everything already POSTed stays
|
|
1032
1064
|
* (no rollback endpoint upstream) and the failure is bubbled back on the root via `failedAt`. */
|
|
1033
1065
|
async createSubtree(children, parentId, projectId, parentPath, root) {
|
|
@@ -1044,6 +1076,7 @@ export class SimplifyQaAdapter {
|
|
|
1044
1076
|
root.failedAt = { path, error: e instanceof Error ? e.message : String(e) };
|
|
1045
1077
|
return out;
|
|
1046
1078
|
}
|
|
1079
|
+
saved.linkWarning = await this.checkChildLinked(parentId, saved.id);
|
|
1047
1080
|
if (child.children?.length) {
|
|
1048
1081
|
saved.children = await this.createSubtree(child.children, saved.id, projectId, path, root);
|
|
1049
1082
|
}
|
|
@@ -1065,23 +1098,56 @@ export class SimplifyQaAdapter {
|
|
|
1065
1098
|
const raw = await this.http.postJson('/pm/module', payload);
|
|
1066
1099
|
return toModuleSaved(raw, name);
|
|
1067
1100
|
}
|
|
1068
|
-
/** PUT /pm/module/:id — partial edit. Controller reads `projectId` off the body for its
|
|
1069
|
-
* `add_module` privilege check (module.controller.js:64), so it MUST be sent even when unchanged;
|
|
1070
|
-
* we default to the connected project. Only `name` / `description` are exposed — the controller
|
|
1071
|
-
* ignores everything else the validator accepts (isRoot, parentId, childIds). */
|
|
1072
1101
|
async updateModule(id, patch) {
|
|
1073
|
-
const projectId = patch.projectId
|
|
1074
|
-
? Number.parseInt(numericId(patch.projectId), 10)
|
|
1075
|
-
: this.requireProjectId();
|
|
1076
|
-
const body = { projectId };
|
|
1077
|
-
if (patch.name !== undefined)
|
|
1078
|
-
body['name'] = patch.name;
|
|
1079
|
-
if (patch.description !== undefined)
|
|
1080
|
-
body['description'] = patch.description;
|
|
1081
1102
|
const resolvedId = numericId(id);
|
|
1082
|
-
|
|
1103
|
+
let raw;
|
|
1104
|
+
if (patch.name !== undefined || patch.description !== undefined) {
|
|
1105
|
+
const projectId = patch.projectId
|
|
1106
|
+
? Number.parseInt(numericId(patch.projectId), 10)
|
|
1107
|
+
: this.requireProjectId();
|
|
1108
|
+
const body = { projectId };
|
|
1109
|
+
if (patch.name !== undefined)
|
|
1110
|
+
body['name'] = patch.name;
|
|
1111
|
+
if (patch.description !== undefined)
|
|
1112
|
+
body['description'] = patch.description;
|
|
1113
|
+
raw = await this.http.putJson(`/pm/module/${resolvedId}`, body);
|
|
1114
|
+
}
|
|
1115
|
+
if (patch.parentId !== undefined) {
|
|
1116
|
+
await this.reparentModule(resolvedId, patch.parentId);
|
|
1117
|
+
}
|
|
1083
1118
|
return toSavedRef(raw ?? { id: resolvedId }, false);
|
|
1084
1119
|
}
|
|
1120
|
+
async reparentModule(id, newParentId) {
|
|
1121
|
+
if (newParentId === '') {
|
|
1122
|
+
await this.http.putJson(`/pm/module/bulkupdate/${id}`, { isRoot: true });
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
const targetId = numericId(newParentId);
|
|
1126
|
+
if (targetId === id) {
|
|
1127
|
+
throw new Error(`Cannot set module ${id} as its own parent.`);
|
|
1128
|
+
}
|
|
1129
|
+
const all = await this.listModules();
|
|
1130
|
+
const childrenOf = new Map();
|
|
1131
|
+
for (const m of all)
|
|
1132
|
+
if (m.parentId)
|
|
1133
|
+
childrenOf.set(m.parentId, [...(childrenOf.get(m.parentId) ?? []), m.id]);
|
|
1134
|
+
const descendants = new Set();
|
|
1135
|
+
const stack = [...(childrenOf.get(id) ?? [])];
|
|
1136
|
+
while (stack.length) {
|
|
1137
|
+
const next = stack.pop();
|
|
1138
|
+
if (descendants.has(next))
|
|
1139
|
+
continue;
|
|
1140
|
+
descendants.add(next);
|
|
1141
|
+
stack.push(...(childrenOf.get(next) ?? []));
|
|
1142
|
+
}
|
|
1143
|
+
if (descendants.has(targetId)) {
|
|
1144
|
+
throw new Error(`Cannot move module ${id} under ${targetId} — it is already a descendant of ${id} (would detach the subtree).`);
|
|
1145
|
+
}
|
|
1146
|
+
if (!all.some((m) => m.id === targetId)) {
|
|
1147
|
+
throw new Error(`Parent module ${targetId} not found.`);
|
|
1148
|
+
}
|
|
1149
|
+
await this.http.putJson(`/pm/module/bulkupdate/${id}`, { parentId: Number.parseInt(targetId, 10), isRoot: false });
|
|
1150
|
+
}
|
|
1085
1151
|
/** PATCH /pm/module — two-phase per module.controller.js:94. `confirm:false` returns the per-id
|
|
1086
1152
|
* dependency footprint (test cases, functions, params, objects, defects, user stories). `confirm:true`
|
|
1087
1153
|
* triggers the actual delete: server drops only ids with EMPTY dependencies; ids with references are
|
|
@@ -1195,12 +1261,55 @@ export class SimplifyQaAdapter {
|
|
|
1195
1261
|
return withTotal(users, raw);
|
|
1196
1262
|
}
|
|
1197
1263
|
// ---------------------------------------------------------------------- Objects (read)
|
|
1198
|
-
async getObject(id) {
|
|
1264
|
+
async getObject(id, opts) {
|
|
1199
1265
|
const raw = await this.http.getJson(`/tm/object/${numericId(id)}`);
|
|
1200
1266
|
let doc = raw?.data ?? raw;
|
|
1201
1267
|
if (Array.isArray(doc))
|
|
1202
1268
|
doc = doc.length > 0 ? doc[0] : {};
|
|
1203
|
-
|
|
1269
|
+
const obj = toObject(doc && typeof doc === 'object' ? doc : {});
|
|
1270
|
+
if (opts?.enrichNames)
|
|
1271
|
+
await this.enrichObjectNames(obj);
|
|
1272
|
+
return obj;
|
|
1273
|
+
}
|
|
1274
|
+
async enrichObjectNames(obj) {
|
|
1275
|
+
if (obj.moduleId) {
|
|
1276
|
+
try {
|
|
1277
|
+
const modules = await this.listModules();
|
|
1278
|
+
const match = modules.find((m) => m.id === obj.moduleId);
|
|
1279
|
+
if (match)
|
|
1280
|
+
obj.moduleName = match.name;
|
|
1281
|
+
}
|
|
1282
|
+
catch { /* best-effort */ }
|
|
1283
|
+
}
|
|
1284
|
+
if (obj.parameterIds?.length) {
|
|
1285
|
+
const names = await this.resolveParameterNames(obj.parameterIds);
|
|
1286
|
+
const resolved = obj.parameterIds.map((pid) => names.get(pid)).filter((n) => !!n);
|
|
1287
|
+
if (resolved.length)
|
|
1288
|
+
obj.parameterNames = resolved;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
async resolveParameterNames(ids) {
|
|
1292
|
+
const map = new Map();
|
|
1293
|
+
const numIds = ids.map((i) => Number.parseInt(i, 10)).filter(Number.isFinite);
|
|
1294
|
+
if (!numIds.length)
|
|
1295
|
+
return map;
|
|
1296
|
+
try {
|
|
1297
|
+
const projectId = this.requireProjectId();
|
|
1298
|
+
const body = this.searchBody([
|
|
1299
|
+
{ column: 'projectId', value: projectId },
|
|
1300
|
+
{ column: 'id', value: numIds, regEx: false },
|
|
1301
|
+
]);
|
|
1302
|
+
const raw = await this.http.postJson('/tm/parameter/search', body);
|
|
1303
|
+
const rows = pickArray(raw, 'data', 'content');
|
|
1304
|
+
for (const row of rows) {
|
|
1305
|
+
const rid = str(row, 'id', '_id', 'parameterId');
|
|
1306
|
+
const name = str(row, 'name');
|
|
1307
|
+
if (rid && name)
|
|
1308
|
+
map.set(rid, name);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
catch { /* best-effort */ }
|
|
1312
|
+
return map;
|
|
1204
1313
|
}
|
|
1205
1314
|
// ---------------------------------------------------------------------- Deep links
|
|
1206
1315
|
productUrlFor(entity) {
|
|
@@ -2249,25 +2358,6 @@ export class SimplifyQaAdapter {
|
|
|
2249
2358
|
})().catch((e) => { this.actionCatalogCache = null; throw e; });
|
|
2250
2359
|
return this.actionCatalogCache;
|
|
2251
2360
|
}
|
|
2252
|
-
/** POST /tm/object/createObjectSeq — get-or-create the shared object row by locator match. The
|
|
2253
|
-
* backend does the dedup (`object.controller.js:268-370`) — same-locators reuses, same-disname-
|
|
2254
|
-
* different-locators creates a suffixed one. Returns the numeric object id.
|
|
2255
|
-
*
|
|
2256
|
-
* ponytail: minimum body — no thumbnail_url / pageScreenShot_url / parameterId. The recording
|
|
2257
|
-
* path enriches these; the tc_save path doesn't have screenshots to attach, and the object
|
|
2258
|
-
* works for execution without them. */
|
|
2259
|
-
/** GET-OR-CREATE by exact `disname`, scoped to the project — despite its name and the old comment
|
|
2260
|
-
* here, `createObjectSeq` is a plain CREATE with no dedup of its own, so calling it unconditionally
|
|
2261
|
-
* on every save minted a fresh duplicate object for the same recorded element every time (root
|
|
2262
|
-
* cause of the "duplicate objects in the Object module" reports). Mirrors the SAME
|
|
2263
|
-
* search-then-reuse pattern already trusted for parameters (findParameterId/getOrCreateParameter):
|
|
2264
|
-
* 1. Exact case-insensitive name match found + SAME locator set → reuse the existing object as-is.
|
|
2265
|
-
* 2. Exact name match found + DIFFERENT locators (the element's attributes drifted) → UPDATE the
|
|
2266
|
-
* existing object in place (via updateObject's own locator-upsert) instead of creating a
|
|
2267
|
-
* sibling — one entry per element, not a growing pile of near-identical copies.
|
|
2268
|
-
* 3. No name match → create a new object, same as before.
|
|
2269
|
-
* Does NOT catch a different name resolving to the same physical element (no cross-project
|
|
2270
|
-
* locator-value search) — that would need a much broader, more expensive scan; out of scope here. */
|
|
2271
2361
|
async resolveObjectId(objectRef, projectId, moduleId, technology, objectTemplateId) {
|
|
2272
2362
|
const name = (objectRef.name ?? '').trim();
|
|
2273
2363
|
const incomingLocators = objectRef.locators ?? [];
|
|
@@ -2620,6 +2710,26 @@ export class SimplifyQaAdapter {
|
|
|
2620
2710
|
throw new Error('createObject: objectType is required (e.g. "WEB", "ANDROID") — it selects the object template ' +
|
|
2621
2711
|
'and cannot be changed after the object is created.');
|
|
2622
2712
|
}
|
|
2713
|
+
const wantedName = (o.name ?? '').trim();
|
|
2714
|
+
if (wantedName) {
|
|
2715
|
+
try {
|
|
2716
|
+
const candidates = await this.searchObjects({ query: wantedName, limit: 20 });
|
|
2717
|
+
const existing = candidates.find((c) => c.name.trim().toLowerCase() === wantedName.toLowerCase());
|
|
2718
|
+
if (existing?.id) {
|
|
2719
|
+
const key = (l) => `${l.name}::${l.value}`;
|
|
2720
|
+
const existingKeys = new Set(existing.locators.map(key));
|
|
2721
|
+
const incomingKeys = new Set((o.locators ?? []).map(key));
|
|
2722
|
+
const sameLocators = existingKeys.size === incomingKeys.size
|
|
2723
|
+
&& [...existingKeys].every((k) => incomingKeys.has(k));
|
|
2724
|
+
return sameLocators || !o.locators?.length
|
|
2725
|
+
? existing
|
|
2726
|
+
: await this.updateObject(existing.id, { locators: o.locators });
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
catch {
|
|
2730
|
+
// Lookup failed — fall through to create rather than block the save over this.
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2623
2733
|
const projectId = this.requireProjectId();
|
|
2624
2734
|
const objecttemplateId = await this.resolveObjectTemplateId(o.objectType);
|
|
2625
2735
|
const moduleId = await this.resolveDefaultModuleId();
|
|
@@ -2707,7 +2817,7 @@ export class SimplifyQaAdapter {
|
|
|
2707
2817
|
* attribute set is even valid for this object), so editing them here — outside the create flow — is
|
|
2708
2818
|
* out of scope; changing them means creating a new object, not "updating" this one.
|
|
2709
2819
|
*/
|
|
2710
|
-
async updateObject(id, changes) {
|
|
2820
|
+
async updateObject(id, changes, opts) {
|
|
2711
2821
|
const numId = numericId(id);
|
|
2712
2822
|
const raw = await this.http.getJson(`/tm/object/${numId}`);
|
|
2713
2823
|
const doc = (raw?.data && typeof raw.data === 'object' ? raw.data : raw) ?? {};
|
|
@@ -2771,7 +2881,10 @@ export class SimplifyQaAdapter {
|
|
|
2771
2881
|
};
|
|
2772
2882
|
const res = await this.http.putJson(`/tm/object/${numId}`, body);
|
|
2773
2883
|
const updated = (res?.data && typeof res.data === 'object' ? res.data : res) ?? doc;
|
|
2774
|
-
|
|
2884
|
+
const obj = toObject(updated);
|
|
2885
|
+
if (opts?.enrichNames)
|
|
2886
|
+
await this.enrichObjectNames(obj);
|
|
2887
|
+
return obj;
|
|
2775
2888
|
}
|
|
2776
2889
|
// ---------------------------------------------------------------------- Actions catalog
|
|
2777
2890
|
async actionsCatalog(platform) {
|
|
@@ -2825,8 +2938,9 @@ export class SimplifyQaAdapter {
|
|
|
2825
2938
|
const meta = await this.fetchParameterMeta(pid);
|
|
2826
2939
|
if (!meta)
|
|
2827
2940
|
continue; // parameter deleted — skip
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2941
|
+
const isProtected = meta.protected || looksSensitive(meta.name);
|
|
2942
|
+
const seed = isProtected ? (rawValue ? '*****' : '(empty)') : rawValue;
|
|
2943
|
+
parameters.push({ paramId: pid, name: meta.name, seed, ...(isProtected ? { protected: true } : {}) });
|
|
2830
2944
|
}
|
|
2831
2945
|
}
|
|
2832
2946
|
return { tcId: q.tcId, versionId: versionId, environmentType: env, parameters };
|
|
@@ -3005,23 +3119,19 @@ export class SimplifyQaAdapter {
|
|
|
3005
3119
|
// Top-level execution doc (carries `type` = 'TC' or 'SUITE', and `executionItems[]` for a suite).
|
|
3006
3120
|
// One extra POST per getRun call; a suite short-circuits here so no /exec/report is needed.
|
|
3007
3121
|
if (Number.isFinite(runIdNum)) {
|
|
3008
|
-
const
|
|
3009
|
-
searchFields: [
|
|
3010
|
-
{ column: 'id', value: runIdNum, regEx: false },
|
|
3011
|
-
{ column: 'projectId', value: projectId, regEx: false },
|
|
3012
|
-
{ column: 'deleted', value: false },
|
|
3013
|
-
],
|
|
3014
|
-
startIndex: 0,
|
|
3015
|
-
limit: 1,
|
|
3016
|
-
};
|
|
3017
|
-
const execRaw = await this.http.postJson('/exec/execution/search', execBody);
|
|
3018
|
-
const execDoc = pickArray(execRaw, 'data', 'content', 'executions')[0];
|
|
3122
|
+
const execDoc = await this.fetchExecDoc(runIdNum, projectId);
|
|
3019
3123
|
if (execDoc && String(execDoc.type ?? '').toUpperCase() === 'SUITE') {
|
|
3020
3124
|
const items = Array.isArray(execDoc.executionItems) ? execDoc.executionItems : [];
|
|
3021
3125
|
const children = items.map((it) => {
|
|
3126
|
+
const testcaseId = str(it, 'testcaseId');
|
|
3127
|
+
const versionId = str(it, 'versionId');
|
|
3128
|
+
const stepId = str(it, 'stepId');
|
|
3022
3129
|
const child = {
|
|
3023
|
-
id:
|
|
3130
|
+
id: testcaseId ?? String(it?.id ?? ''),
|
|
3024
3131
|
state: mapRunState(str(it, 'status') ?? ''),
|
|
3132
|
+
...(testcaseId ? { testcaseId } : {}),
|
|
3133
|
+
...(versionId ? { versionId } : {}),
|
|
3134
|
+
...(stepId ? { stepId } : {}),
|
|
3025
3135
|
};
|
|
3026
3136
|
const name = str(it, 'name');
|
|
3027
3137
|
if (name)
|
|
@@ -3031,10 +3141,14 @@ export class SimplifyQaAdapter {
|
|
|
3031
3141
|
const total = children.length;
|
|
3032
3142
|
const done = children.filter((c) => c.state !== 'queued' && c.state !== 'running').length;
|
|
3033
3143
|
const suiteNum = Number(execDoc.executionTypeId ?? 0);
|
|
3144
|
+
const startedAt = str(execDoc, 'startTime', 'createdAt');
|
|
3145
|
+
const endedAt = str(execDoc, 'endTime');
|
|
3034
3146
|
return {
|
|
3035
3147
|
runId,
|
|
3036
3148
|
state: mapRunState(str(execDoc, 'status') ?? ''),
|
|
3037
3149
|
...(total > 0 ? { progress: { done, total }, children } : {}),
|
|
3150
|
+
...(startedAt ? { startedAt } : {}),
|
|
3151
|
+
...(endedAt ? { endedAt } : {}),
|
|
3038
3152
|
reportUrl: `${base}/user/reports/reports-page?type=SUITE&parentId=${suiteNum}&id=${runIdNum}&projectId=${projectId}`,
|
|
3039
3153
|
};
|
|
3040
3154
|
}
|
|
@@ -3054,14 +3168,33 @@ export class SimplifyQaAdapter {
|
|
|
3054
3168
|
}
|
|
3055
3169
|
const executionRunId = str(chosen, 'id', '_id') ?? '';
|
|
3056
3170
|
const parentId = Number(chosen?.executionItemId ?? 0);
|
|
3171
|
+
const stepResults = await this.fetchStepResults(runIdNum, executionRunId, parentId, projectId);
|
|
3172
|
+
return {
|
|
3173
|
+
runId,
|
|
3174
|
+
state: mapRunState(str(chosen, 'status') ?? ''),
|
|
3175
|
+
stepResults,
|
|
3176
|
+
reportUrl: `${base}/user/reports/reports-page?type=TESTCASE&parentId=${parentId}&id=${runIdNum}&projectId=${projectId}`,
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
async fetchExecDoc(runIdNum, projectId) {
|
|
3180
|
+
const execBody = {
|
|
3181
|
+
searchFields: [
|
|
3182
|
+
{ column: 'id', value: runIdNum, regEx: false },
|
|
3183
|
+
{ column: 'projectId', value: projectId, regEx: false },
|
|
3184
|
+
{ column: 'deleted', value: false },
|
|
3185
|
+
],
|
|
3186
|
+
startIndex: 0,
|
|
3187
|
+
limit: 1,
|
|
3188
|
+
};
|
|
3189
|
+
const execRaw = await this.http.postJson('/exec/execution/search', execBody);
|
|
3190
|
+
return pickArray(execRaw, 'data', 'content', 'executions')[0];
|
|
3191
|
+
}
|
|
3192
|
+
async fetchStepResults(executionId, executionRunId, parentId, projectId) {
|
|
3057
3193
|
const stepsResp = await this.http.postJson('/reports/execution/getstepResult', {
|
|
3058
|
-
executionId
|
|
3059
|
-
executionRunId,
|
|
3060
|
-
parentId,
|
|
3061
|
-
projectId,
|
|
3194
|
+
executionId, executionRunId, parentId, projectId,
|
|
3062
3195
|
});
|
|
3063
3196
|
const rawSteps = Array.isArray(stepsResp?.data) ? stepsResp.data : [];
|
|
3064
|
-
|
|
3197
|
+
return rawSteps.map((s) => {
|
|
3065
3198
|
const step = {
|
|
3066
3199
|
n: typeof s?.seq === 'number' ? s.seq : 0,
|
|
3067
3200
|
description: String(s?.description ?? ''),
|
|
@@ -3091,11 +3224,44 @@ export class SimplifyQaAdapter {
|
|
|
3091
3224
|
}
|
|
3092
3225
|
return step;
|
|
3093
3226
|
});
|
|
3227
|
+
}
|
|
3228
|
+
async getSuiteChildRun(suiteRunId, child) {
|
|
3229
|
+
const projectId = this.requireProjectId();
|
|
3230
|
+
const runIdNum = Number.parseInt(numericId(suiteRunId), 10);
|
|
3231
|
+
const execDoc = await this.fetchExecDoc(runIdNum, projectId);
|
|
3232
|
+
if (!execDoc)
|
|
3233
|
+
throw new Error(`Suite run ${suiteRunId} not found.`);
|
|
3234
|
+
const suiteNum = Number(execDoc.executionTypeId ?? 0);
|
|
3235
|
+
const body = {
|
|
3236
|
+
type: 'SU',
|
|
3237
|
+
projectId,
|
|
3238
|
+
parentId: suiteNum,
|
|
3239
|
+
executionId: runIdNum,
|
|
3240
|
+
tcId: Number.parseInt(numericId(child.testcaseId), 10),
|
|
3241
|
+
...(child.versionId ? { tcVersionId: child.versionId } : {}),
|
|
3242
|
+
...(child.stepId ? { stepId: child.stepId } : {}),
|
|
3243
|
+
};
|
|
3244
|
+
const raw = await this.http.postJson('/reports/execution/getExecutionRuns', body);
|
|
3245
|
+
const runs = Array.isArray(raw?.data) ? raw.data : [];
|
|
3246
|
+
const base = (this.cfg.baseUrl ?? '').replace(/\/+$/, '');
|
|
3247
|
+
if (!runs.length) {
|
|
3248
|
+
return { runId: suiteRunId, state: 'queued' };
|
|
3249
|
+
}
|
|
3250
|
+
let chosen = runs[0];
|
|
3251
|
+
for (const r of runs) {
|
|
3252
|
+
if (typeof r?.status === 'string' && r.status.toUpperCase() === 'FAILED') {
|
|
3253
|
+
chosen = r;
|
|
3254
|
+
break;
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
const executionRunId = str(chosen, 'id', '_id') ?? '';
|
|
3258
|
+
const parentId = Number(chosen?.executionItemId ?? 0);
|
|
3259
|
+
const stepResults = await this.fetchStepResults(runIdNum, executionRunId, parentId, projectId);
|
|
3094
3260
|
return {
|
|
3095
|
-
runId,
|
|
3261
|
+
runId: suiteRunId,
|
|
3096
3262
|
state: mapRunState(str(chosen, 'status') ?? ''),
|
|
3097
3263
|
stepResults,
|
|
3098
|
-
reportUrl: `${base}/user/reports/reports-page?type=TESTCASE&parentId=${parentId}&id=${
|
|
3264
|
+
reportUrl: `${base}/user/reports/reports-page?type=TESTCASE&parentId=${parentId}&id=${runIdNum}&projectId=${projectId}`,
|
|
3099
3265
|
};
|
|
3100
3266
|
}
|
|
3101
3267
|
/** Most recent execution runId for a SUITE — same `/exec/execution/search` the /reports UI's
|