tstyche 6.0.3 → 6.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/dist/tag.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Runs TSTyche in the same process, streaming error messages and test results to the `stderr` and `stdout` in real-time.
3
+ *
4
+ * @returns A promise that resolves if the test run is successful and rejects if it fails.
5
+ */
6
+ declare function tstyche(template: TemplateStringsArray, ...substitutions: Array<string>): Promise<void>;
7
+
8
+ export { tstyche as default };
package/dist/tag.js ADDED
@@ -0,0 +1,36 @@
1
+ import { Cli, EventEmitter, CancellationToken } from './tstyche.js';
2
+
3
+ class StatusHandler {
4
+ #hasError = false;
5
+ hasError() {
6
+ return this.#hasError;
7
+ }
8
+ on([event, payload]) {
9
+ if (event === "run:start") {
10
+ this.#hasError = false;
11
+ return;
12
+ }
13
+ if ("diagnostics" in payload) {
14
+ if (payload.diagnostics.some((diagnostic) => diagnostic.category === "error")) {
15
+ this.#hasError = true;
16
+ }
17
+ }
18
+ }
19
+ }
20
+ async function tstyche(template, ...substitutions) {
21
+ const cli = new Cli({ noErrorExitCode: true });
22
+ const commandLine = String.raw(template, ...substitutions).split(/\s+/);
23
+ const eventEmitter = new EventEmitter();
24
+ const statusHandler = new StatusHandler();
25
+ eventEmitter.addHandler(statusHandler);
26
+ await cli.run(commandLine, new CancellationToken());
27
+ eventEmitter.removeHandler(statusHandler);
28
+ return new Promise((resolve, reject) => {
29
+ if (statusHandler.hasError()) {
30
+ reject(new Error("TSTyche test run failed. Check the output above for details."));
31
+ }
32
+ resolve();
33
+ });
34
+ }
35
+
36
+ export { tstyche as default };
package/dist/tstyche.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type ts from 'typescript';
2
+ import type { WriteStream } from 'node:tty';
2
3
 
