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.
- package/README.md +44 -19
- package/cli.ts +9 -4
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/eslint.config.d.ts +3 -0
- package/dist/eslint.config.d.ts.map +1 -0
- package/dist/eslint.config.js +46 -0
- package/dist/eslint.config.js.map +1 -0
- package/dist/src/Assertions/Contains.d.ts +1 -1
- package/dist/src/Assertions/Contains.d.ts.map +1 -1
- package/dist/src/Assertions/Contains.js +4 -4
- package/dist/src/Assertions/Contains.js.map +1 -1
- package/dist/src/Assertions/Count.d.ts +1 -1
- package/dist/src/Assertions/Count.d.ts.map +1 -1
- package/dist/src/Assertions/Count.js +4 -4
- package/dist/src/Assertions/Count.js.map +1 -1
- package/dist/src/Assertions/Defined.d.ts +1 -1
- package/dist/src/Assertions/Defined.d.ts.map +1 -1
- package/dist/src/Assertions/Defined.js +4 -4
- package/dist/src/Assertions/Defined.js.map +1 -1
- package/dist/src/Assertions/DoesNotContain.d.ts +1 -1
- package/dist/src/Assertions/DoesNotContain.d.ts.map +1 -1
- package/dist/src/Assertions/DoesNotContain.js +4 -4
- package/dist/src/Assertions/DoesNotContain.js.map +1 -1
- package/dist/src/Assertions/DoesNotThrow.d.ts +1 -1
- package/dist/src/Assertions/DoesNotThrow.d.ts.map +1 -1
- package/dist/src/Assertions/DoesNotThrow.js +4 -4
- package/dist/src/Assertions/DoesNotThrow.js.map +1 -1
- package/dist/src/Assertions/Empty.d.ts +1 -1
- package/dist/src/Assertions/Empty.d.ts.map +1 -1
- package/dist/src/Assertions/Empty.js +4 -4
- package/dist/src/Assertions/Empty.js.map +1 -1
- package/dist/src/Assertions/Equal.d.ts +1 -1
- package/dist/src/Assertions/Equal.d.ts.map +1 -1
- package/dist/src/Assertions/Equal.js +4 -4
- package/dist/src/Assertions/Equal.js.map +1 -1
- package/dist/src/Assertions/False.d.ts +1 -1
- package/dist/src/Assertions/False.d.ts.map +1 -1
- package/dist/src/Assertions/False.js +4 -4
- package/dist/src/Assertions/False.js.map +1 -1
- package/dist/src/Assertions/InstanceOf.d.ts +1 -1
- package/dist/src/Assertions/InstanceOf.d.ts.map +1 -1
- package/dist/src/Assertions/InstanceOf.js +4 -4
- package/dist/src/Assertions/InstanceOf.js.map +1 -1
- package/dist/src/Assertions/NotEmpty.d.ts +1 -1
- package/dist/src/Assertions/NotEmpty.d.ts.map +1 -1
- package/dist/src/Assertions/NotEmpty.js +4 -4
- package/dist/src/Assertions/NotEmpty.js.map +1 -1
- package/dist/src/Assertions/NotEqual.d.ts +1 -1
- package/dist/src/Assertions/NotEqual.d.ts.map +1 -1
- package/dist/src/Assertions/NotEqual.js +4 -4
- package/dist/src/Assertions/NotEqual.js.map +1 -1
- package/dist/src/Assertions/NotNull.d.ts +1 -1
- package/dist/src/Assertions/NotNull.d.ts.map +1 -1
- package/dist/src/Assertions/NotNull.js +4 -4
- package/dist/src/Assertions/NotNull.js.map +1 -1
- package/dist/src/Assertions/Null.d.ts +1 -1
- package/dist/src/Assertions/Null.d.ts.map +1 -1
- package/dist/src/Assertions/Null.js +4 -4
- package/dist/src/Assertions/Null.js.map +1 -1
- package/dist/src/Assertions/StringContains.js +5 -5
- package/dist/src/Assertions/StringContains.js.map +1 -1
- package/dist/src/Assertions/StringDoesNotContain.js +5 -5
- package/dist/src/Assertions/StringDoesNotContain.js.map +1 -1
- package/dist/src/Assertions/StringDoesNotEndWith.d.ts.map +1 -1
- package/dist/src/Assertions/StringDoesNotEndWith.js +5 -7
- package/dist/src/Assertions/StringDoesNotEndWith.js.map +1 -1
- package/dist/src/Assertions/StringDoesNotMatch.d.ts +17 -0
- package/dist/src/Assertions/StringDoesNotMatch.d.ts.map +1 -0
- package/dist/src/Assertions/StringDoesNotMatch.js +30 -0
- package/dist/src/Assertions/StringDoesNotMatch.js.map +1 -0
- package/dist/src/Assertions/StringDoesNotStartWith.js +5 -5
- package/dist/src/Assertions/StringDoesNotStartWith.js.map +1 -1
- package/dist/src/Assertions/StringEndsWith.d.ts.map +1 -1
- package/dist/src/Assertions/StringEndsWith.js +5 -7
- package/dist/src/Assertions/StringEndsWith.js.map +1 -1
- package/dist/src/Assertions/StringMatches.d.ts +17 -0
- package/dist/src/Assertions/StringMatches.d.ts.map +1 -0
- package/dist/src/Assertions/StringMatches.js +30 -0
- package/dist/src/Assertions/StringMatches.js.map +1 -0
- package/dist/src/Assertions/StringStartsWith.js +5 -5
- package/dist/src/Assertions/StringStartsWith.js.map +1 -1
- package/dist/src/Assertions/Throws.d.ts +1 -1
- package/dist/src/Assertions/Throws.d.ts.map +1 -1
- package/dist/src/Assertions/Throws.js +5 -5
- package/dist/src/Assertions/Throws.js.map +1 -1
- package/dist/src/Assertions/True.d.ts +1 -1
- package/dist/src/Assertions/True.d.ts.map +1 -1
- package/dist/src/Assertions/True.js +4 -4
- package/dist/src/Assertions/True.js.map +1 -1
- package/dist/src/Assertions/Undefined.d.ts +1 -1
- package/dist/src/Assertions/Undefined.d.ts.map +1 -1
- package/dist/src/Assertions/Undefined.js +4 -4
- package/dist/src/Assertions/Undefined.js.map +1 -1
- package/dist/src/Assertions/index.d.ts +18 -14
- package/dist/src/Assertions/index.d.ts.map +1 -1
- package/dist/src/Assertions/index.js +18 -14
- package/dist/src/Assertions/index.js.map +1 -1
- package/dist/src/CLI.d.ts +1 -3
- package/dist/src/CLI.d.ts.map +1 -1
- package/dist/src/CLI.js +9 -7
- package/dist/src/CLI.js.map +1 -1
- package/dist/src/Factory.d.ts +1 -1
- package/dist/src/Factory.d.ts.map +1 -1
- package/dist/src/Factory.js +7 -7
- package/dist/src/Factory.js.map +1 -1
- package/dist/src/Framework/ResultType.js +1 -1
- package/dist/src/Framework/ResultType.js.map +1 -1
- package/dist/src/Framework/Test.d.ts +1 -1
- package/dist/src/Framework/Test.d.ts.map +1 -1
- package/dist/src/Framework/Test.js +2 -2
- package/dist/src/Framework/Test.js.map +1 -1
- package/dist/src/Framework/TestInfo.d.ts +5 -1
- package/dist/src/Framework/TestInfo.d.ts.map +1 -1
- package/dist/src/Framework/TestName.js +2 -2
- package/dist/src/Framework/TestName.js.map +1 -1
- package/dist/src/Framework/TestSuite.d.ts +4 -2
- package/dist/src/Framework/TestSuite.d.ts.map +1 -1
- package/dist/src/Framework/TestSuite.js +8 -5
- package/dist/src/Framework/TestSuite.js.map +1 -1
- package/dist/src/IO/FileSystem.d.ts +2 -2
- package/dist/src/IO/FileSystem.d.ts.map +1 -1
- package/dist/src/IO/FileSystem.js +6 -5
- package/dist/src/IO/FileSystem.js.map +1 -1
- package/dist/src/IO/Output.d.ts +1 -2
- package/dist/src/IO/Output.d.ts.map +1 -1
- package/dist/src/IO/Output.js.map +1 -1
- package/dist/src/Reporters/ConsoleReporter.d.ts +4 -5
- package/dist/src/Reporters/ConsoleReporter.d.ts.map +1 -1
- package/dist/src/Reporters/ConsoleReporter.js +39 -28
- package/dist/src/Reporters/ConsoleReporter.js.map +1 -1
- package/dist/src/Reporters/FileReporter.d.ts +4 -5
- package/dist/src/Reporters/FileReporter.d.ts.map +1 -1
- package/dist/src/Reporters/FileReporter.js +0 -1
- package/dist/src/Reporters/FileReporter.js.map +1 -1
- package/dist/src/Reporters/JUnitReporter.d.ts.map +1 -1
- package/dist/src/Reporters/JUnitReporter.js +1 -1
- package/dist/src/Reporters/JUnitReporter.js.map +1 -1
- package/dist/src/Reporters/ResultReporter.d.ts +2 -3
- package/dist/src/Reporters/ResultReporter.d.ts.map +1 -1
- package/dist/src/Reporters/ResultReporter.js +1 -0
- package/dist/src/Reporters/ResultReporter.js.map +1 -1
- package/dist/src/Reporters/SonarReporter.d.ts.map +1 -1
- package/dist/src/Reporters/SonarReporter.js +2 -2
- package/dist/src/Reporters/SonarReporter.js.map +1 -1
- package/dist/src/Reporters/XMLReporter.d.ts +1 -1
- package/dist/src/Reporters/XMLReporter.d.ts.map +1 -1
- package/dist/src/Reporters/XMLReporter.js.map +1 -1
- package/dist/src/Runners/Runner.d.ts +3 -3
- package/dist/src/Runners/Runner.d.ts.map +1 -1
- package/dist/src/Runners/Runner.js +1 -1
- package/dist/src/Runners/Runner.js.map +1 -1
- package/dist/src/Runners/TestRunner.d.ts +4 -2
- package/dist/src/Runners/TestRunner.d.ts.map +1 -1
- package/dist/src/Runners/TestRunner.js +13 -8
- package/dist/src/Runners/TestRunner.js.map +1 -1
- package/dist/src/Runners/TestSuiteLoader.d.ts.map +1 -1
- package/dist/src/Runners/TestSuiteLoader.js +32 -18
- package/dist/src/Runners/TestSuiteLoader.js.map +1 -1
- package/dist/src/Runners/TestSuiteRunner.d.ts +4 -4
- package/dist/src/Runners/TestSuiteRunner.d.ts.map +1 -1
- package/dist/src/Runners/TestSuiteRunner.js +3 -3
- package/dist/src/Runners/TestSuiteRunner.js.map +1 -1
- package/dist/xunit.d.ts +2 -2
- package/dist/xunit.d.ts.map +1 -1
- package/dist/xunit.js +3 -3
- package/dist/xunit.js.map +1 -1
- package/eslint.config.ts +44 -0
- package/package.json +18 -14
- package/src/Assertions/Contains.ts +3 -3
- package/src/Assertions/Count.ts +3 -3
- package/src/Assertions/Defined.ts +3 -3
- package/src/Assertions/DoesNotContain.ts +3 -3
- package/src/Assertions/DoesNotThrow.ts +3 -3
- package/src/Assertions/Empty.ts +3 -3
- package/src/Assertions/Equal.ts +4 -3
- package/src/Assertions/False.ts +3 -3
- package/src/Assertions/InstanceOf.ts +3 -3
- package/src/Assertions/NotEmpty.ts +3 -3
- package/src/Assertions/NotEqual.ts +4 -3
- package/src/Assertions/NotNull.ts +3 -3
- package/src/Assertions/Null.ts +3 -3
- package/src/Assertions/StringContains.ts +3 -3
- package/src/Assertions/StringDoesNotContain.ts +3 -3
- package/src/Assertions/StringDoesNotEndWith.ts +3 -5
- package/src/Assertions/StringDoesNotMatch.ts +28 -0
- package/src/Assertions/StringDoesNotStartWith.ts +3 -3
- package/src/Assertions/StringEndsWith.ts +3 -5
- package/src/Assertions/StringMatches.ts +28 -0
- package/src/Assertions/StringStartsWith.ts +3 -3
- package/src/Assertions/Throws.ts +4 -4
- package/src/Assertions/True.ts +3 -3
- package/src/Assertions/Undefined.ts +3 -3
- package/src/Assertions/index.ts +19 -25
- package/src/CLI.ts +11 -7
- package/src/Factory.ts +11 -9
- package/src/Framework/Test.ts +2 -2
- package/src/Framework/TestInfo.ts +7 -1
- package/src/Framework/TestName.ts +2 -2
- package/src/Framework/TestSuite.ts +12 -7
- package/src/IO/FileSystem.ts +7 -5
- package/src/IO/Output.ts +1 -1
- package/src/Reporters/ConsoleReporter.ts +14 -12
- package/src/Reporters/FileReporter.ts +7 -5
- package/src/Reporters/JUnitReporter.ts +3 -2
- package/src/Reporters/ResultReporter.ts +4 -3
- package/src/Reporters/SonarReporter.ts +4 -2
- package/src/Reporters/XMLReporter.ts +2 -2
- package/src/Runners/Runner.ts +5 -5
- package/src/Runners/TestRunner.ts +19 -11
- package/src/Runners/TestSuiteLoader.ts +14 -7
- package/src/Runners/TestSuiteRunner.ts +7 -7
- package/tsconfig.json +3 -0
- 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
|
|
19
|
+
if (haystack?.startsWith(needle)) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
throw new AssertionError({
|
|
24
|
-
message: message
|
|
24
|
+
message: message ?? "Expected string containing expression, but string did not contain expression",
|
|
25
25
|
expected: needle,
|
|
26
26
|
actual: haystack
|
|
27
27
|
});
|
package/src/Assertions/Throws.ts
CHANGED
|
@@ -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: () =>
|
|
17
|
+
export default function Throws(expression: () => unknown, message?: string) {
|
|
18
18
|
try {
|
|
19
19
|
expression();
|
|
20
|
-
} catch
|
|
20
|
+
} catch {
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
throw new AssertionError({
|
|
25
|
-
message: message
|
|
25
|
+
message: message ?? "Expected expression to throw exception, but expression did not throw exception"
|
|
26
26
|
});
|
|
27
27
|
}
|
package/src/Assertions/True.ts
CHANGED
|
@@ -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:
|
|
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
|
|
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:
|
|
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
|
|
23
|
+
message: message ?? "Expected expression to be undefined, but expression is not undefined",
|
|
24
24
|
expected: undefined,
|
|
25
25
|
actual: expression
|
|
26
26
|
});
|
package/src/Assertions/index.ts
CHANGED
|
@@ -1,36 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2
|
-
|
|
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
|
-
|
|
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
|
|
101
|
-
const
|
|
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()
|
|
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
|
-
|
|
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):
|
|
28
|
+
static Reporters(args: Args.CommandLineOptions): readonly ResultReporter[] {
|
|
27
29
|
return [
|
|
28
|
-
|
|
29
|
-
args.junit
|
|
30
|
-
args.sonar
|
|
31
|
-
].filter(r => r !== null)
|
|
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
|
}
|
package/src/Framework/Test.ts
CHANGED
|
@@ -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
|
|
21
|
+
suite.addTest(test_name ?? TestName.toSentenceCase(method_name), info);
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
type
|
|
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
|
-
.
|
|
5
|
-
.
|
|
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
|
|
16
|
+
private tests?: Record<string, TestInfo> = {};
|
|
17
17
|
|
|
18
18
|
addTest(name: string, info: TestInfo) {
|
|
19
|
-
|
|
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] =
|
|
43
|
+
filtered[k] = tests[k];
|
|
40
44
|
});
|
|
45
|
+
|
|
41
46
|
return filtered;
|
|
42
47
|
}
|
|
43
48
|
}
|
package/src/IO/FileSystem.ts
CHANGED
|
@@ -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 =
|
|
11
|
-
return match
|
|
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
|
|
30
|
+
} catch {
|
|
29
31
|
return [];
|
|
30
32
|
}
|
|
31
33
|
}
|
package/src/IO/Output.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2
|
-
import
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
import { AssertionError } from "node:assert";
|
|
3
|
+
|
|
3
4
|
import TestSuite from "../Framework/TestSuite";
|
|
4
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
2
|
-
import
|
|
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> {
|
package/src/Runners/Runner.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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
|
|
2
|
-
|
|
1
|
+
import { AssertionError } from "node:assert";
|
|
2
|
+
|
|
3
3
|
import { ResultType } from "../Framework/ResultType";
|
|
4
|
-
import
|
|
4
|
+
import TestInfo, { AsyncTestInfo, SyncTestInfo } from "../Framework/TestInfo";
|
|
5
5
|
import TestResult from "../Framework/TestResult";
|
|
6
|
-
import
|
|
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:
|
|
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
|
-
|
|
23
|
+
this.reporters.map(r => r.testStarted(suite, name));
|
|
20
24
|
if (info.value === undefined) {
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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.
|
|
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 !==
|
|
56
|
+
if (suite !== null) {
|
|
50
57
|
suites[file] = suite;
|
|
51
58
|
}
|
|
52
59
|
}
|