tstyche 4.0.0-beta.0 → 4.0.0-beta.1
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 +3 -2
- package/build/index.d.cts +4 -0
- package/build/index.d.ts +4 -0
- package/build/tstyche.d.ts +22 -23
- package/build/tstyche.js +218 -35
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -57,9 +57,10 @@ test("handles numbers", () => {
|
|
|
57
57
|
Here is the list of all matchers:
|
|
58
58
|
|
|
59
59
|
- `.toBe()`, `.toBeAssignableTo()`, `.toBeAssignableWith()` compare types or types of expression,
|
|
60
|
-
- `.toAcceptProps()` checks JSX component props
|
|
60
|
+
- `.toAcceptProps()` checks the type of JSX component props,
|
|
61
|
+
- `.toBeApplicable` ensures that the decorator function can be applied,
|
|
61
62
|
- `.toHaveProperty()` looks up keys on an object type,
|
|
62
|
-
- `.toRaiseError()` captures the
|
|
63
|
+
- `.toRaiseError()` captures the message or code of a type error.
|
|
63
64
|
|
|
64
65
|
## Runner
|
|
65
66
|
|
package/build/index.d.cts
CHANGED
|
@@ -97,6 +97,10 @@ interface Matchers {
|
|
|
97
97
|
*/
|
|
98
98
|
(target: unknown): void;
|
|
99
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* Checks if the decorator function can be applied.
|
|
102
|
+
*/
|
|
103
|
+
toBeApplicable: (target: unknown, context: DecoratorContext) => void;
|
|
100
104
|
/**
|
|
101
105
|
* Checks if the source type is assignable to the target type.
|
|
102
106
|
*/
|
package/build/index.d.ts
CHANGED
|
@@ -97,6 +97,10 @@ interface Matchers {
|
|
|
97
97
|
*/
|
|
98
98
|
(target: unknown): void;
|
|
99
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* Checks if the decorator function can be applied.
|
|
102
|
+
*/
|
|
103
|
+
toBeApplicable: (target: unknown, context: DecoratorContext) => void;
|
|
100
104
|
/**
|
|
101
105
|
* Checks if the source type is assignable to the target type.
|
|
102
106
|
*/
|
package/build/tstyche.d.ts
CHANGED
|
@@ -102,24 +102,16 @@ declare class TestTree {
|
|
|
102
102
|
constructor(diagnostics: Set<ts.Diagnostic>, sourceFile: ts.SourceFile);
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
interface MatcherNode extends ts.CallExpression {
|
|
106
|
-
expression: ts.PropertyAccessExpression;
|
|
107
|
-
}
|
|
108
105
|
declare class AssertionNode extends TestTreeNode {
|
|
106
|
+
abilityDiagnostics: Set<ts.Diagnostic> | undefined;
|
|
109
107
|
isNot: boolean;
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
matcherNode: ts.CallExpression | ts.Decorator;
|
|
109
|
+
matcherNameNode: ts.PropertyAccessExpression;
|
|
112
110
|
modifierNode: ts.PropertyAccessExpression;
|
|
113
111
|
notNode: ts.PropertyAccessExpression | undefined;
|
|
114
112
|
source: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
|
|
115
|
-
target: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode
|
|
116
|
-
constructor(compiler: typeof ts, brand: TestTreeNodeBrand, node: ts.CallExpression, parent: TestTree | TestTreeNode, flags: TestTreeNodeFlags, matcherNode:
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
declare class CollectService {
|
|
120
|
-
#private;
|
|
121
|
-
constructor(compiler: typeof ts);
|
|
122
|
-
createTestTree(sourceFile: ts.SourceFile, semanticDiagnostics?: Array<ts.Diagnostic>): TestTree;
|
|
113
|
+
target: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode> | undefined;
|
|
114
|
+
constructor(compiler: typeof ts, brand: TestTreeNodeBrand, node: ts.CallExpression, parent: TestTree | TestTreeNode, flags: TestTreeNodeFlags, matcherNode: ts.CallExpression | ts.Decorator, matcherNameNode: ts.PropertyAccessExpression, modifierNode: ts.PropertyAccessExpression, notNode?: ts.PropertyAccessExpression);
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
declare enum OptionBrand {
|
|
@@ -207,7 +199,7 @@ interface CommandLineOptions {
|
|
|
207
199
|
*/
|
|
208
200
|
failFast?: boolean;
|
|
209
201
|
/**
|
|
210
|
-
* Fetch specified versions of the 'typescript' package and exit.
|
|
202
|
+
* Fetch the specified versions of the 'typescript' package and exit.
|
|
211
203
|
*/
|
|
212
204
|
fetch?: boolean;
|
|
213
205
|
/**
|
|
@@ -330,6 +322,21 @@ declare class Options {
|
|
|
330
322
|
|
|
331
323
|
declare const defaultOptions: Required<ConfigFileOptions>;
|
|
332
324
|
|
|
325
|
+
declare class ProjectService {
|
|
326
|
+
#private;
|
|
327
|
+
constructor(resolvedConfig: ResolvedConfig, compiler: typeof ts);
|
|
328
|
+
closeFile(filePath: string): void;
|
|
329
|
+
getDefaultProject(filePath: string): ts.server.Project | undefined;
|
|
330
|
+
getLanguageService(filePath: string): ts.LanguageService | undefined;
|
|
331
|
+
openFile(filePath: string, sourceText?: string | undefined, projectRootPath?: string | undefined): void;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
declare class CollectService {
|
|
335
|
+
#private;
|
|
336
|
+
constructor(compiler: typeof ts, projectService: ProjectService, resolvedConfig: ResolvedConfig);
|
|
337
|
+
createTestTree(sourceFile: ts.SourceFile, semanticDiagnostics?: Array<ts.Diagnostic>): TestTree;
|
|
338
|
+
}
|
|
339
|
+
|
|
333
340
|
interface EnvironmentOptions {
|
|
334
341
|
/**
|
|
335
342
|
* Is `true` if the process is running in continuous integration environment.
|
|
@@ -592,6 +599,7 @@ declare class ExpectService {
|
|
|
592
599
|
#private;
|
|
593
600
|
private toAcceptProps;
|
|
594
601
|
private toBe;
|
|
602
|
+
private toBeApplicable;
|
|
595
603
|
private toBeAssignableTo;
|
|
596
604
|
private toBeAssignableWith;
|
|
597
605
|
private toHaveProperty;
|
|
@@ -767,15 +775,6 @@ declare class PluginService {
|
|
|
767
775
|
static removeHandlers(): void;
|
|
768
776
|
}
|
|
769
777
|
|
|
770
|
-
declare class ProjectService {
|
|
771
|
-
#private;
|
|
772
|
-
constructor(resolvedConfig: ResolvedConfig, compiler: typeof ts);
|
|
773
|
-
closeFile(filePath: string): void;
|
|
774
|
-
getDefaultProject(filePath: string): ts.server.Project | undefined;
|
|
775
|
-
getLanguageService(filePath: string): ts.LanguageService | undefined;
|
|
776
|
-
openFile(filePath: string, sourceText?: string | undefined, projectRootPath?: string | undefined): void;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
778
|
declare class Runner {
|
|
780
779
|
#private;
|
|
781
780
|
static version: string;
|
package/build/tstyche.js
CHANGED
|
@@ -93,7 +93,7 @@ class DiagnosticOrigin {
|
|
|
93
93
|
this.assertion = assertion;
|
|
94
94
|
}
|
|
95
95
|
static fromAssertion(assertion) {
|
|
96
|
-
const node = assertion.
|
|
96
|
+
const node = assertion.matcherNameNode.name;
|
|
97
97
|
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
|
|
98
98
|
}
|
|
99
99
|
static fromNode(node, assertion) {
|
|
@@ -903,7 +903,7 @@ class Options {
|
|
|
903
903
|
},
|
|
904
904
|
{
|
|
905
905
|
brand: OptionBrand.BareTrue,
|
|
906
|
-
description: "Fetch specified versions of the 'typescript' package and exit.",
|
|
906
|
+
description: "Fetch the specified versions of the 'typescript' package and exit.",
|
|
907
907
|
group: OptionGroup.CommandLine,
|
|
908
908
|
name: "fetch",
|
|
909
909
|
},
|
|
@@ -1948,17 +1948,17 @@ class TestTreeNode {
|
|
|
1948
1948
|
};
|
|
1949
1949
|
switch (this.brand) {
|
|
1950
1950
|
case TestTreeNodeBrand.Describe:
|
|
1951
|
-
for (const
|
|
1952
|
-
if (
|
|
1953
|
-
diagnostics.push(Diagnostic.error(getText(
|
|
1951
|
+
for (const child of this.children) {
|
|
1952
|
+
if (child.brand === TestTreeNodeBrand.Expect) {
|
|
1953
|
+
diagnostics.push(Diagnostic.error(getText(child.node), DiagnosticOrigin.fromNode(getParentCallExpression(child.node))));
|
|
1954
1954
|
}
|
|
1955
1955
|
}
|
|
1956
1956
|
break;
|
|
1957
1957
|
case TestTreeNodeBrand.Test:
|
|
1958
1958
|
case TestTreeNodeBrand.Expect:
|
|
1959
|
-
for (const
|
|
1960
|
-
if (
|
|
1961
|
-
diagnostics.push(Diagnostic.error(getText(
|
|
1959
|
+
for (const child of this.children) {
|
|
1960
|
+
if (child.brand !== TestTreeNodeBrand.Expect) {
|
|
1961
|
+
diagnostics.push(Diagnostic.error(getText(child.node), DiagnosticOrigin.fromNode(child.node)));
|
|
1962
1962
|
}
|
|
1963
1963
|
}
|
|
1964
1964
|
break;
|
|
@@ -1968,21 +1968,24 @@ class TestTreeNode {
|
|
|
1968
1968
|
}
|
|
1969
1969
|
|
|
1970
1970
|
class AssertionNode extends TestTreeNode {
|
|
1971
|
+
abilityDiagnostics;
|
|
1971
1972
|
isNot;
|
|
1972
|
-
matcherName;
|
|
1973
1973
|
matcherNode;
|
|
1974
|
+
matcherNameNode;
|
|
1974
1975
|
modifierNode;
|
|
1975
1976
|
notNode;
|
|
1976
1977
|
source;
|
|
1977
1978
|
target;
|
|
1978
|
-
constructor(compiler, brand, node, parent, flags, matcherNode, modifierNode, notNode) {
|
|
1979
|
+
constructor(compiler, brand, node, parent, flags, matcherNode, matcherNameNode, modifierNode, notNode) {
|
|
1979
1980
|
super(compiler, brand, node, parent, flags);
|
|
1980
1981
|
this.isNot = notNode != null;
|
|
1981
|
-
this.matcherName = matcherNode.expression.name;
|
|
1982
1982
|
this.matcherNode = matcherNode;
|
|
1983
|
+
this.matcherNameNode = matcherNameNode;
|
|
1983
1984
|
this.modifierNode = modifierNode;
|
|
1984
1985
|
this.source = this.node.typeArguments ?? this.node.arguments;
|
|
1985
|
-
|
|
1986
|
+
if (compiler.isCallExpression(this.matcherNode)) {
|
|
1987
|
+
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
1988
|
+
}
|
|
1986
1989
|
for (const diagnostic of parent.diagnostics) {
|
|
1987
1990
|
if (diagnostic.start != null && diagnostic.start >= this.source.pos && diagnostic.start <= this.source.end) {
|
|
1988
1991
|
this.diagnostics.add(diagnostic);
|
|
@@ -1992,6 +1995,86 @@ class AssertionNode extends TestTreeNode {
|
|
|
1992
1995
|
}
|
|
1993
1996
|
}
|
|
1994
1997
|
|
|
1998
|
+
class AbilityLayer {
|
|
1999
|
+
#filePath = "";
|
|
2000
|
+
#nodes = [];
|
|
2001
|
+
#projectService;
|
|
2002
|
+
#resolvedConfig;
|
|
2003
|
+
#text = "";
|
|
2004
|
+
constructor(projectService, resolvedConfig) {
|
|
2005
|
+
this.#projectService = projectService;
|
|
2006
|
+
this.#resolvedConfig = resolvedConfig;
|
|
2007
|
+
}
|
|
2008
|
+
#getErasedRangeText(range) {
|
|
2009
|
+
if (this.#text.indexOf("\n", range.start) >= range.end) {
|
|
2010
|
+
return " ".repeat(range.end - range.start);
|
|
2011
|
+
}
|
|
2012
|
+
const text = [];
|
|
2013
|
+
for (let i = range.start; i < range.end; i++) {
|
|
2014
|
+
switch (this.#text.charAt(i)) {
|
|
2015
|
+
case "\n":
|
|
2016
|
+
case "\r":
|
|
2017
|
+
text.push(this.#text.charAt(i));
|
|
2018
|
+
break;
|
|
2019
|
+
default:
|
|
2020
|
+
text.push(" ");
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
return text.join("");
|
|
2024
|
+
}
|
|
2025
|
+
#addRanges(node, ranges) {
|
|
2026
|
+
this.#nodes.push(node);
|
|
2027
|
+
for (const range of ranges) {
|
|
2028
|
+
const rangeText = this.#getErasedRangeText(range);
|
|
2029
|
+
this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
close() {
|
|
2033
|
+
if (this.#nodes.length > 0) {
|
|
2034
|
+
this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
|
|
2035
|
+
const languageService = this.#projectService.getLanguageService(this.#filePath);
|
|
2036
|
+
const diagnostics = new Set(languageService?.getSemanticDiagnostics(this.#filePath)?.toReversed());
|
|
2037
|
+
if (diagnostics.size > 0) {
|
|
2038
|
+
for (const node of this.#nodes.toReversed()) {
|
|
2039
|
+
for (const diagnostic of diagnostics) {
|
|
2040
|
+
if (diagnostic.start != null &&
|
|
2041
|
+
diagnostic.start >= node.matcherNode.pos &&
|
|
2042
|
+
diagnostic.start <= node.matcherNode.end) {
|
|
2043
|
+
if (!node.abilityDiagnostics) {
|
|
2044
|
+
node.abilityDiagnostics = new Set();
|
|
2045
|
+
}
|
|
2046
|
+
node.abilityDiagnostics.add(diagnostic);
|
|
2047
|
+
diagnostics.delete(diagnostic);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
this.#filePath = "";
|
|
2054
|
+
this.#nodes = [];
|
|
2055
|
+
this.#text = "";
|
|
2056
|
+
}
|
|
2057
|
+
handleNode(assertionNode) {
|
|
2058
|
+
switch (assertionNode.matcherNameNode.name.text) {
|
|
2059
|
+
case "toBeApplicable": {
|
|
2060
|
+
const expectStart = assertionNode.node.pos;
|
|
2061
|
+
const expectExpressionEnd = assertionNode.node.expression.end;
|
|
2062
|
+
const expectEnd = assertionNode.node.end;
|
|
2063
|
+
const matcherNameEnd = assertionNode.matcherNameNode.end;
|
|
2064
|
+
this.#addRanges(assertionNode, [
|
|
2065
|
+
{ end: expectExpressionEnd + 1, start: expectStart },
|
|
2066
|
+
{ end: matcherNameEnd, start: expectEnd - 1 },
|
|
2067
|
+
]);
|
|
2068
|
+
break;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
open(sourceFile) {
|
|
2073
|
+
this.#filePath = sourceFile.fileName;
|
|
2074
|
+
this.#text = sourceFile.text;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
|
|
1995
2078
|
var TestTreeNodeFlags;
|
|
1996
2079
|
(function (TestTreeNodeFlags) {
|
|
1997
2080
|
TestTreeNodeFlags[TestTreeNodeFlags["None"] = 0] = "None";
|
|
@@ -2103,9 +2186,15 @@ class TestTree {
|
|
|
2103
2186
|
}
|
|
2104
2187
|
|
|
2105
2188
|
class CollectService {
|
|
2189
|
+
#abilityLayer;
|
|
2106
2190
|
#compiler;
|
|
2107
|
-
|
|
2191
|
+
#projectService;
|
|
2192
|
+
#resolvedConfig;
|
|
2193
|
+
constructor(compiler, projectService, resolvedConfig) {
|
|
2108
2194
|
this.#compiler = compiler;
|
|
2195
|
+
this.#projectService = projectService;
|
|
2196
|
+
this.#resolvedConfig = resolvedConfig;
|
|
2197
|
+
this.#abilityLayer = new AbilityLayer(this.#projectService, this.#resolvedConfig);
|
|
2109
2198
|
}
|
|
2110
2199
|
#collectTestTreeNodes(node, identifiers, parent) {
|
|
2111
2200
|
if (this.#compiler.isCallExpression(node)) {
|
|
@@ -2125,12 +2214,17 @@ class CollectService {
|
|
|
2125
2214
|
return;
|
|
2126
2215
|
}
|
|
2127
2216
|
const notNode = this.#getChainedNode(modifierNode, "not");
|
|
2128
|
-
const
|
|
2129
|
-
if (!
|
|
2217
|
+
const matcherNameNode = this.#getChainedNode(notNode ?? modifierNode);
|
|
2218
|
+
if (!matcherNameNode) {
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
const matcherNode = this.#getMatcherNode(matcherNameNode);
|
|
2222
|
+
if (!matcherNode) {
|
|
2130
2223
|
return;
|
|
2131
2224
|
}
|
|
2132
|
-
const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, modifierNode, notNode);
|
|
2225
|
+
const assertionNode = new AssertionNode(this.#compiler, meta.brand, node, parent, meta.flags, matcherNode, matcherNameNode, modifierNode, notNode);
|
|
2133
2226
|
parent.children.push(assertionNode);
|
|
2227
|
+
this.#abilityLayer.handleNode(assertionNode);
|
|
2134
2228
|
EventEmitter.dispatch(["collect:node", { testNode: assertionNode }]);
|
|
2135
2229
|
this.#compiler.forEachChild(node, (node) => {
|
|
2136
2230
|
this.#collectTestTreeNodes(node, identifiers, assertionNode);
|
|
@@ -2149,7 +2243,9 @@ class CollectService {
|
|
|
2149
2243
|
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
2150
2244
|
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
2151
2245
|
EventEmitter.dispatch(["collect:start", { testTree }]);
|
|
2246
|
+
this.#abilityLayer.open(sourceFile);
|
|
2152
2247
|
this.#collectTestTreeNodes(sourceFile, new IdentifierLookup(this.#compiler), testTree);
|
|
2248
|
+
this.#abilityLayer.close();
|
|
2153
2249
|
EventEmitter.dispatch(["collect:end", { testTree }]);
|
|
2154
2250
|
return testTree;
|
|
2155
2251
|
}
|
|
@@ -2162,8 +2258,17 @@ class CollectService {
|
|
|
2162
2258
|
}
|
|
2163
2259
|
return parent;
|
|
2164
2260
|
}
|
|
2165
|
-
#
|
|
2166
|
-
|
|
2261
|
+
#getMatcherNode(node) {
|
|
2262
|
+
if (this.#compiler.isCallExpression(node.parent)) {
|
|
2263
|
+
return node.parent;
|
|
2264
|
+
}
|
|
2265
|
+
if (this.#compiler.isDecorator(node.parent)) {
|
|
2266
|
+
return node.parent;
|
|
2267
|
+
}
|
|
2268
|
+
if (this.#compiler.isParenthesizedExpression(node.parent)) {
|
|
2269
|
+
return this.#getMatcherNode(node.parent);
|
|
2270
|
+
}
|
|
2271
|
+
return;
|
|
2167
2272
|
}
|
|
2168
2273
|
}
|
|
2169
2274
|
|
|
@@ -3335,6 +3440,12 @@ class ExpectDiagnosticText {
|
|
|
3335
3440
|
static componentDoesNotAcceptProps(isTypeNode) {
|
|
3336
3441
|
return `${isTypeNode ? "Component type" : "Component"} does not accept props of the given type.`;
|
|
3337
3442
|
}
|
|
3443
|
+
static decoratorCanBeApplied(targetText) {
|
|
3444
|
+
return `The decorator function can be applied${targetText}.`;
|
|
3445
|
+
}
|
|
3446
|
+
static decoratorCanNotBeApplied(targetText) {
|
|
3447
|
+
return `The decorator function can not be applied${targetText}.`;
|
|
3448
|
+
}
|
|
3338
3449
|
static matcherIsNotSupported(matcherNameText) {
|
|
3339
3450
|
return `The '.${matcherNameText}()' matcher is not supported.`;
|
|
3340
3451
|
}
|
|
@@ -3738,6 +3849,76 @@ class ToBe extends RelationMatcherBase {
|
|
|
3738
3849
|
}
|
|
3739
3850
|
}
|
|
3740
3851
|
|
|
3852
|
+
class ToBeApplicable {
|
|
3853
|
+
#compiler;
|
|
3854
|
+
constructor(compiler) {
|
|
3855
|
+
this.#compiler = compiler;
|
|
3856
|
+
}
|
|
3857
|
+
#resolveTargetText(node) {
|
|
3858
|
+
let text = "";
|
|
3859
|
+
switch (node.kind) {
|
|
3860
|
+
case this.#compiler.SyntaxKind.ClassDeclaration:
|
|
3861
|
+
text = "class";
|
|
3862
|
+
break;
|
|
3863
|
+
case this.#compiler.SyntaxKind.MethodDeclaration:
|
|
3864
|
+
text = "method";
|
|
3865
|
+
break;
|
|
3866
|
+
case this.#compiler.SyntaxKind.PropertyDeclaration:
|
|
3867
|
+
text = node.modifiers?.some((modifier) => modifier.kind === this.#compiler.SyntaxKind.AccessorKeyword)
|
|
3868
|
+
? "accessor"
|
|
3869
|
+
: "field";
|
|
3870
|
+
break;
|
|
3871
|
+
case this.#compiler.SyntaxKind.GetAccessor:
|
|
3872
|
+
text = "getter";
|
|
3873
|
+
break;
|
|
3874
|
+
case this.#compiler.SyntaxKind.SetAccessor:
|
|
3875
|
+
text = "setter";
|
|
3876
|
+
break;
|
|
3877
|
+
}
|
|
3878
|
+
if (text.length > 0) {
|
|
3879
|
+
text = ` to this ${text}`;
|
|
3880
|
+
}
|
|
3881
|
+
return text;
|
|
3882
|
+
}
|
|
3883
|
+
#explain(matchWorker, sourceNode) {
|
|
3884
|
+
const targetText = this.#resolveTargetText(matchWorker.assertion.matcherNode.parent);
|
|
3885
|
+
const diagnostics = [];
|
|
3886
|
+
if (matchWorker.assertion.abilityDiagnostics) {
|
|
3887
|
+
for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
|
|
3888
|
+
const text = [
|
|
3889
|
+
ExpectDiagnosticText.decoratorCanNotBeApplied(targetText),
|
|
3890
|
+
typeof diagnostic.messageText === "string"
|
|
3891
|
+
? diagnostic.messageText
|
|
3892
|
+
: Diagnostic.toMessageText(diagnostic.messageText),
|
|
3893
|
+
];
|
|
3894
|
+
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
3895
|
+
diagnostics.push(Diagnostic.error(text.flat(), origin));
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
else {
|
|
3899
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
3900
|
+
diagnostics.push(Diagnostic.error(ExpectDiagnosticText.decoratorCanBeApplied(targetText), origin));
|
|
3901
|
+
}
|
|
3902
|
+
return diagnostics;
|
|
3903
|
+
}
|
|
3904
|
+
match(matchWorker, sourceNode, onDiagnostics) {
|
|
3905
|
+
const type = matchWorker.getType(sourceNode);
|
|
3906
|
+
if (type.getCallSignatures().length === 0) {
|
|
3907
|
+
const expectedText = "of a function type";
|
|
3908
|
+
const text = this.#compiler.isTypeNode(sourceNode)
|
|
3909
|
+
? ExpectDiagnosticText.typeArgumentMustBe("Source", expectedText)
|
|
3910
|
+
: ExpectDiagnosticText.argumentMustBe("source", expectedText);
|
|
3911
|
+
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
3912
|
+
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3915
|
+
return {
|
|
3916
|
+
explain: () => this.#explain(matchWorker, sourceNode),
|
|
3917
|
+
isMatch: !matchWorker.assertion.abilityDiagnostics,
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3741
3922
|
class ToBeAssignableTo extends RelationMatcherBase {
|
|
3742
3923
|
explainText = ExpectDiagnosticText.typeIsAssignableTo;
|
|
3743
3924
|
explainNotText = ExpectDiagnosticText.typeIsNotAssignableTo;
|
|
@@ -3908,6 +4089,7 @@ class ExpectService {
|
|
|
3908
4089
|
#typeChecker;
|
|
3909
4090
|
toAcceptProps;
|
|
3910
4091
|
toBe;
|
|
4092
|
+
toBeApplicable;
|
|
3911
4093
|
toBeAssignableTo;
|
|
3912
4094
|
toBeAssignableWith;
|
|
3913
4095
|
toHaveProperty;
|
|
@@ -3923,41 +4105,42 @@ class ExpectService {
|
|
|
3923
4105
|
}
|
|
3924
4106
|
this.toAcceptProps = new ToAcceptProps(compiler, typeChecker);
|
|
3925
4107
|
this.toBe = new ToBe();
|
|
4108
|
+
this.toBeApplicable = new ToBeApplicable(compiler);
|
|
3926
4109
|
this.toBeAssignableTo = new ToBeAssignableTo();
|
|
3927
4110
|
this.toBeAssignableWith = new ToBeAssignableWith();
|
|
3928
4111
|
this.toHaveProperty = new ToHaveProperty(compiler);
|
|
3929
4112
|
this.toRaiseError = new ToRaiseError(compiler);
|
|
3930
4113
|
}
|
|
3931
4114
|
match(assertion, onDiagnostics) {
|
|
3932
|
-
const matcherNameText = assertion.
|
|
4115
|
+
const matcherNameText = assertion.matcherNameNode.name.text;
|
|
3933
4116
|
if (!assertion.source[0]) {
|
|
3934
4117
|
this.#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
3935
4118
|
return;
|
|
3936
4119
|
}
|
|
3937
4120
|
const matchWorker = new MatchWorker(this.#compiler, this.#typeChecker, assertion);
|
|
4121
|
+
if (!(matcherNameText === "toRaiseError" && assertion.isNot === false) &&
|
|
4122
|
+
this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
4123
|
+
return;
|
|
4124
|
+
}
|
|
3938
4125
|
switch (matcherNameText) {
|
|
3939
4126
|
case "toAcceptProps":
|
|
3940
4127
|
case "toBe":
|
|
3941
4128
|
case "toBeAssignableTo":
|
|
3942
4129
|
case "toBeAssignableWith":
|
|
3943
|
-
if (!assertion.target[0]) {
|
|
4130
|
+
if (!assertion.target?.[0]) {
|
|
3944
4131
|
this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
3945
4132
|
return;
|
|
3946
4133
|
}
|
|
3947
|
-
if (this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
3948
|
-
return;
|
|
3949
|
-
}
|
|
3950
4134
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
4135
|
+
case "toBeApplicable":
|
|
4136
|
+
return this.toBeApplicable.match(matchWorker, assertion.source[0], onDiagnostics);
|
|
3951
4137
|
case "toHaveProperty":
|
|
3952
|
-
if (!assertion.target[0]) {
|
|
4138
|
+
if (!assertion.target?.[0]) {
|
|
3953
4139
|
this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
|
|
3954
4140
|
return;
|
|
3955
4141
|
}
|
|
3956
4142
|
return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3957
4143
|
case "toRaiseError":
|
|
3958
|
-
if (assertion.isNot && this.#rejectsTypeArguments(matchWorker, onDiagnostics)) {
|
|
3959
|
-
return;
|
|
3960
|
-
}
|
|
3961
4144
|
return this.toRaiseError.match(matchWorker, assertion.source[0], [...assertion.target], onDiagnostics);
|
|
3962
4145
|
default:
|
|
3963
4146
|
this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
|
|
@@ -3966,7 +4149,7 @@ class ExpectService {
|
|
|
3966
4149
|
}
|
|
3967
4150
|
#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics) {
|
|
3968
4151
|
const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
|
|
3969
|
-
const origin = DiagnosticOrigin.fromNode(assertion.
|
|
4152
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
|
|
3970
4153
|
onDiagnostics(Diagnostic.error(text, origin));
|
|
3971
4154
|
}
|
|
3972
4155
|
#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
|
|
@@ -3976,23 +4159,23 @@ class ExpectService {
|
|
|
3976
4159
|
}
|
|
3977
4160
|
#onTargetArgumentMustBeProvided(argumentNameText, assertion, onDiagnostics) {
|
|
3978
4161
|
const text = ExpectDiagnosticText.argumentMustBeProvided(argumentNameText);
|
|
3979
|
-
const origin = DiagnosticOrigin.fromNode(assertion.
|
|
4162
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
|
|
3980
4163
|
onDiagnostics(Diagnostic.error(text, origin));
|
|
3981
4164
|
}
|
|
3982
4165
|
#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
|
|
3983
4166
|
const text = ExpectDiagnosticText.argumentOrTypeArgumentMustBeProvided("target", "Target");
|
|
3984
|
-
const origin = DiagnosticOrigin.fromNode(assertion.
|
|
4167
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherNameNode.name);
|
|
3985
4168
|
onDiagnostics(Diagnostic.error(text, origin));
|
|
3986
4169
|
}
|
|
3987
4170
|
#rejectsTypeArguments(matchWorker, onDiagnostics) {
|
|
3988
4171
|
for (const rejectedType of this.#rejectTypes) {
|
|
3989
4172
|
const allowedKeyword = this.#compiler.SyntaxKind[`${Format.capitalize(rejectedType)}Keyword`];
|
|
3990
4173
|
if (matchWorker.assertion.source[0]?.kind === allowedKeyword ||
|
|
3991
|
-
matchWorker.assertion.target[0]?.kind === allowedKeyword) {
|
|
4174
|
+
matchWorker.assertion.target?.[0]?.kind === allowedKeyword) {
|
|
3992
4175
|
continue;
|
|
3993
4176
|
}
|
|
3994
4177
|
for (const argumentName of ["source", "target"]) {
|
|
3995
|
-
const argumentNode = matchWorker.assertion[argumentName][0];
|
|
4178
|
+
const argumentNode = matchWorker.assertion[argumentName]?.[0];
|
|
3996
4179
|
if (!argumentNode) {
|
|
3997
4180
|
continue;
|
|
3998
4181
|
}
|
|
@@ -4089,7 +4272,7 @@ class TestTreeWalker {
|
|
|
4089
4272
|
{ diagnostics: Array.isArray(diagnostics) ? diagnostics : [diagnostics], result: expectResult },
|
|
4090
4273
|
]);
|
|
4091
4274
|
};
|
|
4092
|
-
if (assertion.diagnostics.size > 0 && assertion.
|
|
4275
|
+
if (assertion.diagnostics.size > 0 && assertion.matcherNameNode.name.text !== "toRaiseError") {
|
|
4093
4276
|
onExpectDiagnostics(Diagnostic.fromDiagnostics([...assertion.diagnostics]));
|
|
4094
4277
|
return;
|
|
4095
4278
|
}
|
|
@@ -4173,8 +4356,8 @@ class TaskRunner {
|
|
|
4173
4356
|
constructor(resolvedConfig, compiler) {
|
|
4174
4357
|
this.#resolvedConfig = resolvedConfig;
|
|
4175
4358
|
this.#compiler = compiler;
|
|
4176
|
-
this.#collectService = new CollectService(compiler);
|
|
4177
4359
|
this.#projectService = new ProjectService(this.#resolvedConfig, compiler);
|
|
4360
|
+
this.#collectService = new CollectService(compiler, this.#projectService, this.#resolvedConfig);
|
|
4178
4361
|
}
|
|
4179
4362
|
run(task, cancellationToken) {
|
|
4180
4363
|
if (cancellationToken?.isCancellationRequested) {
|
|
@@ -4238,7 +4421,7 @@ class TaskRunner {
|
|
|
4238
4421
|
class Runner {
|
|
4239
4422
|
#eventEmitter = new EventEmitter();
|
|
4240
4423
|
#resolvedConfig;
|
|
4241
|
-
static version = "4.0.0-beta.
|
|
4424
|
+
static version = "4.0.0-beta.1";
|
|
4242
4425
|
constructor(resolvedConfig) {
|
|
4243
4426
|
this.#resolvedConfig = resolvedConfig;
|
|
4244
4427
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tstyche",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.1",
|
|
4
4
|
"description": "The Essential Type Testing Tool.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -60,17 +60,17 @@
|
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@biomejs/biome": "1.9.4",
|
|
62
62
|
"@rollup/plugin-typescript": "12.1.2",
|
|
63
|
-
"@types/node": "22.
|
|
64
|
-
"@types/react": "19.0
|
|
63
|
+
"@types/node": "22.14.0",
|
|
64
|
+
"@types/react": "19.1.0",
|
|
65
65
|
"ajv": "8.17.1",
|
|
66
66
|
"cspell": "8.18.1",
|
|
67
67
|
"magic-string": "0.30.17",
|
|
68
68
|
"monocart-coverage-reports": "2.12.3",
|
|
69
69
|
"pretty-ansi": "3.0.0",
|
|
70
|
-
"rollup": "4.
|
|
70
|
+
"rollup": "4.39.0",
|
|
71
71
|
"rollup-plugin-dts": "6.2.1",
|
|
72
72
|
"tslib": "2.8.1",
|
|
73
|
-
"typescript": "5.8.
|
|
73
|
+
"typescript": "5.8.3"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"typescript": "5.x"
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"optional": true
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
-
"packageManager": "yarn@4.8.
|
|
83
|
+
"packageManager": "yarn@4.8.1",
|
|
84
84
|
"engines": {
|
|
85
85
|
"node": ">=20.9"
|
|
86
86
|
}
|