tstyche 4.0.0-beta.3 → 4.0.0-beta.4
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 +2 -1
- package/build/index.d.cts +5 -1
- package/build/index.d.ts +5 -1
- package/build/tstyche.d.ts +7 -2
- package/build/tstyche.js +206 -104
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -63,7 +63,8 @@ Here is the list of all matchers:
|
|
|
63
63
|
- `.toBe()`, `.toBeAssignableTo()`, `.toBeAssignableWith()` compare types or types of expression,
|
|
64
64
|
- `.toAcceptProps()` checks the type of JSX component props,
|
|
65
65
|
- `.toBeApplicable` ensures that the decorator function can be applied,
|
|
66
|
-
- `.toBeCallableWith()` checks whether a function
|
|
66
|
+
- `.toBeCallableWith()` checks whether a function is callable with the given arguments,
|
|
67
|
+
- `.toBeConstructableWith()` checks whether a class is constructable with the given arguments,
|
|
67
68
|
- `.toHaveProperty()` looks up keys on an object type,
|
|
68
69
|
- `.toRaiseError()` captures the message or code of a type error.
|
|
69
70
|
|
package/build/index.d.cts
CHANGED
|
@@ -128,9 +128,13 @@ interface Matchers {
|
|
|
128
128
|
(target: unknown): void;
|
|
129
129
|
};
|
|
130
130
|
/**
|
|
131
|
-
* Checks if the source type
|
|
131
|
+
* Checks if the source type is callable with the given arguments.
|
|
132
132
|
*/
|
|
133
133
|
toBeCallableWith: (...target: Array<unknown>) => void;
|
|
134
|
+
/**
|
|
135
|
+
* Checks if the source type is constructable with the given arguments.
|
|
136
|
+
*/
|
|
137
|
+
toBeConstructableWith: (...target: Array<unknown>) => void;
|
|
134
138
|
/**
|
|
135
139
|
* Checks if a property key exists on the source type.
|
|
136
140
|
*/
|
package/build/index.d.ts
CHANGED
|
@@ -128,9 +128,13 @@ interface Matchers {
|
|
|
128
128
|
(target: unknown): void;
|
|
129
129
|
};
|
|
130
130
|
/**
|
|
131
|
-
* Checks if the source type
|
|
131
|
+
* Checks if the source type is callable with the given arguments.
|
|
132
132
|
*/
|
|
133
133
|
toBeCallableWith: (...target: Array<unknown>) => void;
|
|
134
|
+
/**
|
|
135
|
+
* Checks if the source type is constructable with the given arguments.
|
|
136
|
+
*/
|
|
137
|
+
toBeConstructableWith: (...target: Array<unknown>) => void;
|
|
134
138
|
/**
|
|
135
139
|
* Checks if a property key exists on the source type.
|
|
136
140
|
*/
|
package/build/tstyche.d.ts
CHANGED
|
@@ -62,10 +62,14 @@ declare class Diagnostic {
|
|
|
62
62
|
static error(text: string | Array<string>, origin?: DiagnosticOrigin): Diagnostic;
|
|
63
63
|
extendWith(text: string | Array<string>, origin?: DiagnosticOrigin): Diagnostic;
|
|
64
64
|
static fromDiagnostics(diagnostics: Array<ts.Diagnostic>): Array<Diagnostic>;
|
|
65
|
-
static toMessageText(chain: ts.DiagnosticMessageChain): Array<string>;
|
|
66
65
|
static warning(text: string | Array<string>, origin?: DiagnosticOrigin): Diagnostic;
|
|
67
66
|
}
|
|
68
67
|
|
|
68
|
+
declare function diagnosticBelongsToNode(diagnostic: ts.Diagnostic, node: ts.NodeArray<ts.Node> | ts.Node): boolean;
|
|
69
|
+
declare function getDiagnosticMessageText(diagnostic: ts.Diagnostic): string | Array<string>;
|
|
70
|
+
declare function getTextSpanEnd(span: ts.TextSpan): number;
|
|
71
|
+
declare function isDiagnosticWithLocation(diagnostic: ts.Diagnostic): diagnostic is ts.DiagnosticWithLocation;
|
|
72
|
+
|
|
69
73
|
type DiagnosticsHandler<T extends Diagnostic | Array<Diagnostic> = Diagnostic> = (this: void, diagnostics: T) => void;
|
|
70
74
|
|
|
71
75
|
declare enum TestTreeNodeBrand {
|
|
@@ -604,6 +608,7 @@ declare class ExpectService {
|
|
|
604
608
|
private toBeAssignableTo;
|
|
605
609
|
private toBeAssignableWith;
|
|
606
610
|
private toBeCallableWith;
|
|
611
|
+
private toBeConstructableWith;
|
|
607
612
|
private toHaveProperty;
|
|
608
613
|
private toRaiseError;
|
|
609
614
|
constructor(compiler: typeof ts, typeChecker: TypeChecker, resolvedConfig: ResolvedConfig);
|
|
@@ -868,5 +873,5 @@ declare class WatchService {
|
|
|
868
873
|
watch(cancellationToken: CancellationToken): AsyncIterable<Array<Task>>;
|
|
869
874
|
}
|
|
870
875
|
|
|
871
|
-
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, ScribblerJsx, 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 };
|
|
876
|
+
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, ScribblerJsx, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeHandler, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|
|
872
877
|
export type { CodeFrameOptions, CommandLineOptions, ConfigFileOptions, DiagnosticsHandler, EnvironmentOptions, Event, EventHandler, FileWatchHandler, InputHandler, ItemDefinition, MatchResult, OptionDefinition, Plugin, Reporter, ReporterEvent, ResolvedConfig, ScribblerOptions, SelectHookContext, TargetResultStatus, TaskResultStatus, TypeChecker, WatchHandler, WatcherOptions };
|
package/build/tstyche.js
CHANGED
|
@@ -104,6 +104,30 @@ class DiagnosticOrigin {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
function diagnosticBelongsToNode(diagnostic, node) {
|
|
108
|
+
return diagnostic.start != null && diagnostic.start >= node.pos && diagnostic.start <= node.end;
|
|
109
|
+
}
|
|
110
|
+
function diagnosticMessageChainToText(chain) {
|
|
111
|
+
const result = [chain.messageText];
|
|
112
|
+
if (chain.next != null) {
|
|
113
|
+
for (const nextChain of chain.next) {
|
|
114
|
+
result.push(...diagnosticMessageChainToText(nextChain));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
function getDiagnosticMessageText(diagnostic) {
|
|
120
|
+
return typeof diagnostic.messageText === "string"
|
|
121
|
+
? diagnostic.messageText
|
|
122
|
+
: diagnosticMessageChainToText(diagnostic.messageText);
|
|
123
|
+
}
|
|
124
|
+
function getTextSpanEnd(span) {
|
|
125
|
+
return span.start + span.length;
|
|
126
|
+
}
|
|
127
|
+
function isDiagnosticWithLocation(diagnostic) {
|
|
128
|
+
return diagnostic.file != null && diagnostic.start != null && diagnostic.length != null;
|
|
129
|
+
}
|
|
130
|
+
|
|
107
131
|
class Diagnostic {
|
|
108
132
|
category;
|
|
109
133
|
code;
|
|
@@ -141,21 +165,10 @@ class Diagnostic {
|
|
|
141
165
|
if (diagnostic.relatedInformation != null) {
|
|
142
166
|
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
143
167
|
}
|
|
144
|
-
const text =
|
|
145
|
-
? diagnostic.messageText
|
|
146
|
-
: Diagnostic.toMessageText(diagnostic.messageText);
|
|
168
|
+
const text = getDiagnosticMessageText(diagnostic);
|
|
147
169
|
return new Diagnostic(text, DiagnosticCategory.Error, origin).add({ code, related });
|
|
148
170
|
});
|
|
149
171
|
}
|
|
150
|
-
static toMessageText(chain) {
|
|
151
|
-
const result = [chain.messageText];
|
|
152
|
-
if (chain.next != null) {
|
|
153
|
-
for (const nextChain of chain.next) {
|
|
154
|
-
result.push(...Diagnostic.toMessageText(nextChain));
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return result;
|
|
158
|
-
}
|
|
159
172
|
static warning(text, origin) {
|
|
160
173
|
return new Diagnostic(text, DiagnosticCategory.Warning, origin);
|
|
161
174
|
}
|
|
@@ -1927,13 +1940,9 @@ class TestTreeNode {
|
|
|
1927
1940
|
if (node.arguments[0] != null && compiler.isStringLiteralLike(node.arguments[0])) {
|
|
1928
1941
|
this.name = node.arguments[0].text;
|
|
1929
1942
|
}
|
|
1930
|
-
if (node.arguments[1] != null &&
|
|
1931
|
-
compiler.isFunctionLike(node.arguments[1]) &&
|
|
1932
|
-
compiler.isBlock(node.arguments[1].body)) {
|
|
1933
|
-
const blockStart = node.arguments[1].body.getStart();
|
|
1934
|
-
const blockEnd = node.arguments[1].body.getEnd();
|
|
1943
|
+
if (node.arguments[1] != null && compiler.isFunctionLike(node.arguments[1])) {
|
|
1935
1944
|
for (const diagnostic of parent.diagnostics) {
|
|
1936
|
-
if (diagnostic
|
|
1945
|
+
if (diagnosticBelongsToNode(diagnostic, node.arguments[1].body)) {
|
|
1937
1946
|
this.diagnostics.add(diagnostic);
|
|
1938
1947
|
parent.diagnostics.delete(diagnostic);
|
|
1939
1948
|
}
|
|
@@ -1990,7 +1999,7 @@ class AssertionNode extends TestTreeNode {
|
|
|
1990
1999
|
this.target = this.matcherNode.typeArguments ?? this.matcherNode.arguments;
|
|
1991
2000
|
}
|
|
1992
2001
|
for (const diagnostic of parent.diagnostics) {
|
|
1993
|
-
if (diagnostic
|
|
2002
|
+
if (diagnosticBelongsToNode(diagnostic, this.source)) {
|
|
1994
2003
|
this.diagnostics.add(diagnostic);
|
|
1995
2004
|
parent.diagnostics.delete(diagnostic);
|
|
1996
2005
|
}
|
|
@@ -2029,7 +2038,9 @@ class AbilityLayer {
|
|
|
2029
2038
|
#addRanges(node, ranges) {
|
|
2030
2039
|
this.#nodes.push(node);
|
|
2031
2040
|
for (const range of ranges) {
|
|
2032
|
-
const rangeText =
|
|
2041
|
+
const rangeText = range.replacement != null
|
|
2042
|
+
? `${range.replacement}${this.#getErasedRangeText(range).slice(range.replacement.length)}`
|
|
2043
|
+
: this.#getErasedRangeText(range);
|
|
2033
2044
|
this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
|
|
2034
2045
|
}
|
|
2035
2046
|
}
|
|
@@ -2038,12 +2049,9 @@ class AbilityLayer {
|
|
|
2038
2049
|
this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
|
|
2039
2050
|
const languageService = this.#projectService.getLanguageService(this.#filePath);
|
|
2040
2051
|
const diagnostics = new Set(languageService?.getSemanticDiagnostics(this.#filePath));
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
if (diagnostic.start != null &&
|
|
2045
|
-
diagnostic.start >= node.matcherNode.pos &&
|
|
2046
|
-
diagnostic.start <= node.matcherNode.end) {
|
|
2052
|
+
for (const node of this.#nodes.reverse()) {
|
|
2053
|
+
for (const diagnostic of diagnostics) {
|
|
2054
|
+
if (diagnosticBelongsToNode(diagnostic, node.matcherNode)) {
|
|
2047
2055
|
if (!node.abilityDiagnostics) {
|
|
2048
2056
|
node.abilityDiagnostics = new Set();
|
|
2049
2057
|
}
|
|
@@ -2058,29 +2066,24 @@ class AbilityLayer {
|
|
|
2058
2066
|
this.#text = "";
|
|
2059
2067
|
}
|
|
2060
2068
|
handleNode(assertionNode) {
|
|
2069
|
+
const expectStart = assertionNode.node.getStart();
|
|
2070
|
+
const expectExpressionEnd = assertionNode.node.expression.getEnd();
|
|
2071
|
+
const expectEnd = assertionNode.node.getEnd();
|
|
2072
|
+
const matcherNameEnd = assertionNode.matcherNameNode.getEnd();
|
|
2061
2073
|
switch (assertionNode.matcherNameNode.name.text) {
|
|
2062
|
-
case "toBeApplicable":
|
|
2063
|
-
|
|
2064
|
-
const expectExpressionEnd = assertionNode.node.expression.end;
|
|
2065
|
-
const expectEnd = assertionNode.node.end;
|
|
2066
|
-
const matcherNameEnd = assertionNode.matcherNameNode.end;
|
|
2074
|
+
case "toBeApplicable":
|
|
2075
|
+
case "toBeCallableWith":
|
|
2067
2076
|
this.#addRanges(assertionNode, [
|
|
2068
|
-
{ end: expectExpressionEnd
|
|
2069
|
-
{ end: matcherNameEnd, start: expectEnd
|
|
2077
|
+
{ end: expectExpressionEnd, start: expectStart },
|
|
2078
|
+
{ end: matcherNameEnd, start: expectEnd },
|
|
2070
2079
|
]);
|
|
2071
2080
|
break;
|
|
2072
|
-
|
|
2073
|
-
case "toBeCallableWith": {
|
|
2074
|
-
const expectStart = assertionNode.node.pos;
|
|
2075
|
-
const expectExpressionEnd = assertionNode.node.expression.end;
|
|
2076
|
-
const expectEnd = assertionNode.node.end;
|
|
2077
|
-
const matcherNameEnd = assertionNode.matcherNameNode.end;
|
|
2081
|
+
case "toBeConstructableWith":
|
|
2078
2082
|
this.#addRanges(assertionNode, [
|
|
2079
|
-
{ end: expectExpressionEnd
|
|
2080
|
-
{ end: matcherNameEnd, start: expectEnd
|
|
2083
|
+
{ end: expectExpressionEnd, start: expectStart, replacement: "new" },
|
|
2084
|
+
{ end: matcherNameEnd, start: expectEnd },
|
|
2081
2085
|
]);
|
|
2082
2086
|
break;
|
|
2083
|
-
}
|
|
2084
2087
|
}
|
|
2085
2088
|
}
|
|
2086
2089
|
open(sourceFile) {
|
|
@@ -3448,11 +3451,17 @@ class ExpectDiagnosticText {
|
|
|
3448
3451
|
static argumentMustBeProvided(argumentNameText) {
|
|
3449
3452
|
return `An argument for '${argumentNameText}' must be provided.`;
|
|
3450
3453
|
}
|
|
3451
|
-
static
|
|
3452
|
-
return `${isTypeNode ? "Type" : "Expression"}
|
|
3454
|
+
static isCallable(isTypeNode, targetText) {
|
|
3455
|
+
return `${isTypeNode ? "Type" : "Expression"} is callable ${targetText}.`;
|
|
3456
|
+
}
|
|
3457
|
+
static isNotCallable(isTypeNode, targetText) {
|
|
3458
|
+
return `${isTypeNode ? "Type" : "Expression"} is not callable ${targetText}.`;
|
|
3459
|
+
}
|
|
3460
|
+
static isConstructable(isTypeNode, targetText) {
|
|
3461
|
+
return `${isTypeNode ? "Type" : "Expression"} is constructable ${targetText}.`;
|
|
3453
3462
|
}
|
|
3454
|
-
static
|
|
3455
|
-
return `${isTypeNode ? "Type" : "Expression"}
|
|
3463
|
+
static isNotConstructable(isTypeNode, targetText) {
|
|
3464
|
+
return `${isTypeNode ? "Type" : "Expression"} is not constructable ${targetText}.`;
|
|
3456
3465
|
}
|
|
3457
3466
|
static acceptsProps(isTypeNode) {
|
|
3458
3467
|
return `${isTypeNode ? "Component type" : "Component"} accepts props of the given type.`;
|
|
@@ -3472,6 +3481,9 @@ class ExpectDiagnosticText {
|
|
|
3472
3481
|
static hasProperty(typeText, propertyNameText) {
|
|
3473
3482
|
return `Type '${typeText}' has property '${propertyNameText}'.`;
|
|
3474
3483
|
}
|
|
3484
|
+
static didYouMeanToUse(suggestionText) {
|
|
3485
|
+
return `Did you mean to use ${suggestionText}?`;
|
|
3486
|
+
}
|
|
3475
3487
|
static matcherIsNotSupported(matcherNameText) {
|
|
3476
3488
|
return `The '.${matcherNameText}()' matcher is not supported.`;
|
|
3477
3489
|
}
|
|
@@ -3543,18 +3555,18 @@ class MatchWorker {
|
|
|
3543
3555
|
assertion;
|
|
3544
3556
|
#compiler;
|
|
3545
3557
|
#signatureCache = new Map();
|
|
3546
|
-
|
|
3558
|
+
typeChecker;
|
|
3547
3559
|
constructor(compiler, typeChecker, assertion) {
|
|
3548
3560
|
this.#compiler = compiler;
|
|
3549
|
-
this
|
|
3561
|
+
this.typeChecker = typeChecker;
|
|
3550
3562
|
this.assertion = assertion;
|
|
3551
3563
|
}
|
|
3552
3564
|
checkHasApplicableIndexType(sourceNode, targetNode) {
|
|
3553
3565
|
const sourceType = this.getType(sourceNode);
|
|
3554
3566
|
const targetType = this.getType(targetNode);
|
|
3555
|
-
return this
|
|
3567
|
+
return this.typeChecker
|
|
3556
3568
|
.getIndexInfosOfType(sourceType)
|
|
3557
|
-
.some(({ keyType }) => this
|
|
3569
|
+
.some(({ keyType }) => this.typeChecker.isApplicableIndexType(targetType, keyType));
|
|
3558
3570
|
}
|
|
3559
3571
|
checkHasProperty(sourceNode, propertyNameText) {
|
|
3560
3572
|
const sourceType = this.getType(sourceNode);
|
|
@@ -3563,31 +3575,31 @@ class MatchWorker {
|
|
|
3563
3575
|
.some((property) => this.#compiler.unescapeLeadingUnderscores(property.escapedName) === propertyNameText);
|
|
3564
3576
|
}
|
|
3565
3577
|
checkIsAssignableTo(sourceNode, targetNode) {
|
|
3566
|
-
const relation = this
|
|
3578
|
+
const relation = this.typeChecker.relation.assignable;
|
|
3567
3579
|
return this.#checkIsRelatedTo(sourceNode, targetNode, relation);
|
|
3568
3580
|
}
|
|
3569
3581
|
checkIsAssignableWith(sourceNode, targetNode) {
|
|
3570
|
-
const relation = this
|
|
3582
|
+
const relation = this.typeChecker.relation.assignable;
|
|
3571
3583
|
return this.#checkIsRelatedTo(targetNode, sourceNode, relation);
|
|
3572
3584
|
}
|
|
3573
3585
|
checkIsIdenticalTo(sourceNode, targetNode) {
|
|
3574
|
-
const relation = this
|
|
3586
|
+
const relation = this.typeChecker.relation.identity;
|
|
3575
3587
|
return (this.#checkIsRelatedTo(sourceNode, targetNode, relation) &&
|
|
3576
3588
|
this.checkIsAssignableTo(sourceNode, targetNode) &&
|
|
3577
3589
|
this.checkIsAssignableWith(sourceNode, targetNode));
|
|
3578
3590
|
}
|
|
3579
3591
|
#checkIsRelatedTo(sourceNode, targetNode, relation) {
|
|
3580
|
-
const sourceType = relation === this
|
|
3592
|
+
const sourceType = relation === this.typeChecker.relation.identity
|
|
3581
3593
|
? this.#simplifyType(this.getType(sourceNode))
|
|
3582
3594
|
: this.getType(sourceNode);
|
|
3583
|
-
const targetType = relation === this
|
|
3595
|
+
const targetType = relation === this.typeChecker.relation.identity
|
|
3584
3596
|
? this.#simplifyType(this.getType(targetNode))
|
|
3585
3597
|
: this.getType(targetNode);
|
|
3586
|
-
return this
|
|
3598
|
+
return this.typeChecker.isTypeRelatedTo(sourceType, targetType, relation);
|
|
3587
3599
|
}
|
|
3588
3600
|
extendsObjectType(type) {
|
|
3589
3601
|
const nonPrimitiveType = { flags: this.#compiler.TypeFlags.NonPrimitive };
|
|
3590
|
-
return this
|
|
3602
|
+
return this.typeChecker.isTypeAssignableTo(type, nonPrimitiveType);
|
|
3591
3603
|
}
|
|
3592
3604
|
getParameterType(signature, index) {
|
|
3593
3605
|
const parameter = signature.getDeclaration().parameters[index];
|
|
@@ -3608,23 +3620,10 @@ class MatchWorker {
|
|
|
3608
3620
|
return signatures;
|
|
3609
3621
|
}
|
|
3610
3622
|
getTypeText(node) {
|
|
3611
|
-
|
|
3612
|
-
return this.#typeChecker.typeToString(type);
|
|
3623
|
+
return this.typeChecker.typeToString(this.getType(node));
|
|
3613
3624
|
}
|
|
3614
3625
|
getType(node) {
|
|
3615
|
-
return this
|
|
3616
|
-
}
|
|
3617
|
-
isStringOrNumberLiteralType(type) {
|
|
3618
|
-
return !!(type.flags & this.#compiler.TypeFlags.StringOrNumberLiteral);
|
|
3619
|
-
}
|
|
3620
|
-
isObjectType(type) {
|
|
3621
|
-
return !!(type.flags & this.#compiler.TypeFlags.Object);
|
|
3622
|
-
}
|
|
3623
|
-
isUnionType(type) {
|
|
3624
|
-
return !!(type.flags & this.#compiler.TypeFlags.Union);
|
|
3625
|
-
}
|
|
3626
|
-
isUniqueSymbolType(type) {
|
|
3627
|
-
return !!(type.flags & this.#compiler.TypeFlags.UniqueESSymbol);
|
|
3626
|
+
return this.typeChecker.getTypeAtLocation(node);
|
|
3628
3627
|
}
|
|
3629
3628
|
resolveDiagnosticOrigin(symbol, enclosingNode) {
|
|
3630
3629
|
if (symbol.valueDeclaration != null &&
|
|
@@ -3640,7 +3639,7 @@ class MatchWorker {
|
|
|
3640
3639
|
#simplifyType(type) {
|
|
3641
3640
|
if (type.isUnionOrIntersection()) {
|
|
3642
3641
|
const candidateType = this.#simplifyType(type.types[0]);
|
|
3643
|
-
if (type.types.every((type) => this
|
|
3642
|
+
if (type.types.every((type) => this.typeChecker.isTypeRelatedTo(this.#simplifyType(type), candidateType, this.typeChecker.relation.identity))) {
|
|
3644
3643
|
return candidateType;
|
|
3645
3644
|
}
|
|
3646
3645
|
}
|
|
@@ -3648,6 +3647,16 @@ class MatchWorker {
|
|
|
3648
3647
|
}
|
|
3649
3648
|
}
|
|
3650
3649
|
|
|
3650
|
+
function isStringOrNumberLiteralType(compiler, type) {
|
|
3651
|
+
return !!(type.flags & compiler.TypeFlags.StringOrNumberLiteral);
|
|
3652
|
+
}
|
|
3653
|
+
function isUnionType(compiler, type) {
|
|
3654
|
+
return !!(type.flags & compiler.TypeFlags.Union);
|
|
3655
|
+
}
|
|
3656
|
+
function isUniqueSymbolType(compiler, type) {
|
|
3657
|
+
return !!(type.flags & compiler.TypeFlags.UniqueESSymbol);
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3651
3660
|
class ToAcceptProps {
|
|
3652
3661
|
#compiler;
|
|
3653
3662
|
#typeChecker;
|
|
@@ -3681,7 +3690,7 @@ class ToAcceptProps {
|
|
|
3681
3690
|
#isOptionalProperty(symbol) {
|
|
3682
3691
|
return symbol.declarations?.every((declaration) => this.#compiler.isPropertySignature(declaration) && declaration.questionToken != null);
|
|
3683
3692
|
}
|
|
3684
|
-
#checkProperties(
|
|
3693
|
+
#checkProperties(sourceType, targetType) {
|
|
3685
3694
|
const check = (sourceType, targetType) => {
|
|
3686
3695
|
for (const targetProperty of targetType.getProperties()) {
|
|
3687
3696
|
const targetPropertyName = targetProperty.getName();
|
|
@@ -3709,7 +3718,7 @@ class ToAcceptProps {
|
|
|
3709
3718
|
}
|
|
3710
3719
|
return true;
|
|
3711
3720
|
};
|
|
3712
|
-
if (sourceType != null &&
|
|
3721
|
+
if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
|
|
3713
3722
|
return sourceType.types.some((sourceType) => check(sourceType, targetType));
|
|
3714
3723
|
}
|
|
3715
3724
|
return check(sourceType, targetType);
|
|
@@ -3777,7 +3786,7 @@ class ToAcceptProps {
|
|
|
3777
3786
|
}
|
|
3778
3787
|
return { diagnostics, isMatch: false };
|
|
3779
3788
|
};
|
|
3780
|
-
if (sourceType != null &&
|
|
3789
|
+
if (sourceType != null && isUnionType(this.#compiler, sourceType)) {
|
|
3781
3790
|
let accumulator = [];
|
|
3782
3791
|
const isMatch = sourceType.types.some((sourceType) => {
|
|
3783
3792
|
const text = matchWorker.assertion.isNot
|
|
@@ -3808,7 +3817,7 @@ class ToAcceptProps {
|
|
|
3808
3817
|
diagnostics.push(Diagnostic.error(text, origin));
|
|
3809
3818
|
}
|
|
3810
3819
|
const targetType = matchWorker.getType(targetNode);
|
|
3811
|
-
if (!
|
|
3820
|
+
if (!(targetType.flags & this.#compiler.TypeFlags.Object)) {
|
|
3812
3821
|
const expectedText = "of an object type";
|
|
3813
3822
|
const text = this.#compiler.isTypeNode(targetNode)
|
|
3814
3823
|
? ExpectDiagnosticText.typeArgumentMustBe("Target", expectedText)
|
|
@@ -3822,7 +3831,7 @@ class ToAcceptProps {
|
|
|
3822
3831
|
}
|
|
3823
3832
|
const isMatch = signatures.some((signature) => {
|
|
3824
3833
|
const sourceType = matchWorker.getParameterType(signature, 0);
|
|
3825
|
-
return this.#checkProperties(
|
|
3834
|
+
return this.#checkProperties(sourceType, targetType);
|
|
3826
3835
|
});
|
|
3827
3836
|
return {
|
|
3828
3837
|
explain: () => this.#explain(matchWorker, sourceNode, targetNode),
|
|
@@ -3890,12 +3899,7 @@ class ToBeApplicable {
|
|
|
3890
3899
|
const diagnostics = [];
|
|
3891
3900
|
if (matchWorker.assertion.abilityDiagnostics) {
|
|
3892
3901
|
for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
|
|
3893
|
-
const text = [
|
|
3894
|
-
ExpectDiagnosticText.cannotBeApplied(targetText),
|
|
3895
|
-
typeof diagnostic.messageText === "string"
|
|
3896
|
-
? diagnostic.messageText
|
|
3897
|
-
: Diagnostic.toMessageText(diagnostic.messageText),
|
|
3898
|
-
];
|
|
3902
|
+
const text = [ExpectDiagnosticText.cannotBeApplied(targetText), getDiagnosticMessageText(diagnostic)];
|
|
3899
3903
|
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
3900
3904
|
diagnostics.push(Diagnostic.error(text.flat(), origin));
|
|
3901
3905
|
}
|
|
@@ -3951,8 +3955,83 @@ class ToBeCallableWith {
|
|
|
3951
3955
|
constructor(compiler) {
|
|
3952
3956
|
this.#compiler = compiler;
|
|
3953
3957
|
}
|
|
3954
|
-
|
|
3955
|
-
|
|
3958
|
+
#resolveTargetText(nodes) {
|
|
3959
|
+
if (nodes.length === 0) {
|
|
3960
|
+
return "without arguments";
|
|
3961
|
+
}
|
|
3962
|
+
if (nodes.length === 1 && nodes[0]?.kind === this.#compiler.SyntaxKind.SpreadElement) {
|
|
3963
|
+
return "with the given arguments";
|
|
3964
|
+
}
|
|
3965
|
+
return `with the given argument${nodes.length === 1 ? "" : "s"}`;
|
|
3966
|
+
}
|
|
3967
|
+
#explain(matchWorker, sourceNode, targetNodes) {
|
|
3968
|
+
const isTypeNode = this.#compiler.isTypeNode(sourceNode);
|
|
3969
|
+
const targetText = this.#resolveTargetText(targetNodes);
|
|
3970
|
+
const diagnostics = [];
|
|
3971
|
+
if (matchWorker.assertion.abilityDiagnostics) {
|
|
3972
|
+
for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
|
|
3973
|
+
const text = [ExpectDiagnosticText.isNotCallable(isTypeNode, targetText), getDiagnosticMessageText(diagnostic)];
|
|
3974
|
+
let origin;
|
|
3975
|
+
if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
|
|
3976
|
+
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile());
|
|
3977
|
+
}
|
|
3978
|
+
else {
|
|
3979
|
+
origin =
|
|
3980
|
+
targetNodes.length > 0
|
|
3981
|
+
? DiagnosticOrigin.fromNodes(targetNodes)
|
|
3982
|
+
: DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
3983
|
+
}
|
|
3984
|
+
let related;
|
|
3985
|
+
if (diagnostic.relatedInformation != null) {
|
|
3986
|
+
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
3987
|
+
}
|
|
3988
|
+
diagnostics.push(Diagnostic.error(text.flat(), origin).add({ related }));
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
else {
|
|
3992
|
+
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
3993
|
+
diagnostics.push(Diagnostic.error(ExpectDiagnosticText.isCallable(isTypeNode, targetText), origin));
|
|
3994
|
+
}
|
|
3995
|
+
return diagnostics;
|
|
3996
|
+
}
|
|
3997
|
+
match(matchWorker, sourceNode, targetNodes, onDiagnostics) {
|
|
3998
|
+
let type;
|
|
3999
|
+
if (this.#compiler.isCallExpression(sourceNode)) {
|
|
4000
|
+
type = matchWorker.typeChecker.getResolvedSignature(sourceNode)?.getReturnType();
|
|
4001
|
+
}
|
|
4002
|
+
if (this.#compiler.isArrowFunction(sourceNode) ||
|
|
4003
|
+
this.#compiler.isFunctionDeclaration(sourceNode) ||
|
|
4004
|
+
this.#compiler.isFunctionExpression(sourceNode) ||
|
|
4005
|
+
this.#compiler.isExpressionWithTypeArguments(sourceNode) ||
|
|
4006
|
+
this.#compiler.isIdentifier(sourceNode)) {
|
|
4007
|
+
type = matchWorker.getType(sourceNode);
|
|
4008
|
+
}
|
|
4009
|
+
if (!type || type.getCallSignatures().length === 0) {
|
|
4010
|
+
const text = [];
|
|
4011
|
+
if (this.#compiler.isTypeNode(sourceNode)) {
|
|
4012
|
+
text.push(ExpectDiagnosticText.typeArgumentMustBe("Source", "a callable type"));
|
|
4013
|
+
}
|
|
4014
|
+
else {
|
|
4015
|
+
text.push(ExpectDiagnosticText.argumentMustBe("source", "a callable expression"));
|
|
4016
|
+
}
|
|
4017
|
+
if (type != null && type.getConstructSignatures().length > 0) {
|
|
4018
|
+
text.push(ExpectDiagnosticText.didYouMeanToUse("the '.toBeConstructableWith()' matcher"));
|
|
4019
|
+
}
|
|
4020
|
+
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
4021
|
+
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
return {
|
|
4025
|
+
explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
|
|
4026
|
+
isMatch: !matchWorker.assertion.abilityDiagnostics,
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
class ToBeConstructableWith {
|
|
4032
|
+
#compiler;
|
|
4033
|
+
constructor(compiler) {
|
|
4034
|
+
this.#compiler = compiler;
|
|
3956
4035
|
}
|
|
3957
4036
|
#resolveTargetText(nodes) {
|
|
3958
4037
|
if (nodes.length === 0) {
|
|
@@ -3970,16 +4049,12 @@ class ToBeCallableWith {
|
|
|
3970
4049
|
if (matchWorker.assertion.abilityDiagnostics) {
|
|
3971
4050
|
for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
|
|
3972
4051
|
const text = [
|
|
3973
|
-
ExpectDiagnosticText.
|
|
3974
|
-
|
|
3975
|
-
? diagnostic.messageText
|
|
3976
|
-
: Diagnostic.toMessageText(diagnostic.messageText),
|
|
4052
|
+
ExpectDiagnosticText.isNotConstructable(isTypeNode, targetText),
|
|
4053
|
+
getDiagnosticMessageText(diagnostic),
|
|
3977
4054
|
];
|
|
3978
4055
|
let origin;
|
|
3979
|
-
if (
|
|
3980
|
-
diagnostic.start
|
|
3981
|
-
diagnostic.start <= targetNodes.end) {
|
|
3982
|
-
origin = new DiagnosticOrigin(diagnostic.start, diagnostic.start + diagnostic.length, sourceNode.getSourceFile());
|
|
4056
|
+
if (isDiagnosticWithLocation(diagnostic) && diagnosticBelongsToNode(diagnostic, targetNodes)) {
|
|
4057
|
+
origin = new DiagnosticOrigin(diagnostic.start, getTextSpanEnd(diagnostic), sourceNode.getSourceFile());
|
|
3983
4058
|
}
|
|
3984
4059
|
else {
|
|
3985
4060
|
origin =
|
|
@@ -3996,11 +4071,34 @@ class ToBeCallableWith {
|
|
|
3996
4071
|
}
|
|
3997
4072
|
else {
|
|
3998
4073
|
const origin = DiagnosticOrigin.fromAssertion(matchWorker.assertion);
|
|
3999
|
-
diagnostics.push(Diagnostic.error(ExpectDiagnosticText.
|
|
4074
|
+
diagnostics.push(Diagnostic.error(ExpectDiagnosticText.isConstructable(isTypeNode, targetText), origin));
|
|
4000
4075
|
}
|
|
4001
4076
|
return diagnostics;
|
|
4002
4077
|
}
|
|
4003
|
-
match(matchWorker, sourceNode, targetNodes,
|
|
4078
|
+
match(matchWorker, sourceNode, targetNodes, onDiagnostics) {
|
|
4079
|
+
let type;
|
|
4080
|
+
if (this.#compiler.isCallExpression(sourceNode)) {
|
|
4081
|
+
type = matchWorker.typeChecker.getResolvedSignature(sourceNode)?.getReturnType();
|
|
4082
|
+
}
|
|
4083
|
+
if (this.#compiler.isExpressionWithTypeArguments(sourceNode) ||
|
|
4084
|
+
this.#compiler.isIdentifier(sourceNode)) {
|
|
4085
|
+
type = matchWorker.getType(sourceNode);
|
|
4086
|
+
}
|
|
4087
|
+
if (!type || type.getConstructSignatures().length === 0) {
|
|
4088
|
+
const text = [];
|
|
4089
|
+
if (this.#compiler.isTypeNode(sourceNode)) {
|
|
4090
|
+
text.push(ExpectDiagnosticText.typeArgumentMustBe("Source", "a constructable type"));
|
|
4091
|
+
}
|
|
4092
|
+
else {
|
|
4093
|
+
text.push(ExpectDiagnosticText.argumentMustBe("source", "a constructable expression"));
|
|
4094
|
+
}
|
|
4095
|
+
if (type != null && type.getCallSignatures().length > 0) {
|
|
4096
|
+
text.push(ExpectDiagnosticText.didYouMeanToUse("the '.toBeCallableWith()' matcher"));
|
|
4097
|
+
}
|
|
4098
|
+
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
4099
|
+
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4004
4102
|
return {
|
|
4005
4103
|
explain: () => this.#explain(matchWorker, sourceNode, targetNodes),
|
|
4006
4104
|
isMatch: !matchWorker.assertion.abilityDiagnostics,
|
|
@@ -4017,7 +4115,7 @@ class ToHaveProperty {
|
|
|
4017
4115
|
const sourceTypeText = matchWorker.getTypeText(sourceNode);
|
|
4018
4116
|
const targetType = matchWorker.getType(targetNode);
|
|
4019
4117
|
let propertyNameText;
|
|
4020
|
-
if (
|
|
4118
|
+
if (isStringOrNumberLiteralType(this.#compiler, targetType)) {
|
|
4021
4119
|
propertyNameText = targetType.value.toString();
|
|
4022
4120
|
}
|
|
4023
4121
|
else {
|
|
@@ -4042,10 +4140,10 @@ class ToHaveProperty {
|
|
|
4042
4140
|
}
|
|
4043
4141
|
const targetType = matchWorker.getType(targetNode);
|
|
4044
4142
|
let propertyNameText = "";
|
|
4045
|
-
if (
|
|
4143
|
+
if (isStringOrNumberLiteralType(this.#compiler, targetType)) {
|
|
4046
4144
|
propertyNameText = targetType.value.toString();
|
|
4047
4145
|
}
|
|
4048
|
-
else if (
|
|
4146
|
+
else if (isUniqueSymbolType(this.#compiler, targetType)) {
|
|
4049
4147
|
propertyNameText = this.#compiler.unescapeLeadingUnderscores(targetType.escapedName);
|
|
4050
4148
|
}
|
|
4051
4149
|
else {
|
|
@@ -4139,9 +4237,10 @@ class ToRaiseError {
|
|
|
4139
4237
|
if (this.#compiler.isNumericLiteral(targetNode)) {
|
|
4140
4238
|
return Number.parseInt(targetNode.text, 10) === diagnostic.code;
|
|
4141
4239
|
}
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4240
|
+
let messageText = getDiagnosticMessageText(diagnostic);
|
|
4241
|
+
if (Array.isArray(messageText)) {
|
|
4242
|
+
messageText = messageText.join("\n");
|
|
4243
|
+
}
|
|
4145
4244
|
if (this.#compiler.isRegularExpressionLiteral(targetNode)) {
|
|
4146
4245
|
const targetRegex = new RegExp(...targetNode.text.slice(1).split("/"));
|
|
4147
4246
|
return targetRegex.test(messageText);
|
|
@@ -4160,6 +4259,7 @@ class ExpectService {
|
|
|
4160
4259
|
toBeAssignableTo;
|
|
4161
4260
|
toBeAssignableWith;
|
|
4162
4261
|
toBeCallableWith;
|
|
4262
|
+
toBeConstructableWith;
|
|
4163
4263
|
toHaveProperty;
|
|
4164
4264
|
toRaiseError;
|
|
4165
4265
|
constructor(compiler, typeChecker, resolvedConfig) {
|
|
@@ -4177,6 +4277,7 @@ class ExpectService {
|
|
|
4177
4277
|
this.toBeAssignableTo = new ToBeAssignableTo();
|
|
4178
4278
|
this.toBeAssignableWith = new ToBeAssignableWith();
|
|
4179
4279
|
this.toBeCallableWith = new ToBeCallableWith(compiler);
|
|
4280
|
+
this.toBeConstructableWith = new ToBeConstructableWith(compiler);
|
|
4180
4281
|
this.toHaveProperty = new ToHaveProperty(compiler);
|
|
4181
4282
|
this.toRaiseError = new ToRaiseError(compiler);
|
|
4182
4283
|
}
|
|
@@ -4204,6 +4305,7 @@ class ExpectService {
|
|
|
4204
4305
|
case "toBeApplicable":
|
|
4205
4306
|
return this.toBeApplicable.match(matchWorker, assertion.source[0], onDiagnostics);
|
|
4206
4307
|
case "toBeCallableWith":
|
|
4308
|
+
case "toBeConstructableWith":
|
|
4207
4309
|
case "toRaiseError":
|
|
4208
4310
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target, onDiagnostics);
|
|
4209
4311
|
case "toHaveProperty":
|
|
@@ -4491,7 +4593,7 @@ class TaskRunner {
|
|
|
4491
4593
|
class Runner {
|
|
4492
4594
|
#eventEmitter = new EventEmitter();
|
|
4493
4595
|
#resolvedConfig;
|
|
4494
|
-
static version = "4.0.0-beta.
|
|
4596
|
+
static version = "4.0.0-beta.4";
|
|
4495
4597
|
constructor(resolvedConfig) {
|
|
4496
4598
|
this.#resolvedConfig = resolvedConfig;
|
|
4497
4599
|
}
|
|
@@ -4693,4 +4795,4 @@ class Cli {
|
|
|
4693
4795
|
}
|
|
4694
4796
|
}
|
|
4695
4797
|
|
|
4696
|
-
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 };
|
|
4798
|
+
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, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|
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.4",
|
|
4
4
|
"description": "The Essential Type Testing Tool.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -62,14 +62,14 @@
|
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@biomejs/biome": "1.9.4",
|
|
64
64
|
"@rollup/plugin-typescript": "12.1.2",
|
|
65
|
-
"@types/node": "22.14.
|
|
66
|
-
"@types/react": "19.1.
|
|
65
|
+
"@types/node": "22.14.1",
|
|
66
|
+
"@types/react": "19.1.1",
|
|
67
67
|
"ajv": "8.17.1",
|
|
68
68
|
"cspell": "8.18.1",
|
|
69
69
|
"magic-string": "0.30.17",
|
|
70
70
|
"monocart-coverage-reports": "2.12.3",
|
|
71
71
|
"pretty-ansi": "3.0.0",
|
|
72
|
-
"rollup": "4.
|
|
72
|
+
"rollup": "4.40.0",
|
|
73
73
|
"rollup-plugin-dts": "6.2.1",
|
|
74
74
|
"tslib": "2.8.1",
|
|
75
75
|
"typescript": "5.8.3"
|