xunit.ts 1.4.0 → 3.0.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.
Files changed (214) hide show
  1. package/README.md +44 -19
  2. package/cli.ts +9 -4
  3. package/dist/cli.js +4 -4
  4. package/dist/cli.js.map +1 -1
  5. package/dist/eslint.config.d.ts +3 -0
  6. package/dist/eslint.config.d.ts.map +1 -0
  7. package/dist/eslint.config.js +46 -0
  8. package/dist/eslint.config.js.map +1 -0
  9. package/dist/src/Assertions/Contains.d.ts +1 -1
  10. package/dist/src/Assertions/Contains.d.ts.map +1 -1
  11. package/dist/src/Assertions/Contains.js +4 -4
  12. package/dist/src/Assertions/Contains.js.map +1 -1
  13. package/dist/src/Assertions/Count.d.ts +1 -1
  14. package/dist/src/Assertions/Count.d.ts.map +1 -1
  15. package/dist/src/Assertions/Count.js +4 -4
  16. package/dist/src/Assertions/Count.js.map +1 -1
  17. package/dist/src/Assertions/Defined.d.ts +1 -1
  18. package/dist/src/Assertions/Defined.d.ts.map +1 -1
  19. package/dist/src/Assertions/Defined.js +4 -4
  20. package/dist/src/Assertions/Defined.js.map +1 -1
  21. package/dist/src/Assertions/DoesNotContain.d.ts +1 -1
  22. package/dist/src/Assertions/DoesNotContain.d.ts.map +1 -1
  23. package/dist/src/Assertions/DoesNotContain.js +4 -4
  24. package/dist/src/Assertions/DoesNotContain.js.map +1 -1
  25. package/dist/src/Assertions/DoesNotThrow.d.ts +1 -1
  26. package/dist/src/Assertions/DoesNotThrow.d.ts.map +1 -1
  27. package/dist/src/Assertions/DoesNotThrow.js +4 -4
  28. package/dist/src/Assertions/DoesNotThrow.js.map +1 -1
  29. package/dist/src/Assertions/Empty.d.ts +1 -1
  30. package/dist/src/Assertions/Empty.d.ts.map +1 -1
  31. package/dist/src/Assertions/Empty.js +4 -4
  32. package/dist/src/Assertions/Empty.js.map +1 -1
  33. package/dist/src/Assertions/Equal.d.ts +1 -1
  34. package/dist/src/Assertions/Equal.d.ts.map +1 -1
  35. package/dist/src/Assertions/Equal.js +4 -4
  36. package/dist/src/Assertions/Equal.js.map +1 -1
  37. package/dist/src/Assertions/False.d.ts +1 -1
  38. package/dist/src/Assertions/False.d.ts.map +1 -1
  39. package/dist/src/Assertions/False.js +4 -4
  40. package/dist/src/Assertions/False.js.map +1 -1
  41. package/dist/src/Assertions/InstanceOf.d.ts +1 -1
  42. package/dist/src/Assertions/InstanceOf.d.ts.map +1 -1
  43. package/dist/src/Assertions/InstanceOf.js +4 -4
  44. package/dist/src/Assertions/InstanceOf.js.map +1 -1
  45. package/dist/src/Assertions/NotEmpty.d.ts +1 -1
  46. package/dist/src/Assertions/NotEmpty.d.ts.map +1 -1
  47. package/dist/src/Assertions/NotEmpty.js +4 -4
  48. package/dist/src/Assertions/NotEmpty.js.map +1 -1
  49. package/dist/src/Assertions/NotEqual.d.ts +1 -1
  50. package/dist/src/Assertions/NotEqual.d.ts.map +1 -1
  51. package/dist/src/Assertions/NotEqual.js +4 -4
  52. package/dist/src/Assertions/NotEqual.js.map +1 -1
  53. package/dist/src/Assertions/NotNull.d.ts +1 -1
  54. package/dist/src/Assertions/NotNull.d.ts.map +1 -1
  55. package/dist/src/Assertions/NotNull.js +4 -4
  56. package/dist/src/Assertions/NotNull.js.map +1 -1
  57. package/dist/src/Assertions/Null.d.ts +1 -1
  58. package/dist/src/Assertions/Null.d.ts.map +1 -1
  59. package/dist/src/Assertions/Null.js +4 -4
  60. package/dist/src/Assertions/Null.js.map +1 -1
  61. package/dist/src/Assertions/StringContains.js +5 -5
  62. package/dist/src/Assertions/StringContains.js.map +1 -1
  63. package/dist/src/Assertions/StringDoesNotContain.js +5 -5
  64. package/dist/src/Assertions/StringDoesNotContain.js.map +1 -1
  65. package/dist/src/Assertions/StringDoesNotEndWith.d.ts.map +1 -1
  66. package/dist/src/Assertions/StringDoesNotEndWith.js +5 -7
  67. package/dist/src/Assertions/StringDoesNotEndWith.js.map +1 -1
  68. package/dist/src/Assertions/StringDoesNotMatch.d.ts +17 -0
  69. package/dist/src/Assertions/StringDoesNotMatch.d.ts.map +1 -0
  70. package/dist/src/Assertions/StringDoesNotMatch.js +30 -0
  71. package/dist/src/Assertions/StringDoesNotMatch.js.map +1 -0
  72. package/dist/src/Assertions/StringDoesNotStartWith.js +5 -5
  73. package/dist/src/Assertions/StringDoesNotStartWith.js.map +1 -1
  74. package/dist/src/Assertions/StringEndsWith.d.ts.map +1 -1
  75. package/dist/src/Assertions/StringEndsWith.js +5 -7
  76. package/dist/src/Assertions/StringEndsWith.js.map +1 -1
  77. package/dist/src/Assertions/StringMatches.d.ts +17 -0
  78. package/dist/src/Assertions/StringMatches.d.ts.map +1 -0
  79. package/dist/src/Assertions/StringMatches.js +30 -0
  80. package/dist/src/Assertions/StringMatches.js.map +1 -0
  81. package/dist/src/Assertions/StringStartsWith.js +5 -5
  82. package/dist/src/Assertions/StringStartsWith.js.map +1 -1
  83. package/dist/src/Assertions/Throws.d.ts +1 -1
  84. package/dist/src/Assertions/Throws.d.ts.map +1 -1
  85. package/dist/src/Assertions/Throws.js +5 -5
  86. package/dist/src/Assertions/Throws.js.map +1 -1
  87. package/dist/src/Assertions/True.d.ts +1 -1
  88. package/dist/src/Assertions/True.d.ts.map +1 -1
  89. package/dist/src/Assertions/True.js +4 -4
  90. package/dist/src/Assertions/True.js.map +1 -1
  91. package/dist/src/Assertions/Undefined.d.ts +1 -1
  92. package/dist/src/Assertions/Undefined.d.ts.map +1 -1
  93. package/dist/src/Assertions/Undefined.js +4 -4
  94. package/dist/src/Assertions/Undefined.js.map +1 -1
  95. package/dist/src/Assertions/index.d.ts +18 -14
  96. package/dist/src/Assertions/index.d.ts.map +1 -1
  97. package/dist/src/Assertions/index.js +18 -14
  98. package/dist/src/Assertions/index.js.map +1 -1
  99. package/dist/src/CLI.d.ts +1 -3
  100. package/dist/src/CLI.d.ts.map +1 -1
  101. package/dist/src/CLI.js +9 -7
  102. package/dist/src/CLI.js.map +1 -1
  103. package/dist/src/Factory.d.ts +1 -1
  104. package/dist/src/Factory.d.ts.map +1 -1
  105. package/dist/src/Factory.js +7 -7
  106. package/dist/src/Factory.js.map +1 -1
  107. package/dist/src/Framework/ResultType.js +1 -1
  108. package/dist/src/Framework/ResultType.js.map +1 -1
  109. package/dist/src/Framework/Test.d.ts +1 -1
  110. package/dist/src/Framework/Test.d.ts.map +1 -1
  111. package/dist/src/Framework/Test.js +2 -2
  112. package/dist/src/Framework/Test.js.map +1 -1
  113. package/dist/src/Framework/TestInfo.d.ts +5 -1
  114. package/dist/src/Framework/TestInfo.d.ts.map +1 -1
  115. package/dist/src/Framework/TestName.js +2 -2
  116. package/dist/src/Framework/TestName.js.map +1 -1
  117. package/dist/src/Framework/TestSuite.d.ts +4 -2
  118. package/dist/src/Framework/TestSuite.d.ts.map +1 -1
  119. package/dist/src/Framework/TestSuite.js +8 -5
  120. package/dist/src/Framework/TestSuite.js.map +1 -1
  121. package/dist/src/IO/FileSystem.d.ts +2 -2
  122. package/dist/src/IO/FileSystem.d.ts.map +1 -1
  123. package/dist/src/IO/FileSystem.js +6 -5
  124. package/dist/src/IO/FileSystem.js.map +1 -1
  125. package/dist/src/IO/Output.d.ts +1 -2
  126. package/dist/src/IO/Output.d.ts.map +1 -1
  127. package/dist/src/IO/Output.js.map +1 -1
  128. package/dist/src/Reporters/ConsoleReporter.d.ts +4 -5
  129. package/dist/src/Reporters/ConsoleReporter.d.ts.map +1 -1
  130. package/dist/src/Reporters/ConsoleReporter.js +39 -28
  131. package/dist/src/Reporters/ConsoleReporter.js.map +1 -1
  132. package/dist/src/Reporters/FileReporter.d.ts +4 -5
  133. package/dist/src/Reporters/FileReporter.d.ts.map +1 -1
  134. package/dist/src/Reporters/FileReporter.js +0 -1
  135. package/dist/src/Reporters/FileReporter.js.map +1 -1
  136. package/dist/src/Reporters/JUnitReporter.d.ts.map +1 -1
  137. package/dist/src/Reporters/JUnitReporter.js +1 -1
  138. package/dist/src/Reporters/JUnitReporter.js.map +1 -1
  139. package/dist/src/Reporters/ResultReporter.d.ts +2 -3
  140. package/dist/src/Reporters/ResultReporter.d.ts.map +1 -1
  141. package/dist/src/Reporters/ResultReporter.js +1 -0
  142. package/dist/src/Reporters/ResultReporter.js.map +1 -1
  143. package/dist/src/Reporters/SonarReporter.d.ts.map +1 -1
  144. package/dist/src/Reporters/SonarReporter.js +2 -2
  145. package/dist/src/Reporters/SonarReporter.js.map +1 -1
  146. package/dist/src/Reporters/XMLReporter.d.ts +1 -1
  147. package/dist/src/Reporters/XMLReporter.d.ts.map +1 -1
  148. package/dist/src/Reporters/XMLReporter.js.map +1 -1
  149. package/dist/src/Runners/Runner.d.ts +3 -3
  150. package/dist/src/Runners/Runner.d.ts.map +1 -1
  151. package/dist/src/Runners/Runner.js +1 -1
  152. package/dist/src/Runners/Runner.js.map +1 -1
  153. package/dist/src/Runners/TestRunner.d.ts +4 -2
  154. package/dist/src/Runners/TestRunner.d.ts.map +1 -1
  155. package/dist/src/Runners/TestRunner.js +13 -8
  156. package/dist/src/Runners/TestRunner.js.map +1 -1
  157. package/dist/src/Runners/TestSuiteLoader.d.ts.map +1 -1
  158. package/dist/src/Runners/TestSuiteLoader.js +32 -18
  159. package/dist/src/Runners/TestSuiteLoader.js.map +1 -1
  160. package/dist/src/Runners/TestSuiteRunner.d.ts +4 -4
  161. package/dist/src/Runners/TestSuiteRunner.d.ts.map +1 -1
  162. package/dist/src/Runners/TestSuiteRunner.js +3 -3
  163. package/dist/src/Runners/TestSuiteRunner.js.map +1 -1
  164. package/dist/xunit.d.ts +2 -2
  165. package/dist/xunit.d.ts.map +1 -1
  166. package/dist/xunit.js +3 -3
  167. package/dist/xunit.js.map +1 -1
  168. package/eslint.config.ts +44 -0
  169. package/package.json +18 -14
  170. package/src/Assertions/Contains.ts +3 -3
  171. package/src/Assertions/Count.ts +3 -3
  172. package/src/Assertions/Defined.ts +3 -3
  173. package/src/Assertions/DoesNotContain.ts +3 -3
  174. package/src/Assertions/DoesNotThrow.ts +3 -3
  175. package/src/Assertions/Empty.ts +3 -3
  176. package/src/Assertions/Equal.ts +4 -3
  177. package/src/Assertions/False.ts +3 -3
  178. package/src/Assertions/InstanceOf.ts +3 -3
  179. package/src/Assertions/NotEmpty.ts +3 -3
  180. package/src/Assertions/NotEqual.ts +4 -3
  181. package/src/Assertions/NotNull.ts +3 -3
  182. package/src/Assertions/Null.ts +3 -3
  183. package/src/Assertions/StringContains.ts +3 -3
  184. package/src/Assertions/StringDoesNotContain.ts +3 -3
  185. package/src/Assertions/StringDoesNotEndWith.ts +3 -5
  186. package/src/Assertions/StringDoesNotMatch.ts +28 -0
  187. package/src/Assertions/StringDoesNotStartWith.ts +3 -3
  188. package/src/Assertions/StringEndsWith.ts +3 -5
  189. package/src/Assertions/StringMatches.ts +28 -0
  190. package/src/Assertions/StringStartsWith.ts +3 -3
  191. package/src/Assertions/Throws.ts +4 -4
  192. package/src/Assertions/True.ts +3 -3
  193. package/src/Assertions/Undefined.ts +3 -3
  194. package/src/Assertions/index.ts +19 -25
  195. package/src/CLI.ts +11 -7
  196. package/src/Factory.ts +11 -9
  197. package/src/Framework/Test.ts +2 -2
  198. package/src/Framework/TestInfo.ts +7 -1
  199. package/src/Framework/TestName.ts +2 -2
  200. package/src/Framework/TestSuite.ts +12 -7
  201. package/src/IO/FileSystem.ts +7 -5
  202. package/src/IO/Output.ts +1 -1
  203. package/src/Reporters/ConsoleReporter.ts +14 -12
  204. package/src/Reporters/FileReporter.ts +7 -5
  205. package/src/Reporters/JUnitReporter.ts +3 -2
  206. package/src/Reporters/ResultReporter.ts +4 -3
  207. package/src/Reporters/SonarReporter.ts +4 -2
  208. package/src/Reporters/XMLReporter.ts +2 -2
  209. package/src/Runners/Runner.ts +5 -5
  210. package/src/Runners/TestRunner.ts +19 -11
  211. package/src/Runners/TestSuiteLoader.ts +14 -7
  212. package/src/Runners/TestSuiteRunner.ts +7 -7
  213. package/tsconfig.json +3 -0
  214. package/xunit.ts +3 -4
