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.
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ /**
3
+ * Swift / XCUITest parser for azure-test-sync.
4
+ *
5
+ * Handles iOS and macOS UI-automation tests written with Apple's XCTest framework.
6
+ * Tests are `func test*()` methods inside classes that extend `XCTestCase`.
7
+ *
8
+ * Detected test functions:
9
+ * func testUserCanLogin() { ... }
10
+ * func test_login_succeeds() { ... }
11
+ *
12
+ * Detected class blocks (used as the test group / describe equivalent):
13
+ * class LoginTests: XCTestCase { ... }
14
+ *
15
+ * Source mapping:
16
+ * /// Swift doc comment above func test* → TC Title (first non-numbered line)
17
+ * JSDoc /** ... * / doc comment above func test* → TC Title + Steps (same format)
18
+ * Numbered lines "N. text" → TC Steps (action)
19
+ * "N. Check: text" → TC Steps (expected result column)
20
+ * // @tags: smoke, regression → TC Tags (comma-separated list)
21
+ * // @smoke → TC Tag (single-word shorthand)
22
+ * // @{tagPrefix}:N comment above func → Azure TC ID (written back after push)
23
+ * {fileBasename} > {ClassName} > {method} → automatedTestName
24
+ *
25
+ * Method name → title fallback:
26
+ * "testUserCanLogin" → "User can login"
27
+ * "test_submit_form" → "Submit form"
28
+ *
29
+ * ID writeback:
30
+ * Inserts / updates // @tc:12345 immediately above the func test*() line.
31
+ *
32
+ * Path-based auto-tagging: directory segments starting with '@' become tags.
33
+ */
34
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
35
+ if (k2 === undefined) k2 = k;
36
+ var desc = Object.getOwnPropertyDescriptor(m, k);
37
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
38
+ desc = { enumerable: true, get: function() { return m[k]; } };
39
+ }
40
+ Object.defineProperty(o, k2, desc);
41
+ }) : (function(o, m, k, k2) {
42
+ if (k2 === undefined) k2 = k;
43
+ o[k2] = m[k];
44
+ }));
45
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
46
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
47
+ }) : function(o, v) {
48
+ o["default"] = v;
49
+ });
50
+ var __importStar = (this && this.__importStar) || (function () {
51
+ var ownKeys = function(o) {
52
+ ownKeys = Object.getOwnPropertyNames || function (o) {
53
+ var ar = [];
54
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
55
+ return ar;
56
+ };
57
+ return ownKeys(o);
58
+ };
59
+ return function (mod) {
60
+ if (mod && mod.__esModule) return mod;
61
+ var result = {};
62
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
63
+ __setModuleDefault(result, mod);
64
+ return result;
65
+ };
66
+ })();
67
+ Object.defineProperty(exports, "__esModule", { value: true });
68
+ exports.parseSwiftFile = parseSwiftFile;
69
+ const fs = __importStar(require("fs"));
70
+ const path = __importStar(require("path"));
71
+ const shared_1 = require("./shared");
72
+ // ─── Test method detection ────────────────────────────────────────────────────
73
+ const TEST_METHOD_RE = /^\s*func\s+(test\w+)\s*\(/;
74
+ const CLASS_RE = /^\s*(?:(?:open|final|public|internal|private)\s+)*class\s+(\w+)\s*(?::\s*[\w, ]+)?/;
75
+ // ─── Indentation helpers ──────────────────────────────────────────────────────
76
+ function getIndentLength(line) {
77
+ return (line.match(/^(\s*)/) ?? ['', ''])[1].length;
78
+ }
79
+ // ─── Enclosing class ──────────────────────────────────────────────────────────
80
+ function findEnclosingClass(lines, methodLineIdx) {
81
+ const methodIndent = getIndentLength(lines[methodLineIdx]);
82
+ for (let i = methodLineIdx - 1; i >= 0; i--) {
83
+ const lineIndent = getIndentLength(lines[i]);
84
+ if (lineIndent < methodIndent) {
85
+ const m = lines[i].trim().match(CLASS_RE);
86
+ if (m)
87
+ return m[1];
88
+ }
89
+ }
90
+ return undefined;
91
+ }
92
+ // ─── Doc comment extraction ───────────────────────────────────────────────────
93
+ /**
94
+ * Extract Swift doc comments above func test*().
95
+ * Handles both triple-slash (///) single-line and block (/* * /) style.
96
+ * Skips blank lines and single-line // comments on the way up.
97
+ */
98
+ function extractDocBefore(lines, methodLineIdx) {
99
+ let i = methodLineIdx - 1;
100
+ // Skip blank lines and non-doc single-line comments
101
+ while (i >= 0) {
102
+ const t = lines[i].trim();
103
+ if (t === '' || (t.startsWith('//') && !t.startsWith('///'))) {
104
+ i--;
105
+ continue;
106
+ }
107
+ break;
108
+ }
109
+ if (i < 0)
110
+ return [];
111
+ // /// triple-slash style (Swift idiomatic)
112
+ if (lines[i].trim().startsWith('///')) {
113
+ const docLines = [];
114
+ while (i >= 0 && lines[i].trim().startsWith('///')) {
115
+ docLines.unshift(lines[i].trim().replace(/^\/\/\/\s?/, ''));
116
+ i--;
117
+ }
118
+ return docLines.filter((l) => l !== '');
119
+ }
120
+ // /** ... */ block style
121
+ if (!lines[i].trim().endsWith('*/'))
122
+ return [];
123
+ const raw = [];
124
+ raw.unshift(lines[i]);
125
+ i--;
126
+ while (i >= 0) {
127
+ raw.unshift(lines[i]);
128
+ if (lines[i].trim().startsWith('/**') || lines[i].trim().startsWith('/*'))
129
+ break;
130
+ i--;
131
+ }
132
+ return raw
133
+ .map((l) => l
134
+ .replace(/^\s*\/\*\*?\s?/, '')
135
+ .replace(/\s*\*\/\s*$/, '')
136
+ .replace(/^\s*\*\s?/, '')
137
+ .trim())
138
+ .filter((l) => l !== '');
139
+ }
140
+ function extractCommentMetadataAbove(lines, methodLineIdx, tagPrefix) {
141
+ const tags = [];
142
+ let azureId;
143
+ const idRe = new RegExp(`//\\s*@${tagPrefix}:(\\d+)`);
144
+ const tagsListRe = /\/\/\s*@tags?\s*:\s*(.+)/i;
145
+ const singleTagRe = /\/\/\s*@(\w+)\s*$/;
146
+ for (let i = methodLineIdx - 1; i >= 0 && i >= methodLineIdx - 25; i--) {
147
+ const trimmed = lines[i].trim();
148
+ if (trimmed === '')
149
+ break;
150
+ if (!trimmed.startsWith('//') && !trimmed.startsWith('*') && !trimmed.startsWith('/*'))
151
+ break;
152
+ const idMatch = trimmed.match(idRe);
153
+ if (idMatch && azureId === undefined) {
154
+ azureId = parseInt(idMatch[1], 10);
155
+ continue;
156
+ }
157
+ const tagsMatch = trimmed.match(tagsListRe);
158
+ if (tagsMatch) {
159
+ tags.push(...tagsMatch[1].split(',').map((t) => t.trim()).filter(Boolean));
160
+ continue;
161
+ }
162
+ const singleTag = trimmed.match(singleTagRe);
163
+ if (singleTag && singleTag[1] !== tagPrefix) {
164
+ tags.push(singleTag[1]);
165
+ continue;
166
+ }
167
+ }
168
+ return { azureId, tags };
169
+ }
170
+ // ─── Method name → human-readable title ──────────────────────────────────────
171
+ /**
172
+ * Convert a Swift test method name to a sentence-style title.
173
+ * testUserCanLogin → "User can login"
174
+ * testLoginFails_badPassword → "Login fails bad password"
175
+ * test_submit_form → "Submit form"
176
+ */
177
+ function methodNameToTitle(name) {
178
+ let s = name.replace(/^test_?/, '');
179
+ if (!s)
180
+ return name;
181
+ // snake_case → words
182
+ s = s.replace(/_/g, ' ');
183
+ // camelCase → words
184
+ s = s.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
185
+ s = s.toLowerCase().replace(/^./, (c) => c.toUpperCase());
186
+ return s;
187
+ }
188
+ // ─── Doc → title + steps ─────────────────────────────────────────────────────
189
+ const NUMBERED_STEP_RE = /^\d+\.\s+(.+)$/;
190
+ const CHECK_RE = /^[Cc]heck:\s+(.+)$/;
191
+ const META_RE = /^(?:test\s+case|user\s+story)[\s:]/i;
192
+ function parseSummary(docLines, fallbackTitle) {
193
+ let title = '';
194
+ const steps = [];
195
+ for (const line of docLines) {
196
+ if (!line || META_RE.test(line))
197
+ continue;
198
+ const numMatch = NUMBERED_STEP_RE.exec(line);
199
+ if (numMatch) {
200
+ const content = numMatch[1].trim();
201
+ const checkMatch = CHECK_RE.exec(content);
202
+ if (checkMatch) {
203
+ steps.push({ keyword: 'Then', text: checkMatch[1].trim() });
204
+ }
205
+ else {
206
+ steps.push({ keyword: 'Step', text: content });
207
+ }
208
+ continue;
209
+ }
210
+ if (!title)
211
+ title = line;
212
+ }
213
+ return { title: title || fallbackTitle, steps };
214
+ }
215
+ // ─── Public parser ────────────────────────────────────────────────────────────
216
+ function parseSwiftFile(filePath, tagPrefix, linkConfigs) {
217
+ const source = fs.readFileSync(filePath, 'utf8');
218
+ const lines = source.split('\n');
219
+ const fileBaseName = path.basename(filePath).replace(/\.swift$/, '');
220
+ const pathTags = (0, shared_1.extractPathTags)(filePath);
221
+ const results = [];
222
+ for (let i = 0; i < lines.length; i++) {
223
+ const m = lines[i].match(TEST_METHOD_RE);
224
+ if (!m)
225
+ continue;
226
+ const methodName = m[1];
227
+ const methodLineIdx = i;
228
+ const docLines = extractDocBefore(lines, methodLineIdx);
229
+ const { azureId, tags: cTags } = extractCommentMetadataAbove(lines, methodLineIdx, tagPrefix);
230
+ const className = findEnclosingClass(lines, methodLineIdx);
231
+ const allTags = [...new Set([...pathTags, ...cTags])];
232
+ const fallbackTitle = methodNameToTitle(methodName);
233
+ const { title, steps } = parseSummary(docLines, fallbackTitle);
234
+ const automatedTestName = className
235
+ ? [fileBaseName, className, methodName].join(' > ')
236
+ : [fileBaseName, methodName].join(' > ');
237
+ results.push({
238
+ filePath,
239
+ title,
240
+ steps,
241
+ tags: allTags,
242
+ azureId: azureId !== undefined && !isNaN(azureId) ? azureId : undefined,
243
+ line: methodLineIdx + 1,
244
+ linkRefs: (0, shared_1.extractLinkRefs)(allTags, linkConfigs),
245
+ automatedTestName,
246
+ });
247
+ }
248
+ return results;
249
+ }
250
+ //# sourceMappingURL=swift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swift.js","sourceRoot":"","sources":["../../src/parsers/swift.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * TestCafe test parser for azure-test-sync.
3
+ *
4
+ * TestCafe uses a fixture / test API that is fundamentally different from
5
+ * describe() / it(), so it gets its own parser.
6
+ *
7
+ * Detected test functions:
8
+ * test('title', async t => { ... })
9
+ * test.skip('title', async t => { ... })
10
+ * test.only('title', async t => { ... })
11
+ *
12
+ * Detected fixture blocks (used as the test group / describe equivalent):
13
+ * fixture('Fixture title')
14
+ * fixture.skip('Fixture title')
15
+ * fixture.only('Fixture title')
16
+ *
17
+ * Source mapping:
18
+ * JSDoc /** ... * / first non-numbered line → TC Title
19
+ * Numbered lines "N. text" → TC Steps (action)
20
+ * "N. Check: text" → TC Steps (expected result column)
21
+ * // @tags: smoke, regression → TC Tags (comma-separated list)
22
+ * // @smoke → TC Tag (single-word shorthand)
23
+ * // @{tagPrefix}:N comment above test() → Azure TC ID (written back after push)
24
+ * {fileBasename} > {fixture} > {test title} → automatedTestName
25
+ *
26
+ * ID writeback:
27
+ * Inserts / updates // @tc:12345 immediately above the test() line.
28
+ *
29
+ * Path-based auto-tagging: directory segments starting with '@' become tags.
30
+ */
31
+ import { LinkConfig, ParsedTest } from '../types';
32
+ export declare function parseTestCafeFile(filePath: string, tagPrefix: string, linkConfigs?: LinkConfig[]): ParsedTest[];
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ /**
3
+ * TestCafe test parser for azure-test-sync.
4
+ *
5
+ * TestCafe uses a fixture / test API that is fundamentally different from
6
+ * describe() / it(), so it gets its own parser.
7
+ *
8
+ * Detected test functions:
9
+ * test('title', async t => { ... })
10
+ * test.skip('title', async t => { ... })
11
+ * test.only('title', async t => { ... })
12
+ *
13
+ * Detected fixture blocks (used as the test group / describe equivalent):
14
+ * fixture('Fixture title')
15
+ * fixture.skip('Fixture title')
16
+ * fixture.only('Fixture title')
17
+ *
18
+ * Source mapping:
19
+ * JSDoc /** ... * / first non-numbered line → TC Title
20
+ * Numbered lines "N. text" → TC Steps (action)
21
+ * "N. Check: text" → TC Steps (expected result column)
22
+ * // @tags: smoke, regression → TC Tags (comma-separated list)
23
+ * // @smoke → TC Tag (single-word shorthand)
24
+ * // @{tagPrefix}:N comment above test() → Azure TC ID (written back after push)
25
+ * {fileBasename} > {fixture} > {test title} → automatedTestName
26
+ *
27
+ * ID writeback:
28
+ * Inserts / updates // @tc:12345 immediately above the test() line.
29
+ *
30
+ * Path-based auto-tagging: directory segments starting with '@' become tags.
31
+ */
32
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
33
+ if (k2 === undefined) k2 = k;
34
+ var desc = Object.getOwnPropertyDescriptor(m, k);
35
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
36
+ desc = { enumerable: true, get: function() { return m[k]; } };
37
+ }
38
+ Object.defineProperty(o, k2, desc);
39
+ }) : (function(o, m, k, k2) {
40
+ if (k2 === undefined) k2 = k;
41
+ o[k2] = m[k];
42
+ }));
43
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
44
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
45
+ }) : function(o, v) {
46
+ o["default"] = v;
47
+ });
48
+ var __importStar = (this && this.__importStar) || (function () {
49
+ var ownKeys = function(o) {
50
+ ownKeys = Object.getOwnPropertyNames || function (o) {
51
+ var ar = [];
52
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
53
+ return ar;
54
+ };
55
+ return ownKeys(o);
56
+ };
57
+ return function (mod) {
58
+ if (mod && mod.__esModule) return mod;
59
+ var result = {};
60
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
61
+ __setModuleDefault(result, mod);
62
+ return result;
63
+ };
64
+ })();
65
+ Object.defineProperty(exports, "__esModule", { value: true });
66
+ exports.parseTestCafeFile = parseTestCafeFile;
67
+ const fs = __importStar(require("fs"));
68
+ const path = __importStar(require("path"));
69
+ const shared_1 = require("./shared");
70
+ // ─── Test / fixture detection ─────────────────────────────────────────────────
71
+ const TEST_CALL_RE = /^(?:test|test\.skip|test\.only)\s*\(/;
72
+ const TEST_TITLE_RE = /^(?:test|test\.skip|test\.only)\s*\(\s*(['"`])((?:\\.|[^\\])*?)\1/;
73
+ const FIXTURE_TITLE_RE = /^(?:fixture|fixture\.skip|fixture\.only)\s*\(\s*(['"`])((?:\\.|[^\\])*?)\1/;
74
+ // ─── Indentation helpers ──────────────────────────────────────────────────────
75
+ function getIndentLength(line) {
76
+ return (line.match(/^(\s*)/) ?? ['', ''])[1].length;
77
+ }
78
+ // ─── Enclosing fixture ────────────────────────────────────────────────────────
79
+ /**
80
+ * Walk backward from the test() line to find the most recent fixture() title.
81
+ */
82
+ function findEnclosingFixture(lines, testLineIdx) {
83
+ const testIndent = getIndentLength(lines[testLineIdx]);
84
+ for (let i = testLineIdx - 1; i >= 0; i--) {
85
+ const line = lines[i];
86
+ const trimmed = line.trim();
87
+ if (!trimmed)
88
+ continue;
89
+ const lineIndent = getIndentLength(line);
90
+ if (lineIndent <= testIndent) {
91
+ const m = trimmed.match(FIXTURE_TITLE_RE);
92
+ if (m) {
93
+ return m[2].replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\`/g, '`');
94
+ }
95
+ }
96
+ }
97
+ return undefined;
98
+ }
99
+ // ─── JSDoc extraction ─────────────────────────────────────────────────────────
100
+ function extractJsdocBefore(lines, testLineIdx) {
101
+ let i = testLineIdx - 1;
102
+ while (i >= 0) {
103
+ const t = lines[i].trim();
104
+ if (t === '' || t.startsWith('//')) {
105
+ i--;
106
+ continue;
107
+ }
108
+ break;
109
+ }
110
+ if (i < 0 || !lines[i].trim().endsWith('*/'))
111
+ return [];
112
+ const raw = [];
113
+ raw.unshift(lines[i]);
114
+ i--;
115
+ while (i >= 0) {
116
+ raw.unshift(lines[i]);
117
+ if (lines[i].trim().startsWith('/**'))
118
+ break;
119
+ i--;
120
+ }
121
+ return raw
122
+ .map((l) => l
123
+ .replace(/^\s*\/\*\*\s?/, '')
124
+ .replace(/\s*\*\/\s*$/, '')
125
+ .replace(/^\s*\*\s?/, '')
126
+ .trim())
127
+ .filter((l) => l !== '');
128
+ }
129
+ function extractCommentMetadataAbove(lines, testLineIdx, tagPrefix) {
130
+ const tags = [];
131
+ let azureId;
132
+ const idRe = new RegExp(`//\\s*@${tagPrefix}:(\\d+)`);
133
+ const tagsListRe = /\/\/\s*@tags?\s*:\s*(.+)/i;
134
+ const singleTagRe = /\/\/\s*@(\w+)\s*$/;
135
+ for (let i = testLineIdx - 1; i >= 0 && i >= testLineIdx - 25; i--) {
136
+ const trimmed = lines[i].trim();
137
+ if (trimmed === '')
138
+ break;
139
+ if (!trimmed.startsWith('//') && !trimmed.startsWith('*') && !trimmed.startsWith('/*'))
140
+ break;
141
+ const idMatch = trimmed.match(idRe);
142
+ if (idMatch && azureId === undefined) {
143
+ azureId = parseInt(idMatch[1], 10);
144
+ continue;
145
+ }
146
+ const tagsMatch = trimmed.match(tagsListRe);
147
+ if (tagsMatch) {
148
+ tags.push(...tagsMatch[1].split(',').map((t) => t.trim()).filter(Boolean));
149
+ continue;
150
+ }
151
+ const singleTag = trimmed.match(singleTagRe);
152
+ if (singleTag && singleTag[1] !== tagPrefix) {
153
+ tags.push(singleTag[1]);
154
+ continue;
155
+ }
156
+ }
157
+ return { azureId, tags };
158
+ }
159
+ // ─── JSDoc → title + steps ────────────────────────────────────────────────────
160
+ const NUMBERED_STEP_RE = /^\d+\.\s+(.+)$/;
161
+ const CHECK_RE = /^[Cc]heck:\s+(.+)$/;
162
+ const META_RE = /^(?:test\s+case|user\s+story)[\s:]/i;
163
+ function parseSummary(jsdocLines, fallbackTitle) {
164
+ let title = '';
165
+ const steps = [];
166
+ for (const line of jsdocLines) {
167
+ if (!line || META_RE.test(line))
168
+ continue;
169
+ const numMatch = NUMBERED_STEP_RE.exec(line);
170
+ if (numMatch) {
171
+ const content = numMatch[1].trim();
172
+ const checkMatch = CHECK_RE.exec(content);
173
+ if (checkMatch) {
174
+ steps.push({ keyword: 'Then', text: checkMatch[1].trim() });
175
+ }
176
+ else {
177
+ steps.push({ keyword: 'Step', text: content });
178
+ }
179
+ continue;
180
+ }
181
+ if (!title)
182
+ title = line;
183
+ }
184
+ return { title: title || fallbackTitle, steps };
185
+ }
186
+ // ─── Public parser ────────────────────────────────────────────────────────────
187
+ function parseTestCafeFile(filePath, tagPrefix, linkConfigs) {
188
+ const source = fs.readFileSync(filePath, 'utf8');
189
+ const lines = source.split('\n');
190
+ const fileBaseName = path.basename(filePath)
191
+ .replace(/\.(spec|test)\.(js|ts|mjs|cjs)$/, '')
192
+ .replace(/\.(js|ts|mjs|cjs)$/, '');
193
+ const pathTags = (0, shared_1.extractPathTags)(filePath);
194
+ const results = [];
195
+ for (let i = 0; i < lines.length; i++) {
196
+ const trimmed = lines[i].trim();
197
+ if (!TEST_CALL_RE.test(trimmed))
198
+ continue;
199
+ const testLineIdx = i;
200
+ const m = trimmed.match(TEST_TITLE_RE);
201
+ if (!m)
202
+ continue;
203
+ const callTitle = m[2]
204
+ .replace(/\\'/g, "'")
205
+ .replace(/\\"/g, '"')
206
+ .replace(/\\`/g, '`')
207
+ .replace(/\\\\/g, '\\');
208
+ if (!callTitle)
209
+ continue;
210
+ const jsdocLines = extractJsdocBefore(lines, testLineIdx);
211
+ const { azureId, tags: cTags } = extractCommentMetadataAbove(lines, testLineIdx, tagPrefix);
212
+ const fixture = findEnclosingFixture(lines, testLineIdx);
213
+ const allTags = [...new Set([...pathTags, ...cTags])];
214
+ const { title, steps } = parseSummary(jsdocLines, callTitle);
215
+ const automatedTestName = fixture
216
+ ? [fileBaseName, fixture, callTitle].join(' > ')
217
+ : [fileBaseName, callTitle].join(' > ');
218
+ results.push({
219
+ filePath,
220
+ title,
221
+ steps,
222
+ tags: allTags,
223
+ azureId: azureId !== undefined && !isNaN(azureId) ? azureId : undefined,
224
+ line: testLineIdx + 1,
225
+ linkRefs: (0, shared_1.extractLinkRefs)(allTags, linkConfigs),
226
+ automatedTestName,
227
+ });
228
+ }
229
+ return results;
230
+ }
231
+ //# sourceMappingURL=testcafe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testcafe.js","sourceRoot":"","sources":["../../src/parsers/testcafe.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * Sync engine — orchestrates push, pull, and status operations.
3
3
  */
4
+ import { AiSummaryOpts } from '../ai/summarizer';
4
5
  import { SyncConfig, SyncResult } from '../types';
5
6
  export interface SyncOpts {
6
7
  dryRun?: boolean;
7
8
  tags?: string;
8
9
  /** Called after each test case is processed. Useful for rendering a live progress bar. */
9
10
  onProgress?: (done: number, total: number, result: SyncResult) => void;
11
+ /** AI auto-summary options: generate title/steps for tests that have none. */
12
+ aiSummary?: AiSummaryOpts;
10
13
  }
11
14
  export declare function push(config: SyncConfig, configDir: string, opts?: SyncOpts): Promise<SyncResult[]>;
12
15
  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' | 'onProgress'>): Promise<SyncResult[]>;
16
+ export declare function status(config: SyncConfig, configDir: string, opts?: Pick<SyncOpts, 'tags' | 'onProgress' | 'aiSummary'>): Promise<SyncResult[]>;
@@ -46,16 +46,20 @@ const tag_expressions_1 = __importDefault(require("@cucumber/tag-expressions"));
46
46
  const fs = __importStar(require("fs"));
47
47
  const glob_1 = require("glob");
48
48
  const path = __importStar(require("path"));
49
+ const summarizer_1 = require("../ai/summarizer");
49
50
  const client_1 = require("../azure/client");
50
51
  const test_cases_1 = require("../azure/test-cases");
51
52
  const csharp_1 = require("../parsers/csharp");
52
53
  const csv_1 = require("../parsers/csv");
54
+ const dart_1 = require("../parsers/dart");
53
55
  const excel_1 = require("../parsers/excel");
54
56
  const gherkin_1 = require("../parsers/gherkin");
55
57
  const java_1 = require("../parsers/java");
56
58
  const javascript_1 = require("../parsers/javascript");
57
59
  const markdown_1 = require("../parsers/markdown");
58
60
  const python_1 = require("../parsers/python");
61
+ const swift_1 = require("../parsers/swift");
62
+ const testcafe_1 = require("../parsers/testcafe");
59
63
  const cache_1 = require("./cache");
60
64
  const writeback_1 = require("./writeback");
61
65
  // ─── Tag filtering ────────────────────────────────────────────────────────────
@@ -116,8 +120,24 @@ async function parseLocalFiles(filePaths, config, tagsFilter) {
116
120
  tests = (0, python_1.parsePythonFile)(fp, tagPrefix, linkConfigs);
117
121
  break;
118
122
  case 'javascript':
123
+ case 'playwright':
124
+ case 'puppeteer':
125
+ case 'cypress':
126
+ case 'detox':
119
127
  tests = (0, javascript_1.parseJavaScriptFile)(fp, tagPrefix, linkConfigs);
120
128
  break;
129
+ case 'testcafe':
130
+ tests = (0, testcafe_1.parseTestCafeFile)(fp, tagPrefix, linkConfigs);
131
+ break;
132
+ case 'espresso':
133
+ tests = (0, java_1.parseJavaFile)(fp, tagPrefix, linkConfigs);
134
+ break;
135
+ case 'xcuitest':
136
+ tests = (0, swift_1.parseSwiftFile)(fp, tagPrefix, linkConfigs);
137
+ break;
138
+ case 'flutter':
139
+ tests = (0, dart_1.parseDartFile)(fp, tagPrefix, linkConfigs);
140
+ break;
121
141
  default:
122
142
  tests = (0, markdown_1.parseMarkdownFile)(fp, tagPrefix, linkConfigs, attachmentsConfig);
123
143
  }
@@ -171,6 +191,28 @@ async function push(config, configDir, opts = {}) {
171
191
  async function pushSingle(config, configDir, opts) {
172
192
  const files = await discoverFiles(config.local.include, config.local.exclude, configDir);
173
193
  const tests = await parseLocalFiles(files, config, opts.tags);
194
+ // AI auto-summary: for code-based local types, default to the local node-llama-cpp
195
+ // provider (with heuristic fallback) when no explicit aiSummary opts are provided.
196
+ // If no GGUF model path is set, the local provider transparently falls back to
197
+ // heuristic mode so the push always succeeds even without a model installed.
198
+ const CODE_TYPES = new Set(['javascript', 'playwright', 'puppeteer', 'cypress', 'testcafe', 'detox', 'espresso', 'xcuitest', 'flutter', 'java', 'csharp', 'python']);
199
+ const effectiveAiOpts = opts.aiSummary ?? (CODE_TYPES.has(config.local.type) ? { provider: 'local', heuristicFallback: true } : undefined);
200
+ if (effectiveAiOpts) {
201
+ for (const test of tests) {
202
+ const needsSteps = test.steps.length === 0;
203
+ const needsDescription = !test.description;
204
+ if (needsSteps || needsDescription) {
205
+ const result = await (0, summarizer_1.summarizeTest)(test, config.local.type, effectiveAiOpts);
206
+ if (needsSteps) {
207
+ test.title = result.title;
208
+ test.steps = result.steps;
209
+ }
210
+ if (needsDescription && result.description) {
211
+ test.description = result.description;
212
+ }
213
+ }
214
+ }
215
+ }
174
216
  const client = await client_1.AzureClient.create(config);
175
217
  const tagPrefix = config.sync?.tagPrefix ?? 'tc';
176
218
  const titleField = config.sync?.titleField ?? 'System.Title';
@@ -467,7 +509,7 @@ async function pullSingle(config, configDir, opts) {
467
509
  }
468
510
  // ─── Status ───────────────────────────────────────────────────────────────────
469
511
  async function status(config, configDir, opts = {}) {
470
- return push(config, configDir, { dryRun: true, tags: opts.tags, onProgress: opts.onProgress });
512
+ return push(config, configDir, { dryRun: true, tags: opts.tags, onProgress: opts.onProgress, aiSummary: opts.aiSummary });
471
513
  }
472
514
  // ─── Cache helpers ────────────────────────────────────────────────────────────
473
515
  function updateCacheEntry(cache, test, remote) {
@@ -491,7 +533,11 @@ function applyRemoteToLocal(test, newTitle, newSteps, newDescription, localType,
491
533
  else if (localType === 'markdown') {
492
534
  applyRemoteToMarkdown(test, newTitle, newSteps, newDescription, tagPrefix);
493
535
  }
494
- // csv / excel: pull not supported (files are typically generated by external tools)
536
+ else if (localType === 'csv') {
537
+ (0, csv_1.applyRemoteToCsv)(test.filePath, test.title, newTitle, newSteps);
538
+ }
539
+ // excel: pull not yet supported (in-place XML surgery for xlsx is complex)
540
+ // csharp / java / python / javascript: pull not supported (code files are managed locally)
495
541
  }
496
542
  function applyRemoteToGherkin(test, newTitle, newSteps) {
497
543
  const raw = fs.readFileSync(test.filePath, 'utf8');