ado-sync 0.1.26 → 0.1.28
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 +208 -11
- package/dist/azure/test-cases.js +3 -1
- package/dist/azure/test-cases.js.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/parsers/csharp.d.ts +30 -0
- package/dist/parsers/csharp.js +257 -0
- package/dist/parsers/csharp.js.map +1 -0
- package/dist/parsers/java.d.ts +40 -0
- package/dist/parsers/java.js +329 -0
- package/dist/parsers/java.js.map +1 -0
- package/dist/parsers/javascript.d.ts +33 -0
- package/dist/parsers/javascript.js +261 -0
- package/dist/parsers/javascript.js.map +1 -0
- package/dist/parsers/python.d.ts +34 -0
- package/dist/parsers/python.js +305 -0
- package/dist/parsers/python.js.map +1 -0
- package/dist/sync/engine.js +16 -0
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/publish-results.d.ts +31 -5
- package/dist/sync/publish-results.js +533 -28
- package/dist/sync/publish-results.js.map +1 -1
- package/dist/sync/writeback.d.ts +57 -1
- package/dist/sync/writeback.js +243 -0
- package/dist/sync/writeback.js.map +1 -1
- package/dist/types.d.ts +34 -3
- package/docs/publish-test-results.md +372 -7
- package/docs/spec-formats.md +370 -0
- package/package.json +1 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* C# test parser for azure-test-sync.
|
|
4
|
+
*
|
|
5
|
+
* Supports both MSTest and NUnit frameworks:
|
|
6
|
+
*
|
|
7
|
+
* Framework Test marker Category attr Property attr
|
|
8
|
+
* ───────── ─────────── ───────────── ─────────────
|
|
9
|
+
* MSTest [TestMethod] [TestCategory("name")] [TestProperty("key","val")]
|
|
10
|
+
* NUnit [Test] [Category("name")] [Property("key","val")]
|
|
11
|
+
*
|
|
12
|
+
* Source mapping:
|
|
13
|
+
* XML doc <summary> first line → TC Title
|
|
14
|
+
* Numbered lines in <summary> "N. text" → TC Steps (action)
|
|
15
|
+
* "N. Check: text" lines → TC Steps (keyword: 'Then' → expected col)
|
|
16
|
+
* [TestCategory("...")] / [Category("...")] → TC Tags (string literals only)
|
|
17
|
+
* [TestProperty("<tagPrefix>","N")] /
|
|
18
|
+
* [Property("<tagPrefix>","N")] → Azure TC ID (written back after push)
|
|
19
|
+
* Namespace.ClassName.MethodName → automatedTestName (for TRX result linking)
|
|
20
|
+
*
|
|
21
|
+
* ID writeback:
|
|
22
|
+
* MSTest → [TestProperty("tc", "12345")]
|
|
23
|
+
* NUnit → [Property("tc", "12345")]
|
|
24
|
+
*
|
|
25
|
+
* The detected framework is stored in each ParsedTest so writeback uses the
|
|
26
|
+
* correct attribute form without having to re-read the file.
|
|
27
|
+
*
|
|
28
|
+
* Path-based auto-tagging: directory segments starting with '@' become tags.
|
|
29
|
+
*/
|
|
30
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
34
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
35
|
+
}
|
|
36
|
+
Object.defineProperty(o, k2, desc);
|
|
37
|
+
}) : (function(o, m, k, k2) {
|
|
38
|
+
if (k2 === undefined) k2 = k;
|
|
39
|
+
o[k2] = m[k];
|
|
40
|
+
}));
|
|
41
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
42
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
43
|
+
}) : function(o, v) {
|
|
44
|
+
o["default"] = v;
|
|
45
|
+
});
|
|
46
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
47
|
+
var ownKeys = function(o) {
|
|
48
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
49
|
+
var ar = [];
|
|
50
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
51
|
+
return ar;
|
|
52
|
+
};
|
|
53
|
+
return ownKeys(o);
|
|
54
|
+
};
|
|
55
|
+
return function (mod) {
|
|
56
|
+
if (mod && mod.__esModule) return mod;
|
|
57
|
+
var result = {};
|
|
58
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
59
|
+
__setModuleDefault(result, mod);
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
})();
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.parseCsharpFile = parseCsharpFile;
|
|
65
|
+
const fs = __importStar(require("fs"));
|
|
66
|
+
const shared_1 = require("./shared");
|
|
67
|
+
/**
|
|
68
|
+
* Check whether a (trimmed) line is a [TestMethod] (MSTest) or [Test] / [Test(...)] (NUnit)
|
|
69
|
+
* attribute marker. Returns the detected framework, or null if neither.
|
|
70
|
+
*/
|
|
71
|
+
function detectTestMarker(trimmedLine) {
|
|
72
|
+
if (/^\[TestMethod[\]( ]/.test(trimmedLine) || trimmedLine === '[TestMethod]') {
|
|
73
|
+
return 'mstest';
|
|
74
|
+
}
|
|
75
|
+
// [Test] or [Test("name")] but NOT [TestCase], [TestFixture], etc.
|
|
76
|
+
if (/^\[Test[\]( ]/.test(trimmedLine) || trimmedLine === '[Test]') {
|
|
77
|
+
// Exclude [TestCase(...)] — those are parameterised data rows, not a test marker
|
|
78
|
+
if (/^\[TestCase[\]( ]/.test(trimmedLine) || /^\[TestCaseSource[\]( ]/.test(trimmedLine)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
return 'nunit';
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
// ─── Namespace / class extraction ────────────────────────────────────────────
|
|
86
|
+
function extractNamespace(lines) {
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
const m = line.match(/^\s*namespace\s+([\w.]+)/);
|
|
89
|
+
if (m)
|
|
90
|
+
return m[1];
|
|
91
|
+
}
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
function extractClassName(lines) {
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
// Handles optional partial, abstract, sealed modifiers before "class"
|
|
97
|
+
const m = line.match(/^\s*(?:(?:public|internal|private|protected)\s+)?(?:(?:partial|abstract|sealed)\s+)*class\s+(\w+)/);
|
|
98
|
+
if (m)
|
|
99
|
+
return m[1];
|
|
100
|
+
}
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
// ─── Local string constant resolution ────────────────────────────────────────
|
|
104
|
+
function resolveStringConstants(lines) {
|
|
105
|
+
const map = new Map();
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
const m = line.match(/\bconst\s+string\s+(\w+)\s*=\s*"([^"]*)"/);
|
|
108
|
+
if (m)
|
|
109
|
+
map.set(m[1], m[2]);
|
|
110
|
+
}
|
|
111
|
+
return map;
|
|
112
|
+
}
|
|
113
|
+
// ─── XML doc comment extraction ───────────────────────────────────────────────
|
|
114
|
+
function extractSummaryBefore(lines, testMethodLineIdx) {
|
|
115
|
+
const raw = [];
|
|
116
|
+
let i = testMethodLineIdx - 1;
|
|
117
|
+
while (i >= 0 && /^\s*\/\/\//.test(lines[i])) {
|
|
118
|
+
raw.unshift(lines[i]);
|
|
119
|
+
i--;
|
|
120
|
+
}
|
|
121
|
+
const stripped = raw.map((l) => l.replace(/^\s*\/\/\/\s?/, '').trim());
|
|
122
|
+
const startIdx = stripped.findIndex((l) => /<summary>/i.test(l));
|
|
123
|
+
const endIdx = stripped.findIndex((l) => /<\/summary>/i.test(l));
|
|
124
|
+
if (startIdx === -1)
|
|
125
|
+
return stripped;
|
|
126
|
+
return stripped.slice(startIdx + 1, endIdx === -1 ? undefined : endIdx);
|
|
127
|
+
}
|
|
128
|
+
// ─── Summary → title + steps ─────────────────────────────────────────────────
|
|
129
|
+
const META_RE = /^(?:test\s+case|user\s+story)[\s:]/i;
|
|
130
|
+
const NUMBERED_STEP_RE = /^\d+\.\s+(.+)$/;
|
|
131
|
+
const CHECK_RE = /^[Cc]heck:\s+(.+)$/;
|
|
132
|
+
function parseSummary(summaryLines, methodName) {
|
|
133
|
+
let title = '';
|
|
134
|
+
const steps = [];
|
|
135
|
+
for (const line of summaryLines) {
|
|
136
|
+
if (!line || META_RE.test(line))
|
|
137
|
+
continue;
|
|
138
|
+
const numMatch = NUMBERED_STEP_RE.exec(line);
|
|
139
|
+
if (numMatch) {
|
|
140
|
+
const content = numMatch[1].trim();
|
|
141
|
+
const checkMatch = CHECK_RE.exec(content);
|
|
142
|
+
if (checkMatch) {
|
|
143
|
+
// Check: → Then keyword → expected result column when useExpectedResult is true
|
|
144
|
+
steps.push({ keyword: 'Then', text: checkMatch[1].trim() });
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
steps.push({ keyword: 'Step', text: content });
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (!title)
|
|
152
|
+
title = line;
|
|
153
|
+
}
|
|
154
|
+
if (!title) {
|
|
155
|
+
title = methodName.replace(/([A-Z])/g, ' $1').trim();
|
|
156
|
+
}
|
|
157
|
+
return { title, steps };
|
|
158
|
+
}
|
|
159
|
+
function extractAttributesAndMethod(lines, testMethodLineIdx, tagPrefix, constants) {
|
|
160
|
+
const categories = [];
|
|
161
|
+
const properties = new Map();
|
|
162
|
+
let methodName = '';
|
|
163
|
+
let endLine = testMethodLineIdx;
|
|
164
|
+
for (let i = testMethodLineIdx + 1; i < lines.length && i < testMethodLineIdx + 30; i++) {
|
|
165
|
+
const line = lines[i].trim();
|
|
166
|
+
endLine = i;
|
|
167
|
+
// Method signature — extract name and stop
|
|
168
|
+
const methodMatch = line.match(/(?:public|private|protected|internal)\s+(?:(?:async|static|virtual|override)\s+)*(?:void|Task|[\w<>[\]]+)\s+(\w+)\s*\(/);
|
|
169
|
+
if (methodMatch) {
|
|
170
|
+
methodName = methodMatch[1];
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
// [TestCategory("literal"|CONSTANT)] — MSTest
|
|
174
|
+
const msCatMatch = line.match(/\[TestCategory\(\s*(?:"([^"]+)"|([A-Za-z_]\w*))\s*\)\]/);
|
|
175
|
+
if (msCatMatch) {
|
|
176
|
+
const literal = msCatMatch[1];
|
|
177
|
+
const constName = msCatMatch[2];
|
|
178
|
+
if (literal) {
|
|
179
|
+
categories.push(literal);
|
|
180
|
+
}
|
|
181
|
+
else if (constName && constants.has(constName)) {
|
|
182
|
+
categories.push(constants.get(constName));
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
// [Category("literal"|CONSTANT)] — NUnit
|
|
187
|
+
// Use a negative lookbehind concept: ensure it's [Category(], not [TestCategory(]
|
|
188
|
+
const nuCatMatch = line.match(/(?<!\w)\[Category\(\s*(?:"([^"]+)"|([A-Za-z_]\w*))\s*\)\]/);
|
|
189
|
+
if (nuCatMatch) {
|
|
190
|
+
const literal = nuCatMatch[1];
|
|
191
|
+
const constName = nuCatMatch[2];
|
|
192
|
+
if (literal) {
|
|
193
|
+
categories.push(literal);
|
|
194
|
+
}
|
|
195
|
+
else if (constName && constants.has(constName)) {
|
|
196
|
+
categories.push(constants.get(constName));
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// [TestProperty("key", "value")] — MSTest
|
|
201
|
+
const msPropMatch = line.match(/\[TestProperty\(\s*"([^"]+)"\s*,\s*"([^"]*)"\s*\)\]/);
|
|
202
|
+
if (msPropMatch) {
|
|
203
|
+
properties.set(msPropMatch[1], msPropMatch[2]);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
// [Property("key", "value")] — NUnit
|
|
207
|
+
const nuPropMatch = line.match(/(?<!\w)\[Property\(\s*"([^"]+)"\s*,\s*"([^"]*)"\s*\)\]/);
|
|
208
|
+
if (nuPropMatch) {
|
|
209
|
+
properties.set(nuPropMatch[1], nuPropMatch[2]);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (!line || line === '{')
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
return { categories, properties, methodName, endLine };
|
|
216
|
+
}
|
|
217
|
+
// ─── Public parser ────────────────────────────────────────────────────────────
|
|
218
|
+
function parseCsharpFile(filePath, tagPrefix, linkConfigs) {
|
|
219
|
+
const source = fs.readFileSync(filePath, 'utf8');
|
|
220
|
+
const lines = source.split('\n');
|
|
221
|
+
const namespace = extractNamespace(lines);
|
|
222
|
+
const className = extractClassName(lines);
|
|
223
|
+
const constants = resolveStringConstants(lines);
|
|
224
|
+
const pathTags = (0, shared_1.extractPathTags)(filePath);
|
|
225
|
+
const results = [];
|
|
226
|
+
for (let i = 0; i < lines.length; i++) {
|
|
227
|
+
const trimmed = lines[i].trim();
|
|
228
|
+
const framework = detectTestMarker(trimmed);
|
|
229
|
+
if (!framework)
|
|
230
|
+
continue;
|
|
231
|
+
const testMethodLineIdx = i;
|
|
232
|
+
const summaryLines = extractSummaryBefore(lines, testMethodLineIdx);
|
|
233
|
+
const { categories, properties, methodName, endLine } = extractAttributesAndMethod(lines, testMethodLineIdx, tagPrefix, constants);
|
|
234
|
+
if (!methodName) {
|
|
235
|
+
i = endLine;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const { title, steps } = parseSummary(summaryLines, methodName);
|
|
239
|
+
const tcIdStr = properties.get(tagPrefix);
|
|
240
|
+
const azureId = tcIdStr ? parseInt(tcIdStr, 10) : undefined;
|
|
241
|
+
const tags = [...new Set([...pathTags, ...categories])];
|
|
242
|
+
const fqmn = [namespace, className, methodName].filter(Boolean).join('.');
|
|
243
|
+
results.push({
|
|
244
|
+
filePath,
|
|
245
|
+
title,
|
|
246
|
+
steps,
|
|
247
|
+
tags,
|
|
248
|
+
azureId: azureId !== undefined && !isNaN(azureId) ? azureId : undefined,
|
|
249
|
+
line: testMethodLineIdx + 1, // 1-based; writeback uses this
|
|
250
|
+
linkRefs: (0, shared_1.extractLinkRefs)(tags, linkConfigs),
|
|
251
|
+
automatedTestName: fqmn || undefined,
|
|
252
|
+
});
|
|
253
|
+
i = endLine;
|
|
254
|
+
}
|
|
255
|
+
return results;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=csharp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csharp.js","sourceRoot":"","sources":["../../src/parsers/csharp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Java test parser for azure-test-sync.
|
|
3
|
+
*
|
|
4
|
+
* Supports JUnit 4, JUnit 5 (Jupiter), and TestNG frameworks:
|
|
5
|
+
*
|
|
6
|
+
* Framework Test marker Tag / group attr
|
|
7
|
+
* ───────── ─────────── ────────────────
|
|
8
|
+
* JUnit 4 @Test @Category({Smoke.class, Regression.class})
|
|
9
|
+
* JUnit 5 @Test @Tag("smoke")
|
|
10
|
+
* TestNG @Test @Test(groups = {"smoke"}) or @Test(groups = "smoke")
|
|
11
|
+
*
|
|
12
|
+
* Source mapping:
|
|
13
|
+
* Javadoc /** ... * / first non-numbered line → TC Title
|
|
14
|
+
* Numbered lines "N. text" → TC Steps (action)
|
|
15
|
+
* "N. Check: text" → TC Steps (expected result column)
|
|
16
|
+
* @Tag("name") → TC Tags (JUnit 5)
|
|
17
|
+
* @Category({Smoke.class}) → TC Tags (JUnit 4)
|
|
18
|
+
* @Test(groups = {"smoke"}) → TC Tags (TestNG)
|
|
19
|
+
* @Test(description = "...") → TC Title fallback (TestNG)
|
|
20
|
+
* @Tag("tc:N") in the annotation block → Azure TC ID (JUnit 5)
|
|
21
|
+
* // @tc:N comment above @Test → Azure TC ID (JUnit 4 / TestNG / fallback)
|
|
22
|
+
* package.ClassName.methodName → automatedTestName
|
|
23
|
+
*
|
|
24
|
+
* ID writeback:
|
|
25
|
+
* JUnit 5 → @Tag("tc:12345") inserted/updated in the annotation block above @Test.
|
|
26
|
+
* No extra dependency — @Tag is part of junit-jupiter-api.
|
|
27
|
+
* JUnit 4 → // @tc:12345 comment inserted/updated immediately above @Test.
|
|
28
|
+
* TestNG → // @tc:12345 comment inserted/updated immediately above @Test.
|
|
29
|
+
* Note: @Test(tc="...") is NOT valid TestNG syntax; @Test has no such attribute.
|
|
30
|
+
*
|
|
31
|
+
* Path-based auto-tagging: directory segments starting with '@' become tags.
|
|
32
|
+
*/
|
|
33
|
+
import { LinkConfig, ParsedTest } from '../types';
|
|
34
|
+
export type JavaTestFramework = 'junit5' | 'junit4' | 'testng' | 'unknown';
|
|
35
|
+
/**
|
|
36
|
+
* Detect the test framework from import statements.
|
|
37
|
+
* Returns 'junit5' when org.junit.jupiter imports are present.
|
|
38
|
+
*/
|
|
39
|
+
export declare function detectJavaFramework(lines: string[]): JavaTestFramework;
|
|
40
|
+
export declare function parseJavaFile(filePath: string, tagPrefix: string, linkConfigs?: LinkConfig[]): ParsedTest[];
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Java test parser for azure-test-sync.
|
|
4
|
+
*
|
|
5
|
+
* Supports JUnit 4, JUnit 5 (Jupiter), and TestNG frameworks:
|
|
6
|
+
*
|
|
7
|
+
* Framework Test marker Tag / group attr
|
|
8
|
+
* ───────── ─────────── ────────────────
|
|
9
|
+
* JUnit 4 @Test @Category({Smoke.class, Regression.class})
|
|
10
|
+
* JUnit 5 @Test @Tag("smoke")
|
|
11
|
+
* TestNG @Test @Test(groups = {"smoke"}) or @Test(groups = "smoke")
|
|
12
|
+
*
|
|
13
|
+
* Source mapping:
|
|
14
|
+
* Javadoc /** ... * / first non-numbered line → TC Title
|
|
15
|
+
* Numbered lines "N. text" → TC Steps (action)
|
|
16
|
+
* "N. Check: text" → TC Steps (expected result column)
|
|
17
|
+
* @Tag("name") → TC Tags (JUnit 5)
|
|
18
|
+
* @Category({Smoke.class}) → TC Tags (JUnit 4)
|
|
19
|
+
* @Test(groups = {"smoke"}) → TC Tags (TestNG)
|
|
20
|
+
* @Test(description = "...") → TC Title fallback (TestNG)
|
|
21
|
+
* @Tag("tc:N") in the annotation block → Azure TC ID (JUnit 5)
|
|
22
|
+
* // @tc:N comment above @Test → Azure TC ID (JUnit 4 / TestNG / fallback)
|
|
23
|
+
* package.ClassName.methodName → automatedTestName
|
|
24
|
+
*
|
|
25
|
+
* ID writeback:
|
|
26
|
+
* JUnit 5 → @Tag("tc:12345") inserted/updated in the annotation block above @Test.
|
|
27
|
+
* No extra dependency — @Tag is part of junit-jupiter-api.
|
|
28
|
+
* JUnit 4 → // @tc:12345 comment inserted/updated immediately above @Test.
|
|
29
|
+
* TestNG → // @tc:12345 comment inserted/updated immediately above @Test.
|
|
30
|
+
* Note: @Test(tc="...") is NOT valid TestNG syntax; @Test has no such attribute.
|
|
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.detectJavaFramework = detectJavaFramework;
|
|
69
|
+
exports.parseJavaFile = parseJavaFile;
|
|
70
|
+
const fs = __importStar(require("fs"));
|
|
71
|
+
const shared_1 = require("./shared");
|
|
72
|
+
/**
|
|
73
|
+
* Detect the test framework from import statements.
|
|
74
|
+
* Returns 'junit5' when org.junit.jupiter imports are present.
|
|
75
|
+
*/
|
|
76
|
+
function detectJavaFramework(lines) {
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
const trimmed = line.trim();
|
|
79
|
+
if (trimmed.startsWith('import org.junit.jupiter.'))
|
|
80
|
+
return 'junit5';
|
|
81
|
+
if (trimmed.startsWith('import org.testng.'))
|
|
82
|
+
return 'testng';
|
|
83
|
+
if (trimmed.startsWith('import org.junit.Test') || trimmed.startsWith('import org.junit.runner.'))
|
|
84
|
+
return 'junit4';
|
|
85
|
+
}
|
|
86
|
+
return 'unknown';
|
|
87
|
+
}
|
|
88
|
+
// ─── Package / class extraction ───────────────────────────────────────────────
|
|
89
|
+
function extractPackage(lines) {
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
const m = line.match(/^\s*package\s+([\w.]+)\s*;/);
|
|
92
|
+
if (m)
|
|
93
|
+
return m[1];
|
|
94
|
+
}
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
function extractClassName(lines) {
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const m = line.match(/^\s*(?:(?:public|protected|private|abstract|final)\s+)*class\s+(\w+)/);
|
|
100
|
+
if (m)
|
|
101
|
+
return m[1];
|
|
102
|
+
}
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
// ─── @Test annotation detection ───────────────────────────────────────────────
|
|
106
|
+
/**
|
|
107
|
+
* Returns true when the trimmed line is a @Test annotation.
|
|
108
|
+
* Matches @Test and @Test(...) but not @TestFactory / @TestTemplate.
|
|
109
|
+
*/
|
|
110
|
+
function isTestAnnotation(trimmedLine) {
|
|
111
|
+
return /^@Test(?:$|\s*\()/.test(trimmedLine);
|
|
112
|
+
}
|
|
113
|
+
// ─── Javadoc extraction ───────────────────────────────────────────────────────
|
|
114
|
+
/**
|
|
115
|
+
* Walk backward from testLineIdx to find a Javadoc comment (/** ... * /).
|
|
116
|
+
* Skips blank lines, // comments, and @annotation lines.
|
|
117
|
+
*/
|
|
118
|
+
function extractJavadocBefore(lines, testLineIdx) {
|
|
119
|
+
let i = testLineIdx - 1;
|
|
120
|
+
while (i >= 0) {
|
|
121
|
+
const t = lines[i].trim();
|
|
122
|
+
if (t === '' || t.startsWith('//') || (t.startsWith('@') && !t.endsWith('*/'))) {
|
|
123
|
+
i--;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
if (i < 0 || !lines[i].trim().endsWith('*/'))
|
|
129
|
+
return [];
|
|
130
|
+
const raw = [];
|
|
131
|
+
raw.unshift(lines[i]);
|
|
132
|
+
i--;
|
|
133
|
+
while (i >= 0) {
|
|
134
|
+
raw.unshift(lines[i]);
|
|
135
|
+
if (lines[i].trim().startsWith('/**'))
|
|
136
|
+
break;
|
|
137
|
+
i--;
|
|
138
|
+
}
|
|
139
|
+
return raw
|
|
140
|
+
.map((l) => l
|
|
141
|
+
.replace(/^\s*\/\*\*\s?/, '')
|
|
142
|
+
.replace(/\s*\*\/\s*$/, '')
|
|
143
|
+
.replace(/^\s*\*\s?/, '')
|
|
144
|
+
.trim())
|
|
145
|
+
.filter((l) => l !== '');
|
|
146
|
+
}
|
|
147
|
+
// ─── TC ID extraction — @Tag (JUnit 5) + comment fallback ─────────────────────
|
|
148
|
+
/**
|
|
149
|
+
* Scan above the @Test line (up to 25 lines) for a TC ID stored as either:
|
|
150
|
+
* @Tag("{tagPrefix}:N") — JUnit 5 style
|
|
151
|
+
* // @{tagPrefix}:N — comment style (JUnit 4 / TestNG)
|
|
152
|
+
*
|
|
153
|
+
* Stops at a closing brace or class declaration.
|
|
154
|
+
*/
|
|
155
|
+
function extractTcIdAbove(lines, testLineIdx, tagPrefix) {
|
|
156
|
+
const tagRe = new RegExp(`^@Tag\\(\\s*"${tagPrefix}:(\\d+)"\\s*\\)$`);
|
|
157
|
+
const cmmtRe = new RegExp(`//\\s*@${tagPrefix}:(\\d+)`);
|
|
158
|
+
for (let i = testLineIdx - 1; i >= 0 && i >= testLineIdx - 25; i--) {
|
|
159
|
+
const trimmed = lines[i].trim();
|
|
160
|
+
const tagMatch = trimmed.match(tagRe);
|
|
161
|
+
if (tagMatch)
|
|
162
|
+
return parseInt(tagMatch[1], 10);
|
|
163
|
+
const cmtMatch = trimmed.match(cmmtRe);
|
|
164
|
+
if (cmtMatch)
|
|
165
|
+
return parseInt(cmtMatch[1], 10);
|
|
166
|
+
if (trimmed === '}' || /^\s*(?:(?:public|protected|private|abstract|final)\s+)*class\s+/.test(trimmed))
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
function parseTestAnnotationAttrs(testLine) {
|
|
172
|
+
const groups = [];
|
|
173
|
+
let description = '';
|
|
174
|
+
const multiGroupMatch = testLine.match(/\bgroups\s*=\s*\{([^}]*)\}/);
|
|
175
|
+
if (multiGroupMatch) {
|
|
176
|
+
const literals = multiGroupMatch[1].match(/"([^"]+)"/g);
|
|
177
|
+
if (literals)
|
|
178
|
+
groups.push(...literals.map((g) => g.replace(/"/g, '')));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const singleGroupMatch = testLine.match(/\bgroups\s*=\s*"([^"]+)"/);
|
|
182
|
+
if (singleGroupMatch)
|
|
183
|
+
groups.push(singleGroupMatch[1]);
|
|
184
|
+
}
|
|
185
|
+
const descMatch = testLine.match(/\bdescription\s*=\s*"([^"]+)"/);
|
|
186
|
+
if (descMatch)
|
|
187
|
+
description = descMatch[1];
|
|
188
|
+
return { groups, description };
|
|
189
|
+
}
|
|
190
|
+
// ─── Forward scan: annotations + method signature ─────────────────────────────
|
|
191
|
+
const JAVA_KEYWORDS = new Set([
|
|
192
|
+
'if', 'for', 'while', 'switch', 'catch', 'super', 'this', 'new', 'return', 'throw',
|
|
193
|
+
'instanceof', 'assert', 'synchronized',
|
|
194
|
+
]);
|
|
195
|
+
function extractMethodInfo(lines, testLineIdx) {
|
|
196
|
+
const tagValues = [];
|
|
197
|
+
let methodName = '';
|
|
198
|
+
let endLine = testLineIdx;
|
|
199
|
+
// TestNG groups on the @Test line itself — push into tagValues so they surface as TC tags
|
|
200
|
+
const { groups } = parseTestAnnotationAttrs(lines[testLineIdx] ?? '');
|
|
201
|
+
tagValues.push(...groups);
|
|
202
|
+
for (let i = testLineIdx + 1; i < lines.length && i < testLineIdx + 30; i++) {
|
|
203
|
+
const trimmed = lines[i].trim();
|
|
204
|
+
endLine = i;
|
|
205
|
+
// @Tag("value") — JUnit 5 (includes potential tc:N ID tags)
|
|
206
|
+
const tagMatch = trimmed.match(/^@Tag\(\s*"([^"]+)"\s*\)/);
|
|
207
|
+
if (tagMatch) {
|
|
208
|
+
tagValues.push(tagMatch[1]);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
// @Category({Smoke.class, Reg.class}) — JUnit 4
|
|
212
|
+
const multiCatMatch = trimmed.match(/^@Category\(\s*\{([^}]*)\}\s*\)/);
|
|
213
|
+
if (multiCatMatch) {
|
|
214
|
+
const classRefs = multiCatMatch[1].match(/(\w+)\.class/g);
|
|
215
|
+
if (classRefs)
|
|
216
|
+
tagValues.push(...classRefs.map((c) => c.replace('.class', '')));
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
// @Category(Smoke.class) — single
|
|
220
|
+
const singleCatMatch = trimmed.match(/^@Category\(\s*(\w+)\.class\s*\)/);
|
|
221
|
+
if (singleCatMatch) {
|
|
222
|
+
tagValues.push(singleCatMatch[1]);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
// Other @Annotation — skip (e.g. @DisplayName, @Timeout, @AzureTestCase)
|
|
226
|
+
if (trimmed.startsWith('@'))
|
|
227
|
+
continue;
|
|
228
|
+
// Blank / comment interior
|
|
229
|
+
if (trimmed === '' || trimmed.startsWith('//') || trimmed.startsWith('*'))
|
|
230
|
+
continue;
|
|
231
|
+
// Method signature: first word before ( that is not a keyword
|
|
232
|
+
if (trimmed.includes('(')) {
|
|
233
|
+
const m = trimmed.match(/(\w+)\s*\(/);
|
|
234
|
+
if (m && !JAVA_KEYWORDS.has(m[1])) {
|
|
235
|
+
methodName = m[1];
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (trimmed === '{')
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
return { methodName, tagValues, endLine };
|
|
243
|
+
}
|
|
244
|
+
// ─── Javadoc → title + steps ──────────────────────────────────────────────────
|
|
245
|
+
const NUMBERED_STEP_RE = /^\d+\.\s+(.+)$/;
|
|
246
|
+
const CHECK_RE = /^[Cc]heck:\s+(.+)$/;
|
|
247
|
+
const META_RE = /^(?:test\s+case|user\s+story)[\s:]/i;
|
|
248
|
+
function parseSummary(summaryLines, methodName, descriptionFallback) {
|
|
249
|
+
let title = '';
|
|
250
|
+
const steps = [];
|
|
251
|
+
for (const line of summaryLines) {
|
|
252
|
+
if (!line || META_RE.test(line))
|
|
253
|
+
continue;
|
|
254
|
+
const numMatch = NUMBERED_STEP_RE.exec(line);
|
|
255
|
+
if (numMatch) {
|
|
256
|
+
const content = numMatch[1].trim();
|
|
257
|
+
const checkMatch = CHECK_RE.exec(content);
|
|
258
|
+
if (checkMatch) {
|
|
259
|
+
steps.push({ keyword: 'Then', text: checkMatch[1].trim() });
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
steps.push({ keyword: 'Step', text: content });
|
|
263
|
+
}
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (!title)
|
|
267
|
+
title = line;
|
|
268
|
+
}
|
|
269
|
+
if (!title) {
|
|
270
|
+
title = descriptionFallback || methodName.replace(/([A-Z])/g, ' $1').trim();
|
|
271
|
+
}
|
|
272
|
+
return { title, steps };
|
|
273
|
+
}
|
|
274
|
+
// ─── Public parser ────────────────────────────────────────────────────────────
|
|
275
|
+
function parseJavaFile(filePath, tagPrefix, linkConfigs) {
|
|
276
|
+
const source = fs.readFileSync(filePath, 'utf8');
|
|
277
|
+
const lines = source.split('\n');
|
|
278
|
+
const pkg = extractPackage(lines);
|
|
279
|
+
const className = extractClassName(lines);
|
|
280
|
+
const pathTags = (0, shared_1.extractPathTags)(filePath);
|
|
281
|
+
// ID tag regex: matches e.g. "tc:12345" inside a @Tag value
|
|
282
|
+
const idTagValueRe = new RegExp(`^${tagPrefix}:(\\d+)$`);
|
|
283
|
+
const results = [];
|
|
284
|
+
for (let i = 0; i < lines.length; i++) {
|
|
285
|
+
const trimmed = lines[i].trim();
|
|
286
|
+
if (!isTestAnnotation(trimmed))
|
|
287
|
+
continue;
|
|
288
|
+
const testAnnotationLineIdx = i;
|
|
289
|
+
const summaryLines = extractJavadocBefore(lines, testAnnotationLineIdx);
|
|
290
|
+
const { description: testNgDescription, groups: testNgGroups } = parseTestAnnotationAttrs(lines[testAnnotationLineIdx] ?? '');
|
|
291
|
+
const { methodName, tagValues, endLine } = extractMethodInfo(lines, testAnnotationLineIdx);
|
|
292
|
+
if (!methodName) {
|
|
293
|
+
i = endLine;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
// Separate tc-ID tags from regular @Tag values (JUnit 5)
|
|
297
|
+
let azureId;
|
|
298
|
+
const regularTagValues = [];
|
|
299
|
+
for (const tv of tagValues) {
|
|
300
|
+
const m = tv.match(idTagValueRe);
|
|
301
|
+
if (m && azureId === undefined) {
|
|
302
|
+
azureId = parseInt(m[1], 10);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
regularTagValues.push(tv);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Fallback: look for ID above @Test (@Tag or comment)
|
|
309
|
+
if (azureId === undefined) {
|
|
310
|
+
azureId = extractTcIdAbove(lines, testAnnotationLineIdx, tagPrefix);
|
|
311
|
+
}
|
|
312
|
+
const allTags = [...new Set([...pathTags, ...regularTagValues, ...testNgGroups])];
|
|
313
|
+
const { title, steps } = parseSummary(summaryLines, methodName, testNgDescription);
|
|
314
|
+
const fqmn = [pkg, className, methodName].filter(Boolean).join('.');
|
|
315
|
+
results.push({
|
|
316
|
+
filePath,
|
|
317
|
+
title,
|
|
318
|
+
steps,
|
|
319
|
+
tags: allTags,
|
|
320
|
+
azureId: azureId !== undefined && !isNaN(azureId) ? azureId : undefined,
|
|
321
|
+
line: testAnnotationLineIdx + 1, // 1-based; writeback targets this @Test line
|
|
322
|
+
linkRefs: (0, shared_1.extractLinkRefs)(allTags, linkConfigs),
|
|
323
|
+
automatedTestName: fqmn || undefined,
|
|
324
|
+
});
|
|
325
|
+
i = endLine;
|
|
326
|
+
}
|
|
327
|
+
return results;
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=java.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"java.js","sourceRoot":"","sources":["../../src/parsers/java.ts"],"names":[],"mappings":"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"}
|