ado-sync 0.1.29 → 0.1.31
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 +469 -28
- package/dist/ai/summarizer.d.ts +73 -0
- package/dist/ai/summarizer.js +789 -0
- package/dist/ai/summarizer.js.map +1 -0
- package/dist/azure/test-cases.js +17 -2
- package/dist/azure/test-cases.js.map +1 -1
- package/dist/cli.js +29 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/parsers/csv.d.ts +13 -3
- package/dist/parsers/csv.js +87 -9
- package/dist/parsers/csv.js.map +1 -1
- package/dist/parsers/dart.d.ts +31 -0
- package/dist/parsers/dart.js +245 -0
- package/dist/parsers/dart.js.map +1 -0
- package/dist/parsers/excel.js +47 -8
- package/dist/parsers/excel.js.map +1 -1
- package/dist/parsers/javascript.d.ts +17 -7
- package/dist/parsers/javascript.js +20 -9
- package/dist/parsers/javascript.js.map +1 -1
- package/dist/parsers/swift.d.ts +34 -0
- package/dist/parsers/swift.js +250 -0
- package/dist/parsers/swift.js.map +1 -0
- package/dist/parsers/testcafe.d.ts +32 -0
- package/dist/parsers/testcafe.js +231 -0
- package/dist/parsers/testcafe.js.map +1 -0
- package/dist/sync/engine.d.ts +4 -1
- package/dist/sync/engine.js +48 -2
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/writeback.d.ts +6 -7
- package/dist/sync/writeback.js +22 -7
- package/dist/sync/writeback.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/docs/advanced.md +140 -0
- package/docs/configuration.md +1 -1
- package/docs/publish-test-results.md +48 -16
- package/docs/spec-formats.md +549 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -12,6 +12,15 @@ Supports a wide range of test file formats and frameworks:
|
|
|
12
12
|
| `java` | JUnit 4, JUnit 5, TestNG + Selenium | `.java` |
|
|
13
13
|
| `python` | pytest + Selenium | `.py` |
|
|
14
14
|
| `javascript` | Jest, Jasmine, WebdriverIO | `.js` / `.ts` |
|
|
15
|
+
| `playwright` | Playwright Test | `.js` / `.ts` |
|
|
16
|
+
| `puppeteer` | Puppeteer + Jest or Mocha | `.js` / `.ts` |
|
|
17
|
+
| `cypress` | Cypress | `.cy.js` / `.cy.ts` |
|
|
18
|
+
| `testcafe` | TestCafe | `.js` / `.ts` |
|
|
19
|
+
| `detox` | Detox (React Native E2E) | `.js` / `.ts` |
|
|
20
|
+
| `espresso` | Android Espresso (JUnit 4 / Kotlin) | `.java` / `.kt` |
|
|
21
|
+
| `xcuitest` | iOS / macOS XCUITest | `.swift` |
|
|
22
|
+
| `flutter` | Flutter widget & integration tests | `_test.dart` |
|
|
23
|
+
| Appium | Use `javascript` / `java` / `python` / `csharp` | depends on language binding |
|
|
15
24
|
| `csv` | Azure DevOps tabular export | `.csv` |
|
|
16
25
|
| `excel` | Azure DevOps tabular export | `.xlsx` |
|
|
17
26
|
|
|
@@ -51,50 +60,73 @@ On the **first push**, a new Test Case is created in Azure DevOps and its ID is
|
|
|
51
60
|
| Java JUnit 5 | `@Tag("tc:12345")` above `@Test` |
|
|
52
61
|
| Python pytest | `@pytest.mark.tc(12345)` above `def test_*` |
|
|
53
62
|
| JavaScript/TS (Jest/Jasmine/WebdriverIO) | `// @tc:12345` comment above `it()`/`test()` |
|
|
63
|
+
| Playwright | `// @tc:12345` comment above `test()` |
|
|
64
|
+
| Puppeteer | `// @tc:12345` comment above `it()`/`test()` |
|
|
65
|
+
| Cypress | `// @tc:12345` comment above `it()`/`specify()` |
|
|
66
|
+
| TestCafe | `// @tc:12345` comment above `test()` |
|
|
67
|
+
| Detox | `// @tc:12345` comment above `it()`/`test()` |
|
|
68
|
+
| Espresso (Java/Kotlin) | `// @tc:12345` comment above `@Test` |
|
|
69
|
+
| XCUITest (Swift) | `// @tc:12345` comment above `func test*()` |
|
|
70
|
+
| Flutter (Dart) | `// @tc:12345` comment above `testWidgets()`/`test()` |
|
|
54
71
|
| CSV | Numeric ID in column A |
|
|
55
72
|
| Excel | Numeric ID in cell A |
|
|
56
73
|
|
|
57
74
|
---
|
|
58
75
|
|
|
59
|
-
##
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
**Step 1 — Install**
|
|
60
79
|
|
|
61
80
|
```bash
|
|
62
81
|
npm install -g ado-sync
|
|
63
|
-
# or run without installing
|
|
82
|
+
# or run once without installing
|
|
64
83
|
npx ado-sync --help
|
|
65
84
|
```
|
|
66
85
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
## Quick start
|
|
86
|
+
**Step 2 — Generate a config file**
|
|
70
87
|
|
|
71
88
|
```bash
|
|
72
|
-
# 1. Generate a config file
|
|
73
89
|
ado-sync init # creates ado-sync.json
|
|
74
|
-
ado-sync init ado-sync.yml # YAML format
|
|
75
|
-
|
|
76
|
-
# 2. Edit the config with your org, project, plan ID, and token
|
|
77
|
-
export AZURE_DEVOPS_TOKEN=your_personal_access_token
|
|
78
|
-
|
|
79
|
-
# 3. Preview what will be created
|
|
80
|
-
ado-sync push --dry-run
|
|
81
|
-
|
|
82
|
-
# 4. Push to Azure DevOps
|
|
83
|
-
ado-sync push
|
|
90
|
+
ado-sync init ado-sync.yml # YAML format if you prefer
|
|
84
91
|
```
|
|
85
92
|
|
|
86
|
-
|
|
93
|
+
**Step 3 — Fill in your details**
|
|
94
|
+
|
|
95
|
+
Open `ado-sync.json` and replace the placeholders:
|
|
87
96
|
|
|
88
97
|
```json
|
|
89
98
|
{
|
|
90
|
-
"orgUrl": "https://dev.azure.com/
|
|
91
|
-
"project": "
|
|
99
|
+
"orgUrl": "https://dev.azure.com/YOUR-ORG",
|
|
100
|
+
"project": "YOUR-PROJECT-NAME",
|
|
92
101
|
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
93
|
-
"testPlan": { "id":
|
|
102
|
+
"testPlan": { "id": 12345 },
|
|
94
103
|
"local": { "type": "gherkin", "include": "specs/**/*.feature" }
|
|
95
104
|
}
|
|
96
105
|
```
|
|
97
106
|
|
|
107
|
+
| Field | Where to find it |
|
|
108
|
+
|-------|-----------------|
|
|
109
|
+
| `orgUrl` | Azure DevOps → top-left org name → `https://dev.azure.com/<org>` |
|
|
110
|
+
| `project` | Azure DevOps → your project name (shown in the breadcrumb) |
|
|
111
|
+
| `testPlan.id` | Test Plans → click your plan → the number in the URL |
|
|
112
|
+
| `AZURE_DEVOPS_TOKEN` | Azure DevOps → User Settings → Personal Access Tokens → New Token (scope: Test Management read/write) |
|
|
113
|
+
|
|
114
|
+
**Step 4 — Set your token**
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
export AZURE_DEVOPS_TOKEN=your_personal_access_token
|
|
118
|
+
# or add it to a .env file in this directory
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Step 5 — Preview then push**
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
ado-sync push --dry-run # preview — no changes made
|
|
125
|
+
ado-sync push # create / update Test Cases in Azure DevOps
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
On the first push, new Test Cases are created and their IDs are written back into your local files (`@tc:12345`). Every subsequent push updates them.
|
|
129
|
+
|
|
98
130
|
---
|
|
99
131
|
|
|
100
132
|
## CLI reference
|
|
@@ -130,6 +162,13 @@ ado-sync push
|
|
|
130
162
|
ado-sync push --dry-run
|
|
131
163
|
ado-sync push --tags "@smoke and not @wip"
|
|
132
164
|
ado-sync push --config-override testPlan.id=9999
|
|
165
|
+
|
|
166
|
+
# AI-generated test steps for code files (Java, C#, Python, JS/TS, Playwright)
|
|
167
|
+
ado-sync push --ai-provider heuristic # fast regex-based (no model needed)
|
|
168
|
+
ado-sync push --ai-provider local --ai-model ~/.cache/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf
|
|
169
|
+
ado-sync push --ai-provider ollama --ai-model qwen2.5-coder:7b
|
|
170
|
+
ado-sync push --ai-provider openai --ai-key $OPENAI_API_KEY
|
|
171
|
+
ado-sync push --ai-provider none # disable AI summary entirely
|
|
133
172
|
```
|
|
134
173
|
|
|
135
174
|
| Scenario state | Action |
|
|
@@ -152,10 +191,99 @@ ado-sync pull --tags "@smoke"
|
|
|
152
191
|
```bash
|
|
153
192
|
ado-sync status
|
|
154
193
|
ado-sync status --tags "@smoke"
|
|
194
|
+
ado-sync status --ai-provider heuristic
|
|
155
195
|
```
|
|
156
196
|
|
|
157
197
|
Compares local specs against Azure DevOps and prints a diff — no changes made.
|
|
158
198
|
|
|
199
|
+
### AI auto-summary
|
|
200
|
+
|
|
201
|
+
For code-based test types (`java`, `csharp`, `python`, `javascript`, `playwright`, `cypress`, `testcafe`, `detox`, `espresso`, `xcuitest`, `flutter`), ado-sync reads your test function bodies and automatically generates a TC **title**, **description**, and **steps** — so you don't need doc comments on every test.
|
|
202
|
+
|
|
203
|
+
> **No setup required to try it.** `ado-sync push` always works, even without a model — it falls back to fast regex-based analysis automatically.
|
|
204
|
+
|
|
205
|
+
#### Choose a provider
|
|
206
|
+
|
|
207
|
+
| Provider | Quality | Setup |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `local` *(default)* | Good–Excellent | Download a GGUF model file (see below) |
|
|
210
|
+
| `heuristic` | Basic | None — works offline, zero dependencies |
|
|
211
|
+
| `ollama` | Good–Excellent | Install [Ollama](https://ollama.com) + `ollama pull qwen2.5-coder:7b` |
|
|
212
|
+
| `openai` | Excellent | `--ai-key $OPENAI_API_KEY` |
|
|
213
|
+
| `anthropic` | Excellent | `--ai-key $ANTHROPIC_API_KEY` |
|
|
214
|
+
|
|
215
|
+
#### Option A — No setup (heuristic, instant)
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
ado-sync push --ai-provider heuristic
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Uses regex pattern matching. No model download, no internet required. Good for CI pipelines.
|
|
222
|
+
|
|
223
|
+
#### Option B — Local LLM (best privacy, no API cost)
|
|
224
|
+
|
|
225
|
+
`node-llama-cpp` is bundled — **no extra install needed**. You only need to download a model file once.
|
|
226
|
+
|
|
227
|
+
**1. Pick a model size**
|
|
228
|
+
|
|
229
|
+
| Model | RAM needed | Quality |
|
|
230
|
+
|-------|-----------|---------|
|
|
231
|
+
| 1.5B Q4_K_M *(start here)* | ~1.1 GB | Good |
|
|
232
|
+
| 7B Q4_K_M | ~4.5 GB | Better |
|
|
233
|
+
| 14B Q4_K_M | ~8.5 GB | Excellent |
|
|
234
|
+
|
|
235
|
+
**2. Download the model**
|
|
236
|
+
|
|
237
|
+
macOS / Linux:
|
|
238
|
+
```bash
|
|
239
|
+
mkdir -p ~/.cache/ado-sync/models
|
|
240
|
+
curl -L -o ~/.cache/ado-sync/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf \
|
|
241
|
+
"https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/resolve/main/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Windows (PowerShell):
|
|
245
|
+
```powershell
|
|
246
|
+
New-Item -ItemType Directory -Force "$env:LOCALAPPDATA\ado-sync\models"
|
|
247
|
+
Invoke-WebRequest `
|
|
248
|
+
-Uri "https://huggingface.co/Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/resolve/main/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf" `
|
|
249
|
+
-OutFile "$env:LOCALAPPDATA\ado-sync\models\qwen2.5-coder-1.5b-instruct-q4_k_m.gguf"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**3. Run push with the model**
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# macOS / Linux
|
|
256
|
+
ado-sync push --ai-model ~/.cache/ado-sync/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf
|
|
257
|
+
|
|
258
|
+
# Windows
|
|
259
|
+
ado-sync push --ai-model "$env:LOCALAPPDATA\ado-sync\models\qwen2.5-coder-1.5b-instruct-q4_k_m.gguf"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Option C — Ollama (model management UI, easy upgrades)
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# 1. Install Ollama from https://ollama.com, then:
|
|
266
|
+
ollama pull qwen2.5-coder:7b
|
|
267
|
+
|
|
268
|
+
# 2. Push using Ollama
|
|
269
|
+
ado-sync push --ai-provider ollama --ai-model qwen2.5-coder:7b
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Option D — Cloud AI (OpenAI / Anthropic)
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
ado-sync push --ai-provider openai --ai-key $OPENAI_API_KEY
|
|
276
|
+
ado-sync push --ai-provider anthropic --ai-key $ANTHROPIC_API_KEY
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### Disable AI entirely
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
ado-sync push --ai-provider none
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
> Tests with existing doc comments (JSDoc / Javadoc / C# XML doc / Python docstring) that already have both steps and a description are **never overwritten**. Local source files are **never modified** by AI summary.
|
|
286
|
+
|
|
159
287
|
### `publish-test-results`
|
|
160
288
|
|
|
161
289
|
```bash
|
|
@@ -195,7 +323,8 @@ ado-sync push --config-override sync.disableLocalChanges=true
|
|
|
195
323
|
| Topic | Link |
|
|
196
324
|
|-------|------|
|
|
197
325
|
| Full configuration reference | [docs/configuration.md](docs/configuration.md) |
|
|
198
|
-
| Spec file formats (Gherkin, Markdown, C# MSTest, CSV, Excel) | [docs/spec-formats.md](docs/spec-formats.md) |
|
|
326
|
+
| Spec file formats (Gherkin, Markdown, C# MSTest, CSV, Excel, JS/TS) | [docs/spec-formats.md](docs/spec-formats.md) |
|
|
327
|
+
| Work Item Links (User Story, Bug, etc.) | [docs/spec-formats.md#work-item-links](docs/spec-formats.md#work-item-links) |
|
|
199
328
|
| Advanced features (format, state, fieldUpdates, customizations, attachments, CI mode) | [docs/advanced.md](docs/advanced.md) |
|
|
200
329
|
| Publishing test results | [docs/publish-test-results.md](docs/publish-test-results.md) |
|
|
201
330
|
|
|
@@ -217,12 +346,12 @@ ado-sync push
|
|
|
217
346
|
ado-sync pull
|
|
218
347
|
```
|
|
219
348
|
|
|
220
|
-
### C# MSTest / NUnit: create TCs, run tests, publish results
|
|
349
|
+
### C# MSTest / NUnit / SpecFlow: create TCs, run tests, publish results
|
|
221
350
|
|
|
222
351
|
```bash
|
|
223
|
-
# 1. Create TCs and write IDs back into .cs files
|
|
352
|
+
# 1. Create TCs and write IDs back into .cs / .feature files
|
|
224
353
|
ado-sync push --dry-run # preview
|
|
225
|
-
ado-sync push # writes [TestProperty("tc","ID")] / [Property("tc","ID")]
|
|
354
|
+
ado-sync push # writes [TestProperty("tc","ID")] / [Property("tc","ID")] / @tc:ID
|
|
226
355
|
|
|
227
356
|
# 2a. MSTest — TRX contains [TestProperty] values; TC IDs extracted automatically
|
|
228
357
|
dotnet test --logger "trx;LogFileName=results.trx"
|
|
@@ -231,6 +360,10 @@ ado-sync publish-test-results --testResult results/results.trx
|
|
|
231
360
|
# 2b. NUnit — use native XML logger so [Property("tc","ID")] values are included
|
|
232
361
|
dotnet test --logger "nunit3;LogFileName=results.xml"
|
|
233
362
|
ado-sync publish-test-results --testResult results/results.xml
|
|
363
|
+
|
|
364
|
+
# 2c. SpecFlow — TRX format (SpecFlow writes @tc:ID into TestProperty automatically)
|
|
365
|
+
dotnet test --logger "trx;LogFileName=results.trx"
|
|
366
|
+
ado-sync publish-test-results --testResult results/results.trx
|
|
234
367
|
```
|
|
235
368
|
|
|
236
369
|
Recommended `ado-sync.json` for C# MSTest:
|
|
@@ -253,6 +386,25 @@ Recommended `ado-sync.json` for C# MSTest:
|
|
|
253
386
|
}
|
|
254
387
|
```
|
|
255
388
|
|
|
389
|
+
Recommended `ado-sync.json` for C# SpecFlow:
|
|
390
|
+
|
|
391
|
+
```json
|
|
392
|
+
{
|
|
393
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
394
|
+
"project": "MyProject",
|
|
395
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
396
|
+
"testPlan": { "id": 1234 },
|
|
397
|
+
"local": {
|
|
398
|
+
"type": "gherkin",
|
|
399
|
+
"include": ["Features/**/*.feature"]
|
|
400
|
+
},
|
|
401
|
+
"sync": {
|
|
402
|
+
"tagPrefix": "tc",
|
|
403
|
+
"markAutomated": true
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
256
408
|
### Java JUnit / TestNG: create TCs and publish results
|
|
257
409
|
|
|
258
410
|
```bash
|
|
@@ -336,6 +488,40 @@ npx jest --reporters=default --reporters=jest-junit
|
|
|
336
488
|
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
337
489
|
```
|
|
338
490
|
|
|
491
|
+
### Playwright: create TCs and publish results with screenshots
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
# 1. Create TCs and write IDs back into .ts spec files
|
|
495
|
+
ado-sync push --dry-run # preview
|
|
496
|
+
ado-sync push # writes // @tc:ID above each test()
|
|
497
|
+
|
|
498
|
+
# 2. Add @tc:ID tag to test title and run Playwright with JSON reporter
|
|
499
|
+
# playwright.config.ts: reporter: [['json', { outputFile: 'results/playwright.json' }]]
|
|
500
|
+
npx playwright test
|
|
501
|
+
|
|
502
|
+
# 3. Publish — TC IDs from @tc:ID in test title; screenshots/videos from attachments[]
|
|
503
|
+
ado-sync publish-test-results --testResult results/playwright.json
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
Recommended `ado-sync.json` for Playwright:
|
|
507
|
+
|
|
508
|
+
```json
|
|
509
|
+
{
|
|
510
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
511
|
+
"project": "MyProject",
|
|
512
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
513
|
+
"testPlan": { "id": 1234 },
|
|
514
|
+
"local": {
|
|
515
|
+
"type": "playwright",
|
|
516
|
+
"include": ["tests/**/*.spec.ts"],
|
|
517
|
+
"exclude": ["**/*.helper.ts"]
|
|
518
|
+
},
|
|
519
|
+
"sync": {
|
|
520
|
+
"markAutomated": true
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
339
525
|
Recommended `ado-sync.json` for Jest/Jasmine/WebdriverIO:
|
|
340
526
|
|
|
341
527
|
```json
|
|
@@ -355,6 +541,138 @@ Recommended `ado-sync.json` for Jest/Jasmine/WebdriverIO:
|
|
|
355
541
|
}
|
|
356
542
|
```
|
|
357
543
|
|
|
544
|
+
### Detox (React Native): create TCs and push
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
# 1. Create TCs and write IDs back into .ts files
|
|
548
|
+
ado-sync push --dry-run # preview
|
|
549
|
+
ado-sync push # writes // @tc:ID above each it() / test()
|
|
550
|
+
|
|
551
|
+
# 2. Run Detox tests (Jest runner)
|
|
552
|
+
npx detox test --configuration ios.sim.release
|
|
553
|
+
|
|
554
|
+
# 3. Publish results (Jest JUnit reporter)
|
|
555
|
+
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
Recommended `ado-sync.json` for Detox:
|
|
559
|
+
|
|
560
|
+
```json
|
|
561
|
+
{
|
|
562
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
563
|
+
"project": "MyProject",
|
|
564
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
565
|
+
"testPlan": { "id": 1234 },
|
|
566
|
+
"local": {
|
|
567
|
+
"type": "detox",
|
|
568
|
+
"include": ["e2e/**/*.test.ts"]
|
|
569
|
+
},
|
|
570
|
+
"sync": { "markAutomated": true }
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Espresso (Android): create TCs and push
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
# 1. Create TCs and write IDs back into .java / .kt files
|
|
578
|
+
ado-sync push --dry-run # preview
|
|
579
|
+
ado-sync push # writes // @tc:ID above @Test
|
|
580
|
+
|
|
581
|
+
# 2. Run instrumented tests and generate JUnit XML
|
|
582
|
+
./gradlew connectedAndroidTest
|
|
583
|
+
# XML output: app/build/outputs/androidTest-results/connected/TEST-*.xml
|
|
584
|
+
|
|
585
|
+
# 3. Publish results
|
|
586
|
+
ado-sync publish-test-results \
|
|
587
|
+
--testResult "app/build/outputs/androidTest-results/connected/TEST-*.xml" \
|
|
588
|
+
--testResultFormat junit
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Recommended `ado-sync.json` for Espresso:
|
|
592
|
+
|
|
593
|
+
```json
|
|
594
|
+
{
|
|
595
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
596
|
+
"project": "MyProject",
|
|
597
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
598
|
+
"testPlan": { "id": 1234 },
|
|
599
|
+
"local": {
|
|
600
|
+
"type": "espresso",
|
|
601
|
+
"include": ["app/src/androidTest/**/*.java", "app/src/androidTest/**/*.kt"],
|
|
602
|
+
"exclude": ["**/*BaseTest.java"]
|
|
603
|
+
},
|
|
604
|
+
"sync": { "markAutomated": true }
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### XCUITest (iOS): create TCs and push
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
# 1. Create TCs and write IDs back into .swift files
|
|
612
|
+
ado-sync push --dry-run # preview
|
|
613
|
+
ado-sync push # writes // @tc:ID above func test*()
|
|
614
|
+
|
|
615
|
+
# 2. Run XCUITest and export JUnit XML
|
|
616
|
+
xcodebuild test \
|
|
617
|
+
-project MyApp.xcodeproj \
|
|
618
|
+
-scheme MyApp \
|
|
619
|
+
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
|
620
|
+
-resultBundlePath TestResults.xcresult
|
|
621
|
+
xcrun xcresulttool get --path TestResults.xcresult --format junit > results/junit.xml
|
|
622
|
+
|
|
623
|
+
# 3. Publish results
|
|
624
|
+
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Recommended `ado-sync.json` for XCUITest:
|
|
628
|
+
|
|
629
|
+
```json
|
|
630
|
+
{
|
|
631
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
632
|
+
"project": "MyProject",
|
|
633
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
634
|
+
"testPlan": { "id": 1234 },
|
|
635
|
+
"local": {
|
|
636
|
+
"type": "xcuitest",
|
|
637
|
+
"include": ["UITests/**/*.swift"],
|
|
638
|
+
"exclude": ["UITests/**/*Helper.swift", "UITests/**/*Base.swift"]
|
|
639
|
+
},
|
|
640
|
+
"sync": { "markAutomated": true }
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Flutter: create TCs and push
|
|
645
|
+
|
|
646
|
+
```bash
|
|
647
|
+
# 1. Create TCs and write IDs back into _test.dart files
|
|
648
|
+
ado-sync push --dry-run # preview
|
|
649
|
+
ado-sync push # writes // @tc:ID above testWidgets() / test()
|
|
650
|
+
|
|
651
|
+
# 2. Run Flutter tests with machine-readable output
|
|
652
|
+
flutter test --machine > results/flutter_test.jsonl
|
|
653
|
+
# Or generate JUnit XML with the flutter_test_junit package:
|
|
654
|
+
flutter test --reporter junit > results/junit.xml
|
|
655
|
+
|
|
656
|
+
# 3. Publish results
|
|
657
|
+
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
Recommended `ado-sync.json` for Flutter:
|
|
661
|
+
|
|
662
|
+
```json
|
|
663
|
+
{
|
|
664
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
665
|
+
"project": "MyProject",
|
|
666
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
667
|
+
"testPlan": { "id": 1234 },
|
|
668
|
+
"local": {
|
|
669
|
+
"type": "flutter",
|
|
670
|
+
"include": ["test/**/*_test.dart", "integration_test/**/*_test.dart"]
|
|
671
|
+
},
|
|
672
|
+
"sync": { "markAutomated": true }
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
358
676
|
### CI pipeline
|
|
359
677
|
|
|
360
678
|
```yaml
|
|
@@ -364,6 +682,11 @@ Recommended `ado-sync.json` for Jest/Jasmine/WebdriverIO:
|
|
|
364
682
|
env:
|
|
365
683
|
AZURE_DEVOPS_TOKEN: ${{ secrets.AZURE_DEVOPS_TOKEN }}
|
|
366
684
|
|
|
685
|
+
- name: Sync test cases to Azure DevOps (with AI summary)
|
|
686
|
+
run: ado-sync push --ai-provider heuristic --config-override sync.disableLocalChanges=true
|
|
687
|
+
env:
|
|
688
|
+
AZURE_DEVOPS_TOKEN: ${{ secrets.AZURE_DEVOPS_TOKEN }}
|
|
689
|
+
|
|
367
690
|
- name: Publish test results
|
|
368
691
|
run: ado-sync publish-test-results --testResult results/test.trx
|
|
369
692
|
env:
|
|
@@ -378,6 +701,109 @@ ado-sync status
|
|
|
378
701
|
|
|
379
702
|
---
|
|
380
703
|
|
|
704
|
+
## Work Item Links
|
|
705
|
+
|
|
706
|
+
Link each Test Case to related Azure DevOps work items (User Stories, Bugs, etc.) automatically on every push.
|
|
707
|
+
|
|
708
|
+
### Configure `sync.links`
|
|
709
|
+
|
|
710
|
+
```json
|
|
711
|
+
{
|
|
712
|
+
"sync": {
|
|
713
|
+
"links": [
|
|
714
|
+
{
|
|
715
|
+
"prefix": "story",
|
|
716
|
+
"relationship": "Microsoft.VSTS.Common.TestedBy-Reverse",
|
|
717
|
+
"workItemType": "User Story"
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
"prefix": "bug",
|
|
721
|
+
"relationship": "System.LinkTypes.Related",
|
|
722
|
+
"workItemType": "Bug"
|
|
723
|
+
}
|
|
724
|
+
]
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
| Field | Description |
|
|
730
|
+
|-------|-------------|
|
|
731
|
+
| `prefix` | The tag prefix used in your spec files (e.g. `story` → `@story:555`) |
|
|
732
|
+
| `relationship` | ADO relation type (see common values below) |
|
|
733
|
+
| `workItemType` | Optional — used in log output only |
|
|
734
|
+
|
|
735
|
+
**Common relationship values:**
|
|
736
|
+
|
|
737
|
+
| Relationship | Meaning |
|
|
738
|
+
|---|---|
|
|
739
|
+
| `Microsoft.VSTS.Common.TestedBy-Reverse` | Test Case "Tested By" ↔ User Story |
|
|
740
|
+
| `System.LinkTypes.Related` | Simple "Related" link |
|
|
741
|
+
| `System.LinkTypes.Dependency-Forward` | "Successor" (this item depends on) |
|
|
742
|
+
| `System.LinkTypes.Hierarchy-Reverse` | "Parent" link |
|
|
743
|
+
|
|
744
|
+
### Tag your tests
|
|
745
|
+
|
|
746
|
+
**Gherkin (`.feature`):**
|
|
747
|
+
```gherkin
|
|
748
|
+
# @story:555 @bug:789
|
|
749
|
+
Scenario: User can log in
|
|
750
|
+
Given I am on the login page
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
**JavaScript / TypeScript (Jest, Playwright, Cypress, TestCafe, Puppeteer):**
|
|
754
|
+
```typescript
|
|
755
|
+
// @story:555
|
|
756
|
+
// @bug:789
|
|
757
|
+
test('user can log in', async ({ page }) => { ... });
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
**Markdown (`.md`):**
|
|
761
|
+
```markdown
|
|
762
|
+
### User can log in @story:555 @bug:789
|
|
763
|
+
|
|
764
|
+
1. Navigate to the login page
|
|
765
|
+
2. Check: Login form is visible
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Python (pytest):**
|
|
769
|
+
```python
|
|
770
|
+
# @story:555 @bug:789
|
|
771
|
+
def test_user_can_log_in():
|
|
772
|
+
...
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
**C# / Java / Espresso:** Add `// @story:555` in the comment block immediately above the `[TestMethod]` / `@Test` line.
|
|
776
|
+
|
|
777
|
+
**Swift (XCUITest):**
|
|
778
|
+
```swift
|
|
779
|
+
// @story:555
|
|
780
|
+
// @bug:789
|
|
781
|
+
func testUserCanLogin() { ... }
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**Dart (Flutter):**
|
|
785
|
+
```dart
|
|
786
|
+
// @story:555
|
|
787
|
+
// @bug:789
|
|
788
|
+
testWidgets('user can log in', (WidgetTester tester) async { ... });
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Detox / React Native:**
|
|
792
|
+
```typescript
|
|
793
|
+
// @story:555
|
|
794
|
+
// @bug:789
|
|
795
|
+
it('user can log in', async () => { ... });
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### How it works
|
|
799
|
+
|
|
800
|
+
- On each `push`, ado-sync reads the `@story:N` / `@bug:N` tags from the spec file.
|
|
801
|
+
- **New links** found in the file are added to the Test Case in Azure DevOps.
|
|
802
|
+
- **Stale links** (present in Azure but no longer tagged locally) are removed automatically.
|
|
803
|
+
- The sync is non-destructive for links not covered by a configured prefix — only the prefixes listed in `sync.links` are managed.
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
381
807
|
## Environment variables
|
|
382
808
|
|
|
383
809
|
| Variable | Description |
|
|
@@ -413,13 +839,16 @@ The comparison uses title + steps + description. Touch any step to force an upda
|
|
|
413
839
|
Delete `.ado-sync-state.json` to reset the cache. The next push re-populates it from Azure.
|
|
414
840
|
|
|
415
841
|
**CSV/Excel IDs not written back**
|
|
416
|
-
Ensure the file is not open in another application
|
|
842
|
+
Ensure the file is not open in another application and that `sync.disableLocalChanges` is not `true`. If a TC was deleted from Azure and re-created on push, the old ID in column A is now replaced with the new ID automatically.
|
|
417
843
|
|
|
418
844
|
**Excel file not parsed / `No worksheet found`**
|
|
419
|
-
ado-sync
|
|
845
|
+
ado-sync searches for the first worksheet by reading `xl/_rels/workbook.xml.rels` from the xlsx ZIP, falling back to common names (`sheet.xml`, `sheet1.xml`). Non-standard sheet names and multi-sheet workbooks are handled automatically. If parsing still fails, re-export from Azure DevOps.
|
|
420
846
|
|
|
421
|
-
**Pull has no effect on CSV
|
|
422
|
-
|
|
847
|
+
**Pull has no effect on CSV files**
|
|
848
|
+
CSV pull is now supported — `ado-sync pull` updates the Title and step rows in CSV files to match the current Azure DevOps Test Case. Run `ado-sync pull --dry-run` first to preview changes.
|
|
849
|
+
|
|
850
|
+
**Pull has no effect on Excel files**
|
|
851
|
+
Excel (xlsx) pull is not yet supported — only push. Use CSV export instead if bidirectional sync is needed, or pull the changes manually and re-export.
|
|
423
852
|
|
|
424
853
|
**C# categories show as constant names instead of values**
|
|
425
854
|
ado-sync resolves `const string` declarations in the same file. Constants defined in a base class are not resolved — use string literals in `[TestCategory("...")]` for reliable tagging.
|
|
@@ -447,3 +876,15 @@ ado-sync detects `it()`, `test()`, `xit()`, `xtest()`, and `.only`/`.skip`/`.con
|
|
|
447
876
|
|
|
448
877
|
**JavaScript ID not written back**
|
|
449
878
|
ado-sync inserts `// @tc:ID` immediately above the `it()`/`test()` line. There must be no blank line between the comment and the test function call.
|
|
879
|
+
|
|
880
|
+
**`publish-test-results` — "TestPointId, testCaseId must be specified for planned test results"**
|
|
881
|
+
This error means the Test Run was created as a "planned" run (tied to a test plan), which requires test point IDs for each result. ado-sync creates standalone automated runs — do not pass `plan.id` in `runModel`. This is handled automatically; if you see this error, ensure you are on the latest version.
|
|
882
|
+
|
|
883
|
+
**TRX screenshots / `<ResultFiles>` not attached**
|
|
884
|
+
In TRX format, `<ResultFiles>` is a child of `<Output>`, not a direct child of `<UnitTestResult>`. Make sure `TestContext.AddResultFile("path/to/screenshot.png")` is called in your test code. ado-sync reads from the correct nested path automatically.
|
|
885
|
+
|
|
886
|
+
**Attachment paths resolve incorrectly**
|
|
887
|
+
Attachment paths embedded in result files (TRX `<ResultFiles>`, NUnit `<filePath>`, JUnit `[[ATTACHMENT|path]]`, Playwright `attachments[].path`) are resolved **relative to the result file's directory**, not the working directory. Keep result files and screenshots in the same output folder hierarchy as your test runner produces them.
|
|
888
|
+
|
|
889
|
+
**"Invalid AttachmentType specified" from Azure DevOps API**
|
|
890
|
+
Azure DevOps only accepts `GeneralAttachment` and `ConsoleLog` as attachment types. Screenshot and video files are uploaded as `GeneralAttachment` automatically — no special type is needed.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-summary generator for test cases that have no doc comment.
|
|
3
|
+
*
|
|
4
|
+
* Providers:
|
|
5
|
+
*
|
|
6
|
+
* local — Node.js-native LLM via node-llama-cpp (bundled dependency).
|
|
7
|
+
* Runs GGUF models directly in-process; no external server.
|
|
8
|
+
* Requires a GGUF model file path supplied via `model`.
|
|
9
|
+
* Falls back to heuristic if no model path is provided.
|
|
10
|
+
* Recommended model: Qwen2.5-Coder-1.5B-Instruct-Q4_K_M.gguf
|
|
11
|
+
*
|
|
12
|
+
* heuristic — regex pattern matching against the test body; zero
|
|
13
|
+
* dependencies, works fully offline.
|
|
14
|
+
*
|
|
15
|
+
* ollama — local LLM via Ollama REST API (http://localhost:11434).
|
|
16
|
+
* Model suggestion: qwen2.5-coder:7b.
|
|
17
|
+
*
|
|
18
|
+
* openai — OpenAI Chat Completions API (requires API key).
|
|
19
|
+
*
|
|
20
|
+
* anthropic — Anthropic Messages API (requires API key).
|
|
21
|
+
*
|
|
22
|
+
* Usage (engine.ts / CLI):
|
|
23
|
+
* const { title, description, steps } = await summarizeTest(test, localType, {
|
|
24
|
+
* provider: 'local',
|
|
25
|
+
* model: '/path/to/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf',
|
|
26
|
+
* });
|
|
27
|
+
* test.title = title;
|
|
28
|
+
* test.description = description;
|
|
29
|
+
* test.steps = steps;
|
|
30
|
+
*/
|
|
31
|
+
import { ParsedStep, ParsedTest } from '../types';
|
|
32
|
+
export type AiProvider = 'heuristic' | 'local' | 'ollama' | 'openai' | 'anthropic';
|
|
33
|
+
export interface AiSummaryOpts {
|
|
34
|
+
provider: AiProvider;
|
|
35
|
+
/**
|
|
36
|
+
* For `local`: absolute path to a GGUF model file, e.g.
|
|
37
|
+
* ~/.cache/ado-sync/models/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf
|
|
38
|
+
* For `ollama`: model tag, e.g. qwen2.5-coder:7b
|
|
39
|
+
* For `openai`: model name, e.g. gpt-4o-mini
|
|
40
|
+
* For `anthropic`: model name, e.g. claude-haiku-4-5-20251001
|
|
41
|
+
*/
|
|
42
|
+
model?: string;
|
|
43
|
+
/** Base URL for Ollama (default: http://localhost:11434) or OpenAI-compatible endpoint. */
|
|
44
|
+
baseUrl?: string;
|
|
45
|
+
/** API key for openai / anthropic — or $ENV_VAR reference. */
|
|
46
|
+
apiKey?: string;
|
|
47
|
+
/** Fall back to heuristic if the LLM call fails. Default: true. */
|
|
48
|
+
heuristicFallback?: boolean;
|
|
49
|
+
}
|
|
50
|
+
type LocalType = 'gherkin' | 'markdown' | 'csv' | 'excel' | 'csharp' | 'java' | 'python' | 'javascript' | 'playwright' | 'puppeteer' | 'cypress' | 'testcafe' | 'detox' | 'espresso' | 'xcuitest' | 'flutter';
|
|
51
|
+
/**
|
|
52
|
+
* Read the source file and return the raw text of the test function body,
|
|
53
|
+
* starting from the marker line (1-based).
|
|
54
|
+
*
|
|
55
|
+
* For Python: collects lines indented past the `def` line.
|
|
56
|
+
* For all others: collects until balanced braces close.
|
|
57
|
+
*/
|
|
58
|
+
export declare function extractFunctionBody(filePath: string, markerLine: number, localType: LocalType): string;
|
|
59
|
+
export declare function heuristicSummary(body: string, fallbackTitle: string): {
|
|
60
|
+
title: string;
|
|
61
|
+
description: string;
|
|
62
|
+
steps: ParsedStep[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Generate a title, description, and steps for a test that has no doc comment.
|
|
66
|
+
* Mutates nothing — returns new values; callers assign them.
|
|
67
|
+
*/
|
|
68
|
+
export declare function summarizeTest(test: ParsedTest, localType: LocalType, opts: AiSummaryOpts): Promise<{
|
|
69
|
+
title: string;
|
|
70
|
+
description: string;
|
|
71
|
+
steps: ParsedStep[];
|
|
72
|
+
}>;
|
|
73
|
+
export {};
|