ado-sync 0.1.24 → 0.1.27

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 (46) hide show
  1. package/README.md +240 -678
  2. package/dist/azure/client.d.ts +3 -0
  3. package/dist/azure/client.js +6 -0
  4. package/dist/azure/client.js.map +1 -1
  5. package/dist/azure/test-cases.d.ts +8 -3
  6. package/dist/azure/test-cases.js +406 -25
  7. package/dist/azure/test-cases.js.map +1 -1
  8. package/dist/cli.js +51 -4
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.js +111 -5
  11. package/dist/config.js.map +1 -1
  12. package/dist/parsers/csharp.d.ts +30 -0
  13. package/dist/parsers/csharp.js +257 -0
  14. package/dist/parsers/csharp.js.map +1 -0
  15. package/dist/parsers/gherkin.d.ts +4 -1
  16. package/dist/parsers/gherkin.js +19 -4
  17. package/dist/parsers/gherkin.js.map +1 -1
  18. package/dist/parsers/java.d.ts +40 -0
  19. package/dist/parsers/java.js +329 -0
  20. package/dist/parsers/java.js.map +1 -0
  21. package/dist/parsers/javascript.d.ts +33 -0
  22. package/dist/parsers/javascript.js +261 -0
  23. package/dist/parsers/javascript.js.map +1 -0
  24. package/dist/parsers/markdown.d.ts +4 -1
  25. package/dist/parsers/markdown.js +5 -3
  26. package/dist/parsers/markdown.js.map +1 -1
  27. package/dist/parsers/python.d.ts +34 -0
  28. package/dist/parsers/python.js +305 -0
  29. package/dist/parsers/python.js.map +1 -0
  30. package/dist/parsers/shared.d.ts +18 -0
  31. package/dist/parsers/shared.js +40 -0
  32. package/dist/parsers/shared.js.map +1 -1
  33. package/dist/sync/engine.js +114 -5
  34. package/dist/sync/engine.js.map +1 -1
  35. package/dist/sync/publish-results.d.ts +49 -0
  36. package/dist/sync/publish-results.js +476 -0
  37. package/dist/sync/publish-results.js.map +1 -0
  38. package/dist/sync/writeback.d.ts +57 -1
  39. package/dist/sync/writeback.js +243 -0
  40. package/dist/sync/writeback.js.map +1 -1
  41. package/dist/types.d.ts +159 -2
  42. package/docs/advanced.md +350 -0
  43. package/docs/configuration.md +293 -0
  44. package/docs/publish-test-results.md +317 -0
  45. package/docs/spec-formats.md +595 -0
  46. package/package.json +1 -1
