@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testcollab/cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Command-line interface for TestCollab operations",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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