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 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
- ## Installation
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
- Minimal config:
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/my-org",
91
- "project": "MyProject",
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": 1234 },
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. Check that `sync.disableLocalChanges` is not `true`.
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 looks for `xl/worksheets/sheet.xml` or `xl/worksheets/sheet1.xml` inside the xlsx ZIP. Re-export from Azure DevOps to get a compatible file.
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/Excel files**
422
- Pull is not supported for CSV and Excel only push. These formats are managed by external tools.
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 {};