tstyche 4.0.2 → 4.2.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/tstyche.d.ts +25 -4
- package/build/tstyche.js +249 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,8 +70,7 @@ Here is the list of all matchers:
|
|
|
70
70
|
- `.toBeApplicable` ensures that the decorator function can be applied,
|
|
71
71
|
- `.toBeCallableWith()` checks whether a function is callable with the given arguments,
|
|
72
72
|
- `.toBeConstructableWith()` checks whether a class is constructable with the given arguments,
|
|
73
|
-
- `.toHaveProperty()` looks up keys on an object type
|
|
74
|
-
- `.toRaiseError()` captures the message or code of a type error.
|
|
73
|
+
- `.toHaveProperty()` looks up keys on an object type.
|
|
75
74
|
|
|
76
75
|
## Runner
|
|
77
76
|
|
package/build/tstyche.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ interface CommandLineOptions {
|
|
|
41
41
|
}
|
|
42
42
|
interface ConfigFileOptions {
|
|
43
43
|
checkSourceFiles?: boolean;
|
|
44
|
+
checkSuppressedErrors?: boolean;
|
|
44
45
|
failFast?: boolean;
|
|
45
46
|
plugins?: Array<string>;
|
|
46
47
|
rejectAnyType?: boolean;
|
|
@@ -231,7 +232,7 @@ declare enum TestTreeNodeFlags {
|
|
|
231
232
|
declare class WhenNode extends TestTreeNode {
|
|
232
233
|
actionNode: ts.CallExpression;
|
|
233
234
|
actionNameNode: ts.PropertyAccessExpression;
|
|
234
|
-
abilityDiagnostics: Set<ts.Diagnostic
|
|
235
|
+
abilityDiagnostics: Set<ts.Diagnostic>;
|
|
235
236
|
target: ts.NodeArray<ts.Expression> | ts.NodeArray<ts.TypeNode>;
|
|
236
237
|
constructor(compiler: typeof ts, brand: TestTreeNodeBrand, node: ts.CallExpression, parent: TestTree | TestTreeNode, flags: TestTreeNodeFlags, actionNode: ts.CallExpression, actionNameNode: ts.PropertyAccessExpression);
|
|
237
238
|
}
|
|
@@ -248,17 +249,28 @@ declare class TestTreeNode {
|
|
|
248
249
|
getDirectiveRanges(compiler: typeof ts): DirectiveRanges | undefined;
|
|
249
250
|
}
|
|
250
251
|
|
|
252
|
+
interface SuppressedError {
|
|
253
|
+
directive: TextRange;
|
|
254
|
+
ignore: boolean;
|
|
255
|
+
argument?: TextRange;
|
|
256
|
+
diagnostics: Array<ts.Diagnostic>;
|
|
257
|
+
}
|
|
258
|
+
type SuppressedErrors = Array<SuppressedError> & {
|
|
259
|
+
sourceFile: ts.SourceFile;
|
|
260
|
+
};
|
|
261
|
+
|
|
251
262
|
declare class TestTree {
|
|
252
263
|
children: Array<TestTreeNode | AssertionNode | WhenNode>;
|
|
253
264
|
diagnostics: Set<ts.Diagnostic>;
|
|
254
265
|
hasOnly: boolean;
|
|
255
266
|
sourceFile: ts.SourceFile;
|
|
267
|
+
suppressedErrors: SuppressedErrors | undefined;
|
|
256
268
|
constructor(diagnostics: Set<ts.Diagnostic>, sourceFile: ts.SourceFile);
|
|
257
269
|
getDirectiveRanges(compiler: typeof ts): DirectiveRanges | undefined;
|
|
258
270
|
}
|
|
259
271
|
|
|
260
272
|
declare class AssertionNode extends TestTreeNode {
|
|
261
|
-
abilityDiagnostics: Set<ts.Diagnostic
|
|
273
|
+
abilityDiagnostics: Set<ts.Diagnostic>;
|
|
262
274
|
isNot: boolean;
|
|
263
275
|
matcherNode: ts.CallExpression | ts.Decorator;
|
|
264
276
|
matcherNameNode: ts.PropertyAccessExpression;
|
|
@@ -546,6 +558,11 @@ declare class ExpectService {
|
|
|
546
558
|
match(assertion: AssertionNode, onDiagnostics: DiagnosticsHandler<Diagnostic | Array<Diagnostic>>): MatchResult | undefined;
|
|
547
559
|
}
|
|
548
560
|
|
|
561
|
+
declare class Glob {
|
|
562
|
+
#private;
|
|
563
|
+
static toRegex(patterns: Array<string>, target: "directories" | "files"): RegExp;
|
|
564
|
+
}
|
|
565
|
+
|
|
549
566
|
declare class CancellationHandler implements EventHandler {
|
|
550
567
|
#private;
|
|
551
568
|
constructor(cancellationToken: CancellationToken, cancellationReason: CancellationReason);
|
|
@@ -759,6 +776,10 @@ declare class Store {
|
|
|
759
776
|
static validateTag(tag: string): Promise<boolean | undefined>;
|
|
760
777
|
}
|
|
761
778
|
|
|
779
|
+
declare class SuppressedService {
|
|
780
|
+
match(suppressedErrors: SuppressedErrors, onDiagnostics: DiagnosticsHandler<Array<Diagnostic>>): void;
|
|
781
|
+
}
|
|
782
|
+
|
|
762
783
|
declare class Version {
|
|
763
784
|
#private;
|
|
764
785
|
static isGreaterThan(source: string, target: string): boolean;
|
|
@@ -794,5 +815,5 @@ declare class WhenService {
|
|
|
794
815
|
action(when: WhenNode): void;
|
|
795
816
|
}
|
|
796
817
|
|
|
797
|
-
export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, ScribblerJsx, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|
|
798
|
-
export type { CodeFrameOptions, CommandLineOptions, ConfigFileOptions, DiagnosticsHandler, DirectiveRange, DirectiveRanges, EnvironmentOptions, Event, EventHandler, FileWatchHandler, InlineConfig, InputHandler, ItemDefinition, MatchResult, OptionDefinition, Plugin, Reporter, ReporterEvent, ResolvedConfig, ScribblerOptions, SelectHookContext, TargetResultStatus, TaskResultStatus, TextRange, TypeChecker, WatchHandler, WatcherOptions };
|
|
818
|
+
export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, Glob, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, ScribblerJsx, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, SuppressedService, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|
|
819
|
+
export type { CodeFrameOptions, CommandLineOptions, ConfigFileOptions, DiagnosticsHandler, DirectiveRange, DirectiveRanges, EnvironmentOptions, Event, EventHandler, FileWatchHandler, InlineConfig, InputHandler, ItemDefinition, MatchResult, OptionDefinition, Plugin, Reporter, ReporterEvent, ResolvedConfig, ScribblerOptions, SelectHookContext, SuppressedError, SuppressedErrors, TargetResultStatus, TaskResultStatus, TextRange, TypeChecker, WatchHandler, WatcherOptions };
|
package/build/tstyche.js
CHANGED
|
@@ -96,7 +96,7 @@ class Diagnostic {
|
|
|
96
96
|
}
|
|
97
97
|
let related;
|
|
98
98
|
if (diagnostic.relatedInformation != null) {
|
|
99
|
-
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation
|
|
99
|
+
related = Diagnostic.fromDiagnostics(diagnostic.relatedInformation);
|
|
100
100
|
}
|
|
101
101
|
const text = getDiagnosticMessageText(diagnostic);
|
|
102
102
|
return new Diagnostic(text, DiagnosticCategory.Error, origin).add({ code, related });
|
|
@@ -910,6 +910,12 @@ class Options {
|
|
|
910
910
|
group: OptionGroup.ConfigFile,
|
|
911
911
|
name: "checkSourceFiles",
|
|
912
912
|
},
|
|
913
|
+
{
|
|
914
|
+
brand: OptionBrand.Boolean,
|
|
915
|
+
description: "Check errors silenced by '// @ts-expect-error' directives.",
|
|
916
|
+
group: OptionGroup.ConfigFile,
|
|
917
|
+
name: "checkSuppressedErrors",
|
|
918
|
+
},
|
|
913
919
|
{
|
|
914
920
|
brand: OptionBrand.String,
|
|
915
921
|
description: "The path to a TSTyche configuration file.",
|
|
@@ -1394,6 +1400,7 @@ class ConfigParser {
|
|
|
1394
1400
|
|
|
1395
1401
|
const defaultOptions = {
|
|
1396
1402
|
checkSourceFiles: true,
|
|
1403
|
+
checkSuppressedErrors: false,
|
|
1397
1404
|
failFast: false,
|
|
1398
1405
|
plugins: [],
|
|
1399
1406
|
rejectAnyType: true,
|
|
@@ -2735,7 +2742,64 @@ class InputService {
|
|
|
2735
2742
|
}
|
|
2736
2743
|
}
|
|
2737
2744
|
|
|
2738
|
-
class
|
|
2745
|
+
class Braces {
|
|
2746
|
+
static expand(text) {
|
|
2747
|
+
const start = text.indexOf("{");
|
|
2748
|
+
if (start === -1) {
|
|
2749
|
+
return [text];
|
|
2750
|
+
}
|
|
2751
|
+
let position = start;
|
|
2752
|
+
let depth = 0;
|
|
2753
|
+
for (position; position < text.length; position++) {
|
|
2754
|
+
if (text[position] === "{") {
|
|
2755
|
+
depth++;
|
|
2756
|
+
}
|
|
2757
|
+
else if (text[position] === "}") {
|
|
2758
|
+
depth--;
|
|
2759
|
+
}
|
|
2760
|
+
if (depth === 0) {
|
|
2761
|
+
break;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
if (depth !== 0) {
|
|
2765
|
+
return [text];
|
|
2766
|
+
}
|
|
2767
|
+
const before = text.slice(0, start);
|
|
2768
|
+
const options = Braces.#splitOptions(text.slice(start + 1, position));
|
|
2769
|
+
const after = text.slice(position + 1);
|
|
2770
|
+
const result = [];
|
|
2771
|
+
for (const option of options) {
|
|
2772
|
+
for (const expanded of Braces.expand(option + after)) {
|
|
2773
|
+
result.push(before + expanded);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
return result;
|
|
2777
|
+
}
|
|
2778
|
+
static #splitOptions(optionText) {
|
|
2779
|
+
const options = [];
|
|
2780
|
+
let current = "";
|
|
2781
|
+
let depth = 0;
|
|
2782
|
+
for (const character of optionText) {
|
|
2783
|
+
if (character === "," && depth === 0) {
|
|
2784
|
+
options.push(current);
|
|
2785
|
+
current = "";
|
|
2786
|
+
}
|
|
2787
|
+
else {
|
|
2788
|
+
if (character === "{") {
|
|
2789
|
+
depth++;
|
|
2790
|
+
}
|
|
2791
|
+
if (character === "}") {
|
|
2792
|
+
depth--;
|
|
2793
|
+
}
|
|
2794
|
+
current += character;
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
options.push(current);
|
|
2798
|
+
return options;
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
class Glob {
|
|
2739
2803
|
static #reservedCharacterRegex = /[^\w\s/]/g;
|
|
2740
2804
|
static #parse(pattern, usageTarget) {
|
|
2741
2805
|
const segments = pattern.split("/");
|
|
@@ -2754,7 +2818,7 @@ class GlobPattern {
|
|
|
2754
2818
|
optionalSegmentCount++;
|
|
2755
2819
|
}
|
|
2756
2820
|
resultPattern += "\\/";
|
|
2757
|
-
const segmentPattern = segment.replace(
|
|
2821
|
+
const segmentPattern = segment.replace(Glob.#reservedCharacterRegex, Glob.#replaceReservedCharacter);
|
|
2758
2822
|
if (segmentPattern !== segment) {
|
|
2759
2823
|
resultPattern += "(?!(node_modules)(\\/|$))";
|
|
2760
2824
|
}
|
|
@@ -2774,7 +2838,10 @@ class GlobPattern {
|
|
|
2774
2838
|
}
|
|
2775
2839
|
}
|
|
2776
2840
|
static toRegex(patterns, target) {
|
|
2777
|
-
const patternText = patterns
|
|
2841
|
+
const patternText = patterns
|
|
2842
|
+
.flatMap((pattern) => Braces.expand(pattern))
|
|
2843
|
+
.map((pattern) => `(${Glob.#parse(pattern, target)})`)
|
|
2844
|
+
.join("|");
|
|
2778
2845
|
return new RegExp(`^(${patternText})$`);
|
|
2779
2846
|
}
|
|
2780
2847
|
}
|
|
@@ -2832,8 +2899,8 @@ class Select {
|
|
|
2832
2899
|
let matchPatterns = Select.#patternsCache.get(globPatterns);
|
|
2833
2900
|
if (!matchPatterns) {
|
|
2834
2901
|
matchPatterns = {
|
|
2835
|
-
includedDirectory:
|
|
2836
|
-
includedFile:
|
|
2902
|
+
includedDirectory: Glob.toRegex(globPatterns, "directories"),
|
|
2903
|
+
includedFile: Glob.toRegex(globPatterns, "files"),
|
|
2837
2904
|
};
|
|
2838
2905
|
Select.#patternsCache.set(globPatterns, matchPatterns);
|
|
2839
2906
|
}
|
|
@@ -3030,7 +3097,7 @@ class TestTreeNode {
|
|
|
3030
3097
|
}
|
|
3031
3098
|
|
|
3032
3099
|
class AssertionNode extends TestTreeNode {
|
|
3033
|
-
abilityDiagnostics;
|
|
3100
|
+
abilityDiagnostics = new Set();
|
|
3034
3101
|
isNot;
|
|
3035
3102
|
matcherNode;
|
|
3036
3103
|
matcherNameNode;
|
|
@@ -3072,12 +3139,51 @@ class AbilityLayer {
|
|
|
3072
3139
|
#nodes = [];
|
|
3073
3140
|
#projectService;
|
|
3074
3141
|
#resolvedConfig;
|
|
3142
|
+
#suppressedErrorsMap;
|
|
3075
3143
|
#text = "";
|
|
3076
3144
|
constructor(compiler, projectService, resolvedConfig) {
|
|
3077
3145
|
this.#compiler = compiler;
|
|
3078
3146
|
this.#projectService = projectService;
|
|
3079
3147
|
this.#resolvedConfig = resolvedConfig;
|
|
3080
3148
|
}
|
|
3149
|
+
#addRanges(node, ranges) {
|
|
3150
|
+
this.#nodes.push(node);
|
|
3151
|
+
for (const range of ranges) {
|
|
3152
|
+
const rangeText = range.replacement != null
|
|
3153
|
+
? `${range.replacement}${this.#getErasedRangeText(range).slice(range.replacement.length)}`
|
|
3154
|
+
: this.#getErasedRangeText(range);
|
|
3155
|
+
this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
#belongsToNode(diagnostic) {
|
|
3159
|
+
for (const node of this.#nodes) {
|
|
3160
|
+
if (diagnosticBelongsToNode(diagnostic, "matcherNode" in node ? node.matcherNode : node.actionNode)) {
|
|
3161
|
+
node.abilityDiagnostics.add(diagnostic);
|
|
3162
|
+
return true;
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
return false;
|
|
3166
|
+
}
|
|
3167
|
+
#belongsToDirective(diagnostic) {
|
|
3168
|
+
if (!isDiagnosticWithLocation(diagnostic)) {
|
|
3169
|
+
return;
|
|
3170
|
+
}
|
|
3171
|
+
const { file, start } = diagnostic;
|
|
3172
|
+
const lineMap = file.getLineStarts();
|
|
3173
|
+
let line = this.#compiler.getLineAndCharacterOfPosition(file, start).line - 1;
|
|
3174
|
+
while (line >= 0) {
|
|
3175
|
+
const suppressedError = this.#suppressedErrorsMap?.get(line);
|
|
3176
|
+
if (suppressedError != null) {
|
|
3177
|
+
suppressedError.diagnostics.push(diagnostic);
|
|
3178
|
+
break;
|
|
3179
|
+
}
|
|
3180
|
+
const lineText = file.text.slice(lineMap[line], lineMap[line + 1]).trim();
|
|
3181
|
+
if (lineText !== "" && !lineText.startsWith("//")) {
|
|
3182
|
+
break;
|
|
3183
|
+
}
|
|
3184
|
+
line--;
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3081
3187
|
#collectSuppressedErrors() {
|
|
3082
3188
|
const ranges = [];
|
|
3083
3189
|
for (const match of this.#text.matchAll(this.#expectErrorRegex)) {
|
|
@@ -3086,12 +3192,13 @@ class AbilityLayer {
|
|
|
3086
3192
|
const ignoreText = match?.[3];
|
|
3087
3193
|
const argumentSeparatorText = match?.[4];
|
|
3088
3194
|
const argumentText = match?.[5]?.split(/--+/)[0]?.trimEnd();
|
|
3089
|
-
if (typeof offsetText !== "string" || !directiveText
|
|
3195
|
+
if (typeof offsetText !== "string" || !directiveText) {
|
|
3090
3196
|
continue;
|
|
3091
3197
|
}
|
|
3092
3198
|
const start = match.index + offsetText.length;
|
|
3093
3199
|
const range = {
|
|
3094
3200
|
directive: { start, end: start + directiveText.length, text: directiveText },
|
|
3201
|
+
ignore: ignoreText === "!",
|
|
3095
3202
|
diagnostics: [],
|
|
3096
3203
|
};
|
|
3097
3204
|
if (typeof argumentSeparatorText === "string" && typeof argumentText === "string") {
|
|
@@ -3102,52 +3209,24 @@ class AbilityLayer {
|
|
|
3102
3209
|
}
|
|
3103
3210
|
return ranges;
|
|
3104
3211
|
}
|
|
3105
|
-
#getErasedRangeText(range) {
|
|
3106
|
-
if (this.#text.indexOf("\n", range.start) >= range.end) {
|
|
3107
|
-
return " ".repeat(range.end - range.start);
|
|
3108
|
-
}
|
|
3109
|
-
const text = [];
|
|
3110
|
-
for (let index = range.start; index < range.end; index++) {
|
|
3111
|
-
const character = this.#text.charAt(index);
|
|
3112
|
-
switch (character) {
|
|
3113
|
-
case "\n":
|
|
3114
|
-
case "\r":
|
|
3115
|
-
text.push(character);
|
|
3116
|
-
break;
|
|
3117
|
-
default:
|
|
3118
|
-
text.push(" ");
|
|
3119
|
-
}
|
|
3120
|
-
}
|
|
3121
|
-
return text.join("");
|
|
3122
|
-
}
|
|
3123
|
-
#addRanges(node, ranges) {
|
|
3124
|
-
this.#nodes.push(node);
|
|
3125
|
-
for (const range of ranges) {
|
|
3126
|
-
const rangeText = range.replacement != null
|
|
3127
|
-
? `${range.replacement}${this.#getErasedRangeText(range).slice(range.replacement.length)}`
|
|
3128
|
-
: this.#getErasedRangeText(range);
|
|
3129
|
-
this.#text = `${this.#text.slice(0, range.start)}${rangeText}${this.#text.slice(range.end)}`;
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
3212
|
close() {
|
|
3133
|
-
if (this.#nodes.length > 0) {
|
|
3213
|
+
if (this.#nodes.length > 0 || this.#suppressedErrorsMap != null) {
|
|
3134
3214
|
this.#projectService.openFile(this.#filePath, this.#text, this.#resolvedConfig.rootPath);
|
|
3135
3215
|
const languageService = this.#projectService.getLanguageService(this.#filePath);
|
|
3136
|
-
const diagnostics =
|
|
3137
|
-
|
|
3216
|
+
const diagnostics = languageService?.getSemanticDiagnostics(this.#filePath);
|
|
3217
|
+
if (diagnostics != null) {
|
|
3218
|
+
this.#nodes.reverse();
|
|
3138
3219
|
for (const diagnostic of diagnostics) {
|
|
3139
|
-
if (
|
|
3140
|
-
|
|
3141
|
-
node.abilityDiagnostics = new Set();
|
|
3142
|
-
}
|
|
3143
|
-
node.abilityDiagnostics.add(diagnostic);
|
|
3144
|
-
diagnostics.delete(diagnostic);
|
|
3220
|
+
if (this.#belongsToNode(diagnostic)) {
|
|
3221
|
+
continue;
|
|
3145
3222
|
}
|
|
3223
|
+
this.#belongsToDirective(diagnostic);
|
|
3146
3224
|
}
|
|
3147
3225
|
}
|
|
3148
3226
|
}
|
|
3149
3227
|
this.#filePath = "";
|
|
3150
3228
|
this.#nodes = [];
|
|
3229
|
+
this.#suppressedErrorsMap = undefined;
|
|
3151
3230
|
this.#text = "";
|
|
3152
3231
|
}
|
|
3153
3232
|
#eraseTrailingComma(node, parent) {
|
|
@@ -3155,24 +3234,23 @@ class AbilityLayer {
|
|
|
3155
3234
|
this.#addRanges(parent, [{ start: node.end - 1, end: node.end }]);
|
|
3156
3235
|
}
|
|
3157
3236
|
}
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
const whenEnd = whenNode.node.getEnd();
|
|
3162
|
-
const actionNameEnd = whenNode.actionNameNode.getEnd();
|
|
3163
|
-
switch (whenNode.actionNameNode.name.text) {
|
|
3164
|
-
case "isCalledWith":
|
|
3165
|
-
this.#eraseTrailingComma(whenNode.target, whenNode);
|
|
3166
|
-
this.#addRanges(whenNode, [
|
|
3167
|
-
{
|
|
3168
|
-
start: whenStart,
|
|
3169
|
-
end: whenExpressionEnd,
|
|
3170
|
-
replacement: nodeIsChildOfExpressionStatement(this.#compiler, whenNode.actionNode) ? ";" : "",
|
|
3171
|
-
},
|
|
3172
|
-
{ start: whenEnd, end: actionNameEnd },
|
|
3173
|
-
]);
|
|
3174
|
-
break;
|
|
3237
|
+
#getErasedRangeText(range) {
|
|
3238
|
+
if (this.#text.indexOf("\n", range.start) >= range.end) {
|
|
3239
|
+
return " ".repeat(range.end - range.start);
|
|
3175
3240
|
}
|
|
3241
|
+
const text = [];
|
|
3242
|
+
for (let index = range.start; index < range.end; index++) {
|
|
3243
|
+
const character = this.#text.charAt(index);
|
|
3244
|
+
switch (character) {
|
|
3245
|
+
case "\n":
|
|
3246
|
+
case "\r":
|
|
3247
|
+
text.push(character);
|
|
3248
|
+
break;
|
|
3249
|
+
default:
|
|
3250
|
+
text.push(" ");
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
return text.join("");
|
|
3176
3254
|
}
|
|
3177
3255
|
handleAssertion(assertionNode) {
|
|
3178
3256
|
const expectStart = assertionNode.node.getStart();
|
|
@@ -3210,18 +3288,45 @@ class AbilityLayer {
|
|
|
3210
3288
|
break;
|
|
3211
3289
|
}
|
|
3212
3290
|
}
|
|
3213
|
-
#handleSuppressedErrors() {
|
|
3291
|
+
#handleSuppressedErrors(testTree) {
|
|
3214
3292
|
const suppressedErrors = this.#collectSuppressedErrors();
|
|
3293
|
+
if (this.#resolvedConfig.checkSuppressedErrors) {
|
|
3294
|
+
testTree.suppressedErrors = Object.assign(suppressedErrors, { sourceFile: testTree.sourceFile });
|
|
3295
|
+
this.#suppressedErrorsMap = new Map();
|
|
3296
|
+
}
|
|
3215
3297
|
for (const suppressedError of suppressedErrors) {
|
|
3216
3298
|
const { start, end } = suppressedError.directive;
|
|
3217
3299
|
const rangeText = this.#getErasedRangeText({ start: start + 2, end });
|
|
3218
3300
|
this.#text = `${this.#text.slice(0, start + 2)}${rangeText}${this.#text.slice(end)}`;
|
|
3301
|
+
if (this.#suppressedErrorsMap != null) {
|
|
3302
|
+
const { line } = testTree.sourceFile.getLineAndCharacterOfPosition(start);
|
|
3303
|
+
this.#suppressedErrorsMap.set(line, suppressedError);
|
|
3304
|
+
}
|
|
3219
3305
|
}
|
|
3220
3306
|
}
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3307
|
+
handleWhen(whenNode) {
|
|
3308
|
+
const whenStart = whenNode.node.getStart();
|
|
3309
|
+
const whenExpressionEnd = whenNode.node.expression.getEnd();
|
|
3310
|
+
const whenEnd = whenNode.node.getEnd();
|
|
3311
|
+
const actionNameEnd = whenNode.actionNameNode.getEnd();
|
|
3312
|
+
switch (whenNode.actionNameNode.name.text) {
|
|
3313
|
+
case "isCalledWith":
|
|
3314
|
+
this.#eraseTrailingComma(whenNode.target, whenNode);
|
|
3315
|
+
this.#addRanges(whenNode, [
|
|
3316
|
+
{
|
|
3317
|
+
start: whenStart,
|
|
3318
|
+
end: whenExpressionEnd,
|
|
3319
|
+
replacement: nodeIsChildOfExpressionStatement(this.#compiler, whenNode.actionNode) ? ";" : "",
|
|
3320
|
+
},
|
|
3321
|
+
{ start: whenEnd, end: actionNameEnd },
|
|
3322
|
+
]);
|
|
3323
|
+
break;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
open(testTree) {
|
|
3327
|
+
this.#filePath = testTree.sourceFile.fileName;
|
|
3328
|
+
this.#text = testTree.sourceFile.text;
|
|
3329
|
+
this.#handleSuppressedErrors(testTree);
|
|
3225
3330
|
}
|
|
3226
3331
|
}
|
|
3227
3332
|
|
|
@@ -3348,6 +3453,7 @@ class TestTree {
|
|
|
3348
3453
|
diagnostics;
|
|
3349
3454
|
hasOnly = false;
|
|
3350
3455
|
sourceFile;
|
|
3456
|
+
suppressedErrors;
|
|
3351
3457
|
constructor(diagnostics, sourceFile) {
|
|
3352
3458
|
this.diagnostics = diagnostics;
|
|
3353
3459
|
this.sourceFile = sourceFile;
|
|
@@ -3360,7 +3466,7 @@ class TestTree {
|
|
|
3360
3466
|
class WhenNode extends TestTreeNode {
|
|
3361
3467
|
actionNode;
|
|
3362
3468
|
actionNameNode;
|
|
3363
|
-
abilityDiagnostics;
|
|
3469
|
+
abilityDiagnostics = new Set();
|
|
3364
3470
|
target;
|
|
3365
3471
|
constructor(compiler, brand, node, parent, flags, actionNode, actionNameNode) {
|
|
3366
3472
|
super(compiler, brand, node, parent, flags);
|
|
@@ -3459,7 +3565,7 @@ class CollectService {
|
|
|
3459
3565
|
createTestTree(sourceFile, semanticDiagnostics = []) {
|
|
3460
3566
|
const testTree = new TestTree(new Set(semanticDiagnostics), sourceFile);
|
|
3461
3567
|
EventEmitter.dispatch(["collect:start", { tree: testTree }]);
|
|
3462
|
-
this.#abilityLayer.open(
|
|
3568
|
+
this.#abilityLayer.open(testTree);
|
|
3463
3569
|
this.#identifierLookup.open();
|
|
3464
3570
|
this.#collectTestTreeNodes(sourceFile, testTree, testTree);
|
|
3465
3571
|
this.#abilityLayer.close();
|
|
@@ -3596,6 +3702,9 @@ class ProjectService {
|
|
|
3596
3702
|
defaultCompilerOptions.allowImportingTsExtensions = true;
|
|
3597
3703
|
defaultCompilerOptions.verbatimModuleSyntax = true;
|
|
3598
3704
|
}
|
|
3705
|
+
if (Version.isSatisfiedWith(this.#compiler.version, "5.6")) {
|
|
3706
|
+
defaultCompilerOptions.noUncheckedSideEffectImports = true;
|
|
3707
|
+
}
|
|
3599
3708
|
return defaultCompilerOptions;
|
|
3600
3709
|
}
|
|
3601
3710
|
getDefaultProject(filePath) {
|
|
@@ -3669,6 +3778,60 @@ class ProjectService {
|
|
|
3669
3778
|
}
|
|
3670
3779
|
}
|
|
3671
3780
|
|
|
3781
|
+
class SuppressedDiagnosticText {
|
|
3782
|
+
static directiveRequires() {
|
|
3783
|
+
return [
|
|
3784
|
+
"Directive requires an argument.",
|
|
3785
|
+
"Add a fragment of the expected error message after the directive.",
|
|
3786
|
+
"To ignore the directive, append the '!' character after it.",
|
|
3787
|
+
];
|
|
3788
|
+
}
|
|
3789
|
+
static messageDidNotMatch() {
|
|
3790
|
+
return "The diagnostic message did not match.";
|
|
3791
|
+
}
|
|
3792
|
+
static onlySingleError() {
|
|
3793
|
+
return "Only a single error can be suppressed.";
|
|
3794
|
+
}
|
|
3795
|
+
static suppressedError(count = 1) {
|
|
3796
|
+
return `The suppressed error${count === 1 ? "" : "s"}:`;
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3800
|
+
class SuppressedService {
|
|
3801
|
+
match(suppressedErrors, onDiagnostics) {
|
|
3802
|
+
for (const suppressedError of suppressedErrors) {
|
|
3803
|
+
if (suppressedError.diagnostics.length === 0 || suppressedError.ignore) {
|
|
3804
|
+
continue;
|
|
3805
|
+
}
|
|
3806
|
+
if (!suppressedError.argument?.text) {
|
|
3807
|
+
const text = SuppressedDiagnosticText.directiveRequires();
|
|
3808
|
+
const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
|
|
3809
|
+
onDiagnostics([Diagnostic.error(text, origin)]);
|
|
3810
|
+
continue;
|
|
3811
|
+
}
|
|
3812
|
+
const related = [
|
|
3813
|
+
Diagnostic.error(SuppressedDiagnosticText.suppressedError(suppressedError.diagnostics.length)),
|
|
3814
|
+
...Diagnostic.fromDiagnostics(suppressedError.diagnostics, suppressedErrors.sourceFile),
|
|
3815
|
+
];
|
|
3816
|
+
if (suppressedError.diagnostics.length > 1) {
|
|
3817
|
+
const text = [SuppressedDiagnosticText.onlySingleError()];
|
|
3818
|
+
const origin = new DiagnosticOrigin(suppressedError.directive.start, suppressedError.directive.end, suppressedErrors.sourceFile);
|
|
3819
|
+
onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
|
|
3820
|
+
continue;
|
|
3821
|
+
}
|
|
3822
|
+
let messageText = getDiagnosticMessageText(suppressedError.diagnostics[0]);
|
|
3823
|
+
if (Array.isArray(messageText)) {
|
|
3824
|
+
messageText = messageText.join("\n");
|
|
3825
|
+
}
|
|
3826
|
+
if (!messageText.includes(suppressedError.argument.text)) {
|
|
3827
|
+
const text = [SuppressedDiagnosticText.messageDidNotMatch()];
|
|
3828
|
+
const origin = new DiagnosticOrigin(suppressedError.argument.start, suppressedError.argument.end, suppressedErrors.sourceFile);
|
|
3829
|
+
onDiagnostics([Diagnostic.error(text, origin).add({ related })]);
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
|
|
3672
3835
|
var RunMode;
|
|
3673
3836
|
(function (RunMode) {
|
|
3674
3837
|
RunMode[RunMode["Normal"] = 0] = "Normal";
|
|
@@ -3853,7 +4016,9 @@ class MatchWorker {
|
|
|
3853
4016
|
}
|
|
3854
4017
|
}
|
|
3855
4018
|
extendsObjectType(type) {
|
|
3856
|
-
const nonPrimitiveType =
|
|
4019
|
+
const nonPrimitiveType = "getNonPrimitiveType" in this.typeChecker
|
|
4020
|
+
? this.typeChecker.getNonPrimitiveType()
|
|
4021
|
+
: { flags: this.#compiler.TypeFlags.NonPrimitive };
|
|
3857
4022
|
return this.typeChecker.isTypeAssignableTo(type, nonPrimitiveType);
|
|
3858
4023
|
}
|
|
3859
4024
|
getParameterType(signature, index) {
|
|
@@ -4153,7 +4318,7 @@ class ToBeApplicable {
|
|
|
4153
4318
|
#explain(matchWorker, sourceNode) {
|
|
4154
4319
|
const targetText = this.#resolveTargetText(matchWorker.assertion.matcherNode.parent);
|
|
4155
4320
|
const diagnostics = [];
|
|
4156
|
-
if (matchWorker.assertion.abilityDiagnostics) {
|
|
4321
|
+
if (matchWorker.assertion.abilityDiagnostics.size > 0) {
|
|
4157
4322
|
for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
|
|
4158
4323
|
const text = [ExpectDiagnosticText.cannotBeApplied(targetText), getDiagnosticMessageText(diagnostic)];
|
|
4159
4324
|
const origin = DiagnosticOrigin.fromNode(sourceNode);
|
|
@@ -4179,7 +4344,7 @@ class ToBeApplicable {
|
|
|
4179
4344
|
}
|
|
4180
4345
|
return {
|
|
4181
4346
|
explain: () => this.#explain(matchWorker, sourceNode),
|
|
4182
|
-
isMatch:
|
|
4347
|
+
isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
|
|
4183
4348
|
};
|
|
4184
4349
|
}
|
|
4185
4350
|
}
|
|
@@ -4224,7 +4389,7 @@ class AbilityMatcherBase {
|
|
|
4224
4389
|
const isExpression = nodeBelongsToArgumentList(this.compiler, sourceNode);
|
|
4225
4390
|
const targetText = this.#resolveTargetText(targetNodes);
|
|
4226
4391
|
const diagnostics = [];
|
|
4227
|
-
if (matchWorker.assertion.abilityDiagnostics) {
|
|
4392
|
+
if (matchWorker.assertion.abilityDiagnostics.size > 0) {
|
|
4228
4393
|
for (const diagnostic of matchWorker.assertion.abilityDiagnostics) {
|
|
4229
4394
|
let origin;
|
|
4230
4395
|
const text = [];
|
|
@@ -4289,7 +4454,7 @@ class ToBeCallableWith extends AbilityMatcherBase {
|
|
|
4289
4454
|
}
|
|
4290
4455
|
return {
|
|
4291
4456
|
explain: () => this.explain(matchWorker, sourceNode, targetNodes),
|
|
4292
|
-
isMatch:
|
|
4457
|
+
isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
|
|
4293
4458
|
};
|
|
4294
4459
|
}
|
|
4295
4460
|
}
|
|
@@ -4324,7 +4489,7 @@ class ToBeConstructableWith extends AbilityMatcherBase {
|
|
|
4324
4489
|
}
|
|
4325
4490
|
return {
|
|
4326
4491
|
explain: () => this.explain(matchWorker, sourceNode, targetNodes),
|
|
4327
|
-
isMatch:
|
|
4492
|
+
isMatch: matchWorker.assertion.abilityDiagnostics.size === 0,
|
|
4328
4493
|
};
|
|
4329
4494
|
}
|
|
4330
4495
|
}
|
|
@@ -4631,7 +4796,7 @@ class WhenService {
|
|
|
4631
4796
|
this.#onActionIsNotSupported(actionNameText, when, this.#onDiagnostics);
|
|
4632
4797
|
return;
|
|
4633
4798
|
}
|
|
4634
|
-
if (when.abilityDiagnostics
|
|
4799
|
+
if (when.abilityDiagnostics.size > 0) {
|
|
4635
4800
|
const diagnostics = [];
|
|
4636
4801
|
for (const diagnostic of when.abilityDiagnostics) {
|
|
4637
4802
|
if (isDiagnosticWithLocation(diagnostic)) {
|
|
@@ -4827,8 +4992,9 @@ class TestTreeWalker {
|
|
|
4827
4992
|
class TaskRunner {
|
|
4828
4993
|
#collectService;
|
|
4829
4994
|
#compiler;
|
|
4830
|
-
#resolvedConfig;
|
|
4831
4995
|
#projectService;
|
|
4996
|
+
#resolvedConfig;
|
|
4997
|
+
#suppressedService = new SuppressedService();
|
|
4832
4998
|
constructor(compiler, resolvedConfig) {
|
|
4833
4999
|
this.#compiler = compiler;
|
|
4834
5000
|
this.#resolvedConfig = resolvedConfig;
|
|
@@ -4869,6 +5035,11 @@ class TaskRunner {
|
|
|
4869
5035
|
if (inlineConfig?.if?.target != null && !Version.isIncluded(this.#compiler.version, inlineConfig.if.target)) {
|
|
4870
5036
|
runMode |= RunMode.Skip;
|
|
4871
5037
|
}
|
|
5038
|
+
if (testTree.suppressedErrors != null) {
|
|
5039
|
+
this.#suppressedService.match(testTree.suppressedErrors, (diagnostics) => {
|
|
5040
|
+
this.#onDiagnostics(diagnostics, taskResult);
|
|
5041
|
+
});
|
|
5042
|
+
}
|
|
4872
5043
|
if (inlineConfig?.template) {
|
|
4873
5044
|
if (semanticDiagnostics != null && semanticDiagnostics.length > 0) {
|
|
4874
5045
|
this.#onDiagnostics(Diagnostic.fromDiagnostics(semanticDiagnostics), taskResult);
|
|
@@ -4913,7 +5084,7 @@ class TaskRunner {
|
|
|
4913
5084
|
class Runner {
|
|
4914
5085
|
#eventEmitter = new EventEmitter();
|
|
4915
5086
|
#resolvedConfig;
|
|
4916
|
-
static version = "4.0
|
|
5087
|
+
static version = "4.2.0";
|
|
4917
5088
|
constructor(resolvedConfig) {
|
|
4918
5089
|
this.#resolvedConfig = resolvedConfig;
|
|
4919
5090
|
}
|
|
@@ -5113,4 +5284,4 @@ class Cli {
|
|
|
5113
5284
|
}
|
|
5114
5285
|
}
|
|
5115
5286
|
|
|
5116
|
-
export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|
|
5287
|
+
export { AssertionNode, BaseReporter, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, Glob, InputService, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, PluginService, ProjectResult, ProjectService, Reject, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, SourceFile, Store, SummaryReporter, SuppressedService, TargetResult, Task, TaskResult, TestResult, TestTree, TestTreeNode, TestTreeNodeBrand, TestTreeNodeFlags, Text, Version, WatchReporter, WatchService, Watcher, WhenNode, WhenService, addsPackageText, argumentIsProvided, argumentOrTypeArgumentIsProvided, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, nodeBelongsToArgumentList, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|