ado-sync 0.1.30 → 0.1.32
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 +408 -22
- 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/dart.d.ts +31 -0
- package/dist/parsers/dart.js +245 -0
- package/dist/parsers/dart.js.map +1 -0
- package/dist/parsers/javascript.d.ts +29 -11
- package/dist/parsers/javascript.js +118 -15
- 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 +38 -0
- package/dist/parsers/testcafe.js +324 -0
- package/dist/parsers/testcafe.js.map +1 -0
- package/dist/sync/engine.d.ts +4 -1
- package/dist/sync/engine.js +43 -1
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/publish-results.js +34 -5
- package/dist/sync/publish-results.js.map +1 -1
- package/dist/sync/writeback.d.ts +41 -7
- package/dist/sync/writeback.js +205 -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 +4 -4
- package/docs/spec-formats.md +632 -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 | `annotation: { type: 'tc', description: '12345' }` in test options *(preferred)*; or `// @tc:12345` comment fallback |
|
|
64
|
+
| Puppeteer | `// @tc:12345` comment above `it()`/`test()` |
|
|
65
|
+
| Cypress | `// @tc:12345` comment above `it()`/`specify()` |
|
|
66
|
+
| TestCafe | `test.meta('tc', '12345')('title', fn)` *(preferred)*; or `// @tc:12345` comment fallback |
|
|
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
|
|
|
@@ -325,6 +454,23 @@ pytest --junitxml=results/junit.xml
|
|
|
325
454
|
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
326
455
|
```
|
|
327
456
|
|
|
457
|
+
By default, TC linking uses `AutomatedTestName` matching (requires `sync.markAutomated: true`).
|
|
458
|
+
|
|
459
|
+
**Optional — embed TC IDs into JUnit XML** for direct linking (more reliable, works even if the class/method is renamed):
|
|
460
|
+
|
|
461
|
+
Add this to `conftest.py`:
|
|
462
|
+
|
|
463
|
+
```python
|
|
464
|
+
# conftest.py
|
|
465
|
+
def pytest_runtest_makereport(item, call):
|
|
466
|
+
"""Write @pytest.mark.tc(N) as a JUnit XML property for ado-sync to pick up."""
|
|
467
|
+
for marker in item.iter_markers("tc"):
|
|
468
|
+
if marker.args:
|
|
469
|
+
item.user_properties.append(("tc", str(marker.args[0])))
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
pytest will then write each TC ID into the JUnit XML as `<property name="tc" value="N"/>`, and ado-sync will link the result directly to that Test Case — no AutomatedTestName matching needed.
|
|
473
|
+
|
|
328
474
|
Recommended `ado-sync.json` for Python:
|
|
329
475
|
|
|
330
476
|
```json
|
|
@@ -383,7 +529,7 @@ Recommended `ado-sync.json` for Playwright:
|
|
|
383
529
|
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
384
530
|
"testPlan": { "id": 1234 },
|
|
385
531
|
"local": {
|
|
386
|
-
"type": "
|
|
532
|
+
"type": "playwright",
|
|
387
533
|
"include": ["tests/**/*.spec.ts"],
|
|
388
534
|
"exclude": ["**/*.helper.ts"]
|
|
389
535
|
},
|
|
@@ -412,6 +558,138 @@ Recommended `ado-sync.json` for Jest/Jasmine/WebdriverIO:
|
|
|
412
558
|
}
|
|
413
559
|
```
|
|
414
560
|
|
|
561
|
+
### Detox (React Native): create TCs and push
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
# 1. Create TCs and write IDs back into .ts files
|
|
565
|
+
ado-sync push --dry-run # preview
|
|
566
|
+
ado-sync push # writes // @tc:ID above each it() / test()
|
|
567
|
+
|
|
568
|
+
# 2. Run Detox tests (Jest runner)
|
|
569
|
+
npx detox test --configuration ios.sim.release
|
|
570
|
+
|
|
571
|
+
# 3. Publish results (Jest JUnit reporter)
|
|
572
|
+
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Recommended `ado-sync.json` for Detox:
|
|
576
|
+
|
|
577
|
+
```json
|
|
578
|
+
{
|
|
579
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
580
|
+
"project": "MyProject",
|
|
581
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
582
|
+
"testPlan": { "id": 1234 },
|
|
583
|
+
"local": {
|
|
584
|
+
"type": "detox",
|
|
585
|
+
"include": ["e2e/**/*.test.ts"]
|
|
586
|
+
},
|
|
587
|
+
"sync": { "markAutomated": true }
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### Espresso (Android): create TCs and push
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
# 1. Create TCs and write IDs back into .java / .kt files
|
|
595
|
+
ado-sync push --dry-run # preview
|
|
596
|
+
ado-sync push # writes // @tc:ID above @Test
|
|
597
|
+
|
|
598
|
+
# 2. Run instrumented tests and generate JUnit XML
|
|
599
|
+
./gradlew connectedAndroidTest
|
|
600
|
+
# XML output: app/build/outputs/androidTest-results/connected/TEST-*.xml
|
|
601
|
+
|
|
602
|
+
# 3. Publish results
|
|
603
|
+
ado-sync publish-test-results \
|
|
604
|
+
--testResult "app/build/outputs/androidTest-results/connected/TEST-*.xml" \
|
|
605
|
+
--testResultFormat junit
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
Recommended `ado-sync.json` for Espresso:
|
|
609
|
+
|
|
610
|
+
```json
|
|
611
|
+
{
|
|
612
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
613
|
+
"project": "MyProject",
|
|
614
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
615
|
+
"testPlan": { "id": 1234 },
|
|
616
|
+
"local": {
|
|
617
|
+
"type": "espresso",
|
|
618
|
+
"include": ["app/src/androidTest/**/*.java", "app/src/androidTest/**/*.kt"],
|
|
619
|
+
"exclude": ["**/*BaseTest.java"]
|
|
620
|
+
},
|
|
621
|
+
"sync": { "markAutomated": true }
|
|
622
|
+
}
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### XCUITest (iOS): create TCs and push
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
# 1. Create TCs and write IDs back into .swift files
|
|
629
|
+
ado-sync push --dry-run # preview
|
|
630
|
+
ado-sync push # writes // @tc:ID above func test*()
|
|
631
|
+
|
|
632
|
+
# 2. Run XCUITest and export JUnit XML
|
|
633
|
+
xcodebuild test \
|
|
634
|
+
-project MyApp.xcodeproj \
|
|
635
|
+
-scheme MyApp \
|
|
636
|
+
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
|
637
|
+
-resultBundlePath TestResults.xcresult
|
|
638
|
+
xcrun xcresulttool get --path TestResults.xcresult --format junit > results/junit.xml
|
|
639
|
+
|
|
640
|
+
# 3. Publish results
|
|
641
|
+
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Recommended `ado-sync.json` for XCUITest:
|
|
645
|
+
|
|
646
|
+
```json
|
|
647
|
+
{
|
|
648
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
649
|
+
"project": "MyProject",
|
|
650
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
651
|
+
"testPlan": { "id": 1234 },
|
|
652
|
+
"local": {
|
|
653
|
+
"type": "xcuitest",
|
|
654
|
+
"include": ["UITests/**/*.swift"],
|
|
655
|
+
"exclude": ["UITests/**/*Helper.swift", "UITests/**/*Base.swift"]
|
|
656
|
+
},
|
|
657
|
+
"sync": { "markAutomated": true }
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Flutter: create TCs and push
|
|
662
|
+
|
|
663
|
+
```bash
|
|
664
|
+
# 1. Create TCs and write IDs back into _test.dart files
|
|
665
|
+
ado-sync push --dry-run # preview
|
|
666
|
+
ado-sync push # writes // @tc:ID above testWidgets() / test()
|
|
667
|
+
|
|
668
|
+
# 2. Run Flutter tests with machine-readable output
|
|
669
|
+
flutter test --machine > results/flutter_test.jsonl
|
|
670
|
+
# Or generate JUnit XML with the flutter_test_junit package:
|
|
671
|
+
flutter test --reporter junit > results/junit.xml
|
|
672
|
+
|
|
673
|
+
# 3. Publish results
|
|
674
|
+
ado-sync publish-test-results --testResult results/junit.xml --testResultFormat junit
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Recommended `ado-sync.json` for Flutter:
|
|
678
|
+
|
|
679
|
+
```json
|
|
680
|
+
{
|
|
681
|
+
"orgUrl": "https://dev.azure.com/my-org",
|
|
682
|
+
"project": "MyProject",
|
|
683
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
684
|
+
"testPlan": { "id": 1234 },
|
|
685
|
+
"local": {
|
|
686
|
+
"type": "flutter",
|
|
687
|
+
"include": ["test/**/*_test.dart", "integration_test/**/*_test.dart"]
|
|
688
|
+
},
|
|
689
|
+
"sync": { "markAutomated": true }
|
|
690
|
+
}
|
|
691
|
+
```
|
|
692
|
+
|
|
415
693
|
### CI pipeline
|
|
416
694
|
|
|
417
695
|
```yaml
|
|
@@ -421,6 +699,11 @@ Recommended `ado-sync.json` for Jest/Jasmine/WebdriverIO:
|
|
|
421
699
|
env:
|
|
422
700
|
AZURE_DEVOPS_TOKEN: ${{ secrets.AZURE_DEVOPS_TOKEN }}
|
|
423
701
|
|
|
702
|
+
- name: Sync test cases to Azure DevOps (with AI summary)
|
|
703
|
+
run: ado-sync push --ai-provider heuristic --config-override sync.disableLocalChanges=true
|
|
704
|
+
env:
|
|
705
|
+
AZURE_DEVOPS_TOKEN: ${{ secrets.AZURE_DEVOPS_TOKEN }}
|
|
706
|
+
|
|
424
707
|
- name: Publish test results
|
|
425
708
|
run: ado-sync publish-test-results --testResult results/test.trx
|
|
426
709
|
env:
|
|
@@ -435,6 +718,109 @@ ado-sync status
|
|
|
435
718
|
|
|
436
719
|
---
|
|
437
720
|
|
|
721
|
+
## Work Item Links
|
|
722
|
+
|
|
723
|
+
Link each Test Case to related Azure DevOps work items (User Stories, Bugs, etc.) automatically on every push.
|
|
724
|
+
|
|
725
|
+
### Configure `sync.links`
|
|
726
|
+
|
|
727
|
+
```json
|
|
728
|
+
{
|
|
729
|
+
"sync": {
|
|
730
|
+
"links": [
|
|
731
|
+
{
|
|
732
|
+
"prefix": "story",
|
|
733
|
+
"relationship": "Microsoft.VSTS.Common.TestedBy-Reverse",
|
|
734
|
+
"workItemType": "User Story"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
"prefix": "bug",
|
|
738
|
+
"relationship": "System.LinkTypes.Related",
|
|
739
|
+
"workItemType": "Bug"
|
|
740
|
+
}
|
|
741
|
+
]
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
| Field | Description |
|
|
747
|
+
|-------|-------------|
|
|
748
|
+
| `prefix` | The tag prefix used in your spec files (e.g. `story` → `@story:555`) |
|
|
749
|
+
| `relationship` | ADO relation type (see common values below) |
|
|
750
|
+
| `workItemType` | Optional — used in log output only |
|
|
751
|
+
|
|
752
|
+
**Common relationship values:**
|
|
753
|
+
|
|
754
|
+
| Relationship | Meaning |
|
|
755
|
+
|---|---|
|
|
756
|
+
| `Microsoft.VSTS.Common.TestedBy-Reverse` | Test Case "Tested By" ↔ User Story |
|
|
757
|
+
| `System.LinkTypes.Related` | Simple "Related" link |
|
|
758
|
+
| `System.LinkTypes.Dependency-Forward` | "Successor" (this item depends on) |
|
|
759
|
+
| `System.LinkTypes.Hierarchy-Reverse` | "Parent" link |
|
|
760
|
+
|
|
761
|
+
### Tag your tests
|
|
762
|
+
|
|
763
|
+
**Gherkin (`.feature`):**
|
|
764
|
+
```gherkin
|
|
765
|
+
# @story:555 @bug:789
|
|
766
|
+
Scenario: User can log in
|
|
767
|
+
Given I am on the login page
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
**JavaScript / TypeScript (Jest, Playwright, Cypress, TestCafe, Puppeteer):**
|
|
771
|
+
```typescript
|
|
772
|
+
// @story:555
|
|
773
|
+
// @bug:789
|
|
774
|
+
test('user can log in', async ({ page }) => { ... });
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
**Markdown (`.md`):**
|
|
778
|
+
```markdown
|
|
779
|
+
### User can log in @story:555 @bug:789
|
|
780
|
+
|
|
781
|
+
1. Navigate to the login page
|
|
782
|
+
2. Check: Login form is visible
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
**Python (pytest):**
|
|
786
|
+
```python
|
|
787
|
+
# @story:555 @bug:789
|
|
788
|
+
def test_user_can_log_in():
|
|
789
|
+
...
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**C# / Java / Espresso:** Add `// @story:555` in the comment block immediately above the `[TestMethod]` / `@Test` line.
|
|
793
|
+
|
|
794
|
+
**Swift (XCUITest):**
|
|
795
|
+
```swift
|
|
796
|
+
// @story:555
|
|
797
|
+
// @bug:789
|
|
798
|
+
func testUserCanLogin() { ... }
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
**Dart (Flutter):**
|
|
802
|
+
```dart
|
|
803
|
+
// @story:555
|
|
804
|
+
// @bug:789
|
|
805
|
+
testWidgets('user can log in', (WidgetTester tester) async { ... });
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
**Detox / React Native:**
|
|
809
|
+
```typescript
|
|
810
|
+
// @story:555
|
|
811
|
+
// @bug:789
|
|
812
|
+
it('user can log in', async () => { ... });
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### How it works
|
|
816
|
+
|
|
817
|
+
- On each `push`, ado-sync reads the `@story:N` / `@bug:N` tags from the spec file.
|
|
818
|
+
- **New links** found in the file are added to the Test Case in Azure DevOps.
|
|
819
|
+
- **Stale links** (present in Azure but no longer tagged locally) are removed automatically.
|
|
820
|
+
- The sync is non-destructive for links not covered by a configured prefix — only the prefixes listed in `sync.links` are managed.
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
438
824
|
## Environment variables
|
|
439
825
|
|
|
440
826
|
| Variable | Description |
|
|
@@ -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 {};
|