tstyche 3.5.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 +51 -60
- package/build/tstyche.js +393 -493
- 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,
|
|
@@ -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
|
}
|
|
@@ -2150,7 +2427,7 @@ function CommandLineUsageText() {
|
|
|
2150
2427
|
const usage = [
|
|
2151
2428
|
["tstyche", "Run all tests."],
|
|
2152
2429
|
["tstyche path/to/first.test.ts", "Only run the test files with matching path."],
|
|
2153
|
-
["tstyche --target
|
|
2430
|
+
["tstyche --target 5.3,5.6.2,current", "Test on all specified versions of TypeScript."],
|
|
2154
2431
|
];
|
|
2155
2432
|
const usageText = usage.map(([commandText, descriptionText]) => (jsx(Line, { children: [jsx(CommandText, { text: commandText }), jsx(OptionDescriptionText, { text: descriptionText })] })));
|
|
2156
2433
|
return jsx(Text, { children: usageText });
|
|
@@ -2363,21 +2640,11 @@ class ListReporter extends BaseReporter {
|
|
|
2363
2640
|
#hasReportedError = false;
|
|
2364
2641
|
#hasReportedUses = false;
|
|
2365
2642
|
#isFileViewExpanded = false;
|
|
2366
|
-
#seenDeprecations = new Set();
|
|
2367
2643
|
get #isLastFile() {
|
|
2368
2644
|
return this.#fileCount === 0;
|
|
2369
2645
|
}
|
|
2370
2646
|
on([event, payload]) {
|
|
2371
2647
|
switch (event) {
|
|
2372
|
-
case "deprecation:info": {
|
|
2373
|
-
for (const diagnostic of payload.diagnostics) {
|
|
2374
|
-
if (!this.#seenDeprecations.has(diagnostic.text.toString())) {
|
|
2375
|
-
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
2376
|
-
this.#seenDeprecations.add(diagnostic.text.toString());
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
break;
|
|
2380
|
-
}
|
|
2381
2648
|
case "run:start":
|
|
2382
2649
|
this.#isFileViewExpanded = payload.result.tasks.length === 1 && this.resolvedConfig.watch !== true;
|
|
2383
2650
|
break;
|
|
@@ -2429,7 +2696,6 @@ class ListReporter extends BaseReporter {
|
|
|
2429
2696
|
this.#hasReportedError = true;
|
|
2430
2697
|
}
|
|
2431
2698
|
this.#fileView.clear();
|
|
2432
|
-
this.#seenDeprecations.clear();
|
|
2433
2699
|
break;
|
|
2434
2700
|
case "describe:start":
|
|
2435
2701
|
if (this.#isFileViewExpanded) {
|
|
@@ -2800,19 +3066,19 @@ class Debounce {
|
|
|
2800
3066
|
this.#delay = delay;
|
|
2801
3067
|
this.#onResolve = onResolve;
|
|
2802
3068
|
}
|
|
2803
|
-
|
|
3069
|
+
cancel() {
|
|
2804
3070
|
clearTimeout(this.#timeout);
|
|
2805
3071
|
}
|
|
2806
|
-
|
|
2807
|
-
this.
|
|
3072
|
+
refresh() {
|
|
3073
|
+
this.cancel();
|
|
2808
3074
|
this.#timeout = setTimeout(() => {
|
|
2809
3075
|
this.#resolve?.(this.#onResolve());
|
|
2810
3076
|
}, this.#delay);
|
|
2811
3077
|
}
|
|
2812
|
-
|
|
3078
|
+
resolve(value) {
|
|
2813
3079
|
this.#resolve?.(value);
|
|
2814
3080
|
}
|
|
2815
|
-
|
|
3081
|
+
schedule() {
|
|
2816
3082
|
return new Promise((resolve) => {
|
|
2817
3083
|
this.#resolve = resolve;
|
|
2818
3084
|
});
|
|
@@ -2840,13 +3106,13 @@ class WatchService {
|
|
|
2840
3106
|
};
|
|
2841
3107
|
const debounce = new Debounce(100, onResolve);
|
|
2842
3108
|
const onClose = (reason) => {
|
|
2843
|
-
debounce.
|
|
3109
|
+
debounce.cancel();
|
|
2844
3110
|
this.#inputService?.close();
|
|
2845
3111
|
for (const watcher of this.#watchers) {
|
|
2846
3112
|
watcher.close();
|
|
2847
3113
|
}
|
|
2848
3114
|
cancellationToken.cancel(reason);
|
|
2849
|
-
debounce.
|
|
3115
|
+
debounce.resolve([]);
|
|
2850
3116
|
};
|
|
2851
3117
|
if (!environmentOptions.noInteractive) {
|
|
2852
3118
|
const onInput = (chunk) => {
|
|
@@ -2861,9 +3127,9 @@ class WatchService {
|
|
|
2861
3127
|
case "\u000D":
|
|
2862
3128
|
case "\u0020":
|
|
2863
3129
|
case "a":
|
|
2864
|
-
debounce.
|
|
3130
|
+
debounce.cancel();
|
|
2865
3131
|
if (this.#watchedTestFiles.size > 0) {
|
|
2866
|
-
debounce.
|
|
3132
|
+
debounce.resolve([...this.#watchedTestFiles.values()]);
|
|
2867
3133
|
}
|
|
2868
3134
|
break;
|
|
2869
3135
|
}
|
|
@@ -2871,7 +3137,7 @@ class WatchService {
|
|
|
2871
3137
|
this.#inputService = new InputService(onInput);
|
|
2872
3138
|
}
|
|
2873
3139
|
const onChangedFile = (filePath) => {
|
|
2874
|
-
debounce.
|
|
3140
|
+
debounce.refresh();
|
|
2875
3141
|
let task = this.#watchedTestFiles.get(filePath);
|
|
2876
3142
|
if (task != null) {
|
|
2877
3143
|
this.#changedTestFiles.set(filePath, task);
|
|
@@ -2886,7 +3152,7 @@ class WatchService {
|
|
|
2886
3152
|
this.#changedTestFiles.delete(filePath);
|
|
2887
3153
|
this.#watchedTestFiles.delete(filePath);
|
|
2888
3154
|
if (this.#watchedTestFiles.size === 0) {
|
|
2889
|
-
debounce.
|
|
3155
|
+
debounce.cancel();
|
|
2890
3156
|
this.#onDiagnostics(Diagnostic.error(SelectDiagnosticText.noTestFilesWereLeft(this.#resolvedConfig)));
|
|
2891
3157
|
}
|
|
2892
3158
|
};
|
|
@@ -2899,7 +3165,7 @@ class WatchService {
|
|
|
2899
3165
|
watcher.watch();
|
|
2900
3166
|
}
|
|
2901
3167
|
while (!cancellationToken.isCancellationRequested) {
|
|
2902
|
-
const testFiles = await debounce.
|
|
3168
|
+
const testFiles = await debounce.schedule();
|
|
2903
3169
|
if (testFiles.length > 0) {
|
|
2904
3170
|
yield testFiles;
|
|
2905
3171
|
}
|
|
@@ -2907,276 +3173,7 @@ class WatchService {
|
|
|
2907
3173
|
}
|
|
2908
3174
|
}
|
|
2909
3175
|
|
|
2910
|
-
|
|
2911
|
-
(function (TestMemberBrand) {
|
|
2912
|
-
TestMemberBrand["Describe"] = "describe";
|
|
2913
|
-
TestMemberBrand["Test"] = "test";
|
|
2914
|
-
TestMemberBrand["Expect"] = "expect";
|
|
2915
|
-
})(TestMemberBrand || (TestMemberBrand = {}));
|
|
2916
|
-
|
|
2917
|
-
class TestMember {
|
|
2918
|
-
brand;
|
|
2919
|
-
#compiler;
|
|
2920
|
-
diagnostics = new Set();
|
|
2921
|
-
flags;
|
|
2922
|
-
members = [];
|
|
2923
|
-
name = "";
|
|
2924
|
-
node;
|
|
2925
|
-
parent;
|
|
2926
|
-
constructor(compiler, brand, node, parent, flags) {
|
|
2927
|
-
this.brand = brand;
|
|
2928
|
-
this.#compiler = compiler;
|
|
2929
|
-
this.node = node;
|
|
2930
|
-
this.parent = parent;
|
|
2931
|
-
this.flags = flags;
|
|
2932
|
-
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
2933
|
-
this.name = node.arguments[0].text;
|
|
2934
|
-
}
|
|
2935
|
-
if (node.arguments[1] != null &&
|
|
2936
|
-
compiler.isFunctionLike(node.arguments[1]) &&
|
|
2937
|
-
compiler.isBlock(node.arguments[1].body)) {
|
|
2938
|
-
const blockStart = node.arguments[1].body.getStart();
|
|
2939
|
-
const blockEnd = node.arguments[1].body.getEnd();
|
|
2940
|
-
for (const diagnostic of parent.diagnostics) {
|
|
2941
|
-
if (diagnostic.start != null && diagnostic.start >= blockStart && diagnostic.start <= blockEnd) {
|
|
2942
|
-
this.diagnostics.add(diagnostic);
|
|
2943
|
-
parent.diagnostics.delete(diagnostic);
|
|
2944
|
-
}
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
validate() {
|
|
2949
|
-
const diagnostics = [];
|
|
2950
|
-
const getText = (node) => `'${node.expression.getText()}()' cannot be nested within '${this.node.expression.getText()}()'.`;
|
|
2951
|
-
const getParentCallExpression = (node) => {
|
|
2952
|
-
while (!this.#compiler.isCallExpression(node.parent)) {
|
|
2953
|
-
node = node.parent;
|
|
2954
|
-
}
|
|
2955
|
-
return node.parent;
|
|
2956
|
-
};
|
|
2957
|
-
switch (this.brand) {
|
|
2958
|
-
case TestMemberBrand.Describe:
|
|
2959
|
-
for (const member of this.members) {
|
|
2960
|
-
if (member.brand === TestMemberBrand.Expect) {
|
|
2961
|
-
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(getParentCallExpression(member.node))));
|
|
2962
|
-
}
|
|
2963
|
-
}
|
|
2964
|
-
break;
|
|
2965
|
-
case TestMemberBrand.Test:
|
|
2966
|
-
case TestMemberBrand.Expect:
|
|
2967
|
-
for (const member of this.members) {
|
|
2968
|
-
if (member.brand !== TestMemberBrand.Expect) {
|
|
2969
|
-
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
|
|
2970
|
-
}
|
|
2971
|
-
}
|
|
2972
|
-
break;
|
|
2973
|
-
}
|
|
2974
|
-
return diagnostics;
|
|
2975
|
-
}
|
|
2976
|
-
}
|
|
2977
|
-
|
|
2978
|
-
class Assertion extends TestMember {
|
|
2979
|
-
isNot;
|
|
2980
|
-
matcherName;
|
|
2981
|
-
matcherNode;
|
|
2982
|
-
modifierNode;
|
|
2983
|
-
notNode;
|
|
2984
|
-
source;
|
|
2985
|
-
target;
|
|
2986
|
-
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
2987
|
-
super(compiler, brand, node, parent, flags);
|
|
2988
|
-
this.isNot = notNode != null;
|
|
2989
|
-
this.matcherName = matcherNode.expression.name;
|
|
2990
|
-
this.matcherNode = matcherNode;
|
|
2991
|
-
this.modifierNode = modifierNode;
|
|
2992
|
-
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
2993
|
-
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
2994
|
-
for (const diagnostic of parent.diagnostics) {
|
|
2995
|
-
if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
|
|
2996
|
-
this.diagnostics.add(diagnostic);
|
|
2997
|
-
parent.diagnostics.delete(diagnostic);
|
|
2998
|
-
}
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
|
|
3003
|
-
var TestMemberFlags;
|
|
3004
|
-
(function (TestMemberFlags) {
|
|
3005
|
-
TestMemberFlags[TestMemberFlags["None"] = 0] = "None";
|
|
3006
|
-
TestMemberFlags[TestMemberFlags["Fail"] = 1] = "Fail";
|
|
3007
|
-
TestMemberFlags[TestMemberFlags["Only"] = 2] = "Only";
|
|
3008
|
-
TestMemberFlags[TestMemberFlags["Skip"] = 4] = "Skip";
|
|
3009
|
-
TestMemberFlags[TestMemberFlags["Todo"] = 8] = "Todo";
|
|
3010
|
-
})(TestMemberFlags || (TestMemberFlags = {}));
|
|
3011
|
-
|
|
3012
|
-
class IdentifierLookup {
|
|
3013
|
-
#compiler;
|
|
3014
|
-
#identifiers;
|
|
3015
|
-
#moduleSpecifiers = ['"tstyche"', "'tstyche'"];
|
|
3016
|
-
constructor(compiler, identifiers) {
|
|
3017
|
-
this.#compiler = compiler;
|
|
3018
|
-
this.#identifiers = identifiers ?? {
|
|
3019
|
-
namedImports: {
|
|
3020
|
-
describe: undefined,
|
|
3021
|
-
expect: undefined,
|
|
3022
|
-
it: undefined,
|
|
3023
|
-
namespace: undefined,
|
|
3024
|
-
test: undefined,
|
|
3025
|
-
},
|
|
3026
|
-
namespace: undefined,
|
|
3027
|
-
};
|
|
3028
|
-
}
|
|
3029
|
-
handleImportDeclaration(node) {
|
|
3030
|
-
if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
|
|
3031
|
-
node.importClause?.isTypeOnly !== true &&
|
|
3032
|
-
node.importClause?.namedBindings != null) {
|
|
3033
|
-
if (this.#compiler.isNamedImports(node.importClause.namedBindings)) {
|
|
3034
|
-
for (const element of node.importClause.namedBindings.elements) {
|
|
3035
|
-
if (element.isTypeOnly) {
|
|
3036
|
-
continue;
|
|
3037
|
-
}
|
|
3038
|
-
let identifierKey;
|
|
3039
|
-
if (element.propertyName) {
|
|
3040
|
-
identifierKey = element.propertyName.getText();
|
|
3041
|
-
}
|
|
3042
|
-
else {
|
|
3043
|
-
identifierKey = element.name.getText();
|
|
3044
|
-
}
|
|
3045
|
-
if (identifierKey in this.#identifiers.namedImports) {
|
|
3046
|
-
this.#identifiers.namedImports[identifierKey] = element.name.getText();
|
|
3047
|
-
}
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
if (this.#compiler.isNamespaceImport(node.importClause.namedBindings)) {
|
|
3051
|
-
this.#identifiers.namespace = node.importClause.namedBindings.name.getText();
|
|
3052
|
-
}
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
resolveTestMemberMeta(node) {
|
|
3056
|
-
let flags = TestMemberFlags.None;
|
|
3057
|
-
let expression = node.expression;
|
|
3058
|
-
while (this.#compiler.isPropertyAccessExpression(expression)) {
|
|
3059
|
-
if (expression.expression.getText() === this.#identifiers.namespace) {
|
|
3060
|
-
break;
|
|
3061
|
-
}
|
|
3062
|
-
switch (expression.name.getText()) {
|
|
3063
|
-
case "fail":
|
|
3064
|
-
flags |= TestMemberFlags.Fail;
|
|
3065
|
-
break;
|
|
3066
|
-
case "only":
|
|
3067
|
-
flags |= TestMemberFlags.Only;
|
|
3068
|
-
break;
|
|
3069
|
-
case "skip":
|
|
3070
|
-
flags |= TestMemberFlags.Skip;
|
|
3071
|
-
break;
|
|
3072
|
-
case "todo":
|
|
3073
|
-
flags |= TestMemberFlags.Todo;
|
|
3074
|
-
break;
|
|
3075
|
-
}
|
|
3076
|
-
expression = expression.expression;
|
|
3077
|
-
}
|
|
3078
|
-
let identifierName;
|
|
3079
|
-
if (this.#compiler.isPropertyAccessExpression(expression) &&
|
|
3080
|
-
expression.expression.getText() === this.#identifiers.namespace) {
|
|
3081
|
-
identifierName = expression.name.getText();
|
|
3082
|
-
}
|
|
3083
|
-
else {
|
|
3084
|
-
identifierName = Object.keys(this.#identifiers.namedImports).find((key) => this.#identifiers.namedImports[key] === expression.getText());
|
|
3085
|
-
}
|
|
3086
|
-
if (!identifierName) {
|
|
3087
|
-
return;
|
|
3088
|
-
}
|
|
3089
|
-
switch (identifierName) {
|
|
3090
|
-
case "describe":
|
|
3091
|
-
return { brand: TestMemberBrand.Describe, flags };
|
|
3092
|
-
case "it":
|
|
3093
|
-
case "test":
|
|
3094
|
-
return { brand: TestMemberBrand.Test, flags };
|
|
3095
|
-
case "expect":
|
|
3096
|
-
return { brand: TestMemberBrand.Expect, flags };
|
|
3097
|
-
}
|
|
3098
|
-
return;
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
|
|
3102
|
-
class TestTree {
|
|
3103
|
-
diagnostics;
|
|
3104
|
-
members = [];
|
|
3105
|
-
sourceFile;
|
|
3106
|
-
constructor(diagnostics, sourceFile) {
|
|
3107
|
-
this.diagnostics = diagnostics;
|
|
3108
|
-
this.sourceFile = sourceFile;
|
|
3109
|
-
}
|
|
3110
|
-
get hasOnly() {
|
|
3111
|
-
function hasOnly(root) {
|
|
3112
|
-
return root.members.some((branch) => branch.flags & TestMemberFlags.Only || ("members" in branch && hasOnly(branch)));
|
|
3113
|
-
}
|
|
3114
|
-
return hasOnly(this);
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
|
-
|
|
3118
|
-
class CollectService {
|
|
3119
|
-
#compiler;
|
|
3120
|
-
constructor(compiler) {
|
|
3121
|
-
this.#compiler = compiler;
|
|
3122
|
-
}
|
|
3123
|
-
#collectTestMembers(node, identifiers, parent) {
|
|
3124
|
-
if (this.#compiler.isCallExpression(node)) {
|
|
3125
|
-
const meta = identifiers.resolveTestMemberMeta(node);
|
|
3126
|
-
if (meta != null && (meta.brand === TestMemberBrand.Describe || meta.brand === TestMemberBrand.Test)) {
|
|
3127
|
-
const testMember = new TestMember(this.#compiler, meta.brand, node, parent, meta.flags);
|
|
3128
|
-
parent.members.push(testMember);
|
|
3129
|
-
this.#compiler.forEachChild(node, (node) => {
|
|
3130
|
-
this.#collectTestMembers(node, identifiers, testMember);
|
|
3131
|
-
});
|
|
3132
|
-
return;
|
|
3133
|
-
}
|
|
3134
|
-
if (meta != null && meta.brand === TestMemberBrand.Expect) {
|
|
3135
|
-
const modifierNode = this.#getChainedNode(node, "type");
|
|
3136
|
-
if (!modifierNode) {
|
|
3137
|
-
return;
|
|
3138
|
-
}
|
|
3139
|
-
const notNode = this.#getChainedNode(modifierNode, "not");
|
|
3140
|
-
const matcherNode = this.#getChainedNode(notNode ?? modifierNode)?.parent;
|
|
3141
|
-
if (!matcherNode || !this.#isMatcherNode(matcherNode)) {
|
|
3142
|
-
return;
|
|
3143
|
-
}
|
|
3144
|
-
const assertion = new Assertion(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
|
|
3145
|
-
parent.members.push(assertion);
|
|
3146
|
-
this.#compiler.forEachChild(node, (node) => {
|
|
3147
|
-
this.#collectTestMembers(node, identifiers, assertion);
|
|
3148
|
-
});
|
|
3149
|
-
return;
|
|
3150
|
-
}
|
|
3151
|
-
}
|
|
3152
|
-
if (this.#compiler.isImportDeclaration(node)) {
|
|
3153
|
-
identifiers.handleImportDeclaration(node);
|
|
3154
|
-
return;
|
|
3155
|
-
}
|
|
3156
|
-
this.#compiler.forEachChild(node, (node) => {
|
|
3157
|
-
this.#collectTestMembers(node, identifiers, parent);
|
|
3158
|
-
});
|
|
3159
|
-
}
|
|
3160
|
-
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
3161
|
-
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
3162
|
-
this.#collectTestMembers(sourceFile, new IdentifierLookup(this.#compiler), testTree);
|
|
3163
|
-
return testTree;
|
|
3164
|
-
}
|
|
3165
|
-
#getChainedNode({ parent }, name) {
|
|
3166
|
-
if (!this.#compiler.isPropertyAccessExpression(parent)) {
|
|
3167
|
-
return;
|
|
3168
|
-
}
|
|
3169
|
-
if (name != null && name !== parent.name.getText()) {
|
|
3170
|
-
return;
|
|
3171
|
-
}
|
|
3172
|
-
return parent;
|
|
3173
|
-
}
|
|
3174
|
-
#isMatcherNode(node) {
|
|
3175
|
-
return this.#compiler.isCallExpression(node) && this.#compiler.isPropertyAccessExpression(node.expression);
|
|
3176
|
-
}
|
|
3177
|
-
}
|
|
3178
|
-
|
|
3179
|
-
class ProjectService {
|
|
3176
|
+
class ProjectService {
|
|
3180
3177
|
#compiler;
|
|
3181
3178
|
#lastSeenProject = "";
|
|
3182
3179
|
#resolvedConfig;
|
|
@@ -3234,24 +3231,19 @@ class ProjectService {
|
|
|
3234
3231
|
}
|
|
3235
3232
|
#getDefaultCompilerOptions() {
|
|
3236
3233
|
const defaultCompilerOptions = {
|
|
3234
|
+
allowImportingTsExtensions: true,
|
|
3237
3235
|
allowJs: true,
|
|
3238
3236
|
checkJs: true,
|
|
3239
|
-
|
|
3240
|
-
jsx:
|
|
3241
|
-
module:
|
|
3242
|
-
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,
|
|
3243
3242
|
resolveJsonModule: true,
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3243
|
+
strict: true,
|
|
3244
|
+
target: this.#compiler.ScriptTarget.ESNext,
|
|
3245
|
+
verbatimModuleSyntax: true,
|
|
3247
3246
|
};
|
|
3248
|
-
if (Version.isSatisfiedWith(this.#compiler.version, "5.4")) {
|
|
3249
|
-
defaultCompilerOptions.module = "preserve";
|
|
3250
|
-
}
|
|
3251
|
-
if (Version.isSatisfiedWith(this.#compiler.version, "5.0")) {
|
|
3252
|
-
defaultCompilerOptions.allowImportingTsExtensions = true;
|
|
3253
|
-
defaultCompilerOptions.moduleResolution = "bundler";
|
|
3254
|
-
}
|
|
3255
3247
|
return defaultCompilerOptions;
|
|
3256
3248
|
}
|
|
3257
3249
|
getDefaultProject(filePath) {
|
|
@@ -3343,12 +3335,6 @@ class ExpectDiagnosticText {
|
|
|
3343
3335
|
static componentDoesNotAcceptProps(isTypeNode) {
|
|
3344
3336
|
return `${isTypeNode ? "Component type" : "Component"} does not accept props of the given type.`;
|
|
3345
3337
|
}
|
|
3346
|
-
static matcherIsDeprecated(matcherNameText) {
|
|
3347
|
-
return [
|
|
3348
|
-
`The '.${matcherNameText}()' matcher is deprecated and will be removed in TSTyche 4.`,
|
|
3349
|
-
"To learn more, visit https://tstyche.org/releases/tstyche-3",
|
|
3350
|
-
];
|
|
3351
|
-
}
|
|
3352
3338
|
static matcherIsNotSupported(matcherNameText) {
|
|
3353
3339
|
return `The '.${matcherNameText}()' matcher is not supported.`;
|
|
3354
3340
|
}
|
|
@@ -3373,18 +3359,9 @@ class ExpectDiagnosticText {
|
|
|
3373
3359
|
static typeDoesNotHaveProperty(typeText, propertyNameText) {
|
|
3374
3360
|
return `Type '${typeText}' does not have property '${propertyNameText}'.`;
|
|
3375
3361
|
}
|
|
3376
|
-
static typeDoesMatch(sourceTypeText, targetTypeText) {
|
|
3377
|
-
return `Type '${sourceTypeText}' does match type '${targetTypeText}'.`;
|
|
3378
|
-
}
|
|
3379
|
-
static typeDoesNotMatch(sourceTypeText, targetTypeText) {
|
|
3380
|
-
return `Type '${sourceTypeText}' does not match type '${targetTypeText}'.`;
|
|
3381
|
-
}
|
|
3382
3362
|
static typeHasProperty(typeText, propertyNameText) {
|
|
3383
3363
|
return `Type '${typeText}' has property '${propertyNameText}'.`;
|
|
3384
3364
|
}
|
|
3385
|
-
static typeIs(typeText) {
|
|
3386
|
-
return `Type is '${typeText}'.`;
|
|
3387
|
-
}
|
|
3388
3365
|
static typeIsAssignableTo(sourceTypeText, targetTypeText) {
|
|
3389
3366
|
return `Type '${sourceTypeText}' is assignable to type '${targetTypeText}'.`;
|
|
3390
3367
|
}
|
|
@@ -3445,18 +3422,9 @@ class MatchWorker {
|
|
|
3445
3422
|
checkHasApplicableIndexType(sourceNode, targetNode) {
|
|
3446
3423
|
const sourceType = this.getType(sourceNode);
|
|
3447
3424
|
const targetType = this.getType(targetNode);
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
.some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
|
|
3452
|
-
}
|
|
3453
|
-
if (targetType.flags & this.#compiler.TypeFlags.StringLiteral) {
|
|
3454
|
-
return sourceType.getStringIndexType() != null;
|
|
3455
|
-
}
|
|
3456
|
-
if (targetType.flags & this.#compiler.TypeFlags.NumberLiteral) {
|
|
3457
|
-
return (sourceType.getStringIndexType() ?? sourceType.getNumberIndexType()) != null;
|
|
3458
|
-
}
|
|
3459
|
-
return false;
|
|
3425
|
+
return this.#typeChecker
|
|
3426
|
+
.getIndexInfosOfType(sourceType)
|
|
3427
|
+
.some(({ keyType }) => this.#typeChecker.isApplicableIndexType(targetType, keyType));
|
|
3460
3428
|
}
|
|
3461
3429
|
checkHasProperty(sourceNode, propertyNameText) {
|
|
3462
3430
|
const sourceType = this.getType(sourceNode);
|
|
@@ -3478,10 +3446,6 @@ class MatchWorker {
|
|
|
3478
3446
|
this.checkIsAssignableTo(sourceNode, targetNode) &&
|
|
3479
3447
|
this.checkIsAssignableWith(sourceNode, targetNode));
|
|
3480
3448
|
}
|
|
3481
|
-
checkIsSubtype(sourceNode, targetNode) {
|
|
3482
|
-
const relation = this.#typeChecker.relation.subtype;
|
|
3483
|
-
return this.#checkIsRelatedTo(sourceNode, targetNode, relation);
|
|
3484
|
-
}
|
|
3485
3449
|
#checkIsRelatedTo(sourceNode, targetNode, relation) {
|
|
3486
3450
|
const sourceType = relation === this.#typeChecker.relation.identity
|
|
3487
3451
|
? this.#simplifyType(this.getType(sourceNode))
|
|
@@ -3568,26 +3532,6 @@ class MatchWorker {
|
|
|
3568
3532
|
}
|
|
3569
3533
|
}
|
|
3570
3534
|
|
|
3571
|
-
class PrimitiveTypeMatcher {
|
|
3572
|
-
#targetTypeFlag;
|
|
3573
|
-
constructor(targetTypeFlag) {
|
|
3574
|
-
this.#targetTypeFlag = targetTypeFlag;
|
|
3575
|
-
}
|
|
3576
|
-
#explain(matchWorker, sourceNode) {
|
|
3577
|
-
const sourceTypeText = matchWorker.getTypeText(sourceNode);
|
|
3578
|
-
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
3579
|
-
return [Diagnostic.error(ExpectDiagnosticText.typeIs(sourceTypeText), origin)];
|
|
3580
|
-
}
|
|
3581
|
-
match(matchWorker, sourceNode) {
|
|
3582
|
-
const sourceType = matchWorker.getType(sourceNode);
|
|
3583
|
-
const isMatch = !!(sourceType.flags & this.#targetTypeFlag);
|
|
3584
|
-
return {
|
|
3585
|
-
explain: () => this.#explain(matchWorker, sourceNode),
|
|
3586
|
-
isMatch,
|
|
3587
|
-
};
|
|
3588
|
-
}
|
|
3589
|
-
}
|
|
3590
|
-
|
|
3591
3535
|
class ToAcceptProps {
|
|
3592
3536
|
#compiler;
|
|
3593
3537
|
#typeChecker;
|
|
@@ -3875,17 +3819,6 @@ class ToHaveProperty {
|
|
|
3875
3819
|
}
|
|
3876
3820
|
}
|
|
3877
3821
|
|
|
3878
|
-
class ToMatch extends RelationMatcherBase {
|
|
3879
|
-
explainText = ExpectDiagnosticText.typeDoesMatch;
|
|
3880
|
-
explainNotText = ExpectDiagnosticText.typeDoesNotMatch;
|
|
3881
|
-
match(matchWorker, sourceNode, targetNode) {
|
|
3882
|
-
return {
|
|
3883
|
-
explain: () => this.explain(matchWorker, sourceNode, targetNode),
|
|
3884
|
-
isMatch: matchWorker.checkIsSubtype(sourceNode, targetNode),
|
|
3885
|
-
};
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
|
|
3889
3822
|
class ToRaiseError {
|
|
3890
3823
|
#compiler;
|
|
3891
3824
|
constructor(compiler) {
|
|
@@ -3955,14 +3888,17 @@ class ToRaiseError {
|
|
|
3955
3888
|
};
|
|
3956
3889
|
}
|
|
3957
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");
|
|
3958
3897
|
if (this.#compiler.isRegularExpressionLiteral(targetNode)) {
|
|
3959
3898
|
const targetRegex = new RegExp(...targetNode.text.slice(1).split("/"));
|
|
3960
|
-
return targetRegex.test(
|
|
3961
|
-
}
|
|
3962
|
-
if (this.#compiler.isStringLiteralLike(targetNode)) {
|
|
3963
|
-
return this.#compiler.flattenDiagnosticMessageText(diagnostic.messageText, " ", 0).includes(targetNode.text);
|
|
3899
|
+
return targetRegex.test(messageText);
|
|
3964
3900
|
}
|
|
3965
|
-
return
|
|
3901
|
+
return messageText.includes(targetNode.text);
|
|
3966
3902
|
}
|
|
3967
3903
|
}
|
|
3968
3904
|
|
|
@@ -3972,22 +3908,9 @@ class ExpectService {
|
|
|
3972
3908
|
#typeChecker;
|
|
3973
3909
|
toAcceptProps;
|
|
3974
3910
|
toBe;
|
|
3975
|
-
toBeAny;
|
|
3976
3911
|
toBeAssignableTo;
|
|
3977
3912
|
toBeAssignableWith;
|
|
3978
|
-
toBeBigInt;
|
|
3979
|
-
toBeBoolean;
|
|
3980
|
-
toBeNever;
|
|
3981
|
-
toBeNull;
|
|
3982
|
-
toBeNumber;
|
|
3983
|
-
toBeString;
|
|
3984
|
-
toBeSymbol;
|
|
3985
|
-
toBeUndefined;
|
|
3986
|
-
toBeUniqueSymbol;
|
|
3987
|
-
toBeUnknown;
|
|
3988
|
-
toBeVoid;
|
|
3989
3913
|
toHaveProperty;
|
|
3990
|
-
toMatch;
|
|
3991
3914
|
toRaiseError;
|
|
3992
3915
|
constructor(compiler, typeChecker, resolvedConfig) {
|
|
3993
3916
|
this.#compiler = compiler;
|
|
@@ -4000,31 +3923,13 @@ class ExpectService {
|
|
|
4000
3923
|
}
|
|
4001
3924
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
4002
3925
|
this.toBe = new ToBe();
|
|
4003
|
-
this.toBeAny = new PrimitiveTypeMatcher(compiler.TypeFlags.Any);
|
|
4004
3926
|
this.toBeAssignableTo = new ToBeAssignableTo();
|
|
4005
3927
|
this.toBeAssignableWith = new ToBeAssignableWith();
|
|
4006
|
-
this.toBeBigInt = new PrimitiveTypeMatcher(compiler.TypeFlags.BigInt);
|
|
4007
|
-
this.toBeBoolean = new PrimitiveTypeMatcher(compiler.TypeFlags.Boolean);
|
|
4008
|
-
this.toBeNever = new PrimitiveTypeMatcher(compiler.TypeFlags.Never);
|
|
4009
|
-
this.toBeNull = new PrimitiveTypeMatcher(compiler.TypeFlags.Null);
|
|
4010
|
-
this.toBeNumber = new PrimitiveTypeMatcher(compiler.TypeFlags.Number);
|
|
4011
|
-
this.toBeString = new PrimitiveTypeMatcher(compiler.TypeFlags.String);
|
|
4012
|
-
this.toBeSymbol = new PrimitiveTypeMatcher(compiler.TypeFlags.ESSymbol);
|
|
4013
|
-
this.toBeUndefined = new PrimitiveTypeMatcher(compiler.TypeFlags.Undefined);
|
|
4014
|
-
this.toBeUniqueSymbol = new PrimitiveTypeMatcher(compiler.TypeFlags.UniqueESSymbol);
|
|
4015
|
-
this.toBeUnknown = new PrimitiveTypeMatcher(compiler.TypeFlags.Unknown);
|
|
4016
|
-
this.toBeVoid = new PrimitiveTypeMatcher(compiler.TypeFlags.Void);
|
|
4017
3928
|
this.toHaveProperty = new ToHaveProperty(compiler);
|
|
4018
|
-
this.toMatch = new ToMatch();
|
|
4019
3929
|
this.toRaiseError = new ToRaiseError(compiler);
|
|
4020
3930
|
}
|
|
4021
3931
|
match(assertion, onDiagnostics) {
|
|
4022
3932
|
const matcherNameText = assertion.matcherName.getText();
|
|
4023
|
-
if (matcherNameText === "toMatch") {
|
|
4024
|
-
const text = ExpectDiagnosticText.matcherIsDeprecated(matcherNameText);
|
|
4025
|
-
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
4026
|
-
EventEmitter.dispatch(["deprecation:info", { diagnostics: [Diagnostic.warning(text, origin)] }]);
|
|
4027
|
-
}
|
|
4028
3933
|
if (!assertion.source[0]) {
|
|
4029
3934
|
this.#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
4030
3935
|
return;
|
|
@@ -4035,7 +3940,6 @@ class ExpectService {
|
|
|
4035
3940
|
case "toBe":
|
|
4036
3941
|
case "toBeAssignableTo":
|
|
4037
3942
|
case "toBeAssignableWith":
|
|
4038
|
-
case "toMatch":
|
|
4039
3943
|
if (!assertion.target[0]) {
|
|
4040
3944
|
this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
4041
3945
|
return;
|
|
@@ -4044,19 +3948,6 @@ class ExpectService {
|
|
|
4044
3948
|
return;
|
|
4045
3949
|
}
|
|
4046
3950
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
4047
|
-
case "toBeAny":
|
|
4048
|
-
case "toBeBigInt":
|
|
4049
|
-
case "toBeBoolean":
|
|
4050
|
-
case "toBeNever":
|
|
4051
|
-
case "toBeNull":
|
|
4052
|
-
case "toBeNumber":
|
|
4053
|
-
case "toBeString":
|
|
4054
|
-
case "toBeSymbol":
|
|
4055
|
-
case "toBeUndefined":
|
|
4056
|
-
case "toBeUniqueSymbol":
|
|
4057
|
-
case "toBeUnknown":
|
|
4058
|
-
case "toBeVoid":
|
|
4059
|
-
return this[matcherNameText].match(matchWorker, assertion.source[0]);
|
|
4060
3951
|
case "toHaveProperty":
|
|
4061
3952
|
if (!assertion.target[0]) {
|
|
4062
3953
|
this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
|
|
@@ -4095,10 +3986,14 @@ class ExpectService {
|
|
|
4095
3986
|
}
|
|
4096
3987
|
#rejectsTypeArguments(matchWorker, onDiagnostics) {
|
|
4097
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
|
+
}
|
|
4098
3994
|
for (const argumentName of ["source", "target"]) {
|
|
4099
3995
|
const argumentNode = matchWorker.assertion[argumentName][0];
|
|
4100
|
-
if (!argumentNode
|
|
4101
|
-
argumentNode.kind === this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`]) {
|
|
3996
|
+
if (!argumentNode) {
|
|
4102
3997
|
continue;
|
|
4103
3998
|
}
|
|
4104
3999
|
if (matchWorker.getType(argumentNode).flags & this.#compiler.TypeFlags[Format.capitalize(rejectedType)]) {
|
|
@@ -4119,7 +4014,6 @@ class ExpectService {
|
|
|
4119
4014
|
}
|
|
4120
4015
|
|
|
4121
4016
|
class TestTreeWalker {
|
|
4122
|
-
#compiler;
|
|
4123
4017
|
#cancellationToken;
|
|
4124
4018
|
#expectService;
|
|
4125
4019
|
#hasOnly;
|
|
@@ -4128,59 +4022,60 @@ class TestTreeWalker {
|
|
|
4128
4022
|
#taskResult;
|
|
4129
4023
|
constructor(resolvedConfig, compiler, typeChecker, options) {
|
|
4130
4024
|
this.#resolvedConfig = resolvedConfig;
|
|
4131
|
-
this.#compiler = compiler;
|
|
4132
4025
|
this.#cancellationToken = options.cancellationToken;
|
|
4133
4026
|
this.#hasOnly = options.hasOnly || resolvedConfig.only != null || options.position != null;
|
|
4134
4027
|
this.#position = options.position;
|
|
4135
4028
|
this.#taskResult = options.taskResult;
|
|
4136
4029
|
this.#expectService = new ExpectService(compiler, typeChecker, this.#resolvedConfig);
|
|
4137
4030
|
}
|
|
4138
|
-
#resolveRunMode(mode,
|
|
4139
|
-
if (
|
|
4031
|
+
#resolveRunMode(mode, testNode) {
|
|
4032
|
+
if (testNode.flags & TestTreeNodeFlags.Fail) {
|
|
4140
4033
|
mode |= RunMode.Fail;
|
|
4141
4034
|
}
|
|
4142
|
-
if (
|
|
4143
|
-
(this.#resolvedConfig.only != null &&
|
|
4035
|
+
if (testNode.flags & TestTreeNodeFlags.Only ||
|
|
4036
|
+
(this.#resolvedConfig.only != null &&
|
|
4037
|
+
testNode.name.toLowerCase().includes(this.#resolvedConfig.only.toLowerCase()))) {
|
|
4144
4038
|
mode |= RunMode.Only;
|
|
4145
4039
|
}
|
|
4146
|
-
if (
|
|
4147
|
-
(this.#resolvedConfig.skip != null &&
|
|
4040
|
+
if (testNode.flags & TestTreeNodeFlags.Skip ||
|
|
4041
|
+
(this.#resolvedConfig.skip != null &&
|
|
4042
|
+
testNode.name.toLowerCase().includes(this.#resolvedConfig.skip.toLowerCase()))) {
|
|
4148
4043
|
mode |= RunMode.Skip;
|
|
4149
4044
|
}
|
|
4150
|
-
if (
|
|
4045
|
+
if (testNode.flags & TestTreeNodeFlags.Todo) {
|
|
4151
4046
|
mode |= RunMode.Todo;
|
|
4152
4047
|
}
|
|
4153
|
-
if (this.#position != null &&
|
|
4048
|
+
if (this.#position != null && testNode.node.getStart() === this.#position) {
|
|
4154
4049
|
mode |= RunMode.Only;
|
|
4155
4050
|
mode &= ~RunMode.Skip;
|
|
4156
4051
|
}
|
|
4157
4052
|
return mode;
|
|
4158
4053
|
}
|
|
4159
|
-
visit(
|
|
4160
|
-
for (const
|
|
4054
|
+
visit(testNodes, runMode, parentResult) {
|
|
4055
|
+
for (const testNode of testNodes) {
|
|
4161
4056
|
if (this.#cancellationToken?.isCancellationRequested) {
|
|
4162
4057
|
break;
|
|
4163
4058
|
}
|
|
4164
|
-
const validationError =
|
|
4059
|
+
const validationError = testNode.validate();
|
|
4165
4060
|
if (validationError.length > 0) {
|
|
4166
4061
|
EventEmitter.dispatch(["task:error", { diagnostics: validationError, result: this.#taskResult }]);
|
|
4167
4062
|
break;
|
|
4168
4063
|
}
|
|
4169
|
-
switch (
|
|
4170
|
-
case
|
|
4171
|
-
this.#visitDescribe(
|
|
4064
|
+
switch (testNode.brand) {
|
|
4065
|
+
case TestTreeNodeBrand.Describe:
|
|
4066
|
+
this.#visitDescribe(testNode, runMode, parentResult);
|
|
4172
4067
|
break;
|
|
4173
|
-
case
|
|
4174
|
-
this.#visitTest(
|
|
4068
|
+
case TestTreeNodeBrand.Test:
|
|
4069
|
+
this.#visitTest(testNode, runMode, parentResult);
|
|
4175
4070
|
break;
|
|
4176
|
-
case
|
|
4177
|
-
this.#visitAssertion(
|
|
4071
|
+
case TestTreeNodeBrand.Expect:
|
|
4072
|
+
this.#visitAssertion(testNode, runMode, parentResult);
|
|
4178
4073
|
break;
|
|
4179
4074
|
}
|
|
4180
4075
|
}
|
|
4181
4076
|
}
|
|
4182
4077
|
#visitAssertion(assertion, runMode, parentResult) {
|
|
4183
|
-
this.visit(assertion.
|
|
4078
|
+
this.visit(assertion.children, runMode, parentResult);
|
|
4184
4079
|
const expectResult = new ExpectResult(assertion, parentResult);
|
|
4185
4080
|
EventEmitter.dispatch(["expect:start", { result: expectResult }]);
|
|
4186
4081
|
runMode = this.#resolveRunMode(runMode, assertion);
|
|
@@ -4234,7 +4129,7 @@ class TestTreeWalker {
|
|
|
4234
4129
|
]);
|
|
4235
4130
|
}
|
|
4236
4131
|
else {
|
|
4237
|
-
this.visit(describe.
|
|
4132
|
+
this.visit(describe.children, runMode, describeResult);
|
|
4238
4133
|
}
|
|
4239
4134
|
EventEmitter.dispatch(["describe:end", { result: describeResult }]);
|
|
4240
4135
|
}
|
|
@@ -4256,7 +4151,7 @@ class TestTreeWalker {
|
|
|
4256
4151
|
]);
|
|
4257
4152
|
return;
|
|
4258
4153
|
}
|
|
4259
|
-
this.visit(test.
|
|
4154
|
+
this.visit(test.children, runMode, testResult);
|
|
4260
4155
|
if (runMode & RunMode.Skip || (this.#hasOnly && !(runMode & RunMode.Only))) {
|
|
4261
4156
|
EventEmitter.dispatch(["test:skip", { result: testResult }]);
|
|
4262
4157
|
return;
|
|
@@ -4336,17 +4231,27 @@ class TaskRunner {
|
|
|
4336
4231
|
hasOnly: testTree.hasOnly,
|
|
4337
4232
|
position: task.position,
|
|
4338
4233
|
});
|
|
4339
|
-
testTreeWalker.visit(testTree.
|
|
4234
|
+
testTreeWalker.visit(testTree.children, RunMode.Normal, undefined);
|
|
4340
4235
|
}
|
|
4341
4236
|
}
|
|
4342
4237
|
|
|
4343
4238
|
class Runner {
|
|
4344
4239
|
#eventEmitter = new EventEmitter();
|
|
4345
4240
|
#resolvedConfig;
|
|
4346
|
-
static version = "
|
|
4241
|
+
static version = "4.0.0-beta.0";
|
|
4347
4242
|
constructor(resolvedConfig) {
|
|
4348
4243
|
this.#resolvedConfig = resolvedConfig;
|
|
4349
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
|
+
}
|
|
4350
4255
|
async #addReporters() {
|
|
4351
4256
|
if (this.#resolvedConfig.watch && !environmentOptions.noInteractive) {
|
|
4352
4257
|
const watchReporter = new WatchReporter(this.#resolvedConfig);
|
|
@@ -4374,13 +4279,8 @@ class Runner {
|
|
|
4374
4279
|
}
|
|
4375
4280
|
async run(testFiles, cancellationToken = new CancellationToken()) {
|
|
4376
4281
|
const tasks = testFiles.map((testFile) => (testFile instanceof Task ? testFile : new Task(testFile)));
|
|
4377
|
-
|
|
4378
|
-
this.#eventEmitter.addHandler(resultHandler);
|
|
4282
|
+
this.#addHandlers(cancellationToken);
|
|
4379
4283
|
await this.#addReporters();
|
|
4380
|
-
if (this.#resolvedConfig.failFast) {
|
|
4381
|
-
const cancellationHandler = new CancellationHandler(cancellationToken, CancellationReason.FailFast);
|
|
4382
|
-
this.#eventEmitter.addHandler(cancellationHandler);
|
|
4383
|
-
}
|
|
4384
4284
|
await this.#run(tasks, cancellationToken);
|
|
4385
4285
|
if (this.#resolvedConfig.watch) {
|
|
4386
4286
|
await this.#watch(tasks, cancellationToken);
|
|
@@ -4483,9 +4383,9 @@ class Cli {
|
|
|
4483
4383
|
OutputService.writeMessage(formattedText({ ...resolvedConfig, ...environmentOptions }));
|
|
4484
4384
|
continue;
|
|
4485
4385
|
}
|
|
4486
|
-
if (commandLine.includes("--
|
|
4386
|
+
if (commandLine.includes("--fetch")) {
|
|
4487
4387
|
for (const tag of resolvedConfig.target) {
|
|
4488
|
-
await Store.
|
|
4388
|
+
await Store.fetch(tag);
|
|
4489
4389
|
}
|
|
4490
4390
|
continue;
|
|
4491
4391
|
}
|
|
@@ -4540,4 +4440,4 @@ class Cli {
|
|
|
4540
4440
|
}
|
|
4541
4441
|
}
|
|
4542
4442
|
|
|
4543
|
-
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 };
|