ado-sync 0.1.24 → 0.1.26
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 +73 -708
- package/dist/azure/client.d.ts +3 -0
- package/dist/azure/client.js +6 -0
- package/dist/azure/client.js.map +1 -1
- package/dist/azure/test-cases.d.ts +8 -3
- package/dist/azure/test-cases.js +403 -24
- package/dist/azure/test-cases.js.map +1 -1
- package/dist/cli.js +51 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.js +111 -5
- package/dist/config.js.map +1 -1
- package/dist/parsers/gherkin.d.ts +4 -1
- package/dist/parsers/gherkin.js +19 -4
- package/dist/parsers/gherkin.js.map +1 -1
- package/dist/parsers/markdown.d.ts +4 -1
- package/dist/parsers/markdown.js +5 -3
- package/dist/parsers/markdown.js.map +1 -1
- package/dist/parsers/shared.d.ts +18 -0
- package/dist/parsers/shared.js +40 -0
- package/dist/parsers/shared.js.map +1 -1
- package/dist/sync/engine.js +98 -5
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/publish-results.d.ts +33 -0
- package/dist/sync/publish-results.js +281 -0
- package/dist/sync/publish-results.js.map +1 -0
- package/dist/types.d.ts +151 -0
- package/docs/advanced.md +350 -0
- package/docs/configuration.md +293 -0
- package/docs/publish-test-results.md +119 -0
- package/docs/spec-formats.md +225 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Bidirectional sync between local test specs and Azure DevOps Test Cases.
|
|
4
4
|
|
|
5
|
-
Supports **Cucumber / Gherkin** `.feature` files, **prose Markdown** `.md` spec files (including Playwright test-plan markdown
|
|
5
|
+
Supports **Cucumber / Gherkin** `.feature` files, **prose Markdown** `.md` spec files (including Playwright test-plan markdown), and **Azure DevOps tabular exports** in **CSV** and **Excel** (`.xlsx`) format.
|
|
6
6
|
Inspired by [SpecSync](https://docs.specsolutions.eu/specsync/).
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -20,7 +20,7 @@ Local files ado-sync Azure DevOps
|
|
|
20
20
|
@tc:12345 / col A (csv/xlsx)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
On the **first push** of a scenario, a new Test Case is created in Azure DevOps and its ID is written back into the local file as a tag
|
|
23
|
+
On the **first push** of a scenario, a new Test Case is created in Azure DevOps and its ID is written back into the local file as a tag. Every subsequent push uses that ID to update the existing Test Case. Pulling fetches the latest title and steps from Azure and overwrites the local file.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ On the **first push** of a scenario, a new Test Case is created in Azure DevOps
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
npm install -g ado-sync
|
|
31
|
-
# or
|
|
31
|
+
# or run without installing
|
|
32
32
|
npx ado-sync --help
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -36,51 +36,33 @@ npx ado-sync --help
|
|
|
36
36
|
|
|
37
37
|
## Quick start
|
|
38
38
|
|
|
39
|
-
### 1. Generate a config file
|
|
40
|
-
|
|
41
39
|
```bash
|
|
40
|
+
# 1. Generate a config file
|
|
42
41
|
ado-sync init # creates ado-sync.json
|
|
43
|
-
ado-sync init ado-sync.yml #
|
|
42
|
+
ado-sync init ado-sync.yml # YAML format
|
|
43
|
+
|
|
44
|
+
# 2. Edit the config with your org, project, plan ID, and token
|
|
45
|
+
export AZURE_DEVOPS_TOKEN=your_personal_access_token
|
|
46
|
+
|
|
47
|
+
# 3. Preview what will be created
|
|
48
|
+
ado-sync push --dry-run
|
|
49
|
+
|
|
50
|
+
# 4. Push to Azure DevOps
|
|
51
|
+
ado-sync push
|
|
44
52
|
```
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
Minimal config:
|
|
47
55
|
|
|
48
56
|
```json
|
|
49
57
|
{
|
|
50
58
|
"orgUrl": "https://dev.azure.com/my-org",
|
|
51
59
|
"project": "MyProject",
|
|
52
|
-
"auth": {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
"testPlan": {
|
|
57
|
-
"id": 1234,
|
|
58
|
-
"suiteId": 5678
|
|
59
|
-
},
|
|
60
|
-
"local": {
|
|
61
|
-
"type": "gherkin",
|
|
62
|
-
"include": "specs/**/*.feature"
|
|
63
|
-
},
|
|
64
|
-
"sync": {
|
|
65
|
-
"tagPrefix": "tc"
|
|
66
|
-
}
|
|
60
|
+
"auth": { "type": "pat", "token": "$AZURE_DEVOPS_TOKEN" },
|
|
61
|
+
"testPlan": { "id": 1234 },
|
|
62
|
+
"local": { "type": "gherkin", "include": "specs/**/*.feature" }
|
|
67
63
|
}
|
|
68
64
|
```
|
|
69
65
|
|
|
70
|
-
### 3. Set your token
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
export AZURE_DEVOPS_TOKEN=your_personal_access_token
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 4. Push
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
ado-sync push
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
New scenarios are created in Azure DevOps. Their IDs are written back into your local files automatically.
|
|
83
|
-
|
|
84
66
|
---
|
|
85
67
|
|
|
86
68
|
## CLI reference
|
|
@@ -94,49 +76,36 @@ Options:
|
|
|
94
76
|
-h, --help Show help
|
|
95
77
|
|
|
96
78
|
Commands:
|
|
97
|
-
init [output]
|
|
98
|
-
push [options]
|
|
99
|
-
pull [options]
|
|
100
|
-
status [options]
|
|
101
|
-
|
|
79
|
+
init [output] Generate a starter config file
|
|
80
|
+
push [options] Push local specs to Azure DevOps
|
|
81
|
+
pull [options] Pull updates from Azure DevOps into local files
|
|
82
|
+
status [options] Show diff without making changes
|
|
83
|
+
publish-test-results [opts] Publish TRX / JUnit / Cucumber JSON results to Azure DevOps
|
|
84
|
+
help [command] Help for a specific command
|
|
102
85
|
```
|
|
103
86
|
|
|
104
87
|
### `init`
|
|
105
88
|
|
|
106
89
|
```bash
|
|
107
90
|
ado-sync init # creates ado-sync.json
|
|
108
|
-
ado-sync init ado-sync.yml #
|
|
109
|
-
ado-sync init path/to/config.json
|
|
91
|
+
ado-sync init ado-sync.yml # YAML format
|
|
110
92
|
```
|
|
111
93
|
|
|
112
|
-
Creates a starter config file in JSON or YAML format depending on the file extension. Safe — will not overwrite an existing file.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
94
|
### `push`
|
|
117
95
|
|
|
118
96
|
```bash
|
|
119
97
|
ado-sync push
|
|
120
98
|
ado-sync push --dry-run
|
|
121
|
-
ado-sync push --tags "@smoke"
|
|
122
99
|
ado-sync push --tags "@smoke and not @wip"
|
|
123
100
|
ado-sync push --config-override testPlan.id=9999
|
|
124
|
-
ado-sync -c other-config.json push
|
|
125
101
|
```
|
|
126
102
|
|
|
127
|
-
For every scenario / heading in your local spec files:
|
|
128
|
-
|
|
129
103
|
| Scenario state | Action |
|
|
130
104
|
|----------------|--------|
|
|
131
|
-
| No ID tag
|
|
132
|
-
|
|
|
133
|
-
|
|
|
105
|
+
| No ID tag | Creates a new Test Case, writes ID back |
|
|
106
|
+
| ID tag, no changes | Skipped |
|
|
107
|
+
| ID tag, content changed | Updates the existing Test Case |
|
|
134
108
|
| Deleted locally, still in Azure suite | Tagged `ado-sync:removed` in Azure |
|
|
135
|
-
| Has ID tag but Test Case deleted in Azure | Reported as error |
|
|
136
|
-
|
|
137
|
-
`--dry-run` prints what would happen without modifying anything.
|
|
138
|
-
|
|
139
|
-
---
|
|
140
109
|
|
|
141
110
|
### `pull`
|
|
142
111
|
|
|
@@ -144,637 +113,37 @@ For every scenario / heading in your local spec files:
|
|
|
144
113
|
ado-sync pull
|
|
145
114
|
ado-sync pull --dry-run
|
|
146
115
|
ado-sync pull --tags "@smoke"
|
|
147
|
-
ado-sync pull --config-override sync.conflictAction=skip
|
|
148
116
|
```
|
|
149
117
|
|
|
150
|
-
For every locally-linked scenario (has an ID tag):
|
|
151
|
-
|
|
152
|
-
| Remote state | Action |
|
|
153
|
-
|--------------|--------|
|
|
154
|
-
| Title, steps, or description changed in Azure | Updates the local file |
|
|
155
|
-
| No changes | Skipped |
|
|
156
|
-
| Test Case not found | Reported as error |
|
|
157
|
-
|
|
158
|
-
`--dry-run` prints what would change without modifying local files.
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
118
|
### `status`
|
|
163
119
|
|
|
164
120
|
```bash
|
|
165
121
|
ado-sync status
|
|
166
122
|
ado-sync status --tags "@smoke"
|
|
167
|
-
ado-sync status --config-override testPlan.id=9999
|
|
168
123
|
```
|
|
169
124
|
|
|
170
125
|
Compares local specs against Azure DevOps and prints a diff — no changes made.
|
|
171
126
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### `--config-override`
|
|
175
|
-
|
|
176
|
-
All commands accept one or more `--config-override path=value` options to set config values from the command line without editing the file. Useful in CI pipelines.
|
|
127
|
+
### `publish-test-results`
|
|
177
128
|
|
|
178
129
|
```bash
|
|
179
|
-
|
|
180
|
-
ado-sync
|
|
181
|
-
|
|
182
|
-
# Override tag prefix
|
|
183
|
-
ado-sync push --config-override sync.tagPrefix=azure
|
|
184
|
-
|
|
185
|
-
# Multiple overrides (repeat the flag)
|
|
186
|
-
ado-sync push \
|
|
187
|
-
--config-override testPlan.id=9999 \
|
|
188
|
-
--config-override sync.disableLocalChanges=true
|
|
130
|
+
ado-sync publish-test-results --testResult results/test.trx
|
|
131
|
+
ado-sync publish-test-results --testResult results/test.xml --testResultFormat junit --dry-run
|
|
189
132
|
```
|
|
190
133
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## Config file reference
|
|
196
|
-
|
|
197
|
-
Config files can be JSON (`.json`) or YAML (`.yml` / `.yaml`). Run `ado-sync init` to generate a template.
|
|
198
|
-
|
|
199
|
-
### Full example (JSON)
|
|
200
|
-
|
|
201
|
-
```json
|
|
202
|
-
{
|
|
203
|
-
"orgUrl": "https://dev.azure.com/YOUR_ORG",
|
|
204
|
-
"project": "YOUR_PROJECT",
|
|
205
|
-
|
|
206
|
-
"auth": {
|
|
207
|
-
"type": "pat",
|
|
208
|
-
"token": "$AZURE_DEVOPS_TOKEN"
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
"testPlan": {
|
|
212
|
-
"id": 1234,
|
|
213
|
-
"suiteId": 5678,
|
|
214
|
-
"suiteMapping": "flat"
|
|
215
|
-
},
|
|
216
|
-
|
|
217
|
-
"local": {
|
|
218
|
-
"type": "gherkin",
|
|
219
|
-
"include": "specs/**/*.feature",
|
|
220
|
-
"exclude": []
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
"sync": {
|
|
224
|
-
"tagPrefix": "tc",
|
|
225
|
-
"titleField": "System.Title",
|
|
226
|
-
"areaPath": "MyProject\\Team A",
|
|
227
|
-
"iterationPath": "MyProject\\Sprint 1",
|
|
228
|
-
"disableLocalChanges": false,
|
|
229
|
-
"conflictAction": "overwrite",
|
|
230
|
-
"links": [
|
|
231
|
-
{ "prefix": "story", "relationship": "System.LinkTypes.Related" },
|
|
232
|
-
{ "prefix": "bug", "relationship": "System.LinkTypes.Related" }
|
|
233
|
-
]
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
### `orgUrl`
|
|
241
|
-
|
|
242
|
-
Azure DevOps organisation URL. Format: `https://dev.azure.com/YOUR_ORG`.
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
### `project`
|
|
247
|
-
|
|
248
|
-
Azure DevOps project name.
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
### `auth`
|
|
253
|
-
|
|
254
|
-
| Field | Description |
|
|
255
|
-
|-------|-------------|
|
|
256
|
-
| `type` | `"pat"` · `"accessToken"` · `"managedIdentity"` |
|
|
257
|
-
| `token` | PAT or access token value. Prefix with `$` to read from an environment variable (e.g. `"$AZURE_DEVOPS_TOKEN"`). |
|
|
258
|
-
| `applicationIdURI` | Required only when `type` is `"managedIdentity"`. |
|
|
259
|
-
|
|
260
|
-
**PAT permissions required:** Test Management (Read & Write), Work Items (Read & Write).
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
### `testPlan`
|
|
265
|
-
|
|
266
|
-
| Field | Default | Description |
|
|
267
|
-
|-------|---------|-------------|
|
|
268
|
-
| `id` | *(required)* | ID of the Azure DevOps Test Plan. |
|
|
269
|
-
| `suiteId` | plan root | ID of the Test Suite within the plan. Defaults to the plan's root suite. |
|
|
270
|
-
| `suiteMapping` | `"flat"` | `"flat"` — all Test Cases go into one suite. `"byFolder"` — folder structure is mirrored as nested child suites (e.g. `specs/login/` → suite named `login`). |
|
|
271
|
-
|
|
272
|
-
---
|
|
273
|
-
|
|
274
|
-
### `testPlans` (multi-plan)
|
|
275
|
-
|
|
276
|
-
To sync the same repository against multiple Test Plans, use the `testPlans` array instead of (or in addition to) `testPlan`. Each entry overrides `testPlan` and can specify its own file globs.
|
|
277
|
-
|
|
278
|
-
```json
|
|
279
|
-
{
|
|
280
|
-
"testPlans": [
|
|
281
|
-
{
|
|
282
|
-
"id": 1001,
|
|
283
|
-
"suiteId": 2001,
|
|
284
|
-
"include": "specs/smoke/**/*.feature"
|
|
285
|
-
},
|
|
286
|
-
{
|
|
287
|
-
"id": 1002,
|
|
288
|
-
"suiteId": 2002,
|
|
289
|
-
"include": "specs/regression/**/*.feature",
|
|
290
|
-
"suiteMapping": "byFolder"
|
|
291
|
-
}
|
|
292
|
-
]
|
|
293
|
-
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
| Field | Description |
|
|
297
|
-
|-------|-------------|
|
|
298
|
-
| `id` | Test Plan ID |
|
|
299
|
-
| `suiteId` | *(Optional)* Target suite ID |
|
|
300
|
-
| `suiteMapping` | *(Optional)* `"flat"` or `"byFolder"` |
|
|
301
|
-
| `include` | *(Optional)* Override `local.include` for this plan |
|
|
302
|
-
| `exclude` | *(Optional)* Override `local.exclude` for this plan |
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
|
-
### `local`
|
|
307
|
-
|
|
308
|
-
| Field | Description |
|
|
309
|
-
|-------|-------------|
|
|
310
|
-
| `type` | `"gherkin"` · `"markdown"` · `"csv"` · `"excel"` — see [Spec file formats](#spec-file-formats) |
|
|
311
|
-
| `include` | Glob pattern(s) relative to the config file. String or array. |
|
|
312
|
-
| `exclude` | *(Optional)* Glob pattern(s) to exclude. String or array. |
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
### `sync`
|
|
317
|
-
|
|
318
|
-
| Field | Default | Description |
|
|
319
|
-
|-------|---------|-------------|
|
|
320
|
-
| `tagPrefix` | `"tc"` | Prefix used in ID tags. See [ID tags](#id-tags). |
|
|
321
|
-
| `titleField` | `"System.Title"` | Azure DevOps field used as the test case title. |
|
|
322
|
-
| `areaPath` | *(none)* | Area path for newly created Test Cases. |
|
|
323
|
-
| `iterationPath` | *(none)* | Iteration path for newly created Test Cases. |
|
|
324
|
-
| `disableLocalChanges` | `false` | When `true`, no local files are modified — no ID writeback, no pull apply. Use in CI pipelines that should not commit file changes. |
|
|
325
|
-
| `conflictAction` | `"overwrite"` | How to handle conflicts (remote changed since last sync AND local also differs). `"overwrite"` — push local version. `"skip"` — emit a conflict result, leave both sides unchanged. `"fail"` — throw an error listing all conflicts. |
|
|
326
|
-
| `links` | `[]` | Work item link configurations. See [Work item linking](#work-item-linking). |
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## ID tags
|
|
331
|
-
|
|
332
|
-
After a scenario is pushed for the first time, ado-sync writes the Azure Test Case ID back into the local file. The format depends on the spec type.
|
|
333
|
-
|
|
334
|
-
| Format | Writeback location |
|
|
335
|
-
|--------|--------------------|
|
|
336
|
-
| Gherkin | `@tc:12345` tag above the `Scenario:` line |
|
|
337
|
-
| Markdown | `@tc:12345` tag after the `### heading` |
|
|
338
|
-
| CSV | Numeric ID in column A of the matching title row |
|
|
339
|
-
| Excel | Numeric ID in cell A of the matching title row |
|
|
340
|
-
|
|
341
|
-
### Gherkin
|
|
342
|
-
|
|
343
|
-
The ID tag is inserted on its own line immediately above the `Scenario:` or `Scenario Outline:` keyword:
|
|
344
|
-
|
|
345
|
-
```gherkin
|
|
346
|
-
Feature: Login
|
|
347
|
-
|
|
348
|
-
@tc:1042
|
|
349
|
-
Scenario: Successful login with valid credentials
|
|
350
|
-
Given I am on the login page
|
|
351
|
-
When I enter username "standard_user" and password "secret_sauce"
|
|
352
|
-
Then I am redirected to the inventory page
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
For `Scenario Outline`, a **single** parametrized Test Case is created in Azure with a data table — one `@tc:ID` tag for the whole outline, not one per example row.
|
|
356
|
-
|
|
357
|
-
```gherkin
|
|
358
|
-
@tc:1043
|
|
359
|
-
Scenario Outline: Login with different roles
|
|
360
|
-
Given I am logged in as "<role>"
|
|
361
|
-
Then I can see "<dashboard>"
|
|
362
|
-
|
|
363
|
-
Examples:
|
|
364
|
-
| role | dashboard |
|
|
365
|
-
| admin | Admin view |
|
|
366
|
-
| tester | Test view |
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Markdown
|
|
370
|
-
|
|
371
|
-
The ID tag is inserted on the line immediately after the `### heading`:
|
|
372
|
-
|
|
373
|
-
```markdown
|
|
374
|
-
### 1. Login (happy path)
|
|
375
|
-
@tc:1042
|
|
376
|
-
|
|
377
|
-
Assumption: Fresh browser session.
|
|
378
|
-
|
|
379
|
-
Steps:
|
|
380
|
-
1. Enter username `standard_user` in the Username field.
|
|
381
|
-
2. Enter password `secret_sauce` in the Password field.
|
|
382
|
-
3. Click `Login`.
|
|
383
|
-
|
|
384
|
-
Expected results:
|
|
385
|
-
- User is redirected to `/inventory.html`.
|
|
386
|
-
- Product list is visible.
|
|
387
|
-
|
|
388
|
-
---
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
### Custom prefix
|
|
392
|
-
|
|
393
|
-
Change the `sync.tagPrefix` in your config to use a different prefix:
|
|
394
|
-
|
|
395
|
-
```json
|
|
396
|
-
{ "sync": { "tagPrefix": "azure" } }
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
Result:
|
|
400
|
-
- Gherkin: `@azure:1042`
|
|
401
|
-
- Markdown: `@azure:1042`
|
|
402
|
-
|
|
403
|
-
> **Warning:** Changing the prefix on an existing project means all existing ID tags will no longer be recognised. Do a project-wide find-and-replace on the old prefix before changing it.
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Tag filtering
|
|
408
|
-
|
|
409
|
-
All commands accept a `--tags` option to limit which scenarios are synced. The syntax is the standard Cucumber tag expression language.
|
|
410
|
-
|
|
411
|
-
```bash
|
|
412
|
-
# Only sync scenarios tagged @smoke
|
|
413
|
-
ado-sync push --tags "@smoke"
|
|
414
|
-
|
|
415
|
-
# Sync everything except @wip
|
|
416
|
-
ado-sync push --tags "not @wip"
|
|
417
|
-
|
|
418
|
-
# Combine tags with and / or
|
|
419
|
-
ado-sync push --tags "@smoke and not @slow"
|
|
420
|
-
ado-sync pull --tags "@regression or @critical"
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
Tags are evaluated against all tags on a scenario, including:
|
|
424
|
-
|
|
425
|
-
- Tags inherited from the Feature block (Gherkin)
|
|
426
|
-
- Tags on the Scenario / Scenario Outline block
|
|
427
|
-
- Tags on individual Examples tables
|
|
428
|
-
- Inline `<!-- tags: -->` comments in Markdown (see below)
|
|
429
|
-
- **Path-based auto-tags** — directory segments prefixed with `@` are automatically applied as tags to all scenarios in that directory:
|
|
430
|
-
|
|
431
|
-
```
|
|
432
|
-
specs/
|
|
433
|
-
@smoke/
|
|
434
|
-
login.feature ← all scenarios get tag 'smoke'
|
|
435
|
-
@regression/
|
|
436
|
-
@slow/
|
|
437
|
-
checkout.feature ← all scenarios get tags 'regression' and 'slow'
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
```bash
|
|
441
|
-
ado-sync push --tags "@smoke" # only push specs/@smoke/** scenarios
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
### Tags in Markdown
|
|
445
|
-
|
|
446
|
-
Add a `<!-- tags: -->` HTML comment anywhere inside a scenario block to assign tags:
|
|
447
|
-
|
|
448
|
-
```markdown
|
|
449
|
-
### Login with valid credentials
|
|
450
|
-
@tc:1042
|
|
451
|
-
<!-- tags: @smoke, @regression -->
|
|
452
|
-
|
|
453
|
-
Steps:
|
|
454
|
-
1. Navigate to the login page
|
|
455
|
-
2. Enter valid credentials
|
|
456
|
-
3. Click Login
|
|
457
|
-
|
|
458
|
-
Expected results:
|
|
459
|
-
- Dashboard is shown
|
|
460
|
-
|
|
461
|
-
---
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
Path-based auto-tagging also works for Markdown files — placing them inside an `@smoke/` folder automatically adds the `smoke` tag.
|
|
465
|
-
|
|
466
|
-
---
|
|
467
|
-
|
|
468
|
-
## Spec file formats
|
|
469
|
-
|
|
470
|
-
### Gherkin `.feature`
|
|
471
|
-
|
|
472
|
-
Standard Gherkin syntax is supported, including:
|
|
473
|
-
- `Feature` / `Scenario` / `Scenario Outline`
|
|
474
|
-
- `Given` / `When` / `Then` / `And` / `But`
|
|
475
|
-
- Feature-level and scenario-level tags
|
|
476
|
-
- `Scenario Outline` with `Examples` tables — creates a **single** parametrized Test Case in Azure (data table included), not one TC per row
|
|
477
|
-
|
|
478
|
-
```gherkin
|
|
479
|
-
Feature: Checkout
|
|
480
|
-
|
|
481
|
-
@smoke
|
|
482
|
-
Scenario: Add item and complete checkout
|
|
483
|
-
Given I am logged in as "standard_user"
|
|
484
|
-
When I add "Sauce Labs Backpack" to the cart
|
|
485
|
-
And I proceed through checkout with name "Test User" and zip "12345"
|
|
486
|
-
Then I see the order confirmation page
|
|
487
|
-
|
|
488
|
-
Scenario Outline: Checkout with different users
|
|
489
|
-
Given I am logged in as "<user>"
|
|
490
|
-
When I complete a checkout
|
|
491
|
-
Then the result is "<result>"
|
|
492
|
-
|
|
493
|
-
Examples:
|
|
494
|
-
| user | result |
|
|
495
|
-
| standard_user | success |
|
|
496
|
-
| performance_glitch_user | success |
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Markdown `.md`
|
|
500
|
-
|
|
501
|
-
Each `### heading` is treated as one test case. The file can contain any number of scenarios separated by `---`.
|
|
502
|
-
|
|
503
|
-
```markdown
|
|
504
|
-
# My Feature Test Plan
|
|
505
|
-
|
|
506
|
-
## Test scenarios
|
|
507
|
-
|
|
508
|
-
### Login with valid credentials
|
|
509
|
-
<!-- tags: @smoke -->
|
|
134
|
+
See [docs/publish-test-results.md](docs/publish-test-results.md) for full reference.
|
|
510
135
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
Steps:
|
|
514
|
-
1. Navigate to https://example.com/login
|
|
515
|
-
2. Enter username "admin" and password "secret"
|
|
516
|
-
3. Click the Login button
|
|
517
|
-
|
|
518
|
-
Expected results:
|
|
519
|
-
- The dashboard page is shown
|
|
520
|
-
- The username appears in the top navigation
|
|
521
|
-
|
|
522
|
-
---
|
|
523
|
-
|
|
524
|
-
### Login with invalid credentials
|
|
525
|
-
|
|
526
|
-
Steps:
|
|
527
|
-
1. Navigate to https://example.com/login
|
|
528
|
-
2. Enter username "wrong" and password "wrong"
|
|
529
|
-
3. Click the Login button
|
|
530
|
-
|
|
531
|
-
Expected results:
|
|
532
|
-
- An error message "Invalid credentials" is displayed
|
|
533
|
-
- The user remains on the login page
|
|
534
|
-
|
|
535
|
-
---
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
Sections recognised: `Steps:`, `Expected results:` (case-insensitive). All other prose is captured as the test case description. Heading number prefixes (`### 1. Title` or `### Title`) are both supported.
|
|
539
|
-
|
|
540
|
-
#### Playwright test-plan markdown
|
|
541
|
-
|
|
542
|
-
Markdown `.md` files generated by the [Playwright MCP agent](https://playwright.dev/docs/test-agents) are fully supported as a local spec source. Set `local.type: "markdown"` and point `local.include` at the generated files to sync them into Azure DevOps as Test Cases.
|
|
543
|
-
|
|
544
|
-
---
|
|
545
|
-
|
|
546
|
-
### CSV `.csv`
|
|
547
|
-
|
|
548
|
-
Set `local.type: "csv"` to parse Azure DevOps / SpecSync tabular CSV exports.
|
|
549
|
-
|
|
550
|
-
**Expected column layout (9 columns):**
|
|
551
|
-
|
|
552
|
-
| Col | Field | Description |
|
|
553
|
-
|-----|-------|-------------|
|
|
554
|
-
| A (0) | ID | Azure Test Case ID — empty for new cases, filled in after first push |
|
|
555
|
-
| B (1) | Work Item Type | Always `Test Case` (ignored) |
|
|
556
|
-
| C (2) | Title | `Scenario: My test` or just `My test` — non-empty on the header row, empty on step rows |
|
|
557
|
-
| D (3) | Test Step | Step number (1, 2, …) — empty on the header row |
|
|
558
|
-
| E (4) | Step Action | Step text, optionally prefixed with a Gherkin keyword (`Given`, `When`, `Then`, …) |
|
|
559
|
-
| F (5) | Step Expected | Expected result text (optional) |
|
|
560
|
-
| G–I (6–8) | Area Path, Assigned To, State | Preserved but not used during parse |
|
|
561
|
-
|
|
562
|
-
**Example CSV:**
|
|
563
|
-
|
|
564
|
-
```csv
|
|
565
|
-
ID,Work Item Type,Title,Test Step,Step Action,Step Expected,Area Path,Assigned To,State
|
|
566
|
-
"","Test Case","Scenario: Login happy path",,,,...
|
|
567
|
-
,,,,,"1","Given I open the app","",,...
|
|
568
|
-
,,,,,"2","When I enter valid credentials","Then I see the dashboard",...
|
|
569
|
-
"","Test Case","Scenario: Login with bad password",,,,...
|
|
570
|
-
,,,,,"1","Given I enter wrong credentials","Then I see an error",...
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
The first row (column headers) is always skipped automatically.
|
|
574
|
-
|
|
575
|
-
**ID writeback:** After the first push, the numeric TC ID is written into **column A** of the matching title row.
|
|
576
|
-
|
|
577
|
-
> **Note:** Pull (`ado-sync pull`) is **not supported** for CSV files — these files are typically generated by external tools (e.g. the Playwright planner agent) and are not hand-authored. Only push is supported.
|
|
578
|
-
|
|
579
|
-
**Config example:**
|
|
580
|
-
|
|
581
|
-
```json
|
|
582
|
-
{
|
|
583
|
-
"local": {
|
|
584
|
-
"type": "csv",
|
|
585
|
-
"include": "specs/**/*.csv"
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
---
|
|
591
|
-
|
|
592
|
-
### Excel `.xlsx`
|
|
593
|
-
|
|
594
|
-
Set `local.type: "excel"` to parse Azure DevOps / SpecSync tabular Excel exports.
|
|
595
|
-
|
|
596
|
-
The xlsx format uses the same 9-column layout as CSV above. ado-sync reads the raw worksheet XML directly (no heavyweight xlsx library required) and handles both plain-string (`t="str"`) and rich-text inline-string (`t="inlineStr"`) cells produced by Azure DevOps exports.
|
|
597
|
-
|
|
598
|
-
**ID writeback:** After the first push, the numeric TC ID is written into **cell A** of the matching title row and the file is repacked in place.
|
|
599
|
-
|
|
600
|
-
> **Note:** Pull (`ado-sync pull`) is **not supported** for Excel files for the same reason as CSV — these are externally generated files. Only push is supported.
|
|
601
|
-
|
|
602
|
-
**Config example:**
|
|
603
|
-
|
|
604
|
-
```json
|
|
605
|
-
{
|
|
606
|
-
"local": {
|
|
607
|
-
"type": "excel",
|
|
608
|
-
"include": "specs/**/*.xlsx"
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
---
|
|
614
|
-
|
|
615
|
-
## Work item linking
|
|
616
|
-
|
|
617
|
-
Tags matching a configured `links` prefix are turned into Azure DevOps work item relations on the Test Case.
|
|
618
|
-
|
|
619
|
-
### Config
|
|
620
|
-
|
|
621
|
-
```json
|
|
622
|
-
{
|
|
623
|
-
"sync": {
|
|
624
|
-
"links": [
|
|
625
|
-
{ "prefix": "story", "relationship": "System.LinkTypes.Related" },
|
|
626
|
-
{ "prefix": "bug", "relationship": "System.LinkTypes.Related" },
|
|
627
|
-
{ "prefix": "req", "relationship": "Microsoft.VSTS.Common.TestedBy-Reverse" }
|
|
628
|
-
]
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
### Usage in Gherkin
|
|
634
|
-
|
|
635
|
-
```gherkin
|
|
636
|
-
@tc:1042 @story:555 @bug:789
|
|
637
|
-
Scenario: User can add items to cart
|
|
638
|
-
...
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### Usage in Markdown
|
|
642
|
-
|
|
643
|
-
```markdown
|
|
644
|
-
### User can add items to cart
|
|
645
|
-
@tc:1042
|
|
646
|
-
<!-- tags: @story:555, @bug:789 -->
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
On each `push`, relations are synced: new links are added and stale links (whose tag was removed) are deleted. The `relationship` value is the ADO relation type; `"System.LinkTypes.Related"` is the default if omitted.
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
## Suite hierarchy
|
|
654
|
-
|
|
655
|
-
By default, all Test Cases are created in a single flat suite (`suiteMapping: "flat"`). Setting `suiteMapping: "byFolder"` mirrors the folder structure of your spec files as nested child suites in Azure.
|
|
656
|
-
|
|
657
|
-
```json
|
|
658
|
-
{
|
|
659
|
-
"testPlan": {
|
|
660
|
-
"id": 1234,
|
|
661
|
-
"suiteId": 5678,
|
|
662
|
-
"suiteMapping": "byFolder"
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
```
|
|
668
|
-
specs/
|
|
669
|
-
login/
|
|
670
|
-
basic.feature → suite "login" → TC "Successful login"
|
|
671
|
-
checkout/
|
|
672
|
-
happy.feature → suite "checkout" → TC "Add item and checkout"
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
Child suites are created automatically if they do not exist. The suite hierarchy is re-used across runs.
|
|
676
|
-
|
|
677
|
-
---
|
|
678
|
-
|
|
679
|
-
## Conflict detection
|
|
680
|
-
|
|
681
|
-
ado-sync uses a local state cache (`.ado-sync-state.json`) to detect conflicts — cases where **both** the local file and the Azure Test Case were changed since the last sync.
|
|
682
|
-
|
|
683
|
-
The `sync.conflictAction` setting controls what happens:
|
|
684
|
-
|
|
685
|
-
| Value | Behaviour |
|
|
686
|
-
|-------|-----------|
|
|
687
|
-
| `"overwrite"` *(default)* | Push the local version to Azure, overwriting the remote change. |
|
|
688
|
-
| `"skip"` | Emit a `!` conflict result for the scenario and leave both sides unchanged. |
|
|
689
|
-
| `"fail"` | Throw an error listing all conflicting scenarios and abort the sync. |
|
|
690
|
-
|
|
691
|
-
```json
|
|
692
|
-
{ "sync": { "conflictAction": "skip" } }
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
---
|
|
696
|
-
|
|
697
|
-
## Local state cache
|
|
698
|
-
|
|
699
|
-
ado-sync writes a `.ado-sync-state.json` file in the same directory as your config file. This cache stores SHA-256 hashes of the last-synced title, steps, and description for each Test Case, plus the Azure `changedDate` timestamp.
|
|
700
|
-
|
|
701
|
-
**Commit this file to version control** so all team members and CI share the same last-synced state, enabling accurate conflict detection across machines.
|
|
702
|
-
|
|
703
|
-
The cache also speeds up `push` — if neither the local file nor the Azure `changedDate` has changed since the last sync, the Azure API call is skipped entirely and the scenario is marked `skipped`.
|
|
704
|
-
|
|
705
|
-
---
|
|
706
|
-
|
|
707
|
-
## Build server / CI mode
|
|
708
|
-
|
|
709
|
-
Set `sync.disableLocalChanges: true` to prevent ado-sync from writing back to local files. In this mode:
|
|
710
|
-
|
|
711
|
-
- `push` — creates and updates Test Cases in Azure, but does **not** write ID tags back to local files.
|
|
712
|
-
- `pull` — computes what would change but does **not** modify local files (behaves like `--dry-run`).
|
|
713
|
-
|
|
714
|
-
```json
|
|
715
|
-
{ "sync": { "disableLocalChanges": true } }
|
|
716
|
-
```
|
|
136
|
+
### `--config-override`
|
|
717
137
|
|
|
718
|
-
|
|
138
|
+
All commands accept `--config-override path=value` to set config values without editing the file:
|
|
719
139
|
|
|
720
140
|
```bash
|
|
141
|
+
ado-sync push --config-override testPlan.id=9999
|
|
721
142
|
ado-sync push --config-override sync.disableLocalChanges=true
|
|
722
143
|
```
|
|
723
144
|
|
|
724
145
|
---
|
|
725
146
|
|
|
726
|
-
## Removed scenario detection
|
|
727
|
-
|
|
728
|
-
When a scenario is deleted from a local spec file but its Test Case still exists in the Azure suite, ado-sync detects this on the next `push` and appends the tag `ado-sync:removed` to the Azure Test Case (without deleting it). A `−` removed line is printed in the output.
|
|
729
|
-
|
|
730
|
-
To completely remove the Test Case from Azure, delete it manually in Test Plans after reviewing.
|
|
731
|
-
|
|
732
|
-
---
|
|
733
|
-
|
|
734
|
-
## YAML config example
|
|
735
|
-
|
|
736
|
-
```yaml
|
|
737
|
-
orgUrl: https://dev.azure.com/my-org
|
|
738
|
-
project: MyProject
|
|
739
|
-
|
|
740
|
-
auth:
|
|
741
|
-
type: pat
|
|
742
|
-
token: $AZURE_DEVOPS_TOKEN
|
|
743
|
-
|
|
744
|
-
testPlan:
|
|
745
|
-
id: 1234
|
|
746
|
-
suiteId: 5678
|
|
747
|
-
suiteMapping: byFolder
|
|
748
|
-
|
|
749
|
-
local:
|
|
750
|
-
type: gherkin
|
|
751
|
-
include: specs/**/*.feature
|
|
752
|
-
exclude:
|
|
753
|
-
- specs/archive/**
|
|
754
|
-
|
|
755
|
-
sync:
|
|
756
|
-
tagPrefix: tc
|
|
757
|
-
areaPath: "MyProject\\QA Team"
|
|
758
|
-
conflictAction: skip
|
|
759
|
-
disableLocalChanges: false
|
|
760
|
-
links:
|
|
761
|
-
- prefix: story
|
|
762
|
-
relationship: System.LinkTypes.Related
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
---
|
|
766
|
-
|
|
767
|
-
## Environment variables
|
|
768
|
-
|
|
769
|
-
| Variable | Description |
|
|
770
|
-
|----------|-------------|
|
|
771
|
-
| `AZURE_DEVOPS_TOKEN` | PAT or access token. Reference it in config with `"$AZURE_DEVOPS_TOKEN"`. |
|
|
772
|
-
| Any name | Any env var can be used — set `auth.token` to `"$MY_VAR_NAME"`. |
|
|
773
|
-
|
|
774
|
-
You can also use a `.env` file in the working directory. It is loaded automatically.
|
|
775
|
-
|
|
776
|
-
---
|
|
777
|
-
|
|
778
147
|
## Output symbols
|
|
779
148
|
|
|
780
149
|
```
|
|
@@ -789,68 +158,67 @@ You can also use a `.env` file in the working directory. It is loaded automatica
|
|
|
789
158
|
|
|
790
159
|
---
|
|
791
160
|
|
|
792
|
-
##
|
|
161
|
+
## Documentation
|
|
793
162
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
163
|
+
| Topic | Link |
|
|
164
|
+
|-------|------|
|
|
165
|
+
| Full configuration reference | [docs/configuration.md](docs/configuration.md) |
|
|
166
|
+
| Spec file formats (Gherkin, Markdown, CSV, Excel) | [docs/spec-formats.md](docs/spec-formats.md) |
|
|
167
|
+
| Advanced features (format, state, fieldUpdates, customizations, attachments, CI mode) | [docs/advanced.md](docs/advanced.md) |
|
|
168
|
+
| Publishing test results | [docs/publish-test-results.md](docs/publish-test-results.md) |
|
|
799
169
|
|
|
800
|
-
|
|
801
|
-
export AZURE_DEVOPS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
802
|
-
|
|
803
|
-
# Preview what will be created
|
|
804
|
-
ado-sync push --dry-run
|
|
170
|
+
---
|
|
805
171
|
|
|
806
|
-
|
|
807
|
-
ado-sync push
|
|
808
|
-
```
|
|
172
|
+
## Workflow examples
|
|
809
173
|
|
|
810
174
|
### Day-to-day: local changes first
|
|
811
175
|
|
|
812
176
|
```bash
|
|
813
|
-
# Edit your .feature or .md files
|
|
814
|
-
# Then push changes to Azure DevOps
|
|
177
|
+
# Edit your .feature or .md files, then push
|
|
815
178
|
ado-sync push
|
|
816
179
|
```
|
|
817
180
|
|
|
818
181
|
### Day-to-day: Azure changes first
|
|
819
182
|
|
|
820
183
|
```bash
|
|
821
|
-
# Someone edited a
|
|
822
|
-
# Pull the changes into your local files
|
|
184
|
+
# Someone edited a Test Case in the Azure DevOps UI
|
|
823
185
|
ado-sync pull
|
|
824
186
|
```
|
|
825
187
|
|
|
826
|
-
###
|
|
827
|
-
|
|
828
|
-
```bash
|
|
829
|
-
ado-sync status
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
### CI pipeline (no file writeback)
|
|
188
|
+
### CI pipeline
|
|
833
189
|
|
|
834
190
|
```yaml
|
|
835
|
-
# GitHub Actions
|
|
191
|
+
# GitHub Actions
|
|
836
192
|
- name: Sync test cases to Azure DevOps
|
|
837
193
|
run: ado-sync push --config-override sync.disableLocalChanges=true
|
|
838
194
|
env:
|
|
839
195
|
AZURE_DEVOPS_TOKEN: ${{ secrets.AZURE_DEVOPS_TOKEN }}
|
|
196
|
+
|
|
197
|
+
- name: Publish test results
|
|
198
|
+
run: ado-sync publish-test-results --testResult results/test.trx
|
|
199
|
+
env:
|
|
200
|
+
AZURE_DEVOPS_TOKEN: ${{ secrets.AZURE_DEVOPS_TOKEN }}
|
|
840
201
|
```
|
|
841
202
|
|
|
842
|
-
###
|
|
203
|
+
### Check for drift before a PR
|
|
843
204
|
|
|
844
205
|
```bash
|
|
845
|
-
|
|
846
|
-
ado-sync push --config-override testPlan.id=2001
|
|
847
|
-
|
|
848
|
-
# Push to production plan
|
|
849
|
-
ado-sync push --config-override testPlan.id=3001
|
|
206
|
+
ado-sync status
|
|
850
207
|
```
|
|
851
208
|
|
|
852
209
|
---
|
|
853
210
|
|
|
211
|
+
## Environment variables
|
|
212
|
+
|
|
213
|
+
| Variable | Description |
|
|
214
|
+
|----------|-------------|
|
|
215
|
+
| `AZURE_DEVOPS_TOKEN` | PAT or access token. Reference in config with `"$AZURE_DEVOPS_TOKEN"`. |
|
|
216
|
+
| Any name | Any env var — set `auth.token` to `"$MY_VAR_NAME"`. |
|
|
217
|
+
|
|
218
|
+
A `.env` file in the working directory is loaded automatically.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
854
222
|
## Troubleshooting
|
|
855
223
|
|
|
856
224
|
**`No config file found`**
|
|
@@ -860,28 +228,25 @@ Run `ado-sync init` or pass `-c path/to/config.json`.
|
|
|
860
228
|
Your config references `$X` in `auth.token` but the variable is not exported. Run `export X=...` or add it to a `.env` file.
|
|
861
229
|
|
|
862
230
|
**Test Case created but ID not written back**
|
|
863
|
-
Check that the
|
|
231
|
+
Check that the file is writable, or that `sync.disableLocalChanges` is not `true`.
|
|
864
232
|
|
|
865
233
|
**`Test case #N not found in Azure DevOps`**
|
|
866
234
|
The test case was deleted in Azure. Remove the ID tag from the local file to recreate it, or restore the test case in Azure.
|
|
867
235
|
|
|
868
236
|
**`Failed to parse <file>`**
|
|
869
|
-
Gherkin syntax error
|
|
237
|
+
Gherkin syntax error. Run `npx cucumber-js --dry-run` to identify the problem line.
|
|
870
238
|
|
|
871
239
|
**Changes not detected on push**
|
|
872
|
-
The comparison uses title + steps + description.
|
|
240
|
+
The comparison uses title + steps + description. Touch any step to force an update, or reset the cache by deleting `.ado-sync-state.json`.
|
|
873
241
|
|
|
874
242
|
**Conflict detected unexpectedly**
|
|
875
|
-
Delete `.ado-sync-state.json` to reset the cache. The next push
|
|
876
|
-
|
|
877
|
-
**`init` generated JSON instead of YAML**
|
|
878
|
-
Pass the filename as a positional argument: `ado-sync init ado-sync.yml` (not `--output`).
|
|
243
|
+
Delete `.ado-sync-state.json` to reset the cache. The next push re-populates it from Azure.
|
|
879
244
|
|
|
880
245
|
**CSV/Excel IDs not written back**
|
|
881
|
-
Ensure the file is not open in
|
|
246
|
+
Ensure the file is not open in another application. Check that `sync.disableLocalChanges` is not `true`.
|
|
882
247
|
|
|
883
248
|
**Excel file not parsed / `No worksheet found`**
|
|
884
|
-
ado-sync looks for `xl/worksheets/sheet.xml` or `xl/worksheets/sheet1.xml` inside the xlsx ZIP.
|
|
249
|
+
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.
|
|
885
250
|
|
|
886
251
|
**Pull has no effect on CSV/Excel files**
|
|
887
|
-
Pull is not supported for CSV and Excel
|
|
252
|
+
Pull is not supported for CSV and Excel — only push. These formats are managed by external tools.
|