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.
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * Gherkin / Cucumber .feature file parser.
4
+ *
5
+ * Uses the modern @cucumber/gherkin generateMessages() API (same approach as
6
+ * playwright-bdd). This produces both the GherkinDocument (AST) and Pickles
7
+ * (compiled, example-substituted scenarios) in a single pass.
8
+ *
9
+ * Pickles are the canonical unit of work — they already have:
10
+ * - Example values substituted in titles and step text
11
+ * - Background steps merged into every scenario
12
+ * - Tags inherited from Feature + Scenario + Examples blocks
13
+ * - Step type (Context/Action/Outcome) instead of keyword
14
+ *
15
+ * ID tag convention: @tc:12345 (prefix configurable via sync.tagPrefix)
16
+ *
17
+ * Path-based auto-tagging:
18
+ * Directory segments prefixed with @ are added as tags automatically.
19
+ * e.g. specs/@smoke/@regression/login.feature → tags: ['smoke', 'regression']
20
+ */
21
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.extractAzureId = extractAzureId;
56
+ exports.extractPathTags = extractPathTags;
57
+ exports.parseGherkinFile = parseGherkinFile;
58
+ const gherkin_1 = require("@cucumber/gherkin");
59
+ const messages_1 = require("@cucumber/messages");
60
+ const fs = __importStar(require("fs"));
61
+ const path = __importStar(require("path"));
62
+ // ─── Step type → keyword mapping ─────────────────────────────────────────────
63
+ const STEP_TYPE_KEYWORD = {
64
+ Context: 'Given',
65
+ Action: 'When',
66
+ Outcome: 'Then',
67
+ Unknown: 'Step',
68
+ };
69
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
70
+ /** Strip leading @ from a Gherkin tag name. */
71
+ function stripAt(name) {
72
+ return name.startsWith('@') ? name.slice(1) : name;
73
+ }
74
+ /** Extract Azure Test Case ID from a list of tag names. e.g. ['tc:123', 'smoke'] → 123 */
75
+ function extractAzureId(tags, tagPrefix) {
76
+ const prefix = tagPrefix + ':';
77
+ for (const tag of tags) {
78
+ if (tag.startsWith(prefix)) {
79
+ const n = parseInt(tag.slice(prefix.length), 10);
80
+ if (!isNaN(n))
81
+ return n;
82
+ }
83
+ }
84
+ return undefined;
85
+ }
86
+ /**
87
+ * Extract auto-tags from directory segments that start with '@'.
88
+ *
89
+ * Given /project/specs/@smoke/@regression/login.feature
90
+ * returns ['smoke', 'regression']
91
+ */
92
+ function extractPathTags(filePath) {
93
+ const segments = filePath.split(path.sep);
94
+ const tags = [];
95
+ // Walk directory segments (not the filename itself)
96
+ for (let i = 0; i < segments.length - 1; i++) {
97
+ const seg = segments[i];
98
+ // A segment may contain multiple @tags separated by spaces or be just one tag
99
+ const matches = seg.match(/@[^\s@/\\]+/g);
100
+ if (matches) {
101
+ tags.push(...matches.map(stripAt));
102
+ }
103
+ }
104
+ return tags;
105
+ }
106
+ /**
107
+ * Given a GherkinDocument and a pickle, find the source line of the
108
+ * scenario that produced this pickle (using astNodeIds).
109
+ */
110
+ function findScenarioLine(doc, pickle) {
111
+ const scenarioId = pickle.astNodeIds[0];
112
+ for (const child of doc.feature?.children ?? []) {
113
+ if (child.scenario?.id === scenarioId) {
114
+ return child.scenario.location?.line ?? 1;
115
+ }
116
+ if (child.rule) {
117
+ for (const ruleChild of child.rule.children ?? []) {
118
+ if (ruleChild.scenario?.id === scenarioId) {
119
+ return ruleChild.scenario.location?.line ?? 1;
120
+ }
121
+ }
122
+ }
123
+ }
124
+ return 1;
125
+ }
126
+ function pickleStepToParsedStep(step) {
127
+ return {
128
+ keyword: STEP_TYPE_KEYWORD[step.type ?? 'Unknown'] ?? 'Step',
129
+ text: step.text.trim(),
130
+ };
131
+ }
132
+ // ─── Public parser ────────────────────────────────────────────────────────────
133
+ function parseGherkinFile(filePath, tagPrefix) {
134
+ const source = fs.readFileSync(filePath, 'utf8');
135
+ const newId = messages_1.IdGenerator.uuid();
136
+ let messages;
137
+ try {
138
+ messages = (0, gherkin_1.generateMessages)(source, filePath, messages_1.SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN, {
139
+ newId,
140
+ includeGherkinDocument: true,
141
+ includePickles: true,
142
+ includeSource: false,
143
+ });
144
+ }
145
+ catch (err) {
146
+ throw new Error(`Failed to parse ${filePath}: ${err.message}`);
147
+ }
148
+ // Surface any parse errors from the envelope stream
149
+ const parseErrors = messages.filter((m) => m.parseError);
150
+ if (parseErrors.length > 0) {
151
+ const msg = parseErrors.map((m) => m.parseError?.message).join('; ');
152
+ throw new Error(`Gherkin parse error in ${filePath}: ${msg}`);
153
+ }
154
+ const docEnvelope = messages.find((m) => m.gherkinDocument);
155
+ if (!docEnvelope?.gherkinDocument?.feature)
156
+ return [];
157
+ const doc = docEnvelope.gherkinDocument;
158
+ const pickles = messages
159
+ .filter((m) => m.pickle)
160
+ .map((m) => m.pickle);
161
+ // Tags from directory path segments (e.g. specs/@smoke/ → 'smoke')
162
+ const pathTags = extractPathTags(filePath);
163
+ return pickles.map((pickle) => {
164
+ // Pickle tags already include Feature + Scenario + Examples tags (inherited)
165
+ const pickleTags = pickle.tags.map((t) => stripAt(t.name));
166
+ const allTags = [...new Set([...pathTags, ...pickleTags])];
167
+ return {
168
+ filePath,
169
+ title: pickle.name.trim(),
170
+ steps: pickle.steps.map(pickleStepToParsedStep),
171
+ tags: allTags,
172
+ azureId: extractAzureId(allTags, tagPrefix),
173
+ line: findScenarioLine(doc, pickle),
174
+ };
175
+ });
176
+ }
177
+ //# sourceMappingURL=gherkin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gherkin.js","sourceRoot":"","sources":["../../src/parsers/gherkin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Markdown test spec parser.
3
+ *
4
+ * Expected file structure:
5
+ *
6
+ * # Plan title
7
+ *
8
+ * ## Test scenarios ← optional section heading (ignored)
9
+ *
10
+ * ### 1. Scenario title ← H3 heading = one test case
11
+ *
12
+ * Assumption: ... ← optional prose, used as description
13
+ *
14
+ * Steps:
15
+ * 1. Do this
16
+ * 2. Do that
17
+ *
18
+ * Expected results:
19
+ * - Result A
20
+ * - Result B
21
+ *
22
+ * <!-- azure-tc: 12345 --> ← written back after first push
23
+ *
24
+ * --- ← separator between scenarios
25
+ *
26
+ * ID tag convention: <!-- {tagPrefix}: 12345 --> anywhere within the scenario block
27
+ * e.g. with default tagPrefix "tc": <!-- tc: 12345 -->
28
+ * The prefix is set via sync.tagPrefix in the config file.
29
+ */
30
+ import { ParsedTest } from '../types';
31
+ export declare function parseMarkdownFile(filePath: string, tagPrefix: string): ParsedTest[];
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * Markdown test spec parser.
4
+ *
5
+ * Expected file structure:
6
+ *
7
+ * # Plan title
8
+ *
9
+ * ## Test scenarios ← optional section heading (ignored)
10
+ *
11
+ * ### 1. Scenario title ← H3 heading = one test case
12
+ *
13
+ * Assumption: ... ← optional prose, used as description
14
+ *
15
+ * Steps:
16
+ * 1. Do this
17
+ * 2. Do that
18
+ *
19
+ * Expected results:
20
+ * - Result A
21
+ * - Result B
22
+ *
23
+ * <!-- azure-tc: 12345 --> ← written back after first push
24
+ *
25
+ * --- ← separator between scenarios
26
+ *
27
+ * ID tag convention: <!-- {tagPrefix}: 12345 --> anywhere within the scenario block
28
+ * e.g. with default tagPrefix "tc": <!-- tc: 12345 -->
29
+ * The prefix is set via sync.tagPrefix in the config file.
30
+ */
31
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
32
+ if (k2 === undefined) k2 = k;
33
+ var desc = Object.getOwnPropertyDescriptor(m, k);
34
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
35
+ desc = { enumerable: true, get: function() { return m[k]; } };
36
+ }
37
+ Object.defineProperty(o, k2, desc);
38
+ }) : (function(o, m, k, k2) {
39
+ if (k2 === undefined) k2 = k;
40
+ o[k2] = m[k];
41
+ }));
42
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
43
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
44
+ }) : function(o, v) {
45
+ o["default"] = v;
46
+ });
47
+ var __importStar = (this && this.__importStar) || (function () {
48
+ var ownKeys = function(o) {
49
+ ownKeys = Object.getOwnPropertyNames || function (o) {
50
+ var ar = [];
51
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
52
+ return ar;
53
+ };
54
+ return ownKeys(o);
55
+ };
56
+ return function (mod) {
57
+ if (mod && mod.__esModule) return mod;
58
+ var result = {};
59
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
60
+ __setModuleDefault(result, mod);
61
+ return result;
62
+ };
63
+ })();
64
+ Object.defineProperty(exports, "__esModule", { value: true });
65
+ exports.parseMarkdownFile = parseMarkdownFile;
66
+ const fs = __importStar(require("fs"));
67
+ // ─── Regexes ─────────────────────────────────────────────────────────────────
68
+ const H3_RE = /^###\s+(?:\d+\.\s+)?(.+)$/; // ### N. Title or ### Title
69
+ const STEPS_HEADING_RE = /^steps\s*:/i;
70
+ const EXPECTED_HEADING_RE = /^expected\s+results?\s*:/i;
71
+ const NUMBERED_STEP_RE = /^\s*\d+\.\s+(.+)$/;
72
+ const BULLET_STEP_RE = /^\s*[-*]\s+(.+)$/;
73
+ const SEPARATOR_RE = /^---+\s*$/;
74
+ const mdTcCommentRe = (prefix) => new RegExp(`<!--\\s*${prefix}\\s*:\\s*(\\d+)\\s*-->`, 'i');
75
+ function splitIntoScenarios(lines) {
76
+ const blocks = [];
77
+ let current = null;
78
+ for (let i = 0; i < lines.length; i++) {
79
+ const line = lines[i];
80
+ const h3Match = line.match(H3_RE);
81
+ if (h3Match) {
82
+ if (current)
83
+ blocks.push(current);
84
+ current = { title: h3Match[1].trim(), startLine: i + 1, lines: [] };
85
+ }
86
+ else if (current) {
87
+ if (SEPARATOR_RE.test(line)) {
88
+ blocks.push(current);
89
+ current = null;
90
+ }
91
+ else {
92
+ current.lines.push(line);
93
+ }
94
+ }
95
+ }
96
+ if (current)
97
+ blocks.push(current);
98
+ return blocks;
99
+ }
100
+ function parseScenarioBlock(block, filePath, tagPrefix) {
101
+ const lines = block.lines;
102
+ const tcRe = mdTcCommentRe(tagPrefix);
103
+ // Find ID comment
104
+ let azureId;
105
+ const tcComment = lines.join('\n').match(tcRe);
106
+ if (tcComment) {
107
+ azureId = parseInt(tcComment[1], 10);
108
+ }
109
+ // Extract sections
110
+ let section = 'description';
111
+ const descLines = [];
112
+ const stepLines = [];
113
+ const expectedLines = [];
114
+ for (const line of lines) {
115
+ if (tcRe.test(line))
116
+ continue; // skip ID comment lines
117
+ if (STEPS_HEADING_RE.test(line.trim())) {
118
+ section = 'steps';
119
+ continue;
120
+ }
121
+ if (EXPECTED_HEADING_RE.test(line.trim())) {
122
+ section = 'expected';
123
+ continue;
124
+ }
125
+ // A new unrecognised heading resets to 'other'
126
+ if (/^#{1,6}\s/.test(line)) {
127
+ section = 'other';
128
+ }
129
+ switch (section) {
130
+ case 'description':
131
+ descLines.push(line);
132
+ break;
133
+ case 'steps':
134
+ if (NUMBERED_STEP_RE.test(line) || BULLET_STEP_RE.test(line))
135
+ stepLines.push(line);
136
+ break;
137
+ case 'expected':
138
+ if (NUMBERED_STEP_RE.test(line) || BULLET_STEP_RE.test(line))
139
+ expectedLines.push(line);
140
+ break;
141
+ }
142
+ }
143
+ // Build steps
144
+ const parsedSteps = stepLines.map((l) => {
145
+ const m = l.match(NUMBERED_STEP_RE) ?? l.match(BULLET_STEP_RE);
146
+ return { keyword: 'Step', text: (m ? m[1] : l).trim() };
147
+ });
148
+ // Attach expected results as the expected value of the last step,
149
+ // or add a dedicated verification step if there are no regular steps.
150
+ const expectedText = expectedLines
151
+ .map((l) => {
152
+ const m = l.match(NUMBERED_STEP_RE) ?? l.match(BULLET_STEP_RE);
153
+ return (m ? m[1] : l).trim();
154
+ })
155
+ .join('\n');
156
+ if (expectedText) {
157
+ if (parsedSteps.length > 0) {
158
+ parsedSteps[parsedSteps.length - 1].expected = expectedText;
159
+ }
160
+ else {
161
+ parsedSteps.push({ keyword: 'Verify', text: 'Expected results', expected: expectedText });
162
+ }
163
+ }
164
+ // Description: trim trailing blank lines
165
+ const description = descLines
166
+ .join('\n')
167
+ .replace(/<!--[\s\S]*?-->/g, '')
168
+ .trim() || undefined;
169
+ return {
170
+ filePath,
171
+ title: block.title,
172
+ description,
173
+ steps: parsedSteps,
174
+ tags: [], // markdown specs don't have Gherkin tags
175
+ azureId,
176
+ line: block.startLine,
177
+ };
178
+ }
179
+ function parseMarkdownFile(filePath, tagPrefix) {
180
+ const source = fs.readFileSync(filePath, 'utf8');
181
+ const lines = source.split('\n');
182
+ const blocks = splitIntoScenarios(lines);
183
+ return blocks.map((b) => parseScenarioBlock(b, filePath, tagPrefix));
184
+ }
185
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/parsers/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Sync engine — orchestrates push, pull, and status operations.
3
+ */
4
+ import { SyncConfig, SyncResult } from '../types';
5
+ export interface SyncOpts {
6
+ dryRun?: boolean;
7
+ /** Cucumber tag expression to restrict which scenarios are synced.
8
+ * Examples: "@smoke" "@smoke and not @wip" "not @manual" */
9
+ tags?: string;
10
+ }
11
+ export declare function push(config: SyncConfig, configDir: string, opts?: SyncOpts): Promise<SyncResult[]>;
12
+ export declare function pull(config: SyncConfig, configDir: string, opts?: SyncOpts): Promise<SyncResult[]>;
13
+ export declare function status(config: SyncConfig, configDir: string, opts?: Pick<SyncOpts, 'tags'>): Promise<SyncResult[]>;