@testcollab/cli 1.6.0 → 1.7.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 +14 -0
- package/package.json +1 -1
- package/src/commands/report.js +58 -4
- package/src/index.js +1 -0
package/README.md
CHANGED
|
@@ -85,6 +85,20 @@ tc report \
|
|
|
85
85
|
| `--result-file <path>` | Yes | Path to the result file |
|
|
86
86
|
| `--api-key <key>` | No | TestCollab API key (or set `TESTCOLLAB_TOKEN` env var) |
|
|
87
87
|
| `--api-url <url>` | No | API base URL override (default: `https://api.testcollab.io`). Use `https://api-eu.testcollab.io` for EU region. |
|
|
88
|
+
| `--skip-missing` | No | Mark test cases in the test plan but not in the result file as **skipped** |
|
|
89
|
+
|
|
90
|
+
#### `--skip-missing`
|
|
91
|
+
|
|
92
|
+
By default, test cases in the test plan that don't appear in the result file are left untouched. When `--skip-missing` is passed, these unmatched cases are automatically marked as **skipped**. This is useful when your result file only contains the tests that actually ran, and you want the full test plan status to reflect that anything not executed was skipped.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
tc report \
|
|
96
|
+
--project 123 \
|
|
97
|
+
--test-plan-id 555 \
|
|
98
|
+
--format junit \
|
|
99
|
+
--result-file ./results.xml \
|
|
100
|
+
--skip-missing
|
|
101
|
+
```
|
|
88
102
|
|
|
89
103
|
#### Mapping test cases
|
|
90
104
|
|
package/package.json
CHANGED
package/src/commands/report.js
CHANGED
|
@@ -834,7 +834,8 @@ async function uploadUsingReporterFlow({
|
|
|
834
834
|
apiUrl,
|
|
835
835
|
hasConfig,
|
|
836
836
|
resultsToUpload,
|
|
837
|
-
unresolvedIds
|
|
837
|
+
unresolvedIds,
|
|
838
|
+
skipMissing = false
|
|
838
839
|
}) {
|
|
839
840
|
const tcApiInstance = new TcApiClient({
|
|
840
841
|
accessToken: apiKey,
|
|
@@ -883,9 +884,11 @@ async function uploadUsingReporterFlow({
|
|
|
883
884
|
|
|
884
885
|
const unmatchedCaseIds = new Set();
|
|
885
886
|
const unmatchedConfigIds = new Set();
|
|
887
|
+
const matchedExecCaseIds = new Set();
|
|
886
888
|
let matched = 0;
|
|
887
889
|
let updated = 0;
|
|
888
890
|
let errors = 0;
|
|
891
|
+
let skippedMissing = 0;
|
|
889
892
|
|
|
890
893
|
const configIds = Object.keys(resultsToUpload);
|
|
891
894
|
|
|
@@ -916,6 +919,7 @@ async function uploadUsingReporterFlow({
|
|
|
916
919
|
}
|
|
917
920
|
|
|
918
921
|
matched += 1;
|
|
922
|
+
matchedExecCaseIds.add(execCase.id);
|
|
919
923
|
|
|
920
924
|
const updatePayload = buildUpdatePayload({
|
|
921
925
|
execCase,
|
|
@@ -955,10 +959,54 @@ async function uploadUsingReporterFlow({
|
|
|
955
959
|
}
|
|
956
960
|
}
|
|
957
961
|
|
|
962
|
+
// --skip-missing: mark all assigned cases not present in the result file as skipped
|
|
963
|
+
if (skipMissing) {
|
|
964
|
+
const missingCases = casesAssigned.filter(
|
|
965
|
+
(assignedCase) => assignedCase && assignedCase.id && !matchedExecCaseIds.has(assignedCase.id)
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
if (missingCases.length) {
|
|
969
|
+
console.log(`\n⏭️ --skip-missing: marking ${missingCases.length} unmatched test case(s) as skipped...`);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
for (const execCase of missingCases) {
|
|
973
|
+
try {
|
|
974
|
+
const skipPayload = {
|
|
975
|
+
id: execCase.id,
|
|
976
|
+
test_plan_test_case: execCase.test_plan_test_case?.id || execCase.test_plan_test_case,
|
|
977
|
+
project: projectId,
|
|
978
|
+
status: RUN_RESULT_MAP.skip,
|
|
979
|
+
test_plan: testPlanId
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
if (execCase.test_plan_config && execCase.test_plan_config.id) {
|
|
983
|
+
skipPayload.test_plan_config = execCase.test_plan_config.id;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
if (Array.isArray(execCase?.test_case_revision?.steps) && execCase.test_case_revision.steps.length) {
|
|
987
|
+
skipPayload.step_wise_result = execCase.test_case_revision.steps.map((step) => ({
|
|
988
|
+
...step,
|
|
989
|
+
status: RUN_RESULT_MAP.skip
|
|
990
|
+
}));
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
const updateResult = await tcApiInstance.updateCaseRunResult(execCase.id, skipPayload);
|
|
994
|
+
if (updateResult && updateResult.id) {
|
|
995
|
+
skippedMissing += 1;
|
|
996
|
+
} else {
|
|
997
|
+
errors += 1;
|
|
998
|
+
}
|
|
999
|
+
} catch {
|
|
1000
|
+
errors += 1;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
958
1005
|
return {
|
|
959
1006
|
matched,
|
|
960
1007
|
updated,
|
|
961
1008
|
errors,
|
|
1009
|
+
skippedMissing,
|
|
962
1010
|
unresolvedIds: unique(unresolvedIds || []),
|
|
963
1011
|
unmatchedCaseIds: unique([...unmatchedCaseIds]),
|
|
964
1012
|
unmatchedConfigIds: unique([...unmatchedConfigIds])
|
|
@@ -1001,6 +1049,9 @@ function validateRequiredOptions({ apiKey, project, testPlanId }) {
|
|
|
1001
1049
|
function logUploadSummary(formatLabel, summary) {
|
|
1002
1050
|
console.log(`✅ ${formatLabel} report processed (${summary.matched || 0} matched, ${summary.updated || 0} updated)`);
|
|
1003
1051
|
|
|
1052
|
+
if (summary.skippedMissing) {
|
|
1053
|
+
console.log(`⏭️ ${summary.skippedMissing} test case(s) not in result file marked as skipped`);
|
|
1054
|
+
}
|
|
1004
1055
|
if (summary.unresolvedIds?.length) {
|
|
1005
1056
|
console.warn(`⚠️ ${summary.unresolvedIds.length} testcase(s) missing TestCollab ID`);
|
|
1006
1057
|
}
|
|
@@ -1029,7 +1080,8 @@ export async function report(options) {
|
|
|
1029
1080
|
testPlanId,
|
|
1030
1081
|
format,
|
|
1031
1082
|
resultFile,
|
|
1032
|
-
apiUrl
|
|
1083
|
+
apiUrl,
|
|
1084
|
+
skipMissing
|
|
1033
1085
|
} = options;
|
|
1034
1086
|
|
|
1035
1087
|
// Resolve API key: --api-key flag takes precedence, then TESTCOLLAB_TOKEN env var
|
|
@@ -1076,7 +1128,8 @@ export async function report(options) {
|
|
|
1076
1128
|
apiUrl,
|
|
1077
1129
|
hasConfig: parsedReport.hasConfig,
|
|
1078
1130
|
resultsToUpload: parsedReport.resultsToUpload,
|
|
1079
|
-
unresolvedIds: parsedReport.unresolvedIds
|
|
1131
|
+
unresolvedIds: parsedReport.unresolvedIds,
|
|
1132
|
+
skipMissing: Boolean(skipMissing)
|
|
1080
1133
|
});
|
|
1081
1134
|
|
|
1082
1135
|
logUploadSummary('JUnit', summary);
|
|
@@ -1105,7 +1158,8 @@ export async function report(options) {
|
|
|
1105
1158
|
apiUrl,
|
|
1106
1159
|
hasConfig: parsedReport.hasConfig,
|
|
1107
1160
|
resultsToUpload: parsedReport.resultsToUpload,
|
|
1108
|
-
unresolvedIds: parsedReport.unresolvedIds
|
|
1161
|
+
unresolvedIds: parsedReport.unresolvedIds,
|
|
1162
|
+
skipMissing: Boolean(skipMissing)
|
|
1109
1163
|
});
|
|
1110
1164
|
|
|
1111
1165
|
logUploadSummary('Mochawesome', summary);
|
package/src/index.js
CHANGED
|
@@ -51,6 +51,7 @@ program
|
|
|
51
51
|
.requiredOption('--format <type>', 'Result format: mochawesome or junit')
|
|
52
52
|
.requiredOption('--result-file <path>', 'Path to test result file')
|
|
53
53
|
.option('--api-url <url>', 'TestCollab API base URL override', 'https://api.testcollab.io')
|
|
54
|
+
.option('--skip-missing', 'Mark test cases in the test plan but not in the result file as skipped', false)
|
|
54
55
|
.action(report);
|
|
55
56
|
|
|
56
57
|
// Add specgen command
|