ado-sync 0.1.2

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 ADDED
@@ -0,0 +1,527 @@
1
+ # ado-sync
2
+
3
+ Bidirectional sync between local test specs and Azure DevOps Test Cases.
4
+
5
+ Supports **Cucumber / Gherkin** `.feature` files and **prose Markdown** `.md` spec files.
6
+ Inspired by [SpecSync](https://docs.specsolutions.eu/specsync/).
7
+
8
+ ---
9
+
10
+ ## How it works
11
+
12
+ ```
13
+ Local files ado-sync Azure DevOps
14
+ ────────────── ─────────────── ────────────────
15
+ .feature files ── push ──► create / update ──────────► Test Cases
16
+ .md spec files ◄── pull ── apply changes ◄────────── (Work Items)
17
+ write ID back
18
+ @tc:12345 / <!-- tc: 12345 -->
19
+ ```
20
+
21
+ 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 or comment. 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.
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ npm install -g ado-sync
29
+ # or use locally
30
+ npx ado-sync --help
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Quick start
36
+
37
+ ### 1. Generate a config file
38
+
39
+ ```bash
40
+ ado-sync init
41
+ ```
42
+
43
+ This creates `ado-sync.json` in the current directory.
44
+
45
+ ### 2. Edit the config
46
+
47
+ ```json
48
+ {
49
+ "orgUrl": "https://dev.azure.com/my-org",
50
+ "project": "MyProject",
51
+ "auth": {
52
+ "type": "pat",
53
+ "token": "$AZURE_DEVOPS_TOKEN"
54
+ },
55
+ "testPlan": {
56
+ "id": 1234,
57
+ "suiteId": 5678
58
+ },
59
+ "local": {
60
+ "type": "gherkin",
61
+ "include": "specs/**/*.feature"
62
+ },
63
+ "sync": {
64
+ "tagPrefix": "tc"
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### 3. Set your token
70
+
71
+ ```bash
72
+ export AZURE_DEVOPS_TOKEN=your_personal_access_token
73
+ ```
74
+
75
+ ### 4. Push
76
+
77
+ ```bash
78
+ ado-sync push
79
+ ```
80
+
81
+ New scenarios are created in Azure DevOps. Their IDs are written back into your local files automatically.
82
+
83
+ ---
84
+
85
+ ## CLI reference
86
+
87
+ ```
88
+ ado-sync [options] [command]
89
+
90
+ Options:
91
+ -c, --config <path> Path to config file (default: ado-sync.json)
92
+ -V, --version Print version
93
+ -h, --help Show help
94
+
95
+ Commands:
96
+ init [options] Generate a starter config file
97
+ push [options] Push local specs to Azure DevOps
98
+ pull [options] Pull updates from Azure DevOps into local files
99
+ status Show what would change without making any modifications
100
+ help [command] Help for a specific command
101
+ ```
102
+
103
+ ### `init`
104
+
105
+ ```bash
106
+ ado-sync init
107
+ ado-sync init --output path/to/my-config.json
108
+ ```
109
+
110
+ Creates a starter `ado-sync.json`. Safe — will not overwrite an existing file.
111
+
112
+ ---
113
+
114
+ ### `push`
115
+
116
+ ```bash
117
+ ado-sync push
118
+ ado-sync push --dry-run
119
+ ado-sync push --tags "@smoke"
120
+ ado-sync push --tags "@smoke and not @wip"
121
+ ado-sync -c other-config.json push
122
+ ```
123
+
124
+ For every scenario / heading in your local spec files:
125
+
126
+ | Scenario state | Action |
127
+ |----------------|--------|
128
+ | No ID tag yet | Creates a new Test Case, writes ID back to file |
129
+ | Has ID tag, no changes | Skipped |
130
+ | Has ID tag, title or steps changed | Updates the existing Test Case |
131
+ | Has ID tag but Test Case deleted in Azure | Reported as error |
132
+
133
+ `--dry-run` prints what would happen without modifying anything.
134
+
135
+ ---
136
+
137
+ ### `pull`
138
+
139
+ ```bash
140
+ ado-sync pull
141
+ ado-sync pull --dry-run
142
+ ado-sync pull --tags "@smoke"
143
+ ```
144
+
145
+ For every locally-linked scenario (has an ID tag):
146
+
147
+ | Remote state | Action |
148
+ |--------------|--------|
149
+ | Title or steps changed in Azure | Updates the local file |
150
+ | No changes | Skipped |
151
+ | Test Case not found | Reported as error |
152
+
153
+ `--dry-run` prints what would change without modifying local files.
154
+
155
+ ---
156
+
157
+ ### `status`
158
+
159
+ ```bash
160
+ ado-sync status
161
+ ado-sync status --tags "@smoke"
162
+ ```
163
+
164
+ Compares local specs against Azure DevOps and prints a diff — no changes made. Equivalent to `push --dry-run`.
165
+
166
+ ---
167
+
168
+ ## Config file reference
169
+
170
+ Config files can be JSON (`.json`) or YAML (`.yml` / `.yaml`).
171
+
172
+ ```json
173
+ {
174
+ "orgUrl": "https://dev.azure.com/YOUR_ORG",
175
+ "project": "YOUR_PROJECT",
176
+
177
+ "auth": {
178
+ "type": "pat",
179
+ "token": "$AZURE_DEVOPS_TOKEN",
180
+ "applicationIdURI": ""
181
+ },
182
+
183
+ "testPlan": {
184
+ "id": 1234,
185
+ "suiteId": 5678
186
+ },
187
+
188
+ "local": {
189
+ "type": "gherkin",
190
+ "include": "specs/**/*.feature",
191
+ "exclude": []
192
+ },
193
+
194
+ "sync": {
195
+ "tagPrefix": "tc",
196
+ "areaPath": "MyProject\\Team A",
197
+ "iterationPath": "MyProject\\Sprint 1"
198
+ }
199
+ }
200
+ ```
201
+
202
+ ### `orgUrl`
203
+ Azure DevOps organisation URL. Format: `https://dev.azure.com/YOUR_ORG`.
204
+
205
+ ### `project`
206
+ Azure DevOps project name.
207
+
208
+ ### `auth`
209
+
210
+ | Field | Description |
211
+ |-------|-------------|
212
+ | `type` | `"pat"` · `"accessToken"` · `"managedIdentity"` |
213
+ | `token` | PAT or access token value. Prefix with `$` to read from an environment variable (e.g. `"$AZURE_DEVOPS_TOKEN"`). |
214
+ | `applicationIdURI` | Required only when `type` is `"managedIdentity"`. |
215
+
216
+ **PAT permissions required:** Test Management (Read & Write), Work Items (Read & Write).
217
+
218
+ ### `testPlan`
219
+
220
+ | Field | Description |
221
+ |-------|-------------|
222
+ | `id` | ID of the Azure DevOps Test Plan new test cases are added to. |
223
+ | `suiteId` | *(Optional)* ID of the Test Suite within the plan. Defaults to the plan's root suite. |
224
+
225
+ ### `local`
226
+
227
+ | Field | Description |
228
+ |-------|-------------|
229
+ | `type` | `"gherkin"` for `.feature` files · `"markdown"` for prose `.md` specs. |
230
+ | `include` | Glob pattern(s) relative to the config file. String or array. |
231
+ | `exclude` | *(Optional)* Glob pattern(s) to exclude. String or array. |
232
+
233
+ ### `sync`
234
+
235
+ | Field | Default | Description |
236
+ |-------|---------|-------------|
237
+ | `tagPrefix` | `"tc"` | Prefix used in ID tags. See [ID tags](#id-tags) below. |
238
+ | `areaPath` | *(none)* | Area path for newly created Test Cases. |
239
+ | `iterationPath` | *(none)* | Iteration path for newly created Test Cases. |
240
+
241
+ ---
242
+
243
+ ## ID tags
244
+
245
+ 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.
246
+
247
+ ### Gherkin
248
+
249
+ The ID tag is inserted on its own line immediately above the `Scenario:` keyword, using the `@{tagPrefix}:{id}` convention:
250
+
251
+ ```gherkin
252
+ Feature: Login
253
+
254
+ @tc:1042
255
+ Scenario: Successful login with valid credentials
256
+ Given I am on the login page
257
+ When I enter username "standard_user" and password "secret_sauce"
258
+ Then I am redirected to the inventory page
259
+ ```
260
+
261
+ Tags from the feature or scenario are preserved. The ID tag is always kept as the first tag on the line or alongside existing tags.
262
+
263
+ ### Markdown
264
+
265
+ The ID comment is inserted on the line immediately after the `### heading`, using `<!-- {tagPrefix}: {id} -->`:
266
+
267
+ ```markdown
268
+ ### 1. Login (happy path)
269
+ <!-- tc: 1042 -->
270
+
271
+ Assumption: Fresh browser session.
272
+
273
+ Steps:
274
+ 1. Enter username `standard_user` in the Username field.
275
+ 2. Enter password `secret_sauce` in the Password field.
276
+ 3. Click `Login`.
277
+
278
+ Expected results:
279
+ - User is redirected to `/inventory.html`.
280
+ - Product list is visible.
281
+
282
+ ---
283
+ ```
284
+
285
+ The comment is invisible when the Markdown is rendered.
286
+
287
+ ### Custom prefix
288
+
289
+ Change the `sync.tagPrefix` in your config to use a different prefix:
290
+
291
+ ```json
292
+ { "sync": { "tagPrefix": "azure" } }
293
+ ```
294
+
295
+ Result:
296
+ - Gherkin: `@azure:1042`
297
+ - Markdown: `<!-- azure: 1042 -->`
298
+
299
+ > **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.
300
+
301
+ ---
302
+
303
+ ## Tag filtering
304
+
305
+ All commands accept a `--tags` option to limit which scenarios are synced. The syntax is the standard Cucumber tag expression language.
306
+
307
+ ```bash
308
+ # Only sync scenarios tagged @smoke
309
+ ado-sync push --tags "@smoke"
310
+
311
+ # Sync everything except @wip
312
+ ado-sync push --tags "not @wip"
313
+
314
+ # Combine tags with and / or
315
+ ado-sync push --tags "@smoke and not @slow"
316
+ ado-sync pull --tags "@regression or @critical"
317
+ ```
318
+
319
+ Tags are evaluated against all tags on a scenario, including:
320
+ - Tags inherited from the Feature block
321
+ - Tags on the Scenario / Scenario Outline block
322
+ - Tags on individual Examples tables
323
+ - **Path-based auto-tags** — directory segments prefixed with `@` are automatically applied as tags to all scenarios in that directory:
324
+
325
+ ```
326
+ specs/
327
+ @smoke/
328
+ login.feature ← all scenarios get tag 'smoke'
329
+ @regression/
330
+ @slow/
331
+ checkout.feature ← all scenarios get tags 'regression' and 'slow'
332
+ ```
333
+
334
+ This lets you filter by folder without adding tags to every scenario manually:
335
+ ```bash
336
+ ado-sync push --tags "@smoke" # only push specs/@smoke/** scenarios
337
+ ```
338
+
339
+ > Path-based auto-tagging applies to Gherkin files only. Markdown specs do not have a tag system, so `--tags` has no effect on them.
340
+
341
+ ---
342
+
343
+ ## Spec file formats
344
+
345
+ ### Gherkin `.feature`
346
+
347
+ Standard Gherkin syntax is supported, including:
348
+ - `Feature` / `Scenario` / `Scenario Outline`
349
+ - `Given` / `When` / `Then` / `And` / `But`
350
+ - Feature-level and scenario-level tags
351
+ - `Examples` tables — each row becomes a separate Azure Test Case
352
+
353
+ ```gherkin
354
+ Feature: Checkout
355
+
356
+ @smoke
357
+ Scenario: Add item and complete checkout
358
+ Given I am logged in as "standard_user"
359
+ When I add "Sauce Labs Backpack" to the cart
360
+ And I proceed through checkout with name "Test User" and zip "12345"
361
+ Then I see the order confirmation page
362
+
363
+ Scenario Outline: Checkout with different users
364
+ Given I am logged in as "<user>"
365
+ When I complete a checkout
366
+ Then the result is "<result>"
367
+
368
+ Examples:
369
+ | user | result |
370
+ | standard_user | success |
371
+ | performance_glitch_user | success |
372
+ ```
373
+
374
+ ### Markdown `.md`
375
+
376
+ Each `### heading` is treated as one test case. The file can contain any number of scenarios separated by `---`.
377
+
378
+ ```markdown
379
+ # My Feature Test Plan
380
+
381
+ ## Test scenarios
382
+
383
+ ### Login with valid credentials
384
+
385
+ Steps:
386
+ 1. Navigate to https://example.com/login
387
+ 2. Enter username "admin" and password "secret"
388
+ 3. Click the Login button
389
+
390
+ Expected results:
391
+ - The dashboard page is shown
392
+ - The username appears in the top navigation
393
+
394
+ ---
395
+
396
+ ### Login with invalid credentials
397
+
398
+ Steps:
399
+ 1. Navigate to https://example.com/login
400
+ 2. Enter username "wrong" and password "wrong"
401
+ 3. Click the Login button
402
+
403
+ Expected results:
404
+ - An error message "Invalid credentials" is displayed
405
+ - The user remains on the login page
406
+
407
+ ---
408
+ ```
409
+
410
+ Headings can optionally have a number prefix (`### 1. Title` or `### Title` — both work).
411
+ Sections recognised: `Steps:`, `Expected results:` (case-insensitive). All other prose is captured as the test case description.
412
+
413
+ ---
414
+
415
+ ## YAML config example
416
+
417
+ ```yaml
418
+ orgUrl: https://dev.azure.com/my-org
419
+ project: MyProject
420
+
421
+ auth:
422
+ type: pat
423
+ token: $AZURE_DEVOPS_TOKEN
424
+
425
+ testPlan:
426
+ id: 1234
427
+ suiteId: 5678
428
+
429
+ local:
430
+ type: markdown
431
+ include:
432
+ - specs/**/*.md
433
+ exclude:
434
+ - specs/archive/**
435
+
436
+ sync:
437
+ tagPrefix: tc
438
+ areaPath: "MyProject\\QA Team"
439
+ ```
440
+
441
+ ---
442
+
443
+ ## Environment variables
444
+
445
+ | Variable | Description |
446
+ |----------|-------------|
447
+ | `AZURE_DEVOPS_TOKEN` | PAT or access token. Reference it in config with `"$AZURE_DEVOPS_TOKEN"`. |
448
+ | Any name | Any env var can be used — set `auth.token` to `"$MY_VAR_NAME"`. |
449
+
450
+ You can also use a `.env` file in the working directory. It is loaded automatically.
451
+
452
+ ---
453
+
454
+ ## Output symbols
455
+
456
+ ```
457
+ + created — new Test Case created in Azure DevOps
458
+ ~ updated — existing Test Case updated
459
+ ↓ pulled — local file updated from Azure DevOps
460
+ = skipped — no changes detected
461
+ ! conflict — both sides changed (manual resolution needed)
462
+ ✗ error — something went wrong (see detail message)
463
+ ```
464
+
465
+ ---
466
+
467
+ ## Workflow examples
468
+
469
+ ### First-time setup
470
+
471
+ ```bash
472
+ # Generate config
473
+ ado-sync init
474
+
475
+ # Edit ado-sync.json with your org, project, plan ID, and token
476
+ export AZURE_DEVOPS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
477
+
478
+ # Preview what will be created
479
+ ado-sync push --dry-run
480
+
481
+ # Create test cases in Azure DevOps
482
+ ado-sync push
483
+ ```
484
+
485
+ ### Day-to-day: local changes first
486
+
487
+ ```bash
488
+ # Edit your .feature or .md files locally
489
+ # Then push changes to Azure DevOps
490
+ ado-sync push
491
+ ```
492
+
493
+ ### Day-to-day: Azure changes first
494
+
495
+ ```bash
496
+ # Someone edited a test case in Azure DevOps Test Plans UI
497
+ # Pull the changes into your local files
498
+ ado-sync pull
499
+ ```
500
+
501
+ ### Check for drift before a PR
502
+
503
+ ```bash
504
+ ado-sync status
505
+ ```
506
+
507
+ ---
508
+
509
+ ## Troubleshooting
510
+
511
+ **`No config file found`**
512
+ Run `ado-sync init` or pass `-c path/to/config.json`.
513
+
514
+ **`Environment variable 'X' is not set`**
515
+ Your config references `$X` in `auth.token` but the variable is not exported. Run `export X=...` or add it to a `.env` file.
516
+
517
+ **Test Case created but ID not written back**
518
+ Check that the local file is writable. On the next `push` it will try again.
519
+
520
+ **`Test case #N not found in Azure DevOps`**
521
+ 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.
522
+
523
+ **`Failed to parse <file>`**
524
+ Gherkin syntax error in a `.feature` file. Run `npx cucumber-js --dry-run` to identify the problem line.
525
+
526
+ **Changes not detected on push**
527
+ The comparison is title + step text. Changing only description or area path requires a manual update via `ado-sync push` after touching a step.
@@ -0,0 +1,17 @@
1
+ import * as CoreApi from 'azure-devops-node-api/CoreApi';
2
+ import * as TestPlanApi from 'azure-devops-node-api/TestPlanApi';
3
+ import * as WorkItemTrackingApi from 'azure-devops-node-api/WorkItemTrackingApi';
4
+ import { SyncConfig } from '../types';
5
+ export declare class AzureClient {
6
+ private config;
7
+ private connection;
8
+ private _witApi;
9
+ private _testPlanApi;
10
+ private _coreApi;
11
+ private constructor();
12
+ static create(config: SyncConfig): Promise<AzureClient>;
13
+ private connect;
14
+ getWitApi(): Promise<WorkItemTrackingApi.IWorkItemTrackingApi>;
15
+ getTestPlanApi(): Promise<TestPlanApi.ITestPlanApi>;
16
+ getCoreApi(): Promise<CoreApi.ICoreApi>;
17
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AzureClient = void 0;
37
+ const identity_1 = require("@azure/identity");
38
+ const azdev = __importStar(require("azure-devops-node-api"));
39
+ const azure_devops_node_api_1 = require("azure-devops-node-api");
40
+ class AzureClient {
41
+ config;
42
+ connection;
43
+ _witApi;
44
+ _testPlanApi;
45
+ _coreApi;
46
+ constructor(config) {
47
+ this.config = config;
48
+ }
49
+ static async create(config) {
50
+ const client = new AzureClient(config);
51
+ await client.connect();
52
+ return client;
53
+ }
54
+ async connect() {
55
+ const { orgUrl, auth } = this.config;
56
+ let authHandler;
57
+ if (auth.type === 'managedIdentity') {
58
+ const credential = new identity_1.DefaultAzureCredential();
59
+ const token = await credential.getToken(auth.applicationIdURI);
60
+ authHandler = azdev.getBearerHandler(token.token);
61
+ }
62
+ else if (auth.type === 'accessToken') {
63
+ authHandler = azdev.getBearerHandler(auth.token);
64
+ }
65
+ else {
66
+ // PAT
67
+ authHandler = azdev.getPersonalAccessTokenHandler(auth.token);
68
+ }
69
+ this.connection = new azure_devops_node_api_1.WebApi(orgUrl, authHandler);
70
+ }
71
+ async getWitApi() {
72
+ if (!this._witApi)
73
+ this._witApi = await this.connection.getWorkItemTrackingApi();
74
+ return this._witApi;
75
+ }
76
+ async getTestPlanApi() {
77
+ if (!this._testPlanApi)
78
+ this._testPlanApi = await this.connection.getTestPlanApi();
79
+ return this._testPlanApi;
80
+ }
81
+ async getCoreApi() {
82
+ if (!this._coreApi)
83
+ this._coreApi = await this.connection.getCoreApi();
84
+ return this._coreApi;
85
+ }
86
+ }
87
+ exports.AzureClient = AzureClient;
88
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/azure/client.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8CAAyD;AACzD,6DAA+C;AAC/C,iEAA+C;AAQ/C,MAAa,WAAW;IAMM;IALpB,UAAU,CAAU;IACpB,OAAO,CAA4C;IACnD,YAAY,CAA4B;IACxC,QAAQ,CAAoB;IAEpC,YAA4B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAElD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAkB;QACpC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QAErC,IAAI,WAA4B,CAAC;QAEjC,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,IAAI,iCAAsB,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAiB,CAAC,CAAC;YAChE,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpD,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACvC,WAAW,GAAG,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM;YACN,WAAW,GAAG,KAAK,CAAC,6BAA6B,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,IAAI,8BAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC;QACjF,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,IAAI,CAAC,YAAY,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;QACnF,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AA/CD,kCA+CC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * CRUD operations for Azure DevOps Test Cases (Work Items of type "Test Case").
3
+ *
4
+ * Azure stores test steps as XML in the field:
5
+ * Microsoft.VSTS.TCM.Steps
6
+ *
7
+ * Step XML shape:
8
+ * <steps id="0" last="N">
9
+ * <step id="2" type="ValidateStep">
10
+ * <parameterizedString isformatted="true">&lt;DIV&gt;Action text&lt;/DIV&gt;</parameterizedString>
11
+ * <parameterizedString isformatted="true">&lt;DIV&gt;Expected text&lt;/DIV&gt;</parameterizedString>
12
+ * <description/>
13
+ * </step>
14
+ * </steps>
15
+ */
16
+ import { AzureTestCase, ParsedTest, SyncConfig } from '../types';
17
+ import { AzureClient } from './client';
18
+ export declare function getTestCase(client: AzureClient, id: number): Promise<AzureTestCase | null>;
19
+ export declare function createTestCase(client: AzureClient, test: ParsedTest, config: SyncConfig): Promise<number>;
20
+ export declare function updateTestCase(client: AzureClient, id: number, test: ParsedTest, config: SyncConfig): Promise<void>;
21
+ export declare function updateLocalFromAzure(client: AzureClient, id: number): Promise<AzureTestCase | null>;
22
+ export declare function getTestCasesInSuite(client: AzureClient, config: SyncConfig, suiteId?: number): Promise<AzureTestCase[]>;