@@ -1,4 +1,4 @@
1
- import { AssertionError } from "assert";
1
+ import { AssertionError } from "node:assert";
2
2
 
3
3
  /**
4
4
  * Asserts that a string begins with a given substring
@@ -16,12 +16,12 @@ import { AssertionError } from "assert";
16
16
  * this.assert.stringStartsWith(needle, haystack);
17
17
  */
18
18
  export default function StringStartsWith(needle: string, haystack: string | null, message?: string) {
19
- if (haystack !== undefined && haystack !== null && haystack.indexOf(needle) === 0) {
19
+ if (haystack?.startsWith(needle)) {
20
20
  return;
21
21
  }
22
22
 
23
23
  throw new AssertionError({
24
- message: message || "Expected string containing expression, but string did not contain expression",
24
+ message: message ?? "Expected string containing expression, but string did not contain expression",
25
25
  expected: needle,
26
26
  actual: haystack
27
27
  });
@@ -1,4 +1,4 @@
1
- import { AssertionError } from "assert";
1
+ import { AssertionError } from "node:assert";
2
2
 
3
3
  /**
4
4
  * Asserts that an expression throws an error/exception
@@ -14,14 +14,14 @@ import { AssertionError } from "assert";
14
14
  * @example
15
15
  * this.assert.throws(() => expression);
16
16
  */
17
- export default function Throws(expression: () => any, message?: string) {
17
+ export default function Throws(expression: () => unknown, message?: string) {
18
18
  try {
19
19
  expression();
20
- } catch (exception) {
20
+ } catch {
21
21
  return;
22
22
  }
23
23
 
24
24
  throw new AssertionError({
25
- message: message || "Expected expression to throw exception, but expression did not throw exception"
25
+ message: message ?? "Expected expression to throw exception, but expression did not throw exception"
26
26
  });
27
27
  }
@@ -1,4 +1,4 @@
1
- import { AssertionError } from "assert";
1
+ import { AssertionError } from "node:assert";
2
2
 
3
3
  /**
4
4
  * Asserts that a given expression evaluates to `true`
@@ -14,13 +14,13 @@ import { AssertionError } from "assert";
14
14
  * @example
15
15
  * this.assert.true(expression);
16
16
  */
17
- export default function True(expression: any, message?: string) {
17
+ export default function True(expression: unknown, message?: string) {
18
18
  if (expression === true) {
19
19
  return;
20
20
  }
21
21
 
22
22
  throw new AssertionError({
23
- message: message || "Expected expression to be true, but expression is not true",
23
+ message: message ?? "Expected expression to be true, but expression is not true",
24
24
  expected: true,
25
25
  actual: expression
26
26
  });
@@ -1,4 +1,4 @@
1
- import { AssertionError } from "assert";
1
+ import { AssertionError } from "node:assert";
2
2
 
3
3
  /**
4
4
  * Asserts that a given value is equal to `undefined`
@@ -14,13 +14,13 @@ import { AssertionError } from "assert";
14
14
  * @example
15
15
  * this.assert.undefined(expression);
16
16
  */
17
- export default function Undefined(expression: any, message?: string) {
17
+ export default function Undefined(expression: unknown, message?: string) {
18
18
  if (expression === undefined) {
19
19
  return;
20
20
  }
21
21
 
22
22
  throw new AssertionError({
23
- message: message || "Expected expression to be undefined, but expression is not undefined",
23
+ message: message ?? "Expected expression to be undefined, but expression is not undefined",
24
24
  expected: undefined,
25
25
  actual: expression
26
26
  });
@@ -1,36 +1,27 @@
1
- import is_true from "./True";
2
- import is_false from "./False";
3
-
4
- import is_undefined from "./Undefined";
1
+ import contains from "./Contains";
2
+ import count from "./Count";
5
3
  import is_defined from "./Defined";
6
-
7
- import is_null from "./Null";
8
- import not_null from "./NotNull";
9
-
10
- import equal from "./Equal";
11
- import not_equal from "./NotEqual";
12
-
4
+ import not_contains from "./DoesNotContain";
5
+ import does_not_throw from "./DoesNotThrow";
13
6
  import empty from "./Empty";
7
+ import equal from "./Equal";
8
+ import is_false from "./False";
9
+ import instance_of from "./InstanceOf";
14
10
  import not_empty from "./NotEmpty";
15
-
16
- import count from "./Count";
17
-
18
- import contains from "./Contains";
19
- import not_contains from "./DoesNotContain";
20
-
11
+ import not_equal from "./NotEqual";
12
+ import not_null from "./NotNull";
13
+ import is_null from "./Null";
21
14
  import string_contains from "./StringContains";
22
15
  import string_not_contains from "./StringDoesNotContain";
23
-
24
- import string_starts from "./StringStartsWith";
16
+ import string_not_ends from "./StringDoesNotEndWith";
17
+ import string_not_matches from "./StringDoesNotMatch";
25
18
  import string_not_starts from "./StringDoesNotStartWith";
26
-
27
19
  import string_ends from "./StringEndsWith";
28
- import string_not_ends from "./StringDoesNotEndWith";
29
-
30
- import instance_of from "./InstanceOf";
31
-
20
+ import string_matches from "./StringMatches";
21
+ import string_starts from "./StringStartsWith";
32
22
  import throws from "./Throws";
33
- import does_not_throw from "./DoesNotThrow";
23
+ import is_true from "./True";
24
+ import is_undefined from "./Undefined";
34
25
 
35
26
  export default new class AssertionLibrary {
36
27
  true = is_true;
@@ -62,6 +53,9 @@ export default new class AssertionLibrary {
62
53
  stringEndsWith = string_ends;
63
54
  stringDoesNotEndWith = string_not_ends;
64
55
 
56
+ stringMatches = string_matches;
57
+ stringDoesNotMatch = string_not_matches;
58
+
65
59
  instanceOf = instance_of;
66
60
 
67
61
  throws = throws;
package/src/CLI.ts CHANGED
@@ -1,10 +1,12 @@
1
- import Usage from "command-line-usage";
2
- import JUnitReporter from "./Reporters/JUnitReporter";
3
- import Process from "process";
1
+ import Process from "node:process";
2
+
4
3
  import Args from "command-line-args";
4
+ import Usage from "command-line-usage";
5
5
  import SafeRegex from "lodash.escaperegexp";
6
- import Runner from "./Runners/Runner";
6
+
7
+ import JUnitReporter from "./Reporters/JUnitReporter";
7
8
  import SonarReporter from "./Reporters/SonarReporter";
9
+ import Runner from "./Runners/Runner";
8
10
 
9
11
  export default class CLI {
10
12
  private static readonly options: Usage.OptionDefinition[] = [
@@ -97,13 +99,15 @@ export default class CLI {
97
99
  const runner = this.runnerFactory(args);
98
100
 
99
101
  try {
100
- const filters = args.filter ?? [];
101
- const results = await runner.runAll(args.dir, filters.map(SafeRegex));
102
+ const argFilter = args.filter as string[] | undefined;
103
+ const filters: string[] = argFilter ?? [];
104
+ const regexFilters = filters.map((f: string) => new RegExp(SafeRegex(f)));
105
+ const results = await runner.runAll(args.dir as string, regexFilters);
102
106
  return Runner.allTestsPassed(results);
103
107
  } catch (error) {
104
108
  if (error instanceof Error) {
105
109
  this.process.stderr.write(`An unhandled ${error.name} occurred: ${error.message}\n`);
106
- this.process.stderr.write(error.stack?.toString() || "(no call stack)\n");
110
+ this.process.stderr.write(error.stack?.toString() ?? "(no call stack)\n");
107
111
  }
108
112
  return false;
109
113
  }
package/src/Factory.ts CHANGED
@@ -1,15 +1,17 @@
1
+ import fs from "node:fs/promises";
2
+
1
3
  import Args from "command-line-args";
2
- import ConsoleReporter from "./Reporters/ConsoleReporter";
4
+
3
5
  import FileSystem from "./IO/FileSystem";
4
- import JUnitReporter from "./Reporters/JUnitReporter";
5
6
  import Output from "./IO/Output";
7
+ import ConsoleReporter from "./Reporters/ConsoleReporter";
8
+ import JUnitReporter from "./Reporters/JUnitReporter";
6
9
  import ResultReporter from "./Reporters/ResultReporter";
10
+ import SonarReporter from "./Reporters/SonarReporter";
7
11
  import Runner from "./Runners/Runner";
8
12
  import TestRunner from "./Runners/TestRunner";
9
13
  import TestSuiteLoader from "./Runners/TestSuiteLoader";
10
14
  import TestSuiteRunner from "./Runners/TestSuiteRunner";
11
- import fs from "fs/promises";
12
- import SonarReporter from "./Reporters/SonarReporter";
13
15
 
14
16
  export default class Factory {
15
17
  static readonly file_system = new FileSystem(fs);
@@ -23,11 +25,11 @@ export default class Factory {
23
25
  return new Runner(loader, test_suite_runner, reporters);
24
26
  }
25
27
 
26
- static Reporters(args: Args.CommandLineOptions): ReadonlyArray<ResultReporter> {
28
+ static Reporters(args: Args.CommandLineOptions): readonly ResultReporter[] {
27
29
  return [
28
- !args.quiet ? new ConsoleReporter(new Output(process.stdout)) : null,
29
- args.junit !== undefined ? new JUnitReporter(Factory.file_system, args.junit ?? JUnitReporter.defaultFileName) : null,
30
- args.sonar !== undefined ? new SonarReporter(Factory.file_system, args.sonar ?? SonarReporter.defaultFileName) : null
31
- ].filter(r => r !== null) as ResultReporter[];
30
+ args.quiet ? null : new ConsoleReporter(new Output(process.stdout)),
31
+ args.junit === undefined ? null : new JUnitReporter(Factory.file_system, args.junit as string | undefined ?? JUnitReporter.defaultFileName),
32
+ args.sonar === undefined ? null : new SonarReporter(Factory.file_system, args.sonar as string | undefined ?? SonarReporter.defaultFileName)
33
+ ].filter(r => r !== null);
32
34
  }
33
35
  }
@@ -1,6 +1,6 @@
1
- import TestSuite from "./TestSuite";
2
1
  import TestInfo from "./TestInfo";
3
2
  import TestName from "./TestName";
3
+ import TestSuite from "./TestSuite";
4
4
 
5
5
  /**
6
6
  * Annotates a test method within a {@link TestSuite}
@@ -18,6 +18,6 @@ import TestName from "./TestName";
18
18
  */
19
19
  export default function Test(test_name?: string) {
20
20
  return function (suite: TestSuite, method_name: string, info: TestInfo) {
21
- suite.addTest(test_name || TestName.toSentenceCase(method_name), info);
21
+ suite.addTest(test_name ?? TestName.toSentenceCase(method_name), info);
22
22
  };
23
23
  }
@@ -1,2 +1,8 @@
1
- type TestInfo = TypedPropertyDescriptor<() => Promise<void>>;
1
+ type AsyncTest = () => Promise<void>;
2
+ type SyncTest = () => void;
3
+
4
+ export type AsyncTestInfo = TypedPropertyDescriptor<AsyncTest>;
5
+ export type SyncTestInfo = TypedPropertyDescriptor<SyncTest>;
6
+
7
+ type TestInfo = AsyncTestInfo | SyncTestInfo;
2
8
  export default TestInfo;
@@ -1,8 +1,8 @@
1
1
  export default class TestName {
2
2
  static toSentenceCase(test_name: string) {
3
3
  const result = test_name
4
- .replace(/_([A-Z])/gi, (substring: string, match: string) => match.toUpperCase())
5
- .replace(/([A-Z])/g, " $1")
4
+ .replaceAll(/_([A-Z])/gi, (substring: string, match: string) => match.toUpperCase())
5
+ .replaceAll(/([A-Z])/g, " $1")
6
6
  .trim();
7
7
  return result.charAt(0).toUpperCase() + result.slice(1);
8
8
  }
@@ -1,5 +1,5 @@
1
- import TestInfo from "./TestInfo";
2
1
  import AssertionLibrary from "../Assertions";
2
+ import TestInfo from "./TestInfo";
3
3
 
4
4
  /**
5
5
  * Defines a container of tests
@@ -13,12 +13,10 @@ import AssertionLibrary from "../Assertions";
13
13
  export default abstract class TestSuite {
14
14
  assert = AssertionLibrary;
15
15
 
16
- private tests: Record<string, TestInfo> = {};
16
+ private tests?: Record<string, TestInfo> = {};
17
17
 
18
18
  addTest(name: string, info: TestInfo) {
19
- if (this.tests === undefined || this.tests === null) {
20
- this.tests = {};
21
- }
19
+ this.tests ??= {};
22
20
  this.tests[name] = info;
23
21
  }
24
22
 
@@ -33,11 +31,18 @@ export default abstract class TestSuite {
33
31
  }
34
32
 
35
33
  filteredTests(filters: RegExp[]) {
34
+ const tests = this.tests;
35
+ if (tests === undefined)
36
+ return {};
37
+
38
+ const keys = Object.keys(tests)
39
+ .filter(k => filters.map(f => f.test(`${this.constructor.name}.${tests[k].value?.name}`)).some(Boolean));
40
+
36
41
  const filtered: Record<string, TestInfo> = {};
37
- const keys = Object.keys(this.tests).filter(k => filters.map(f => f.test(`${this.constructor.name}.${this.tests[k].value?.name}`)).filter(m => m).length > 0);
38
42
  keys.forEach(k => {
39
- filtered[k] = this.tests[k];
43
+ filtered[k] = tests[k];
40
44
  });
45
+
41
46
  return filtered;
42
47
  }
43
48
  }
@@ -1,14 +1,16 @@
1
- import fs_promises from "fs/promises";
2
- import path from "path";
1
+ import fs_promises from "node:fs/promises";
2
+ import path from "node:path";
3
3
 
4
4
  export default class FileSystem {
5
5
 
6
6
  constructor(private readonly fs: typeof fs_promises) {
7
7
  }
8
8
 
9
+ private static readonly matchExtension: RegExp = new RegExp(/\.(\w+)$/);
10
+
9
11
  static extension(file: string) {
10
- const match = file.match(/\.(\w+)$/);
11
- return match !== undefined && match !== null && match.length > 1
12
+ const match = this.matchExtension.exec(file);
13
+ return match && match.length > 1
12
14
  ? match[1]
13
15
  : "";
14
16
  }
@@ -25,7 +27,7 @@ export default class FileSystem {
25
27
  : [ item_path ]);
26
28
  }
27
29
  return files;
28
- } catch (e) {
30
+ } catch {
29
31
  return [];
30
32
  }
31
33
  }
package/src/IO/Output.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { WriteStream } from "tty";
1
+ import { WriteStream } from "node:tty";
2
2
 
3
3
  export default class Output {
4
4
 
@@ -1,11 +1,14 @@
1
- import ResultReporter from "./ResultReporter";
2
- import TestSuite from "../Framework/TestSuite";
3
- import Output from "../IO/Output";
4
- import TestName from "../Framework/TestName";
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { AssertionError } from "node:assert";
3
+
5
4
  import colors from "colors";
6
- import { AssertionError } from "assert";
7
- import TestSuiteResults from "../Framework/TestSuiteResults";
5
+
8
6
  import { ResultType } from "../Framework/ResultType";
7
+ import TestName from "../Framework/TestName";
8
+ import TestSuite from "../Framework/TestSuite";
9
+ import TestSuiteResults from "../Framework/TestSuiteResults";
10
+ import Output from "../IO/Output";
11
+ import ResultReporter from "./ResultReporter";
9
12
 
10
13
  export default class ConsoleReporter implements ResultReporter {
11
14
 
@@ -42,11 +45,10 @@ export default class ConsoleReporter implements ResultReporter {
42
45
  testErrored(suite: TestSuite, test_name: string, error: Error, duration: number): void {
43
46
  this.out.write(` (${Math.round(duration)} ms)`);
44
47
  this.out.overwrite(` ${colors.red("✘")}\n`);
45
- this.out.writeLine(` ${error.stack}`);
48
+ this.out.writeLine(` ${error.stack ?? ""}`);
46
49
  this.out.writeLine();
47
50
  }
48
51
 
49
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
52
  testIncomplete(suite: TestSuite, test_name: string): void {
51
53
  this.out.overwrite(` ${colors.yellow("?")}\n`);
52
54
  }
@@ -61,16 +63,16 @@ export default class ConsoleReporter implements ResultReporter {
61
63
  this.out.writeLine();
62
64
  }
63
65
 
64
- runCompleted(suites: Record<string, TestSuiteResults>): void {
66
+ async runCompleted(suites: Record<string, TestSuiteResults>): Promise<void> {
65
67
  const results = Object.values(suites);
66
68
  if (!results.length) {
67
69
  this.out.writeLine("No tests found!");
68
- return;
70
+ return Promise.resolve();
69
71
  }
70
72
 
71
73
  const sum = (result_type?: ResultType) => results
72
- .map((suite_result) => result_type !== undefined ? suite_result.count(result_type) : suite_result.total())
73
- .reduce((acc, current) => acc + current);
74
+ .map((suite_result) => result_type === undefined ? suite_result.total() : suite_result.count(result_type))
75
+ .reduce((acc, current) => acc + current, 0);
74
76
 
75
77
  const result = (result_type?: ResultType, color: (string: string) => string = colors.white) => {
76
78
  const count = sum(result_type).toString();
@@ -1,11 +1,13 @@
1
- import ResultReporter from "./ResultReporter";
2
- import TestSuiteResults from "../Framework/TestSuiteResults";
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { AssertionError } from "node:assert";
3
+
3
4
  import TestSuite from "../Framework/TestSuite";
4
- import { AssertionError } from "assert";
5
+ import TestSuiteResults from "../Framework/TestSuiteResults";
5
6
  import FileSystem from "../IO/FileSystem";
7
+ import ResultReporter from "./ResultReporter";
6
8
 
7
9
  export default abstract class FileReporter implements ResultReporter {
8
- /* eslint-disable @typescript-eslint/no-unused-vars */
10
+
9
11
  constructor(protected readonly file_system: FileSystem, protected readonly path: string) {
10
12
  }
11
13
 
@@ -41,5 +43,5 @@ export default abstract class FileReporter implements ResultReporter {
41
43
  return;
42
44
  }
43
45
 
44
- abstract runCompleted(results: Record<string, TestSuiteResults>): void;
46
+ abstract runCompleted(results: Record<string, TestSuiteResults>): Promise<void>;
45
47
  }
@@ -1,8 +1,9 @@
1
- import TestSuiteResults from "../Framework/TestSuiteResults";
2
1
  import xml from "xml";
3
- import TestName from "../Framework/TestName";
2
+
4
3
  import { ResultType } from "../Framework/ResultType";
4
+ import TestName from "../Framework/TestName";
5
5
  import TestResult from "../Framework/TestResult";
6
+ import TestSuiteResults from "../Framework/TestSuiteResults";
6
7
  import XMLReporter from "./XMLReporter";
7
8
 
8
9
  export default class JUnitReporter extends XMLReporter {
@@ -1,5 +1,6 @@
1
+ import { AssertionError } from "node:assert";
2
+
1
3
  import TestSuite from "../Framework/TestSuite";
2
- import { AssertionError } from "assert";
3
4
  import TestSuiteResults from "../Framework/TestSuiteResults";
4
5
 
5
6
  export default interface ResultReporter {
@@ -19,5 +20,5 @@ export default interface ResultReporter {
19
20
 
20
21
  suiteCompleted(suite: TestSuite, results: TestSuiteResults): void;
21
22
 
22
- runCompleted(results: Record<string, TestSuiteResults>): void;
23
- }
23
+ runCompleted(results: Record<string, TestSuiteResults>): Promise<void>;
24
+ };
@@ -1,9 +1,11 @@
1
- import TestSuiteResults from "../Framework/TestSuiteResults";
1
+ import path from "node:path";
2
+
2
3
  import xml from "xml";
4
+
3
5
  import { ResultType } from "../Framework/ResultType";
4
6
  import TestResult from "../Framework/TestResult";
7
+ import TestSuiteResults from "../Framework/TestSuiteResults";
5
8
  import XMLReporter from "./XMLReporter";
6
- import path from "path";
7
9
 
8
10
  export default class SonarReporter extends XMLReporter {
9
11
  static readonly defaultFileName: string = "sonar.xml";
@@ -1,5 +1,5 @@
1
- import FileReporter from "./FileReporter";
2
- import TestSuiteResults from "../Framework/TestSuiteResults";
1
+ import TestSuiteResults from "../Framework/TestSuiteResults";
2
+ import FileReporter from "./FileReporter";
3
3
 
4
4
  export default abstract class XMLReporter extends FileReporter {
5
5
  async runCompleted(results: Record<string, TestSuiteResults>): Promise<void> {
@@ -1,12 +1,12 @@
1
+ import { ResultType } from "../Framework/ResultType";
2
+ import TestSuiteResults from "../Framework/TestSuiteResults";
3
+ import ResultReporter from "../Reporters/ResultReporter";
1
4
  import TestSuiteLoader from "./TestSuiteLoader";
2
5
  import TestSuiteRunner from "./TestSuiteRunner";
3
- import ResultReporter from "../Reporters/ResultReporter";
4
- import TestSuiteResults from "../Framework/TestSuiteResults";
5
- import { ResultType } from "../Framework/ResultType";
6
6
 
7
7
  export default class Runner {
8
8
 
9
- constructor(private readonly loader: TestSuiteLoader, private readonly runner: TestSuiteRunner, private readonly reporters: ReadonlyArray<ResultReporter>) {
9
+ constructor(private readonly loader: TestSuiteLoader, private readonly runner: TestSuiteRunner, private readonly reporters: readonly ResultReporter[]) {
10
10
  }
11
11
 
12
12
  static allTestsPassed(results: Record<string, TestSuiteResults>): boolean {
@@ -16,7 +16,7 @@ export default class Runner {
16
16
  }
17
17
 
18
18
  async runAll(dir: string, filters: RegExp[]): Promise<Record<string, TestSuiteResults>> {
19
- await Promise.all(this.reporters.map(r => r.runStarted()));
19
+ this.reporters.map(r => r.runStarted());
20
20
  const results: Record<string, TestSuiteResults> = {};
21
21
  const suites = await this.loader.loadTestSuites(dir, filters);
22
22
  for (const file of Object.keys(suites)) {
@@ -1,13 +1,14 @@
1
- import TestInfo from "../Framework/TestInfo";
2
- import TestSuite from "../Framework/TestSuite";
1
+ import { AssertionError } from "node:assert";
2
+
3
3
  import { ResultType } from "../Framework/ResultType";
4
- import ResultReporter from "../Reporters/ResultReporter";
4
+ import TestInfo, { AsyncTestInfo, SyncTestInfo } from "../Framework/TestInfo";
5
5
  import TestResult from "../Framework/TestResult";
6
- import { AssertionError } from "assert";
6
+ import TestSuite from "../Framework/TestSuite";
7
+ import ResultReporter from "../Reporters/ResultReporter";
7
8
 
8
9
  export default class TestRunner {
9
10
 
10
- constructor(private readonly reporters: ReadonlyArray<ResultReporter>) {
11
+ constructor(private readonly reporters: readonly ResultReporter[]) {
11
12
  }
12
13
 
13
14
  private static msSince(start: [ number, number ]) {
@@ -15,29 +16,36 @@ export default class TestRunner {
15
16
  return duration[0] * 1_000 + duration[1] / 1_000_000;
16
17
  }
17
18
 
19
+ private static isSyncTest = (test?: TestInfo): test is SyncTestInfo|undefined => test !== undefined;
20
+ private static isAsyncTest = (test?: TestInfo): test is AsyncTestInfo|undefined => test !== undefined;
21
+
18
22
  async runTest(name: string, info: TestInfo, suite: TestSuite): Promise<TestResult> {
19
- await Promise.all(this.reporters.map(r => r.testStarted(suite, name)));
23
+ this.reporters.map(r => r.testStarted(suite, name));
20
24
  if (info.value === undefined) {
21
- await Promise.all(this.reporters.map(r => r.testIncomplete(suite, name)));
25
+ this.reporters.map(r => r.testIncomplete(suite, name));
22
26
  return new TestResult(ResultType.Incomplete, 0);
23
27
  }
24
28
 
25
29
  const start = process.hrtime();
26
30
  try {
27
- await info.value.call(suite);
31
+ if (TestRunner.isAsyncTest(info))
32
+ await info.value.call(suite);
33
+ else if (TestRunner.isSyncTest(info))
34
+ info.value.call(suite);
35
+
28
36
  const duration = TestRunner.msSince(start);
29
- await Promise.all(this.reporters.map(r => r.testPassed(suite, name, duration)));
37
+ this.reporters.map(r => r.testPassed(suite, name, duration));
30
38
  return new TestResult(ResultType.Passed, duration);
31
39
 
32
40
  } catch (error) {
33
41
  const duration = TestRunner.msSince(start);
34
42
  const typedError = error as Error;
35
43
  if (typedError instanceof AssertionError) {
36
- await Promise.all(this.reporters.map(r => r.testFailed(suite, name, typedError, duration)));
44
+ this.reporters.map(r => r.testFailed(suite, name, typedError, duration));
37
45
  return new TestResult(ResultType.Failed, duration, typedError);
38
46
  }
39
47
 
40
- await Promise.all(this.reporters.map(r => r.testErrored(suite, name, typedError, duration)));
48
+ this.reporters.map(r => r.testErrored(suite, name, typedError, duration));
41
49
  return new TestResult(ResultType.Error, duration, typedError);
42
50
  }
43
51
  }
@@ -1,6 +1,7 @@
1
+ import path from "node:path";
2
+
1
3
  import TestSuite from "../Framework/TestSuite";
2
4
  import FileSystem from "../IO/FileSystem";
3
- import path from "path";
4
5
 
5
6
  export default class TestSuiteLoader {
6
7
  constructor(private readonly file_system: FileSystem) {
@@ -8,17 +9,23 @@ export default class TestSuiteLoader {
8
9
 
9
10
  static async loadTestSuite(file: string, filters: RegExp[]) {
10
11
  const module_path = TestSuiteLoader.getModulePath(__dirname, file);
11
- const test_class = await import(module_path);
12
- if (!(test_class.default?.prototype instanceof TestSuite)) {
12
+ const test_class = await import(module_path) as { default?: new () => TestSuite };
13
+ const constructor = test_class.default;
14
+ if (!constructor) {
15
+ return null;
16
+ }
17
+
18
+ const prototype = constructor.prototype as TestSuite;
19
+ if (!(prototype instanceof TestSuite)) {
13
20
  return null;
14
21
  }
15
22
 
16
- const tests = test_class.default?.prototype.getTests(filters);
23
+ const tests = prototype.getTests(filters);
17
24
  if (tests === undefined || Object.keys(tests).length === 0) {
18
25
  return null;
19
26
  }
20
27
 
21
- const suite: TestSuite = new test_class.default();
28
+ const suite = new constructor();
22
29
  suite.setTests(tests);
23
30
  return suite;
24
31
  }
@@ -35,7 +42,7 @@ export default class TestSuiteLoader {
35
42
  }
36
43
 
37
44
  static isFromNodeModules(dir: string) {
38
- return dir.indexOf("node_modules") !== -1;
45
+ return dir.includes("node_modules");
39
46
  }
40
47
 
41
48
  async loadTestSuites(dir: string, filters: RegExp[]): Promise<Record<string, TestSuite>> {
@@ -46,7 +53,7 @@ export default class TestSuiteLoader {
46
53
 
47
54
  for (const file of files) {
48
55
  const suite = await TestSuiteLoader.loadTestSuite(file, filters);
49
- if (suite !== undefined && suite !== null) {
56
+ if (suite !== null) {
50
57
  suites[file] = suite;
51
58
  }
52
59
  }