@@ -0,0 +1,317 @@
1
+ # publish-test-results
2
+
3
+ Parses test result files (TRX, NUnit XML, JUnit, Cucumber JSON) and publishes them to an Azure DevOps Test Run, linking results back to Test Cases either directly by TC ID (when available in the result file) or by `AutomatedTestName` matching.
4
+
5
+ ---
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ ado-sync publish-test-results \
11
+ --testResult results/test-results.trx \
12
+ --runName "CI run #42"
13
+
14
+ # Multiple result files
15
+ ado-sync publish-test-results \
16
+ --testResult results/unit.trx \
17
+ --testResult results/integration.xml \
18
+ --testResultFormat junit
19
+
20
+ # Dry run — parse and summarise without publishing
21
+ ado-sync publish-test-results --testResult results/test.trx --dry-run
22
+
23
+ # Associate with a build
24
+ ado-sync publish-test-results \
25
+ --testResult results/test.trx \
26
+ --buildId 12345
27
+ ```
28
+
29
+ ### Options
30
+
31
+ | Option | Description |
32
+ |--------|-------------|
33
+ | `--testResult <path>` | Path to a result file. Repeatable. |
34
+ | `--testResultFormat <format>` | `trx` · `nunitXml` · `junit` · `cucumberJson`. Auto-detected from file extension/content when omitted. |
35
+ | `--runName <name>` | Name for the Test Run in Azure DevOps. Defaults to `ado-sync <ISO timestamp>`. |
36
+ | `--buildId <id>` | Build ID to associate with the Test Run. |
37
+ | `--dry-run` | Parse results and print summary without creating a run in Azure. |
38
+ | `--config-override` | Override config values (repeatable, same as other commands). |
39
+
40
+ ---
41
+
42
+ ## Supported formats
43
+
44
+ | Format | Extension | Auto-detected | TC ID in file? |
45
+ |--------|-----------|---------------|----------------|
46
+ | TRX (MSTest / VSTest) | `.trx` | Yes (`<TestRun>` root) | Yes — via `[TestProperty("tc","ID")]` in `TestDefinitions` |
47
+ | NUnit XML (native) | `.xml` | Yes (`<test-run>` root) | Yes — via `[Property("tc","ID")]` on each `test-case` |
48
+ | JUnit XML | `.xml` | Yes (`<testsuites>` / `<testsuite>` root) | Optional — via `<property name="tc" value="ID"/>` (see below) |
49
+ | Cucumber JSON | `.json` | Yes (JSON array) | Yes — via `@tc:ID` tag on scenario |
50
+
51
+ > **NUnit via TRX**: when NUnit tests are run through the VSTest adapter (`--logger trx`), `[Property]` values are **not** included in the TRX output. Use `--logger "nunit3;LogFileName=results.xml"` to get the native NUnit XML format, which does include property values.
52
+
53
+ ### How TC linking works
54
+
55
+ Results are linked to Azure Test Cases in priority order:
56
+
57
+ 1. **TC ID from file** (preferred) — when the result file contains a TC ID (`[TestProperty]`, `[Property]`, `<property name="tc">`, or `@tc:` tag), the result is posted with `testCase.id` set directly. This is robust to class/method renames.
58
+ 2. **AutomatedTestName matching** (fallback) — when no TC ID is found, the result is posted with `automatedTestName` = the fully-qualified method name. Azure DevOps links it to a TC whose `AutomatedTestName` field matches. Requires `sync.markAutomated: true` on push.
59
+
60
+ ---
61
+
62
+ ## Per-framework guide
63
+
64
+ ### C# MSTest
65
+
66
+ ```bash
67
+ dotnet test --logger "trx;LogFileName=results.trx"
68
+ ado-sync publish-test-results --testResult results/results.trx
69
+ ```
70
+
71
+ TC IDs are read from `[TestProperty("tc","ID")]` embedded in the TRX — no extra config needed.
72
+
73
+ ---
74
+
75
+ ### C# NUnit
76
+
77
+ ```bash
78
+ # Use native NUnit XML (includes [Property] values)
79
+ dotnet test --logger "nunit3;LogFileName=results.xml"
80
+ ado-sync publish-test-results --testResult results/results.xml
81
+
82
+ # TRX via VSTest adapter (TC IDs NOT included — uses AutomatedTestName matching)
83
+ dotnet test --logger "trx;LogFileName=results.trx"
84
+ ado-sync publish-test-results --testResult results/results.trx
85
+ ```
86
+
87
+ ---
88
+
89
+ ### Java JUnit 4 / JUnit 5 (Maven Surefire)
90
+
91
+ Maven Surefire generates JUnit XML with `classname` = FQCN and `name` = method name. ado-sync builds `automatedTestName` as `FQCN.methodName` on push, which matches the `classname.name` format in the JUnit XML automatically.
92
+
93
+ ```bash
94
+ # Run tests (Surefire writes target/surefire-reports/TEST-*.xml)
95
+ mvn test
96
+
97
+ # Publish — TC linking uses AutomatedTestName matching
98
+ ado-sync publish-test-results \
99
+ --testResult "target/surefire-reports/TEST-*.xml" \
100
+ --testResultFormat junit
101
+ ```
102
+
103
+ Recommended config:
104
+ ```json
105
+ { "sync": { "markAutomated": true } }
106
+ ```
107
+
108
+ **Optional — write TC IDs into JUnit XML** for direct linking (more reliable):
109
+
110
+ Add a JUnit 5 extension or JUnit 4 rule that reads the `@Tag("tc:ID")` / `// @tc:ID` value and calls `recordProperty` to embed it into the XML. With Surefire, test properties are written as `<property>` elements inside each `<testcase>`.
111
+
112
+ ---
113
+
114
+ ### Java TestNG
115
+
116
+ TestNG's Surefire reporter generates the same JUnit XML format. Same commands as JUnit above.
117
+
118
+ ```bash
119
+ mvn test # or: ./gradlew test
120
+
121
+ ado-sync publish-test-results \
122
+ --testResult "target/surefire-reports/TEST-*.xml" \
123
+ --testResultFormat junit
124
+ ```
125
+
126
+ ---
127
+
128
+ ### Python pytest
129
+
130
+ ```bash
131
+ # Run tests and generate JUnit XML
132
+ pytest --junitxml=results/junit.xml
133
+
134
+ # Publish — uses AutomatedTestName matching (classname.name from JUnit XML)
135
+ ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
136
+ ```
137
+
138
+ Recommended config:
139
+ ```json
140
+ { "sync": { "markAutomated": true } }
141
+ ```
142
+
143
+ **Optional — embed TC IDs into JUnit XML** for direct linking.
144
+
145
+ Add the following to your `conftest.py`:
146
+
147
+ ```python
148
+ # conftest.py
149
+ def pytest_runtest_makereport(item, call):
150
+ """Write @pytest.mark.tc(N) as a JUnit XML property for ado-sync to pick up."""
151
+ for marker in item.iter_markers("tc"):
152
+ if marker.args:
153
+ item.user_properties.append(("tc", str(marker.args[0])))
154
+ ```
155
+
156
+ With this hook, pytest writes:
157
+ ```xml
158
+ <testcase name="test_foo" classname="tests.module.TestClass">
159
+ <properties>
160
+ <property name="tc" value="1041"/>
161
+ </properties>
162
+ </testcase>
163
+ ```
164
+
165
+ ado-sync will extract the `tc` property and link the result directly to TC 1041, without needing AutomatedTestName matching.
166
+
167
+ ---
168
+
169
+ ### JavaScript / TypeScript — Jest
170
+
171
+ Install `jest-junit`:
172
+ ```bash
173
+ npm install --save-dev jest-junit
174
+ ```
175
+
176
+ Run tests:
177
+ ```bash
178
+ JEST_JUNIT_OUTPUT_DIR=results JEST_JUNIT_OUTPUT_NAME=junit.xml \
179
+ npx jest --reporters=default --reporters=jest-junit
180
+ ```
181
+
182
+ Publish:
183
+ ```bash
184
+ ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
185
+ ```
186
+
187
+ > **TC linking for Jest**: jest-junit does not embed TC IDs in the XML. Linking uses `AutomatedTestName` matching. Set `sync.markAutomated: true` and ensure the `JEST_JUNIT_CLASSNAME` and `JEST_JUNIT_TITLE` env vars match the `automatedTestName` format stored in the TC (`{fileBasename} > {describe} > {testTitle}`).
188
+ >
189
+ > Set these env vars to align the format:
190
+ > ```
191
+ > JEST_JUNIT_CLASSNAME="{classname}" # default: suite hierarchy
192
+ > JEST_JUNIT_TITLE="{title}" # default: test title
193
+ > ```
194
+
195
+ ---
196
+
197
+ ### JavaScript / TypeScript — WebdriverIO
198
+
199
+ WebdriverIO supports JUnit XML via `@wdio/junit-reporter`:
200
+
201
+ ```bash
202
+ # Install (if not already present)
203
+ npm install --save-dev @wdio/junit-reporter
204
+ ```
205
+
206
+ Add to `wdio.conf.ts`:
207
+ ```typescript
208
+ reporters: [['junit', { outputDir: './results', outputFileFormat: () => 'junit.xml' }]]
209
+ ```
210
+
211
+ Run tests:
212
+ ```bash
213
+ npx wdio run wdio.conf.ts
214
+ ```
215
+
216
+ Publish:
217
+ ```bash
218
+ ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
219
+ ```
220
+
221
+ ---
222
+
223
+ ### Gherkin / Cucumber (JS)
224
+
225
+ ```bash
226
+ # Run with Cucumber JSON reporter
227
+ npx cucumber-js --format json:results/cucumber.json
228
+
229
+ # Publish — TC IDs are read from @tc:ID tags on each scenario
230
+ ado-sync publish-test-results --testResult results/cucumber.json
231
+ ```
232
+
233
+ TC IDs from `@tc:12345` tags on scenarios are extracted directly from the JSON — no AutomatedTestName matching needed.
234
+
235
+ ---
236
+
237
+ | Framework | Result format | TC ID in file | Fallback |
238
+ |-----------|---------------|---------------|---------|
239
+ | C# MSTest | TRX | ✅ `[TestProperty("tc","ID")]` | — |
240
+ | C# NUnit | NUnit XML | ✅ `[Property("tc","ID")]` | — |
241
+ | Java JUnit 4/5 | JUnit XML | ⚠️ optional (see above) | AutomatedTestName |
242
+ | Java TestNG | JUnit XML | ⚠️ optional (see above) | AutomatedTestName |
243
+ | Python pytest | JUnit XML | ⚠️ optional (conftest.py) | AutomatedTestName |
244
+ | Jest | JUnit XML | ❌ | AutomatedTestName |
245
+ | WebdriverIO | JUnit XML | ❌ | AutomatedTestName |
246
+ | Cucumber JS | Cucumber JSON | ✅ `@tc:ID` tag | — |
247
+
248
+ ---
249
+
250
+ ## Outcome mapping
251
+
252
+ | Source outcome | Azure outcome |
253
+ |----------------|---------------|
254
+ | `passed` / `pass` / `success` | `Passed` |
255
+ | `failed` / `fail` / `failure` / `error` | `Failed` |
256
+ | `skipped` / `ignored` / `pending` / `notExecuted` | `NotExecuted` |
257
+ | `inconclusive` | `Inconclusive` (or override with `treatInconclusiveAs`) |
258
+
259
+ ---
260
+
261
+ ## Configuration
262
+
263
+ Results can also be configured in the config file under `publishTestResults`:
264
+
265
+ ```json
266
+ {
267
+ "publishTestResults": {
268
+ "testResult": {
269
+ "sources": [
270
+ { "value": "results/unit.trx", "format": "trx" },
271
+ { "value": "results/integration.xml", "format": "junit" }
272
+ ]
273
+ },
274
+ "treatInconclusiveAs": "Failed",
275
+ "testRunSettings": {
276
+ "name": "My CI Run",
277
+ "comment": "Automated sync run",
278
+ "runType": "Automated"
279
+ },
280
+ "testResultSettings": {
281
+ "comment": "Published by ado-sync"
282
+ },
283
+ "testConfiguration": {
284
+ "name": "Default"
285
+ }
286
+ }
287
+ }
288
+ ```
289
+
290
+ ### `publishTestResults` fields
291
+
292
+ | Field | Description |
293
+ |-------|-------------|
294
+ | `testResult.sources` | Array of `{ value, format }` objects. `value` is a path relative to config dir. |
295
+ | `treatInconclusiveAs` | Override inconclusive outcome. e.g. `"Failed"` or `"NotExecuted"`. |
296
+ | `flakyTestOutcome` | How to handle flaky tests: `"lastAttemptOutcome"` *(default)* · `"firstAttemptOutcome"` · `"worstOutcome"`. |
297
+ | `testConfiguration.name` | Name of the Azure test configuration to associate. |
298
+ | `testConfiguration.id` | ID of the Azure test configuration. |
299
+ | `testRunSettings.name` | Name for the Test Run. |
300
+ | `testRunSettings.comment` | Comment attached to the Test Run. |
301
+ | `testRunSettings.runType` | `"Automated"` *(default)* · `"Manual"`. |
302
+ | `testResultSettings.comment` | Comment applied to every test result. |
303
+ | `publishAttachmentsForPassingTests` | `"none"` *(default)* · `"files"` · `"all"`. |
304
+
305
+ ---
306
+
307
+ ## Output
308
+
309
+ ```
310
+ ado-sync publish-test-results
311
+ Config: ado-sync.json
312
+
313
+ Total results: 42
314
+ 38 passed 3 failed 1 other
315
+ Run ID: 9876
316
+ URL: https://dev.azure.com/my-org/MyProject/_testManagement/runs?runId=9876
317
+ ```