tstyche 3.0.0-beta.1 → 3.0.0-beta.2
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 -2
- package/build/tstyche.d.ts +66 -52
- package/build/tstyche.js +419 -314
- package/package.json +15 -15
package/build/tstyche.js
CHANGED
|
@@ -87,15 +87,6 @@ class DescribeResult {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
var ResultStatus;
|
|
91
|
-
(function (ResultStatus) {
|
|
92
|
-
ResultStatus["Runs"] = "runs";
|
|
93
|
-
ResultStatus["Passed"] = "passed";
|
|
94
|
-
ResultStatus["Failed"] = "failed";
|
|
95
|
-
ResultStatus["Skipped"] = "skipped";
|
|
96
|
-
ResultStatus["Todo"] = "todo";
|
|
97
|
-
})(ResultStatus || (ResultStatus = {}));
|
|
98
|
-
|
|
99
90
|
class ExpectResult {
|
|
100
91
|
assertion;
|
|
101
92
|
diagnostics = [];
|
|
@@ -144,6 +135,15 @@ class Result {
|
|
|
144
135
|
}
|
|
145
136
|
}
|
|
146
137
|
|
|
138
|
+
var ResultStatus;
|
|
139
|
+
(function (ResultStatus) {
|
|
140
|
+
ResultStatus["Runs"] = "runs";
|
|
141
|
+
ResultStatus["Passed"] = "passed";
|
|
142
|
+
ResultStatus["Failed"] = "failed";
|
|
143
|
+
ResultStatus["Skipped"] = "skipped";
|
|
144
|
+
ResultStatus["Todo"] = "todo";
|
|
145
|
+
})(ResultStatus || (ResultStatus = {}));
|
|
146
|
+
|
|
147
147
|
class TargetResult {
|
|
148
148
|
results = new Map();
|
|
149
149
|
status = "runs";
|
|
@@ -193,23 +193,20 @@ class ResultHandler {
|
|
|
193
193
|
#testResult;
|
|
194
194
|
handleEvent([eventName, payload]) {
|
|
195
195
|
switch (eventName) {
|
|
196
|
-
case "run:start":
|
|
196
|
+
case "run:start":
|
|
197
197
|
this.#result = payload.result;
|
|
198
198
|
this.#result.timing.start = Date.now();
|
|
199
199
|
break;
|
|
200
|
-
|
|
201
|
-
case "run:end": {
|
|
200
|
+
case "run:end":
|
|
202
201
|
this.#result.timing.end = Date.now();
|
|
203
202
|
this.#result = undefined;
|
|
204
203
|
break;
|
|
205
|
-
|
|
206
|
-
case "target:start": {
|
|
204
|
+
case "target:start":
|
|
207
205
|
this.#result.results.push(payload.result);
|
|
208
206
|
this.#targetResult = payload.result;
|
|
209
207
|
this.#targetResult.timing.start = Date.now();
|
|
210
208
|
break;
|
|
211
|
-
|
|
212
|
-
case "target:end": {
|
|
209
|
+
case "target:end":
|
|
213
210
|
if (this.#targetResult.status === "failed") {
|
|
214
211
|
this.#result.targetCount.failed++;
|
|
215
212
|
}
|
|
@@ -220,13 +217,11 @@ class ResultHandler {
|
|
|
220
217
|
this.#targetResult.timing.end = Date.now();
|
|
221
218
|
this.#targetResult = undefined;
|
|
222
219
|
break;
|
|
223
|
-
|
|
224
|
-
case "store:error": {
|
|
220
|
+
case "store:error":
|
|
225
221
|
if (payload.diagnostics.some(({ category }) => category === "error")) {
|
|
226
222
|
this.#targetResult.status = "failed";
|
|
227
223
|
}
|
|
228
224
|
break;
|
|
229
|
-
}
|
|
230
225
|
case "project:uses": {
|
|
231
226
|
let projectResult = this.#targetResult.results.get(payload.projectConfigFilePath);
|
|
232
227
|
if (!projectResult) {
|
|
@@ -236,24 +231,21 @@ class ResultHandler {
|
|
|
236
231
|
this.#projectResult = projectResult;
|
|
237
232
|
break;
|
|
238
233
|
}
|
|
239
|
-
case "project:error":
|
|
234
|
+
case "project:error":
|
|
240
235
|
this.#targetResult.status = "failed";
|
|
241
236
|
this.#projectResult.diagnostics.push(...payload.diagnostics);
|
|
242
237
|
break;
|
|
243
|
-
|
|
244
|
-
case "task:start": {
|
|
238
|
+
case "task:start":
|
|
245
239
|
this.#projectResult.results.push(payload.result);
|
|
246
240
|
this.#taskResult = payload.result;
|
|
247
241
|
this.#taskResult.timing.start = Date.now();
|
|
248
242
|
break;
|
|
249
|
-
|
|
250
|
-
case "task:error": {
|
|
243
|
+
case "task:error":
|
|
251
244
|
this.#targetResult.status = "failed";
|
|
252
245
|
this.#taskResult.status = "failed";
|
|
253
246
|
this.#taskResult.diagnostics.push(...payload.diagnostics);
|
|
254
247
|
break;
|
|
255
|
-
|
|
256
|
-
case "task:end": {
|
|
248
|
+
case "task:end":
|
|
257
249
|
if (this.#taskResult.status === "failed" ||
|
|
258
250
|
this.#taskResult.expectCount.failed > 0 ||
|
|
259
251
|
this.#taskResult.testCount.failed > 0) {
|
|
@@ -268,8 +260,7 @@ class ResultHandler {
|
|
|
268
260
|
this.#taskResult.timing.end = Date.now();
|
|
269
261
|
this.#taskResult = undefined;
|
|
270
262
|
break;
|
|
271
|
-
|
|
272
|
-
case "describe:start": {
|
|
263
|
+
case "describe:start":
|
|
273
264
|
if (this.#describeResult) {
|
|
274
265
|
this.#describeResult.results.push(payload.result);
|
|
275
266
|
}
|
|
@@ -279,13 +270,11 @@ class ResultHandler {
|
|
|
279
270
|
this.#describeResult = payload.result;
|
|
280
271
|
this.#describeResult.timing.start = Date.now();
|
|
281
272
|
break;
|
|
282
|
-
|
|
283
|
-
case "describe:end": {
|
|
273
|
+
case "describe:end":
|
|
284
274
|
this.#describeResult.timing.end = Date.now();
|
|
285
275
|
this.#describeResult = this.#describeResult.parent;
|
|
286
276
|
break;
|
|
287
|
-
|
|
288
|
-
case "test:start": {
|
|
277
|
+
case "test:start":
|
|
289
278
|
if (this.#describeResult) {
|
|
290
279
|
this.#describeResult.results.push(payload.result);
|
|
291
280
|
}
|
|
@@ -295,8 +284,7 @@ class ResultHandler {
|
|
|
295
284
|
this.#testResult = payload.result;
|
|
296
285
|
this.#testResult.timing.start = Date.now();
|
|
297
286
|
break;
|
|
298
|
-
|
|
299
|
-
case "test:error": {
|
|
287
|
+
case "test:error":
|
|
300
288
|
this.#result.testCount.failed++;
|
|
301
289
|
this.#taskResult.testCount.failed++;
|
|
302
290
|
this.#testResult.status = "failed";
|
|
@@ -304,40 +292,35 @@ class ResultHandler {
|
|
|
304
292
|
this.#testResult.timing.end = Date.now();
|
|
305
293
|
this.#testResult = undefined;
|
|
306
294
|
break;
|
|
307
|
-
|
|
308
|
-
case "test:fail": {
|
|
295
|
+
case "test:fail":
|
|
309
296
|
this.#result.testCount.failed++;
|
|
310
297
|
this.#taskResult.testCount.failed++;
|
|
311
298
|
this.#testResult.status = "failed";
|
|
312
299
|
this.#testResult.timing.end = Date.now();
|
|
313
300
|
this.#testResult = undefined;
|
|
314
301
|
break;
|
|
315
|
-
|
|
316
|
-
case "test:pass": {
|
|
302
|
+
case "test:pass":
|
|
317
303
|
this.#result.testCount.passed++;
|
|
318
304
|
this.#taskResult.testCount.passed++;
|
|
319
305
|
this.#testResult.status = "passed";
|
|
320
306
|
this.#testResult.timing.end = Date.now();
|
|
321
307
|
this.#testResult = undefined;
|
|
322
308
|
break;
|
|
323
|
-
|
|
324
|
-
case "test:skip": {
|
|
309
|
+
case "test:skip":
|
|
325
310
|
this.#result.testCount.skipped++;
|
|
326
311
|
this.#taskResult.testCount.skipped++;
|
|
327
312
|
this.#testResult.status = "skipped";
|
|
328
313
|
this.#testResult.timing.end = Date.now();
|
|
329
314
|
this.#testResult = undefined;
|
|
330
315
|
break;
|
|
331
|
-
|
|
332
|
-
case "test:todo": {
|
|
316
|
+
case "test:todo":
|
|
333
317
|
this.#result.testCount.todo++;
|
|
334
318
|
this.#taskResult.testCount.todo++;
|
|
335
319
|
this.#testResult.status = "todo";
|
|
336
320
|
this.#testResult.timing.end = Date.now();
|
|
337
321
|
this.#testResult = undefined;
|
|
338
322
|
break;
|
|
339
|
-
|
|
340
|
-
case "expect:start": {
|
|
323
|
+
case "expect:start":
|
|
341
324
|
if (this.#testResult) {
|
|
342
325
|
this.#testResult.results.push(payload.result);
|
|
343
326
|
}
|
|
@@ -347,8 +330,7 @@ class ResultHandler {
|
|
|
347
330
|
this.#expectResult = payload.result;
|
|
348
331
|
this.#expectResult.timing.start = Date.now();
|
|
349
332
|
break;
|
|
350
|
-
|
|
351
|
-
case "expect:error": {
|
|
333
|
+
case "expect:error":
|
|
352
334
|
this.#result.expectCount.failed++;
|
|
353
335
|
this.#taskResult.expectCount.failed++;
|
|
354
336
|
if (this.#testResult) {
|
|
@@ -359,8 +341,7 @@ class ResultHandler {
|
|
|
359
341
|
this.#expectResult.timing.end = Date.now();
|
|
360
342
|
this.#expectResult = undefined;
|
|
361
343
|
break;
|
|
362
|
-
|
|
363
|
-
case "expect:fail": {
|
|
344
|
+
case "expect:fail":
|
|
364
345
|
this.#result.expectCount.failed++;
|
|
365
346
|
this.#taskResult.expectCount.failed++;
|
|
366
347
|
if (this.#testResult) {
|
|
@@ -370,8 +351,7 @@ class ResultHandler {
|
|
|
370
351
|
this.#expectResult.timing.end = Date.now();
|
|
371
352
|
this.#expectResult = undefined;
|
|
372
353
|
break;
|
|
373
|
-
|
|
374
|
-
case "expect:pass": {
|
|
354
|
+
case "expect:pass":
|
|
375
355
|
this.#result.expectCount.passed++;
|
|
376
356
|
this.#taskResult.expectCount.passed++;
|
|
377
357
|
if (this.#testResult) {
|
|
@@ -381,8 +361,7 @@ class ResultHandler {
|
|
|
381
361
|
this.#expectResult.timing.end = Date.now();
|
|
382
362
|
this.#expectResult = undefined;
|
|
383
363
|
break;
|
|
384
|
-
|
|
385
|
-
case "expect:skip": {
|
|
364
|
+
case "expect:skip":
|
|
386
365
|
this.#result.expectCount.skipped++;
|
|
387
366
|
this.#taskResult.expectCount.skipped++;
|
|
388
367
|
if (this.#testResult) {
|
|
@@ -392,7 +371,6 @@ class ResultHandler {
|
|
|
392
371
|
this.#expectResult.timing.end = Date.now();
|
|
393
372
|
this.#expectResult = undefined;
|
|
394
373
|
break;
|
|
395
|
-
}
|
|
396
374
|
}
|
|
397
375
|
}
|
|
398
376
|
}
|
|
@@ -530,29 +508,25 @@ function SquiggleLineText({ gutterWidth, indentWidth = 0, squiggleColor, squiggl
|
|
|
530
508
|
return (jsx(Line, { children: [" ".repeat(gutterWidth), jsx(Text, { color: "90", children: " | " }), " ".repeat(indentWidth), jsx(Text, { color: squiggleColor, children: "~".repeat(squiggleWidth === 0 ? 1 : squiggleWidth) })] }));
|
|
531
509
|
}
|
|
532
510
|
function CodeSpanText({ diagnosticCategory, diagnosticOrigin }) {
|
|
533
|
-
const
|
|
511
|
+
const lineMap = diagnosticOrigin.sourceFile.getLineStarts();
|
|
534
512
|
const { character: firstMarkedLineCharacter, line: firstMarkedLine } = diagnosticOrigin.sourceFile.getLineAndCharacterOfPosition(diagnosticOrigin.start);
|
|
535
513
|
const { character: lastMarkedLineCharacter, line: lastMarkedLine } = diagnosticOrigin.sourceFile.getLineAndCharacterOfPosition(diagnosticOrigin.end);
|
|
536
514
|
const firstLine = Math.max(firstMarkedLine - 2, 0);
|
|
537
|
-
const lastLine = Math.min(firstLine + 5,
|
|
515
|
+
const lastLine = Math.min(firstLine + 5, lineMap.length - 1);
|
|
538
516
|
const gutterWidth = (lastLine + 1).toString().length + 2;
|
|
539
517
|
let highlightColor;
|
|
540
518
|
switch (diagnosticCategory) {
|
|
541
|
-
case "error":
|
|
519
|
+
case "error":
|
|
542
520
|
highlightColor = "31";
|
|
543
521
|
break;
|
|
544
|
-
|
|
545
|
-
case "warning": {
|
|
522
|
+
case "warning":
|
|
546
523
|
highlightColor = "33";
|
|
547
524
|
break;
|
|
548
|
-
}
|
|
549
525
|
}
|
|
550
526
|
const codeSpan = [];
|
|
551
527
|
for (let index = firstLine; index <= lastLine; index++) {
|
|
552
|
-
const lineStart =
|
|
553
|
-
const lineEnd = index ===
|
|
554
|
-
? diagnosticOrigin.sourceFile.text.length
|
|
555
|
-
: diagnosticOrigin.sourceFile.getPositionOfLineAndCharacter(index + 1, 0);
|
|
528
|
+
const lineStart = lineMap[index];
|
|
529
|
+
const lineEnd = index === lineMap.length - 1 ? diagnosticOrigin.sourceFile.text.length : lineMap[index + 1];
|
|
556
530
|
const lineText = diagnosticOrigin.sourceFile.text.slice(lineStart, lineEnd).trimEnd().replace(/\t/g, " ");
|
|
557
531
|
if (index >= firstMarkedLine && index <= lastMarkedLine) {
|
|
558
532
|
codeSpan.push(jsx(CodeLineText, { gutterWidth: gutterWidth, lineNumber: index + 1, lineNumberColor: highlightColor, lineText: lineText }));
|
|
@@ -588,14 +562,12 @@ function DiagnosticText({ diagnostic }) {
|
|
|
588
562
|
function diagnosticText(diagnostic) {
|
|
589
563
|
let prefix;
|
|
590
564
|
switch (diagnostic.category) {
|
|
591
|
-
case "error":
|
|
565
|
+
case "error":
|
|
592
566
|
prefix = jsx(Text, { color: "31", children: "Error: " });
|
|
593
567
|
break;
|
|
594
|
-
|
|
595
|
-
case "warning": {
|
|
568
|
+
case "warning":
|
|
596
569
|
prefix = jsx(Text, { color: "33", children: "Warning: " });
|
|
597
570
|
break;
|
|
598
|
-
}
|
|
599
571
|
}
|
|
600
572
|
return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { diagnostic: diagnostic })] }));
|
|
601
573
|
}
|
|
@@ -611,21 +583,18 @@ function taskStatusText(status, task) {
|
|
|
611
583
|
let statusColor;
|
|
612
584
|
let statusText;
|
|
613
585
|
switch (status) {
|
|
614
|
-
case "runs":
|
|
586
|
+
case "runs":
|
|
615
587
|
statusColor = "33";
|
|
616
588
|
statusText = "runs";
|
|
617
589
|
break;
|
|
618
|
-
|
|
619
|
-
case "passed": {
|
|
590
|
+
case "passed":
|
|
620
591
|
statusColor = "32";
|
|
621
592
|
statusText = "pass";
|
|
622
593
|
break;
|
|
623
|
-
|
|
624
|
-
case "failed": {
|
|
594
|
+
case "failed":
|
|
625
595
|
statusColor = "31";
|
|
626
596
|
statusText = "fail";
|
|
627
597
|
break;
|
|
628
|
-
}
|
|
629
598
|
}
|
|
630
599
|
return (jsx(Line, { children: [jsx(Text, { color: statusColor, children: statusText }), " ", jsx(FileNameText, { filePath: task.filePath })] }));
|
|
631
600
|
}
|
|
@@ -701,8 +670,8 @@ function helpText(optionDefinitions, tstycheVersion) {
|
|
|
701
670
|
}
|
|
702
671
|
|
|
703
672
|
class ConfigDiagnosticText {
|
|
704
|
-
static
|
|
705
|
-
return
|
|
673
|
+
static expected(element) {
|
|
674
|
+
return `Expected ${element}.`;
|
|
706
675
|
}
|
|
707
676
|
static expectsListItemType(optionName, optionBrand) {
|
|
708
677
|
return `Item of the '${optionName}' list must be of type ${optionBrand}.`;
|
|
@@ -722,6 +691,9 @@ class ConfigDiagnosticText {
|
|
|
722
691
|
return optionName;
|
|
723
692
|
}
|
|
724
693
|
}
|
|
694
|
+
static seen(element) {
|
|
695
|
+
return `The ${element} was seen here.`;
|
|
696
|
+
}
|
|
725
697
|
static testFileMatchCannotStartWith(segment) {
|
|
726
698
|
return [
|
|
727
699
|
`A test file match pattern cannot start with '${segment}'.`,
|
|
@@ -761,9 +733,6 @@ class DiagnosticOrigin {
|
|
|
761
733
|
const node = assertion.matcherName;
|
|
762
734
|
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
|
|
763
735
|
}
|
|
764
|
-
static fromJsonNode(node, sourceFile, skipTrivia) {
|
|
765
|
-
return new DiagnosticOrigin(skipTrivia(node.pos, sourceFile), node.end, sourceFile);
|
|
766
|
-
}
|
|
767
736
|
static fromNode(node, assertion) {
|
|
768
737
|
return new DiagnosticOrigin(node.getStart(), node.getEnd(), node.getSourceFile(), assertion);
|
|
769
738
|
}
|
|
@@ -824,8 +793,48 @@ var DiagnosticCategory;
|
|
|
824
793
|
DiagnosticCategory["Warning"] = "warning";
|
|
825
794
|
})(DiagnosticCategory || (DiagnosticCategory = {}));
|
|
826
795
|
|
|
796
|
+
class SourceFile {
|
|
797
|
+
fileName;
|
|
798
|
+
#lineMap;
|
|
799
|
+
text;
|
|
800
|
+
constructor(fileName, text) {
|
|
801
|
+
this.fileName = fileName;
|
|
802
|
+
this.text = text;
|
|
803
|
+
this.#lineMap = this.#createLineMap();
|
|
804
|
+
}
|
|
805
|
+
#createLineMap() {
|
|
806
|
+
const result = [0];
|
|
807
|
+
let position = 0;
|
|
808
|
+
while (position < this.text.length) {
|
|
809
|
+
if (this.text.charAt(position - 1) === "\r") {
|
|
810
|
+
position++;
|
|
811
|
+
}
|
|
812
|
+
if (this.text.charAt(position - 1) === "\n") {
|
|
813
|
+
result.push(position);
|
|
814
|
+
}
|
|
815
|
+
position++;
|
|
816
|
+
}
|
|
817
|
+
result.push(position);
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
getLineStarts() {
|
|
821
|
+
return this.#lineMap;
|
|
822
|
+
}
|
|
823
|
+
getLineAndCharacterOfPosition(position) {
|
|
824
|
+
const line = this.#lineMap.findLastIndex((line) => line <= position);
|
|
825
|
+
const character = position - this.#lineMap[line];
|
|
826
|
+
return { line, character };
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
827
830
|
class OptionDefinitionsMap {
|
|
828
831
|
static #definitions = [
|
|
832
|
+
{
|
|
833
|
+
brand: "string",
|
|
834
|
+
description: "The Url to the config file validation schema.",
|
|
835
|
+
group: 4,
|
|
836
|
+
name: "$schema",
|
|
837
|
+
},
|
|
829
838
|
{
|
|
830
839
|
brand: "string",
|
|
831
840
|
description: "The path to a TSTyche configuration file.",
|
|
@@ -949,14 +958,12 @@ class OptionUsageText {
|
|
|
949
958
|
switch (optionName) {
|
|
950
959
|
case "target": {
|
|
951
960
|
switch (this.#optionGroup) {
|
|
952
|
-
case 2:
|
|
961
|
+
case 2:
|
|
953
962
|
usageText.push("Value for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target latest', '--target 4.9,5.3.2,current'.");
|
|
954
963
|
break;
|
|
955
|
-
|
|
956
|
-
case 4: {
|
|
964
|
+
case 4:
|
|
957
965
|
usageText.push("Item of the 'target' list must be a supported version tag.");
|
|
958
966
|
break;
|
|
959
|
-
}
|
|
960
967
|
}
|
|
961
968
|
const supportedTags = await this.#storeService.getSupportedTags();
|
|
962
969
|
if (supportedTags != null) {
|
|
@@ -1062,13 +1069,12 @@ class OptionValidator {
|
|
|
1062
1069
|
async check(optionName, optionValue, optionBrand, origin) {
|
|
1063
1070
|
switch (optionName) {
|
|
1064
1071
|
case "config":
|
|
1065
|
-
case "rootPath":
|
|
1072
|
+
case "rootPath":
|
|
1066
1073
|
if (!existsSync(optionValue)) {
|
|
1067
1074
|
this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.fileDoesNotExist(optionValue), origin));
|
|
1068
1075
|
}
|
|
1069
1076
|
break;
|
|
1070
|
-
|
|
1071
|
-
case "target": {
|
|
1077
|
+
case "target":
|
|
1072
1078
|
if ((await this.#storeService.validateTag(optionValue)) === false) {
|
|
1073
1079
|
this.#onDiagnostics(Diagnostic.error([
|
|
1074
1080
|
ConfigDiagnosticText.versionIsNotSupported(optionValue),
|
|
@@ -1076,21 +1082,18 @@ class OptionValidator {
|
|
|
1076
1082
|
], origin));
|
|
1077
1083
|
}
|
|
1078
1084
|
break;
|
|
1079
|
-
|
|
1080
|
-
case "testFileMatch": {
|
|
1085
|
+
case "testFileMatch":
|
|
1081
1086
|
for (const segment of ["/", "../"]) {
|
|
1082
1087
|
if (optionValue.startsWith(segment)) {
|
|
1083
1088
|
this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.testFileMatchCannotStartWith(segment), origin));
|
|
1084
1089
|
}
|
|
1085
1090
|
}
|
|
1086
1091
|
break;
|
|
1087
|
-
|
|
1088
|
-
case "watch": {
|
|
1092
|
+
case "watch":
|
|
1089
1093
|
if (environmentOptions.isCi) {
|
|
1090
1094
|
this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.watchCannotBeEnabled(), origin));
|
|
1091
1095
|
}
|
|
1092
1096
|
break;
|
|
1093
|
-
}
|
|
1094
1097
|
}
|
|
1095
1098
|
}
|
|
1096
1099
|
}
|
|
@@ -1147,20 +1150,18 @@ class CommandLineOptionsWorker {
|
|
|
1147
1150
|
async #parseOptionValue(commandLineArgs, index, optionDefinition) {
|
|
1148
1151
|
let optionValue = this.#resolveOptionValue(commandLineArgs[index]);
|
|
1149
1152
|
switch (optionDefinition.brand) {
|
|
1150
|
-
case "bareTrue":
|
|
1153
|
+
case "bareTrue":
|
|
1151
1154
|
await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand);
|
|
1152
1155
|
this.#commandLineOptions[optionDefinition.name] = true;
|
|
1153
1156
|
break;
|
|
1154
|
-
|
|
1155
|
-
case "boolean": {
|
|
1157
|
+
case "boolean":
|
|
1156
1158
|
await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand);
|
|
1157
1159
|
this.#commandLineOptions[optionDefinition.name] = optionValue !== "false";
|
|
1158
1160
|
if (optionValue === "false" || optionValue === "true") {
|
|
1159
1161
|
index++;
|
|
1160
1162
|
}
|
|
1161
1163
|
break;
|
|
1162
|
-
|
|
1163
|
-
case "list": {
|
|
1164
|
+
case "list":
|
|
1164
1165
|
if (optionValue !== "") {
|
|
1165
1166
|
const optionValues = optionValue
|
|
1166
1167
|
.split(",")
|
|
@@ -1175,8 +1176,7 @@ class CommandLineOptionsWorker {
|
|
|
1175
1176
|
}
|
|
1176
1177
|
await this.#onExpectsValue(optionDefinition);
|
|
1177
1178
|
break;
|
|
1178
|
-
|
|
1179
|
-
case "string": {
|
|
1179
|
+
case "string":
|
|
1180
1180
|
if (optionValue !== "") {
|
|
1181
1181
|
if (optionDefinition.name === "config") {
|
|
1182
1182
|
optionValue = Path.resolve(optionValue);
|
|
@@ -1188,7 +1188,6 @@ class CommandLineOptionsWorker {
|
|
|
1188
1188
|
}
|
|
1189
1189
|
await this.#onExpectsValue(optionDefinition);
|
|
1190
1190
|
break;
|
|
1191
|
-
}
|
|
1192
1191
|
}
|
|
1193
1192
|
return index;
|
|
1194
1193
|
}
|
|
@@ -1197,150 +1196,296 @@ class CommandLineOptionsWorker {
|
|
|
1197
1196
|
}
|
|
1198
1197
|
}
|
|
1199
1198
|
|
|
1199
|
+
class JsonNode {
|
|
1200
|
+
origin;
|
|
1201
|
+
text;
|
|
1202
|
+
constructor(text, origin) {
|
|
1203
|
+
this.origin = origin;
|
|
1204
|
+
this.text = text;
|
|
1205
|
+
}
|
|
1206
|
+
getValue(options) {
|
|
1207
|
+
if (this.text == null) {
|
|
1208
|
+
return undefined;
|
|
1209
|
+
}
|
|
1210
|
+
if (/^['"]/.test(this.text)) {
|
|
1211
|
+
return this.text.slice(1, -1);
|
|
1212
|
+
}
|
|
1213
|
+
if (options?.expectsIdentifier) {
|
|
1214
|
+
return this.text;
|
|
1215
|
+
}
|
|
1216
|
+
if (this.text === "true") {
|
|
1217
|
+
return true;
|
|
1218
|
+
}
|
|
1219
|
+
if (this.text === "false") {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
if (/^\d/.test(this.text)) {
|
|
1223
|
+
return Number.parseFloat(this.text);
|
|
1224
|
+
}
|
|
1225
|
+
return undefined;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
class JsonScanner {
|
|
1230
|
+
#currentPosition = 0;
|
|
1231
|
+
#previousPosition = 0;
|
|
1232
|
+
#sourceFile;
|
|
1233
|
+
constructor(sourceFile) {
|
|
1234
|
+
this.#sourceFile = sourceFile;
|
|
1235
|
+
}
|
|
1236
|
+
#getOrigin() {
|
|
1237
|
+
return new DiagnosticOrigin(this.#previousPosition, this.#currentPosition, this.#sourceFile);
|
|
1238
|
+
}
|
|
1239
|
+
isRead() {
|
|
1240
|
+
return !(this.#currentPosition < this.#sourceFile.text.length);
|
|
1241
|
+
}
|
|
1242
|
+
#peekCharacter() {
|
|
1243
|
+
return this.#sourceFile.text.charAt(this.#currentPosition);
|
|
1244
|
+
}
|
|
1245
|
+
#peekNextCharacter() {
|
|
1246
|
+
return this.#sourceFile.text.charAt(this.#currentPosition + 1);
|
|
1247
|
+
}
|
|
1248
|
+
peekToken(token) {
|
|
1249
|
+
this.#skipTrivia();
|
|
1250
|
+
return this.#peekCharacter() === token;
|
|
1251
|
+
}
|
|
1252
|
+
read() {
|
|
1253
|
+
this.#skipTrivia();
|
|
1254
|
+
if (/[:\]}]/.test(this.#peekCharacter())) {
|
|
1255
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
1256
|
+
}
|
|
1257
|
+
let quoteCharacter = "";
|
|
1258
|
+
let text = "";
|
|
1259
|
+
this.#previousPosition = this.#currentPosition;
|
|
1260
|
+
if (/['"]/.test(this.#peekCharacter())) {
|
|
1261
|
+
quoteCharacter = text += this.#readCharacter();
|
|
1262
|
+
}
|
|
1263
|
+
while (!this.isRead()) {
|
|
1264
|
+
text += this.#readCharacter();
|
|
1265
|
+
if (text.slice(-1) === quoteCharacter || (!quoteCharacter && /[\s,:\]}]/.test(this.#peekCharacter()))) {
|
|
1266
|
+
break;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return new JsonNode(text, this.#getOrigin());
|
|
1270
|
+
}
|
|
1271
|
+
#readCharacter() {
|
|
1272
|
+
return this.#sourceFile.text.charAt(this.#currentPosition++);
|
|
1273
|
+
}
|
|
1274
|
+
readToken(token) {
|
|
1275
|
+
this.#skipTrivia();
|
|
1276
|
+
this.#previousPosition = this.#currentPosition;
|
|
1277
|
+
if (this.#peekCharacter() === token) {
|
|
1278
|
+
this.#currentPosition++;
|
|
1279
|
+
return new JsonNode(token, this.#getOrigin());
|
|
1280
|
+
}
|
|
1281
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
1282
|
+
}
|
|
1283
|
+
skip() {
|
|
1284
|
+
this.#skipTrivia();
|
|
1285
|
+
this.#previousPosition = this.#currentPosition;
|
|
1286
|
+
if (/[\s,:\]}]/.test(this.#peekCharacter())) {
|
|
1287
|
+
return new JsonNode(undefined, this.#getOrigin());
|
|
1288
|
+
}
|
|
1289
|
+
let text = "";
|
|
1290
|
+
let closingCharacter = "";
|
|
1291
|
+
if (/[[{'"]/.test(this.#peekCharacter())) {
|
|
1292
|
+
text += this.#readCharacter();
|
|
1293
|
+
switch (text) {
|
|
1294
|
+
case "[":
|
|
1295
|
+
closingCharacter = "]";
|
|
1296
|
+
break;
|
|
1297
|
+
case "{":
|
|
1298
|
+
closingCharacter = "}";
|
|
1299
|
+
break;
|
|
1300
|
+
default:
|
|
1301
|
+
closingCharacter = text;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
while (!this.isRead()) {
|
|
1305
|
+
text += this.#readCharacter();
|
|
1306
|
+
if (text.slice(-1) === closingCharacter || (!closingCharacter && /[\s,:\]}]/.test(this.#peekCharacter()))) {
|
|
1307
|
+
break;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return new JsonNode(text, this.#getOrigin());
|
|
1311
|
+
}
|
|
1312
|
+
#skipTrivia() {
|
|
1313
|
+
while (!this.isRead()) {
|
|
1314
|
+
if (/\s/.test(this.#peekCharacter())) {
|
|
1315
|
+
this.#currentPosition++;
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
if (this.#peekCharacter() === "/") {
|
|
1319
|
+
if (this.#peekNextCharacter() === "/") {
|
|
1320
|
+
this.#currentPosition += 2;
|
|
1321
|
+
while (!this.isRead()) {
|
|
1322
|
+
if (this.#readCharacter() === "\n") {
|
|
1323
|
+
break;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
if (this.#peekNextCharacter() === "*") {
|
|
1329
|
+
this.#currentPosition += 2;
|
|
1330
|
+
while (!this.isRead()) {
|
|
1331
|
+
if (this.#peekCharacter() === "*" && this.#peekNextCharacter() === "/") {
|
|
1332
|
+
this.#currentPosition += 2;
|
|
1333
|
+
break;
|
|
1334
|
+
}
|
|
1335
|
+
this.#currentPosition++;
|
|
1336
|
+
}
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
this.#previousPosition = this.#currentPosition;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1200
1346
|
class ConfigFileOptionsWorker {
|
|
1201
|
-
#compiler;
|
|
1202
1347
|
#configFileOptionDefinitions;
|
|
1203
1348
|
#configFileOptions;
|
|
1204
|
-
#
|
|
1349
|
+
#jsonScanner;
|
|
1205
1350
|
#onDiagnostics;
|
|
1206
1351
|
#optionGroup = 4;
|
|
1207
1352
|
#optionValidator;
|
|
1353
|
+
#sourceFile;
|
|
1208
1354
|
#storeService;
|
|
1209
|
-
constructor(
|
|
1210
|
-
this.#compiler = compiler;
|
|
1355
|
+
constructor(configFileOptions, sourceFile, storeService, onDiagnostics) {
|
|
1211
1356
|
this.#configFileOptions = configFileOptions;
|
|
1212
|
-
this.#
|
|
1357
|
+
this.#sourceFile = sourceFile;
|
|
1213
1358
|
this.#storeService = storeService;
|
|
1214
1359
|
this.#onDiagnostics = onDiagnostics;
|
|
1215
1360
|
this.#configFileOptionDefinitions = OptionDefinitionsMap.for(this.#optionGroup);
|
|
1361
|
+
this.#jsonScanner = new JsonScanner(this.#sourceFile);
|
|
1216
1362
|
this.#optionValidator = new OptionValidator(this.#optionGroup, this.#storeService, this.#onDiagnostics);
|
|
1217
1363
|
}
|
|
1218
|
-
#
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
const sourceFile = this.#compiler.parseJsonText(this.#configFilePath, sourceText);
|
|
1224
|
-
if (sourceFile.parseDiagnostics.length > 0) {
|
|
1225
|
-
this.#onDiagnostics(Diagnostic.fromDiagnostics(sourceFile.parseDiagnostics, this.#compiler));
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
const rootExpression = sourceFile.statements[0]?.expression;
|
|
1229
|
-
if (!rootExpression) {
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
|
-
if (!this.#compiler.isObjectLiteralExpression(rootExpression)) {
|
|
1233
|
-
const origin = DiagnosticOrigin.fromJsonNode(rootExpression, sourceFile, this.#skipTrivia);
|
|
1234
|
-
this.#onDiagnostics(Diagnostic.error("The root value of a configuration file must be an object literal.", origin));
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1237
|
-
for (const property of rootExpression.properties) {
|
|
1238
|
-
if (this.#compiler.isPropertyAssignment(property)) {
|
|
1239
|
-
if (!this.#isDoubleQuotedString(property.name, sourceFile)) {
|
|
1240
|
-
const origin = DiagnosticOrigin.fromJsonNode(property.name, sourceFile, this.#skipTrivia);
|
|
1241
|
-
this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.doubleQuotesExpected(), origin));
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
const optionName = property.name.text;
|
|
1245
|
-
if (optionName === "$schema") {
|
|
1246
|
-
continue;
|
|
1247
|
-
}
|
|
1248
|
-
const optionDefinition = this.#configFileOptionDefinitions.get(optionName);
|
|
1249
|
-
if (optionDefinition) {
|
|
1250
|
-
this.#configFileOptions[optionDefinition.name] = await this.#parseOptionValue(sourceFile, property.initializer, optionDefinition);
|
|
1251
|
-
}
|
|
1252
|
-
else {
|
|
1253
|
-
const origin = DiagnosticOrigin.fromJsonNode(property.name, sourceFile, this.#skipTrivia);
|
|
1254
|
-
this.#onDiagnostics(Diagnostic.error(ConfigDiagnosticText.unknownOption(optionName), origin));
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
return;
|
|
1364
|
+
#onRequiresValue(optionDefinition, jsonNode, isListItem) {
|
|
1365
|
+
const text = isListItem
|
|
1366
|
+
? ConfigDiagnosticText.expectsListItemType(optionDefinition.name, optionDefinition.brand)
|
|
1367
|
+
: ConfigDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand, this.#optionGroup);
|
|
1368
|
+
this.#onDiagnostics(Diagnostic.error(text, jsonNode.origin));
|
|
1259
1369
|
}
|
|
1260
|
-
async #
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1370
|
+
async #parseValue(optionDefinition, isListItem = false) {
|
|
1371
|
+
let jsonNode;
|
|
1372
|
+
let optionValue;
|
|
1373
|
+
switch (optionDefinition.brand) {
|
|
1374
|
+
case "boolean": {
|
|
1375
|
+
jsonNode = this.#jsonScanner.read();
|
|
1376
|
+
optionValue = jsonNode.getValue();
|
|
1377
|
+
if (typeof optionValue !== "boolean") {
|
|
1378
|
+
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1379
|
+
break;
|
|
1265
1380
|
}
|
|
1266
1381
|
break;
|
|
1267
1382
|
}
|
|
1268
|
-
case
|
|
1269
|
-
|
|
1270
|
-
|
|
1383
|
+
case "string": {
|
|
1384
|
+
jsonNode = this.#jsonScanner.read();
|
|
1385
|
+
optionValue = jsonNode.getValue();
|
|
1386
|
+
if (typeof optionValue !== "string") {
|
|
1387
|
+
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1388
|
+
break;
|
|
1271
1389
|
}
|
|
1390
|
+
if (optionDefinition.name === "rootPath") {
|
|
1391
|
+
optionValue = Path.resolve(Path.dirname(this.#sourceFile.fileName), optionValue);
|
|
1392
|
+
}
|
|
1393
|
+
await this.#optionValidator.check(optionDefinition.name, optionValue, optionDefinition.brand, jsonNode.origin);
|
|
1272
1394
|
break;
|
|
1273
1395
|
}
|
|
1274
|
-
case
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
this.#
|
|
1278
|
-
|
|
1396
|
+
case "list": {
|
|
1397
|
+
const leftBracketToken = this.#jsonScanner.readToken("[");
|
|
1398
|
+
if (!leftBracketToken.text) {
|
|
1399
|
+
jsonNode = this.#jsonScanner.skip();
|
|
1400
|
+
this.#onRequiresValue(optionDefinition, jsonNode, isListItem);
|
|
1401
|
+
break;
|
|
1279
1402
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1403
|
+
optionValue = [];
|
|
1404
|
+
while (!this.#jsonScanner.isRead()) {
|
|
1405
|
+
if (this.#jsonScanner.peekToken("]")) {
|
|
1406
|
+
break;
|
|
1284
1407
|
}
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
if (optionDefinition.brand === "list") {
|
|
1293
|
-
const value = [];
|
|
1294
|
-
for (const element of valueExpression.elements) {
|
|
1295
|
-
value.push(await this.#parseOptionValue(sourceFile, element, optionDefinition.items, true));
|
|
1408
|
+
const item = await this.#parseValue(optionDefinition.items, true);
|
|
1409
|
+
if (item != null) {
|
|
1410
|
+
optionValue.push(item);
|
|
1411
|
+
}
|
|
1412
|
+
const commaToken = this.#jsonScanner.readToken(",");
|
|
1413
|
+
if (!commaToken.text) {
|
|
1414
|
+
break;
|
|
1296
1415
|
}
|
|
1297
|
-
|
|
1416
|
+
}
|
|
1417
|
+
const rightBracketToken = this.#jsonScanner.readToken("]");
|
|
1418
|
+
if (!rightBracketToken.text) {
|
|
1419
|
+
const text = ConfigDiagnosticText.expected("closing ']'");
|
|
1420
|
+
const relatedText = ConfigDiagnosticText.seen("opening '['");
|
|
1421
|
+
const diagnostic = Diagnostic.error(text, rightBracketToken.origin).add({
|
|
1422
|
+
related: [Diagnostic.error(relatedText, leftBracketToken.origin)],
|
|
1423
|
+
});
|
|
1424
|
+
this.#onDiagnostics(diagnostic);
|
|
1298
1425
|
}
|
|
1299
1426
|
break;
|
|
1300
1427
|
}
|
|
1301
1428
|
}
|
|
1302
|
-
|
|
1303
|
-
? ConfigDiagnosticText.expectsListItemType(optionDefinition.name, optionDefinition.brand)
|
|
1304
|
-
: ConfigDiagnosticText.requiresValueType(optionDefinition.name, optionDefinition.brand, this.#optionGroup);
|
|
1305
|
-
const origin = DiagnosticOrigin.fromJsonNode(valueExpression, sourceFile, this.#skipTrivia);
|
|
1306
|
-
this.#onDiagnostics(Diagnostic.error(text, origin));
|
|
1307
|
-
return;
|
|
1429
|
+
return optionValue;
|
|
1308
1430
|
}
|
|
1309
|
-
#
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1431
|
+
async #parseObject() {
|
|
1432
|
+
const leftBraceToken = this.#jsonScanner.readToken("{");
|
|
1433
|
+
if (this.#jsonScanner.isRead()) {
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (!leftBraceToken.text) {
|
|
1437
|
+
const text = ConfigDiagnosticText.expected("'{'");
|
|
1438
|
+
this.#onDiagnostics(Diagnostic.error(text, leftBraceToken.origin));
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
while (!this.#jsonScanner.isRead()) {
|
|
1442
|
+
if (this.#jsonScanner.peekToken("}")) {
|
|
1443
|
+
break;
|
|
1315
1444
|
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1445
|
+
const optionNameNode = this.#jsonScanner.read();
|
|
1446
|
+
const optionName = optionNameNode.getValue({ expectsIdentifier: true });
|
|
1447
|
+
if (!optionName) {
|
|
1448
|
+
const text = ConfigDiagnosticText.expected("option name");
|
|
1449
|
+
this.#onDiagnostics(Diagnostic.error(text, optionNameNode.origin));
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
const optionDefinition = this.#configFileOptionDefinitions.get(optionName);
|
|
1453
|
+
if (!optionDefinition) {
|
|
1454
|
+
const text = ConfigDiagnosticText.unknownOption(optionName);
|
|
1455
|
+
this.#onDiagnostics(Diagnostic.error(text, optionNameNode.origin));
|
|
1456
|
+
if (this.#jsonScanner.readToken(":")) {
|
|
1457
|
+
this.#jsonScanner.skip();
|
|
1326
1458
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
if (text.charAt(position) === "*" && text.charAt(position + 1) === "/") {
|
|
1331
|
-
position += 2;
|
|
1332
|
-
break;
|
|
1333
|
-
}
|
|
1334
|
-
position++;
|
|
1335
|
-
}
|
|
1336
|
-
continue;
|
|
1459
|
+
const commaToken = this.#jsonScanner.readToken(",");
|
|
1460
|
+
if (!commaToken.text) {
|
|
1461
|
+
break;
|
|
1337
1462
|
}
|
|
1338
|
-
position++;
|
|
1339
1463
|
continue;
|
|
1340
1464
|
}
|
|
1341
|
-
|
|
1465
|
+
if (this.#jsonScanner.peekToken(":")) {
|
|
1466
|
+
this.#jsonScanner.readToken(":");
|
|
1467
|
+
}
|
|
1468
|
+
const parsedValue = await this.#parseValue(optionDefinition);
|
|
1469
|
+
if (optionDefinition.name !== "$schema") {
|
|
1470
|
+
this.#configFileOptions[optionDefinition.name] = parsedValue;
|
|
1471
|
+
}
|
|
1472
|
+
const commaToken = this.#jsonScanner.readToken(",");
|
|
1473
|
+
if (!commaToken.text) {
|
|
1474
|
+
break;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
const rightBraceToken = this.#jsonScanner.readToken("}");
|
|
1478
|
+
if (!rightBraceToken.text) {
|
|
1479
|
+
const text = ConfigDiagnosticText.expected("closing '}'");
|
|
1480
|
+
const relatedText = ConfigDiagnosticText.seen("opening '{'");
|
|
1481
|
+
const diagnostic = Diagnostic.error(text, rightBraceToken.origin).add({
|
|
1482
|
+
related: [Diagnostic.error(relatedText, leftBraceToken.origin)],
|
|
1483
|
+
});
|
|
1484
|
+
this.#onDiagnostics(diagnostic);
|
|
1342
1485
|
}
|
|
1343
|
-
|
|
1486
|
+
}
|
|
1487
|
+
async parse() {
|
|
1488
|
+
await this.#parseObject();
|
|
1344
1489
|
}
|
|
1345
1490
|
}
|
|
1346
1491
|
|
|
@@ -1357,7 +1502,7 @@ class ConfigService {
|
|
|
1357
1502
|
#configFilePath = Path.resolve(defaultOptions.rootPath, "./tstyche.config.json");
|
|
1358
1503
|
#pathMatch = [];
|
|
1359
1504
|
#onDiagnostics(diagnostics) {
|
|
1360
|
-
EventEmitter.dispatch(["config:error", { diagnostics:
|
|
1505
|
+
EventEmitter.dispatch(["config:error", { diagnostics: [diagnostics] }]);
|
|
1361
1506
|
}
|
|
1362
1507
|
async parseCommandLine(commandLineArgs, storeService) {
|
|
1363
1508
|
this.#commandLineOptions = {};
|
|
@@ -1379,12 +1524,9 @@ class ConfigService {
|
|
|
1379
1524
|
const configFileText = await fs.readFile(this.#configFilePath, {
|
|
1380
1525
|
encoding: "utf8",
|
|
1381
1526
|
});
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
}
|
|
1386
|
-
const configFileWorker = new ConfigFileOptionsWorker(compiler, this.#configFileOptions, this.#configFilePath, storeService, this.#onDiagnostics);
|
|
1387
|
-
await configFileWorker.parse(configFileText);
|
|
1527
|
+
const sourceFile = new SourceFile(this.#configFilePath, configFileText);
|
|
1528
|
+
const configFileWorker = new ConfigFileOptionsWorker(this.#configFileOptions, sourceFile, storeService, this.#onDiagnostics);
|
|
1529
|
+
await configFileWorker.parse();
|
|
1388
1530
|
}
|
|
1389
1531
|
resolveConfig() {
|
|
1390
1532
|
return {
|
|
@@ -1406,6 +1548,7 @@ var OptionBrand;
|
|
|
1406
1548
|
OptionBrand["BareTrue"] = "bareTrue";
|
|
1407
1549
|
OptionBrand["List"] = "list";
|
|
1408
1550
|
})(OptionBrand || (OptionBrand = {}));
|
|
1551
|
+
|
|
1409
1552
|
var OptionGroup;
|
|
1410
1553
|
(function (OptionGroup) {
|
|
1411
1554
|
OptionGroup[OptionGroup["CommandLine"] = 2] = "CommandLine";
|
|
@@ -1589,31 +1732,26 @@ class RunReporter extends Reporter {
|
|
|
1589
1732
|
}
|
|
1590
1733
|
handleEvent([eventName, payload]) {
|
|
1591
1734
|
switch (eventName) {
|
|
1592
|
-
case "run:start":
|
|
1735
|
+
case "run:start":
|
|
1593
1736
|
this.#isFileViewExpanded = payload.result.tasks.length === 1 && this.#resolvedConfig.watch !== true;
|
|
1594
1737
|
break;
|
|
1595
|
-
|
|
1596
|
-
case "store:adds": {
|
|
1738
|
+
case "store:adds":
|
|
1597
1739
|
this.outputService.writeMessage(addsPackageText(payload.packageVersion, payload.packagePath));
|
|
1598
1740
|
this.#hasReportedAdds = true;
|
|
1599
1741
|
break;
|
|
1600
|
-
|
|
1601
|
-
case "store:error": {
|
|
1742
|
+
case "store:error":
|
|
1602
1743
|
for (const diagnostic of payload.diagnostics) {
|
|
1603
1744
|
this.outputService.writeError(diagnosticText(diagnostic));
|
|
1604
1745
|
}
|
|
1605
1746
|
break;
|
|
1606
|
-
|
|
1607
|
-
case "target:start": {
|
|
1747
|
+
case "target:start":
|
|
1608
1748
|
this.#fileCount = payload.result.tasks.length;
|
|
1609
1749
|
break;
|
|
1610
|
-
|
|
1611
|
-
case "target:end": {
|
|
1750
|
+
case "target:end":
|
|
1612
1751
|
this.#currentCompilerVersion = undefined;
|
|
1613
1752
|
this.#currentProjectConfigFilePath = undefined;
|
|
1614
1753
|
break;
|
|
1615
|
-
|
|
1616
|
-
case "project:uses": {
|
|
1754
|
+
case "project:uses":
|
|
1617
1755
|
if (this.#currentCompilerVersion !== payload.compilerVersion ||
|
|
1618
1756
|
this.#currentProjectConfigFilePath !== payload.projectConfigFilePath) {
|
|
1619
1757
|
this.outputService.writeMessage(usesCompilerText(payload.compilerVersion, payload.projectConfigFilePath, {
|
|
@@ -1624,28 +1762,24 @@ class RunReporter extends Reporter {
|
|
|
1624
1762
|
this.#currentProjectConfigFilePath = payload.projectConfigFilePath;
|
|
1625
1763
|
}
|
|
1626
1764
|
break;
|
|
1627
|
-
|
|
1628
|
-
case "project:error": {
|
|
1765
|
+
case "project:error":
|
|
1629
1766
|
for (const diagnostic of payload.diagnostics) {
|
|
1630
1767
|
this.outputService.writeError(diagnosticText(diagnostic));
|
|
1631
1768
|
}
|
|
1632
1769
|
break;
|
|
1633
|
-
|
|
1634
|
-
case "task:start": {
|
|
1770
|
+
case "task:start":
|
|
1635
1771
|
if (!this.#resolvedConfig.noInteractive) {
|
|
1636
1772
|
this.outputService.writeMessage(taskStatusText(payload.result.status, payload.result.task));
|
|
1637
1773
|
}
|
|
1638
1774
|
this.#fileCount--;
|
|
1639
1775
|
this.#hasReportedError = false;
|
|
1640
1776
|
break;
|
|
1641
|
-
|
|
1642
|
-
case "task:error": {
|
|
1777
|
+
case "task:error":
|
|
1643
1778
|
for (const diagnostic of payload.diagnostics) {
|
|
1644
1779
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
1645
1780
|
}
|
|
1646
1781
|
break;
|
|
1647
|
-
|
|
1648
|
-
case "task:end": {
|
|
1782
|
+
case "task:end":
|
|
1649
1783
|
if (!this.#resolvedConfig.noInteractive) {
|
|
1650
1784
|
this.outputService.eraseLastLine();
|
|
1651
1785
|
}
|
|
@@ -1657,32 +1791,27 @@ class RunReporter extends Reporter {
|
|
|
1657
1791
|
}
|
|
1658
1792
|
this.#fileView.clear();
|
|
1659
1793
|
break;
|
|
1660
|
-
|
|
1661
|
-
case "describe:start": {
|
|
1794
|
+
case "describe:start":
|
|
1662
1795
|
if (this.#isFileViewExpanded) {
|
|
1663
1796
|
this.#fileView.beginDescribe(payload.result.describe.name);
|
|
1664
1797
|
}
|
|
1665
1798
|
break;
|
|
1666
|
-
|
|
1667
|
-
case "describe:end": {
|
|
1799
|
+
case "describe:end":
|
|
1668
1800
|
if (this.#isFileViewExpanded) {
|
|
1669
1801
|
this.#fileView.endDescribe();
|
|
1670
1802
|
}
|
|
1671
1803
|
break;
|
|
1672
|
-
|
|
1673
|
-
case "test:skip": {
|
|
1804
|
+
case "test:skip":
|
|
1674
1805
|
if (this.#isFileViewExpanded) {
|
|
1675
1806
|
this.#fileView.addTest("skip", payload.result.test.name);
|
|
1676
1807
|
}
|
|
1677
1808
|
break;
|
|
1678
|
-
|
|
1679
|
-
case "test:todo": {
|
|
1809
|
+
case "test:todo":
|
|
1680
1810
|
if (this.#isFileViewExpanded) {
|
|
1681
1811
|
this.#fileView.addTest("todo", payload.result.test.name);
|
|
1682
1812
|
}
|
|
1683
1813
|
break;
|
|
1684
|
-
|
|
1685
|
-
case "test:error": {
|
|
1814
|
+
case "test:error":
|
|
1686
1815
|
if (this.#isFileViewExpanded) {
|
|
1687
1816
|
this.#fileView.addTest("fail", payload.result.test.name);
|
|
1688
1817
|
}
|
|
@@ -1690,26 +1819,22 @@ class RunReporter extends Reporter {
|
|
|
1690
1819
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
1691
1820
|
}
|
|
1692
1821
|
break;
|
|
1693
|
-
|
|
1694
|
-
case "test:fail": {
|
|
1822
|
+
case "test:fail":
|
|
1695
1823
|
if (this.#isFileViewExpanded) {
|
|
1696
1824
|
this.#fileView.addTest("fail", payload.result.test.name);
|
|
1697
1825
|
}
|
|
1698
1826
|
break;
|
|
1699
|
-
|
|
1700
|
-
case "test:pass": {
|
|
1827
|
+
case "test:pass":
|
|
1701
1828
|
if (this.#isFileViewExpanded) {
|
|
1702
1829
|
this.#fileView.addTest("pass", payload.result.test.name);
|
|
1703
1830
|
}
|
|
1704
1831
|
break;
|
|
1705
|
-
}
|
|
1706
1832
|
case "expect:error":
|
|
1707
|
-
case "expect:fail":
|
|
1833
|
+
case "expect:fail":
|
|
1708
1834
|
for (const diagnostic of payload.diagnostics) {
|
|
1709
1835
|
this.#fileView.addMessage(diagnosticText(diagnostic));
|
|
1710
1836
|
}
|
|
1711
1837
|
break;
|
|
1712
|
-
}
|
|
1713
1838
|
}
|
|
1714
1839
|
}
|
|
1715
1840
|
}
|
|
@@ -1723,14 +1848,12 @@ class SetupReporter extends Reporter {
|
|
|
1723
1848
|
if ("diagnostics" in payload) {
|
|
1724
1849
|
for (const diagnostic of payload.diagnostics) {
|
|
1725
1850
|
switch (diagnostic.category) {
|
|
1726
|
-
case "error":
|
|
1851
|
+
case "error":
|
|
1727
1852
|
this.outputService.writeError(diagnosticText(diagnostic));
|
|
1728
1853
|
break;
|
|
1729
|
-
|
|
1730
|
-
case "warning": {
|
|
1854
|
+
case "warning":
|
|
1731
1855
|
this.outputService.writeWarning(diagnosticText(diagnostic));
|
|
1732
1856
|
break;
|
|
1733
|
-
}
|
|
1734
1857
|
}
|
|
1735
1858
|
}
|
|
1736
1859
|
}
|
|
@@ -1739,20 +1862,17 @@ class SetupReporter extends Reporter {
|
|
|
1739
1862
|
|
|
1740
1863
|
class SummaryReporter extends Reporter {
|
|
1741
1864
|
handleEvent([eventName, payload]) {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
}));
|
|
1754
|
-
break;
|
|
1755
|
-
}
|
|
1865
|
+
if (eventName === "run:end") {
|
|
1866
|
+
this.outputService.writeMessage(summaryText({
|
|
1867
|
+
duration: payload.result.timing.duration,
|
|
1868
|
+
expectCount: payload.result.expectCount,
|
|
1869
|
+
fileCount: payload.result.fileCount,
|
|
1870
|
+
onlyMatch: payload.result.resolvedConfig.only,
|
|
1871
|
+
pathMatch: payload.result.resolvedConfig.pathMatch,
|
|
1872
|
+
skipMatch: payload.result.resolvedConfig.skip,
|
|
1873
|
+
targetCount: payload.result.targetCount,
|
|
1874
|
+
testCount: payload.result.testCount,
|
|
1875
|
+
}));
|
|
1756
1876
|
}
|
|
1757
1877
|
}
|
|
1758
1878
|
}
|
|
@@ -1760,22 +1880,19 @@ class SummaryReporter extends Reporter {
|
|
|
1760
1880
|
class WatchReporter extends Reporter {
|
|
1761
1881
|
handleEvent([eventName, payload]) {
|
|
1762
1882
|
switch (eventName) {
|
|
1763
|
-
case "run:start":
|
|
1883
|
+
case "run:start":
|
|
1764
1884
|
this.outputService.clearTerminal();
|
|
1765
1885
|
break;
|
|
1766
|
-
|
|
1767
|
-
case "run:end": {
|
|
1886
|
+
case "run:end":
|
|
1768
1887
|
this.outputService.writeMessage(watchUsageText());
|
|
1769
1888
|
break;
|
|
1770
|
-
|
|
1771
|
-
case "watch:error": {
|
|
1889
|
+
case "watch:error":
|
|
1772
1890
|
this.outputService.clearTerminal();
|
|
1773
1891
|
for (const diagnostic of payload.diagnostics) {
|
|
1774
1892
|
this.outputService.writeError(diagnosticText(diagnostic));
|
|
1775
1893
|
}
|
|
1776
1894
|
this.outputService.writeMessage(waitingForFileChangesText());
|
|
1777
1895
|
break;
|
|
1778
|
-
}
|
|
1779
1896
|
}
|
|
1780
1897
|
}
|
|
1781
1898
|
}
|
|
@@ -2082,19 +2199,17 @@ class WatchService {
|
|
|
2082
2199
|
case "\u0004":
|
|
2083
2200
|
case "\u001B":
|
|
2084
2201
|
case "q":
|
|
2085
|
-
case "x":
|
|
2202
|
+
case "x":
|
|
2086
2203
|
onClose("watchClose");
|
|
2087
2204
|
break;
|
|
2088
|
-
}
|
|
2089
2205
|
case "\u000D":
|
|
2090
2206
|
case "\u0020":
|
|
2091
|
-
case "a":
|
|
2207
|
+
case "a":
|
|
2092
2208
|
debounce.clearTimeout();
|
|
2093
|
-
if (this.#watchedTestFiles.size
|
|
2209
|
+
if (this.#watchedTestFiles.size > 0) {
|
|
2094
2210
|
debounce.resolveWith([...this.#watchedTestFiles.values()]);
|
|
2095
2211
|
}
|
|
2096
2212
|
break;
|
|
2097
|
-
}
|
|
2098
2213
|
}
|
|
2099
2214
|
};
|
|
2100
2215
|
this.#inputService = new InputService(onInput);
|
|
@@ -2176,23 +2291,21 @@ class TestMember {
|
|
|
2176
2291
|
return node.parent;
|
|
2177
2292
|
};
|
|
2178
2293
|
switch (this.brand) {
|
|
2179
|
-
case "describe":
|
|
2294
|
+
case "describe":
|
|
2180
2295
|
for (const member of this.members) {
|
|
2181
2296
|
if (member.brand === "expect") {
|
|
2182
2297
|
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(getParentCallExpression(member.node))));
|
|
2183
2298
|
}
|
|
2184
2299
|
}
|
|
2185
2300
|
break;
|
|
2186
|
-
}
|
|
2187
2301
|
case "test":
|
|
2188
|
-
case "expect":
|
|
2302
|
+
case "expect":
|
|
2189
2303
|
for (const member of this.members) {
|
|
2190
2304
|
if (member.brand !== "expect") {
|
|
2191
2305
|
diagnostics.push(Diagnostic.error(getText(member.node), DiagnosticOrigin.fromNode(member.node)));
|
|
2192
2306
|
}
|
|
2193
2307
|
}
|
|
2194
2308
|
break;
|
|
2195
|
-
}
|
|
2196
2309
|
}
|
|
2197
2310
|
return diagnostics;
|
|
2198
2311
|
}
|
|
@@ -2277,22 +2390,18 @@ class IdentifierLookup {
|
|
|
2277
2390
|
break;
|
|
2278
2391
|
}
|
|
2279
2392
|
switch (expression.name.getText()) {
|
|
2280
|
-
case "fail":
|
|
2393
|
+
case "fail":
|
|
2281
2394
|
flags |= 1;
|
|
2282
2395
|
break;
|
|
2283
|
-
|
|
2284
|
-
case "only": {
|
|
2396
|
+
case "only":
|
|
2285
2397
|
flags |= 2;
|
|
2286
2398
|
break;
|
|
2287
|
-
|
|
2288
|
-
case "skip": {
|
|
2399
|
+
case "skip":
|
|
2289
2400
|
flags |= 4;
|
|
2290
2401
|
break;
|
|
2291
|
-
|
|
2292
|
-
case "todo": {
|
|
2402
|
+
case "todo":
|
|
2293
2403
|
flags |= 8;
|
|
2294
2404
|
break;
|
|
2295
|
-
}
|
|
2296
2405
|
}
|
|
2297
2406
|
expression = expression.expression;
|
|
2298
2407
|
}
|
|
@@ -2403,6 +2512,7 @@ var TestMemberBrand;
|
|
|
2403
2512
|
TestMemberBrand["Test"] = "test";
|
|
2404
2513
|
TestMemberBrand["Expect"] = "expect";
|
|
2405
2514
|
})(TestMemberBrand || (TestMemberBrand = {}));
|
|
2515
|
+
|
|
2406
2516
|
var TestMemberFlags;
|
|
2407
2517
|
(function (TestMemberFlags) {
|
|
2408
2518
|
TestMemberFlags[TestMemberFlags["None"] = 0] = "None";
|
|
@@ -3170,13 +3280,12 @@ class ExpectService {
|
|
|
3170
3280
|
case "toBe":
|
|
3171
3281
|
case "toBeAssignableTo":
|
|
3172
3282
|
case "toBeAssignableWith":
|
|
3173
|
-
case "toMatch":
|
|
3283
|
+
case "toMatch":
|
|
3174
3284
|
if (!assertion.target[0]) {
|
|
3175
3285
|
this.#onTargetArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics);
|
|
3176
3286
|
return;
|
|
3177
3287
|
}
|
|
3178
3288
|
return this[matcherNameText].match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3179
|
-
}
|
|
3180
3289
|
case "toBeAny":
|
|
3181
3290
|
case "toBeBigInt":
|
|
3182
3291
|
case "toBeBoolean":
|
|
@@ -3188,27 +3297,26 @@ class ExpectService {
|
|
|
3188
3297
|
case "toBeUndefined":
|
|
3189
3298
|
case "toBeUniqueSymbol":
|
|
3190
3299
|
case "toBeUnknown":
|
|
3191
|
-
case "toBeVoid":
|
|
3300
|
+
case "toBeVoid":
|
|
3192
3301
|
return this[matcherNameText].match(matchWorker, assertion.source[0]);
|
|
3193
|
-
|
|
3194
|
-
case "toHaveProperty": {
|
|
3302
|
+
case "toHaveProperty":
|
|
3195
3303
|
if (!assertion.target[0]) {
|
|
3196
3304
|
this.#onTargetArgumentMustBeProvided("key", assertion, onDiagnostics);
|
|
3197
3305
|
return;
|
|
3198
3306
|
}
|
|
3199
3307
|
return this.toHaveProperty.match(matchWorker, assertion.source[0], assertion.target[0], onDiagnostics);
|
|
3200
|
-
|
|
3201
|
-
case "toRaiseError": {
|
|
3308
|
+
case "toRaiseError":
|
|
3202
3309
|
return this.toRaiseError.match(matchWorker, assertion.source[0], [...assertion.target], onDiagnostics);
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
|
|
3206
|
-
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
3207
|
-
onDiagnostics(Diagnostic.error(text, origin));
|
|
3208
|
-
}
|
|
3310
|
+
default:
|
|
3311
|
+
this.#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics);
|
|
3209
3312
|
}
|
|
3210
3313
|
return;
|
|
3211
3314
|
}
|
|
3315
|
+
#onMatcherIsNotSupported(matcherNameText, assertion, onDiagnostics) {
|
|
3316
|
+
const text = ExpectDiagnosticText.matcherIsNotSupported(matcherNameText);
|
|
3317
|
+
const origin = DiagnosticOrigin.fromNode(assertion.matcherName);
|
|
3318
|
+
onDiagnostics(Diagnostic.error(text, origin));
|
|
3319
|
+
}
|
|
3212
3320
|
#onSourceArgumentOrTypeArgumentMustBeProvided(assertion, onDiagnostics) {
|
|
3213
3321
|
const text = ExpectDiagnosticText.argumentOrTypeArgumentMustBeProvided("source", "Source");
|
|
3214
3322
|
const origin = DiagnosticOrigin.fromNode(assertion.node.expression);
|
|
@@ -3275,18 +3383,15 @@ class TestTreeWorker {
|
|
|
3275
3383
|
break;
|
|
3276
3384
|
}
|
|
3277
3385
|
switch (member.brand) {
|
|
3278
|
-
case "describe":
|
|
3386
|
+
case "describe":
|
|
3279
3387
|
this.#visitDescribe(member, runMode, parentResult);
|
|
3280
3388
|
break;
|
|
3281
|
-
|
|
3282
|
-
case "test": {
|
|
3389
|
+
case "test":
|
|
3283
3390
|
this.#visitTest(member, runMode, parentResult);
|
|
3284
3391
|
break;
|
|
3285
|
-
|
|
3286
|
-
case "expect": {
|
|
3392
|
+
case "expect":
|
|
3287
3393
|
this.#visitAssertion(member, runMode, parentResult);
|
|
3288
3394
|
break;
|
|
3289
|
-
}
|
|
3290
3395
|
}
|
|
3291
3396
|
}
|
|
3292
3397
|
}
|
|
@@ -3510,7 +3615,7 @@ class TSTyche {
|
|
|
3510
3615
|
#runner;
|
|
3511
3616
|
#selectService;
|
|
3512
3617
|
#storeService;
|
|
3513
|
-
static version = "3.0.0-beta.
|
|
3618
|
+
static version = "3.0.0-beta.2";
|
|
3514
3619
|
constructor(resolvedConfig, outputService, selectService, storeService) {
|
|
3515
3620
|
this.#resolvedConfig = resolvedConfig;
|
|
3516
3621
|
this.#outputService = outputService;
|
|
@@ -4052,7 +4157,7 @@ class Cli {
|
|
|
4052
4157
|
}
|
|
4053
4158
|
const selectService = new SelectService(resolvedConfig);
|
|
4054
4159
|
let testFiles = [];
|
|
4055
|
-
if (resolvedConfig.testFileMatch.length
|
|
4160
|
+
if (resolvedConfig.testFileMatch.length > 0) {
|
|
4056
4161
|
testFiles = await selectService.selectFiles();
|
|
4057
4162
|
if (testFiles.length === 0) {
|
|
4058
4163
|
if (commandLineArguments.includes("--watch")) {
|
|
@@ -4103,4 +4208,4 @@ class Cli {
|
|
|
4103
4208
|
}
|
|
4104
4209
|
}
|
|
4105
4210
|
|
|
4106
|
-
export { Assertion, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, ConfigDiagnosticText, ConfigService, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, OptionBrand, OptionDefinitionsMap, OptionGroup, OutputService, Path, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, RunReporter, Runner, Scribbler, SelectDiagnosticText, SelectService, SetupReporter, StoreService, SummaryReporter, TSTyche, TargetResult, Task, TaskResult, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|
|
4211
|
+
export { Assertion, CancellationHandler, CancellationReason, CancellationToken, Cli, CollectService, Color, ConfigDiagnosticText, ConfigService, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, EventEmitter, ExitCodeHandler, ExpectResult, ExpectService, FileWatcher, InputService, Line, OptionBrand, OptionDefinitionsMap, OptionGroup, OutputService, Path, ProjectResult, ProjectService, Result, ResultCount, ResultHandler, ResultStatus, ResultTiming, RunReporter, Runner, Scribbler, SelectDiagnosticText, SelectService, SetupReporter, SourceFile, StoreService, SummaryReporter, TSTyche, TargetResult, Task, TaskResult, TestMember, TestMemberBrand, TestMemberFlags, TestResult, TestTree, Text, Version, WatchReporter, WatchService, Watcher, addsPackageText, defaultOptions, describeNameText, diagnosticText, environmentOptions, fileViewText, formattedText, helpText, summaryText, taskStatusText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
|