risei 2.0.1 → 3.1.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 +50 -28
- package/index.js +18 -66
- package/package.json +7 -6
- package/system/CallTypes.js +10 -0
- package/system/Choices.js +92 -0
- package/system/ClassTestGroup.js +8 -8
- package/system/LocalCaller.js +47 -7
- package/system/MethodSpoofer.js +2 -2
- package/system/MethodTestGroup.js +8 -8
- package/system/Moment.js +29 -29
- package/system/NameAnalyzer.js +26 -26
- package/system/ObjectAnalyzer.js +36 -0
- package/system/PropertySpoofer.js +1 -1
- package/system/SpoofDef.js +73 -63
- package/system/TerminalReporter.js +102 -25
- package/system/TestDef.js +75 -17
- package/system/TestFinder.js +31 -29
- package/system/TestFrame.js +21 -12
- package/system/TestGroup.js +25 -25
- package/system/TestResult.js +63 -13
- package/system/TestRunner.js +44 -16
- package/system/TestStages.js +85 -26
- package/system/TestSummary.js +38 -37
- package/system/TotalComparer.js +54 -17
- package/system/TotalDisplayer.js +29 -6
- package/system/TypeAnalyzer.js +117 -26
- package/system/TypeIdentifier.js +10 -6
- package/system/Types.js +1 -0
- package/system/ATestCaller.js +0 -61
- package/system/ATestFinder.js +0 -38
- package/system/ATestReporter.js +0 -26
- package/system/ChosenTestFinder.js +0 -31
- package/system/Risei.js +0 -118
package/system/TestDef.js
CHANGED
|
@@ -4,6 +4,8 @@ import TypeAnalyzer from "./TypeAnalyzer.js";
|
|
|
4
4
|
import NameAnalyzer from "./NameAnalyzer.js";
|
|
5
5
|
import TypeIdentifier from "./TypeIdentifier.js";
|
|
6
6
|
import Types from "./Types.js";
|
|
7
|
+
import CallTypes from "./CallTypes.js";
|
|
8
|
+
import TotalDisplayer from "./TotalDisplayer.js";
|
|
7
9
|
|
|
8
10
|
/* Defines what is found in a test definition, typically as
|
|
9
11
|
a nonce object rather than an instance of this class. */
|
|
@@ -13,6 +15,7 @@ export default class TestDef {
|
|
|
13
15
|
|
|
14
16
|
static staticName = "static";
|
|
15
17
|
static throwName = "throw";
|
|
18
|
+
static polyName = "poly";
|
|
16
19
|
static constructorName = "constructor";
|
|
17
20
|
static nonceLocalCallableName = "nonce";
|
|
18
21
|
|
|
@@ -56,13 +59,12 @@ export default class TestDef {
|
|
|
56
59
|
target;
|
|
57
60
|
|
|
58
61
|
actual;
|
|
59
|
-
thrown;
|
|
62
|
+
thrown; // Throw in inner scope (tested code).
|
|
63
|
+
trouble; // Throw in outer scope (test system or .do / .undo / .from).
|
|
60
64
|
|
|
61
65
|
// endregion Fields
|
|
62
66
|
|
|
63
|
-
// region
|
|
64
|
-
|
|
65
|
-
// region Test definition, short names
|
|
67
|
+
// region Definition properties
|
|
66
68
|
|
|
67
69
|
/* These short names make JSON-like definitions easier. */
|
|
68
70
|
|
|
@@ -154,9 +156,9 @@ export default class TestDef {
|
|
|
154
156
|
this.counteract = value;
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
// endregion
|
|
159
|
+
// endregion Definition properties
|
|
158
160
|
|
|
159
|
-
// region
|
|
161
|
+
// region State properties
|
|
160
162
|
|
|
161
163
|
get isRunnable() /* passed */ {
|
|
162
164
|
let isRunnable
|
|
@@ -182,19 +184,28 @@ export default class TestDef {
|
|
|
182
184
|
|
|
183
185
|
return is || stated;
|
|
184
186
|
}
|
|
185
|
-
|
|
187
|
+
|
|
186
188
|
get isThrowTest() /* passed */ {
|
|
187
189
|
let is = this.andStringContains(TestDef.throwName);
|
|
188
190
|
return is;
|
|
189
191
|
}
|
|
190
192
|
|
|
193
|
+
get isMonoCallTest() /* passed */ {
|
|
194
|
+
return !this.isPolyCallTest;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
get isPolyCallTest() /* passed */ {
|
|
198
|
+
let is = this.andStringContains(TestDef.polyName);
|
|
199
|
+
return is;
|
|
200
|
+
}
|
|
201
|
+
|
|
191
202
|
get isConstructorTest() /* passed */ {
|
|
192
203
|
let plainTarget = NameAnalyzer.plainNameOf(this.of);
|
|
193
204
|
return plainTarget === TestDef.constructorName;
|
|
194
205
|
}
|
|
195
206
|
|
|
196
207
|
get isMethodTest() /* passed */ {
|
|
197
|
-
return !this.isPropertyTest;
|
|
208
|
+
return !this.isPropertyTest && !this.isConstructorTest;
|
|
198
209
|
}
|
|
199
210
|
|
|
200
211
|
get isPropertyTest() /* passed */ {
|
|
@@ -207,6 +218,22 @@ export default class TestDef {
|
|
|
207
218
|
return isPropertyTest;
|
|
208
219
|
}
|
|
209
220
|
|
|
221
|
+
get callType() /* passed */ {
|
|
222
|
+
let type;
|
|
223
|
+
|
|
224
|
+
if (this.isMethodTest) {
|
|
225
|
+
type = this.isMonoCallTest ? CallTypes.methodMono : CallTypes.methodPoly;
|
|
226
|
+
}
|
|
227
|
+
else if (this.isPropertyTest) {
|
|
228
|
+
type = this.isMonoCallTest ? CallTypes.propMono : CallTypes.propPoly;
|
|
229
|
+
}
|
|
230
|
+
else if (this.isConstructorTest) {
|
|
231
|
+
type = this.isMonoCallTest ? CallTypes.conMono : CallTypes.conPoly;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return type;
|
|
235
|
+
}
|
|
236
|
+
|
|
210
237
|
get isRetrievalTest() /* passed */ {
|
|
211
238
|
// Falsy code elements never are a retrieval.
|
|
212
239
|
if (!this.from) {
|
|
@@ -252,20 +279,34 @@ export default class TestDef {
|
|
|
252
279
|
get runName() /* passed */ {
|
|
253
280
|
let plain = NameAnalyzer.plainNameOf(this.of);
|
|
254
281
|
|
|
255
|
-
if (this.
|
|
282
|
+
if (!this.isPropertyTest) {
|
|
256
283
|
return `${ plain }()`;
|
|
257
284
|
}
|
|
258
|
-
|
|
259
|
-
if (this.isPropertyTest) {
|
|
285
|
+
else {
|
|
260
286
|
return `.${ plain }`;
|
|
261
287
|
}
|
|
262
288
|
}
|
|
263
289
|
|
|
264
|
-
|
|
290
|
+
get anyUnsoughtThrow() /* passed */ {
|
|
291
|
+
// Prop is true when throw is sought.
|
|
292
|
+
if (this.isThrowTest) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
265
295
|
|
|
266
|
-
|
|
296
|
+
return this.thrown;
|
|
297
|
+
}
|
|
267
298
|
|
|
268
|
-
|
|
299
|
+
get doesHaveUnsoughtThrow() /* passed */ {
|
|
300
|
+
return this.anyUnsoughtThrow !== undefined;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
get doesHaveTrouble() /* passed */ {
|
|
304
|
+
return this.trouble !== undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// endregion State properties
|
|
308
|
+
|
|
309
|
+
// region State methods
|
|
269
310
|
|
|
270
311
|
/* Needed externally, and dependency
|
|
271
312
|
of all .and-based properties. */
|
|
@@ -277,12 +318,11 @@ export default class TestDef {
|
|
|
277
318
|
return doesContain;
|
|
278
319
|
}
|
|
279
320
|
|
|
280
|
-
// endregion
|
|
321
|
+
// endregion State methods
|
|
281
322
|
|
|
282
323
|
// region Initing, including constructor()
|
|
283
324
|
|
|
284
|
-
/*
|
|
285
|
-
when not .-prefixed, they are purely keywords. */
|
|
325
|
+
/* Long names used, since some short names, like `for`, are parsed as keywords. */
|
|
286
326
|
constructor(nature, type, spoofed, initors, method, inputs, output, source, factors, enact, counteract) /* ok */ {
|
|
287
327
|
this.nature = nature; // .for
|
|
288
328
|
this.type = type; // .on
|
|
@@ -416,4 +456,22 @@ export default class TestDef {
|
|
|
416
456
|
|
|
417
457
|
// endregion Initing, including constructor()
|
|
418
458
|
|
|
459
|
+
toString() /* verified */ {
|
|
460
|
+
let displayer = new TotalDisplayer();
|
|
461
|
+
|
|
462
|
+
let text = `TestDef{ for:${ displayer.display(this.for) }, `
|
|
463
|
+
+ `on:${ displayer.display(this.on) }, `
|
|
464
|
+
+ `with:${ displayer.display(this.with) }, `
|
|
465
|
+
+ `of:${ displayer.display(this.of) }, `
|
|
466
|
+
+ `plus:${ displayer.display(this.plus) }, `
|
|
467
|
+
+ `in:${ displayer.display(this.in) }, `
|
|
468
|
+
+ `out:${ displayer.display(this.out) }, `
|
|
469
|
+
+ `from:${ displayer.display(this.from) }, `
|
|
470
|
+
+ `and:${ displayer.display(this.and) }, `
|
|
471
|
+
+ `do:${ displayer.display(this.do) }, `
|
|
472
|
+
+ `undo:${ displayer.display(this.undo) } }`
|
|
473
|
+
|
|
474
|
+
return text;
|
|
475
|
+
}
|
|
476
|
+
|
|
419
477
|
}
|
package/system/TestFinder.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
/* TestFinder
|
|
3
|
+
/* TestFinder retrieves test sources based on .risei.tests in package.json. */
|
|
4
4
|
|
|
5
|
-
import ATestFinder from "./ATestFinder.js";
|
|
6
5
|
import ATestSource from "./ATestSource.js";
|
|
6
|
+
import Choices from "./Choices.js";
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { minimatch } from "minimatch";
|
|
10
10
|
|
|
11
|
-
export default class TestFinder
|
|
11
|
+
export default class TestFinder {
|
|
12
12
|
// region Definitions
|
|
13
13
|
|
|
14
14
|
static NO_SUCH_FILE = "ENOENT";
|
|
@@ -21,20 +21,37 @@ export default class TestFinder extends ATestFinder {
|
|
|
21
21
|
// endregion Definitions
|
|
22
22
|
|
|
23
23
|
// region Fields
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
#testSources;
|
|
26
|
+
#thrown = [];
|
|
27
|
+
|
|
25
28
|
#sought;
|
|
26
29
|
|
|
27
30
|
// endregion Fields
|
|
28
31
|
|
|
32
|
+
// region Properties
|
|
33
|
+
|
|
34
|
+
get testSources() {
|
|
35
|
+
return this.#testSources;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set testSources(value) {
|
|
39
|
+
this.#testSources = value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get thrown() {
|
|
43
|
+
return this.#thrown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// endregion Properties
|
|
47
|
+
|
|
29
48
|
constructor() {
|
|
30
|
-
|
|
49
|
+
this.#testSources = [];
|
|
31
50
|
}
|
|
32
51
|
|
|
33
52
|
async findAllTests() {
|
|
34
|
-
|
|
35
|
-
let
|
|
36
|
-
this.#sought = source.tests;
|
|
37
|
-
let testPaths = this.pathsFromTargetingSource();
|
|
53
|
+
this.#sought = this.supplyTestTargeting();
|
|
54
|
+
let testPaths = this.pathsFromTargeting();
|
|
38
55
|
|
|
39
56
|
testPaths = this.sortTestPaths(testPaths);
|
|
40
57
|
|
|
@@ -54,28 +71,13 @@ export default class TestFinder extends ATestFinder {
|
|
|
54
71
|
return tests;
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Contents of package.json are just a single JSON object.
|
|
63
|
-
let packageJson = fs.readFileSync(pathAndName, { encoding: "utf8" });
|
|
64
|
-
packageJson = JSON.parse(packageJson);
|
|
65
|
-
|
|
66
|
-
// Can't run or reasonably recover if this is not defined.
|
|
67
|
-
if (!packageJson.risei) {
|
|
68
|
-
throw new Error(
|
|
69
|
-
`For Risei tests to run, this app's package.json must contain `
|
|
70
|
-
+ `a node like this: "risei": { tests: "path-or-glob-here" }. `
|
|
71
|
-
+ `One glob or path may be used, or an array of them.`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Only Risei's own metadata is needed.
|
|
75
|
-
return packageJson.risei;
|
|
74
|
+
// Spoofing point for self-testing.
|
|
75
|
+
supplyTestTargeting() {
|
|
76
|
+
// Value retrieved from package.json.
|
|
77
|
+
return Choices.testTargeting;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
pathsFromTargeting() {
|
|
79
81
|
let paths = [];
|
|
80
82
|
let root = process.cwd();
|
|
81
83
|
|
package/system/TestFrame.js
CHANGED
|
@@ -9,12 +9,12 @@ export default class TestFrame {
|
|
|
9
9
|
// A TestStages is inited for each test.
|
|
10
10
|
this.#stages = new TestStages();
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
run(test) /* passed */ {
|
|
12
|
+
|
|
13
|
+
async run(test) /* passed */ {
|
|
14
14
|
// Outer try for fails of groundwork requested.
|
|
15
15
|
try {
|
|
16
16
|
// Spoofing and so on.
|
|
17
|
-
this.#stages.anyPreTargetingGroundwork(test);
|
|
17
|
+
await this.#stages.anyPreTargetingGroundwork(test);
|
|
18
18
|
|
|
19
19
|
// Target may be instance, prototype for constructors, the class
|
|
20
20
|
// itself for statics, or nonce for properties. Sets test.target.
|
|
@@ -25,17 +25,18 @@ export default class TestFrame {
|
|
|
25
25
|
// Method under test may be on instance, class,
|
|
26
26
|
// or nonce for properties. Sets test.callable.
|
|
27
27
|
let callable = this.#stages.supplyCallableName(test);
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
// Setting (spoofing) properties and so on.
|
|
30
|
-
this.#stages.anyPostTargetingGroundwork(test);
|
|
31
|
-
|
|
30
|
+
await this.#stages.anyPostTargetingGroundwork(test);
|
|
31
|
+
|
|
32
32
|
// Inner try for fails of targeted code only.
|
|
33
33
|
try {
|
|
34
34
|
// Tests usually look for this.
|
|
35
|
-
test.actual = target[callable](...test.in);
|
|
35
|
+
test.actual = await target[callable](...test.in);
|
|
36
36
|
}
|
|
37
37
|
catch (thrown) {
|
|
38
38
|
// Sometimes tests look for this.
|
|
39
|
+
// Otherwise, listed in gold bar.
|
|
39
40
|
test.thrown = thrown;
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -46,13 +47,21 @@ export default class TestFrame {
|
|
|
46
47
|
test.didPass = this.#stages.compareResults(test.output, test.actual);
|
|
47
48
|
}
|
|
48
49
|
catch (thrown) {
|
|
49
|
-
|
|
50
|
+
// Fail of the test system, or of code in .do or .from.
|
|
51
|
+
test.trouble = thrown;
|
|
50
52
|
}
|
|
51
53
|
finally {
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
+
// Always undoing any groundwork changes made.
|
|
55
|
+
try {
|
|
56
|
+
await this.#stages.anyGroundworkReversal(test);
|
|
57
|
+
}
|
|
58
|
+
catch (thrown) {
|
|
59
|
+
// Fail of the test system, or of code in .undo;
|
|
60
|
+
// only needed when no preceding throw.
|
|
61
|
+
if (test.trouble === undefined) {
|
|
62
|
+
test.trouble = thrown;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
54
65
|
}
|
|
55
|
-
|
|
56
|
-
// console.log(`cruft : q, outer try-catch : after`);
|
|
57
66
|
}
|
|
58
67
|
}
|
package/system/TestGroup.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
/**/
|
|
2
|
-
|
|
3
|
-
export default class TestGroup {
|
|
4
|
-
// region Private fields
|
|
5
|
-
|
|
6
|
-
#group;
|
|
7
|
-
|
|
8
|
-
// endregion Private fields
|
|
9
|
-
|
|
10
|
-
// region Properties
|
|
11
|
-
|
|
12
|
-
get group() {
|
|
13
|
-
return this.#group;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
set group(value) {
|
|
17
|
-
this.#group = value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// endregion Properties
|
|
21
|
-
|
|
22
|
-
constructor(group) {
|
|
23
|
-
this.#group = group;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
1
|
+
/**/
|
|
2
|
+
|
|
3
|
+
export default class TestGroup {
|
|
4
|
+
// region Private fields
|
|
5
|
+
|
|
6
|
+
#group;
|
|
7
|
+
|
|
8
|
+
// endregion Private fields
|
|
9
|
+
|
|
10
|
+
// region Properties
|
|
11
|
+
|
|
12
|
+
get group() {
|
|
13
|
+
return this.#group;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
set group(value) {
|
|
17
|
+
this.#group = value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// endregion Properties
|
|
21
|
+
|
|
22
|
+
constructor(group) {
|
|
23
|
+
this.#group = group;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/system/TestResult.js
CHANGED
|
@@ -19,9 +19,19 @@ export default class TestResult {
|
|
|
19
19
|
#expectedText;
|
|
20
20
|
#actualText;
|
|
21
21
|
|
|
22
|
+
#thrown;
|
|
23
|
+
#trouble;
|
|
24
|
+
|
|
25
|
+
#thrownText;
|
|
26
|
+
#troubleText;
|
|
27
|
+
|
|
28
|
+
#wasThrowTest;
|
|
29
|
+
|
|
30
|
+
#doesHaveThrow;
|
|
31
|
+
#doesHaveTrouble;
|
|
32
|
+
|
|
22
33
|
#didPass;
|
|
23
34
|
#actual;
|
|
24
|
-
#thrown;
|
|
25
35
|
|
|
26
36
|
// endregion Fields
|
|
27
37
|
|
|
@@ -91,19 +101,39 @@ export default class TestResult {
|
|
|
91
101
|
this.#didPass = value;
|
|
92
102
|
}
|
|
93
103
|
|
|
104
|
+
get wasThrowTest() {
|
|
105
|
+
return this.#wasThrowTest;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get doesHaveThrow() {
|
|
109
|
+
return this.#doesHaveThrow;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get doesHaveTrouble() {
|
|
113
|
+
return this.#doesHaveTrouble;
|
|
114
|
+
}
|
|
115
|
+
|
|
94
116
|
get anyThrow() {
|
|
95
117
|
return this.#thrown;
|
|
96
118
|
}
|
|
97
119
|
|
|
98
|
-
|
|
99
|
-
this.#
|
|
120
|
+
get anyTrouble() {
|
|
121
|
+
return this.#trouble;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get anyThrowText() {
|
|
125
|
+
return this.#thrownText;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get anyTroubleText() {
|
|
129
|
+
return this.#troubleText;
|
|
100
130
|
}
|
|
101
131
|
|
|
102
132
|
// endregion Outcome properties
|
|
103
133
|
|
|
104
134
|
// region Construction
|
|
105
135
|
|
|
106
|
-
constructor(test) {
|
|
136
|
+
constructor(test) /* ok */ {
|
|
107
137
|
this.#test = test;
|
|
108
138
|
this.#displayer = new TotalDisplayer();
|
|
109
139
|
}
|
|
@@ -112,56 +142,76 @@ export default class TestResult {
|
|
|
112
142
|
|
|
113
143
|
// region Before run: setNature() and dependencies
|
|
114
144
|
|
|
115
|
-
setNature() {
|
|
145
|
+
setNature() /* passed */ {
|
|
116
146
|
this.#identityText = this.#calculateIdentityText();
|
|
117
147
|
this.#initorsText = this.#calculateInitorsText();
|
|
118
148
|
this.#inputsText = this.#calculateInputsText();
|
|
119
149
|
this.#expectedText = this.#calculateExpectedText();
|
|
150
|
+
this.#wasThrowTest = this.#test.isThrowTest;
|
|
120
151
|
}
|
|
121
152
|
|
|
122
|
-
#calculateIdentityText() {
|
|
153
|
+
#calculateIdentityText() /* verified */ {
|
|
123
154
|
let text = `${ this.test.for } `;
|
|
124
155
|
return text;
|
|
125
156
|
}
|
|
126
157
|
|
|
127
|
-
#calculateInitorsText() {
|
|
158
|
+
#calculateInitorsText() /* verified */ {
|
|
128
159
|
let text = this.#displayItemRow(this.test.with);
|
|
129
160
|
text = `Initors: ${ text }. `;
|
|
130
161
|
return text;
|
|
131
162
|
}
|
|
132
163
|
|
|
133
|
-
#calculateInputsText() {
|
|
164
|
+
#calculateInputsText() /* verified */ {
|
|
134
165
|
let text = this.#displayItemRow(this.test.in);
|
|
135
166
|
text = `Inputs: ${ text }. `;
|
|
136
167
|
return text;
|
|
137
168
|
}
|
|
138
169
|
|
|
139
|
-
#calculateExpectedText() {
|
|
170
|
+
#calculateExpectedText() /* verified */ {
|
|
140
171
|
let text = this.#displaySingleItem(this.test.out);
|
|
141
172
|
text = `Expected: ${ text }. `;
|
|
142
173
|
return text;
|
|
143
174
|
}
|
|
144
|
-
|
|
175
|
+
|
|
145
176
|
// endregion Before run: setNature() and dependencies
|
|
146
177
|
|
|
147
178
|
// region After run: setResults() and dependencies
|
|
148
179
|
|
|
149
|
-
setResults() {
|
|
180
|
+
setResults() /* passed */ {
|
|
150
181
|
// Direct results.
|
|
151
182
|
this.#didPass = this.test.didPass;
|
|
152
183
|
this.#actual = this.test.actual;
|
|
153
|
-
this.#thrown = this.test.anyThrow;
|
|
154
184
|
|
|
185
|
+
this.#thrown = this.test.thrown;
|
|
186
|
+
this.#trouble = this.test.trouble;
|
|
187
|
+
|
|
188
|
+
this.#doesHaveThrow = this.test.doesHaveUnsoughtThrow;
|
|
189
|
+
this.#doesHaveTrouble = this.test.doesHaveTrouble;
|
|
190
|
+
|
|
155
191
|
// Derived displayable results.
|
|
156
192
|
this.#actualText = this.#calculateActualText();
|
|
193
|
+
this.#thrownText = this.#calculateThrownText("Unsought throw in tested code", this.#thrown);
|
|
194
|
+
this.#troubleText = this.#calculateThrownText("Throw in framing code", this.#trouble);
|
|
157
195
|
}
|
|
158
196
|
|
|
159
|
-
#calculateActualText() {
|
|
197
|
+
#calculateActualText() /* verified */ {
|
|
160
198
|
let text = this.#displaySingleItem(this.test.actual);
|
|
161
199
|
text = `Actual: ${ text }. `;
|
|
162
200
|
return text;
|
|
163
201
|
}
|
|
164
202
|
|
|
203
|
+
#calculateThrownText(scope, thrown) /* verified */ {
|
|
204
|
+
// No throw to display.
|
|
205
|
+
if (thrown === undefined) {
|
|
206
|
+
return; // Undefined.
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let text = `${ scope }: ${ this.#test.on.name }: ${ this.#test.runName }: `
|
|
210
|
+
+ `\"${ this.#test.for }\": ${ thrown.message }`;
|
|
211
|
+
|
|
212
|
+
return text;
|
|
213
|
+
}
|
|
214
|
+
|
|
165
215
|
// endregion After run: setResults() and dependencies
|
|
166
216
|
|
|
167
217
|
// region Displaying test args
|
package/system/TestRunner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
/* Runs tests one by one as
|
|
3
|
+
/* Runs tests one by one as TestCaller or equivalent requests them. */
|
|
4
4
|
|
|
5
5
|
import TestDef from "./TestDef.js";
|
|
6
6
|
import TestGroup from "./TestGroup.js";
|
|
@@ -25,6 +25,10 @@ export default class TestRunner {
|
|
|
25
25
|
#numberRun;
|
|
26
26
|
#numberPassed;
|
|
27
27
|
#numberFailed;
|
|
28
|
+
|
|
29
|
+
#thrown;
|
|
30
|
+
#trouble;
|
|
31
|
+
|
|
28
32
|
#allDidPass;
|
|
29
33
|
|
|
30
34
|
// endregion Private fields
|
|
@@ -67,6 +71,14 @@ export default class TestRunner {
|
|
|
67
71
|
this.#numberFailed = value;
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
get thrown() {
|
|
75
|
+
return this.#thrown;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get trouble() {
|
|
79
|
+
return this.#trouble;
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
get allDidPass() {
|
|
71
83
|
return this.#allDidPass;
|
|
72
84
|
}
|
|
@@ -86,6 +98,9 @@ export default class TestRunner {
|
|
|
86
98
|
this.#numberPassed = 0;
|
|
87
99
|
this.#numberFailed = 0;
|
|
88
100
|
|
|
101
|
+
this.#thrown = [];
|
|
102
|
+
this.#trouble = [];
|
|
103
|
+
|
|
89
104
|
this.#allDidPass = true;
|
|
90
105
|
}
|
|
91
106
|
|
|
@@ -101,9 +116,12 @@ export default class TestRunner {
|
|
|
101
116
|
|
|
102
117
|
// Generator for all test results,
|
|
103
118
|
// including groups and summary.
|
|
104
|
-
*
|
|
119
|
+
async * runTests() {
|
|
105
120
|
if (!this.#tests) {
|
|
106
|
-
throw new Error(
|
|
121
|
+
throw new Error(
|
|
122
|
+
"No tests available to run. TestRunner's .tests "
|
|
123
|
+
+ "must be set before an attempt to run is made."
|
|
124
|
+
);
|
|
107
125
|
}
|
|
108
126
|
|
|
109
127
|
// Converting so that property names can be relied on.
|
|
@@ -126,21 +144,24 @@ export default class TestRunner {
|
|
|
126
144
|
|
|
127
145
|
// Each new method / prop name should be a group,
|
|
128
146
|
// and so should the first method for a class.
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
let runName = test.runName;
|
|
148
|
+
|
|
149
|
+
if (runName !== methodGroup.group || atFirstClassMethod) {
|
|
150
|
+
methodGroup.group = runName;
|
|
131
151
|
atFirstClassMethod = false;
|
|
132
152
|
yield methodGroup;
|
|
133
153
|
}
|
|
134
154
|
|
|
135
|
-
let result = this
|
|
155
|
+
let result = await this.#runOneTest(test);
|
|
156
|
+
|
|
136
157
|
yield result;
|
|
137
158
|
}
|
|
138
159
|
|
|
139
160
|
// Iterative returning of final results summary.
|
|
140
|
-
yield this
|
|
161
|
+
yield this.#summarize();
|
|
141
162
|
}
|
|
142
163
|
|
|
143
|
-
runOneTest(test) {
|
|
164
|
+
async #runOneTest(test) {
|
|
144
165
|
// Default outputs. Never left undefined.
|
|
145
166
|
test.didPass = false;
|
|
146
167
|
test.anyThrow = null;
|
|
@@ -151,13 +172,13 @@ export default class TestRunner {
|
|
|
151
172
|
result.setNature();
|
|
152
173
|
|
|
153
174
|
// Actually running the test.
|
|
154
|
-
this.#frame.run(test);
|
|
175
|
+
await this.#frame.run(test);
|
|
155
176
|
|
|
156
177
|
// Gathering facts based on the test run.
|
|
157
178
|
result.setResults();
|
|
158
179
|
|
|
159
180
|
// For later summarizing.
|
|
160
|
-
this.#retainTestResults(
|
|
181
|
+
this.#retainTestResults(result);
|
|
161
182
|
|
|
162
183
|
// Results and test back to the caller.
|
|
163
184
|
return result;
|
|
@@ -165,22 +186,30 @@ export default class TestRunner {
|
|
|
165
186
|
|
|
166
187
|
// region Dependencies of runOneTest()
|
|
167
188
|
|
|
168
|
-
#retainTestResults(
|
|
189
|
+
#retainTestResults(result) {
|
|
169
190
|
this.#numberRun++;
|
|
170
191
|
|
|
171
|
-
if (
|
|
192
|
+
if (result.doesHaveThrow) {
|
|
193
|
+
this.#thrown.push(result.anyThrowText);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (result.doesHaveTrouble) {
|
|
197
|
+
this.#trouble.push(result.anyTroubleText);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (result.didPass) {
|
|
172
201
|
this.#numberPassed++;
|
|
173
202
|
}
|
|
174
203
|
else {
|
|
175
204
|
this.#numberFailed++;
|
|
176
205
|
}
|
|
177
206
|
|
|
178
|
-
this.#allDidPass &&=
|
|
207
|
+
this.#allDidPass &&= result.didPass;
|
|
179
208
|
}
|
|
180
209
|
|
|
181
210
|
// endregion Dependencies of runOneTest()
|
|
182
211
|
|
|
183
|
-
summarize() {
|
|
212
|
+
#summarize() {
|
|
184
213
|
let summary = `Ran ${ this.#numberRun } test${ this.#numberRun !== 1 ? "s" : "" }. `
|
|
185
214
|
+ `${ this.#allDidPass ? "All tests passed. " : "" }`
|
|
186
215
|
+ `${ this.#numberPassed } passed. `
|
|
@@ -188,9 +217,8 @@ export default class TestRunner {
|
|
|
188
217
|
|
|
189
218
|
let anyWereRun = this.#numberRun > 0;
|
|
190
219
|
|
|
191
|
-
return new TestSummary(summary, this.#allDidPass, anyWereRun);
|
|
220
|
+
return new TestSummary(summary, this.#allDidPass, anyWereRun, this.#thrown, this.#trouble);
|
|
192
221
|
}
|
|
193
222
|
|
|
194
223
|
// endregion Running tests
|
|
195
|
-
|
|
196
224
|
}
|