3
4
  declare enum CancellationReason {
4
5
  ConfigChange = "configChange",
@@ -15,9 +16,13 @@ declare class CancellationToken {
15
16
  reset(): void;
16
17
  }
17
18
 
19
+ interface CliOptions {
20
+ noErrorExitCode?: boolean;
21
+ }
18
22
  declare class Cli {
19
23
  #private;
20
- run(commandLine: Array<string>, cancellationToken?: CancellationToken): Promise<void>;
24
+ constructor(options?: CliOptions);
25
+ run(commandLine: ReadonlyArray<string>, cancellationToken?: CancellationToken): Promise<void>;
21
26
  }
22
27
 
23
28
  declare enum DiagnosticCategory {
@@ -143,12 +148,15 @@ interface CommandLineOptions {
143
148
  listFiles?: boolean;
144
149
  only?: string;
145
150
  prune?: boolean;
151
+ quiet?: boolean;
146
152
  reporters?: Array<string>;
153
+ root?: string;
147
154
  showConfig?: boolean;
148
155
  skip?: string;
149
156
  target?: Array<string>;
150
157
  tsconfig?: string;
151
158
  update?: boolean;
159
+ verbose?: boolean;
152
160
  version?: boolean;
153
161
  watch?: boolean;
154
162
  }
@@ -157,6 +165,7 @@ interface ConfigFileOptions {
157
165
  checkSuppressedErrors?: boolean;
158
166
  failFast?: boolean;
159
167
  fixtureFileMatch?: Array<string>;
168
+ quiet?: boolean;
160
169
  rejectAnyType?: boolean;
161
170
  rejectNeverType?: boolean;
162
171
  reporters?: Array<string>;
@@ -164,6 +173,7 @@ interface ConfigFileOptions {
164
173
  target?: Array<string>;
165
174
  testFileMatch?: Array<string>;
166
175
  tsconfig?: string;
176
+ verbose?: boolean;
167
177
  }
168
178
 
169
179
  interface InlineConfig {
@@ -191,11 +201,11 @@ interface DirectiveRange {
191
201
 
192
202
  declare class Config {
193
203
  #private;
194
- static parseCommandLine(commandLine: Array<string>): Promise<{
204
+ static parseCommandLine(commandLine: ReadonlyArray<string>): Promise<{
195
205
  commandLineOptions: CommandLineOptions;
196
206
  pathMatch: Array<string>;
197
207
  }>;
198
- static parseConfigFile(filePath?: string): Promise<{
208
+ static parseConfigFile(configPath?: string, rootPath?: string): Promise<{
199
209
  configFileOptions: ConfigFileOptions;
200
210
  configFilePath: string;
201
211
  }>;
@@ -205,7 +215,7 @@ declare class Config {
205
215
  commandLineOptions?: Omit<CommandLineOptions, "config">;
206
216
  pathMatch?: Array<string>;
207
217
  }): ResolvedConfig;
208
- static resolveConfigFilePath(filePath?: string): string;
218
+ static resolveConfigFilePath(configPath?: string, rootPath?: string): string;
209
219
  }
210
220
 
211
221
  declare enum OptionBrand {
@@ -300,6 +310,11 @@ declare abstract class BaseReporter implements Reporter {
300
310
  abstract on([event, payload]: ReporterEvent): void;
301
311
  }
302
312
 
313
+ declare class DotReporter extends BaseReporter {
314
+ #private;
315
+ on([event, payload]: ReporterEvent): void;
316
+ }
317
+
303
318
  declare class ListReporter extends BaseReporter {
304
319
  #private;
305
320
  on([event, payload]: ReporterEvent): void;
@@ -584,7 +599,9 @@ interface TextProps {
584
599
  }
585
600
  declare function Text({ children, color, indent }: TextProps): ScribblerJsx.Element;
586
601
 
587
- declare function addsPackageText(packageVersion: string, packagePath: string): ScribblerJsx.Element;
602
+ declare function addsPackageText(packageVersion: string, packagePath: string, options?: {
603
+ short?: boolean;
604
+ }): ScribblerJsx.Element;
588
605
 
589
606
  declare function describeNameText(name: string, indent?: number): ScribblerJsx.Element;
590
607
 
@@ -596,18 +613,29 @@ interface CodeFrameOptions {
596
613
 
597
614
  declare function diagnosticText(diagnostic: Diagnostic, codeFrameOptions?: CodeFrameOptions): ScribblerJsx.Element;
598
615
 
599
- declare function fileStatusText(status: FileResultStatus, file: FileLocation): ScribblerJsx.Element;
616
+ declare function dotText(status: ResultStatus): ScribblerJsx.Element;
600
617
 
601
- declare function fileViewText(lines: Array<ScribblerJsx.Element>, addEmptyFinalLine: boolean): ScribblerJsx.Element;
618
+ declare function fileStatusText(status: FileResultStatus, file: FileLocation): ScribblerJsx.Element;
602
619
 
603
620
  declare function formattedText(input: string | Array<string> | Record<string, unknown>): ScribblerJsx.Element;
604
621
 
605
622
  declare function helpText(options: Map<string, OptionDefinition>, version: string): ScribblerJsx.Element;
606
623
 
624
+ declare class StreamController {
625
+ #private;
626
+ constructor(stream: WriteStream);
627
+ disable(): void;
628
+ enable(): void;
629
+ write(text: string): void;
630
+ }
631
+
607
632
  declare class OutputService {
608
633
  #private;
634
+ static errorStream: StreamController;
635
+ static outputStream: StreamController;
609
636
  static clearTerminal(): void;
610
637
  static eraseLastLine(): void;
638
+ static writeBlankLine(count?: number): void;
611
639
  static writeError(element: ScribblerJsx.Element | Array<ScribblerJsx.Element>): void;
612
640
  static writeMessage(element: ScribblerJsx.Element | Array<ScribblerJsx.Element>): void;
613
641
  static writeWarning(element: ScribblerJsx.Element | Array<ScribblerJsx.Element>): void;
@@ -628,7 +656,8 @@ declare function summaryText({ targetCounts, fileCounts, testCounts, assertionCo
628
656
  declare function testNameText(status: Exclude<TestResultStatus, ResultStatus.Runs>, name: string, indent?: number): ScribblerJsx.Element;
629
657
 
630
658
  declare function usesCompilerText(compilerVersion: string, projectConfigFilePath: string | undefined, options?: {
631
- prependEmptyLine: boolean;
659
+ prependEmptyLine?: boolean;
660
+ short?: boolean;
632
661
  }): ScribblerJsx.Element;
633
662
 
634
663
  declare function waitingForFileChangesText(): ScribblerJsx.Element;
@@ -714,5 +743,5 @@ declare class Version {
714
743
  static isSatisfiedWith(source: string, target: string): boolean;
715
744
  }
716
745
 
717
- export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectResult, Result, ResultStatus, Runner, Scribbler, ScribblerJsx, Select, SelectDiagnosticText, SetupReporter, Store, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileStatusText, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, prologueText, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
746
+ export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, DotReporter, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectResult, Result, ResultStatus, Runner, Scribbler, ScribblerJsx, Select, SelectDiagnosticText, SetupReporter, Store, StreamController, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, dotText, environmentOptions, fileStatusText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, prologueText, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
718
747
  export type { AssertionCounts, AssertionResultStatus, CodeFrameOptions, CommandLineOptions, ConfigFileOptions, DiagnosticsHandler, DirectiveRange, EnvironmentOptions, Event, EventHandler, FileCounts, FileResultStatus, InlineConfig, ItemDefinition, OptionDefinition, Reporter, ReporterEvent, ResolvedConfig, ResultCounts, ResultTiming, ScribblerOptions, SuppressedCounts, SuppressedResultStatus, TargetCounts, TargetResultStatus, TestCounts, TestResultStatus, TextRange };
package/dist/tstyche.js CHANGED
@@ -375,7 +375,7 @@ class ConfigDiagnosticText {
375
375
  static fileMatchPatternCannotStartWith(optionName, segment) {
376
376
  return [
377
377
  `A '${optionName}' pattern cannot start with '${segment}'.`,
378
- "The files are only collected within the 'rootPath' directory.",
378
+ "The files are only collected within the root directory.",
379
379
  ];
380
380
  }
381
381
  static inspectSupportedVersions() {
@@ -1099,6 +1099,12 @@ class Options {
1099
1099
  group: 2,
1100
1100
  name: "prune",
1101
1101
  },
1102
+ {
1103
+ brand: "boolean",
1104
+ description: "Silence all test runner output except errors and warnings.",
1105
+ group: 2 | 4,
1106
+ name: "quiet",
1107
+ },
1102
1108
  {
1103
1109
  brand: "boolean",
1104
1110
  description: "Reject the 'any' type passed as an argument to the 'expect()' function or a matcher.",
@@ -1121,6 +1127,12 @@ class Options {
1121
1127
  },
1122
1128
  name: "reporters",
1123
1129
  },
1130
+ {
1131
+ brand: "string",
1132
+ description: "The path to a directory containing files of a test project.",
1133
+ group: 2,
1134
+ name: "root",
1135
+ },
1124
1136
  {
1125
1137
  brand: "string",
1126
1138
  description: "The path to a directory containing files of a test project.",
@@ -1167,6 +1179,12 @@ class Options {
1167
1179
  group: 2,
1168
1180
  name: "update",
1169
1181
  },
1182
+ {
1183
+ brand: "boolean",
1184
+ description: "Enable detailed logging.",
1185
+ group: 2 | 4,
1186
+ name: "verbose",
1187
+ },
1170
1188
  {
1171
1189
  brand: "true",
1172
1190
  description: "Print the version number and exit.",
@@ -1193,7 +1211,7 @@ class Options {
1193
1211
  return optionName.startsWith("--") ? optionName.slice(2) : optionName;
1194
1212
  }
1195
1213
  static #isBuiltinReporter(optionValue) {
1196
- return ["list", "summary"].includes(optionValue);
1214
+ return ["dot", "list", "summary"].includes(optionValue);
1197
1215
  }
1198
1216
  static #isLookupStrategy(optionValue) {
1199
1217
  return ["findup", "ignore"].includes(optionValue);
@@ -1202,11 +1220,15 @@ class Options {
1202
1220
  const canonicalOptionName = Options.#getCanonicalOptionName(optionName);
1203
1221
  switch (canonicalOptionName) {
1204
1222
  case "config":
1223
+ case "root":
1205
1224
  case "rootPath":
1206
1225
  case "tsconfig":
1207
1226
  if (canonicalOptionName === "tsconfig" && Options.#isLookupStrategy(optionValue)) {
1208
1227
  break;
1209
1228
  }
1229
+ if (optionValue.startsWith("file:")) {
1230
+ optionValue = fileURLToPath(optionValue);
1231
+ }
1210
1232
  optionValue = Path.resolve(rootPath, optionValue);
1211
1233
  break;
1212
1234
  case "reporters":
@@ -1231,6 +1253,7 @@ class Options {
1231
1253
  const canonicalOptionName = Options.#getCanonicalOptionName(optionName);
1232
1254
  switch (canonicalOptionName) {
1233
1255
  case "config":
1256
+ case "root":
1234
1257
  case "rootPath":
1235
1258
  case "tsconfig":
1236
1259
  if (canonicalOptionName === "tsconfig" && Options.#isLookupStrategy(optionValue)) {
@@ -1553,6 +1576,7 @@ const defaultOptions = {
1553
1576
  checkSuppressedErrors: true,
1554
1577
  failFast: false,
1555
1578
  fixtureFileMatch: ["**/__fixtures__/*.{ts,tsx}", "**/fixtures/*.{ts,tsx}"],
1579
+ quiet: false,
1556
1580
  rejectAnyType: true,
1557
1581
  rejectNeverType: true,
1558
1582
  reporters: ["list", "summary"],
@@ -1560,6 +1584,7 @@ const defaultOptions = {
1560
1584
  target: ["*"],
1561
1585
  testFileMatch: ["**/*.tst.*", "**/__typetests__/*.test.*", "**/typetests/*.test.*"],
1562
1586
  tsconfig: "findup",
1587
+ verbose: false,
1563
1588
  };
1564
1589
 
1565
1590
  class Config {
@@ -1573,10 +1598,10 @@ class Config {
1573
1598
  await commandLineParser.parse(commandLine);
1574
1599
  return { commandLineOptions, pathMatch };
1575
1600
  }
1576
- static async parseConfigFile(filePath) {
1577
- const configFilePath = Config.resolveConfigFilePath(filePath);
1601
+ static async parseConfigFile(configPath, rootPath) {
1602
+ const configFilePath = Config.resolveConfigFilePath(configPath, rootPath);
1578
1603
  const configFileOptions = {
1579
- rootPath: Path.dirname(configFilePath),
1604
+ rootPath: rootPath ?? Path.dirname(configFilePath),
1580
1605
  };
1581
1606
  if (existsSync(configFilePath)) {
1582
1607
  const configFileText = await fs.readFile(configFilePath, {
@@ -1599,10 +1624,16 @@ class Config {
1599
1624
  if ("config" in resolvedConfig) {
1600
1625
  delete resolvedConfig.config;
1601
1626
  }
1627
+ if ("root" in resolvedConfig) {
1628
+ delete resolvedConfig.root;
1629
+ }
1602
1630
  return resolvedConfig;
1603
1631
  }
1604
- static resolveConfigFilePath(filePath) {
1605
- return filePath != null ? Path.resolve(filePath) : Path.resolve("./tstyche.config.json");
1632
+ static resolveConfigFilePath(configPath, rootPath = ".") {
1633
+ if (configPath != null) {
1634
+ return Path.resolve(configPath);
1635
+ }
1636
+ return Path.resolve(rootPath, "./tstyche.config.json");
1606
1637
  }
1607
1638
  }
1608
1639
 
@@ -2198,7 +2229,10 @@ class Scribbler {
2198
2229
  }
2199
2230
  }
2200
2231
 
2201
- function addsPackageText(packageVersion, packagePath) {
2232
+ function addsPackageText(packageVersion, packagePath, options) {
2233
+ if (options?.short) {
2234
+ return (jsx(Line, { children: jsx(Text, { color: "90", children: packageVersion }) }));
2235
+ }
2202
2236
  return (jsx(Line, { children: [jsx(Text, { color: "90", children: "adds" }), " TypeScript ", packageVersion, jsx(Text, { color: "90", children: [" to ", packagePath] })] }));
2203
2237
  }
2204
2238
 
@@ -2295,6 +2329,16 @@ function diagnosticText(diagnostic, codeFrameOptions = {}) {
2295
2329
  return (jsx(Text, { children: [prefix, jsx(DiagnosticText, { codeFrameOptions: codeFrameOptions, diagnostic: diagnostic })] }));
2296
2330
  }
2297
2331
 
2332
+ function dotText(status) {
2333
+ let statusColor;
2334
+ let statusText = "·";
2335
+ if (status === "failed") {
2336
+ statusColor = "31";
2337
+ statusText = "×";
2338
+ }
2339
+ return jsx(Text, { color: statusColor, children: statusText });
2340
+ }
2341
+
2298
2342
  function getStatusColor(status) {
2299
2343
  switch (status) {
2300
2344
  case "runs":
@@ -2342,10 +2386,6 @@ function fileStatusText(status, file) {
2342
2386
  return (jsx(Line, { children: [jsx(Text, { color: getStatusColor(status), children: statusText }), " ", jsx(FileNameText, { filePath: file.path })] }));
2343
2387
  }
2344
2388
 
2345
- function fileViewText(lines, addEmptyFinalLine) {
2346
- return (jsx(Text, { children: [[...lines], addEmptyFinalLine ? jsx(Line, {}) : undefined] }));
2347
- }
2348
-
2349
2389
  function formattedText(input) {
2350
2390
  if (typeof input === "string") {
2351
2391
  return jsx(Line, { children: input });
@@ -2415,35 +2455,56 @@ function helpText(options, version) {
2415
2455
  return (jsx(Text, { children: [jsx(HelpHeaderText, { tstycheVersion: version }), jsx(Line, {}), jsx(CommandLineUsageText, {}), jsx(Line, {}), jsx(CommandLineOptionsText, { optionDefinitions: options }), jsx(Line, {}), jsx(HelpFooterText, {}), jsx(Line, {})] }));
2416
2456
  }
2417
2457
 
2458
+ class StreamController {
2459
+ #isEnabled = true;
2460
+ #stream;
2461
+ constructor(stream) {
2462
+ this.#stream = stream;
2463
+ }
2464
+ disable() {
2465
+ this.#isEnabled = false;
2466
+ }
2467
+ enable() {
2468
+ this.#isEnabled = true;
2469
+ }
2470
+ write(text) {
2471
+ this.#isEnabled && this.#stream.write(text);
2472
+ }
2473
+ }
2474
+
2418
2475
  class OutputService {
2476
+ static errorStream = new StreamController(process.stderr);
2477
+ static outputStream = new StreamController(process.stdout);
2419
2478
  static #isClear = false;
2420
- static #scribbler = new Scribbler();
2421
- static #stderr = process.stderr;
2422
- static #stdout = process.stdout;
2479
+ static #newLine = "\n";
2480
+ static #scribbler = new Scribbler({ newLine: OutputService.#newLine });
2423
2481
  static clearTerminal() {
2424
2482
  if (!OutputService.#isClear) {
2425
- OutputService.#stdout.write("\u001B[2J\u001B[3J\u001B[H");
2483
+ OutputService.outputStream.write("\u001B[2J\u001B[3J\u001B[H");
2426
2484
  OutputService.#isClear = true;
2427
2485
  }
2428
2486
  }
2429
2487
  static eraseLastLine() {
2430
- OutputService.#stdout.write("\u001B[1A\u001B[0K");
2488
+ OutputService.outputStream.write("\u001B[1A\u001B[0K");
2431
2489
  }
2432
- static #writeTo(stream, element) {
2490
+ static #write(stream, element) {
2433
2491
  const elements = Array.isArray(element) ? element : [element];
2434
2492
  for (const element of elements) {
2435
2493
  stream.write(OutputService.#scribbler.render(element));
2436
2494
  }
2437
2495
  OutputService.#isClear = false;
2438
2496
  }
2497
+ static writeBlankLine(count = 1) {
2498
+ OutputService.outputStream.write(OutputService.#newLine.repeat(count));
2499
+ }
2439
2500
  static writeError(element) {
2440
- OutputService.#writeTo(OutputService.#stderr, element);
2501
+ OutputService.#write(OutputService.errorStream, element);
2441
2502
  }
2442
2503
  static writeMessage(element) {
2443
- OutputService.#writeTo(OutputService.#stdout, element);
2504
+ OutputService.#write(OutputService.outputStream, element);
2444
2505
  }
2445
2506
  static writeWarning(element) {
2446
- OutputService.#writeTo(OutputService.#stderr, element);
2507
+ OutputService.#write(OutputService.errorStream, element);
2447
2508
  }
2448
2509
  }
2449
2510
 
@@ -2503,6 +2564,9 @@ function testNameText(status, name, indent = 0) {
2503
2564
  }
2504
2565
 
2505
2566
  function usesCompilerText(compilerVersion, projectConfigFilePath, options) {
2567
+ if (options?.short) {
2568
+ return jsx(Text, { color: "34", children: compilerVersion });
2569
+ }
2506
2570
  let projectConfigPathText;
2507
2571
  if (projectConfigFilePath != null) {
2508
2572
  projectConfigPathText = (jsx(Text, { color: "90", children: [" with ", Path.relative("", projectConfigFilePath)] }));
@@ -2532,6 +2596,55 @@ class BaseReporter {
2532
2596
  }
2533
2597
  }
2534
2598
 
2599
+ class DotReporter extends BaseReporter {
2600
+ #diagnostics = [];
2601
+ #hasReportedAdds = false;
2602
+ on([event, payload]) {
2603
+ switch (event) {
2604
+ case "store:adds":
2605
+ OutputService.writeMessage(addsPackageText(payload.packageVersion, payload.packagePath, { short: true }));
2606
+ this.#hasReportedAdds = true;
2607
+ break;
2608
+ case "store:error":
2609
+ for (const diagnostic of payload.diagnostics) {
2610
+ OutputService.writeError(diagnosticText(diagnostic));
2611
+ }
2612
+ break;
2613
+ case "project:uses":
2614
+ if (this.#hasReportedAdds) {
2615
+ if (!environmentOptions.noInteractive) {
2616
+ OutputService.eraseLastLine();
2617
+ }
2618
+ this.#hasReportedAdds = false;
2619
+ }
2620
+ if (this.resolvedConfig.target.length > 1) {
2621
+ OutputService.writeMessage(usesCompilerText(payload.compilerVersion, payload.projectConfigFilePath, { short: true }));
2622
+ }
2623
+ break;
2624
+ case "target:end":
2625
+ OutputService.writeBlankLine(2);
2626
+ for (const diagnostic of this.#diagnostics) {
2627
+ OutputService.writeError(diagnosticText(diagnostic));
2628
+ }
2629
+ this.#diagnostics = [];
2630
+ break;
2631
+ case "file:end":
2632
+ OutputService.writeMessage(dotText(payload.result.status));
2633
+ break;
2634
+ case "project:error":
2635
+ case "file:error":
2636
+ case "directive:error":
2637
+ case "collect:error":
2638
+ case "test:error":
2639
+ case "suppressed:error":
2640
+ case "expect:error":
2641
+ case "expect:fail":
2642
+ this.#diagnostics.push(...payload.diagnostics);
2643
+ break;
2644
+ }
2645
+ }
2646
+ }
2647
+
2535
2648
  class FileView {
2536
2649
  #indent = 0;
2537
2650
  #lines = [];
@@ -2557,8 +2670,8 @@ class FileView {
2557
2670
  getMessages() {
2558
2671
  return this.#messages;
2559
2672
  }
2560
- getViewText(options) {
2561
- return fileViewText(this.#lines, options?.appendEmptyLine || this.hasErrors());
2673
+ getView() {
2674
+ return this.#lines;
2562
2675
  }
2563
2676
  hasErrors() {
2564
2677
  return this.#messages.length > 0;
@@ -2571,14 +2684,12 @@ class ListReporter extends BaseReporter {
2571
2684
  #hasReportedAdds = false;
2572
2685
  #hasReportedError = false;
2573
2686
  #hasReportedUses = false;
2574
- #isFileViewExpanded = false;
2575
- #isLastFile() {
2576
- return this.#fileCount === 0;
2577
- }
2687
+ #isVerbose = false;
2578
2688
  on([event, payload]) {
2579
2689
  switch (event) {
2580
2690
  case "run:start":
2581
- this.#isFileViewExpanded = payload.result.files.length === 1 && this.resolvedConfig.watch !== true;
2691
+ this.#isVerbose =
2692
+ this.resolvedConfig.verbose || (payload.result.files.length === 1 && this.resolvedConfig.watch !== true);
2582
2693
  break;
2583
2694
  case "store:adds":
2584
2695
  OutputService.writeMessage(addsPackageText(payload.packageVersion, payload.packagePath));
@@ -2625,7 +2736,10 @@ class ListReporter extends BaseReporter {
2625
2736
  OutputService.eraseLastLine();
2626
2737
  }
2627
2738
  OutputService.writeMessage(fileStatusText(payload.result.status, payload.result.file));
2628
- OutputService.writeMessage(this.#fileView.getViewText({ appendEmptyLine: this.#isLastFile() }));
2739
+ OutputService.writeMessage(this.#fileView.getView());
2740
+ if (this.#isVerbose || this.#fileCount === 0 || this.#fileView.hasErrors()) {
2741
+ OutputService.writeBlankLine();
2742
+ }
2629
2743
  if (this.#fileView.hasErrors()) {
2630
2744
  OutputService.writeError(this.#fileView.getMessages());
2631
2745
  this.#hasReportedError = true;
@@ -2633,32 +2747,32 @@ class ListReporter extends BaseReporter {
2633
2747
  this.#fileView.clear();
2634
2748
  break;
2635
2749
  case "describe:start":
2636
- if (this.#isFileViewExpanded) {
2750
+ if (this.#isVerbose) {
2637
2751
  this.#fileView.beginDescribe(payload.result.describe.name);
2638
2752
  }
2639
2753
  break;
2640
2754
  case "describe:end":
2641
- if (this.#isFileViewExpanded) {
2755
+ if (this.#isVerbose) {
2642
2756
  this.#fileView.endDescribe();
2643
2757
  }
2644
2758
  break;
2645
2759
  case "test:skip":
2646
- if (this.#isFileViewExpanded) {
2760
+ if (this.#isVerbose) {
2647
2761
  this.#fileView.addTest("skipped", payload.result.test.name);
2648
2762
  }
2649
2763
  break;
2650
2764
  case "test:fixme":
2651
- if (this.#isFileViewExpanded) {
2765
+ if (this.#isVerbose) {
2652
2766
  this.#fileView.addTest("fixme", payload.result.test.name);
2653
2767
  }
2654
2768
  break;
2655
2769
  case "test:todo":
2656
- if (this.#isFileViewExpanded) {
2770
+ if (this.#isVerbose) {
2657
2771
  this.#fileView.addTest("todo", payload.result.test.name);
2658
2772
  }
2659
2773
  break;
2660
2774
  case "test:error":
2661
- if (this.#isFileViewExpanded) {
2775
+ if (this.#isVerbose) {
2662
2776
  this.#fileView.addTest("failed", payload.result.test.name);
2663
2777
  }
2664
2778
  for (const diagnostic of payload.diagnostics) {
@@ -2666,12 +2780,12 @@ class ListReporter extends BaseReporter {
2666
2780
  }
2667
2781
  break;
2668
2782
  case "test:fail":
2669
- if (this.#isFileViewExpanded) {
2783
+ if (this.#isVerbose) {
2670
2784
  this.#fileView.addTest("failed", payload.result.test.name);
2671
2785
  }
2672
2786
  break;
2673
2787
  case "test:pass":
2674
- if (this.#isFileViewExpanded) {
2788
+ if (this.#isVerbose) {
2675
2789
  this.#fileView.addTest("passed", payload.result.test.name);
2676
2790
  }
2677
2791
  break;
@@ -2954,7 +3068,7 @@ class Glob {
2954
3068
  class SelectDiagnosticText {
2955
3069
  static #pathSelectOptions(resolvedConfig) {
2956
3070
  const text = [
2957
- `Root path: ${resolvedConfig.rootPath}`,
3071
+ `Root directory: ${resolvedConfig.rootPath}`,
2958
3072
  `Test file match: ${resolvedConfig.testFileMatch.join(", ")}`,
2959
3073
  ];
2960
3074
  if (resolvedConfig.pathMatch.length > 0) {
@@ -5914,7 +6028,7 @@ class FileRunner {
5914
6028
  class Runner {
5915
6029
  #eventEmitter = new EventEmitter();
5916
6030
  #resolvedConfig;
5917
- static version = "6.0.3";
6031
+ static version = "6.2.0";
5918
6032
  constructor(resolvedConfig) {
5919
6033
  this.#resolvedConfig = resolvedConfig;
5920
6034
  }
@@ -5933,14 +6047,16 @@ class Runner {
5933
6047
  }
5934
6048
  for (const reporter of this.#resolvedConfig.reporters) {
5935
6049
  switch (reporter) {
6050
+ case "dot": {
6051
+ this.#eventEmitter.addReporter(new DotReporter(this.#resolvedConfig));
6052
+ break;
6053
+ }
5936
6054
  case "list": {
5937
- const listReporter = new ListReporter(this.#resolvedConfig);
5938
- this.#eventEmitter.addReporter(listReporter);
6055
+ this.#eventEmitter.addReporter(new ListReporter(this.#resolvedConfig));
5939
6056
  break;
5940
6057
  }
5941
6058
  case "summary": {
5942
- const summaryReporter = new SummaryReporter(this.#resolvedConfig);
5943
- this.#eventEmitter.addReporter(summaryReporter);
6059
+ this.#eventEmitter.addReporter(new SummaryReporter(this.#resolvedConfig));
5944
6060
  break;
5945
6061
  }
5946
6062
  default: {
@@ -5952,6 +6068,9 @@ class Runner {
5952
6068
  }
5953
6069
  }
5954
6070
  async run(files, cancellationToken = new CancellationToken()) {
6071
+ if (this.#resolvedConfig.quiet) {
6072
+ OutputService.outputStream.disable();
6073
+ }
5955
6074
  if (!this.#resolvedConfig.watch) {
5956
6075
  OutputService.writeMessage(prologueText(Runner.version, this.#resolvedConfig.rootPath));
5957
6076
  }
@@ -5964,6 +6083,9 @@ class Runner {
5964
6083
  }
5965
6084
  this.#eventEmitter.removeReporters();
5966
6085
  this.#eventEmitter.removeHandlers();
6086
+ if (this.#resolvedConfig.quiet) {
6087
+ OutputService.outputStream.enable();
6088
+ }
5967
6089
  }
5968
6090
  async #run(files, cancellationToken) {
5969
6091
  const result = new Result(files);
@@ -5995,11 +6117,15 @@ class Runner {
5995
6117
 
5996
6118
  class Cli {
5997
6119
  #eventEmitter = new EventEmitter();
6120
+ #noErrorExitCode;
6121
+ constructor(options) {
6122
+ this.#noErrorExitCode = options?.noErrorExitCode ?? false;
6123
+ }
5998
6124
  async run(commandLine, cancellationToken = new CancellationToken()) {
5999
6125
  const cancellationHandler = new CancellationHandler(cancellationToken, "configError");
6000
6126
  this.#eventEmitter.addHandler(cancellationHandler);
6001
6127
  const exitCodeHandler = new ExitCodeHandler();
6002
- this.#eventEmitter.addHandler(exitCodeHandler);
6128
+ !this.#noErrorExitCode && this.#eventEmitter.addHandler(exitCodeHandler);
6003
6129
  const setupReporter = new SetupReporter();
6004
6130
  this.#eventEmitter.addReporter(setupReporter);
6005
6131
  if (commandLine.includes("--help")) {
@@ -6038,7 +6164,7 @@ class Cli {
6038
6164
  this.#eventEmitter.addHandler(cancellationHandler);
6039
6165
  this.#eventEmitter.addReporter(setupReporter);
6040
6166
  }
6041
- const { configFileOptions, configFilePath } = await Config.parseConfigFile(commandLineOptions.config);
6167
+ const { configFileOptions, configFilePath } = await Config.parseConfigFile(commandLineOptions.config, commandLineOptions.root);
6042
6168
  const resolvedConfig = Config.resolve({
6043
6169
  configFileOptions,
6044
6170
  configFilePath,
@@ -6110,4 +6236,4 @@ class Cli {
6110
6236
  }
6111
6237
  }
6112
6238
 
6113
- export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectResult, Result, ResultStatus, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, Store, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, environmentOptions, fileStatusText, fileViewText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, prologueText, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
6239
+ export { BaseReporter, CancellationReason, CancellationToken, Cli, Color, Config, ConfigDiagnosticText, DescribeResult, Diagnostic, DiagnosticCategory, DiagnosticOrigin, Directive, DotReporter, EventEmitter, ExpectResult, FileLocation, FileResult, Line, ListReporter, OptionBrand, OptionGroup, Options, OutputService, Path, ProjectResult, Result, ResultStatus, Runner, Scribbler, Select, SelectDiagnosticText, SetupReporter, Store, StreamController, SummaryReporter, SuppressedResult, TargetResult, TestResult, Text, Version, WatchReporter, addsPackageText, defaultOptions, describeNameText, diagnosticBelongsToNode, diagnosticText, dotText, environmentOptions, fileStatusText, formattedText, getDiagnosticMessageText, getTextSpanEnd, helpText, isDiagnosticWithLocation, prologueText, summaryText, testNameText, usesCompilerText, waitingForFileChangesText, watchUsageText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tstyche",
3
- "version": "6.0.3",
3
+ "version": "6.2.0",
4
4
  "description": "Everything You Need for Type Testing.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -25,6 +25,7 @@
25
25
  "import": "./dist/index.js",
26
26
  "require": "./dist/index.cjs"
27
27
  },
28
+ "./tag": "./dist/tag.js",
28
29
  "./tstyche": "./dist/tstyche.js",
29
30
  "./package.json": "./package.json"
30
31
  },