tstyche 2.0.0-beta.1 → 2.0.0-rc.0
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/build/tstyche.d.ts +48 -59
- package/build/tstyche.js +302 -363
- package/package.json +4 -5
package/build/tstyche.js
CHANGED
|
@@ -3,10 +3,9 @@ import path from 'node:path';
|
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import os from 'node:os';
|
|
6
|
-
import { existsSync,
|
|
6
|
+
import { existsSync, writeFileSync, rmSync } from 'node:fs';
|
|
7
7
|
import fs from 'node:fs/promises';
|
|
8
8
|
import vm from 'node:vm';
|
|
9
|
-
import https from 'node:https';
|
|
10
9
|
import { spawn } from 'node:child_process';
|
|
11
10
|
|
|
12
11
|
class EventEmitter {
|
|
@@ -34,18 +33,21 @@ class EventEmitter {
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
class Path {
|
|
36
|
+
static normalizeSlashes;
|
|
37
|
+
static {
|
|
38
|
+
if (path.sep === "/") {
|
|
39
|
+
Path.normalizeSlashes = (filePath) => filePath;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
Path.normalizeSlashes = (filePath) => filePath.replace(/\\/g, "/");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
37
45
|
static dirname(filePath) {
|
|
38
46
|
return Path.normalizeSlashes(path.dirname(filePath));
|
|
39
47
|
}
|
|
40
48
|
static join(...filePaths) {
|
|
41
49
|
return Path.normalizeSlashes(path.join(...filePaths));
|
|
42
50
|
}
|
|
43
|
-
static normalizeSlashes(filePath) {
|
|
44
|
-
if (path.sep === "/") {
|
|
45
|
-
return filePath;
|
|
46
|
-
}
|
|
47
|
-
return filePath.replace(/\\/g, "/");
|
|
48
|
-
}
|
|
49
51
|
static relative(from, to) {
|
|
50
52
|
let relativePath = path.relative(from, to);
|
|
51
53
|
if (!relativePath.startsWith("./")) {
|
|
@@ -61,16 +63,11 @@ class Path {
|
|
|
61
63
|
class TestFile {
|
|
62
64
|
path;
|
|
63
65
|
position;
|
|
64
|
-
constructor(identifier) {
|
|
65
|
-
this.path = Path.normalizeSlashes(this.#
|
|
66
|
+
constructor(identifier, position) {
|
|
67
|
+
this.path = Path.normalizeSlashes(this.#toPath(identifier));
|
|
68
|
+
this.position = position;
|
|
66
69
|
}
|
|
67
|
-
|
|
68
|
-
if (options.position != null) {
|
|
69
|
-
this.position = options.position;
|
|
70
|
-
}
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
#resolvePath(identifier) {
|
|
70
|
+
#toPath(identifier) {
|
|
74
71
|
if (typeof identifier === "string" && !identifier.startsWith("file:")) {
|
|
75
72
|
return identifier;
|
|
76
73
|
}
|
|
@@ -304,21 +301,21 @@ class CodeSpanText {
|
|
|
304
301
|
this.props = props;
|
|
305
302
|
}
|
|
306
303
|
render() {
|
|
307
|
-
const lastLineInFile = this.props.
|
|
308
|
-
const { character: markedCharacter, line: markedLine } = this.props.
|
|
304
|
+
const lastLineInFile = this.props.sourceFile.getLineAndCharacterOfPosition(this.props.sourceFile.text.length).line;
|
|
305
|
+
const { character: markedCharacter, line: markedLine } = this.props.sourceFile.getLineAndCharacterOfPosition(this.props.start);
|
|
309
306
|
const firstLine = Math.max(markedLine - 2, 0);
|
|
310
307
|
const lastLine = Math.min(firstLine + 5, lastLineInFile);
|
|
311
308
|
const lineNumberMaxWidth = String(lastLine + 1).length;
|
|
312
309
|
const codeSpan = [];
|
|
313
310
|
for (let index = firstLine; index <= lastLine; index++) {
|
|
314
|
-
const lineStart = this.props.
|
|
311
|
+
const lineStart = this.props.sourceFile.getPositionOfLineAndCharacter(index, 0);
|
|
315
312
|
const lineEnd = index === lastLineInFile
|
|
316
|
-
? this.props.
|
|
317
|
-
: this.props.
|
|
313
|
+
? this.props.sourceFile.text.length
|
|
314
|
+
: this.props.sourceFile.getPositionOfLineAndCharacter(index + 1, 0);
|
|
318
315
|
const lineNumberText = String(index + 1);
|
|
319
|
-
const lineText = this.props.
|
|
316
|
+
const lineText = this.props.sourceFile.text.slice(lineStart, lineEnd).trimEnd().replace(/\t/g, " ");
|
|
320
317
|
if (index === markedLine) {
|
|
321
|
-
codeSpan.push(jsx(Line, { children: [jsx(Text, { color: "31", children: ">" }), " ", lineNumberText.padStart(lineNumberMaxWidth), " ", jsx(Text, { color: "90", children: "|" }), " ", lineText] }), jsx(Line, { children: [" ".repeat(lineNumberMaxWidth + 3), jsx(Text, { color: "90", children: "|" }), " ".repeat(markedCharacter + 1), jsx(Text, { color: "31", children: "^" })] }));
|
|
318
|
+
codeSpan.push(jsx(Line, { children: [jsx(Text, { color: "31", children: ">" }), jsx(Text, { children: " " }), lineNumberText.padStart(lineNumberMaxWidth), jsx(Text, { children: " " }), jsx(Text, { color: "90", children: "|" }), " ", lineText] }), jsx(Line, { children: [" ".repeat(lineNumberMaxWidth + 3), jsx(Text, { color: "90", children: "|" }), " ".repeat(markedCharacter + 1), jsx(Text, { color: "31", children: "^" })] }));
|
|
322
319
|
}
|
|
323
320
|
else {
|
|
324
321
|
codeSpan.push(jsx(Line, { children: [" ".repeat(2), jsx(Text, { color: "90", children: [lineNumberText.padStart(lineNumberMaxWidth), " | ", lineText || ""] })] }));
|
|
@@ -328,7 +325,7 @@ class CodeSpanText {
|
|
|
328
325
|
jsx(Text, { color: "90", children: " ❭ " }),
|
|
329
326
|
jsx(Text, { children: ancestor }),
|
|
330
327
|
]);
|
|
331
|
-
const location = (jsx(Line, { children: [" ".repeat(lineNumberMaxWidth + 5), jsx(Text, { color: "90", children: "at" }), " ", jsx(Text, { color: "36", children: Path.relative("", this.props.
|
|
328
|
+
const location = (jsx(Line, { children: [" ".repeat(lineNumberMaxWidth + 5), jsx(Text, { color: "90", children: "at" }), jsx(Text, { children: " " }), jsx(Text, { color: "36", children: Path.relative("", this.props.sourceFile.fileName) }), jsx(Text, { color: "90", children: [":", String(markedLine + 1), ":", String(markedCharacter + 1)] }), breadcrumbs] }));
|
|
332
329
|
return (jsx(Text, { children: [codeSpan, jsx(Line, {}), location] }));
|
|
333
330
|
}
|
|
334
331
|
}
|
|
@@ -619,21 +616,21 @@ class RanFilesText {
|
|
|
619
616
|
this.props = props;
|
|
620
617
|
}
|
|
621
618
|
render() {
|
|
622
|
-
const
|
|
619
|
+
const testNameMatchText = [];
|
|
623
620
|
if (this.props.onlyMatch != null) {
|
|
624
|
-
|
|
621
|
+
testNameMatchText.push(jsx(Text, { children: [jsx(Text, { color: "90", children: "matching " }), jsx(MatchText, { text: this.props.onlyMatch })] }));
|
|
625
622
|
}
|
|
626
623
|
if (this.props.skipMatch != null) {
|
|
627
|
-
|
|
624
|
+
testNameMatchText.push(jsx(Text, { children: [this.props.onlyMatch == null ? undefined : jsx(Text, { color: "90", children: " and " }), jsx(Text, { color: "90", children: "not matching " }), jsx(MatchText, { text: this.props.skipMatch })] }));
|
|
628
625
|
}
|
|
629
|
-
let
|
|
626
|
+
let pathMatchText;
|
|
630
627
|
if (this.props.pathMatch.length > 0) {
|
|
631
|
-
|
|
628
|
+
pathMatchText = (jsx(Text, { children: [jsx(Text, { color: "90", children: "test files matching " }), jsx(MatchText, { text: this.props.pathMatch }), jsx(Text, { color: "90", children: "." })] }));
|
|
632
629
|
}
|
|
633
630
|
else {
|
|
634
|
-
|
|
631
|
+
pathMatchText = jsx(Text, { color: "90", children: "all test files." });
|
|
635
632
|
}
|
|
636
|
-
return (jsx(Line, { children: [jsx(Text, { color: "90", children: "Ran " }),
|
|
633
|
+
return (jsx(Line, { children: [jsx(Text, { color: "90", children: "Ran " }), testNameMatchText.length > 0 ? jsx(Text, { color: "90", children: "tests " }) : undefined, testNameMatchText, testNameMatchText.length > 0 ? jsx(Text, { color: "90", children: " in " }) : undefined, pathMatchText] }));
|
|
637
634
|
}
|
|
638
635
|
}
|
|
639
636
|
function summaryText({ duration, expectCount, fileCount, onlyMatch, pathMatch, skipMatch, targetCount, testCount, }) {
|
|
@@ -728,7 +725,6 @@ class FileViewService {
|
|
|
728
725
|
}
|
|
729
726
|
|
|
730
727
|
class RuntimeReporter {
|
|
731
|
-
resolvedConfig;
|
|
732
728
|
#currentCompilerVersion;
|
|
733
729
|
#currentProjectConfigFilePath;
|
|
734
730
|
#fileCount = 0;
|
|
@@ -736,10 +732,11 @@ class RuntimeReporter {
|
|
|
736
732
|
#hasReportedAdds = false;
|
|
737
733
|
#hasReportedError = false;
|
|
738
734
|
#isFileViewExpanded = false;
|
|
735
|
+
#resolvedConfig;
|
|
739
736
|
#outputService;
|
|
740
737
|
#seenDeprecations = new Set();
|
|
741
738
|
constructor(resolvedConfig, outputService) {
|
|
742
|
-
this
|
|
739
|
+
this.#resolvedConfig = resolvedConfig;
|
|
743
740
|
this.#outputService = outputService;
|
|
744
741
|
}
|
|
745
742
|
get #isLastFile() {
|
|
@@ -757,7 +754,7 @@ class RuntimeReporter {
|
|
|
757
754
|
break;
|
|
758
755
|
}
|
|
759
756
|
case "run:start": {
|
|
760
|
-
this.#isFileViewExpanded = payload.result.testFiles.length === 1 && this
|
|
757
|
+
this.#isFileViewExpanded = payload.result.testFiles.length === 1 && this.#resolvedConfig.watch !== true;
|
|
761
758
|
break;
|
|
762
759
|
}
|
|
763
760
|
case "store:info": {
|
|
@@ -960,8 +957,8 @@ class WatchReporter {
|
|
|
960
957
|
}
|
|
961
958
|
|
|
962
959
|
class ResultTiming {
|
|
963
|
-
end =
|
|
964
|
-
start =
|
|
960
|
+
end = Number.NaN;
|
|
961
|
+
start = Number.NaN;
|
|
965
962
|
get duration() {
|
|
966
963
|
return this.end - this.start;
|
|
967
964
|
}
|
|
@@ -989,8 +986,8 @@ var ResultStatus;
|
|
|
989
986
|
|
|
990
987
|
class ExpectResult {
|
|
991
988
|
assertion;
|
|
992
|
-
parent;
|
|
993
989
|
diagnostics = [];
|
|
990
|
+
parent;
|
|
994
991
|
status = "runs";
|
|
995
992
|
timing = new ResultTiming();
|
|
996
993
|
constructor(assertion, parent) {
|
|
@@ -1010,12 +1007,12 @@ class ResultCount {
|
|
|
1010
1007
|
}
|
|
1011
1008
|
|
|
1012
1009
|
class FileResult {
|
|
1013
|
-
testFile;
|
|
1014
1010
|
diagnostics = [];
|
|
1015
1011
|
expectCount = new ResultCount();
|
|
1016
1012
|
results = [];
|
|
1017
1013
|
status = "runs";
|
|
1018
1014
|
testCount = new ResultCount();
|
|
1015
|
+
testFile;
|
|
1019
1016
|
timing = new ResultTiming();
|
|
1020
1017
|
constructor(testFile) {
|
|
1021
1018
|
this.testFile = testFile;
|
|
@@ -1024,8 +1021,8 @@ class FileResult {
|
|
|
1024
1021
|
|
|
1025
1022
|
class ProjectResult {
|
|
1026
1023
|
compilerVersion;
|
|
1027
|
-
projectConfigFilePath;
|
|
1028
1024
|
diagnostics = [];
|
|
1025
|
+
projectConfigFilePath;
|
|
1029
1026
|
results = [];
|
|
1030
1027
|
constructor(compilerVersion, projectConfigFilePath) {
|
|
1031
1028
|
this.compilerVersion = compilerVersion;
|
|
@@ -1034,13 +1031,13 @@ class ProjectResult {
|
|
|
1034
1031
|
}
|
|
1035
1032
|
|
|
1036
1033
|
class Result {
|
|
1037
|
-
resolvedConfig;
|
|
1038
|
-
testFiles;
|
|
1039
1034
|
expectCount = new ResultCount();
|
|
1040
1035
|
fileCount = new ResultCount();
|
|
1036
|
+
resolvedConfig;
|
|
1041
1037
|
results = [];
|
|
1042
1038
|
targetCount = new ResultCount();
|
|
1043
1039
|
testCount = new ResultCount();
|
|
1040
|
+
testFiles;
|
|
1044
1041
|
timing = new ResultTiming();
|
|
1045
1042
|
constructor(resolvedConfig, testFiles) {
|
|
1046
1043
|
this.resolvedConfig = resolvedConfig;
|
|
@@ -1265,11 +1262,11 @@ class ResultHandler {
|
|
|
1265
1262
|
}
|
|
1266
1263
|
|
|
1267
1264
|
class TargetResult {
|
|
1268
|
-
versionTag;
|
|
1269
|
-
testFiles;
|
|
1270
1265
|
results = new Map();
|
|
1271
1266
|
status = "runs";
|
|
1267
|
+
testFiles;
|
|
1272
1268
|
timing = new ResultTiming();
|
|
1269
|
+
versionTag;
|
|
1273
1270
|
constructor(versionTag, testFiles) {
|
|
1274
1271
|
this.versionTag = versionTag;
|
|
1275
1272
|
this.testFiles = testFiles;
|
|
@@ -1277,12 +1274,12 @@ class TargetResult {
|
|
|
1277
1274
|
}
|
|
1278
1275
|
|
|
1279
1276
|
class TestResult {
|
|
1280
|
-
test;
|
|
1281
|
-
parent;
|
|
1282
1277
|
diagnostics = [];
|
|
1283
1278
|
expectCount = new ResultCount();
|
|
1279
|
+
parent;
|
|
1284
1280
|
results = [];
|
|
1285
1281
|
status = "runs";
|
|
1282
|
+
test;
|
|
1286
1283
|
timing = new ResultTiming();
|
|
1287
1284
|
constructor(test, parent) {
|
|
1288
1285
|
this.test = test;
|
|
@@ -1328,27 +1325,27 @@ var CancellationReason;
|
|
|
1328
1325
|
})(CancellationReason || (CancellationReason = {}));
|
|
1329
1326
|
|
|
1330
1327
|
class Watcher {
|
|
1331
|
-
targetPath;
|
|
1332
1328
|
#abortController = new AbortController();
|
|
1333
1329
|
#onChanged;
|
|
1334
1330
|
#onRemoved;
|
|
1335
1331
|
#recursive;
|
|
1332
|
+
#targetPath;
|
|
1336
1333
|
#watcher;
|
|
1337
|
-
constructor(targetPath, onChanged, onRemoved,
|
|
1338
|
-
this
|
|
1334
|
+
constructor(targetPath, onChanged, onRemoved, options) {
|
|
1335
|
+
this.#targetPath = targetPath;
|
|
1339
1336
|
this.#onChanged = onChanged;
|
|
1340
1337
|
this.#onRemoved = onRemoved ?? onChanged;
|
|
1341
|
-
this.#recursive = recursive;
|
|
1338
|
+
this.#recursive = options?.recursive;
|
|
1342
1339
|
}
|
|
1343
1340
|
close() {
|
|
1344
1341
|
this.#abortController.abort();
|
|
1345
1342
|
}
|
|
1346
1343
|
async watch() {
|
|
1347
|
-
this.#watcher = fs.watch(this
|
|
1344
|
+
this.#watcher = fs.watch(this.#targetPath, { recursive: this.#recursive, signal: this.#abortController.signal });
|
|
1348
1345
|
try {
|
|
1349
1346
|
for await (const event of this.#watcher) {
|
|
1350
1347
|
if (event.filename != null) {
|
|
1351
|
-
const filePath = Path.resolve(this
|
|
1348
|
+
const filePath = Path.resolve(this.#targetPath, event.filename);
|
|
1352
1349
|
if (existsSync(filePath)) {
|
|
1353
1350
|
await this.#onChanged(filePath);
|
|
1354
1351
|
}
|
|
@@ -1375,12 +1372,31 @@ class FileWatcher extends Watcher {
|
|
|
1375
1372
|
}
|
|
1376
1373
|
}
|
|
1377
1374
|
|
|
1375
|
+
class DiagnosticOrigin {
|
|
1376
|
+
breadcrumbs;
|
|
1377
|
+
end;
|
|
1378
|
+
sourceFile;
|
|
1379
|
+
start;
|
|
1380
|
+
constructor(start, end, sourceFile, breadcrumbs) {
|
|
1381
|
+
this.start = start;
|
|
1382
|
+
this.end = end;
|
|
1383
|
+
this.sourceFile = sourceFile;
|
|
1384
|
+
this.breadcrumbs = breadcrumbs;
|
|
1385
|
+
}
|
|
1386
|
+
static fromJsonNode(node, sourceFile, skipTrivia) {
|
|
1387
|
+
return new DiagnosticOrigin(skipTrivia(node.pos, sourceFile), node.end, sourceFile);
|
|
1388
|
+
}
|
|
1389
|
+
static fromNode(node, breadcrumbs) {
|
|
1390
|
+
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), breadcrumbs);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1378
1394
|
class Diagnostic {
|
|
1379
|
-
text;
|
|
1380
1395
|
category;
|
|
1381
|
-
origin;
|
|
1382
1396
|
code;
|
|
1383
1397
|
related;
|
|
1398
|
+
origin;
|
|
1399
|
+
text;
|
|
1384
1400
|
constructor(text, category, origin) {
|
|
1385
1401
|
this.text = text;
|
|
1386
1402
|
this.category = category;
|
|
@@ -1408,11 +1424,7 @@ class Diagnostic {
|
|
|
1408
1424
|
let origin;
|
|
1409
1425
|
const text = compiler.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
1410
1426
|
if (Diagnostic.isTsDiagnosticWithLocation(diagnostic)) {
|
|
1411
|
-
origin =
|
|
1412
|
-
end: diagnostic.start + diagnostic.length,
|
|
1413
|
-
file: diagnostic.file,
|
|
1414
|
-
start: diagnostic.start,
|
|
1415
|
-
};
|
|
1427
|
+
origin = new DiagnosticOrigin(diagnostic.start, diagnostic.start + diagnostic.length, diagnostic.file);
|
|
1416
1428
|
}
|
|
1417
1429
|
return new Diagnostic(text, category, origin).add({ code });
|
|
1418
1430
|
});
|
|
@@ -1599,6 +1611,12 @@ class OptionDiagnosticText {
|
|
|
1599
1611
|
return optionName;
|
|
1600
1612
|
}
|
|
1601
1613
|
}
|
|
1614
|
+
static testFileMatchCannotStartWith(segment) {
|
|
1615
|
+
return [
|
|
1616
|
+
`A test file match pattern cannot start with '${segment}'.`,
|
|
1617
|
+
"The test files are only collected within the 'rootPath' directory.",
|
|
1618
|
+
];
|
|
1619
|
+
}
|
|
1602
1620
|
static requiresValueType(optionName, optionBrand, optionGroup) {
|
|
1603
1621
|
optionName = OptionDiagnosticText.#optionName(optionName, optionGroup);
|
|
1604
1622
|
return `Option '${optionName}' requires a value of type ${optionBrand}.`;
|
|
@@ -1681,30 +1699,22 @@ class OptionValidator {
|
|
|
1681
1699
|
}
|
|
1682
1700
|
break;
|
|
1683
1701
|
}
|
|
1702
|
+
case "testFileMatch": {
|
|
1703
|
+
for (const segment of ["/", "../"]) {
|
|
1704
|
+
if (optionValue.startsWith(segment)) {
|
|
1705
|
+
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.testFileMatchCannotStartWith(segment), origin));
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1684
1710
|
case "watch": {
|
|
1685
1711
|
if (Environment.isCi) {
|
|
1686
1712
|
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.watchCannotBeEnabledInCiEnvironment(), origin));
|
|
1687
|
-
break;
|
|
1688
|
-
}
|
|
1689
|
-
if (!this.#isWatchSupported()) {
|
|
1690
|
-
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.watchIsNotAvailable(), origin));
|
|
1691
1713
|
}
|
|
1692
1714
|
break;
|
|
1693
1715
|
}
|
|
1694
1716
|
}
|
|
1695
1717
|
}
|
|
1696
|
-
#isWatchSupported() {
|
|
1697
|
-
let isRecursiveWatchAvailable;
|
|
1698
|
-
try {
|
|
1699
|
-
const watcher = watch(Path.resolve("./"), { persistent: false, recursive: true });
|
|
1700
|
-
watcher.close();
|
|
1701
|
-
isRecursiveWatchAvailable = true;
|
|
1702
|
-
}
|
|
1703
|
-
catch {
|
|
1704
|
-
isRecursiveWatchAvailable = false;
|
|
1705
|
-
}
|
|
1706
|
-
return isRecursiveWatchAvailable;
|
|
1707
|
-
}
|
|
1708
1718
|
}
|
|
1709
1719
|
|
|
1710
1720
|
class CommandLineOptionsWorker {
|
|
@@ -1810,7 +1820,7 @@ class CommandLineOptionsWorker {
|
|
|
1810
1820
|
}
|
|
1811
1821
|
|
|
1812
1822
|
class ConfigFileOptionsWorker {
|
|
1813
|
-
compiler;
|
|
1823
|
+
#compiler;
|
|
1814
1824
|
#configFileOptionDefinitions;
|
|
1815
1825
|
#configFileOptions;
|
|
1816
1826
|
#configFilePath;
|
|
@@ -1819,7 +1829,7 @@ class ConfigFileOptionsWorker {
|
|
|
1819
1829
|
#optionValidator;
|
|
1820
1830
|
#storeService;
|
|
1821
1831
|
constructor(compiler, configFileOptions, configFilePath, storeService, onDiagnostic) {
|
|
1822
|
-
this
|
|
1832
|
+
this.#compiler = compiler;
|
|
1823
1833
|
this.#configFileOptions = configFileOptions;
|
|
1824
1834
|
this.#configFilePath = configFilePath;
|
|
1825
1835
|
this.#storeService = storeService;
|
|
@@ -1828,31 +1838,27 @@ class ConfigFileOptionsWorker {
|
|
|
1828
1838
|
this.#optionValidator = new OptionValidator(this.#optionGroup, this.#storeService, this.#onDiagnostic);
|
|
1829
1839
|
}
|
|
1830
1840
|
#isDoubleQuotedString(node, sourceFile) {
|
|
1831
|
-
return (node.kind === this
|
|
1841
|
+
return (node.kind === this.#compiler.SyntaxKind.StringLiteral &&
|
|
1832
1842
|
sourceFile.text.slice(this.#skipTrivia(node.pos, sourceFile), node.end).startsWith('"'));
|
|
1833
1843
|
}
|
|
1834
1844
|
async parse(sourceText) {
|
|
1835
|
-
const
|
|
1836
|
-
if (
|
|
1837
|
-
for (const diagnostic of Diagnostic.fromDiagnostics(
|
|
1845
|
+
const sourceFile = this.#compiler.parseJsonText(this.#configFilePath, sourceText);
|
|
1846
|
+
if (sourceFile.parseDiagnostics.length > 0) {
|
|
1847
|
+
for (const diagnostic of Diagnostic.fromDiagnostics(sourceFile.parseDiagnostics, this.#compiler)) {
|
|
1838
1848
|
this.#onDiagnostic(diagnostic);
|
|
1839
1849
|
}
|
|
1840
1850
|
return;
|
|
1841
1851
|
}
|
|
1842
|
-
const rootExpression =
|
|
1843
|
-
if (rootExpression == null || !this
|
|
1844
|
-
const origin =
|
|
1852
|
+
const rootExpression = sourceFile.statements[0]?.expression;
|
|
1853
|
+
if (rootExpression == null || !this.#compiler.isObjectLiteralExpression(rootExpression)) {
|
|
1854
|
+
const origin = new DiagnosticOrigin(0, 0, sourceFile);
|
|
1845
1855
|
this.#onDiagnostic(Diagnostic.error("The root value of a configuration file must be an object literal.", origin));
|
|
1846
1856
|
return;
|
|
1847
1857
|
}
|
|
1848
1858
|
for (const property of rootExpression.properties) {
|
|
1849
|
-
if (this
|
|
1850
|
-
if (!this.#isDoubleQuotedString(property.name,
|
|
1851
|
-
const origin =
|
|
1852
|
-
end: property.end,
|
|
1853
|
-
file: configSourceFile,
|
|
1854
|
-
start: this.#skipTrivia(property.pos, configSourceFile),
|
|
1855
|
-
};
|
|
1859
|
+
if (this.#compiler.isPropertyAssignment(property)) {
|
|
1860
|
+
if (!this.#isDoubleQuotedString(property.name, sourceFile)) {
|
|
1861
|
+
const origin = DiagnosticOrigin.fromJsonNode(property, sourceFile, this.#skipTrivia);
|
|
1856
1862
|
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.doubleQuotesExpected(), origin));
|
|
1857
1863
|
continue;
|
|
1858
1864
|
}
|
|
@@ -1862,14 +1868,10 @@ class ConfigFileOptionsWorker {
|
|
|
1862
1868
|
}
|
|
1863
1869
|
const optionDefinition = this.#configFileOptionDefinitions.get(optionName);
|
|
1864
1870
|
if (optionDefinition) {
|
|
1865
|
-
this.#configFileOptions[optionDefinition.name] = await this.#parseOptionValue(
|
|
1871
|
+
this.#configFileOptions[optionDefinition.name] = await this.#parseOptionValue(sourceFile, property.initializer, optionDefinition);
|
|
1866
1872
|
}
|
|
1867
1873
|
else {
|
|
1868
|
-
const origin =
|
|
1869
|
-
end: property.end,
|
|
1870
|
-
file: configSourceFile,
|
|
1871
|
-
start: this.#skipTrivia(property.pos, configSourceFile),
|
|
1872
|
-
};
|
|
1874
|
+
const origin = DiagnosticOrigin.fromJsonNode(property, sourceFile, this.#skipTrivia);
|
|
1873
1875
|
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.unknownOption(optionName), origin));
|
|
1874
1876
|
}
|
|
1875
1877
|
}
|
|
@@ -1878,25 +1880,21 @@ class ConfigFileOptionsWorker {
|
|
|
1878
1880
|
}
|
|
1879
1881
|
async #parseOptionValue(sourceFile, valueExpression, optionDefinition, isListItem = false) {
|
|
1880
1882
|
switch (valueExpression.kind) {
|
|
1881
|
-
case this
|
|
1883
|
+
case this.#compiler.SyntaxKind.TrueKeyword: {
|
|
1882
1884
|
if (optionDefinition.brand === "boolean") {
|
|
1883
1885
|
return true;
|
|
1884
1886
|
}
|
|
1885
1887
|
break;
|
|
1886
1888
|
}
|
|
1887
|
-
case this
|
|
1889
|
+
case this.#compiler.SyntaxKind.FalseKeyword: {
|
|
1888
1890
|
if (optionDefinition.brand === "boolean") {
|
|
1889
1891
|
return false;
|
|
1890
1892
|
}
|
|
1891
1893
|
break;
|
|
1892
1894
|
}
|
|
1893
|
-
case this
|
|
1895
|
+
case this.#compiler.SyntaxKind.StringLiteral: {
|
|
1894
1896
|
if (!this.#isDoubleQuotedString(valueExpression, sourceFile)) {
|
|
1895
|
-
const origin =
|
|
1896
|
-
end: valueExpression.end,
|
|
1897
|
-
file: sourceFile,
|
|
1898
|
-
start: this.#skipTrivia(valueExpression.pos, sourceFile),
|
|
1899
|
-
};
|
|
1897
|
+
const origin = DiagnosticOrigin.fromJsonNode(valueExpression, sourceFile, this.#skipTrivia);
|
|
1900
1898
|
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.doubleQuotesExpected(), origin));
|
|
1901
1899
|
return;
|
|
1902
1900
|
}
|
|
@@ -1905,17 +1903,13 @@ class ConfigFileOptionsWorker {
|
|
|
1905
1903
|
if (optionDefinition.name === "rootPath") {
|
|
1906
1904
|
value = Path.resolve(Path.dirname(this.#configFilePath), value);
|
|
1907
1905
|
}
|
|
1908
|
-
const origin =
|
|
1909
|
-
end: valueExpression.end,
|
|
1910
|
-
file: sourceFile,
|
|
1911
|
-
start: this.#skipTrivia(valueExpression.pos, sourceFile),
|
|
1912
|
-
};
|
|
1906
|
+
const origin = DiagnosticOrigin.fromJsonNode(valueExpression, sourceFile, this.#skipTrivia);
|
|
1913
1907
|
await this.#optionValidator.check(optionDefinition.name, value, optionDefinition.brand, origin);
|
|
1914
1908
|
return value;
|
|
1915
1909
|
}
|
|
1916
1910
|
break;
|
|
1917
1911
|
}
|
|
1918
|
-
case this
|
|
1912
|
+
case this.#compiler.SyntaxKind.ArrayLiteralExpression: {
|
|
1919
1913
|
if (optionDefinition.brand === "list") {
|
|
1920
1914
|
const value = [];
|
|
1921
1915
|
for (const element of valueExpression.elements) {
|
|
@@ -1926,14 +1920,10 @@ class ConfigFileOptionsWorker {
|
|
|
1926
1920
|
break;
|
|
1927
1921
|
}
|
|
1928
1922
|
}
|
|
1929
|
-
const origin = {
|
|
1930
|
-
end: valueExpression.end,
|
|
1931
|
-
file: sourceFile,
|
|
1932
|
-
start: this.#skipTrivia(valueExpression.pos, sourceFile),
|
|
1933
|
-
};
|
|
1934
1923
|
const text = isListItem
|
|
1935
1924
|
? OptionDiagnosticText.expectsListItemType(optionDefinition.name, optionDefinition.brand)
|
|
1936
1925
|
: OptionDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand, this.#optionGroup);
|
|
1926
|
+
const origin = DiagnosticOrigin.fromJsonNode(valueExpression, sourceFile, this.#skipTrivia);
|
|
1937
1927
|
this.#onDiagnostic(Diagnostic.error(text, origin));
|
|
1938
1928
|
return;
|
|
1939
1929
|
}
|
|
@@ -1988,14 +1978,14 @@ const defaultOptions = {
|
|
|
1988
1978
|
testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
|
|
1989
1979
|
};
|
|
1990
1980
|
class ConfigService {
|
|
1991
|
-
compiler;
|
|
1992
1981
|
#commandLineOptions = {};
|
|
1982
|
+
#compiler;
|
|
1993
1983
|
#configFileOptions = {};
|
|
1994
1984
|
#configFilePath = Path.resolve(defaultOptions.rootPath, "./tstyche.config.json");
|
|
1995
1985
|
#pathMatch = [];
|
|
1996
1986
|
#storeService;
|
|
1997
1987
|
constructor(compiler, storeService) {
|
|
1998
|
-
this
|
|
1988
|
+
this.#compiler = compiler;
|
|
1999
1989
|
this.#storeService = storeService;
|
|
2000
1990
|
}
|
|
2001
1991
|
#onDiagnostic(diagnostic) {
|
|
@@ -2021,7 +2011,7 @@ class ConfigService {
|
|
|
2021
2011
|
const configFileText = await fs.readFile(this.#configFilePath, {
|
|
2022
2012
|
encoding: "utf8",
|
|
2023
2013
|
});
|
|
2024
|
-
const configFileWorker = new ConfigFileOptionsWorker(this
|
|
2014
|
+
const configFileWorker = new ConfigFileOptionsWorker(this.#compiler, this.#configFileOptions, this.#configFilePath, this.#storeService, this.#onDiagnostic);
|
|
2025
2015
|
await configFileWorker.parse(configFileText);
|
|
2026
2016
|
}
|
|
2027
2017
|
resolveConfig() {
|
|
@@ -2080,16 +2070,16 @@ class Timer {
|
|
|
2080
2070
|
}
|
|
2081
2071
|
|
|
2082
2072
|
class WatchService {
|
|
2083
|
-
resolvedConfig;
|
|
2084
2073
|
#changedTestFiles = new Map();
|
|
2085
2074
|
#inputService;
|
|
2075
|
+
#resolvedConfig;
|
|
2086
2076
|
#runCallback;
|
|
2087
2077
|
#selectService;
|
|
2088
2078
|
#timer = new Timer();
|
|
2089
2079
|
#watchers = [];
|
|
2090
2080
|
#watchedTestFiles;
|
|
2091
2081
|
constructor(resolvedConfig, runCallback, selectService, testFiles) {
|
|
2092
|
-
this
|
|
2082
|
+
this.#resolvedConfig = resolvedConfig;
|
|
2093
2083
|
this.#runCallback = runCallback;
|
|
2094
2084
|
this.#selectService = selectService;
|
|
2095
2085
|
this.#watchedTestFiles = new Map(testFiles.map((testFile) => [testFile.path, testFile]));
|
|
@@ -2157,39 +2147,37 @@ class WatchService {
|
|
|
2157
2147
|
this.#changedTestFiles.delete(filePath);
|
|
2158
2148
|
this.#watchedTestFiles.delete(filePath);
|
|
2159
2149
|
if (this.#watchedTestFiles.size === 0) {
|
|
2160
|
-
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.noTestFilesWereLeft(this
|
|
2150
|
+
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.noTestFilesWereLeft(this.#resolvedConfig)));
|
|
2161
2151
|
}
|
|
2162
2152
|
};
|
|
2163
|
-
this.#watchers.push(new Watcher(this
|
|
2153
|
+
this.#watchers.push(new Watcher(this.#resolvedConfig.rootPath, onChangedFile, onRemovedFile, { recursive: true }));
|
|
2164
2154
|
const onChangedConfigFile = () => {
|
|
2165
2155
|
cancellationToken?.cancel("configChange");
|
|
2166
2156
|
};
|
|
2167
|
-
this.#watchers.push(new FileWatcher(this
|
|
2157
|
+
this.#watchers.push(new FileWatcher(this.#resolvedConfig.configFilePath, onChangedConfigFile));
|
|
2168
2158
|
return Promise.all(this.#watchers.map((watcher) => watcher.watch()));
|
|
2169
2159
|
}
|
|
2170
2160
|
}
|
|
2171
2161
|
|
|
2172
2162
|
class TestMember {
|
|
2173
2163
|
brand;
|
|
2174
|
-
node;
|
|
2175
|
-
parent;
|
|
2176
|
-
flags;
|
|
2177
|
-
compiler;
|
|
2178
2164
|
diagnostics = new Set();
|
|
2165
|
+
flags;
|
|
2179
2166
|
members = [];
|
|
2180
2167
|
name = "";
|
|
2181
|
-
|
|
2168
|
+
node;
|
|
2169
|
+
parent;
|
|
2170
|
+
constructor(compiler, brand, node, parent, flags) {
|
|
2182
2171
|
this.brand = brand;
|
|
2183
2172
|
this.node = node;
|
|
2184
2173
|
this.parent = parent;
|
|
2185
2174
|
this.flags = flags;
|
|
2186
|
-
|
|
2187
|
-
if (node.arguments[0] != null && this.compiler.isStringLiteralLike(node.arguments[0])) {
|
|
2175
|
+
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
2188
2176
|
this.name = node.arguments[0].text;
|
|
2189
2177
|
}
|
|
2190
2178
|
if (node.arguments[1] != null &&
|
|
2191
|
-
|
|
2192
|
-
|
|
2179
|
+
compiler.isFunctionLike(node.arguments[1]) &&
|
|
2180
|
+
compiler.isBlock(node.arguments[1].body)) {
|
|
2193
2181
|
const blockStart = node.arguments[1].body.getStart();
|
|
2194
2182
|
const blockEnd = node.arguments[1].body.getEnd();
|
|
2195
2183
|
for (const diagnostic of parent.diagnostics) {
|
|
@@ -2216,11 +2204,7 @@ class TestMember {
|
|
|
2216
2204
|
case "describe": {
|
|
2217
2205
|
for (const member of this.members) {
|
|
2218
2206
|
if (member.brand === "expect") {
|
|
2219
|
-
diagnostics.push(Diagnostic.error(getText(member.node),
|
|
2220
|
-
end: member.node.getEnd(),
|
|
2221
|
-
file: member.node.getSourceFile(),
|
|
2222
|
-
start: member.node.getStart(),
|
|
2223
|
-
}));
|
|
2207
|
+
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
|
|
2224
2208
|
}
|
|
2225
2209
|
}
|
|
2226
2210
|
break;
|
|
@@ -2229,11 +2213,7 @@ class TestMember {
|
|
|
2229
2213
|
case "expect": {
|
|
2230
2214
|
for (const member of this.members) {
|
|
2231
2215
|
if (member.brand !== "expect") {
|
|
2232
|
-
diagnostics.push(Diagnostic.error(getText(member.node),
|
|
2233
|
-
end: member.node.getEnd(),
|
|
2234
|
-
file: member.node.getSourceFile(),
|
|
2235
|
-
start: member.node.getStart(),
|
|
2236
|
-
}));
|
|
2216
|
+
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
|
|
2237
2217
|
}
|
|
2238
2218
|
}
|
|
2239
2219
|
break;
|
|
@@ -2244,16 +2224,15 @@ class TestMember {
|
|
|
2244
2224
|
}
|
|
2245
2225
|
|
|
2246
2226
|
class Assertion extends TestMember {
|
|
2227
|
+
isNot;
|
|
2247
2228
|
matcherNode;
|
|
2248
2229
|
modifierNode;
|
|
2249
2230
|
notNode;
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2231
|
+
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
2232
|
+
super(compiler, brand, node, parent, flags);
|
|
2233
|
+
this.isNot = notNode != null;
|
|
2253
2234
|
this.matcherNode = matcherNode;
|
|
2254
2235
|
this.modifierNode = modifierNode;
|
|
2255
|
-
this.notNode = notNode;
|
|
2256
|
-
this.isNot = notNode != null;
|
|
2257
2236
|
const argStart = this.source[0]?.getStart();
|
|
2258
2237
|
const argEnd = this.source[0]?.getEnd();
|
|
2259
2238
|
for (const diagnostic of parent.diagnostics) {
|
|
@@ -2295,11 +2274,11 @@ class Assertion extends TestMember {
|
|
|
2295
2274
|
}
|
|
2296
2275
|
|
|
2297
2276
|
class IdentifierLookup {
|
|
2298
|
-
compiler;
|
|
2277
|
+
#compiler;
|
|
2299
2278
|
#identifiers;
|
|
2300
2279
|
#moduleSpecifiers = ['"tstyche"', "'tstyche'"];
|
|
2301
2280
|
constructor(compiler, identifiers) {
|
|
2302
|
-
this
|
|
2281
|
+
this.#compiler = compiler;
|
|
2303
2282
|
this.#identifiers = identifiers ?? {
|
|
2304
2283
|
namedImports: {
|
|
2305
2284
|
describe: undefined,
|
|
@@ -2321,7 +2300,7 @@ class IdentifierLookup {
|
|
|
2321
2300
|
if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
|
|
2322
2301
|
node.importClause?.isTypeOnly !== true &&
|
|
2323
2302
|
node.importClause?.namedBindings != null) {
|
|
2324
|
-
if (this
|
|
2303
|
+
if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
|
|
2325
2304
|
for (const element of node.importClause.namedBindings.elements) {
|
|
2326
2305
|
if (element.isTypeOnly) {
|
|
2327
2306
|
continue;
|
|
@@ -2338,7 +2317,7 @@ class IdentifierLookup {
|
|
|
2338
2317
|
}
|
|
2339
2318
|
}
|
|
2340
2319
|
}
|
|
2341
|
-
if (this
|
|
2320
|
+
if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
|
|
2342
2321
|
this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
|
|
2343
2322
|
}
|
|
2344
2323
|
}
|
|
@@ -2346,7 +2325,7 @@ class IdentifierLookup {
|
|
|
2346
2325
|
resolveTestMemberMeta(node) {
|
|
2347
2326
|
let flags = 0;
|
|
2348
2327
|
let expression = node.expression;
|
|
2349
|
-
while (this
|
|
2328
|
+
while (this.#compiler.isPropertyAccessExpression(expression)) {
|
|
2350
2329
|
if (expression.expression.getText() === this.#identifiers.namespace) {
|
|
2351
2330
|
break;
|
|
2352
2331
|
}
|
|
@@ -2371,7 +2350,7 @@ class IdentifierLookup {
|
|
|
2371
2350
|
expression = expression.expression;
|
|
2372
2351
|
}
|
|
2373
2352
|
let identifierName;
|
|
2374
|
-
if (this
|
|
2353
|
+
if (this.#compiler.isPropertyAccessExpression(expression) &&
|
|
2375
2354
|
expression.expression.getText() === this.#identifiers.namespace) {
|
|
2376
2355
|
identifierName = expression.name.getText();
|
|
2377
2356
|
}
|
|
@@ -2395,12 +2374,10 @@ class IdentifierLookup {
|
|
|
2395
2374
|
}
|
|
2396
2375
|
|
|
2397
2376
|
class TestTree {
|
|
2398
|
-
compiler;
|
|
2399
2377
|
diagnostics;
|
|
2400
|
-
sourceFile;
|
|
2401
2378
|
members = [];
|
|
2402
|
-
|
|
2403
|
-
|
|
2379
|
+
sourceFile;
|
|
2380
|
+
constructor(diagnostics, sourceFile) {
|
|
2404
2381
|
this.diagnostics = diagnostics;
|
|
2405
2382
|
this.sourceFile = sourceFile;
|
|
2406
2383
|
}
|
|
@@ -2413,8 +2390,8 @@ class TestTree {
|
|
|
2413
2390
|
}
|
|
2414
2391
|
|
|
2415
2392
|
class CollectService {
|
|
2416
|
-
compiler;
|
|
2417
|
-
matcherIdentifiers = [
|
|
2393
|
+
#compiler;
|
|
2394
|
+
#matcherIdentifiers = [
|
|
2418
2395
|
"toBe",
|
|
2419
2396
|
"toBeAny",
|
|
2420
2397
|
"toBeAssignable",
|
|
@@ -2436,69 +2413,69 @@ class CollectService {
|
|
|
2436
2413
|
"toMatch",
|
|
2437
2414
|
"toRaiseError",
|
|
2438
2415
|
];
|
|
2439
|
-
modifierIdentifiers = ["type"];
|
|
2440
|
-
notIdentifier = "not";
|
|
2416
|
+
#modifierIdentifiers = ["type"];
|
|
2417
|
+
#notIdentifier = "not";
|
|
2441
2418
|
constructor(compiler) {
|
|
2442
|
-
this
|
|
2419
|
+
this.#compiler = compiler;
|
|
2443
2420
|
}
|
|
2444
2421
|
#collectTestMembers(node, identifiers, parent) {
|
|
2445
|
-
if (this
|
|
2446
|
-
this
|
|
2447
|
-
this.#collectTestMembers(node, new IdentifierLookup(this
|
|
2422
|
+
if (this.#compiler.isBlock(node)) {
|
|
2423
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2424
|
+
this.#collectTestMembers(node, new IdentifierLookup(this.#compiler, identifiers.clone()), parent);
|
|
2448
2425
|
});
|
|
2449
2426
|
return;
|
|
2450
2427
|
}
|
|
2451
|
-
if (this
|
|
2428
|
+
if (this.#compiler.isCallExpression(node)) {
|
|
2452
2429
|
const meta = identifiers.resolveTestMemberMeta(node);
|
|
2453
2430
|
if (meta != null && (meta.brand === "describe" || meta.brand === "test")) {
|
|
2454
|
-
const testMember = new TestMember(meta.brand, node, parent, meta.flags);
|
|
2431
|
+
const testMember = new TestMember(this.#compiler, meta.brand, node, parent, meta.flags);
|
|
2455
2432
|
parent.members.push(testMember);
|
|
2456
|
-
this
|
|
2433
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2457
2434
|
this.#collectTestMembers(node, identifiers, testMember);
|
|
2458
2435
|
});
|
|
2459
2436
|
return;
|
|
2460
2437
|
}
|
|
2461
2438
|
if (meta != null && meta.brand === "expect") {
|
|
2462
|
-
const modifierNode = this.#getMatchingChainNode(node, this
|
|
2439
|
+
const modifierNode = this.#getMatchingChainNode(node, this.#modifierIdentifiers);
|
|
2463
2440
|
if (!modifierNode) {
|
|
2464
2441
|
return;
|
|
2465
2442
|
}
|
|
2466
|
-
const notNode = this.#getMatchingChainNode(modifierNode, [this
|
|
2467
|
-
const matcherNode = this.#getMatchingChainNode(notNode ?? modifierNode, this
|
|
2443
|
+
const notNode = this.#getMatchingChainNode(modifierNode, [this.#notIdentifier]);
|
|
2444
|
+
const matcherNode = this.#getMatchingChainNode(notNode ?? modifierNode, this.#matcherIdentifiers)?.parent;
|
|
2468
2445
|
if (matcherNode == null || !this.#isMatcherNode(matcherNode)) {
|
|
2469
2446
|
return;
|
|
2470
2447
|
}
|
|
2471
|
-
const assertion = new Assertion(meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
|
|
2448
|
+
const assertion = new Assertion(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
|
|
2472
2449
|
parent.members.push(assertion);
|
|
2473
|
-
this
|
|
2450
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2474
2451
|
this.#collectTestMembers(node, identifiers, assertion);
|
|
2475
2452
|
});
|
|
2476
2453
|
return;
|
|
2477
2454
|
}
|
|
2478
2455
|
}
|
|
2479
|
-
if (this
|
|
2456
|
+
if (this.#compiler.isImportDeclaration(node)) {
|
|
2480
2457
|
identifiers.handleImportDeclaration(node);
|
|
2481
2458
|
return;
|
|
2482
2459
|
}
|
|
2483
|
-
if (this
|
|
2484
|
-
if (this
|
|
2485
|
-
this
|
|
2460
|
+
if (this.#compiler.isVariableDeclaration(node)) ;
|
|
2461
|
+
if (this.#compiler.isBinaryExpression(node)) ;
|
|
2462
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2486
2463
|
this.#collectTestMembers(node, identifiers, parent);
|
|
2487
2464
|
});
|
|
2488
2465
|
}
|
|
2489
2466
|
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
2490
|
-
const testTree = new TestTree(
|
|
2491
|
-
this.#collectTestMembers(sourceFile, new IdentifierLookup(this
|
|
2467
|
+
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
2468
|
+
this.#collectTestMembers(sourceFile, new IdentifierLookup(this.#compiler), testTree);
|
|
2492
2469
|
return testTree;
|
|
2493
2470
|
}
|
|
2494
2471
|
#getMatchingChainNode({ parent }, name) {
|
|
2495
|
-
if (this
|
|
2472
|
+
if (this.#compiler.isPropertyAccessExpression(parent) && name.includes(parent.name.getText())) {
|
|
2496
2473
|
return parent;
|
|
2497
2474
|
}
|
|
2498
2475
|
return;
|
|
2499
2476
|
}
|
|
2500
2477
|
#isMatcherNode(node) {
|
|
2501
|
-
return this
|
|
2478
|
+
return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
|
|
2502
2479
|
}
|
|
2503
2480
|
}
|
|
2504
2481
|
|
|
@@ -2518,8 +2495,8 @@ var TestMemberFlags;
|
|
|
2518
2495
|
})(TestMemberFlags || (TestMemberFlags = {}));
|
|
2519
2496
|
|
|
2520
2497
|
class PrimitiveTypeMatcher {
|
|
2521
|
-
typeChecker;
|
|
2522
2498
|
#targetTypeFlag;
|
|
2499
|
+
typeChecker;
|
|
2523
2500
|
constructor(typeChecker, targetTypeFlag) {
|
|
2524
2501
|
this.typeChecker = typeChecker;
|
|
2525
2502
|
this.#targetTypeFlag = targetTypeFlag;
|
|
@@ -2538,8 +2515,8 @@ class PrimitiveTypeMatcher {
|
|
|
2538
2515
|
}
|
|
2539
2516
|
|
|
2540
2517
|
class RelationMatcherBase {
|
|
2541
|
-
typeChecker;
|
|
2542
2518
|
relationExplanationVerb = "is";
|
|
2519
|
+
typeChecker;
|
|
2543
2520
|
constructor(typeChecker) {
|
|
2544
2521
|
this.typeChecker = typeChecker;
|
|
2545
2522
|
}
|
|
@@ -2729,8 +2706,8 @@ class ToRaiseError {
|
|
|
2729
2706
|
}
|
|
2730
2707
|
|
|
2731
2708
|
class Expect {
|
|
2732
|
-
compiler;
|
|
2733
|
-
typeChecker;
|
|
2709
|
+
#compiler;
|
|
2710
|
+
#typeChecker;
|
|
2734
2711
|
toBe;
|
|
2735
2712
|
toBeAny;
|
|
2736
2713
|
toBeAssignable;
|
|
@@ -2752,36 +2729,36 @@ class Expect {
|
|
|
2752
2729
|
toMatch;
|
|
2753
2730
|
toRaiseError;
|
|
2754
2731
|
constructor(compiler, typeChecker) {
|
|
2755
|
-
this
|
|
2756
|
-
this
|
|
2757
|
-
this.toBe = new ToBe(
|
|
2758
|
-
this.toBeAny = new PrimitiveTypeMatcher(
|
|
2759
|
-
this.toBeAssignable = new ToBeAssignableWith(
|
|
2760
|
-
this.toBeAssignableTo = new ToBeAssignableTo(
|
|
2761
|
-
this.toBeAssignableWith = new ToBeAssignableWith(
|
|
2762
|
-
this.toBeBigInt = new PrimitiveTypeMatcher(
|
|
2763
|
-
this.toBeBoolean = new PrimitiveTypeMatcher(
|
|
2764
|
-
this.toBeNever = new PrimitiveTypeMatcher(
|
|
2765
|
-
this.toBeNull = new PrimitiveTypeMatcher(
|
|
2766
|
-
this.toBeNumber = new PrimitiveTypeMatcher(
|
|
2767
|
-
this.toBeString = new PrimitiveTypeMatcher(
|
|
2768
|
-
this.toBeSymbol = new PrimitiveTypeMatcher(
|
|
2769
|
-
this.toBeUndefined = new PrimitiveTypeMatcher(
|
|
2770
|
-
this.toBeUniqueSymbol = new PrimitiveTypeMatcher(
|
|
2771
|
-
this.toBeUnknown = new PrimitiveTypeMatcher(
|
|
2772
|
-
this.toBeVoid = new PrimitiveTypeMatcher(
|
|
2773
|
-
this.toEqual = new ToBe(
|
|
2774
|
-
this.toHaveProperty = new ToHaveProperty(
|
|
2775
|
-
this.toMatch = new ToMatch(
|
|
2776
|
-
this.toRaiseError = new ToRaiseError(
|
|
2732
|
+
this.#compiler = compiler;
|
|
2733
|
+
this.#typeChecker = typeChecker;
|
|
2734
|
+
this.toBe = new ToBe(typeChecker);
|
|
2735
|
+
this.toBeAny = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Any);
|
|
2736
|
+
this.toBeAssignable = new ToBeAssignableWith(typeChecker);
|
|
2737
|
+
this.toBeAssignableTo = new ToBeAssignableTo(typeChecker);
|
|
2738
|
+
this.toBeAssignableWith = new ToBeAssignableWith(typeChecker);
|
|
2739
|
+
this.toBeBigInt = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.BigInt);
|
|
2740
|
+
this.toBeBoolean = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Boolean);
|
|
2741
|
+
this.toBeNever = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Never);
|
|
2742
|
+
this.toBeNull = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Null);
|
|
2743
|
+
this.toBeNumber = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Number);
|
|
2744
|
+
this.toBeString = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.String);
|
|
2745
|
+
this.toBeSymbol = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.ESSymbol);
|
|
2746
|
+
this.toBeUndefined = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Undefined);
|
|
2747
|
+
this.toBeUniqueSymbol = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.UniqueESSymbol);
|
|
2748
|
+
this.toBeUnknown = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Unknown);
|
|
2749
|
+
this.toBeVoid = new PrimitiveTypeMatcher(typeChecker, compiler.TypeFlags.Void);
|
|
2750
|
+
this.toEqual = new ToBe(typeChecker);
|
|
2751
|
+
this.toHaveProperty = new ToHaveProperty(compiler, typeChecker);
|
|
2752
|
+
this.toMatch = new ToMatch(typeChecker);
|
|
2753
|
+
this.toRaiseError = new ToRaiseError(compiler, typeChecker);
|
|
2777
2754
|
}
|
|
2778
2755
|
static assertTypeChecker(typeChecker) {
|
|
2779
2756
|
return "isTypeRelatedTo" in typeChecker && "relation" in typeChecker;
|
|
2780
2757
|
}
|
|
2781
2758
|
#getType(node) {
|
|
2782
|
-
return this
|
|
2783
|
-
? this
|
|
2784
|
-
: this
|
|
2759
|
+
return this.#compiler.isExpression(node)
|
|
2760
|
+
? this.#typeChecker.getTypeAtLocation(node)
|
|
2761
|
+
: this.#typeChecker.getTypeFromTypeNode(node);
|
|
2785
2762
|
}
|
|
2786
2763
|
#getTypes(nodes) {
|
|
2787
2764
|
return nodes.map((node) => this.#getType(node));
|
|
@@ -2790,10 +2767,10 @@ class Expect {
|
|
|
2790
2767
|
return types.every((type) => this.#isStringOrNumberLiteralType(type));
|
|
2791
2768
|
}
|
|
2792
2769
|
#isStringOrNumberLiteralType(type) {
|
|
2793
|
-
return Boolean(type.flags & this
|
|
2770
|
+
return Boolean(type.flags & this.#compiler.TypeFlags.StringOrNumberLiteral);
|
|
2794
2771
|
}
|
|
2795
2772
|
#isUniqueSymbolType(type) {
|
|
2796
|
-
return Boolean(type.flags & this
|
|
2773
|
+
return Boolean(type.flags & this.#compiler.TypeFlags.UniqueESSymbol);
|
|
2797
2774
|
}
|
|
2798
2775
|
match(assertion, expectResult) {
|
|
2799
2776
|
const matcherNameText = assertion.matcherName.getText();
|
|
@@ -2839,9 +2816,9 @@ class Expect {
|
|
|
2839
2816
|
return;
|
|
2840
2817
|
}
|
|
2841
2818
|
const sourceType = this.#getType(assertion.source[0]);
|
|
2842
|
-
const nonPrimitiveType = { flags: this
|
|
2843
|
-
if (sourceType.flags & (this
|
|
2844
|
-
!this
|
|
2819
|
+
const nonPrimitiveType = { flags: this.#compiler.TypeFlags.NonPrimitive };
|
|
2820
|
+
if (sourceType.flags & (this.#compiler.TypeFlags.Any | this.#compiler.TypeFlags.Never) ||
|
|
2821
|
+
!this.#typeChecker.isTypeRelatedTo(sourceType, nonPrimitiveType, this.#typeChecker.relation.assignable)) {
|
|
2845
2822
|
this.#onSourceArgumentMustBeObjectType(assertion.source[0], expectResult);
|
|
2846
2823
|
return;
|
|
2847
2824
|
}
|
|
@@ -2880,29 +2857,17 @@ class Expect {
|
|
|
2880
2857
|
`'.${matcherNameText}()' is deprecated and will be removed in TSTyche 3.`,
|
|
2881
2858
|
"To learn more, visit https://tstyche.org/release-notes/tstyche-2",
|
|
2882
2859
|
];
|
|
2883
|
-
const origin =
|
|
2884
|
-
end: assertion.matcherName.getEnd(),
|
|
2885
|
-
file: assertion.matcherName.getSourceFile(),
|
|
2886
|
-
start: assertion.matcherName.getStart(),
|
|
2887
|
-
};
|
|
2860
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
2888
2861
|
EventEmitter.dispatch(["deprecation:info", { diagnostics: [Diagnostic.warning(text, origin)] }]);
|
|
2889
2862
|
}
|
|
2890
2863
|
#onKeyArgumentMustBeOfType(node, expectResult) {
|
|
2891
|
-
const receivedTypeText = this
|
|
2864
|
+
const receivedTypeText = this.#typeChecker.typeToString(this.#getType(node));
|
|
2892
2865
|
const text = `An argument for 'key' must be of type 'string | number | symbol', received: '${receivedTypeText}'.`;
|
|
2893
|
-
const origin =
|
|
2894
|
-
end: node.getEnd(),
|
|
2895
|
-
file: node.getSourceFile(),
|
|
2896
|
-
start: node.getStart(),
|
|
2897
|
-
};
|
|
2866
|
+
const origin = DiagnosticOrigin.fromNode(node);
|
|
2898
2867
|
EventEmitter.dispatch(["expect:error", { diagnostics: [Diagnostic.error(text, origin)], result: expectResult }]);
|
|
2899
2868
|
}
|
|
2900
2869
|
#onKeyArgumentMustBeProvided(assertion, expectResult) {
|
|
2901
|
-
const origin =
|
|
2902
|
-
end: assertion.matcherName.getEnd(),
|
|
2903
|
-
file: assertion.matcherName.getSourceFile(),
|
|
2904
|
-
start: assertion.matcherName.getStart(),
|
|
2905
|
-
};
|
|
2870
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
2906
2871
|
EventEmitter.dispatch([
|
|
2907
2872
|
"expect:error",
|
|
2908
2873
|
{
|
|
@@ -2913,11 +2878,7 @@ class Expect {
|
|
|
2913
2878
|
}
|
|
2914
2879
|
#onNotSupportedMatcherName(assertion, expectResult) {
|
|
2915
2880
|
const matcherNameText = assertion.matcherName.getText();
|
|
2916
|
-
const origin =
|
|
2917
|
-
end: assertion.matcherName.getEnd(),
|
|
2918
|
-
file: assertion.matcherName.getSourceFile(),
|
|
2919
|
-
start: assertion.matcherName.getStart(),
|
|
2920
|
-
};
|
|
2881
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
2921
2882
|
EventEmitter.dispatch([
|
|
2922
2883
|
"expect:error",
|
|
2923
2884
|
{
|
|
@@ -2927,22 +2888,14 @@ class Expect {
|
|
|
2927
2888
|
]);
|
|
2928
2889
|
}
|
|
2929
2890
|
#onSourceArgumentMustBeObjectType(node, expectResult) {
|
|
2930
|
-
const sourceText = this
|
|
2931
|
-
const receivedTypeText = this
|
|
2891
|
+
const sourceText = this.#compiler.isTypeNode(node) ? "A type argument for 'Source'" : "An argument for 'source'";
|
|
2892
|
+
const receivedTypeText = this.#typeChecker.typeToString(this.#getType(node));
|
|
2932
2893
|
const text = `${sourceText} must be of an object type, received: '${receivedTypeText}'.`;
|
|
2933
|
-
const origin =
|
|
2934
|
-
end: node.getEnd(),
|
|
2935
|
-
file: node.getSourceFile(),
|
|
2936
|
-
start: node.getStart(),
|
|
2937
|
-
};
|
|
2894
|
+
const origin = DiagnosticOrigin.fromNode(node);
|
|
2938
2895
|
EventEmitter.dispatch(["expect:error", { diagnostics: [Diagnostic.error(text, origin)], result: expectResult }]);
|
|
2939
2896
|
}
|
|
2940
2897
|
#onSourceArgumentMustBeProvided(assertion, expectResult) {
|
|
2941
|
-
const origin =
|
|
2942
|
-
end: assertion.node.getEnd(),
|
|
2943
|
-
file: assertion.node.getSourceFile(),
|
|
2944
|
-
start: assertion.node.getStart(),
|
|
2945
|
-
};
|
|
2898
|
+
const origin = DiagnosticOrigin.fromNode(assertion.node);
|
|
2946
2899
|
EventEmitter.dispatch([
|
|
2947
2900
|
"expect:error",
|
|
2948
2901
|
{
|
|
@@ -2954,11 +2907,7 @@ class Expect {
|
|
|
2954
2907
|
]);
|
|
2955
2908
|
}
|
|
2956
2909
|
#onTargetArgumentMustBeProvided(assertion, expectResult) {
|
|
2957
|
-
const origin =
|
|
2958
|
-
end: assertion.matcherName.getEnd(),
|
|
2959
|
-
file: assertion.matcherName.getSourceFile(),
|
|
2960
|
-
start: assertion.matcherName.getStart(),
|
|
2961
|
-
};
|
|
2910
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
2962
2911
|
EventEmitter.dispatch([
|
|
2963
2912
|
"expect:error",
|
|
2964
2913
|
{
|
|
@@ -2974,12 +2923,8 @@ class Expect {
|
|
|
2974
2923
|
for (const node of nodes) {
|
|
2975
2924
|
const receivedType = this.#getType(node);
|
|
2976
2925
|
if (!this.#isStringOrNumberLiteralType(receivedType)) {
|
|
2977
|
-
const receivedTypeText = this
|
|
2978
|
-
const origin =
|
|
2979
|
-
end: node.getEnd(),
|
|
2980
|
-
file: node.getSourceFile(),
|
|
2981
|
-
start: node.getStart(),
|
|
2982
|
-
};
|
|
2926
|
+
const receivedTypeText = this.#typeChecker.typeToString(this.#getType(node));
|
|
2927
|
+
const origin = DiagnosticOrigin.fromNode(node);
|
|
2983
2928
|
diagnostics.push(Diagnostic.error(`An argument for 'target' must be of type 'string | number', received: '${receivedTypeText}'.`, origin));
|
|
2984
2929
|
}
|
|
2985
2930
|
}
|
|
@@ -3019,10 +2964,10 @@ class Version {
|
|
|
3019
2964
|
}
|
|
3020
2965
|
|
|
3021
2966
|
class ProjectService {
|
|
3022
|
-
compiler;
|
|
2967
|
+
#compiler;
|
|
3023
2968
|
#service;
|
|
3024
2969
|
constructor(compiler) {
|
|
3025
|
-
this
|
|
2970
|
+
this.#compiler = compiler;
|
|
3026
2971
|
function doNothing() {
|
|
3027
2972
|
}
|
|
3028
2973
|
function returnFalse() {
|
|
@@ -3044,7 +2989,7 @@ class ProjectService {
|
|
|
3044
2989
|
startGroup: doNothing,
|
|
3045
2990
|
};
|
|
3046
2991
|
const host = {
|
|
3047
|
-
...this
|
|
2992
|
+
...this.#compiler.sys,
|
|
3048
2993
|
clearImmediate,
|
|
3049
2994
|
clearTimeout,
|
|
3050
2995
|
setImmediate,
|
|
@@ -3052,9 +2997,9 @@ class ProjectService {
|
|
|
3052
2997
|
watchDirectory: () => noopWatcher,
|
|
3053
2998
|
watchFile: () => noopWatcher,
|
|
3054
2999
|
};
|
|
3055
|
-
this.#service = new this
|
|
3000
|
+
this.#service = new this.#compiler.server.ProjectService({
|
|
3056
3001
|
allowLocalPluginLoads: true,
|
|
3057
|
-
cancellationToken: this
|
|
3002
|
+
cancellationToken: this.#compiler.server.nullCancellationToken,
|
|
3058
3003
|
host,
|
|
3059
3004
|
logger: noopLogger,
|
|
3060
3005
|
session: undefined,
|
|
@@ -3079,17 +3024,17 @@ class ProjectService {
|
|
|
3079
3024
|
strictNullChecks: true,
|
|
3080
3025
|
target: "esnext",
|
|
3081
3026
|
};
|
|
3082
|
-
if (Version.isSatisfiedWith(this
|
|
3027
|
+
if (Version.isSatisfiedWith(this.#compiler.version, "5.4")) {
|
|
3083
3028
|
defaultCompilerOptions.module = "preserve";
|
|
3084
3029
|
}
|
|
3085
|
-
if (Version.isSatisfiedWith(this
|
|
3030
|
+
if (Version.isSatisfiedWith(this.#compiler.version, "5.0")) {
|
|
3086
3031
|
defaultCompilerOptions["allowImportingTsExtensions"] = true;
|
|
3087
3032
|
defaultCompilerOptions.moduleResolution = "bundler";
|
|
3088
3033
|
}
|
|
3089
3034
|
return defaultCompilerOptions;
|
|
3090
3035
|
}
|
|
3091
3036
|
getDefaultProject(filePath) {
|
|
3092
|
-
return this.#service.getDefaultProjectForFile(this
|
|
3037
|
+
return this.#service.getDefaultProjectForFile(this.#compiler.server.toNormalizedPath(filePath), true);
|
|
3093
3038
|
}
|
|
3094
3039
|
getLanguageService(filePath) {
|
|
3095
3040
|
const project = this.getDefaultProject(filePath);
|
|
@@ -3102,30 +3047,30 @@ class ProjectService {
|
|
|
3102
3047
|
const { configFileErrors, configFileName } = this.#service.openClientFile(filePath, sourceText, undefined, projectRootPath);
|
|
3103
3048
|
EventEmitter.dispatch([
|
|
3104
3049
|
"project:info",
|
|
3105
|
-
{ compilerVersion: this
|
|
3050
|
+
{ compilerVersion: this.#compiler.version, projectConfigFilePath: configFileName },
|
|
3106
3051
|
]);
|
|
3107
3052
|
if (configFileErrors && configFileErrors.length > 0) {
|
|
3108
3053
|
EventEmitter.dispatch([
|
|
3109
3054
|
"project:error",
|
|
3110
|
-
{ diagnostics: Diagnostic.fromDiagnostics(configFileErrors, this
|
|
3055
|
+
{ diagnostics: Diagnostic.fromDiagnostics(configFileErrors, this.#compiler) },
|
|
3111
3056
|
]);
|
|
3112
3057
|
}
|
|
3113
3058
|
}
|
|
3114
3059
|
}
|
|
3115
3060
|
|
|
3116
3061
|
class TestTreeWorker {
|
|
3117
|
-
|
|
3118
|
-
compiler;
|
|
3062
|
+
#compiler;
|
|
3119
3063
|
#cancellationToken;
|
|
3120
3064
|
#expect;
|
|
3121
3065
|
#fileResult;
|
|
3122
3066
|
#hasOnly;
|
|
3123
3067
|
#position;
|
|
3068
|
+
#resolvedConfig;
|
|
3124
3069
|
constructor(resolvedConfig, compiler, expect, options) {
|
|
3125
|
-
this
|
|
3126
|
-
this
|
|
3127
|
-
this.#cancellationToken = options.cancellationToken;
|
|
3070
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3071
|
+
this.#compiler = compiler;
|
|
3128
3072
|
this.#expect = expect;
|
|
3073
|
+
this.#cancellationToken = options.cancellationToken;
|
|
3129
3074
|
this.#fileResult = options.fileResult;
|
|
3130
3075
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
3131
3076
|
this.#position = options.position;
|
|
@@ -3135,11 +3080,11 @@ class TestTreeWorker {
|
|
|
3135
3080
|
mode |= 1;
|
|
3136
3081
|
}
|
|
3137
3082
|
if (member.flags & 2 ||
|
|
3138
|
-
(this
|
|
3083
|
+
(this.#resolvedConfig.only != null && member.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
|
|
3139
3084
|
mode |= 2;
|
|
3140
3085
|
}
|
|
3141
3086
|
if (member.flags & 4 ||
|
|
3142
|
-
(this
|
|
3087
|
+
(this.#resolvedConfig.skip != null && member.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
|
|
3143
3088
|
mode |= 4;
|
|
3144
3089
|
}
|
|
3145
3090
|
if (member.flags & 8) {
|
|
@@ -3196,7 +3141,7 @@ class TestTreeWorker {
|
|
|
3196
3141
|
EventEmitter.dispatch([
|
|
3197
3142
|
"expect:error",
|
|
3198
3143
|
{
|
|
3199
|
-
diagnostics: Diagnostic.fromDiagnostics([...assertion.diagnostics], this
|
|
3144
|
+
diagnostics: Diagnostic.fromDiagnostics([...assertion.diagnostics], this.#compiler),
|
|
3200
3145
|
result: expectResult,
|
|
3201
3146
|
},
|
|
3202
3147
|
]);
|
|
@@ -3209,11 +3154,7 @@ class TestTreeWorker {
|
|
|
3209
3154
|
if (assertion.isNot ? !matchResult.isMatch : matchResult.isMatch) {
|
|
3210
3155
|
if (runMode & 1) {
|
|
3211
3156
|
const text = ["The assertion was supposed to fail, but it passed.", "Consider removing the '.fail' flag."];
|
|
3212
|
-
const origin =
|
|
3213
|
-
end: assertion.node.getEnd(),
|
|
3214
|
-
file: assertion.node.getSourceFile(),
|
|
3215
|
-
start: assertion.node.getStart(),
|
|
3216
|
-
};
|
|
3157
|
+
const origin = DiagnosticOrigin.fromNode(assertion.node);
|
|
3217
3158
|
EventEmitter.dispatch([
|
|
3218
3159
|
"expect:error",
|
|
3219
3160
|
{ diagnostics: [Diagnostic.error(text, origin)], result: expectResult },
|
|
@@ -3227,19 +3168,13 @@ class TestTreeWorker {
|
|
|
3227
3168
|
EventEmitter.dispatch(["expect:pass", { result: expectResult }]);
|
|
3228
3169
|
}
|
|
3229
3170
|
else {
|
|
3230
|
-
const origin =
|
|
3231
|
-
|
|
3232
|
-
end: assertion.matcherName.getEnd(),
|
|
3233
|
-
file: assertion.matcherName.getSourceFile(),
|
|
3234
|
-
start: assertion.matcherName.getStart(),
|
|
3235
|
-
};
|
|
3236
|
-
const diagnostics = [];
|
|
3237
|
-
for (const diagnostic of matchResult.explain()) {
|
|
3171
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherName, assertion.ancestorNames);
|
|
3172
|
+
const diagnostics = matchResult.explain().map((diagnostic) => {
|
|
3238
3173
|
if (diagnostic.origin == null) {
|
|
3239
|
-
diagnostic.add({ origin });
|
|
3174
|
+
return diagnostic.add({ origin });
|
|
3240
3175
|
}
|
|
3241
|
-
|
|
3242
|
-
}
|
|
3176
|
+
return diagnostic;
|
|
3177
|
+
});
|
|
3243
3178
|
EventEmitter.dispatch(["expect:fail", { diagnostics, result: expectResult }]);
|
|
3244
3179
|
}
|
|
3245
3180
|
}
|
|
@@ -3252,7 +3187,7 @@ class TestTreeWorker {
|
|
|
3252
3187
|
EventEmitter.dispatch([
|
|
3253
3188
|
"file:error",
|
|
3254
3189
|
{
|
|
3255
|
-
diagnostics: Diagnostic.fromDiagnostics([...describe.diagnostics], this
|
|
3190
|
+
diagnostics: Diagnostic.fromDiagnostics([...describe.diagnostics], this.#compiler),
|
|
3256
3191
|
result: this.#fileResult,
|
|
3257
3192
|
},
|
|
3258
3193
|
]);
|
|
@@ -3274,7 +3209,7 @@ class TestTreeWorker {
|
|
|
3274
3209
|
EventEmitter.dispatch([
|
|
3275
3210
|
"test:error",
|
|
3276
3211
|
{
|
|
3277
|
-
diagnostics: Diagnostic.fromDiagnostics([...test.diagnostics], this
|
|
3212
|
+
diagnostics: Diagnostic.fromDiagnostics([...test.diagnostics], this.#compiler),
|
|
3278
3213
|
result: testResult,
|
|
3279
3214
|
},
|
|
3280
3215
|
]);
|
|
@@ -3295,13 +3230,13 @@ class TestTreeWorker {
|
|
|
3295
3230
|
}
|
|
3296
3231
|
|
|
3297
3232
|
class TestFileRunner {
|
|
3298
|
-
|
|
3299
|
-
compiler;
|
|
3233
|
+
#compiler;
|
|
3300
3234
|
#collectService;
|
|
3235
|
+
#resolvedConfig;
|
|
3301
3236
|
#projectService;
|
|
3302
3237
|
constructor(resolvedConfig, compiler) {
|
|
3303
|
-
this
|
|
3304
|
-
this
|
|
3238
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3239
|
+
this.#compiler = compiler;
|
|
3305
3240
|
this.#collectService = new CollectService(compiler);
|
|
3306
3241
|
this.#projectService = new ProjectService(compiler);
|
|
3307
3242
|
}
|
|
@@ -3309,7 +3244,7 @@ class TestFileRunner {
|
|
|
3309
3244
|
if (cancellationToken?.isCancellationRequested === true) {
|
|
3310
3245
|
return;
|
|
3311
3246
|
}
|
|
3312
|
-
this.#projectService.openFile(testFile.path, undefined, this
|
|
3247
|
+
this.#projectService.openFile(testFile.path, undefined, this.#resolvedConfig.rootPath);
|
|
3313
3248
|
const fileResult = new FileResult(testFile);
|
|
3314
3249
|
EventEmitter.dispatch(["file:start", { result: fileResult }]);
|
|
3315
3250
|
this.#runFile(testFile, fileResult, cancellationToken);
|
|
@@ -3326,7 +3261,7 @@ class TestFileRunner {
|
|
|
3326
3261
|
EventEmitter.dispatch([
|
|
3327
3262
|
"file:error",
|
|
3328
3263
|
{
|
|
3329
|
-
diagnostics: Diagnostic.fromDiagnostics(syntacticDiagnostics, this
|
|
3264
|
+
diagnostics: Diagnostic.fromDiagnostics(syntacticDiagnostics, this.#compiler),
|
|
3330
3265
|
result: fileResult,
|
|
3331
3266
|
},
|
|
3332
3267
|
]);
|
|
@@ -3346,7 +3281,7 @@ class TestFileRunner {
|
|
|
3346
3281
|
EventEmitter.dispatch([
|
|
3347
3282
|
"file:error",
|
|
3348
3283
|
{
|
|
3349
|
-
diagnostics: Diagnostic.fromDiagnostics([...testTree.diagnostics], this
|
|
3284
|
+
diagnostics: Diagnostic.fromDiagnostics([...testTree.diagnostics], this.#compiler),
|
|
3350
3285
|
result: fileResult,
|
|
3351
3286
|
},
|
|
3352
3287
|
]);
|
|
@@ -3358,8 +3293,8 @@ class TestFileRunner {
|
|
|
3358
3293
|
EventEmitter.dispatch(["file:error", { diagnostics: [Diagnostic.error(text)], result: fileResult }]);
|
|
3359
3294
|
return;
|
|
3360
3295
|
}
|
|
3361
|
-
const expect = new Expect(this
|
|
3362
|
-
const testTreeWorker = new TestTreeWorker(this
|
|
3296
|
+
const expect = new Expect(this.#compiler, typeChecker);
|
|
3297
|
+
const testTreeWorker = new TestTreeWorker(this.#resolvedConfig, this.#compiler, expect, {
|
|
3363
3298
|
cancellationToken,
|
|
3364
3299
|
fileResult,
|
|
3365
3300
|
hasOnly: testTree.hasOnly,
|
|
@@ -3370,12 +3305,12 @@ class TestFileRunner {
|
|
|
3370
3305
|
}
|
|
3371
3306
|
|
|
3372
3307
|
class TaskRunner {
|
|
3373
|
-
resolvedConfig;
|
|
3374
3308
|
#eventEmitter = new EventEmitter();
|
|
3309
|
+
#resolvedConfig;
|
|
3375
3310
|
#selectService;
|
|
3376
3311
|
#storeService;
|
|
3377
3312
|
constructor(resolvedConfig, selectService, storeService) {
|
|
3378
|
-
this
|
|
3313
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3379
3314
|
this.#selectService = selectService;
|
|
3380
3315
|
this.#storeService = storeService;
|
|
3381
3316
|
this.#eventEmitter.addHandler(new ResultHandler());
|
|
@@ -3385,11 +3320,11 @@ class TaskRunner {
|
|
|
3385
3320
|
}
|
|
3386
3321
|
async run(testFiles, cancellationToken = new CancellationToken()) {
|
|
3387
3322
|
let cancellationHandler;
|
|
3388
|
-
if (this
|
|
3323
|
+
if (this.#resolvedConfig.failFast) {
|
|
3389
3324
|
cancellationHandler = new CancellationHandler(cancellationToken, "failFast");
|
|
3390
3325
|
this.#eventEmitter.addHandler(cancellationHandler);
|
|
3391
3326
|
}
|
|
3392
|
-
if (this
|
|
3327
|
+
if (this.#resolvedConfig.watch === true) {
|
|
3393
3328
|
await this.#watch(testFiles, cancellationToken);
|
|
3394
3329
|
}
|
|
3395
3330
|
else {
|
|
@@ -3400,14 +3335,14 @@ class TaskRunner {
|
|
|
3400
3335
|
}
|
|
3401
3336
|
}
|
|
3402
3337
|
async #run(testFiles, cancellationToken) {
|
|
3403
|
-
const result = new Result(this
|
|
3338
|
+
const result = new Result(this.#resolvedConfig, testFiles);
|
|
3404
3339
|
EventEmitter.dispatch(["run:start", { result }]);
|
|
3405
|
-
for (const versionTag of this
|
|
3340
|
+
for (const versionTag of this.#resolvedConfig.target) {
|
|
3406
3341
|
const targetResult = new TargetResult(versionTag, testFiles);
|
|
3407
3342
|
EventEmitter.dispatch(["target:start", { result: targetResult }]);
|
|
3408
3343
|
const compiler = await this.#storeService.load(versionTag, cancellationToken);
|
|
3409
3344
|
if (compiler) {
|
|
3410
|
-
const testFileRunner = new TestFileRunner(this
|
|
3345
|
+
const testFileRunner = new TestFileRunner(this.#resolvedConfig, compiler);
|
|
3411
3346
|
for (const testFile of testFiles) {
|
|
3412
3347
|
testFileRunner.run(testFile, cancellationToken);
|
|
3413
3348
|
}
|
|
@@ -3424,7 +3359,7 @@ class TaskRunner {
|
|
|
3424
3359
|
const runCallback = async (testFiles) => {
|
|
3425
3360
|
await this.#run(testFiles, cancellationToken);
|
|
3426
3361
|
};
|
|
3427
|
-
const watchModeManager = new WatchService(this
|
|
3362
|
+
const watchModeManager = new WatchService(this.#resolvedConfig, runCallback, this.#selectService, testFiles);
|
|
3428
3363
|
cancellationToken?.onCancellationRequested((reason) => {
|
|
3429
3364
|
if (reason !== "failFast") {
|
|
3430
3365
|
watchModeManager.close();
|
|
@@ -3435,26 +3370,26 @@ class TaskRunner {
|
|
|
3435
3370
|
}
|
|
3436
3371
|
|
|
3437
3372
|
class TSTyche {
|
|
3438
|
-
resolvedConfig;
|
|
3439
3373
|
#eventEmitter = new EventEmitter();
|
|
3440
3374
|
#outputService;
|
|
3375
|
+
#resolvedConfig;
|
|
3441
3376
|
#selectService;
|
|
3442
3377
|
#storeService;
|
|
3443
3378
|
#taskRunner;
|
|
3444
|
-
static version = "2.0.0-
|
|
3379
|
+
static version = "2.0.0-rc.0";
|
|
3445
3380
|
constructor(resolvedConfig, outputService, selectService, storeService) {
|
|
3446
|
-
this
|
|
3381
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3447
3382
|
this.#outputService = outputService;
|
|
3448
3383
|
this.#selectService = selectService;
|
|
3449
3384
|
this.#storeService = storeService;
|
|
3450
|
-
this.#taskRunner = new TaskRunner(this
|
|
3385
|
+
this.#taskRunner = new TaskRunner(this.#resolvedConfig, this.#selectService, this.#storeService);
|
|
3451
3386
|
}
|
|
3452
3387
|
close() {
|
|
3453
3388
|
this.#taskRunner.close();
|
|
3454
3389
|
}
|
|
3455
3390
|
async run(testFiles, cancellationToken = new CancellationToken()) {
|
|
3456
|
-
this.#eventEmitter.addHandler(new RuntimeReporter(this
|
|
3457
|
-
if (this
|
|
3391
|
+
this.#eventEmitter.addHandler(new RuntimeReporter(this.#resolvedConfig, this.#outputService));
|
|
3392
|
+
if (this.#resolvedConfig.watch === true) {
|
|
3458
3393
|
this.#eventEmitter.addHandler(new WatchReporter(this.#outputService));
|
|
3459
3394
|
}
|
|
3460
3395
|
else {
|
|
@@ -3472,6 +3407,9 @@ class GlobPattern {
|
|
|
3472
3407
|
let resultPattern = "\\.";
|
|
3473
3408
|
let optionalSegmentCount = 0;
|
|
3474
3409
|
for (const segment of segments) {
|
|
3410
|
+
if (segment === ".") {
|
|
3411
|
+
continue;
|
|
3412
|
+
}
|
|
3475
3413
|
if (segment === "**") {
|
|
3476
3414
|
resultPattern += "(\\/(?!(node_modules)(\\/|$))[^./][^/]*)*?";
|
|
3477
3415
|
continue;
|
|
@@ -3507,11 +3445,11 @@ class GlobPattern {
|
|
|
3507
3445
|
}
|
|
3508
3446
|
|
|
3509
3447
|
class SelectService {
|
|
3510
|
-
resolvedConfig;
|
|
3511
3448
|
#includeDirectoryRegex;
|
|
3512
3449
|
#includeFileRegex;
|
|
3450
|
+
#resolvedConfig;
|
|
3513
3451
|
constructor(resolvedConfig) {
|
|
3514
|
-
this
|
|
3452
|
+
this.#resolvedConfig = resolvedConfig;
|
|
3515
3453
|
this.#includeDirectoryRegex = GlobPattern.toRegex(resolvedConfig.testFileMatch, "directories");
|
|
3516
3454
|
this.#includeFileRegex = GlobPattern.toRegex(resolvedConfig.testFileMatch, "files");
|
|
3517
3455
|
}
|
|
@@ -3519,14 +3457,14 @@ class SelectService {
|
|
|
3519
3457
|
return this.#includeDirectoryRegex.test(directoryPath);
|
|
3520
3458
|
}
|
|
3521
3459
|
#isFileIncluded(filePath) {
|
|
3522
|
-
if (this
|
|
3523
|
-
!this
|
|
3460
|
+
if (this.#resolvedConfig.pathMatch.length > 0 &&
|
|
3461
|
+
!this.#resolvedConfig.pathMatch.some((match) => filePath.toLowerCase().includes(match.toLowerCase()))) {
|
|
3524
3462
|
return false;
|
|
3525
3463
|
}
|
|
3526
3464
|
return this.#includeFileRegex.test(filePath);
|
|
3527
3465
|
}
|
|
3528
3466
|
isTestFile(filePath) {
|
|
3529
|
-
return this.#isFileIncluded(Path.relative(this
|
|
3467
|
+
return this.#isFileIncluded(Path.relative(this.#resolvedConfig.rootPath, filePath));
|
|
3530
3468
|
}
|
|
3531
3469
|
#onDiagnostic(diagnostic) {
|
|
3532
3470
|
EventEmitter.dispatch(["select:error", { diagnostics: [diagnostic] }]);
|
|
@@ -3536,12 +3474,12 @@ class SelectService {
|
|
|
3536
3474
|
const testFilePaths = [];
|
|
3537
3475
|
await this.#visitDirectory(currentPath, testFilePaths);
|
|
3538
3476
|
if (testFilePaths.length === 0) {
|
|
3539
|
-
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.noTestFilesWereSelected(this
|
|
3477
|
+
this.#onDiagnostic(Diagnostic.error(OptionDiagnosticText.noTestFilesWereSelected(this.#resolvedConfig)));
|
|
3540
3478
|
}
|
|
3541
3479
|
return testFilePaths.sort();
|
|
3542
3480
|
}
|
|
3543
3481
|
async #visitDirectory(currentPath, testFilePaths) {
|
|
3544
|
-
const targetPath = Path.join(this
|
|
3482
|
+
const targetPath = Path.join(this.#resolvedConfig.rootPath, currentPath);
|
|
3545
3483
|
let entries = [];
|
|
3546
3484
|
try {
|
|
3547
3485
|
entries = await fs.readdir(targetPath, { withFileTypes: true });
|
|
@@ -3573,6 +3511,21 @@ class SelectService {
|
|
|
3573
3511
|
}
|
|
3574
3512
|
}
|
|
3575
3513
|
|
|
3514
|
+
class StoreDiagnosticText {
|
|
3515
|
+
static failedToFetchMetadata(registryUrl) {
|
|
3516
|
+
return `Failed to fetch metadata of the 'typescript' package from '${registryUrl.toString()}'.`;
|
|
3517
|
+
}
|
|
3518
|
+
static failedWithStatusCode(code) {
|
|
3519
|
+
return `Request failed with status code ${String(code)}.`;
|
|
3520
|
+
}
|
|
3521
|
+
static maybeNetworkConnectionIssue() {
|
|
3522
|
+
return "Might be there is an issue with the registry or the network connection.";
|
|
3523
|
+
}
|
|
3524
|
+
static setupTimeoutExceeded(timeout) {
|
|
3525
|
+
return `Setup timeout of ${String(timeout / 1000)}s was exceeded.`;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3576
3529
|
class ManifestWorker {
|
|
3577
3530
|
#manifestFileName = "store-manifest.json";
|
|
3578
3531
|
#manifestFilePath;
|
|
@@ -3593,35 +3546,6 @@ class ManifestWorker {
|
|
|
3593
3546
|
}
|
|
3594
3547
|
return manifest;
|
|
3595
3548
|
}
|
|
3596
|
-
async #fetch() {
|
|
3597
|
-
return new Promise((resolve, reject) => {
|
|
3598
|
-
const request = https.get(new URL("typescript", this.#registryUrl), {
|
|
3599
|
-
headers: { accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*" },
|
|
3600
|
-
timeout: this.#timeout,
|
|
3601
|
-
}, (response) => {
|
|
3602
|
-
if (response.statusCode !== 200) {
|
|
3603
|
-
reject(new Error(`Request failed with status code ${String(response.statusCode)}.`));
|
|
3604
|
-
response.resume();
|
|
3605
|
-
return;
|
|
3606
|
-
}
|
|
3607
|
-
response.setEncoding("utf8");
|
|
3608
|
-
let rawData = "";
|
|
3609
|
-
response.on("data", (chunk) => {
|
|
3610
|
-
rawData += chunk;
|
|
3611
|
-
});
|
|
3612
|
-
response.on("end", () => {
|
|
3613
|
-
const packageMetadata = JSON.parse(rawData);
|
|
3614
|
-
resolve(packageMetadata);
|
|
3615
|
-
});
|
|
3616
|
-
});
|
|
3617
|
-
request.on("error", (error) => {
|
|
3618
|
-
reject(error);
|
|
3619
|
-
});
|
|
3620
|
-
request.on("timeout", () => {
|
|
3621
|
-
request.destroy();
|
|
3622
|
-
});
|
|
3623
|
-
});
|
|
3624
|
-
}
|
|
3625
3549
|
isOutdated(manifest, ageTolerance = 0) {
|
|
3626
3550
|
if (Date.now() - manifest.lastUpdated > 2 * 60 * 60 * 1000 + ageTolerance * 1000) {
|
|
3627
3551
|
return true;
|
|
@@ -3637,20 +3561,35 @@ class ManifestWorker {
|
|
|
3637
3561
|
};
|
|
3638
3562
|
let packageMetadata;
|
|
3639
3563
|
try {
|
|
3640
|
-
|
|
3564
|
+
const response = await fetch(new URL("typescript", this.#registryUrl), {
|
|
3565
|
+
headers: { accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*" },
|
|
3566
|
+
signal: AbortSignal.timeout(this.#timeout),
|
|
3567
|
+
});
|
|
3568
|
+
if (!response.ok) {
|
|
3569
|
+
this.#onDiagnostic(Diagnostic.error([
|
|
3570
|
+
StoreDiagnosticText.failedToFetchMetadata(this.#registryUrl),
|
|
3571
|
+
StoreDiagnosticText.failedWithStatusCode(response.status),
|
|
3572
|
+
]));
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3575
|
+
packageMetadata = (await response.json());
|
|
3641
3576
|
}
|
|
3642
3577
|
catch (error) {
|
|
3643
3578
|
if (options?.quite === true) {
|
|
3644
3579
|
return;
|
|
3645
3580
|
}
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3581
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
3582
|
+
this.#onDiagnostic(Diagnostic.error([
|
|
3583
|
+
StoreDiagnosticText.failedToFetchMetadata(this.#registryUrl),
|
|
3584
|
+
StoreDiagnosticText.setupTimeoutExceeded(this.#timeout),
|
|
3585
|
+
]));
|
|
3649
3586
|
}
|
|
3650
3587
|
else {
|
|
3651
|
-
|
|
3588
|
+
this.#onDiagnostic(Diagnostic.error([
|
|
3589
|
+
StoreDiagnosticText.failedToFetchMetadata(this.#registryUrl),
|
|
3590
|
+
StoreDiagnosticText.maybeNetworkConnectionIssue(),
|
|
3591
|
+
]));
|
|
3652
3592
|
}
|
|
3653
|
-
this.#onDiagnostic(Diagnostic.fromError(text, error));
|
|
3654
3593
|
return;
|
|
3655
3594
|
}
|
|
3656
3595
|
manifest.versions = Object.keys(packageMetadata.versions)
|
|
@@ -4068,10 +4007,10 @@ class Cli {
|
|
|
4068
4007
|
};
|
|
4069
4008
|
const onRemoved = () => {
|
|
4070
4009
|
};
|
|
4071
|
-
watchers.push(new Watcher(resolvedConfig.rootPath, onChangedTestFile, onRemoved, true));
|
|
4010
|
+
watchers.push(new Watcher(resolvedConfig.rootPath, onChangedTestFile, onRemoved, { recursive: true }));
|
|
4072
4011
|
}
|
|
4073
4012
|
return Promise.all(watchers.map((watcher) => watcher.watch()));
|
|
4074
4013
|
}
|
|
4075
4014
|
}
|
|
4076
4015
|
|
|
4077
|
-
export { Assertion, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, ConfigService, DescribeResult, Diagnostic, DiagnosticCategory, Environment, EventEmitter, ExitCodeHandler, Expect, ExpectResult, FileResult, FileWatcher, InputService, Line, OptionBrand, OptionDefinitionsMap, OptionDiagnosticText, OptionGroup, OutputService, Path, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, RuntimeReporter, Scribbler, SelectService, SetupReporter, StoreService, SummaryReporter, TSTyche, TargetResult, TaskRunner, TestFile, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageStepText, defaultOptions, describeNameText, diagnosticText, fileStatusText, fileViewText, formattedText, helpText, summaryText, testNameText, usesCompilerStepText, waitingForFileChangesText, watchUsageText };
|
|
4016
|
+
export { Assertion, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, ConfigService, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Environment, EventEmitter, ExitCodeHandler, Expect, ExpectResult, FileResult, FileWatcher, InputService, Line, OptionBrand, OptionDefinitionsMap, OptionDiagnosticText, OptionGroup, OutputService, Path, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, RuntimeReporter, Scribbler, SelectService, SetupReporter, StoreService, SummaryReporter, TSTyche, TargetResult, TaskRunner, TestFile, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageStepText, defaultOptions, describeNameText, diagnosticText, fileStatusText, fileViewText, formattedText, helpText, summaryText, testNameText, usesCompilerStepText, waitingForFileChangesText, watchUsageText };
|