tstyche 5.0.0-beta.0 → 5.0.0-beta.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 +1 -1
- package/build/4.x/index.d.cts +251 -0
- package/build/4.x/index.d.ts +251 -0
- package/build/index.d.cts +18 -18
- package/build/index.d.ts +18 -18
- package/build/tstyche.d.ts +180 -274
- package/build/tstyche.js +715 -533
- package/package.json +12 -1
package/build/tstyche.js
CHANGED
|
@@ -50,70 +50,41 @@ class EventEmitter {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
class
|
|
54
|
-
|
|
55
|
-
static {
|
|
56
|
-
if (path.sep === "/") {
|
|
57
|
-
Path.normalizeSlashes = (filePath) => filePath;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
Path.normalizeSlashes = (filePath) => filePath.replace(/\\/g, "/");
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
static dirname(filePath) {
|
|
64
|
-
return Path.normalizeSlashes(path.dirname(filePath));
|
|
65
|
-
}
|
|
66
|
-
static join(...filePaths) {
|
|
67
|
-
return Path.normalizeSlashes(path.join(...filePaths));
|
|
68
|
-
}
|
|
69
|
-
static relative(from, to) {
|
|
70
|
-
const relativePath = Path.normalizeSlashes(path.relative(from, to));
|
|
71
|
-
if (/^\.\.?\//.test(relativePath)) {
|
|
72
|
-
return relativePath;
|
|
73
|
-
}
|
|
74
|
-
return `./${relativePath}`;
|
|
75
|
-
}
|
|
76
|
-
static resolve(...filePaths) {
|
|
77
|
-
return Path.normalizeSlashes(path.resolve(...filePaths));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
class SourceFile {
|
|
82
|
-
fileName;
|
|
83
|
-
#lineMap;
|
|
53
|
+
class JsonNode {
|
|
54
|
+
origin;
|
|
84
55
|
text;
|
|
85
|
-
constructor(
|
|
86
|
-
this.
|
|
56
|
+
constructor(text, origin) {
|
|
57
|
+
this.origin = origin;
|
|
87
58
|
this.text = text;
|
|
88
|
-
this.#lineMap = this.#createLineMap();
|
|
89
59
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
while (position < this.text.length) {
|
|
94
|
-
if (this.text.charAt(position - 1) === "\r") {
|
|
95
|
-
position++;
|
|
96
|
-
}
|
|
97
|
-
if (this.text.charAt(position - 1) === "\n") {
|
|
98
|
-
result.push(position);
|
|
99
|
-
}
|
|
100
|
-
position++;
|
|
60
|
+
getValue(options) {
|
|
61
|
+
if (this.text == null) {
|
|
62
|
+
return undefined;
|
|
101
63
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
64
|
+
if (/^['"]/.test(this.text)) {
|
|
65
|
+
return this.text.slice(1, -1);
|
|
66
|
+
}
|
|
67
|
+
if (options?.expectsIdentifier) {
|
|
68
|
+
return this.text;
|
|
69
|
+
}
|
|
70
|
+
if (this.text === "true") {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (this.text === "false") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (/^\d/.test(this.text)) {
|
|
77
|
+
return Number.parseFloat(this.text);
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
112
80
|
}
|
|
113
81
|
}
|
|
114
82
|
|
|
115
83
|
class SourceService {
|
|
116
84
|
static #files = new Map();
|
|
85
|
+
static delete(filePath) {
|
|
86
|
+
SourceService.#files.delete(filePath);
|
|
87
|
+
}
|
|
117
88
|
static get(source) {
|
|
118
89
|
const file = SourceService.#files.get(source.fileName);
|
|
119
90
|
if (file != null) {
|
|
@@ -225,6 +196,171 @@ var DiagnosticCategory;
|
|
|
225
196
|
DiagnosticCategory["Warning"] = "warning";
|
|
226
197
|
})(DiagnosticCategory || (DiagnosticCategory = {}));
|
|
227
198
|
|
|
199
|
+
class JsonScanner {
|
|
200
|
+
#end;
|
|
201
|
+
#position;
|
|
202
|
+
#previousPosition;
|
|
203
|
+
#sourceFile;
|
|
204
|
+
constructor(sourceFile, options) {
|
|
205
|
+
this.#end = options?.end ?? sourceFile.text.length;
|
|
206
|
+
this.#position = options?.start ?? 0;
|
|
207
|
+
this.#previousPosition = options?.start ?? 0;
|
|
208
|
+
this.#sourceFile = sourceFile;
|
|
209
|
+
}
|
|
210
|
+
#getOrigin() {
|
|
211
|
+
return new DiagnosticOrigin(this.#previousPosition, this.#position, this.#sourceFile);
|
|
212
|
+
}
|
|
213
|
+
isRead() {
|
|
214
|
+
return !(this.#position < this.#end);
|
|
215
|
+
}
|
|
216
|
+
#peekCharacter() {
|
|
217
|
+
return this.#sourceFile.text.charAt(this.#position);
|
|
218
|
+
}
|
|
219
|
+
#peekNextCharacter() {
|
|
220
|
+
return this.#sourceFile.text.charAt(this.#position + 1);
|
|
221
|
+
}
|
|
222
|
+
peekToken(token) {
|
|
223
|
+
this.#skipTrivia();
|
|
224
|
+
return this.#peekCharacter() === token;
|
|
225
|
+
}
|
|
226
|
+
read() {
|
|
227
|
+
this.#skipTrivia();
|
|
228
|
+
this.#previousPosition = this.#position;
|
|
229
|
+
if (/[\s,:\]}]/.test(this.#peekCharacter())) {
|
|
230
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
231
|
+
}
|
|
232
|
+
let text = "";
|
|
233
|
+
let closingTokenText = "";
|
|
234
|
+
if (/[[{'"]/.test(this.#peekCharacter())) {
|
|
235
|
+
text += this.#readCharacter();
|
|
236
|
+
switch (text) {
|
|
237
|
+
case "[":
|
|
238
|
+
closingTokenText = "]";
|
|
239
|
+
break;
|
|
240
|
+
case "{":
|
|
241
|
+
closingTokenText = "}";
|
|
242
|
+
break;
|
|
243
|
+
default:
|
|
244
|
+
closingTokenText = text;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
while (!this.isRead()) {
|
|
248
|
+
text += this.#readCharacter();
|
|
249
|
+
if (text.slice(-1) === closingTokenText || (!closingTokenText && /[\s,:\]}]/.test(this.#peekCharacter()))) {
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return new JsonNode(text, this.#getOrigin());
|
|
254
|
+
}
|
|
255
|
+
#readCharacter() {
|
|
256
|
+
return this.#sourceFile.text.charAt(this.#position++);
|
|
257
|
+
}
|
|
258
|
+
readToken(token) {
|
|
259
|
+
this.#skipTrivia();
|
|
260
|
+
this.#previousPosition = this.#position;
|
|
261
|
+
const character = this.#peekCharacter();
|
|
262
|
+
if (typeof token === "string" ? token === character : token.test(character)) {
|
|
263
|
+
this.#position++;
|
|
264
|
+
return new JsonNode(character, this.#getOrigin());
|
|
265
|
+
}
|
|
266
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
267
|
+
}
|
|
268
|
+
#skipTrivia() {
|
|
269
|
+
while (!this.isRead()) {
|
|
270
|
+
if (/\s/.test(this.#peekCharacter())) {
|
|
271
|
+
this.#position++;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (this.#peekCharacter() === "/") {
|
|
275
|
+
if (this.#peekNextCharacter() === "/") {
|
|
276
|
+
this.#position += 2;
|
|
277
|
+
while (!this.isRead()) {
|
|
278
|
+
if (this.#readCharacter() === "\n") {
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (this.#peekNextCharacter() === "*") {
|
|
285
|
+
this.#position += 2;
|
|
286
|
+
while (!this.isRead()) {
|
|
287
|
+
if (this.#peekCharacter() === "*" && this.#peekNextCharacter() === "/") {
|
|
288
|
+
this.#position += 2;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
this.#position++;
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
this.#previousPosition = this.#position;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
class JsonSourceFile {
|
|
303
|
+
fileName;
|
|
304
|
+
#lineMap;
|
|
305
|
+
text;
|
|
306
|
+
constructor(fileName, text) {
|
|
307
|
+
this.fileName = fileName;
|
|
308
|
+
this.text = text;
|
|
309
|
+
this.#lineMap = this.#createLineMap();
|
|
310
|
+
}
|
|
311
|
+
#createLineMap() {
|
|
312
|
+
const result = [0];
|
|
313
|
+
let position = 0;
|
|
314
|
+
while (position < this.text.length) {
|
|
315
|
+
if (this.text.charAt(position - 1) === "\r") {
|
|
316
|
+
position++;
|
|
317
|
+
}
|
|
318
|
+
if (this.text.charAt(position - 1) === "\n") {
|
|
319
|
+
result.push(position);
|
|
320
|
+
}
|
|
321
|
+
position++;
|
|
322
|
+
}
|
|
323
|
+
result.push(position);
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
getLineStarts() {
|
|
327
|
+
return this.#lineMap;
|
|
328
|
+
}
|
|
329
|
+
getLineAndCharacterOfPosition(position) {
|
|
330
|
+
const line = this.#lineMap.findLastIndex((line) => line <= position);
|
|
331
|
+
const character = position - this.#lineMap[line];
|
|
332
|
+
return { line, character };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
class Path {
|
|
337
|
+
static normalizeSlashes;
|
|
338
|
+
static {
|
|
339
|
+
if (path.sep === "/") {
|
|
340
|
+
Path.normalizeSlashes = (filePath) => filePath;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
Path.normalizeSlashes = (filePath) => filePath.replace(/\\/g, "/");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
static dirname(filePath) {
|
|
347
|
+
return Path.normalizeSlashes(path.dirname(filePath));
|
|
348
|
+
}
|
|
349
|
+
static join(...filePaths) {
|
|
350
|
+
return Path.normalizeSlashes(path.join(...filePaths));
|
|
351
|
+
}
|
|
352
|
+
static relative(from, to) {
|
|
353
|
+
const relativePath = Path.normalizeSlashes(path.relative(from, to));
|
|
354
|
+
if (/^\.\.?\//.test(relativePath)) {
|
|
355
|
+
return relativePath;
|
|
356
|
+
}
|
|
357
|
+
return `./${relativePath}`;
|
|
358
|
+
}
|
|
359
|
+
static resolve(...filePaths) {
|
|
360
|
+
return Path.normalizeSlashes(path.resolve(...filePaths));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
228
364
|
class ConfigDiagnosticText {
|
|
229
365
|
static expected(element) {
|
|
230
366
|
return `Expected ${element}.`;
|
|
@@ -1470,139 +1606,6 @@ const defaultOptions = {
|
|
|
1470
1606
|
tsconfig: "findup",
|
|
1471
1607
|
};
|
|
1472
1608
|
|
|
1473
|
-
class JsonNode {
|
|
1474
|
-
origin;
|
|
1475
|
-
text;
|
|
1476
|
-
constructor(text, origin) {
|
|
1477
|
-
this.origin = origin;
|
|
1478
|
-
this.text = text;
|
|
1479
|
-
}
|
|
1480
|
-
getValue(options) {
|
|
1481
|
-
if (this.text == null) {
|
|
1482
|
-
return undefined;
|
|
1483
|
-
}
|
|
1484
|
-
if (/^['"]/.test(this.text)) {
|
|
1485
|
-
return this.text.slice(1, -1);
|
|
1486
|
-
}
|
|
1487
|
-
if (options?.expectsIdentifier) {
|
|
1488
|
-
return this.text;
|
|
1489
|
-
}
|
|
1490
|
-
if (this.text === "true") {
|
|
1491
|
-
return true;
|
|
1492
|
-
}
|
|
1493
|
-
if (this.text === "false") {
|
|
1494
|
-
return false;
|
|
1495
|
-
}
|
|
1496
|
-
if (/^\d/.test(this.text)) {
|
|
1497
|
-
return Number.parseFloat(this.text);
|
|
1498
|
-
}
|
|
1499
|
-
return undefined;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
class JsonScanner {
|
|
1504
|
-
#end;
|
|
1505
|
-
#position;
|
|
1506
|
-
#previousPosition;
|
|
1507
|
-
#sourceFile;
|
|
1508
|
-
constructor(sourceFile, options) {
|
|
1509
|
-
this.#end = options?.end ?? sourceFile.text.length;
|
|
1510
|
-
this.#position = options?.start ?? 0;
|
|
1511
|
-
this.#previousPosition = options?.start ?? 0;
|
|
1512
|
-
this.#sourceFile = sourceFile;
|
|
1513
|
-
}
|
|
1514
|
-
#getOrigin() {
|
|
1515
|
-
return new DiagnosticOrigin(this.#previousPosition, this.#position, this.#sourceFile);
|
|
1516
|
-
}
|
|
1517
|
-
isRead() {
|
|
1518
|
-
return !(this.#position < this.#end);
|
|
1519
|
-
}
|
|
1520
|
-
#peekCharacter() {
|
|
1521
|
-
return this.#sourceFile.text.charAt(this.#position);
|
|
1522
|
-
}
|
|
1523
|
-
#peekNextCharacter() {
|
|
1524
|
-
return this.#sourceFile.text.charAt(this.#position + 1);
|
|
1525
|
-
}
|
|
1526
|
-
peekToken(token) {
|
|
1527
|
-
this.#skipTrivia();
|
|
1528
|
-
return this.#peekCharacter() === token;
|
|
1529
|
-
}
|
|
1530
|
-
read() {
|
|
1531
|
-
this.#skipTrivia();
|
|
1532
|
-
this.#previousPosition = this.#position;
|
|
1533
|
-
if (/[\s,:\]}]/.test(this.#peekCharacter())) {
|
|
1534
|
-
return new JsonNode(undefined, this.#getOrigin());
|
|
1535
|
-
}
|
|
1536
|
-
let text = "";
|
|
1537
|
-
let closingTokenText = "";
|
|
1538
|
-
if (/[[{'"]/.test(this.#peekCharacter())) {
|
|
1539
|
-
text += this.#readCharacter();
|
|
1540
|
-
switch (text) {
|
|
1541
|
-
case "[":
|
|
1542
|
-
closingTokenText = "]";
|
|
1543
|
-
break;
|
|
1544
|
-
case "{":
|
|
1545
|
-
closingTokenText = "}";
|
|
1546
|
-
break;
|
|
1547
|
-
default:
|
|
1548
|
-
closingTokenText = text;
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
while (!this.isRead()) {
|
|
1552
|
-
text += this.#readCharacter();
|
|
1553
|
-
if (text.slice(-1) === closingTokenText || (!closingTokenText && /[\s,:\]}]/.test(this.#peekCharacter()))) {
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
return new JsonNode(text, this.#getOrigin());
|
|
1558
|
-
}
|
|
1559
|
-
#readCharacter() {
|
|
1560
|
-
return this.#sourceFile.text.charAt(this.#position++);
|
|
1561
|
-
}
|
|
1562
|
-
readToken(token) {
|
|
1563
|
-
this.#skipTrivia();
|
|
1564
|
-
this.#previousPosition = this.#position;
|
|
1565
|
-
const character = this.#peekCharacter();
|
|
1566
|
-
if (typeof token === "string" ? token === character : token.test(character)) {
|
|
1567
|
-
this.#position++;
|
|
1568
|
-
return new JsonNode(character, this.#getOrigin());
|
|
1569
|
-
}
|
|
1570
|
-
return new JsonNode(undefined, this.#getOrigin());
|
|
1571
|
-
}
|
|
1572
|
-
#skipTrivia() {
|
|
1573
|
-
while (!this.isRead()) {
|
|
1574
|
-
if (/\s/.test(this.#peekCharacter())) {
|
|
1575
|
-
this.#position++;
|
|
1576
|
-
continue;
|
|
1577
|
-
}
|
|
1578
|
-
if (this.#peekCharacter() === "/") {
|
|
1579
|
-
if (this.#peekNextCharacter() === "/") {
|
|
1580
|
-
this.#position += 2;
|
|
1581
|
-
while (!this.isRead()) {
|
|
1582
|
-
if (this.#readCharacter() === "\n") {
|
|
1583
|
-
break;
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
continue;
|
|
1587
|
-
}
|
|
1588
|
-
if (this.#peekNextCharacter() === "*") {
|
|
1589
|
-
this.#position += 2;
|
|
1590
|
-
while (!this.isRead()) {
|
|
1591
|
-
if (this.#peekCharacter() === "*" && this.#peekNextCharacter() === "/") {
|
|
1592
|
-
this.#position += 2;
|
|
1593
|
-
break;
|
|
1594
|
-
}
|
|
1595
|
-
this.#position++;
|
|
1596
|
-
}
|
|
1597
|
-
continue;
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
break;
|
|
1601
|
-
}
|
|
1602
|
-
this.#previousPosition = this.#position;
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
1609
|
class Config {
|
|
1607
1610
|
static #onDiagnostics(diagnostic) {
|
|
1608
1611
|
EventEmitter.dispatch(["config:error", { diagnostics: [diagnostic] }]);
|
|
@@ -1623,7 +1626,7 @@ class Config {
|
|
|
1623
1626
|
const configFileText = await fs.readFile(configFilePath, {
|
|
1624
1627
|
encoding: "utf8",
|
|
1625
1628
|
});
|
|
1626
|
-
const sourceFile = new
|
|
1629
|
+
const sourceFile = new JsonSourceFile(configFilePath, configFileText);
|
|
1627
1630
|
const configFileParser = new ConfigParser(configFileOptions, 4, sourceFile, new JsonScanner(sourceFile), Config.#onDiagnostics);
|
|
1628
1631
|
await configFileParser.parse();
|
|
1629
1632
|
}
|
|
@@ -1827,19 +1830,33 @@ class ExitCodeHandler {
|
|
|
1827
1830
|
}
|
|
1828
1831
|
}
|
|
1829
1832
|
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1833
|
+
function createObjectFromKeys(keys, defaultValue) {
|
|
1834
|
+
return Object.fromEntries(keys.map((key) => [key, defaultValue]));
|
|
1835
|
+
}
|
|
1836
|
+
function createTargetCounts() {
|
|
1837
|
+
return createObjectFromKeys(["failed", "passed"], 0);
|
|
1838
|
+
}
|
|
1839
|
+
function createFileCounts() {
|
|
1840
|
+
return createObjectFromKeys(["failed", "passed"], 0);
|
|
1841
|
+
}
|
|
1842
|
+
function createTestCounts() {
|
|
1843
|
+
return createObjectFromKeys(["failed", "passed", "fixme", "skipped", "todo"], 0);
|
|
1844
|
+
}
|
|
1845
|
+
function createAssertionCounts() {
|
|
1846
|
+
return createObjectFromKeys(["failed", "passed", "fixme", "skipped", "todo"], 0);
|
|
1847
|
+
}
|
|
1848
|
+
function createSuppressedCounts() {
|
|
1849
|
+
return createObjectFromKeys(["failed", "matched", "ignored"], 0);
|
|
1850
|
+
}
|
|
1851
|
+
function createResultTiming() {
|
|
1852
|
+
return createObjectFromKeys(["start", "end"], Number.NaN);
|
|
1836
1853
|
}
|
|
1837
1854
|
|
|
1838
1855
|
class DescribeResult {
|
|
1839
1856
|
describe;
|
|
1840
1857
|
parent;
|
|
1841
1858
|
results = [];
|
|
1842
|
-
timing =
|
|
1859
|
+
timing = createResultTiming();
|
|
1843
1860
|
constructor(describe, parent) {
|
|
1844
1861
|
this.describe = describe;
|
|
1845
1862
|
this.parent = parent;
|
|
@@ -1848,35 +1865,23 @@ class DescribeResult {
|
|
|
1848
1865
|
|
|
1849
1866
|
class ExpectResult {
|
|
1850
1867
|
expect;
|
|
1851
|
-
diagnostics = [];
|
|
1852
1868
|
parent;
|
|
1853
1869
|
status = "runs";
|
|
1854
|
-
timing =
|
|
1870
|
+
timing = createResultTiming();
|
|
1855
1871
|
constructor(expect, parent) {
|
|
1856
1872
|
this.expect = expect;
|
|
1857
1873
|
this.parent = parent;
|
|
1858
1874
|
}
|
|
1859
1875
|
}
|
|
1860
1876
|
|
|
1861
|
-
class ResultCount {
|
|
1862
|
-
failed = 0;
|
|
1863
|
-
passed = 0;
|
|
1864
|
-
skipped = 0;
|
|
1865
|
-
fixme = 0;
|
|
1866
|
-
todo = 0;
|
|
1867
|
-
get total() {
|
|
1868
|
-
return this.failed + this.passed + this.skipped + this.fixme + this.todo;
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
1877
|
class FileResult {
|
|
1873
|
-
|
|
1874
|
-
expectCount = new ResultCount();
|
|
1878
|
+
assertionCounts = createAssertionCounts();
|
|
1875
1879
|
file;
|
|
1876
1880
|
results = [];
|
|
1881
|
+
suppressedCounts = createSuppressedCounts();
|
|
1877
1882
|
status = "runs";
|
|
1878
|
-
|
|
1879
|
-
timing =
|
|
1883
|
+
testCounts = createTestCounts();
|
|
1884
|
+
timing = createResultTiming();
|
|
1880
1885
|
constructor(file) {
|
|
1881
1886
|
this.file = file;
|
|
1882
1887
|
}
|
|
@@ -1884,7 +1889,6 @@ class FileResult {
|
|
|
1884
1889
|
|
|
1885
1890
|
class ProjectResult {
|
|
1886
1891
|
compilerVersion;
|
|
1887
|
-
diagnostics = [];
|
|
1888
1892
|
projectConfigFilePath;
|
|
1889
1893
|
results = [];
|
|
1890
1894
|
constructor(compilerVersion, projectConfigFilePath) {
|
|
@@ -1894,13 +1898,14 @@ class ProjectResult {
|
|
|
1894
1898
|
}
|
|
1895
1899
|
|
|
1896
1900
|
class Result {
|
|
1897
|
-
|
|
1898
|
-
|
|
1901
|
+
assertionCounts = createAssertionCounts();
|
|
1902
|
+
fileCounts = createFileCounts();
|
|
1899
1903
|
files;
|
|
1900
1904
|
results = [];
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1905
|
+
suppressedCounts = createSuppressedCounts();
|
|
1906
|
+
targetCounts = createTargetCounts();
|
|
1907
|
+
testCounts = createTestCounts();
|
|
1908
|
+
timing = createResultTiming();
|
|
1904
1909
|
constructor(files) {
|
|
1905
1910
|
this.files = files;
|
|
1906
1911
|
}
|
|
@@ -1910,18 +1915,27 @@ var ResultStatus;
|
|
|
1910
1915
|
(function (ResultStatus) {
|
|
1911
1916
|
ResultStatus["Runs"] = "runs";
|
|
1912
1917
|
ResultStatus["Passed"] = "passed";
|
|
1918
|
+
ResultStatus["Matched"] = "matched";
|
|
1913
1919
|
ResultStatus["Failed"] = "failed";
|
|
1914
|
-
ResultStatus["Skipped"] = "skipped";
|
|
1915
1920
|
ResultStatus["Fixme"] = "fixme";
|
|
1921
|
+
ResultStatus["Skipped"] = "skipped";
|
|
1922
|
+
ResultStatus["Ignored"] = "ignored";
|
|
1916
1923
|
ResultStatus["Todo"] = "todo";
|
|
1917
1924
|
})(ResultStatus || (ResultStatus = {}));
|
|
1918
1925
|
|
|
1926
|
+
class SuppressedResult {
|
|
1927
|
+
suppressed;
|
|
1928
|
+
constructor(suppressed) {
|
|
1929
|
+
this.suppressed = suppressed;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1919
1933
|
class TargetResult {
|
|
1920
1934
|
files;
|
|
1921
1935
|
results = new Map();
|
|
1922
1936
|
status = "runs";
|
|
1923
1937
|
target;
|
|
1924
|
-
timing =
|
|
1938
|
+
timing = createResultTiming();
|
|
1925
1939
|
constructor(target, files) {
|
|
1926
1940
|
this.target = target;
|
|
1927
1941
|
this.files = files;
|
|
@@ -1929,13 +1943,12 @@ class TargetResult {
|
|
|
1929
1943
|
}
|
|
1930
1944
|
|
|
1931
1945
|
class TestResult {
|
|
1932
|
-
|
|
1933
|
-
expectCount = new ResultCount();
|
|
1946
|
+
assertionCounts = createAssertionCounts();
|
|
1934
1947
|
parent;
|
|
1935
1948
|
results = [];
|
|
1936
1949
|
status = "runs";
|
|
1937
1950
|
test;
|
|
1938
|
-
timing =
|
|
1951
|
+
timing = createResultTiming();
|
|
1939
1952
|
constructor(test, parent) {
|
|
1940
1953
|
this.test = test;
|
|
1941
1954
|
this.parent = parent;
|
|
@@ -1967,10 +1980,10 @@ class ResultHandler {
|
|
|
1967
1980
|
break;
|
|
1968
1981
|
case "target:end":
|
|
1969
1982
|
if (this.#targetResult.status === "failed") {
|
|
1970
|
-
this.#result.
|
|
1983
|
+
this.#result.targetCounts.failed++;
|
|
1971
1984
|
}
|
|
1972
1985
|
else {
|
|
1973
|
-
this.#result.
|
|
1986
|
+
this.#result.targetCounts.passed++;
|
|
1974
1987
|
this.#targetResult.status = "passed";
|
|
1975
1988
|
}
|
|
1976
1989
|
this.#targetResult.timing.end = Date.now();
|
|
@@ -1992,7 +2005,6 @@ class ResultHandler {
|
|
|
1992
2005
|
}
|
|
1993
2006
|
case "project:error":
|
|
1994
2007
|
this.#targetResult.status = "failed";
|
|
1995
|
-
this.#projectResult.diagnostics.push(...payload.diagnostics);
|
|
1996
2008
|
break;
|
|
1997
2009
|
case "file:start":
|
|
1998
2010
|
this.#projectResult.results.push(payload.result);
|
|
@@ -2004,18 +2016,17 @@ class ResultHandler {
|
|
|
2004
2016
|
case "collect:error":
|
|
2005
2017
|
this.#targetResult.status = "failed";
|
|
2006
2018
|
this.#fileResult.status = "failed";
|
|
2007
|
-
this.#fileResult.diagnostics.push(...payload.diagnostics);
|
|
2008
2019
|
break;
|
|
2009
2020
|
case "file:end":
|
|
2010
2021
|
if (this.#fileResult.status === "failed" ||
|
|
2011
|
-
this.#fileResult.
|
|
2012
|
-
this.#fileResult.
|
|
2013
|
-
this.#result.
|
|
2022
|
+
this.#fileResult.assertionCounts.failed > 0 ||
|
|
2023
|
+
this.#fileResult.testCounts.failed > 0) {
|
|
2024
|
+
this.#result.fileCounts.failed++;
|
|
2014
2025
|
this.#targetResult.status = "failed";
|
|
2015
2026
|
this.#fileResult.status = "failed";
|
|
2016
2027
|
}
|
|
2017
2028
|
else {
|
|
2018
|
-
this.#result.
|
|
2029
|
+
this.#result.fileCounts.passed++;
|
|
2019
2030
|
this.#fileResult.status = "passed";
|
|
2020
2031
|
}
|
|
2021
2032
|
this.#fileResult.timing.end = Date.now();
|
|
@@ -2046,44 +2057,37 @@ class ResultHandler {
|
|
|
2046
2057
|
this.#testResult.timing.start = Date.now();
|
|
2047
2058
|
break;
|
|
2048
2059
|
case "test:error":
|
|
2049
|
-
this.#result.testCount.failed++;
|
|
2050
|
-
this.#fileResult.testCount.failed++;
|
|
2051
|
-
this.#testResult.status = "failed";
|
|
2052
|
-
this.#testResult.diagnostics.push(...payload.diagnostics);
|
|
2053
|
-
this.#testResult.timing.end = Date.now();
|
|
2054
|
-
this.#testResult = undefined;
|
|
2055
|
-
break;
|
|
2056
2060
|
case "test:fail":
|
|
2057
|
-
this.#result.
|
|
2058
|
-
this.#fileResult.
|
|
2061
|
+
this.#result.testCounts.failed++;
|
|
2062
|
+
this.#fileResult.testCounts.failed++;
|
|
2059
2063
|
this.#testResult.status = "failed";
|
|
2060
2064
|
this.#testResult.timing.end = Date.now();
|
|
2061
2065
|
this.#testResult = undefined;
|
|
2062
2066
|
break;
|
|
2063
2067
|
case "test:pass":
|
|
2064
|
-
this.#result.
|
|
2065
|
-
this.#fileResult.
|
|
2068
|
+
this.#result.testCounts.passed++;
|
|
2069
|
+
this.#fileResult.testCounts.passed++;
|
|
2066
2070
|
this.#testResult.status = "passed";
|
|
2067
2071
|
this.#testResult.timing.end = Date.now();
|
|
2068
2072
|
this.#testResult = undefined;
|
|
2069
2073
|
break;
|
|
2070
2074
|
case "test:skip":
|
|
2071
|
-
this.#result.
|
|
2072
|
-
this.#fileResult.
|
|
2075
|
+
this.#result.testCounts.skipped++;
|
|
2076
|
+
this.#fileResult.testCounts.skipped++;
|
|
2073
2077
|
this.#testResult.status = "skipped";
|
|
2074
2078
|
this.#testResult.timing.end = Date.now();
|
|
2075
2079
|
this.#testResult = undefined;
|
|
2076
2080
|
break;
|
|
2077
2081
|
case "test:fixme":
|
|
2078
|
-
this.#result.
|
|
2079
|
-
this.#fileResult.
|
|
2082
|
+
this.#result.testCounts.fixme++;
|
|
2083
|
+
this.#fileResult.testCounts.fixme++;
|
|
2080
2084
|
this.#testResult.status = "fixme";
|
|
2081
2085
|
this.#testResult.timing.end = Date.now();
|
|
2082
2086
|
this.#testResult = undefined;
|
|
2083
2087
|
break;
|
|
2084
2088
|
case "test:todo":
|
|
2085
|
-
this.#result.
|
|
2086
|
-
this.#fileResult.
|
|
2089
|
+
this.#result.testCounts.todo++;
|
|
2090
|
+
this.#fileResult.testCounts.todo++;
|
|
2087
2091
|
this.#testResult.status = "todo";
|
|
2088
2092
|
this.#testResult.timing.end = Date.now();
|
|
2089
2093
|
this.#testResult = undefined;
|
|
@@ -2099,56 +2103,60 @@ class ResultHandler {
|
|
|
2099
2103
|
this.#expectResult.timing.start = Date.now();
|
|
2100
2104
|
break;
|
|
2101
2105
|
case "expect:error":
|
|
2102
|
-
this.#result.expectCount.failed++;
|
|
2103
|
-
this.#fileResult.expectCount.failed++;
|
|
2104
|
-
if (this.#testResult) {
|
|
2105
|
-
this.#testResult.expectCount.failed++;
|
|
2106
|
-
}
|
|
2107
|
-
this.#expectResult.status = "failed";
|
|
2108
|
-
this.#expectResult.diagnostics.push(...payload.diagnostics);
|
|
2109
|
-
this.#expectResult.timing.end = Date.now();
|
|
2110
|
-
this.#expectResult = undefined;
|
|
2111
|
-
break;
|
|
2112
2106
|
case "expect:fail":
|
|
2113
|
-
this.#result.
|
|
2114
|
-
this.#fileResult.
|
|
2107
|
+
this.#result.assertionCounts.failed++;
|
|
2108
|
+
this.#fileResult.assertionCounts.failed++;
|
|
2115
2109
|
if (this.#testResult) {
|
|
2116
|
-
this.#testResult.
|
|
2110
|
+
this.#testResult.assertionCounts.failed++;
|
|
2117
2111
|
}
|
|
2118
2112
|
this.#expectResult.status = "failed";
|
|
2119
2113
|
this.#expectResult.timing.end = Date.now();
|
|
2120
2114
|
this.#expectResult = undefined;
|
|
2121
2115
|
break;
|
|
2122
2116
|
case "expect:pass":
|
|
2123
|
-
this.#result.
|
|
2124
|
-
this.#fileResult.
|
|
2117
|
+
this.#result.assertionCounts.passed++;
|
|
2118
|
+
this.#fileResult.assertionCounts.passed++;
|
|
2125
2119
|
if (this.#testResult) {
|
|
2126
|
-
this.#testResult.
|
|
2120
|
+
this.#testResult.assertionCounts.passed++;
|
|
2127
2121
|
}
|
|
2128
2122
|
this.#expectResult.status = "passed";
|
|
2129
2123
|
this.#expectResult.timing.end = Date.now();
|
|
2130
2124
|
this.#expectResult = undefined;
|
|
2131
2125
|
break;
|
|
2132
2126
|
case "expect:skip":
|
|
2133
|
-
this.#result.
|
|
2134
|
-
this.#fileResult.
|
|
2127
|
+
this.#result.assertionCounts.skipped++;
|
|
2128
|
+
this.#fileResult.assertionCounts.skipped++;
|
|
2135
2129
|
if (this.#testResult) {
|
|
2136
|
-
this.#testResult.
|
|
2130
|
+
this.#testResult.assertionCounts.skipped++;
|
|
2137
2131
|
}
|
|
2138
2132
|
this.#expectResult.status = "skipped";
|
|
2139
2133
|
this.#expectResult.timing.end = Date.now();
|
|
2140
2134
|
this.#expectResult = undefined;
|
|
2141
2135
|
break;
|
|
2142
2136
|
case "expect:fixme":
|
|
2143
|
-
this.#result.
|
|
2144
|
-
this.#fileResult.
|
|
2137
|
+
this.#result.assertionCounts.fixme++;
|
|
2138
|
+
this.#fileResult.assertionCounts.fixme++;
|
|
2145
2139
|
if (this.#testResult) {
|
|
2146
|
-
this.#testResult.
|
|
2140
|
+
this.#testResult.assertionCounts.fixme++;
|
|
2147
2141
|
}
|
|
2148
2142
|
this.#expectResult.status = "fixme";
|
|
2149
2143
|
this.#expectResult.timing.end = Date.now();
|
|
2150
2144
|
this.#expectResult = undefined;
|
|
2151
2145
|
break;
|
|
2146
|
+
case "suppressed:error":
|
|
2147
|
+
this.#result.suppressedCounts.failed++;
|
|
2148
|
+
this.#fileResult.suppressedCounts.failed++;
|
|
2149
|
+
this.#targetResult.status = "failed";
|
|
2150
|
+
this.#fileResult.status = "failed";
|
|
2151
|
+
break;
|
|
2152
|
+
case "suppressed:match":
|
|
2153
|
+
this.#result.suppressedCounts.matched++;
|
|
2154
|
+
this.#fileResult.suppressedCounts.matched++;
|
|
2155
|
+
break;
|
|
2156
|
+
case "suppressed:ignore":
|
|
2157
|
+
this.#result.suppressedCounts.ignored++;
|
|
2158
|
+
this.#fileResult.suppressedCounts.ignored++;
|
|
2159
|
+
break;
|
|
2152
2160
|
}
|
|
2153
2161
|
}
|
|
2154
2162
|
}
|
|
@@ -2331,6 +2339,30 @@ function diagnosticText(diagnostic, codeFrameOptions = {}) {
|
|
|
2331
2339
|
return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { codeFrameOptions: codeFrameOptions, diagnostic: diagnostic })] }));
|
|
2332
2340
|
}
|
|
2333
2341
|
|
|
2342
|
+
function getStatusColor(status) {
|
|
2343
|
+
switch (status) {
|
|
2344
|
+
case "runs":
|
|
2345
|
+
return "33";
|
|
2346
|
+
case "passed":
|
|
2347
|
+
case "matched":
|
|
2348
|
+
return "32";
|
|
2349
|
+
case "failed":
|
|
2350
|
+
return "31";
|
|
2351
|
+
case "fixme":
|
|
2352
|
+
case "skipped":
|
|
2353
|
+
case "ignored":
|
|
2354
|
+
return "33";
|
|
2355
|
+
case "todo":
|
|
2356
|
+
return "35";
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
function duration(timing) {
|
|
2360
|
+
return timing.end - timing.start;
|
|
2361
|
+
}
|
|
2362
|
+
function total(counts) {
|
|
2363
|
+
return Object.values(counts).reduce((sum, value) => sum + value, 0);
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2334
2366
|
function FileNameText({ filePath }) {
|
|
2335
2367
|
const relativePath = Path.relative("", filePath);
|
|
2336
2368
|
const lastPathSeparator = relativePath.lastIndexOf("/");
|
|
@@ -2339,23 +2371,19 @@ function FileNameText({ filePath }) {
|
|
|
2339
2371
|
return (jsx(Text, { children: [jsx(Text, { color: "90", children: directoryNameText }), fileNameText] }));
|
|
2340
2372
|
}
|
|
2341
2373
|
function fileStatusText(status, file) {
|
|
2342
|
-
let statusColor;
|
|
2343
2374
|
let statusText;
|
|
2344
2375
|
switch (status) {
|
|
2345
2376
|
case "runs":
|
|
2346
|
-
statusColor = "33";
|
|
2347
2377
|
statusText = "runs";
|
|
2348
2378
|
break;
|
|
2349
2379
|
case "passed":
|
|
2350
|
-
statusColor = "32";
|
|
2351
2380
|
statusText = "pass";
|
|
2352
2381
|
break;
|
|
2353
2382
|
case "failed":
|
|
2354
|
-
statusColor = "31";
|
|
2355
2383
|
statusText = "fail";
|
|
2356
2384
|
break;
|
|
2357
2385
|
}
|
|
2358
|
-
return (jsx(Line, { children: [jsx(Text, { color:
|
|
2386
|
+
return (jsx(Line, { children: [jsx(Text, { color: getStatusColor(status), children: statusText }), " ", jsx(FileNameText, { filePath: file.path })] }));
|
|
2359
2387
|
}
|
|
2360
2388
|
|
|
2361
2389
|
function fileViewText(lines, addEmptyFinalLine) {
|
|
@@ -2466,36 +2494,52 @@ class OutputService {
|
|
|
2466
2494
|
function RowText({ label, text }) {
|
|
2467
2495
|
return (jsx(Line, { children: [`${label}:`.padEnd(12), text] }));
|
|
2468
2496
|
}
|
|
2469
|
-
function
|
|
2470
|
-
|
|
2497
|
+
function CountsText({ counts, total }) {
|
|
2498
|
+
const countsText = Object.entries(counts).map(([status, count]) => {
|
|
2499
|
+
return (jsx(Text, { children: count > 0 ? (jsx(Text, { children: [jsx(Text, { color: getStatusColor(status), children: [count, " ", status] }), jsx(Text, { children: ", " })] })) : undefined }));
|
|
2500
|
+
});
|
|
2501
|
+
const totalText = (jsx(Text, { children: [total, " ", "total"] }));
|
|
2502
|
+
return (jsx(Text, { children: [countsText, totalText] }));
|
|
2471
2503
|
}
|
|
2472
|
-
function DurationText({
|
|
2504
|
+
function DurationText({ timing }) {
|
|
2505
|
+
const seconds = duration(timing) / 1000;
|
|
2473
2506
|
return jsx(Text, { children: `${Math.round(seconds * 10) / 10}s` });
|
|
2474
2507
|
}
|
|
2475
|
-
function summaryText({
|
|
2476
|
-
const
|
|
2477
|
-
const
|
|
2478
|
-
const
|
|
2479
|
-
const
|
|
2480
|
-
|
|
2508
|
+
function summaryText({ targetCounts, fileCounts, testCounts, assertionCounts, suppressedCounts, timing, }) {
|
|
2509
|
+
const targetCountsTotal = total(targetCounts);
|
|
2510
|
+
const targetCountsText = (jsx(RowText, { label: "Targets", text: jsx(CountsText, { counts: targetCounts, total: targetCountsTotal }) }));
|
|
2511
|
+
const fileCountsTotal = total(fileCounts);
|
|
2512
|
+
const fileCountsText = (jsx(RowText, { label: "Test files", text: jsx(CountsText, { counts: fileCounts, total: fileCountsTotal }) }));
|
|
2513
|
+
const testCountsTotal = total(testCounts);
|
|
2514
|
+
const testCountsText = testCountsTotal > 0 ? (jsx(RowText, { label: "Tests", text: jsx(CountsText, { counts: testCounts, total: testCountsTotal }) })) : undefined;
|
|
2515
|
+
const assertionCountsTotal = total(assertionCounts);
|
|
2516
|
+
const assertionCountsText = assertionCountsTotal > 0 ? (jsx(RowText, { label: "Assertions", text: jsx(CountsText, { counts: assertionCounts, total: assertionCountsTotal }) })) : undefined;
|
|
2517
|
+
const suppressedCountsTotal = total(suppressedCounts);
|
|
2518
|
+
const suppressedCountsText = suppressedCountsTotal > 0 ? (jsx(RowText, { label: "Suppressed", text: jsx(CountsText, { counts: suppressedCounts, total: suppressedCountsTotal }) })) : undefined;
|
|
2519
|
+
const durationText = jsx(RowText, { label: "Duration", text: jsx(DurationText, { timing: timing }) });
|
|
2520
|
+
return (jsx(Text, { children: [targetCountsText, fileCountsText, testCountsText, assertionCountsText, suppressedCountsText, durationText] }));
|
|
2481
2521
|
}
|
|
2482
2522
|
|
|
2483
|
-
function
|
|
2523
|
+
function testNameText(status, name, indent = 0) {
|
|
2524
|
+
let statusText;
|
|
2484
2525
|
switch (status) {
|
|
2485
|
-
case "
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2526
|
+
case "passed":
|
|
2527
|
+
statusText = "+";
|
|
2528
|
+
break;
|
|
2529
|
+
case "failed":
|
|
2530
|
+
statusText = "×";
|
|
2531
|
+
break;
|
|
2532
|
+
case "skipped":
|
|
2533
|
+
statusText = "- skip";
|
|
2534
|
+
break;
|
|
2491
2535
|
case "fixme":
|
|
2492
|
-
|
|
2536
|
+
statusText = "- fixme";
|
|
2537
|
+
break;
|
|
2493
2538
|
case "todo":
|
|
2494
|
-
|
|
2539
|
+
statusText = "- todo";
|
|
2540
|
+
break;
|
|
2495
2541
|
}
|
|
2496
|
-
}
|
|
2497
|
-
function testNameText(status, name, indent = 0) {
|
|
2498
|
-
return (jsx(Line, { indent: indent + 1, children: [jsx(StatusText, { status: status }), " ", jsx(Text, { color: "90", children: name })] }));
|
|
2542
|
+
return (jsx(Line, { indent: indent + 1, children: [jsx(Text, { color: getStatusColor(status), children: statusText }), " ", jsx(Text, { color: "90", children: name })] }));
|
|
2499
2543
|
}
|
|
2500
2544
|
|
|
2501
2545
|
function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
|
|
@@ -2551,9 +2595,6 @@ class FileView {
|
|
|
2551
2595
|
#indent = 0;
|
|
2552
2596
|
#lines = [];
|
|
2553
2597
|
#messages = [];
|
|
2554
|
-
get hasErrors() {
|
|
2555
|
-
return this.#messages.length > 0;
|
|
2556
|
-
}
|
|
2557
2598
|
addMessage(message) {
|
|
2558
2599
|
this.#messages.push(message);
|
|
2559
2600
|
}
|
|
@@ -2576,7 +2617,10 @@ class FileView {
|
|
|
2576
2617
|
return this.#messages;
|
|
2577
2618
|
}
|
|
2578
2619
|
getViewText(options) {
|
|
2579
|
-
return fileViewText(this.#lines, options?.appendEmptyLine || this.hasErrors);
|
|
2620
|
+
return fileViewText(this.#lines, options?.appendEmptyLine || this.hasErrors());
|
|
2621
|
+
}
|
|
2622
|
+
hasErrors() {
|
|
2623
|
+
return this.#messages.length > 0;
|
|
2580
2624
|
}
|
|
2581
2625
|
}
|
|
2582
2626
|
|
|
@@ -2587,7 +2631,7 @@ class ListReporter extends BaseReporter {
|
|
|
2587
2631
|
#hasReportedError = false;
|
|
2588
2632
|
#hasReportedUses = false;
|
|
2589
2633
|
#isFileViewExpanded = false;
|
|
2590
|
-
|
|
2634
|
+
#isLastFile() {
|
|
2591
2635
|
return this.#fileCount === 0;
|
|
2592
2636
|
}
|
|
2593
2637
|
on([event, payload]) {
|
|
@@ -2630,6 +2674,7 @@ class ListReporter extends BaseReporter {
|
|
|
2630
2674
|
case "file:error":
|
|
2631
2675
|
case "directive:error":
|
|
2632
2676
|
case "collect:error":
|
|
2677
|
+
case "suppressed:error":
|
|
2633
2678
|
for (const diagnostic of payload.diagnostics) {
|
|
2634
2679
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
2635
2680
|
}
|
|
@@ -2639,8 +2684,8 @@ class ListReporter extends BaseReporter {
|
|
|
2639
2684
|
OutputService.eraseLastLine();
|
|
2640
2685
|
}
|
|
2641
2686
|
OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
|
|
2642
|
-
OutputService.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile }));
|
|
2643
|
-
if (this.#fileView.hasErrors) {
|
|
2687
|
+
OutputService.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile() }));
|
|
2688
|
+
if (this.#fileView.hasErrors()) {
|
|
2644
2689
|
OutputService.writeError(this.#fileView.getMessages());
|
|
2645
2690
|
this.#hasReportedError = true;
|
|
2646
2691
|
}
|
|
@@ -2658,7 +2703,7 @@ class ListReporter extends BaseReporter {
|
|
|
2658
2703
|
break;
|
|
2659
2704
|
case "test:skip":
|
|
2660
2705
|
if (this.#isFileViewExpanded) {
|
|
2661
|
-
this.#fileView.addTest("
|
|
2706
|
+
this.#fileView.addTest("skipped", payload.result.test.name);
|
|
2662
2707
|
}
|
|
2663
2708
|
break;
|
|
2664
2709
|
case "test:fixme":
|
|
@@ -2673,7 +2718,7 @@ class ListReporter extends BaseReporter {
|
|
|
2673
2718
|
break;
|
|
2674
2719
|
case "test:error":
|
|
2675
2720
|
if (this.#isFileViewExpanded) {
|
|
2676
|
-
this.#fileView.addTest("
|
|
2721
|
+
this.#fileView.addTest("failed", payload.result.test.name);
|
|
2677
2722
|
}
|
|
2678
2723
|
for (const diagnostic of payload.diagnostics) {
|
|
2679
2724
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
@@ -2681,12 +2726,12 @@ class ListReporter extends BaseReporter {
|
|
|
2681
2726
|
break;
|
|
2682
2727
|
case "test:fail":
|
|
2683
2728
|
if (this.#isFileViewExpanded) {
|
|
2684
|
-
this.#fileView.addTest("
|
|
2729
|
+
this.#fileView.addTest("failed", payload.result.test.name);
|
|
2685
2730
|
}
|
|
2686
2731
|
break;
|
|
2687
2732
|
case "test:pass":
|
|
2688
2733
|
if (this.#isFileViewExpanded) {
|
|
2689
|
-
this.#fileView.addTest("
|
|
2734
|
+
this.#fileView.addTest("passed", payload.result.test.name);
|
|
2690
2735
|
}
|
|
2691
2736
|
break;
|
|
2692
2737
|
case "expect:error":
|
|
@@ -2727,11 +2772,12 @@ class SummaryReporter extends BaseReporter {
|
|
|
2727
2772
|
}
|
|
2728
2773
|
if (event === "run:end") {
|
|
2729
2774
|
OutputService.writeMessage(summaryText({
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2775
|
+
targetCounts: payload.result.targetCounts,
|
|
2776
|
+
fileCounts: payload.result.fileCounts,
|
|
2777
|
+
testCounts: payload.result.testCounts,
|
|
2778
|
+
assertionCounts: payload.result.assertionCounts,
|
|
2779
|
+
suppressedCounts: payload.result.suppressedCounts,
|
|
2780
|
+
timing: payload.result.timing,
|
|
2735
2781
|
}));
|
|
2736
2782
|
}
|
|
2737
2783
|
}
|
|
@@ -2783,18 +2829,18 @@ var CancellationReason;
|
|
|
2783
2829
|
class CancellationToken {
|
|
2784
2830
|
#isCancelled = false;
|
|
2785
2831
|
#reason;
|
|
2786
|
-
get isCancellationRequested() {
|
|
2787
|
-
return this.#isCancelled;
|
|
2788
|
-
}
|
|
2789
|
-
get reason() {
|
|
2790
|
-
return this.#reason;
|
|
2791
|
-
}
|
|
2792
2832
|
cancel(reason) {
|
|
2793
2833
|
if (!this.#isCancelled) {
|
|
2794
2834
|
this.#isCancelled = true;
|
|
2795
2835
|
this.#reason = reason;
|
|
2796
2836
|
}
|
|
2797
2837
|
}
|
|
2838
|
+
isCancellationRequested() {
|
|
2839
|
+
return this.#isCancelled;
|
|
2840
|
+
}
|
|
2841
|
+
getReason() {
|
|
2842
|
+
return this.#reason;
|
|
2843
|
+
}
|
|
2798
2844
|
reset() {
|
|
2799
2845
|
if (this.#isCancelled) {
|
|
2800
2846
|
this.#isCancelled = false;
|
|
@@ -3179,7 +3225,7 @@ class WatchService {
|
|
|
3179
3225
|
for (const watcher of this.#watchers) {
|
|
3180
3226
|
watcher.watch();
|
|
3181
3227
|
}
|
|
3182
|
-
while (!cancellationToken.isCancellationRequested) {
|
|
3228
|
+
while (!cancellationToken.isCancellationRequested()) {
|
|
3183
3229
|
const testFiles = await debounce.schedule();
|
|
3184
3230
|
if (testFiles.length > 0) {
|
|
3185
3231
|
yield testFiles;
|
|
@@ -3188,6 +3234,29 @@ class WatchService {
|
|
|
3188
3234
|
}
|
|
3189
3235
|
}
|
|
3190
3236
|
|
|
3237
|
+
function compareDiagnostics(a, b) {
|
|
3238
|
+
if (a.file?.fileName !== b.file?.fileName) {
|
|
3239
|
+
return false;
|
|
3240
|
+
}
|
|
3241
|
+
return deepCompareKeys(a, b, ["start", "length", "code", "messageText"]);
|
|
3242
|
+
}
|
|
3243
|
+
function deepCompareKeys(a, b, keys) {
|
|
3244
|
+
if (a == null || b == null) {
|
|
3245
|
+
return a === b;
|
|
3246
|
+
}
|
|
3247
|
+
if (typeof a !== typeof b) {
|
|
3248
|
+
return false;
|
|
3249
|
+
}
|
|
3250
|
+
if (typeof a !== "object") {
|
|
3251
|
+
return a === b;
|
|
3252
|
+
}
|
|
3253
|
+
for (const key of Object.keys(a).filter((key) => keys.includes(key))) {
|
|
3254
|
+
if (!(key in b) || !deepCompareKeys(a[key], b[key], keys)) {
|
|
3255
|
+
return false;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
return true;
|
|
3259
|
+
}
|
|
3191
3260
|
function nodeBelongsToArgumentList(compiler, node) {
|
|
3192
3261
|
return compiler.isCallExpression(node.parent) && node.parent.arguments.some((argument) => argument === node);
|
|
3193
3262
|
}
|
|
@@ -3197,70 +3266,189 @@ function nodeIsChildOfExpressionStatement(compiler, node) {
|
|
|
3197
3266
|
|
|
3198
3267
|
class AbilityLayer {
|
|
3199
3268
|
#compiler;
|
|
3200
|
-
#
|
|
3201
|
-
#filePath = "";
|
|
3269
|
+
#editor;
|
|
3202
3270
|
#nodes = [];
|
|
3203
|
-
|
|
3204
|
-
#resolvedConfig;
|
|
3205
|
-
#suppressedErrorsMap;
|
|
3206
|
-
#text = "";
|
|
3207
|
-
constructor(compiler, projectService, resolvedConfig) {
|
|
3271
|
+
constructor(compiler, editor) {
|
|
3208
3272
|
this.#compiler = compiler;
|
|
3209
|
-
this.#
|
|
3210
|
-
this.#resolvedConfig = resolvedConfig;
|
|
3211
|
-
}
|
|
3212
|
-
#addRanges(node, ranges) {
|
|
3213
|
-
this.#nodes.push(node);
|
|
3214
|
-
for (const range of ranges) {
|
|
3215
|
-
const rangeText = range.replacement != null
|
|
3216
|
-
? `${range.replacement}${this.#getErasedRangeText(range).slice(range.replacement.length)}`
|
|
3217
|
-
: this.#getErasedRangeText(range);
|
|
3218
|
-
this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
|
|
3219
|
-
}
|
|
3273
|
+
this.#editor = editor;
|
|
3220
3274
|
}
|
|
3221
3275
|
#belongsToNode(node, diagnostic) {
|
|
3222
3276
|
switch (node.brand) {
|
|
3223
3277
|
case "expect":
|
|
3224
|
-
return (diagnosticBelongsToNode(diagnostic, node.matcherNode)
|
|
3225
|
-
|
|
3278
|
+
return (diagnosticBelongsToNode(diagnostic, node.matcherNode) ||
|
|
3279
|
+
diagnosticBelongsToNode(diagnostic, node.source));
|
|
3226
3280
|
case "when":
|
|
3227
3281
|
return (diagnosticBelongsToNode(diagnostic, node.actionNode) &&
|
|
3228
3282
|
!diagnosticBelongsToNode(diagnostic, node.target));
|
|
3229
3283
|
}
|
|
3230
3284
|
return false;
|
|
3231
3285
|
}
|
|
3286
|
+
close(diagnostics) {
|
|
3287
|
+
if (diagnostics != null && this.#nodes.length > 0) {
|
|
3288
|
+
this.#nodes.reverse();
|
|
3289
|
+
for (const diagnostic of diagnostics) {
|
|
3290
|
+
this.#mapToNodes(diagnostic);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
this.#nodes = [];
|
|
3294
|
+
}
|
|
3232
3295
|
#mapToNodes(diagnostic) {
|
|
3233
3296
|
for (const node of this.#nodes) {
|
|
3234
3297
|
if (this.#belongsToNode(node, diagnostic)) {
|
|
3235
3298
|
node.abilityDiagnostics.add(diagnostic);
|
|
3236
|
-
|
|
3299
|
+
break;
|
|
3237
3300
|
}
|
|
3238
3301
|
}
|
|
3239
|
-
return false;
|
|
3240
3302
|
}
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
const
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3303
|
+
visitExpect(expect) {
|
|
3304
|
+
const expectStart = expect.node.getStart();
|
|
3305
|
+
const expectExpressionEnd = expect.node.expression.getEnd();
|
|
3306
|
+
const expectEnd = expect.node.getEnd();
|
|
3307
|
+
const matcherNameEnd = expect.matcherNameNode.getEnd();
|
|
3308
|
+
switch (expect.matcherNameNode.name.text) {
|
|
3309
|
+
case "toBeApplicable":
|
|
3310
|
+
this.#nodes.push(expect);
|
|
3311
|
+
this.#editor.replaceRanges([
|
|
3312
|
+
[expectStart, expectExpressionEnd],
|
|
3313
|
+
[expectEnd, matcherNameEnd],
|
|
3314
|
+
]);
|
|
3315
|
+
break;
|
|
3316
|
+
case "toBeCallableWith": {
|
|
3317
|
+
this.#nodes.push(expect);
|
|
3318
|
+
const sourceNode = expect.source[0];
|
|
3319
|
+
if (!sourceNode) {
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
if (nodeBelongsToArgumentList(this.#compiler, sourceNode)) {
|
|
3323
|
+
this.#editor.eraseTrailingComma(expect.source);
|
|
3324
|
+
this.#editor.replaceRanges([
|
|
3325
|
+
[
|
|
3326
|
+
expectStart,
|
|
3327
|
+
expectExpressionEnd,
|
|
3328
|
+
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
|
|
3329
|
+
],
|
|
3330
|
+
[expectEnd, matcherNameEnd],
|
|
3331
|
+
]);
|
|
3332
|
+
}
|
|
3333
|
+
else {
|
|
3334
|
+
const sourceText = sourceNode.getFullText();
|
|
3335
|
+
this.#editor.replaceRanges([
|
|
3336
|
+
[
|
|
3337
|
+
expectStart,
|
|
3338
|
+
matcherNameEnd,
|
|
3339
|
+
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode)
|
|
3340
|
+
? `;(undefined as any as ${sourceText})`
|
|
3341
|
+
: `(undefined as any as ${sourceText})`,
|
|
3342
|
+
],
|
|
3343
|
+
]);
|
|
3344
|
+
}
|
|
3252
3345
|
break;
|
|
3253
3346
|
}
|
|
3254
|
-
|
|
3255
|
-
|
|
3347
|
+
case "toBeConstructableWith":
|
|
3348
|
+
this.#nodes.push(expect);
|
|
3349
|
+
this.#editor.eraseTrailingComma(expect.source);
|
|
3350
|
+
this.#editor.replaceRanges([
|
|
3351
|
+
[
|
|
3352
|
+
expectStart,
|
|
3353
|
+
expectExpressionEnd,
|
|
3354
|
+
nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new",
|
|
3355
|
+
],
|
|
3356
|
+
[expectEnd, matcherNameEnd],
|
|
3357
|
+
]);
|
|
3358
|
+
break;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
visitWhen(when) {
|
|
3362
|
+
const whenStart = when.node.getStart();
|
|
3363
|
+
const whenExpressionEnd = when.node.expression.getEnd();
|
|
3364
|
+
const whenEnd = when.node.getEnd();
|
|
3365
|
+
const actionNameEnd = when.actionNameNode.getEnd();
|
|
3366
|
+
switch (when.actionNameNode.name.text) {
|
|
3367
|
+
case "isCalledWith":
|
|
3368
|
+
this.#nodes.push(when);
|
|
3369
|
+
this.#editor.eraseTrailingComma(when.target);
|
|
3370
|
+
this.#editor.replaceRanges([
|
|
3371
|
+
[whenStart, whenExpressionEnd, nodeIsChildOfExpressionStatement(this.#compiler, when.actionNode) ? ";" : ""],
|
|
3372
|
+
[whenEnd, actionNameEnd],
|
|
3373
|
+
]);
|
|
3256
3374
|
break;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
class SourceTextEditor {
|
|
3380
|
+
#filePath = "";
|
|
3381
|
+
#sourceFile;
|
|
3382
|
+
#text = "";
|
|
3383
|
+
open(sourceFile) {
|
|
3384
|
+
this.#sourceFile = sourceFile;
|
|
3385
|
+
this.#filePath = sourceFile.fileName;
|
|
3386
|
+
this.#text = sourceFile.text;
|
|
3387
|
+
}
|
|
3388
|
+
close() {
|
|
3389
|
+
if (this.#sourceFile != null) {
|
|
3390
|
+
SourceService.set(this.#sourceFile);
|
|
3391
|
+
this.#sourceFile = undefined;
|
|
3392
|
+
}
|
|
3393
|
+
this.#filePath = "";
|
|
3394
|
+
this.#text = "";
|
|
3395
|
+
}
|
|
3396
|
+
eraseTrailingComma(node) {
|
|
3397
|
+
if (node.hasTrailingComma) {
|
|
3398
|
+
this.replaceRange(node.end - 1, node.end);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
#getErasedRange(start, end) {
|
|
3402
|
+
if (this.#text.indexOf("\n", start) >= end) {
|
|
3403
|
+
return " ".repeat(end - start);
|
|
3404
|
+
}
|
|
3405
|
+
const text = [];
|
|
3406
|
+
for (let index = start; index < end; index++) {
|
|
3407
|
+
const character = this.#text.charAt(index);
|
|
3408
|
+
switch (character) {
|
|
3409
|
+
case "\n":
|
|
3410
|
+
case "\r":
|
|
3411
|
+
text.push(character);
|
|
3412
|
+
break;
|
|
3413
|
+
default:
|
|
3414
|
+
text.push(" ");
|
|
3257
3415
|
}
|
|
3258
|
-
line--;
|
|
3259
3416
|
}
|
|
3417
|
+
return text.join("");
|
|
3418
|
+
}
|
|
3419
|
+
getFilePath() {
|
|
3420
|
+
return this.#filePath;
|
|
3421
|
+
}
|
|
3422
|
+
getText() {
|
|
3423
|
+
return this.#text;
|
|
3424
|
+
}
|
|
3425
|
+
replaceRange(start, end, replacement) {
|
|
3426
|
+
const rangeText = replacement != null
|
|
3427
|
+
? `${replacement}${this.#getErasedRange(start, end).slice(replacement.length)}`
|
|
3428
|
+
: this.#getErasedRange(start, end);
|
|
3429
|
+
this.#text = `${this.#text.slice(0, start)}${rangeText}${this.#text.slice(end)}`;
|
|
3430
|
+
}
|
|
3431
|
+
replaceRanges(ranges) {
|
|
3432
|
+
for (const [start, end, replacement] of ranges) {
|
|
3433
|
+
this.replaceRange(start, end, replacement);
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
class SuppressedLayer {
|
|
3439
|
+
#compiler;
|
|
3440
|
+
#editor;
|
|
3441
|
+
#expectErrorRegex = /^(\s*)(\/\/ *@ts-expect-error)(!?)(:? *)(.*)?$/gim;
|
|
3442
|
+
#resolvedConfig;
|
|
3443
|
+
#suppressedErrorsMap;
|
|
3444
|
+
constructor(compiler, editor, resolvedConfig) {
|
|
3445
|
+
this.#compiler = compiler;
|
|
3446
|
+
this.#editor = editor;
|
|
3447
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3260
3448
|
}
|
|
3261
|
-
#collectSuppressedErrors() {
|
|
3449
|
+
#collectSuppressedErrors(text) {
|
|
3262
3450
|
const ranges = [];
|
|
3263
|
-
for (const match of
|
|
3451
|
+
for (const match of text.matchAll(this.#expectErrorRegex)) {
|
|
3264
3452
|
const offsetText = match?.[1];
|
|
3265
3453
|
const directiveText = match?.[2];
|
|
3266
3454
|
const ignoreText = match?.[3];
|
|
@@ -3283,126 +3471,89 @@ class AbilityLayer {
|
|
|
3283
3471
|
}
|
|
3284
3472
|
return ranges;
|
|
3285
3473
|
}
|
|
3286
|
-
close(
|
|
3287
|
-
if (
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
const languageService = this.#projectService.getLanguageService(this.#filePath);
|
|
3291
|
-
const diagnostics = languageService?.getSemanticDiagnostics(this.#filePath);
|
|
3292
|
-
if (diagnostics != null) {
|
|
3293
|
-
this.#nodes.reverse();
|
|
3294
|
-
for (const diagnostic of diagnostics) {
|
|
3295
|
-
if (this.#mapToNodes(diagnostic)) {
|
|
3296
|
-
continue;
|
|
3297
|
-
}
|
|
3298
|
-
this.#mapToDirectives(diagnostic);
|
|
3299
|
-
}
|
|
3474
|
+
close(diagnostics) {
|
|
3475
|
+
if (diagnostics != null && this.#suppressedErrorsMap != null) {
|
|
3476
|
+
for (const diagnostic of diagnostics) {
|
|
3477
|
+
this.#mapToDirectives(diagnostic);
|
|
3300
3478
|
}
|
|
3301
3479
|
}
|
|
3302
|
-
this.#filePath = "";
|
|
3303
|
-
this.#nodes = [];
|
|
3304
3480
|
this.#suppressedErrorsMap = undefined;
|
|
3305
|
-
this.#text = "";
|
|
3306
3481
|
}
|
|
3307
|
-
#
|
|
3308
|
-
if (
|
|
3309
|
-
|
|
3310
|
-
}
|
|
3311
|
-
}
|
|
3312
|
-
#getErasedRangeText(range) {
|
|
3313
|
-
if (this.#text.indexOf("\n", range.start) >= range.end) {
|
|
3314
|
-
return " ".repeat(range.end - range.start);
|
|
3315
|
-
}
|
|
3316
|
-
const text = [];
|
|
3317
|
-
for (let index = range.start; index < range.end; index++) {
|
|
3318
|
-
const character = this.#text.charAt(index);
|
|
3319
|
-
switch (character) {
|
|
3320
|
-
case "\n":
|
|
3321
|
-
case "\r":
|
|
3322
|
-
text.push(character);
|
|
3323
|
-
break;
|
|
3324
|
-
default:
|
|
3325
|
-
text.push(" ");
|
|
3326
|
-
}
|
|
3482
|
+
#mapToDirectives(diagnostic) {
|
|
3483
|
+
if (!isDiagnosticWithLocation(diagnostic)) {
|
|
3484
|
+
return;
|
|
3327
3485
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
switch (expect.matcherNameNode.name.text) {
|
|
3336
|
-
case "toBeApplicable":
|
|
3337
|
-
this.#addRanges(expect, [
|
|
3338
|
-
{ start: expectStart, end: expectExpressionEnd },
|
|
3339
|
-
{ start: expectEnd, end: matcherNameEnd },
|
|
3340
|
-
]);
|
|
3341
|
-
break;
|
|
3342
|
-
case "toBeCallableWith":
|
|
3343
|
-
this.#eraseTrailingComma(expect.source, expect);
|
|
3344
|
-
this.#addRanges(expect, [
|
|
3345
|
-
{
|
|
3346
|
-
start: expectStart,
|
|
3347
|
-
end: expectExpressionEnd,
|
|
3348
|
-
replacement: nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? ";" : "",
|
|
3349
|
-
},
|
|
3350
|
-
{ start: expectEnd, end: matcherNameEnd },
|
|
3351
|
-
]);
|
|
3486
|
+
const { file, start } = diagnostic;
|
|
3487
|
+
const lineMap = file.getLineStarts();
|
|
3488
|
+
let line = this.#compiler.getLineAndCharacterOfPosition(file, start).line - 1;
|
|
3489
|
+
while (line >= 0) {
|
|
3490
|
+
const suppressedError = this.#suppressedErrorsMap?.get(line);
|
|
3491
|
+
if (suppressedError != null) {
|
|
3492
|
+
suppressedError.diagnostics.push(diagnostic);
|
|
3352
3493
|
break;
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
{
|
|
3357
|
-
start: expectStart,
|
|
3358
|
-
end: expectExpressionEnd,
|
|
3359
|
-
replacement: nodeIsChildOfExpressionStatement(this.#compiler, expect.matcherNode) ? "; new" : "new",
|
|
3360
|
-
},
|
|
3361
|
-
{ start: expectEnd, end: matcherNameEnd },
|
|
3362
|
-
]);
|
|
3494
|
+
}
|
|
3495
|
+
const lineText = file.text.slice(lineMap[line], lineMap[line + 1]).trim();
|
|
3496
|
+
if (lineText !== "" && !lineText.startsWith("//")) {
|
|
3363
3497
|
break;
|
|
3498
|
+
}
|
|
3499
|
+
line--;
|
|
3364
3500
|
}
|
|
3365
3501
|
}
|
|
3366
|
-
|
|
3367
|
-
const suppressedErrors = this.#collectSuppressedErrors();
|
|
3502
|
+
open(tree) {
|
|
3503
|
+
const suppressedErrors = this.#collectSuppressedErrors(this.#editor.getText());
|
|
3368
3504
|
if (this.#resolvedConfig.checkSuppressedErrors) {
|
|
3369
|
-
|
|
3505
|
+
tree.suppressedErrors = suppressedErrors;
|
|
3370
3506
|
this.#suppressedErrorsMap = new Map();
|
|
3371
3507
|
}
|
|
3372
3508
|
for (const suppressedError of suppressedErrors) {
|
|
3373
3509
|
const { start, end } = suppressedError.directive;
|
|
3374
|
-
|
|
3375
|
-
this.#text = `${this.#text.slice(0, start + 2)}${rangeText}${this.#text.slice(end)}`;
|
|
3510
|
+
this.#editor.replaceRange(start + 2, end);
|
|
3376
3511
|
if (this.#suppressedErrorsMap != null) {
|
|
3377
|
-
const { line } =
|
|
3512
|
+
const { line } = tree.sourceFile.getLineAndCharacterOfPosition(start);
|
|
3378
3513
|
this.#suppressedErrorsMap.set(line, suppressedError);
|
|
3379
3514
|
}
|
|
3380
3515
|
}
|
|
3381
3516
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
class Layers {
|
|
3520
|
+
#abilityLayer;
|
|
3521
|
+
#editor = new SourceTextEditor();
|
|
3522
|
+
#projectService;
|
|
3523
|
+
#suppressedDiagnostics;
|
|
3524
|
+
#suppressedLayer;
|
|
3525
|
+
constructor(compiler, projectService, resolvedConfig) {
|
|
3526
|
+
this.#projectService = projectService;
|
|
3527
|
+
this.#abilityLayer = new AbilityLayer(compiler, this.#editor);
|
|
3528
|
+
this.#suppressedLayer = new SuppressedLayer(compiler, this.#editor, resolvedConfig);
|
|
3529
|
+
}
|
|
3530
|
+
close() {
|
|
3531
|
+
let isSeenDiagnostic;
|
|
3532
|
+
if (this.#suppressedDiagnostics != null) {
|
|
3533
|
+
const seenDiagnostics = this.#suppressedDiagnostics;
|
|
3534
|
+
this.#suppressedDiagnostics = undefined;
|
|
3535
|
+
isSeenDiagnostic = (diagnostic) => !seenDiagnostics.some((seenDiagnostic) => compareDiagnostics(diagnostic, seenDiagnostic));
|
|
3536
|
+
}
|
|
3537
|
+
const abilityDiagnostics = this.#projectService.getDiagnostics(this.#editor.getFilePath(), this.#editor.getText(), isSeenDiagnostic);
|
|
3538
|
+
this.#abilityLayer.close(abilityDiagnostics);
|
|
3539
|
+
this.#editor.close();
|
|
3540
|
+
}
|
|
3541
|
+
open(tree) {
|
|
3542
|
+
this.#editor.open(tree.sourceFile);
|
|
3543
|
+
this.#suppressedLayer.open(tree);
|
|
3544
|
+
this.#suppressedDiagnostics = this.#projectService.getDiagnostics(this.#editor.getFilePath(), this.#editor.getText());
|
|
3545
|
+
this.#suppressedLayer.close(this.#suppressedDiagnostics);
|
|
3546
|
+
}
|
|
3547
|
+
visit(node) {
|
|
3548
|
+
switch (node.brand) {
|
|
3549
|
+
case "expect":
|
|
3550
|
+
this.#abilityLayer.visitExpect(node);
|
|
3551
|
+
break;
|
|
3552
|
+
case "when":
|
|
3553
|
+
this.#abilityLayer.visitWhen(node);
|
|
3398
3554
|
break;
|
|
3399
3555
|
}
|
|
3400
3556
|
}
|
|
3401
|
-
open(testTree) {
|
|
3402
|
-
this.#filePath = testTree.sourceFile.fileName;
|
|
3403
|
-
this.#text = testTree.sourceFile.text;
|
|
3404
|
-
this.#handleSuppressedErrors(testTree);
|
|
3405
|
-
}
|
|
3406
3557
|
}
|
|
3407
3558
|
|
|
3408
3559
|
class CollectDiagnosticText {
|
|
@@ -3546,14 +3697,15 @@ class IdentifierLookup {
|
|
|
3546
3697
|
}
|
|
3547
3698
|
switch (identifier) {
|
|
3548
3699
|
case "describe":
|
|
3549
|
-
return { brand: "describe", flags
|
|
3700
|
+
return { brand: "describe", flags };
|
|
3550
3701
|
case "it":
|
|
3702
|
+
return { brand: "it", flags };
|
|
3551
3703
|
case "test":
|
|
3552
|
-
return { brand: "test", flags
|
|
3704
|
+
return { brand: "test", flags };
|
|
3553
3705
|
case "expect":
|
|
3554
|
-
return { brand: "expect", flags
|
|
3706
|
+
return { brand: "expect", flags };
|
|
3555
3707
|
case "when":
|
|
3556
|
-
return { brand: "when", flags
|
|
3708
|
+
return { brand: "when", flags };
|
|
3557
3709
|
}
|
|
3558
3710
|
return;
|
|
3559
3711
|
}
|
|
@@ -3591,12 +3743,12 @@ class WhenNode extends TestTreeNode {
|
|
|
3591
3743
|
}
|
|
3592
3744
|
|
|
3593
3745
|
class CollectService {
|
|
3594
|
-
#
|
|
3746
|
+
#layers;
|
|
3595
3747
|
#compiler;
|
|
3596
3748
|
#identifierLookup;
|
|
3597
3749
|
constructor(compiler, projectService, resolvedConfig) {
|
|
3598
3750
|
this.#compiler = compiler;
|
|
3599
|
-
this.#
|
|
3751
|
+
this.#layers = new Layers(compiler, projectService, resolvedConfig);
|
|
3600
3752
|
this.#identifierLookup = new IdentifierLookup(compiler);
|
|
3601
3753
|
}
|
|
3602
3754
|
#collectTestTreeNodes(node, parent, testTree) {
|
|
@@ -3606,7 +3758,9 @@ class CollectService {
|
|
|
3606
3758
|
if (!this.#checkNode(node, meta, parent)) {
|
|
3607
3759
|
return;
|
|
3608
3760
|
}
|
|
3609
|
-
if (meta.brand === "describe" ||
|
|
3761
|
+
if (meta.brand === "describe" ||
|
|
3762
|
+
meta.brand === "it" ||
|
|
3763
|
+
meta.brand === "test") {
|
|
3610
3764
|
const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
|
|
3611
3765
|
this.#compiler.forEachChild(node, (node) => {
|
|
3612
3766
|
this.#collectTestTreeNodes(node, testTreeNode, testTree);
|
|
@@ -3617,19 +3771,28 @@ class CollectService {
|
|
|
3617
3771
|
if (meta.brand === "expect") {
|
|
3618
3772
|
const modifierNode = this.#getChainedNode(node, "type");
|
|
3619
3773
|
if (!modifierNode) {
|
|
3774
|
+
const text = "'expect()' must be followed by the '.type' modifier.";
|
|
3775
|
+
const origin = DiagnosticOrigin.fromNode(node);
|
|
3776
|
+
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3620
3777
|
return;
|
|
3621
3778
|
}
|
|
3622
3779
|
const notNode = this.#getChainedNode(modifierNode, "not");
|
|
3623
3780
|
const matcherNameNode = this.#getChainedNode(notNode ?? modifierNode);
|
|
3624
3781
|
if (!matcherNameNode) {
|
|
3782
|
+
const text = "The final element in the chain must be a matcher.";
|
|
3783
|
+
const origin = DiagnosticOrigin.fromNode(notNode ?? modifierNode);
|
|
3784
|
+
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3625
3785
|
return;
|
|
3626
3786
|
}
|
|
3627
3787
|
const matcherNode = this.#getMatcherNode(matcherNameNode);
|
|
3628
3788
|
if (!matcherNode) {
|
|
3789
|
+
const text = "The matcher must be called with an argument.";
|
|
3790
|
+
const origin = DiagnosticOrigin.fromNode(matcherNameNode);
|
|
3791
|
+
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3629
3792
|
return;
|
|
3630
3793
|
}
|
|
3631
3794
|
const expectNode = new ExpectNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
|
|
3632
|
-
this.#
|
|
3795
|
+
this.#layers.visit(expectNode);
|
|
3633
3796
|
this.#compiler.forEachChild(node, (node) => {
|
|
3634
3797
|
this.#collectTestTreeNodes(node, expectNode, testTree);
|
|
3635
3798
|
});
|
|
@@ -3639,24 +3802,32 @@ class CollectService {
|
|
|
3639
3802
|
if (meta.brand === "when") {
|
|
3640
3803
|
const actionNameNode = this.#getChainedNode(node);
|
|
3641
3804
|
if (!actionNameNode) {
|
|
3805
|
+
const text = "The final element in the chain must be an action.";
|
|
3806
|
+
const origin = DiagnosticOrigin.fromNode(node);
|
|
3807
|
+
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3642
3808
|
return;
|
|
3643
3809
|
}
|
|
3644
3810
|
const actionNode = this.#getActionNode(actionNameNode);
|
|
3645
3811
|
if (!actionNode) {
|
|
3812
|
+
const text = "The action must be called with an argument.";
|
|
3813
|
+
const origin = DiagnosticOrigin.fromNode(actionNameNode);
|
|
3814
|
+
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3646
3815
|
return;
|
|
3647
3816
|
}
|
|
3648
3817
|
this.#compiler.forEachChild(actionNode, (node) => {
|
|
3649
3818
|
if (this.#compiler.isCallExpression(node)) {
|
|
3650
3819
|
const meta = this.#identifierLookup.resolveTestTreeNodeMeta(node);
|
|
3651
|
-
if (meta?.brand === "describe" ||
|
|
3652
|
-
|
|
3820
|
+
if (meta?.brand === "describe" ||
|
|
3821
|
+
meta?.brand === "it" ||
|
|
3822
|
+
meta?.brand === "test") {
|
|
3823
|
+
const text = CollectDiagnosticText.cannotBeNestedWithin(meta.brand, "when");
|
|
3653
3824
|
const origin = DiagnosticOrigin.fromNode(node);
|
|
3654
3825
|
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3655
3826
|
}
|
|
3656
3827
|
}
|
|
3657
3828
|
});
|
|
3658
3829
|
const whenNode = new WhenNode(this.#compiler, meta.brand, node, parent, meta.flags, actionNode, actionNameNode);
|
|
3659
|
-
this.#
|
|
3830
|
+
this.#layers.visit(whenNode);
|
|
3660
3831
|
this.#onNode(whenNode, parent, testTree);
|
|
3661
3832
|
return;
|
|
3662
3833
|
}
|
|
@@ -3673,16 +3844,16 @@ class CollectService {
|
|
|
3673
3844
|
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
3674
3845
|
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
3675
3846
|
EventEmitter.dispatch(["collect:start", { tree: testTree }]);
|
|
3676
|
-
this.#
|
|
3847
|
+
this.#layers.open(testTree);
|
|
3677
3848
|
this.#identifierLookup.open();
|
|
3678
3849
|
this.#collectTestTreeNodes(sourceFile, testTree, testTree);
|
|
3679
|
-
this.#
|
|
3850
|
+
this.#layers.close();
|
|
3680
3851
|
EventEmitter.dispatch(["collect:end", { tree: testTree }]);
|
|
3681
3852
|
return testTree;
|
|
3682
3853
|
}
|
|
3683
3854
|
#checkNode(node, meta, parent) {
|
|
3684
3855
|
if ("brand" in parent && !this.#isNodeAllowed(meta, parent)) {
|
|
3685
|
-
const text = CollectDiagnosticText.cannotBeNestedWithin(meta.
|
|
3856
|
+
const text = CollectDiagnosticText.cannotBeNestedWithin(meta.brand, parent.brand);
|
|
3686
3857
|
const origin = DiagnosticOrigin.fromNode(node);
|
|
3687
3858
|
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
3688
3859
|
return false;
|
|
@@ -3692,8 +3863,11 @@ class CollectService {
|
|
|
3692
3863
|
#isNodeAllowed(meta, parent) {
|
|
3693
3864
|
switch (meta.brand) {
|
|
3694
3865
|
case "describe":
|
|
3866
|
+
case "it":
|
|
3695
3867
|
case "test":
|
|
3696
|
-
if (parent.brand === "
|
|
3868
|
+
if (parent.brand === "it" ||
|
|
3869
|
+
parent.brand === "test" ||
|
|
3870
|
+
parent.brand === "expect") {
|
|
3697
3871
|
return false;
|
|
3698
3872
|
}
|
|
3699
3873
|
break;
|
|
@@ -3749,6 +3923,7 @@ var TestTreeNodeBrand;
|
|
|
3749
3923
|
(function (TestTreeNodeBrand) {
|
|
3750
3924
|
TestTreeNodeBrand["Describe"] = "describe";
|
|
3751
3925
|
TestTreeNodeBrand["Test"] = "test";
|
|
3926
|
+
TestTreeNodeBrand["It"] = "it";
|
|
3752
3927
|
TestTreeNodeBrand["Expect"] = "expect";
|
|
3753
3928
|
TestTreeNodeBrand["When"] = "when";
|
|
3754
3929
|
})(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
|
|
@@ -3808,6 +3983,7 @@ class ProjectService {
|
|
|
3808
3983
|
}
|
|
3809
3984
|
closeFile(filePath) {
|
|
3810
3985
|
this.#service.closeClientFile(filePath);
|
|
3986
|
+
SourceService.delete(filePath);
|
|
3811
3987
|
}
|
|
3812
3988
|
#getDefaultCompilerOptions() {
|
|
3813
3989
|
const defaultCompilerOptions = {
|
|
@@ -3837,6 +4013,15 @@ class ProjectService {
|
|
|
3837
4013
|
}
|
|
3838
4014
|
return project;
|
|
3839
4015
|
}
|
|
4016
|
+
getDiagnostics(filePath, sourceText, shouldInclude) {
|
|
4017
|
+
this.openFile(filePath, sourceText);
|
|
4018
|
+
const languageService = this.getLanguageService(filePath);
|
|
4019
|
+
const diagnostics = languageService?.getSemanticDiagnostics(filePath);
|
|
4020
|
+
if (diagnostics != null && shouldInclude != null) {
|
|
4021
|
+
return diagnostics.filter(shouldInclude);
|
|
4022
|
+
}
|
|
4023
|
+
return diagnostics;
|
|
4024
|
+
}
|
|
3840
4025
|
getLanguageService(filePath) {
|
|
3841
4026
|
const project = this.getDefaultProject(filePath);
|
|
3842
4027
|
return project?.getLanguageService(true);
|
|
@@ -3846,7 +4031,7 @@ class ProjectService {
|
|
|
3846
4031
|
const { fileNames } = this.#compiler.parseJsonSourceFileConfigFileContent(configSourceFile, this.#compiler.sys, Path.dirname(this.#resolvedConfig.tsconfig), undefined, this.#resolvedConfig.tsconfig);
|
|
3847
4032
|
return fileNames.includes(filePath);
|
|
3848
4033
|
}
|
|
3849
|
-
openFile(filePath, sourceText
|
|
4034
|
+
openFile(filePath, sourceText) {
|
|
3850
4035
|
switch (this.#resolvedConfig.tsconfig) {
|
|
3851
4036
|
case "findup":
|
|
3852
4037
|
break;
|
|
@@ -3858,7 +4043,7 @@ class ProjectService {
|
|
|
3858
4043
|
? () => this.#resolvedConfig.tsconfig
|
|
3859
4044
|
: () => undefined;
|
|
3860
4045
|
}
|
|
3861
|
-
const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined,
|
|
4046
|
+
const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined, this.#resolvedConfig.rootPath);
|
|
3862
4047
|
if (configFileName !== this.#lastSeenProject) {
|
|
3863
4048
|
this.#lastSeenProject = configFileName;
|
|
3864
4049
|
EventEmitter.dispatch([
|
|
@@ -3926,12 +4111,17 @@ class SuppressedDiagnosticText {
|
|
|
3926
4111
|
}
|
|
3927
4112
|
|
|
3928
4113
|
class SuppressedService {
|
|
3929
|
-
match(testTree
|
|
4114
|
+
match(testTree) {
|
|
3930
4115
|
if (!testTree.suppressedErrors) {
|
|
3931
4116
|
return;
|
|
3932
4117
|
}
|
|
3933
4118
|
for (const suppressedError of testTree.suppressedErrors) {
|
|
3934
|
-
|
|
4119
|
+
const suppressedResult = new SuppressedResult(suppressedError);
|
|
4120
|
+
if (suppressedError.diagnostics.length === 0) {
|
|
4121
|
+
continue;
|
|
4122
|
+
}
|
|
4123
|
+
if (suppressedError.ignore) {
|
|
4124
|
+
EventEmitter.dispatch(["suppressed:ignore", { result: suppressedResult }]);
|
|
3935
4125
|
continue;
|
|
3936
4126
|
}
|
|
3937
4127
|
const related = [
|
|
@@ -3941,12 +4131,12 @@ class SuppressedService {
|
|
|
3941
4131
|
const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, testTree.sourceFile);
|
|
3942
4132
|
if (!suppressedError.argument?.text) {
|
|
3943
4133
|
const text = SuppressedDiagnosticText.directiveRequires();
|
|
3944
|
-
onDiagnostics(
|
|
4134
|
+
this.#onDiagnostics(Diagnostic.error(text, origin).add({ related }), suppressedResult);
|
|
3945
4135
|
continue;
|
|
3946
4136
|
}
|
|
3947
4137
|
if (suppressedError.diagnostics.length > 1) {
|
|
3948
|
-
const text =
|
|
3949
|
-
onDiagnostics(
|
|
4138
|
+
const text = SuppressedDiagnosticText.onlySingleError();
|
|
4139
|
+
this.#onDiagnostics(Diagnostic.error(text, origin).add({ related }), suppressedResult);
|
|
3950
4140
|
continue;
|
|
3951
4141
|
}
|
|
3952
4142
|
let messageText = getDiagnosticMessageText(suppressedError.diagnostics[0]);
|
|
@@ -3954,10 +4144,12 @@ class SuppressedService {
|
|
|
3954
4144
|
messageText = messageText.join("\n");
|
|
3955
4145
|
}
|
|
3956
4146
|
if (!this.#matchMessage(messageText, suppressedError.argument.text)) {
|
|
3957
|
-
const text =
|
|
4147
|
+
const text = SuppressedDiagnosticText.messageDidNotMatch();
|
|
3958
4148
|
const origin = new DiagnosticOrigin(suppressedError.argument.start, suppressedError.argument.end, testTree.sourceFile);
|
|
3959
|
-
onDiagnostics(
|
|
4149
|
+
this.#onDiagnostics(Diagnostic.error(text, origin).add({ related }), suppressedResult);
|
|
4150
|
+
continue;
|
|
3960
4151
|
}
|
|
4152
|
+
EventEmitter.dispatch(["suppressed:match", { result: suppressedResult }]);
|
|
3961
4153
|
}
|
|
3962
4154
|
}
|
|
3963
4155
|
#matchMessage(source, target) {
|
|
@@ -3973,6 +4165,9 @@ class SuppressedService {
|
|
|
3973
4165
|
}
|
|
3974
4166
|
return source.includes(target);
|
|
3975
4167
|
}
|
|
4168
|
+
#onDiagnostics(diagnostic, result) {
|
|
4169
|
+
EventEmitter.dispatch(["suppressed:error", { diagnostics: [diagnostic], result }]);
|
|
4170
|
+
}
|
|
3976
4171
|
}
|
|
3977
4172
|
|
|
3978
4173
|
class EnsureDiagnosticText {
|
|
@@ -4075,11 +4270,11 @@ class ExpectDiagnosticText {
|
|
|
4075
4270
|
static isNotAssignableTo(sourceTypeText, targetTypeText) {
|
|
4076
4271
|
return `Type '${sourceTypeText}' is not assignable to type '${targetTypeText}'.`;
|
|
4077
4272
|
}
|
|
4078
|
-
static
|
|
4079
|
-
return `Type '${sourceTypeText}' is assignable
|
|
4273
|
+
static isAssignableFrom(sourceTypeText, targetTypeText) {
|
|
4274
|
+
return `Type '${sourceTypeText}' is assignable from type '${targetTypeText}'.`;
|
|
4080
4275
|
}
|
|
4081
|
-
static
|
|
4082
|
-
return `Type '${sourceTypeText}' is not assignable
|
|
4276
|
+
static isNotAssignableFrom(sourceTypeText, targetTypeText) {
|
|
4277
|
+
return `Type '${sourceTypeText}' is not assignable from type '${targetTypeText}'.`;
|
|
4083
4278
|
}
|
|
4084
4279
|
static isTheSame(sourceTypeText, targetTypeText) {
|
|
4085
4280
|
return `Type '${sourceTypeText}' is the same as type '${targetTypeText}'.`;
|
|
@@ -4293,7 +4488,7 @@ class ToAcceptProps {
|
|
|
4293
4488
|
}
|
|
4294
4489
|
if (this.#isOptionalProperty(targetProperty) && !this.#isOptionalProperty(sourceProperty)) {
|
|
4295
4490
|
const text = [
|
|
4296
|
-
ExpectDiagnosticText.
|
|
4491
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4297
4492
|
ExpectDiagnosticText.requiresProperty(sourceTypeText, targetPropertyName),
|
|
4298
4493
|
];
|
|
4299
4494
|
const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
|
|
@@ -4306,9 +4501,9 @@ class ToAcceptProps {
|
|
|
4306
4501
|
const targetPropertyTypeText = this.#typeChecker.typeToString(targetPropertyType);
|
|
4307
4502
|
const sourcePropertyTypeText = this.#typeChecker.typeToString(sourcePropertyType);
|
|
4308
4503
|
const text = [
|
|
4309
|
-
ExpectDiagnosticText.
|
|
4504
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4310
4505
|
ExpectDiagnosticText.typesOfPropertyAreNotCompatible(targetPropertyName),
|
|
4311
|
-
ExpectDiagnosticText.
|
|
4506
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourcePropertyTypeText, targetPropertyTypeText),
|
|
4312
4507
|
];
|
|
4313
4508
|
const origin = matchWorker.resolveDiagnosticOrigin(targetProperty, targetNode);
|
|
4314
4509
|
diagnostics.push(diagnostic.extendWith(text, origin));
|
|
@@ -4320,7 +4515,7 @@ class ToAcceptProps {
|
|
|
4320
4515
|
const targetProperty = targetType.getProperty(sourcePropertyName);
|
|
4321
4516
|
if (!targetProperty && !this.#isOptionalProperty(sourceProperty)) {
|
|
4322
4517
|
const text = [
|
|
4323
|
-
ExpectDiagnosticText.
|
|
4518
|
+
ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText),
|
|
4324
4519
|
ExpectDiagnosticText.requiresProperty(sourceTypeText, sourcePropertyName),
|
|
4325
4520
|
];
|
|
4326
4521
|
diagnostics.push(diagnostic.extendWith(text));
|
|
@@ -4328,7 +4523,7 @@ class ToAcceptProps {
|
|
|
4328
4523
|
}
|
|
4329
4524
|
}
|
|
4330
4525
|
if (diagnostics.length === 0) {
|
|
4331
|
-
const text = ExpectDiagnosticText.
|
|
4526
|
+
const text = ExpectDiagnosticText.isAssignableFrom(sourceTypeText, targetTypeText);
|
|
4332
4527
|
diagnostics.push(diagnostic.extendWith(text));
|
|
4333
4528
|
return { diagnostics, isMatch: true };
|
|
4334
4529
|
}
|
|
@@ -4338,8 +4533,8 @@ class ToAcceptProps {
|
|
|
4338
4533
|
let accumulator = [];
|
|
4339
4534
|
const isMatch = sourceType.types.some((sourceType) => {
|
|
4340
4535
|
const text = matchWorker.assertionNode.isNot
|
|
4341
|
-
? ExpectDiagnosticText.
|
|
4342
|
-
: ExpectDiagnosticText.
|
|
4536
|
+
? ExpectDiagnosticText.isAssignableFrom(sourceTypeText, targetTypeText)
|
|
4537
|
+
: ExpectDiagnosticText.isNotAssignableFrom(sourceTypeText, targetTypeText);
|
|
4343
4538
|
const { diagnostics, isMatch } = explain(sourceType, targetType, diagnostic.extendWith(text));
|
|
4344
4539
|
if (isMatch) {
|
|
4345
4540
|
accumulator = diagnostics;
|
|
@@ -4480,24 +4675,24 @@ class ToBeApplicable {
|
|
|
4480
4675
|
}
|
|
4481
4676
|
}
|
|
4482
4677
|
|
|
4483
|
-
class
|
|
4484
|
-
explainText = ExpectDiagnosticText.
|
|
4485
|
-
explainNotText = ExpectDiagnosticText.
|
|
4678
|
+
class ToBeAssignableFrom extends RelationMatcherBase {
|
|
4679
|
+
explainText = ExpectDiagnosticText.isAssignableFrom;
|
|
4680
|
+
explainNotText = ExpectDiagnosticText.isNotAssignableFrom;
|
|
4486
4681
|
match(matchWorker, sourceNode, targetNode) {
|
|
4487
4682
|
return {
|
|
4488
4683
|
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
4489
|
-
isMatch: matchWorker.
|
|
4684
|
+
isMatch: matchWorker.checkIsAssignableWith(sourceNode, targetNode),
|
|
4490
4685
|
};
|
|
4491
4686
|
}
|
|
4492
4687
|
}
|
|
4493
4688
|
|
|
4494
|
-
class
|
|
4495
|
-
explainText = ExpectDiagnosticText.
|
|
4496
|
-
explainNotText = ExpectDiagnosticText.
|
|
4689
|
+
class ToBeAssignableTo extends RelationMatcherBase {
|
|
4690
|
+
explainText = ExpectDiagnosticText.isAssignableTo;
|
|
4691
|
+
explainNotText = ExpectDiagnosticText.isNotAssignableTo;
|
|
4497
4692
|
match(matchWorker, sourceNode, targetNode) {
|
|
4498
4693
|
return {
|
|
4499
4694
|
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
4500
|
-
isMatch: matchWorker.
|
|
4695
|
+
isMatch: matchWorker.checkIsAssignableTo(sourceNode, targetNode),
|
|
4501
4696
|
};
|
|
4502
4697
|
}
|
|
4503
4698
|
}
|
|
@@ -4549,19 +4744,8 @@ class ToBeCallableWith extends AbilityMatcherBase {
|
|
|
4549
4744
|
explainText = ExpectDiagnosticText.isCallable;
|
|
4550
4745
|
explainNotText = ExpectDiagnosticText.isNotCallable;
|
|
4551
4746
|
match(matchWorker, sourceNode, targetNodes, onDiagnostics) {
|
|
4552
|
-
|
|
4553
|
-
if (
|
|
4554
|
-
type = matchWorker.typeChecker.getResolvedSignature(sourceNode)?.getReturnType();
|
|
4555
|
-
}
|
|
4556
|
-
if (this.compiler.isArrowFunction(sourceNode) ||
|
|
4557
|
-
this.compiler.isFunctionDeclaration(sourceNode) ||
|
|
4558
|
-
this.compiler.isFunctionExpression(sourceNode) ||
|
|
4559
|
-
this.compiler.isExpressionWithTypeArguments(sourceNode) ||
|
|
4560
|
-
this.compiler.isIdentifier(sourceNode) ||
|
|
4561
|
-
this.compiler.isPropertyAccessExpression(sourceNode)) {
|
|
4562
|
-
type = matchWorker.getType(sourceNode);
|
|
4563
|
-
}
|
|
4564
|
-
if (!type || type.getCallSignatures().length === 0) {
|
|
4747
|
+
const sourceType = matchWorker.getType(sourceNode);
|
|
4748
|
+
if (sourceType.getCallSignatures().length === 0) {
|
|
4565
4749
|
const text = [];
|
|
4566
4750
|
if (nodeBelongsToArgumentList(this.compiler, sourceNode)) {
|
|
4567
4751
|
text.push(ExpectDiagnosticText.argumentMustBe("source", "a callable expression"));
|
|
@@ -4569,7 +4753,7 @@ class ToBeCallableWith extends AbilityMatcherBase {
|
|
|
4569
4753
|
else {
|
|
4570
4754
|
text.push(ExpectDiagnosticText.typeArgumentMustBe("Source", "a callable type"));
|
|
4571
4755
|
}
|
|
4572
|
-
if (
|
|
4756
|
+
if (sourceType.getConstructSignatures().length > 0) {
|
|
4573
4757
|
text.push(ExpectDiagnosticText.didYouMeanToUse("the '.toBeConstructableWith()' matcher"));
|
|
4574
4758
|
}
|
|
4575
4759
|
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
@@ -4768,8 +4952,8 @@ class ExpectService {
|
|
|
4768
4952
|
toAcceptProps;
|
|
4769
4953
|
toBe;
|
|
4770
4954
|
toBeApplicable;
|
|
4955
|
+
toBeAssignableFrom;
|
|
4771
4956
|
toBeAssignableTo;
|
|
4772
|
-
toBeAssignableWith;
|
|
4773
4957
|
toBeCallableWith;
|
|
4774
4958
|
toBeConstructableWith;
|
|
4775
4959
|
toHaveProperty;
|
|
@@ -4781,8 +4965,8 @@ class ExpectService {
|
|
|
4781
4965
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
4782
4966
|
this.toBe = new ToBe();
|
|
4783
4967
|
this.toBeApplicable = new ToBeApplicable(compiler);
|
|
4968
|
+
this.toBeAssignableFrom = new ToBeAssignableFrom();
|
|
4784
4969
|
this.toBeAssignableTo = new ToBeAssignableTo();
|
|
4785
|
-
this.toBeAssignableWith = new ToBeAssignableWith();
|
|
4786
4970
|
this.toBeCallableWith = new ToBeCallableWith(compiler);
|
|
4787
4971
|
this.toBeConstructableWith = new ToBeConstructableWith(compiler);
|
|
4788
4972
|
this.toHaveProperty = new ToHaveProperty(compiler);
|
|
@@ -4804,8 +4988,8 @@ class ExpectService {
|
|
|
4804
4988
|
switch (matcherNameText) {
|
|
4805
4989
|
case "toAcceptProps":
|
|
4806
4990
|
case "toBe":
|
|
4991
|
+
case "toBeAssignableFrom":
|
|
4807
4992
|
case "toBeAssignableTo":
|
|
4808
|
-
case "toBeAssignableWith":
|
|
4809
4993
|
if (!argumentOrTypeArgumentIsProvided("target", "Target", assertionNode.target?.[0], assertionNode.matcherNameNode.name, onDiagnostics)) {
|
|
4810
4994
|
return;
|
|
4811
4995
|
}
|
|
@@ -5000,8 +5184,7 @@ class FixmeService {
|
|
|
5000
5184
|
FixmeService.#range = FixmeService.#range?.previous;
|
|
5001
5185
|
}
|
|
5002
5186
|
if (isFail === false) {
|
|
5003
|
-
const
|
|
5004
|
-
const text = [FixmeDiagnosticText.wasSupposedToFail(targetText), FixmeDiagnosticText.considerRemoving()];
|
|
5187
|
+
const text = [FixmeDiagnosticText.wasSupposedToFail(owner.brand), FixmeDiagnosticText.considerRemoving()];
|
|
5005
5188
|
const origin = new DiagnosticOrigin(directive.namespace.start, directive.directive.end, directive.sourceFile);
|
|
5006
5189
|
onFileDiagnostics([Diagnostic.error(text, origin)]);
|
|
5007
5190
|
}
|
|
@@ -5053,7 +5236,7 @@ class TestTreeWalker {
|
|
|
5053
5236
|
}
|
|
5054
5237
|
async visit(nodes, runModeFlags, parentResult) {
|
|
5055
5238
|
for (const node of nodes) {
|
|
5056
|
-
if (this.#cancellationToken?.isCancellationRequested) {
|
|
5239
|
+
if (this.#cancellationToken?.isCancellationRequested()) {
|
|
5057
5240
|
break;
|
|
5058
5241
|
}
|
|
5059
5242
|
const fixmeDirective = Directive.getDirectiveRange(this.#compiler, node, "fixme");
|
|
@@ -5064,6 +5247,7 @@ class TestTreeWalker {
|
|
|
5064
5247
|
case "describe":
|
|
5065
5248
|
await this.#visitDescribe(node, runModeFlags, parentResult);
|
|
5066
5249
|
break;
|
|
5250
|
+
case "it":
|
|
5067
5251
|
case "test":
|
|
5068
5252
|
await this.#visitTest(node, runModeFlags, parentResult);
|
|
5069
5253
|
break;
|
|
@@ -5162,7 +5346,7 @@ class TestTreeWalker {
|
|
|
5162
5346
|
EventEmitter.dispatch(["test:skip", { result: testResult }]);
|
|
5163
5347
|
return;
|
|
5164
5348
|
}
|
|
5165
|
-
const isPass = testResult.
|
|
5349
|
+
const isPass = testResult.assertionCounts.failed === 0;
|
|
5166
5350
|
if (FixmeService.isFixme(test, isPass)) {
|
|
5167
5351
|
EventEmitter.dispatch(["test:fixme", { result: testResult }]);
|
|
5168
5352
|
return;
|
|
@@ -5195,10 +5379,10 @@ class FileRunner {
|
|
|
5195
5379
|
EventEmitter.dispatch(["file:error", { diagnostics, result }]);
|
|
5196
5380
|
}
|
|
5197
5381
|
async run(file, cancellationToken) {
|
|
5198
|
-
if (cancellationToken.isCancellationRequested) {
|
|
5382
|
+
if (cancellationToken.isCancellationRequested()) {
|
|
5199
5383
|
return;
|
|
5200
5384
|
}
|
|
5201
|
-
this.#projectService.openFile(file.path
|
|
5385
|
+
this.#projectService.openFile(file.path);
|
|
5202
5386
|
const fileResult = new FileResult(file);
|
|
5203
5387
|
EventEmitter.dispatch(["file:start", { result: fileResult }]);
|
|
5204
5388
|
await this.#run(file, fileResult, cancellationToken);
|
|
@@ -5235,13 +5419,11 @@ class FileRunner {
|
|
|
5235
5419
|
this.#onDiagnostics([Diagnostic.error("A template test file must export a string.")], fileResult);
|
|
5236
5420
|
return;
|
|
5237
5421
|
}
|
|
5238
|
-
this.#projectService.openFile(file.path, testText
|
|
5422
|
+
this.#projectService.openFile(file.path, testText);
|
|
5239
5423
|
return this.#resolveFileFacts(file, fileResult, runModeFlags);
|
|
5240
5424
|
}
|
|
5241
5425
|
const testTree = this.#collectService.createTestTree(sourceFile, semanticDiagnostics);
|
|
5242
|
-
this.#suppressedService.match(testTree
|
|
5243
|
-
this.#onDiagnostics(diagnostics, fileResult);
|
|
5244
|
-
});
|
|
5426
|
+
this.#suppressedService.match(testTree);
|
|
5245
5427
|
return { runModeFlags, testTree, typeChecker };
|
|
5246
5428
|
}
|
|
5247
5429
|
async #run(file, fileResult, cancellationToken) {
|
|
@@ -5272,7 +5454,7 @@ class FileRunner {
|
|
|
5272
5454
|
class Runner {
|
|
5273
5455
|
#eventEmitter = new EventEmitter();
|
|
5274
5456
|
#resolvedConfig;
|
|
5275
|
-
static version = "5.0.0-beta.
|
|
5457
|
+
static version = "5.0.0-beta.2";
|
|
5276
5458
|
constructor(resolvedConfig) {
|
|
5277
5459
|
this.#resolvedConfig = resolvedConfig;
|
|
5278
5460
|
}
|
|
@@ -5336,7 +5518,7 @@ class Runner {
|
|
|
5336
5518
|
EventEmitter.dispatch(["target:end", { result: targetResult }]);
|
|
5337
5519
|
}
|
|
5338
5520
|
EventEmitter.dispatch(["run:end", { result }]);
|
|
5339
|
-
if (cancellationToken.
|
|
5521
|
+
if (cancellationToken.getReason() === "failFast") {
|
|
5340
5522
|
cancellationToken.reset();
|
|
5341
5523
|
}
|
|
5342
5524
|
}
|
|
@@ -5382,11 +5564,11 @@ class Cli {
|
|
|
5382
5564
|
return;
|
|
5383
5565
|
}
|
|
5384
5566
|
const { commandLineOptions, pathMatch } = await Config.parseCommandLine(commandLine);
|
|
5385
|
-
if (cancellationToken.isCancellationRequested) {
|
|
5567
|
+
if (cancellationToken.isCancellationRequested()) {
|
|
5386
5568
|
return;
|
|
5387
5569
|
}
|
|
5388
5570
|
do {
|
|
5389
|
-
if (cancellationToken.
|
|
5571
|
+
if (cancellationToken.getReason() === "configChange") {
|
|
5390
5572
|
cancellationToken.reset();
|
|
5391
5573
|
exitCodeHandler.resetCode();
|
|
5392
5574
|
OutputService.clearTerminal();
|
|
@@ -5400,7 +5582,7 @@ class Cli {
|
|
|
5400
5582
|
commandLineOptions,
|
|
5401
5583
|
pathMatch,
|
|
5402
5584
|
});
|
|
5403
|
-
if (cancellationToken.isCancellationRequested) {
|
|
5585
|
+
if (cancellationToken.isCancellationRequested()) {
|
|
5404
5586
|
if (commandLine.includes("--watch")) {
|
|
5405
5587
|
await this.#waitForChangedFiles(resolvedConfig, cancellationToken);
|
|
5406
5588
|
}
|
|
@@ -5441,7 +5623,7 @@ class Cli {
|
|
|
5441
5623
|
const runner = new Runner(resolvedConfig);
|
|
5442
5624
|
await runner.run(testFiles, cancellationToken);
|
|
5443
5625
|
PluginService.removeHandlers();
|
|
5444
|
-
} while (cancellationToken.
|
|
5626
|
+
} while (cancellationToken.getReason() === "configChange");
|
|
5445
5627
|
this.#eventEmitter.removeHandlers();
|
|
5446
5628
|
}
|
|
5447
5629
|
#waitForChangedFiles(resolvedConfig, cancellationToken) {
|
|
@@ -5472,4 +5654,4 @@ class Cli {
|
|
|
5472
5654
|
}
|
|
5473
5655
|
}
|
|
5474
5656
|
|
|
5475
|
-
export { BaseReporter,
|
|
5657
|
+
export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, Result, ResultStatus, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, Store, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileStatusText, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|