tstyche 3.4.0 → 4.0.0-beta.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/README.md +1 -2
- package/build/index.d.cts +0 -69
- package/build/index.d.ts +0 -69
- package/build/tstyche.d.ts +58 -61
- package/build/tstyche.js +421 -514
- package/package.json +12 -15
package/build/tstyche.js
CHANGED
|
@@ -140,15 +140,15 @@ class Diagnostic {
|
|
|
140
140
|
}
|
|
141
141
|
const text = typeof diagnostic.messageText === "string"
|
|
142
142
|
? diagnostic.messageText
|
|
143
|
-
: Diagnostic
|
|
143
|
+
: Diagnostic.toMessageText(diagnostic.messageText);
|
|
144
144
|
return new Diagnostic(text, DiagnosticCategory.Error, origin).add({ code, related });
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
|
-
static
|
|
147
|
+
static toMessageText(chain) {
|
|
148
148
|
const result = [chain.messageText];
|
|
149
149
|
if (chain.next != null) {
|
|
150
150
|
for (const nextChain of chain.next) {
|
|
151
|
-
result.push(...Diagnostic
|
|
151
|
+
result.push(...Diagnostic.toMessageText(nextChain));
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
return result;
|
|
@@ -363,9 +363,6 @@ class Version {
|
|
|
363
363
|
static isSatisfiedWith(source, target) {
|
|
364
364
|
return source === target || Version.#satisfies(source, target);
|
|
365
365
|
}
|
|
366
|
-
static isVersionTag(target) {
|
|
367
|
-
return /^\d+/.test(target);
|
|
368
|
-
}
|
|
369
366
|
static #satisfies(source, target) {
|
|
370
367
|
const sourceElements = source.split(/\.|-/);
|
|
371
368
|
const targetElements = target.split(/\.|-/);
|
|
@@ -394,8 +391,8 @@ class StoreDiagnosticText {
|
|
|
394
391
|
static failedToFetchMetadata(registry) {
|
|
395
392
|
return `Failed to fetch metadata of the 'typescript' package from '${registry}'.`;
|
|
396
393
|
}
|
|
397
|
-
static
|
|
398
|
-
return `Failed to
|
|
394
|
+
static failedToFetchPackage(version) {
|
|
395
|
+
return `Failed to fetch the 'typescript@${version}' package.`;
|
|
399
396
|
}
|
|
400
397
|
static failedToUpdateMetadata(registry) {
|
|
401
398
|
return `Failed to update metadata of the 'typescript' package from '${registry}'.`;
|
|
@@ -499,9 +496,10 @@ class LockService {
|
|
|
499
496
|
}
|
|
500
497
|
|
|
501
498
|
class Manifest {
|
|
502
|
-
static #version = "
|
|
499
|
+
static #version = "3";
|
|
503
500
|
$version;
|
|
504
501
|
lastUpdated;
|
|
502
|
+
minorVersions;
|
|
505
503
|
npmRegistry;
|
|
506
504
|
packages;
|
|
507
505
|
resolutions;
|
|
@@ -509,6 +507,7 @@ class Manifest {
|
|
|
509
507
|
constructor(data) {
|
|
510
508
|
this.$version = data.$version ?? Manifest.#version;
|
|
511
509
|
this.lastUpdated = data.lastUpdated ?? Date.now();
|
|
510
|
+
this.minorVersions = data.minorVersions;
|
|
512
511
|
this.npmRegistry = data.npmRegistry;
|
|
513
512
|
this.packages = data.packages;
|
|
514
513
|
this.resolutions = data.resolutions;
|
|
@@ -542,6 +541,7 @@ class Manifest {
|
|
|
542
541
|
const manifestData = {
|
|
543
542
|
$version: this.$version,
|
|
544
543
|
lastUpdated: this.lastUpdated,
|
|
544
|
+
minorVersions: this.minorVersions,
|
|
545
545
|
npmRegistry: this.npmRegistry,
|
|
546
546
|
packages: this.packages,
|
|
547
547
|
resolutions: this.resolutions,
|
|
@@ -556,7 +556,7 @@ class ManifestService {
|
|
|
556
556
|
#manifestFilePath;
|
|
557
557
|
#npmRegistry;
|
|
558
558
|
#storePath;
|
|
559
|
-
#supportedVersionRegex = /^(
|
|
559
|
+
#supportedVersionRegex = /^(5)\.\d\.\d$/;
|
|
560
560
|
constructor(storePath, npmRegistry, fetcher) {
|
|
561
561
|
this.#storePath = storePath;
|
|
562
562
|
this.#npmRegistry = npmRegistry;
|
|
@@ -591,7 +591,7 @@ class ManifestService {
|
|
|
591
591
|
packages[tag] = { integrity: meta.dist.integrity, tarball: meta.dist.tarball };
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
|
-
const minorVersions = new Set(versions.map((version) => version.slice(0, -2)));
|
|
594
|
+
const minorVersions = [...new Set(versions.map((version) => version.slice(0, -2)))];
|
|
595
595
|
for (const tag of minorVersions) {
|
|
596
596
|
const resolvedVersion = versions.findLast((version) => version.startsWith(tag));
|
|
597
597
|
if (resolvedVersion != null) {
|
|
@@ -608,7 +608,7 @@ class ManifestService {
|
|
|
608
608
|
}
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
|
-
return new Manifest({ npmRegistry: this.#npmRegistry, packages, resolutions, versions });
|
|
611
|
+
return new Manifest({ minorVersions, npmRegistry: this.#npmRegistry, packages, resolutions, versions });
|
|
612
612
|
}
|
|
613
613
|
async open(options) {
|
|
614
614
|
if (!existsSync(this.#manifestFilePath)) {
|
|
@@ -685,9 +685,6 @@ class PackageService {
|
|
|
685
685
|
if (response?.body != null) {
|
|
686
686
|
const targetPath = `${packagePath}-${Math.random().toString(32).slice(2)}`;
|
|
687
687
|
for await (const file of TarReader.extract(response.body)) {
|
|
688
|
-
if (!file.name.startsWith("package/")) {
|
|
689
|
-
continue;
|
|
690
|
-
}
|
|
691
688
|
const filePath = Path.join(targetPath, file.name.replace("package/", ""));
|
|
692
689
|
const directoryPath = Path.dirname(filePath);
|
|
693
690
|
if (!existsSync(directoryPath)) {
|
|
@@ -702,7 +699,7 @@ class PackageService {
|
|
|
702
699
|
}
|
|
703
700
|
async ensure(packageVersion, manifest) {
|
|
704
701
|
let packagePath = Path.join(this.#storePath, `typescript@${packageVersion}`);
|
|
705
|
-
const diagnostic = Diagnostic.error(StoreDiagnosticText.
|
|
702
|
+
const diagnostic = Diagnostic.error(StoreDiagnosticText.failedToFetchPackage(packageVersion));
|
|
706
703
|
if (await this.#lockService.isLocked(packagePath, diagnostic)) {
|
|
707
704
|
return;
|
|
708
705
|
}
|
|
@@ -742,11 +739,7 @@ class Store {
|
|
|
742
739
|
Store.#packageService = new PackageService(Store.#storePath, Store.#fetcher, Store.#lockService);
|
|
743
740
|
Store.#manifestService = new ManifestService(Store.#storePath, Store.#npmRegistry, Store.#fetcher);
|
|
744
741
|
}
|
|
745
|
-
static async
|
|
746
|
-
await Store.open();
|
|
747
|
-
return Store.#supportedTags;
|
|
748
|
-
}
|
|
749
|
-
static async install(tag) {
|
|
742
|
+
static async fetch(tag) {
|
|
750
743
|
if (tag === "current") {
|
|
751
744
|
return;
|
|
752
745
|
}
|
|
@@ -799,14 +792,11 @@ class Store {
|
|
|
799
792
|
modulePath = Path.resolve(modulePath, "../tsserverlibrary.js");
|
|
800
793
|
}
|
|
801
794
|
const sourceText = await fs.readFile(modulePath, { encoding: "utf8" });
|
|
802
|
-
const toExpose = [
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
toExpose.push("getTypeOfSymbol");
|
|
808
|
-
}
|
|
809
|
-
toExpose.push("isTypeRelatedTo", "relation: { assignable: assignableRelation, identity: identityRelation, subtype: strictSubtypeRelation }");
|
|
795
|
+
const toExpose = [
|
|
796
|
+
"isApplicableIndexType",
|
|
797
|
+
"isTypeRelatedTo",
|
|
798
|
+
"relation: { assignable: assignableRelation, identity: identityRelation }",
|
|
799
|
+
];
|
|
810
800
|
const modifiedSourceText = sourceText.replace("return checker;", `return { ...checker, ${toExpose.join(", ")} };`);
|
|
811
801
|
const compiledWrapper = vm.compileFunction(modifiedSourceText, ["exports", "require", "module", "__filename", "__dirname"], { filename: modulePath });
|
|
812
802
|
compiledWrapper(exports, createRequire(modulePath), module, modulePath, Path.dirname(modulePath));
|
|
@@ -857,7 +847,7 @@ class Target {
|
|
|
857
847
|
}
|
|
858
848
|
await Store.open();
|
|
859
849
|
if (Store.manifest != null) {
|
|
860
|
-
let versions =
|
|
850
|
+
let versions = [...Store.manifest.minorVersions];
|
|
861
851
|
for (const comparator of query.split(" ")) {
|
|
862
852
|
versions = Target.#filter(comparator, versions);
|
|
863
853
|
}
|
|
@@ -913,15 +903,15 @@ class Options {
|
|
|
913
903
|
},
|
|
914
904
|
{
|
|
915
905
|
brand: OptionBrand.BareTrue,
|
|
916
|
-
description: "
|
|
906
|
+
description: "Fetch specified versions of the 'typescript' package and exit.",
|
|
917
907
|
group: OptionGroup.CommandLine,
|
|
918
|
-
name: "
|
|
908
|
+
name: "fetch",
|
|
919
909
|
},
|
|
920
910
|
{
|
|
921
911
|
brand: OptionBrand.BareTrue,
|
|
922
|
-
description: "
|
|
912
|
+
description: "Print the list of command line options with brief descriptions and exit.",
|
|
923
913
|
group: OptionGroup.CommandLine,
|
|
924
|
-
name: "
|
|
914
|
+
name: "help",
|
|
925
915
|
},
|
|
926
916
|
{
|
|
927
917
|
brand: OptionBrand.BareTrue,
|
|
@@ -1157,11 +1147,11 @@ class CommandLineParser {
|
|
|
1157
1147
|
this.#onDiagnostics = onDiagnostics;
|
|
1158
1148
|
this.#options = Options.for(OptionGroup.CommandLine);
|
|
1159
1149
|
}
|
|
1160
|
-
|
|
1150
|
+
#onExpectsValue(optionName, optionBrand) {
|
|
1161
1151
|
const text = [
|
|
1162
1152
|
ConfigDiagnosticText.expectsValue(optionName),
|
|
1163
|
-
|
|
1164
|
-
]
|
|
1153
|
+
...ConfigDiagnosticText.usage(optionName, optionBrand),
|
|
1154
|
+
];
|
|
1165
1155
|
this.#onDiagnostics(Diagnostic.error(text));
|
|
1166
1156
|
}
|
|
1167
1157
|
async parse(commandLineArgs) {
|
|
@@ -1215,7 +1205,7 @@ class CommandLineParser {
|
|
|
1215
1205
|
index++;
|
|
1216
1206
|
break;
|
|
1217
1207
|
}
|
|
1218
|
-
|
|
1208
|
+
this.#onExpectsValue(optionName, optionDefinition.brand);
|
|
1219
1209
|
break;
|
|
1220
1210
|
case OptionBrand.String:
|
|
1221
1211
|
if (optionValue !== "") {
|
|
@@ -1225,7 +1215,7 @@ class CommandLineParser {
|
|
|
1225
1215
|
index++;
|
|
1226
1216
|
break;
|
|
1227
1217
|
}
|
|
1228
|
-
|
|
1218
|
+
this.#onExpectsValue(optionName, optionDefinition.brand);
|
|
1229
1219
|
break;
|
|
1230
1220
|
}
|
|
1231
1221
|
return index;
|
|
@@ -1504,11 +1494,11 @@ class ConfigFileParser {
|
|
|
1504
1494
|
}
|
|
1505
1495
|
|
|
1506
1496
|
const defaultOptions = {
|
|
1507
|
-
checkSourceFiles:
|
|
1497
|
+
checkSourceFiles: true,
|
|
1508
1498
|
failFast: false,
|
|
1509
1499
|
plugins: [],
|
|
1510
|
-
rejectAnyType:
|
|
1511
|
-
rejectNeverType:
|
|
1500
|
+
rejectAnyType: true,
|
|
1501
|
+
rejectNeverType: true,
|
|
1512
1502
|
reporters: ["list", "summary"],
|
|
1513
1503
|
rootPath: Path.resolve("./"),
|
|
1514
1504
|
target: environmentOptions.typescriptModule != null ? ["current"] : ["latest"],
|
|
@@ -1909,6 +1899,293 @@ class ResultHandler {
|
|
|
1909
1899
|
}
|
|
1910
1900
|
}
|
|
1911
1901
|
|
|
1902
|
+
var TestTreeNodeBrand;
|
|
1903
|
+
(function (TestTreeNodeBrand) {
|
|
1904
|
+
TestTreeNodeBrand["Describe"] = "describe";
|
|
1905
|
+
TestTreeNodeBrand["Test"] = "test";
|
|
1906
|
+
TestTreeNodeBrand["Expect"] = "expect";
|
|
1907
|
+
})(TestTreeNodeBrand || (TestTreeNodeBrand = {}));
|
|
1908
|
+
|
|
1909
|
+
class TestTreeNode {
|
|
1910
|
+
brand;
|
|
1911
|
+
children = [];
|
|
1912
|
+
#compiler;
|
|
1913
|
+
diagnostics = new Set();
|
|
1914
|
+
flags;
|
|
1915
|
+
name = "";
|
|
1916
|
+
node;
|
|
1917
|
+
parent;
|
|
1918
|
+
constructor(compiler, brand, node, parent, flags) {
|
|
1919
|
+
this.brand = brand;
|
|
1920
|
+
this.#compiler = compiler;
|
|
1921
|
+
this.node = node;
|
|
1922
|
+
this.parent = parent;
|
|
1923
|
+
this.flags = flags;
|
|
1924
|
+
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
1925
|
+
this.name = node.arguments[0].text;
|
|
1926
|
+
}
|
|
1927
|
+
if (node.arguments[1] != null &&
|
|
1928
|
+
compiler.isFunctionLike(node.arguments[1]) &&
|
|
1929
|
+
compiler.isBlock(node.arguments[1].body)) {
|
|
1930
|
+
const blockStart = node.arguments[1].body.getStart();
|
|
1931
|
+
const blockEnd = node.arguments[1].body.getEnd();
|
|
1932
|
+
for (const diagnostic of parent.diagnostics) {
|
|
1933
|
+
if (diagnostic.start != null && diagnostic.start >= blockStart && diagnostic.start <= blockEnd) {
|
|
1934
|
+
this.diagnostics.add(diagnostic);
|
|
1935
|
+
parent.diagnostics.delete(diagnostic);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
validate() {
|
|
1941
|
+
const diagnostics = [];
|
|
1942
|
+
const getText = (node) => `'${node.expression.getText()}()' cannot be nested within '${this.node.expression.getText()}()'.`;
|
|
1943
|
+
const getParentCallExpression = (node) => {
|
|
1944
|
+
while (!this.#compiler.isCallExpression(node.parent)) {
|
|
1945
|
+
node = node.parent;
|
|
1946
|
+
}
|
|
1947
|
+
return node.parent;
|
|
1948
|
+
};
|
|
1949
|
+
switch (this.brand) {
|
|
1950
|
+
case TestTreeNodeBrand.Describe:
|
|
1951
|
+
for (const member of this.children) {
|
|
1952
|
+
if (member.brand === TestTreeNodeBrand.Expect) {
|
|
1953
|
+
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(getParentCallExpression(member.node))));
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
break;
|
|
1957
|
+
case TestTreeNodeBrand.Test:
|
|
1958
|
+
case TestTreeNodeBrand.Expect:
|
|
1959
|
+
for (const member of this.children) {
|
|
1960
|
+
if (member.brand !== TestTreeNodeBrand.Expect) {
|
|
1961
|
+
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
break;
|
|
1965
|
+
}
|
|
1966
|
+
return diagnostics;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
class AssertionNode extends TestTreeNode {
|
|
1971
|
+
isNot;
|
|
1972
|
+
matcherName;
|
|
1973
|
+
matcherNode;
|
|
1974
|
+
modifierNode;
|
|
1975
|
+
notNode;
|
|
1976
|
+
source;
|
|
1977
|
+
target;
|
|
1978
|
+
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
1979
|
+
super(compiler, brand, node, parent, flags);
|
|
1980
|
+
this.isNot = notNode != null;
|
|
1981
|
+
this.matcherName = matcherNode.expression.name;
|
|
1982
|
+
this.matcherNode = matcherNode;
|
|
1983
|
+
this.modifierNode = modifierNode;
|
|
1984
|
+
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
1985
|
+
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
1986
|
+
for (const diagnostic of parent.diagnostics) {
|
|
1987
|
+
if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
|
|
1988
|
+
this.diagnostics.add(diagnostic);
|
|
1989
|
+
parent.diagnostics.delete(diagnostic);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
var TestTreeNodeFlags;
|
|
1996
|
+
(function (TestTreeNodeFlags) {
|
|
1997
|
+
TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
|
|
1998
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Fail"] = 1] = "Fail";
|
|
1999
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Only"] = 2] = "Only";
|
|
2000
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Skip"] = 4] = "Skip";
|
|
2001
|
+
TestTreeNodeFlags[TestTreeNodeFlags["Todo"] = 8] = "Todo";
|
|
2002
|
+
})(TestTreeNodeFlags || (TestTreeNodeFlags = {}));
|
|
2003
|
+
|
|
2004
|
+
class IdentifierLookup {
|
|
2005
|
+
#compiler;
|
|
2006
|
+
#identifiers;
|
|
2007
|
+
#moduleSpecifiers = ['"tstyche"', "'tstyche'"];
|
|
2008
|
+
constructor(compiler, identifiers) {
|
|
2009
|
+
this.#compiler = compiler;
|
|
2010
|
+
this.#identifiers = identifiers ?? {
|
|
2011
|
+
namedImports: {
|
|
2012
|
+
describe: undefined,
|
|
2013
|
+
expect: undefined,
|
|
2014
|
+
it: undefined,
|
|
2015
|
+
namespace: undefined,
|
|
2016
|
+
test: undefined,
|
|
2017
|
+
},
|
|
2018
|
+
namespace: undefined,
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
handleImportDeclaration(node) {
|
|
2022
|
+
if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
|
|
2023
|
+
node.importClause?.isTypeOnly !== true &&
|
|
2024
|
+
node.importClause?.namedBindings != null) {
|
|
2025
|
+
if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
|
|
2026
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
2027
|
+
if (element.isTypeOnly) {
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
let identifierKey;
|
|
2031
|
+
if (element.propertyName) {
|
|
2032
|
+
identifierKey = element.propertyName.getText();
|
|
2033
|
+
}
|
|
2034
|
+
else {
|
|
2035
|
+
identifierKey = element.name.getText();
|
|
2036
|
+
}
|
|
2037
|
+
if (identifierKey in this.#identifiers.namedImports) {
|
|
2038
|
+
this.#identifiers.namedImports[identifierKey] = element.name.getText();
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
|
|
2043
|
+
this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
resolveTestMemberMeta(node) {
|
|
2048
|
+
let flags = TestTreeNodeFlags.None;
|
|
2049
|
+
let expression = node.expression;
|
|
2050
|
+
while (this.#compiler.isPropertyAccessExpression(expression)) {
|
|
2051
|
+
if (expression.expression.getText() === this.#identifiers.namespace) {
|
|
2052
|
+
break;
|
|
2053
|
+
}
|
|
2054
|
+
switch (expression.name.getText()) {
|
|
2055
|
+
case "fail":
|
|
2056
|
+
flags |= TestTreeNodeFlags.Fail;
|
|
2057
|
+
break;
|
|
2058
|
+
case "only":
|
|
2059
|
+
flags |= TestTreeNodeFlags.Only;
|
|
2060
|
+
break;
|
|
2061
|
+
case "skip":
|
|
2062
|
+
flags |= TestTreeNodeFlags.Skip;
|
|
2063
|
+
break;
|
|
2064
|
+
case "todo":
|
|
2065
|
+
flags |= TestTreeNodeFlags.Todo;
|
|
2066
|
+
break;
|
|
2067
|
+
}
|
|
2068
|
+
expression = expression.expression;
|
|
2069
|
+
}
|
|
2070
|
+
let identifierName;
|
|
2071
|
+
if (this.#compiler.isPropertyAccessExpression(expression) &&
|
|
2072
|
+
expression.expression.getText() === this.#identifiers.namespace) {
|
|
2073
|
+
identifierName = expression.name.getText();
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
identifierName = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
|
|
2077
|
+
}
|
|
2078
|
+
if (!identifierName) {
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
switch (identifierName) {
|
|
2082
|
+
case "describe":
|
|
2083
|
+
return { brand: TestTreeNodeBrand.Describe, flags };
|
|
2084
|
+
case "it":
|
|
2085
|
+
case "test":
|
|
2086
|
+
return { brand: TestTreeNodeBrand.Test, flags };
|
|
2087
|
+
case "expect":
|
|
2088
|
+
return { brand: TestTreeNodeBrand.Expect, flags };
|
|
2089
|
+
}
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
class TestTree {
|
|
2095
|
+
children = [];
|
|
2096
|
+
diagnostics;
|
|
2097
|
+
hasOnly = false;
|
|
2098
|
+
sourceFile;
|
|
2099
|
+
constructor(diagnostics, sourceFile) {
|
|
2100
|
+
this.diagnostics = diagnostics;
|
|
2101
|
+
this.sourceFile = sourceFile;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
class CollectService {
|
|
2106
|
+
#compiler;
|
|
2107
|
+
constructor(compiler) {
|
|
2108
|
+
this.#compiler = compiler;
|
|
2109
|
+
}
|
|
2110
|
+
#collectTestTreeNodes(node, identifiers, parent) {
|
|
2111
|
+
if (this.#compiler.isCallExpression(node)) {
|
|
2112
|
+
const meta = identifiers.resolveTestMemberMeta(node);
|
|
2113
|
+
if (meta != null && (meta.brand === TestTreeNodeBrand.Describe || meta.brand === TestTreeNodeBrand.Test)) {
|
|
2114
|
+
const testTreeNode = new TestTreeNode(this.#compiler, meta.brand, node, parent, meta.flags);
|
|
2115
|
+
parent.children.push(testTreeNode);
|
|
2116
|
+
EventEmitter.dispatch(["collect:node", { testNode: testTreeNode }]);
|
|
2117
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2118
|
+
this.#collectTestTreeNodes(node, identifiers, testTreeNode);
|
|
2119
|
+
});
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
if (meta != null && meta.brand === TestTreeNodeBrand.Expect) {
|
|
2123
|
+
const modifierNode = this.#getChainedNode(node, "type");
|
|
2124
|
+
if (!modifierNode) {
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
const notNode = this.#getChainedNode(modifierNode, "not");
|
|
2128
|
+
const matcherNode = this.#getChainedNode(notNode ?? modifierNode)?.parent;
|
|
2129
|
+
if (!matcherNode || !this.#isMatcherNode(matcherNode)) {
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
|
|
2133
|
+
parent.children.push(assertionNode);
|
|
2134
|
+
EventEmitter.dispatch(["collect:node", { testNode: assertionNode }]);
|
|
2135
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2136
|
+
this.#collectTestTreeNodes(node, identifiers, assertionNode);
|
|
2137
|
+
});
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
if (this.#compiler.isImportDeclaration(node)) {
|
|
2142
|
+
identifiers.handleImportDeclaration(node);
|
|
2143
|
+
return;
|
|
2144
|
+
}
|
|
2145
|
+
this.#compiler.forEachChild(node, (node) => {
|
|
2146
|
+
this.#collectTestTreeNodes(node, identifiers, parent);
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
2150
|
+
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
2151
|
+
EventEmitter.dispatch(["collect:start", { testTree }]);
|
|
2152
|
+
this.#collectTestTreeNodes(sourceFile, new IdentifierLookup(this.#compiler), testTree);
|
|
2153
|
+
EventEmitter.dispatch(["collect:end", { testTree }]);
|
|
2154
|
+
return testTree;
|
|
2155
|
+
}
|
|
2156
|
+
#getChainedNode({ parent }, name) {
|
|
2157
|
+
if (!this.#compiler.isPropertyAccessExpression(parent)) {
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
if (name != null && name !== parent.name.getText()) {
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
return parent;
|
|
2164
|
+
}
|
|
2165
|
+
#isMatcherNode(node) {
|
|
2166
|
+
return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
class TestTreeHandler {
|
|
2171
|
+
testTree;
|
|
2172
|
+
on([event, payload]) {
|
|
2173
|
+
switch (event) {
|
|
2174
|
+
case "collect:start":
|
|
2175
|
+
this.testTree = payload.testTree;
|
|
2176
|
+
break;
|
|
2177
|
+
case "collect:end":
|
|
2178
|
+
this.testTree = undefined;
|
|
2179
|
+
break;
|
|
2180
|
+
case "collect:node":
|
|
2181
|
+
if (payload.testNode.flags & TestTreeNodeFlags.Only) {
|
|
2182
|
+
this.testTree.hasOnly = true;
|
|
2183
|
+
}
|
|
2184
|
+
break;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
|
|
1912
2189
|
function jsx(type, props) {
|
|
1913
2190
|
return { props, type };
|
|
1914
2191
|
}
|
|
@@ -2013,12 +2290,15 @@ function CodeLineText({ gutterWidth, lineNumber, lineNumberColor = Color.Gray, l
|
|
|
2013
2290
|
function SquiggleLineText({ gutterWidth, indentWidth = 0, squiggleColor, squiggleWidth }) {
|
|
2014
2291
|
return (jsx(Line, { children: [" ".repeat(gutterWidth), jsx(Text, { color: Color.Gray, children: " | " }), " ".repeat(indentWidth), jsx(Text, { color: squiggleColor, children: "~".repeat(squiggleWidth === 0 ? 1 : squiggleWidth) })] }));
|
|
2015
2292
|
}
|
|
2016
|
-
function
|
|
2293
|
+
function CodeFrameText({ diagnosticCategory, diagnosticOrigin, options }) {
|
|
2294
|
+
const linesAbove = options?.linesAbove ?? 2;
|
|
2295
|
+
const linesBelow = options?.linesBelow ?? 3;
|
|
2296
|
+
const showBreadcrumbs = options?.showBreadcrumbs ?? true;
|
|
2017
2297
|
const lineMap = diagnosticOrigin.sourceFile.getLineStarts();
|
|
2018
2298
|
const { character: firstMarkedLineCharacter, line: firstMarkedLine } = diagnosticOrigin.sourceFile.getLineAndCharacterOfPosition(diagnosticOrigin.start);
|
|
2019
2299
|
const { character: lastMarkedLineCharacter, line: lastMarkedLine } = diagnosticOrigin.sourceFile.getLineAndCharacterOfPosition(diagnosticOrigin.end);
|
|
2020
|
-
const firstLine = Math.max(firstMarkedLine -
|
|
2021
|
-
const lastLine = Math.min(
|
|
2300
|
+
const firstLine = Math.max(firstMarkedLine - linesAbove, 0);
|
|
2301
|
+
const lastLine = Math.min(lastMarkedLine + linesBelow, lineMap.length - 1);
|
|
2022
2302
|
const gutterWidth = (lastLine + 1).toString().length + 2;
|
|
2023
2303
|
let highlightColor;
|
|
2024
2304
|
switch (diagnosticCategory) {
|
|
@@ -2029,43 +2309,47 @@ function CodeSpanText({ diagnosticCategory, diagnosticOrigin }) {
|
|
|
2029
2309
|
highlightColor = Color.Yellow;
|
|
2030
2310
|
break;
|
|
2031
2311
|
}
|
|
2032
|
-
const
|
|
2312
|
+
const codeFrame = [];
|
|
2033
2313
|
for (let index = firstLine; index <= lastLine; index++) {
|
|
2034
2314
|
const lineStart = lineMap[index];
|
|
2035
2315
|
const lineEnd = index === lineMap.length - 1 ? diagnosticOrigin.sourceFile.text.length : lineMap[index + 1];
|
|
2036
2316
|
const lineText = diagnosticOrigin.sourceFile.text.slice(lineStart, lineEnd).trimEnd().replace(/\t/g, " ");
|
|
2037
2317
|
if (index >= firstMarkedLine && index <= lastMarkedLine) {
|
|
2038
|
-
|
|
2318
|
+
codeFrame.push(jsx(CodeLineText, { gutterWidth: gutterWidth, lineNumber: index + 1, lineNumberColor: highlightColor, lineText: lineText }));
|
|
2039
2319
|
if (index === firstMarkedLine) {
|
|
2040
2320
|
const squiggleLength = index === lastMarkedLine
|
|
2041
2321
|
? lastMarkedLineCharacter - firstMarkedLineCharacter
|
|
2042
2322
|
: lineText.length - firstMarkedLineCharacter;
|
|
2043
|
-
|
|
2323
|
+
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, indentWidth: firstMarkedLineCharacter, squiggleColor: highlightColor, squiggleWidth: squiggleLength }));
|
|
2044
2324
|
}
|
|
2045
2325
|
else if (index === lastMarkedLine) {
|
|
2046
|
-
|
|
2326
|
+
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, squiggleColor: highlightColor, squiggleWidth: lastMarkedLineCharacter }));
|
|
2047
2327
|
}
|
|
2048
2328
|
else {
|
|
2049
|
-
|
|
2329
|
+
codeFrame.push(jsx(SquiggleLineText, { gutterWidth: gutterWidth, squiggleColor: highlightColor, squiggleWidth: lineText.length }));
|
|
2050
2330
|
}
|
|
2051
2331
|
}
|
|
2052
2332
|
else {
|
|
2053
|
-
|
|
2333
|
+
codeFrame.push(jsx(CodeLineText, { gutterWidth: gutterWidth, lineNumber: index + 1, lineText: lineText }));
|
|
2054
2334
|
}
|
|
2055
2335
|
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2336
|
+
let breadcrumbs;
|
|
2337
|
+
if (showBreadcrumbs && diagnosticOrigin.assertion != null) {
|
|
2338
|
+
breadcrumbs = jsx(BreadcrumbsText, { ancestor: diagnosticOrigin.assertion.parent });
|
|
2339
|
+
}
|
|
2340
|
+
const location = (jsx(Line, { children: [" ".repeat(gutterWidth + 2), jsx(Text, { color: Color.Gray, children: " at " }), jsx(Text, { color: Color.Cyan, children: Path.relative("", diagnosticOrigin.sourceFile.fileName) }), jsx(Text, { color: Color.Gray, children: `:${firstMarkedLine + 1}:${firstMarkedLineCharacter + 1}` }), breadcrumbs] }));
|
|
2341
|
+
return (jsx(Text, { children: [codeFrame, jsx(Line, {}), location] }));
|
|
2058
2342
|
}
|
|
2059
2343
|
|
|
2060
|
-
function DiagnosticText({ diagnostic }) {
|
|
2344
|
+
function DiagnosticText({ codeFrameOptions, diagnostic }) {
|
|
2061
2345
|
const code = diagnostic.code ? jsx(Text, { color: Color.Gray, children: [" ", diagnostic.code] }) : undefined;
|
|
2062
2346
|
const text = Array.isArray(diagnostic.text) ? diagnostic.text : [diagnostic.text];
|
|
2063
2347
|
const message = text.map((text, index) => (jsx(Text, { children: [index === 1 ? jsx(Line, {}) : undefined, jsx(Line, { children: [text, index === 0 ? code : undefined] })] })));
|
|
2064
2348
|
const related = diagnostic.related?.map((relatedDiagnostic) => jsx(DiagnosticText, { diagnostic: relatedDiagnostic }));
|
|
2065
|
-
const
|
|
2066
|
-
return (jsx(Text, { children: [message,
|
|
2349
|
+
const codeFrame = diagnostic.origin ? (jsx(Text, { children: [jsx(Line, {}), jsx(CodeFrameText, { diagnosticCategory: diagnostic.category, diagnosticOrigin: diagnostic.origin, options: codeFrameOptions })] })) : undefined;
|
|
2350
|
+
return (jsx(Text, { children: [message, codeFrame, jsx(Line, {}), jsx(Text, { indent: 2, children: related })] }));
|
|
2067
2351
|
}
|
|
2068
|
-
function diagnosticText(diagnostic) {
|
|
2352
|
+
function diagnosticText(diagnostic, codeFrameOptions = {}) {
|
|
2069
2353
|
let prefix;
|
|
2070
2354
|
switch (diagnostic.category) {
|
|
2071
2355
|
case DiagnosticCategory.Error:
|
|
@@ -2075,7 +2359,7 @@ function diagnosticText(diagnostic) {
|
|
|
2075
2359
|
prefix = jsx(Text, { color: Color.Yellow, children: "Warning: " });
|
|
2076
2360
|
break;
|
|
2077
2361
|
}
|
|
2078
|
-
return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { diagnostic: diagnostic })] }));
|
|
2362
|
+
return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { codeFrameOptions: codeFrameOptions, diagnostic: diagnostic })] }));
|
|
2079
2363
|
}
|
|
2080
2364
|
|
|
2081
2365
|
function FileNameText({ filePath }) {
|
|
@@ -2143,7 +2427,7 @@ function CommandLineUsageText() {
|
|
|
2143
2427
|
const usage = [
|
|
2144
2428
|
["tstyche", "Run all tests."],
|
|
2145
2429
|
["tstyche path/to/first.test.ts", "Only run the test files with matching path."],
|
|
2146
|
-
["tstyche --target
|
|
2430
|
+
["tstyche --target 5.3,5.6.2,current", "Test on all specified versions of TypeScript."],
|
|
2147
2431
|
];
|
|
2148
2432
|
const usageText = usage.map(([commandText, descriptionText]) => (jsx(Line, { children: [jsx(CommandText, { text: commandText }), jsx(OptionDescriptionText, { text: descriptionText })] })));
|
|
2149
2433
|
return jsx(Text, { children: usageText });
|
|
@@ -2356,21 +2640,11 @@ class ListReporter extends BaseReporter {
|
|
|
2356
2640
|
#hasReportedError = false;
|
|
2357
2641
|
#hasReportedUses = false;
|
|
2358
2642
|
#isFileViewExpanded = false;
|
|
2359
|
-
#seenDeprecations = new Set();
|
|
2360
2643
|
get #isLastFile() {
|
|
2361
2644
|
return this.#fileCount === 0;
|
|
2362
2645
|
}
|
|
2363
2646
|
on([event, payload]) {
|
|
2364
2647
|
switch (event) {
|
|
2365
|
-
case "deprecation:info": {
|
|
2366
|
-
for (const diagnostic of payload.diagnostics) {
|
|
2367
|
-
if (!this.#seenDeprecations.has(diagnostic.text.toString())) {
|
|
2368
|
-
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
2369
|
-
this.#seenDeprecations.add(diagnostic.text.toString());
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
break;
|
|
2373
|
-
}
|
|
2374
2648
|
case "run:start":
|
|
2375
2649
|
this.#isFileViewExpanded = payload.result.tasks.length === 1 && this.resolvedConfig.watch !== true;
|
|
2376
2650
|
break;
|
|
@@ -2422,7 +2696,6 @@ class ListReporter extends BaseReporter {
|
|
|
2422
2696
|
this.#hasReportedError = true;
|
|
2423
2697
|
}
|
|
2424
2698
|
this.#fileView.clear();
|
|
2425
|
-
this.#seenDeprecations.clear();
|
|
2426
2699
|
break;
|
|
2427
2700
|
case "describe:start":
|
|
2428
2701
|
if (this.#isFileViewExpanded) {
|
|
@@ -2793,19 +3066,19 @@ class Debounce {
|
|
|
2793
3066
|
this.#delay = delay;
|
|
2794
3067
|
this.#onResolve = onResolve;
|
|
2795
3068
|
}
|
|
2796
|
-
|
|
3069
|
+
cancel() {
|
|
2797
3070
|
clearTimeout(this.#timeout);
|
|
2798
3071
|
}
|
|
2799
|
-
|
|
2800
|
-
this.
|
|
3072
|
+
refresh() {
|
|
3073
|
+
this.cancel();
|
|
2801
3074
|
this.#timeout = setTimeout(() => {
|
|
2802
3075
|
this.#resolve?.(this.#onResolve());
|
|
2803
3076
|
}, this.#delay);
|
|
2804
3077
|
}
|
|
2805
|
-
|
|
3078
|
+
resolve(value) {
|
|
2806
3079
|
this.#resolve?.(value);
|
|
2807
3080
|
}
|
|
2808
|
-
|
|
3081
|
+
schedule() {
|
|
2809
3082
|
return new Promise((resolve) => {
|
|
2810
3083
|
this.#resolve = resolve;
|
|
2811
3084
|
});
|
|
@@ -2833,13 +3106,13 @@ class WatchService {
|
|
|
2833
3106
|
};
|
|
2834
3107
|
const debounce = new Debounce(100, onResolve);
|
|
2835
3108
|
const onClose = (reason) => {
|
|
2836
|
-
debounce.
|
|
3109
|
+
debounce.cancel();
|
|
2837
3110
|
this.#inputService?.close();
|
|
2838
3111
|
for (const watcher of this.#watchers) {
|
|
2839
3112
|
watcher.close();
|
|
2840
3113
|
}
|
|
2841
3114
|
cancellationToken.cancel(reason);
|
|
2842
|
-
debounce.
|
|
3115
|
+
debounce.resolve([]);
|
|
2843
3116
|
};
|
|
2844
3117
|
if (!environmentOptions.noInteractive) {
|
|
2845
3118
|
const onInput = (chunk) => {
|
|
@@ -2854,9 +3127,9 @@ class WatchService {
|
|
|
2854
3127
|
case "\u000D":
|
|
2855
3128
|
case "\u0020":
|
|
2856
3129
|
case "a":
|
|
2857
|
-
debounce.
|
|
3130
|
+
debounce.cancel();
|
|
2858
3131
|
if (this.#watchedTestFiles.size > 0) {
|
|
2859
|
-
debounce.
|
|
3132
|
+
debounce.resolve([...this.#watchedTestFiles.values()]);
|
|
2860
3133
|
}
|
|
2861
3134
|
break;
|
|
2862
3135
|
}
|
|
@@ -2864,7 +3137,7 @@ class WatchService {
|
|
|
2864
3137
|
this.#inputService = new InputService(onInput);
|
|
2865
3138
|
}
|
|
2866
3139
|
const onChangedFile = (filePath) => {
|
|
2867
|
-
debounce.
|
|
3140
|
+
debounce.refresh();
|
|
2868
3141
|
let task = this.#watchedTestFiles.get(filePath);
|
|
2869
3142
|
if (task != null) {
|
|
2870
3143
|
this.#changedTestFiles.set(filePath, task);
|
|
@@ -2879,7 +3152,7 @@ class WatchService {
|
|
|
2879
3152
|
this.#changedTestFiles.delete(filePath);
|
|
2880
3153
|
this.#watchedTestFiles.delete(filePath);
|
|
2881
3154
|
if (this.#watchedTestFiles.size === 0) {
|
|
2882
|
-
debounce.
|
|
3155
|
+
debounce.cancel();
|
|
2883
3156
|
this.#onDiagnostics(Diagnostic.error(SelectDiagnosticText.noTestFilesWereLeft(this.#resolvedConfig)));
|
|
2884
3157
|
}
|
|
2885
3158
|
};
|
|
@@ -2892,7 +3165,7 @@ class WatchService {
|
|
|
2892
3165
|
watcher.watch();
|
|
2893
3166
|
}
|
|
2894
3167
|
while (!cancellationToken.isCancellationRequested) {
|
|
2895
|
-
const testFiles = await debounce.
|
|
3168
|
+
const testFiles = await debounce.schedule();
|
|
2896
3169
|
if (testFiles.length > 0) {
|
|
2897
3170
|
yield testFiles;
|
|
2898
3171
|
}
|
|
@@ -2900,276 +3173,7 @@ class WatchService {
|
|
|
2900
3173
|
}
|
|
2901
3174
|
}
|
|
2902
3175
|
|
|
2903
|
-
|
|
2904
|
-
(function (TestMemberBrand) {
|
|
2905
|
-
TestMemberBrand["Describe"] = "describe";
|
|
2906
|
-
TestMemberBrand["Test"] = "test";
|
|
2907
|
-
TestMemberBrand["Expect"] = "expect";
|
|
2908
|
-
})(TestMemberBrand || (TestMemberBrand = {}));
|
|
2909
|
-
|
|
2910
|
-
class TestMember {
|
|
2911
|
-
brand;
|
|
2912
|
-
#compiler;
|
|
2913
|
-
diagnostics = new Set();
|
|
2914
|
-
flags;
|
|
2915
|
-
members = [];
|
|
2916
|
-
name = "";
|
|
2917
|
-
node;
|
|
2918
|
-
parent;
|
|
2919
|
-
constructor(compiler, brand, node, parent, flags) {
|
|
2920
|
-
this.brand = brand;
|
|
2921
|
-
this.#compiler = compiler;
|
|
2922
|
-
this.node = node;
|
|
2923
|
-
this.parent = parent;
|
|
2924
|
-
this.flags = flags;
|
|
2925
|
-
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
2926
|
-
this.name = node.arguments[0].text;
|
|
2927
|
-
}
|
|
2928
|
-
if (node.arguments[1] != null &&
|
|
2929
|
-
compiler.isFunctionLike(node.arguments[1]) &&
|
|
2930
|
-
compiler.isBlock(node.arguments[1].body)) {
|
|
2931
|
-
const blockStart = node.arguments[1].body.getStart();
|
|
2932
|
-
const blockEnd = node.arguments[1].body.getEnd();
|
|
2933
|
-
for (const diagnostic of parent.diagnostics) {
|
|
2934
|
-
if (diagnostic.start != null && diagnostic.start >= blockStart && diagnostic.start <= blockEnd) {
|
|
2935
|
-
this.diagnostics.add(diagnostic);
|
|
2936
|
-
parent.diagnostics.delete(diagnostic);
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
}
|
|
2941
|
-
validate() {
|
|
2942
|
-
const diagnostics = [];
|
|
2943
|
-
const getText = (node) => `'${node.expression.getText()}()' cannot be nested within '${this.node.expression.getText()}()'.`;
|
|
2944
|
-
const getParentCallExpression = (node) => {
|
|
2945
|
-
while (!this.#compiler.isCallExpression(node.parent)) {
|
|
2946
|
-
node = node.parent;
|
|
2947
|
-
}
|
|
2948
|
-
return node.parent;
|
|
2949
|
-
};
|
|
2950
|
-
switch (this.brand) {
|
|
2951
|
-
case TestMemberBrand.Describe:
|
|
2952
|
-
for (const member of this.members) {
|
|
2953
|
-
if (member.brand === TestMemberBrand.Expect) {
|
|
2954
|
-
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(getParentCallExpression(member.node))));
|
|
2955
|
-
}
|
|
2956
|
-
}
|
|
2957
|
-
break;
|
|
2958
|
-
case TestMemberBrand.Test:
|
|
2959
|
-
case TestMemberBrand.Expect:
|
|
2960
|
-
for (const member of this.members) {
|
|
2961
|
-
if (member.brand !== TestMemberBrand.Expect) {
|
|
2962
|
-
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
|
|
2963
|
-
}
|
|
2964
|
-
}
|
|
2965
|
-
break;
|
|
2966
|
-
}
|
|
2967
|
-
return diagnostics;
|
|
2968
|
-
}
|
|
2969
|
-
}
|
|
2970
|
-
|
|
2971
|
-
class Assertion extends TestMember {
|
|
2972
|
-
isNot;
|
|
2973
|
-
matcherName;
|
|
2974
|
-
matcherNode;
|
|
2975
|
-
modifierNode;
|
|
2976
|
-
notNode;
|
|
2977
|
-
source;
|
|
2978
|
-
target;
|
|
2979
|
-
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
2980
|
-
super(compiler, brand, node, parent, flags);
|
|
2981
|
-
this.isNot = notNode != null;
|
|
2982
|
-
this.matcherName = matcherNode.expression.name;
|
|
2983
|
-
this.matcherNode = matcherNode;
|
|
2984
|
-
this.modifierNode = modifierNode;
|
|
2985
|
-
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
2986
|
-
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
2987
|
-
for (const diagnostic of parent.diagnostics) {
|
|
2988
|
-
if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
|
|
2989
|
-
this.diagnostics.add(diagnostic);
|
|
2990
|
-
parent.diagnostics.delete(diagnostic);
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
}
|
|
2995
|
-
|
|
2996
|
-
var TestMemberFlags;
|
|
2997
|
-
(function (TestMemberFlags) {
|
|
2998
|
-
TestMemberFlags[TestMemberFlags["None"] = 0] = "None";
|
|
2999
|
-
TestMemberFlags[TestMemberFlags["Fail"] = 1] = "Fail";
|
|
3000
|
-
TestMemberFlags[TestMemberFlags["Only"] = 2] = "Only";
|
|
3001
|
-
TestMemberFlags[TestMemberFlags["Skip"] = 4] = "Skip";
|
|
3002
|
-
TestMemberFlags[TestMemberFlags["Todo"] = 8] = "Todo";
|
|
3003
|
-
})(TestMemberFlags || (TestMemberFlags = {}));
|
|
3004
|
-
|
|
3005
|
-
class IdentifierLookup {
|
|
3006
|
-
#compiler;
|
|
3007
|
-
#identifiers;
|
|
3008
|
-
#moduleSpecifiers = ['"tstyche"', "'tstyche'"];
|
|
3009
|
-
constructor(compiler, identifiers) {
|
|
3010
|
-
this.#compiler = compiler;
|
|
3011
|
-
this.#identifiers = identifiers ?? {
|
|
3012
|
-
namedImports: {
|
|
3013
|
-
describe: undefined,
|
|
3014
|
-
expect: undefined,
|
|
3015
|
-
it: undefined,
|
|
3016
|
-
namespace: undefined,
|
|
3017
|
-
test: undefined,
|
|
3018
|
-
},
|
|
3019
|
-
namespace: undefined,
|
|
3020
|
-
};
|
|
3021
|
-
}
|
|
3022
|
-
handleImportDeclaration(node) {
|
|
3023
|
-
if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
|
|
3024
|
-
node.importClause?.isTypeOnly !== true &&
|
|
3025
|
-
node.importClause?.namedBindings != null) {
|
|
3026
|
-
if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
|
|
3027
|
-
for (const element of node.importClause.namedBindings.elements) {
|
|
3028
|
-
if (element.isTypeOnly) {
|
|
3029
|
-
continue;
|
|
3030
|
-
}
|
|
3031
|
-
let identifierKey;
|
|
3032
|
-
if (element.propertyName) {
|
|
3033
|
-
identifierKey = element.propertyName.getText();
|
|
3034
|
-
}
|
|
3035
|
-
else {
|
|
3036
|
-
identifierKey = element.name.getText();
|
|
3037
|
-
}
|
|
3038
|
-
if (identifierKey in this.#identifiers.namedImports) {
|
|
3039
|
-
this.#identifiers.namedImports[identifierKey] = element.name.getText();
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
}
|
|
3043
|
-
if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
|
|
3044
|
-
this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
|
|
3045
|
-
}
|
|
3046
|
-
}
|
|
3047
|
-
}
|
|
3048
|
-
resolveTestMemberMeta(node) {
|
|
3049
|
-
let flags = TestMemberFlags.None;
|
|
3050
|
-
let expression = node.expression;
|
|
3051
|
-
while (this.#compiler.isPropertyAccessExpression(expression)) {
|
|
3052
|
-
if (expression.expression.getText() === this.#identifiers.namespace) {
|
|
3053
|
-
break;
|
|
3054
|
-
}
|
|
3055
|
-
switch (expression.name.getText()) {
|
|
3056
|
-
case "fail":
|
|
3057
|
-
flags |= TestMemberFlags.Fail;
|
|
3058
|
-
break;
|
|
3059
|
-
case "only":
|
|
3060
|
-
flags |= TestMemberFlags.Only;
|
|
3061
|
-
break;
|
|
3062
|
-
case "skip":
|
|
3063
|
-
flags |= TestMemberFlags.Skip;
|
|
3064
|
-
break;
|
|
3065
|
-
case "todo":
|
|
3066
|
-
flags |= TestMemberFlags.Todo;
|
|
3067
|
-
break;
|
|
3068
|
-
}
|
|
3069
|
-
expression = expression.expression;
|
|
3070
|
-
}
|
|
3071
|
-
let identifierName;
|
|
3072
|
-
if (this.#compiler.isPropertyAccessExpression(expression) &&
|
|
3073
|
-
expression.expression.getText() === this.#identifiers.namespace) {
|
|
3074
|
-
identifierName = expression.name.getText();
|
|
3075
|
-
}
|
|
3076
|
-
else {
|
|
3077
|
-
identifierName = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
|
|
3078
|
-
}
|
|
3079
|
-
if (!identifierName) {
|
|
3080
|
-
return;
|
|
3081
|
-
}
|
|
3082
|
-
switch (identifierName) {
|
|
3083
|
-
case "describe":
|
|
3084
|
-
return { brand: TestMemberBrand.Describe, flags };
|
|
3085
|
-
case "it":
|
|
3086
|
-
case "test":
|
|
3087
|
-
return { brand: TestMemberBrand.Test, flags };
|
|
3088
|
-
case "expect":
|
|
3089
|
-
return { brand: TestMemberBrand.Expect, flags };
|
|
3090
|
-
}
|
|
3091
|
-
return;
|
|
3092
|
-
}
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3095
|
-
class TestTree {
|
|
3096
|
-
diagnostics;
|
|
3097
|
-
members = [];
|
|
3098
|
-
sourceFile;
|
|
3099
|
-
constructor(diagnostics, sourceFile) {
|
|
3100
|
-
this.diagnostics = diagnostics;
|
|
3101
|
-
this.sourceFile = sourceFile;
|
|
3102
|
-
}
|
|
3103
|
-
get hasOnly() {
|
|
3104
|
-
function hasOnly(root) {
|
|
3105
|
-
return root.members.some((branch) => branch.flags & TestMemberFlags.Only || ("members" in branch && hasOnly(branch)));
|
|
3106
|
-
}
|
|
3107
|
-
return hasOnly(this);
|
|
3108
|
-
}
|
|
3109
|
-
}
|
|
3110
|
-
|
|
3111
|
-
class CollectService {
|
|
3112
|
-
#compiler;
|
|
3113
|
-
constructor(compiler) {
|
|
3114
|
-
this.#compiler = compiler;
|
|
3115
|
-
}
|
|
3116
|
-
#collectTestMembers(node, identifiers, parent) {
|
|
3117
|
-
if (this.#compiler.isCallExpression(node)) {
|
|
3118
|
-
const meta = identifiers.resolveTestMemberMeta(node);
|
|
3119
|
-
if (meta != null && (meta.brand === TestMemberBrand.Describe || meta.brand === TestMemberBrand.Test)) {
|
|
3120
|
-
const testMember = new TestMember(this.#compiler, meta.brand, node, parent, meta.flags);
|
|
3121
|
-
parent.members.push(testMember);
|
|
3122
|
-
this.#compiler.forEachChild(node, (node) => {
|
|
3123
|
-
this.#collectTestMembers(node, identifiers, testMember);
|
|
3124
|
-
});
|
|
3125
|
-
return;
|
|
3126
|
-
}
|
|
3127
|
-
if (meta != null && meta.brand === TestMemberBrand.Expect) {
|
|
3128
|
-
const modifierNode = this.#getChainedNode(node, "type");
|
|
3129
|
-
if (!modifierNode) {
|
|
3130
|
-
return;
|
|
3131
|
-
}
|
|
3132
|
-
const notNode = this.#getChainedNode(modifierNode, "not");
|
|
3133
|
-
const matcherNode = this.#getChainedNode(notNode ?? modifierNode)?.parent;
|
|
3134
|
-
if (!matcherNode || !this.#isMatcherNode(matcherNode)) {
|
|
3135
|
-
return;
|
|
3136
|
-
}
|
|
3137
|
-
const assertion = new Assertion(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
|
|
3138
|
-
parent.members.push(assertion);
|
|
3139
|
-
this.#compiler.forEachChild(node, (node) => {
|
|
3140
|
-
this.#collectTestMembers(node, identifiers, assertion);
|
|
3141
|
-
});
|
|
3142
|
-
return;
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
if (this.#compiler.isImportDeclaration(node)) {
|
|
3146
|
-
identifiers.handleImportDeclaration(node);
|
|
3147
|
-
return;
|
|
3148
|
-
}
|
|
3149
|
-
this.#compiler.forEachChild(node, (node) => {
|
|
3150
|
-
this.#collectTestMembers(node, identifiers, parent);
|
|
3151
|
-
});
|
|
3152
|
-
}
|
|
3153
|
-
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
3154
|
-
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
3155
|
-
this.#collectTestMembers(sourceFile, new IdentifierLookup(this.#compiler), testTree);
|
|
3156
|
-
return testTree;
|
|
3157
|
-
}
|
|
3158
|
-
#getChainedNode({ parent }, name) {
|
|
3159
|
-
if (!this.#compiler.isPropertyAccessExpression(parent)) {
|
|
3160
|
-
return;
|
|
3161
|
-
}
|
|
3162
|
-
if (name != null && name !== parent.name.getText()) {
|
|
3163
|
-
return;
|
|
3164
|
-
}
|
|
3165
|
-
return parent;
|
|
3166
|
-
}
|
|
3167
|
-
#isMatcherNode(node) {
|
|
3168
|
-
return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
|
|
3172
|
-
class ProjectService {
|
|
3176
|
+
class ProjectService {
|
|
3173
3177
|
#compiler;
|
|
3174
3178
|
#lastSeenProject = "";
|
|
3175
3179
|
#resolvedConfig;
|
|
@@ -3227,24 +3231,19 @@ class ProjectService {
|
|
|
3227
3231
|
}
|
|
3228
3232
|
#getDefaultCompilerOptions() {
|
|
3229
3233
|
const defaultCompilerOptions = {
|
|
3234
|
+
allowImportingTsExtensions: true,
|
|
3230
3235
|
allowJs: true,
|
|
3231
3236
|
checkJs: true,
|
|
3232
|
-
|
|
3233
|
-
jsx:
|
|
3234
|
-
module:
|
|
3235
|
-
moduleResolution:
|
|
3237
|
+
exactOptionalPropertyTypes: true,
|
|
3238
|
+
jsx: this.#compiler.JsxEmit.Preserve,
|
|
3239
|
+
module: this.#compiler.ModuleKind.NodeNext,
|
|
3240
|
+
moduleResolution: this.#compiler.ModuleResolutionKind.NodeNext,
|
|
3241
|
+
noUncheckedIndexedAccess: true,
|
|
3236
3242
|
resolveJsonModule: true,
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3243
|
+
strict: true,
|
|
3244
|
+
target: this.#compiler.ScriptTarget.ESNext,
|
|
3245
|
+
verbatimModuleSyntax: true,
|
|
3240
3246
|
};
|
|
3241
|
-
if (Version.isSatisfiedWith(this.#compiler.version, "5.4")) {
|
|
3242
|
-
defaultCompilerOptions.module = "preserve";
|
|
3243
|
-
}
|
|
3244
|
-
if (Version.isSatisfiedWith(this.#compiler.version, "5.0")) {
|
|
3245
|
-
defaultCompilerOptions.allowImportingTsExtensions = true;
|
|
3246
|
-
defaultCompilerOptions.moduleResolution = "bundler";
|
|
3247
|
-
}
|
|
3248
3247
|
return defaultCompilerOptions;
|
|
3249
3248
|
}
|
|
3250
3249
|
getDefaultProject(filePath) {
|
|
@@ -3336,12 +3335,6 @@ class ExpectDiagnosticText {
|
|
|
3336
3335
|
static componentDoesNotAcceptProps(isTypeNode) {
|
|
3337
3336
|
return `${isTypeNode ? "Component type" : "Component"} does not accept props of the given type.`;
|
|
3338
3337
|
}
|
|
3339
|
-
static matcherIsDeprecated(matcherNameText) {
|
|
3340
|
-
return [
|
|
3341
|
-
`The '.${matcherNameText}()' matcher is deprecated and will be removed in TSTyche 4.`,
|
|
3342
|
-
"To learn more, visit https://tstyche.org/releases/tstyche-3",
|
|
3343
|
-
];
|
|
3344
|
-
}
|
|
3345
3338
|
static matcherIsNotSupported(matcherNameText) {
|
|
3346
3339
|
return `The '.${matcherNameText}()' matcher is not supported.`;
|
|
3347
3340
|
}
|
|
@@ -3366,18 +3359,9 @@ class ExpectDiagnosticText {
|
|
|
3366
3359
|
static typeDoesNotHaveProperty(typeText, propertyNameText) {
|
|
3367
3360
|
return `Type '${typeText}' does not have property '${propertyNameText}'.`;
|
|
3368
3361
|
}
|
|
3369
|
-
static typeDoesMatch(sourceTypeText, targetTypeText) {
|
|
3370
|
-
return `Type '${sourceTypeText}' does match type '${targetTypeText}'.`;
|
|
3371
|
-
}
|
|
3372
|
-
static typeDoesNotMatch(sourceTypeText, targetTypeText) {
|
|
3373
|
-
return `Type '${sourceTypeText}' does not match type '${targetTypeText}'.`;
|
|
3374
|
-
}
|
|
3375
3362
|
static typeHasProperty(typeText, propertyNameText) {
|
|
3376
3363
|
return `Type '${typeText}' has property '${propertyNameText}'.`;
|
|
3377
3364
|
}
|
|
3378
|
-
static typeIs(typeText) {
|
|
3379
|
-
return `Type is '${typeText}'.`;
|
|
3380
|
-
}
|
|
3381
3365
|
static typeIsAssignableTo(sourceTypeText, targetTypeText) {
|
|
3382
3366
|
return `Type '${sourceTypeText}' is assignable to type '${targetTypeText}'.`;
|
|
3383
3367
|
}
|
|
@@ -3438,18 +3422,9 @@ class MatchWorker {
|
|
|
3438
3422
|
checkHasApplicableIndexType(sourceNode, targetNode) {
|
|
3439
3423
|
const sourceType = this.getType(sourceNode);
|
|
3440
3424
|
const targetType = this.getType(targetNode);
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
.some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
|
|
3445
|
-
}
|
|
3446
|
-
if (targetType.flags & this.#compiler.TypeFlags.StringLiteral) {
|
|
3447
|
-
return sourceType.getStringIndexType() != null;
|
|
3448
|
-
}
|
|
3449
|
-
if (targetType.flags & this.#compiler.TypeFlags.NumberLiteral) {
|
|
3450
|
-
return (sourceType.getStringIndexType() ?? sourceType.getNumberIndexType()) != null;
|
|
3451
|
-
}
|
|
3452
|
-
return false;
|
|
3425
|
+
return this.#typeChecker
|
|
3426
|
+
.getIndexInfosOfType(sourceType)
|
|
3427
|
+
.some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
|
|
3453
3428
|
}
|
|
3454
3429
|
checkHasProperty(sourceNode, propertyNameText) {
|
|
3455
3430
|
const sourceType = this.getType(sourceNode);
|
|
@@ -3471,10 +3446,6 @@ class MatchWorker {
|
|
|
3471
3446
|
this.checkIsAssignableTo(sourceNode, targetNode) &&
|
|
3472
3447
|
this.checkIsAssignableWith(sourceNode, targetNode));
|
|
3473
3448
|
}
|
|
3474
|
-
checkIsSubtype(sourceNode, targetNode) {
|
|
3475
|
-
const relation = this.#typeChecker.relation.subtype;
|
|
3476
|
-
return this.#checkIsRelatedTo(sourceNode, targetNode, relation);
|
|
3477
|
-
}
|
|
3478
3449
|
#checkIsRelatedTo(sourceNode, targetNode, relation) {
|
|
3479
3450
|
const sourceType = relation === this.#typeChecker.relation.identity
|
|
3480
3451
|
? this.#simplifyType(this.getType(sourceNode))
|
|
@@ -3561,26 +3532,6 @@ class MatchWorker {
|
|
|
3561
3532
|
}
|
|
3562
3533
|
}
|
|
3563
3534
|
|
|
3564
|
-
class PrimitiveTypeMatcher {
|
|
3565
|
-
#targetTypeFlag;
|
|
3566
|
-
constructor(targetTypeFlag) {
|
|
3567
|
-
this.#targetTypeFlag = targetTypeFlag;
|
|
3568
|
-
}
|
|
3569
|
-
#explain(matchWorker, sourceNode) {
|
|
3570
|
-
const sourceTypeText = matchWorker.getTypeText(sourceNode);
|
|
3571
|
-
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
3572
|
-
return [Diagnostic.error(ExpectDiagnosticText.typeIs(sourceTypeText), origin)];
|
|
3573
|
-
}
|
|
3574
|
-
match(matchWorker, sourceNode) {
|
|
3575
|
-
const sourceType = matchWorker.getType(sourceNode);
|
|
3576
|
-
const isMatch = !!(sourceType.flags & this.#targetTypeFlag);
|
|
3577
|
-
return {
|
|
3578
|
-
explain: () => this.#explain(matchWorker, sourceNode),
|
|
3579
|
-
isMatch,
|
|
3580
|
-
};
|
|
3581
|
-
}
|
|
3582
|
-
}
|
|
3583
|
-
|
|
3584
3535
|
class ToAcceptProps {
|
|
3585
3536
|
#compiler;
|
|
3586
3537
|
#typeChecker;
|
|
@@ -3868,17 +3819,6 @@ class ToHaveProperty {
|
|
|
3868
3819
|
}
|
|
3869
3820
|
}
|
|
3870
3821
|
|
|
3871
|
-
class ToMatch extends RelationMatcherBase {
|
|
3872
|
-
explainText = ExpectDiagnosticText.typeDoesMatch;
|
|
3873
|
-
explainNotText = ExpectDiagnosticText.typeDoesNotMatch;
|
|
3874
|
-
match(matchWorker, sourceNode, targetNode) {
|
|
3875
|
-
return {
|
|
3876
|
-
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
3877
|
-
isMatch: matchWorker.checkIsSubtype(sourceNode, targetNode),
|
|
3878
|
-
};
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
3881
|
-
|
|
3882
3822
|
class ToRaiseError {
|
|
3883
3823
|
#compiler;
|
|
3884
3824
|
constructor(compiler) {
|
|
@@ -3948,14 +3888,17 @@ class ToRaiseError {
|
|
|
3948
3888
|
};
|
|
3949
3889
|
}
|
|
3950
3890
|
#matchExpectedError(diagnostic, targetNode) {
|
|
3891
|
+
if (this.#compiler.isNumericLiteral(targetNode)) {
|
|
3892
|
+
return Number.parseInt(targetNode.text, 10) === diagnostic.code;
|
|
3893
|
+
}
|
|
3894
|
+
const messageText = typeof diagnostic.messageText === "string"
|
|
3895
|
+
? diagnostic.messageText
|
|
3896
|
+
: Diagnostic.toMessageText(diagnostic.messageText).join("\n");
|
|
3951
3897
|
if (this.#compiler.isRegularExpressionLiteral(targetNode)) {
|
|
3952
3898
|
const targetRegex = new RegExp(...targetNode.text.slice(1).split("/"));
|
|
3953
|
-
return targetRegex.test(
|
|
3954
|
-
}
|
|
3955
|
-
if (this.#compiler.isStringLiteralLike(targetNode)) {
|
|
3956
|
-
return this.#compiler.flattenDiagnosticMessageText(diagnostic.messageText, " ", 0).includes(targetNode.text);
|
|
3899
|
+
return targetRegex.test(messageText);
|
|
3957
3900
|
}
|
|
3958
|
-
return
|
|
3901
|
+
return messageText.includes(targetNode.text);
|
|
3959
3902
|
}
|
|
3960
3903
|
}
|
|
3961
3904
|
|
|
@@ -3965,22 +3908,9 @@ class ExpectService {
|
|
|
3965
3908
|
#typeChecker;
|
|
3966
3909
|
toAcceptProps;
|
|
3967
3910
|
toBe;
|
|
3968
|
-
toBeAny;
|
|
3969
3911
|
toBeAssignableTo;
|
|
3970
3912
|
toBeAssignableWith;
|
|
3971
|
-
toBeBigInt;
|
|
3972
|
-
toBeBoolean;
|
|
3973
|
-
toBeNever;
|
|
3974
|
-
toBeNull;
|
|
3975
|
-
toBeNumber;
|
|
3976
|
-
toBeString;
|
|
3977
|
-
toBeSymbol;
|
|
3978
|
-
toBeUndefined;
|
|
3979
|
-
toBeUniqueSymbol;
|
|
3980
|
-
toBeUnknown;
|
|
3981
|
-
toBeVoid;
|
|
3982
3913
|
toHaveProperty;
|
|
3983
|
-
toMatch;
|
|
3984
3914
|
toRaiseError;
|
|
3985
3915
|
constructor(compiler, typeChecker, resolvedConfig) {
|
|
3986
3916
|
this.#compiler = compiler;
|
|
@@ -3993,31 +3923,13 @@ class ExpectService {
|
|
|
3993
3923
|
}
|
|
3994
3924
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
3995
3925
|
this.toBe = new ToBe();
|
|
3996
|
-
this.toBeAny = new PrimitiveTypeMatcher(compiler.TypeFlags.Any);
|
|
3997
3926
|
this.toBeAssignableTo = new ToBeAssignableTo();
|
|
3998
3927
|
this.toBeAssignableWith = new ToBeAssignableWith();
|
|
3999
|
-
this.toBeBigInt = new PrimitiveTypeMatcher(compiler.TypeFlags.BigInt);
|
|
4000
|
-
this.toBeBoolean = new PrimitiveTypeMatcher(compiler.TypeFlags.Boolean);
|
|
4001
|
-
this.toBeNever = new PrimitiveTypeMatcher(compiler.TypeFlags.Never);
|
|
4002
|
-
this.toBeNull = new PrimitiveTypeMatcher(compiler.TypeFlags.Null);
|
|
4003
|
-
this.toBeNumber = new PrimitiveTypeMatcher(compiler.TypeFlags.Number);
|
|
4004
|
-
this.toBeString = new PrimitiveTypeMatcher(compiler.TypeFlags.String);
|
|
4005
|
-
this.toBeSymbol = new PrimitiveTypeMatcher(compiler.TypeFlags.ESSymbol);
|
|
4006
|
-
this.toBeUndefined = new PrimitiveTypeMatcher(compiler.TypeFlags.Undefined);
|
|
4007
|
-
this.toBeUniqueSymbol = new PrimitiveTypeMatcher(compiler.TypeFlags.UniqueESSymbol);
|
|
4008
|
-
this.toBeUnknown = new PrimitiveTypeMatcher(compiler.TypeFlags.Unknown);
|
|
4009
|
-
this.toBeVoid = new PrimitiveTypeMatcher(compiler.TypeFlags.Void);
|
|
4010
3928
|
this.toHaveProperty = new ToHaveProperty(compiler);
|
|
4011
|
-
this.toMatch = new ToMatch();
|
|
4012
3929
|
this.toRaiseError = new ToRaiseError(compiler);
|
|
4013
3930
|
}
|
|
4014
3931
|
match(assertion, onDiagnostics) {
|
|
4015
3932
|
const matcherNameText = assertion.matcherName.getText();
|
|
4016
|
-
if (matcherNameText === "toMatch") {
|
|
4017
|
-
const text = ExpectDiagnosticText.matcherIsDeprecated(matcherNameText);
|
|
4018
|
-
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
4019
|
-
EventEmitter.dispatch(["deprecation:info", { diagnostics: [Diagnostic.warning(text, origin)] }]);
|
|
4020
|
-
}
|
|
4021
3933
|
if (!assertion.source[0]) {
|
|
4022
3934
|
this.#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
4023
3935
|
return;
|
|
@@ -4028,7 +3940,6 @@ class ExpectService {
|
|
|
4028
3940
|
case "toBe":
|
|
4029
3941
|
case "toBeAssignableTo":
|
|
4030
3942
|
case "toBeAssignableWith":
|
|
4031
|
-
case "toMatch":
|
|
4032
3943
|
if (!assertion.target[0]) {
|
|
4033
3944
|
this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
4034
3945
|
return;
|
|
@@ -4037,19 +3948,6 @@ class ExpectService {
|
|
|
4037
3948
|
return;
|
|
4038
3949
|
}
|
|
4039
3950
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
4040
|
-
case "toBeAny":
|
|
4041
|
-
case "toBeBigInt":
|
|
4042
|
-
case "toBeBoolean":
|
|
4043
|
-
case "toBeNever":
|
|
4044
|
-
case "toBeNull":
|
|
4045
|
-
case "toBeNumber":
|
|
4046
|
-
case "toBeString":
|
|
4047
|
-
case "toBeSymbol":
|
|
4048
|
-
case "toBeUndefined":
|
|
4049
|
-
case "toBeUniqueSymbol":
|
|
4050
|
-
case "toBeUnknown":
|
|
4051
|
-
case "toBeVoid":
|
|
4052
|
-
return this[matcherNameText].match(matchWorker, assertion.source[0]);
|
|
4053
3951
|
case "toHaveProperty":
|
|
4054
3952
|
if (!assertion.target[0]) {
|
|
4055
3953
|
this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
|
|
@@ -4088,10 +3986,14 @@ class ExpectService {
|
|
|
4088
3986
|
}
|
|
4089
3987
|
#rejectsTypeArguments(matchWorker, onDiagnostics) {
|
|
4090
3988
|
for (const rejectedType of this.#rejectTypes) {
|
|
3989
|
+
const allowedKeyword = this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`];
|
|
3990
|
+
if (matchWorker.assertion.source[0]?.kind === allowedKeyword ||
|
|
3991
|
+
matchWorker.assertion.target[0]?.kind === allowedKeyword) {
|
|
3992
|
+
continue;
|
|
3993
|
+
}
|
|
4091
3994
|
for (const argumentName of ["source", "target"]) {
|
|
4092
3995
|
const argumentNode = matchWorker.assertion[argumentName][0];
|
|
4093
|
-
if (!argumentNode
|
|
4094
|
-
argumentNode.kind === this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`]) {
|
|
3996
|
+
if (!argumentNode) {
|
|
4095
3997
|
continue;
|
|
4096
3998
|
}
|
|
4097
3999
|
if (matchWorker.getType(argumentNode).flags & this.#compiler.TypeFlags[Format.capitalize(rejectedType)]) {
|
|
@@ -4112,7 +4014,6 @@ class ExpectService {
|
|
|
4112
4014
|
}
|
|
4113
4015
|
|
|
4114
4016
|
class TestTreeWalker {
|
|
4115
|
-
#compiler;
|
|
4116
4017
|
#cancellationToken;
|
|
4117
4018
|
#expectService;
|
|
4118
4019
|
#hasOnly;
|
|
@@ -4121,59 +4022,60 @@ class TestTreeWalker {
|
|
|
4121
4022
|
#taskResult;
|
|
4122
4023
|
constructor(resolvedConfig, compiler, typeChecker, options) {
|
|
4123
4024
|
this.#resolvedConfig = resolvedConfig;
|
|
4124
|
-
this.#compiler = compiler;
|
|
4125
4025
|
this.#cancellationToken = options.cancellationToken;
|
|
4126
4026
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
4127
4027
|
this.#position = options.position;
|
|
4128
4028
|
this.#taskResult = options.taskResult;
|
|
4129
4029
|
this.#expectService = new ExpectService(compiler, typeChecker, this.#resolvedConfig);
|
|
4130
4030
|
}
|
|
4131
|
-
#resolveRunMode(mode,
|
|
4132
|
-
if (
|
|
4031
|
+
#resolveRunMode(mode, testNode) {
|
|
4032
|
+
if (testNode.flags & TestTreeNodeFlags.Fail) {
|
|
4133
4033
|
mode |= RunMode.Fail;
|
|
4134
4034
|
}
|
|
4135
|
-
if (
|
|
4136
|
-
(this.#resolvedConfig.only != null &&
|
|
4035
|
+
if (testNode.flags & TestTreeNodeFlags.Only ||
|
|
4036
|
+
(this.#resolvedConfig.only != null &&
|
|
4037
|
+
testNode.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
|
|
4137
4038
|
mode |= RunMode.Only;
|
|
4138
4039
|
}
|
|
4139
|
-
if (
|
|
4140
|
-
(this.#resolvedConfig.skip != null &&
|
|
4040
|
+
if (testNode.flags & TestTreeNodeFlags.Skip ||
|
|
4041
|
+
(this.#resolvedConfig.skip != null &&
|
|
4042
|
+
testNode.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
|
|
4141
4043
|
mode |= RunMode.Skip;
|
|
4142
4044
|
}
|
|
4143
|
-
if (
|
|
4045
|
+
if (testNode.flags & TestTreeNodeFlags.Todo) {
|
|
4144
4046
|
mode |= RunMode.Todo;
|
|
4145
4047
|
}
|
|
4146
|
-
if (this.#position != null &&
|
|
4048
|
+
if (this.#position != null && testNode.node.getStart() === this.#position) {
|
|
4147
4049
|
mode |= RunMode.Only;
|
|
4148
4050
|
mode &= ~RunMode.Skip;
|
|
4149
4051
|
}
|
|
4150
4052
|
return mode;
|
|
4151
4053
|
}
|
|
4152
|
-
visit(
|
|
4153
|
-
for (const
|
|
4054
|
+
visit(testNodes, runMode, parentResult) {
|
|
4055
|
+
for (const testNode of testNodes) {
|
|
4154
4056
|
if (this.#cancellationToken?.isCancellationRequested) {
|
|
4155
4057
|
break;
|
|
4156
4058
|
}
|
|
4157
|
-
const validationError =
|
|
4059
|
+
const validationError = testNode.validate();
|
|
4158
4060
|
if (validationError.length > 0) {
|
|
4159
4061
|
EventEmitter.dispatch(["task:error", { diagnostics: validationError, result: this.#taskResult }]);
|
|
4160
4062
|
break;
|
|
4161
4063
|
}
|
|
4162
|
-
switch (
|
|
4163
|
-
case
|
|
4164
|
-
this.#visitDescribe(
|
|
4064
|
+
switch (testNode.brand) {
|
|
4065
|
+
case TestTreeNodeBrand.Describe:
|
|
4066
|
+
this.#visitDescribe(testNode, runMode, parentResult);
|
|
4165
4067
|
break;
|
|
4166
|
-
case
|
|
4167
|
-
this.#visitTest(
|
|
4068
|
+
case TestTreeNodeBrand.Test:
|
|
4069
|
+
this.#visitTest(testNode, runMode, parentResult);
|
|
4168
4070
|
break;
|
|
4169
|
-
case
|
|
4170
|
-
this.#visitAssertion(
|
|
4071
|
+
case TestTreeNodeBrand.Expect:
|
|
4072
|
+
this.#visitAssertion(testNode, runMode, parentResult);
|
|
4171
4073
|
break;
|
|
4172
4074
|
}
|
|
4173
4075
|
}
|
|
4174
4076
|
}
|
|
4175
4077
|
#visitAssertion(assertion, runMode, parentResult) {
|
|
4176
|
-
this.visit(assertion.
|
|
4078
|
+
this.visit(assertion.children, runMode, parentResult);
|
|
4177
4079
|
const expectResult = new ExpectResult(assertion, parentResult);
|
|
4178
4080
|
EventEmitter.dispatch(["expect:start", { result: expectResult }]);
|
|
4179
4081
|
runMode = this.#resolveRunMode(runMode, assertion);
|
|
@@ -4227,7 +4129,7 @@ class TestTreeWalker {
|
|
|
4227
4129
|
]);
|
|
4228
4130
|
}
|
|
4229
4131
|
else {
|
|
4230
|
-
this.visit(describe.
|
|
4132
|
+
this.visit(describe.children, runMode, describeResult);
|
|
4231
4133
|
}
|
|
4232
4134
|
EventEmitter.dispatch(["describe:end", { result: describeResult }]);
|
|
4233
4135
|
}
|
|
@@ -4249,7 +4151,7 @@ class TestTreeWalker {
|
|
|
4249
4151
|
]);
|
|
4250
4152
|
return;
|
|
4251
4153
|
}
|
|
4252
|
-
this.visit(test.
|
|
4154
|
+
this.visit(test.children, runMode, testResult);
|
|
4253
4155
|
if (runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) {
|
|
4254
4156
|
EventEmitter.dispatch(["test:skip", { result: testResult }]);
|
|
4255
4157
|
return;
|
|
@@ -4329,17 +4231,27 @@ class TaskRunner {
|
|
|
4329
4231
|
hasOnly: testTree.hasOnly,
|
|
4330
4232
|
position: task.position,
|
|
4331
4233
|
});
|
|
4332
|
-
testTreeWalker.visit(testTree.
|
|
4234
|
+
testTreeWalker.visit(testTree.children, RunMode.Normal, undefined);
|
|
4333
4235
|
}
|
|
4334
4236
|
}
|
|
4335
4237
|
|
|
4336
4238
|
class Runner {
|
|
4337
4239
|
#eventEmitter = new EventEmitter();
|
|
4338
4240
|
#resolvedConfig;
|
|
4339
|
-
static version = "
|
|
4241
|
+
static version = "4.0.0-beta.0";
|
|
4340
4242
|
constructor(resolvedConfig) {
|
|
4341
4243
|
this.#resolvedConfig = resolvedConfig;
|
|
4342
4244
|
}
|
|
4245
|
+
#addHandlers(cancellationToken) {
|
|
4246
|
+
const resultHandler = new ResultHandler();
|
|
4247
|
+
this.#eventEmitter.addHandler(resultHandler);
|
|
4248
|
+
const testTreeHandler = new TestTreeHandler();
|
|
4249
|
+
this.#eventEmitter.addHandler(testTreeHandler);
|
|
4250
|
+
if (this.#resolvedConfig.failFast) {
|
|
4251
|
+
const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
|
|
4252
|
+
this.#eventEmitter.addHandler(cancellationHandler);
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4343
4255
|
async #addReporters() {
|
|
4344
4256
|
if (this.#resolvedConfig.watch && !environmentOptions.noInteractive) {
|
|
4345
4257
|
const watchReporter = new WatchReporter(this.#resolvedConfig);
|
|
@@ -4367,13 +4279,8 @@ class Runner {
|
|
|
4367
4279
|
}
|
|
4368
4280
|
async run(testFiles, cancellationToken = new CancellationToken()) {
|
|
4369
4281
|
const tasks = testFiles.map((testFile) => (testFile instanceof Task ? testFile : new Task(testFile)));
|
|
4370
|
-
|
|
4371
|
-
this.#eventEmitter.addHandler(resultHandler);
|
|
4282
|
+
this.#addHandlers(cancellationToken);
|
|
4372
4283
|
await this.#addReporters();
|
|
4373
|
-
if (this.#resolvedConfig.failFast) {
|
|
4374
|
-
const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
|
|
4375
|
-
this.#eventEmitter.addHandler(cancellationHandler);
|
|
4376
|
-
}
|
|
4377
4284
|
await this.#run(tasks, cancellationToken);
|
|
4378
4285
|
if (this.#resolvedConfig.watch) {
|
|
4379
4286
|
await this.#watch(tasks, cancellationToken);
|
|
@@ -4476,9 +4383,9 @@ class Cli {
|
|
|
4476
4383
|
OutputService.writeMessage(formattedText({ ...resolvedConfig, ...environmentOptions }));
|
|
4477
4384
|
continue;
|
|
4478
4385
|
}
|
|
4479
|
-
if (commandLine.includes("--
|
|
4386
|
+
if (commandLine.includes("--fetch")) {
|
|
4480
4387
|
for (const tag of resolvedConfig.target) {
|
|
4481
|
-
await Store.
|
|
4388
|
+
await Store.fetch(tag);
|
|
4482
4389
|
}
|
|
4483
4390
|
continue;
|
|
4484
4391
|
}
|
|
@@ -4533,4 +4440,4 @@ class Cli {
|
|
|
4533
4440
|
}
|
|
4534
4441
|
}
|
|
4535
4442
|
|
|
4536
|
-
export {
|
|
4443
|
+
export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeHandler, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|