risei 1.3.4 → 2.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 +142 -379
- package/Read-me reduced.md +294 -0
- package/Read-me redux.md +581 -0
- package/index.js +5 -5
- package/package.json +10 -11
- package/system/ASpoofingFixture.js +7 -8
- package/system/ATestCaller.js +3 -3
- package/system/ATestFinder.js +2 -2
- package/system/ATestReporter.js +2 -1
- package/system/ATestSource.js +5 -1
- package/system/ChosenTestFinder.js +4 -4
- package/system/ClassTestGroup.js +2 -2
- package/system/LocalCaller.js +5 -5
- package/system/{ClassMethodSpoofer.js → MethodSpoofer.js} +54 -48
- package/system/MethodTestGroup.js +2 -2
- package/system/Moment.js +1 -1
- package/system/NameAnalyzer.js +26 -0
- package/system/PropertySpoofer.js +156 -0
- package/system/Risei.js +5 -5
- package/system/SpoofDef.js +260 -0
- package/system/TerminalReporter.js +8 -8
- package/system/{TestDefinition.js → TestDef.js} +153 -107
- package/system/TestFinder.js +3 -3
- package/system/TestFrame.js +15 -52
- package/system/TestGroup.js +1 -1
- package/system/TestResult.js +2 -2
- package/system/TestRunner.js +23 -107
- package/system/TestStages.js +80 -76
- package/system/TestSummary.js +1 -1
- package/system/TotalComparer.js +60 -11
- package/system/TotalDisplayer.js +33 -12
- package/system/TypeAnalyzer.js +41 -79
- package/system/TypeIdentifier.js +18 -8
- package/system/Types.js +3 -1
- package/test-target-objects/ConditionalThrowTarget.js +11 -0
- package/test-target-objects/Counter.js +46 -0
- package/test-target-objects/DomTarget.js +37 -0
- package/test-target-objects/InterSpoofer.js +230 -0
- package/test-target-objects/MixedContents.js +33 -0
- package/test-target-objects/MutationTarget.js +37 -0
- package/test-target-objects/ObjectComposer.js +34 -0
- package/test-target-objects/PolySpoofableInner.js +29 -0
- package/test-target-objects/PolySpoofableOuter.js +52 -0
- package/test-target-objects/PropertiesTarget.js +98 -0
- package/test-target-objects/Returner.js +7 -0
- package/test-target-objects/Searcher.js +25 -0
- package/test-target-objects/Sorter.js +91 -0
- package/test-target-objects/Spoofable.js +36 -0
- package/test-target-objects/StateTarget.js +34 -0
- package/test-target-objects/StaticTarget.js +17 -0
- package/test-target-objects/TestableTarget.js +57 -0
- package/trial-tests/SelfTests.outward-rt.js +511 -0
- package/trial-tests/TopicTests.outward-rt.js +313 -0
- package/usage-examples/Gold-bar-example.png +0 -0
- package/usage-examples/Title-example.png +0 -0
- package/xternal-tests/ASpoofingFixture.tests.js +242 -0
- package/xternal-tests/MethodSpoofer.tests.js +130 -0
- package/xternal-tests/SpoofDef.tests.js +91 -0
- package/xternal-tests/TotalComparer.tests.js +1055 -0
- package/xternal-tests/package.json +7 -0
- package/system/AComparer.js +0 -9
- package/system/ATestFixture.js +0 -44
- package/system/ClassPropertySpoofer.js +0 -185
- package/system/ObjectMethodSpoofer.js +0 -58
- package/system/ObjectPropertySpoofer.js +0 -136
- package/system/SpoofDefinition.js +0 -243
- package/system/TestFrameChooser.js +0 -54
- package/system/TestFrames.js +0 -232
- package/system/TotalCopier.js +0 -106
package/system/TestRunner.js
CHANGED
|
@@ -2,41 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
/* Runs tests one by one as an ATestCaller requests them. */
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
5
|
+
import TestDef from "./TestDef.js";
|
|
6
|
+
import TestGroup from "./TestGroup.js";
|
|
7
|
+
import ClassTestGroup from "./ClassTestGroup.js";
|
|
8
|
+
import MethodTestGroup from "./MethodTestGroup.js";
|
|
9
|
+
import TestResult from "./TestResult.js";
|
|
10
|
+
import TestSummary from "./TestSummary.js";
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import { TestFrames } from "./TestFrames.js";
|
|
12
|
+
import TestFrame from "./TestFrame.js";
|
|
14
13
|
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import { SpoofDefinition } from "./SpoofDefinition.js";
|
|
18
|
-
|
|
19
|
-
export class TestRunner {
|
|
20
|
-
// region Static definition fields
|
|
21
|
-
|
|
22
|
-
// Used for a special test structural case;
|
|
23
|
-
// classes have one constructor by this name.
|
|
24
|
-
static #constructorName = "constructor";
|
|
25
|
-
|
|
26
|
-
// endregion Static definition fields
|
|
14
|
+
import MethodSpoofer from "./MethodSpoofer.js";
|
|
15
|
+
import SpoofDef from "./SpoofDef.js";
|
|
27
16
|
|
|
17
|
+
export default class TestRunner {
|
|
28
18
|
// region Private fields
|
|
29
19
|
|
|
30
20
|
// Components and state.
|
|
31
21
|
#tests;
|
|
32
|
-
|
|
33
|
-
#classSpoofer;
|
|
34
|
-
#instanceSpoofer;
|
|
35
|
-
|
|
36
|
-
#frameSource;
|
|
37
|
-
#frameChooser;
|
|
38
|
-
|
|
39
|
-
#comparer;
|
|
22
|
+
#frame;
|
|
40
23
|
|
|
41
24
|
// Results summary.
|
|
42
25
|
#numberRun;
|
|
@@ -48,6 +31,8 @@ export class TestRunner {
|
|
|
48
31
|
|
|
49
32
|
// region Properties
|
|
50
33
|
|
|
34
|
+
/* inputs */
|
|
35
|
+
|
|
51
36
|
get tests() {
|
|
52
37
|
return this.#tests;
|
|
53
38
|
}
|
|
@@ -56,25 +41,7 @@ export class TestRunner {
|
|
|
56
41
|
this.#tests = value;
|
|
57
42
|
}
|
|
58
43
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
get classSpoofer() {
|
|
62
|
-
return this.#classSpoofer;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
set classSpoofer(value) {
|
|
66
|
-
this.#classSpoofer = value;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get instanceSpoofer() {
|
|
70
|
-
return this.#instanceSpoofer;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
set instanceSpoofer(value) {
|
|
74
|
-
this.#instanceSpoofer = value;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// endregion Spoofing
|
|
44
|
+
/* outputs */
|
|
78
45
|
|
|
79
46
|
get numberRun() {
|
|
80
47
|
return this.#numberRun;
|
|
@@ -112,11 +79,8 @@ export class TestRunner {
|
|
|
112
79
|
|
|
113
80
|
constructor() {
|
|
114
81
|
this.#tests = [];
|
|
115
|
-
|
|
116
|
-
this.#
|
|
117
|
-
|
|
118
|
-
this.#frameSource = new TestFrames();
|
|
119
|
-
this.#frameChooser = new TestFrameChooser(this.#frameSource);
|
|
82
|
+
|
|
83
|
+
this.#frame = new TestFrame();
|
|
120
84
|
|
|
121
85
|
this.#numberRun = 0;
|
|
122
86
|
this.#numberPassed = 0;
|
|
@@ -143,7 +107,7 @@ export class TestRunner {
|
|
|
143
107
|
}
|
|
144
108
|
|
|
145
109
|
// Converting so that property names can be relied on.
|
|
146
|
-
this.#tests =
|
|
110
|
+
this.#tests = TestDef.fromNonceTuples(this.tests);
|
|
147
111
|
|
|
148
112
|
// Needed for displaying classes and methods as groups.
|
|
149
113
|
let classGroup = new ClassTestGroup();
|
|
@@ -160,10 +124,10 @@ export class TestRunner {
|
|
|
160
124
|
yield classGroup;
|
|
161
125
|
}
|
|
162
126
|
|
|
163
|
-
// Each new method name should be a group,
|
|
164
|
-
// and so should
|
|
165
|
-
if (test.
|
|
166
|
-
methodGroup.group = test.
|
|
127
|
+
// Each new method / prop name should be a group,
|
|
128
|
+
// and so should the first method for a class.
|
|
129
|
+
if (test.runName !== methodGroup.group || atFirstClassMethod) {
|
|
130
|
+
methodGroup.group = test.runName;
|
|
167
131
|
atFirstClassMethod = false;
|
|
168
132
|
yield methodGroup;
|
|
169
133
|
}
|
|
@@ -181,35 +145,14 @@ export class TestRunner {
|
|
|
181
145
|
test.didPass = false;
|
|
182
146
|
test.anyThrow = null;
|
|
183
147
|
|
|
184
|
-
// Spoofing based on any spoof
|
|
185
|
-
// definitions in .with and .of.
|
|
186
|
-
this.#anyObjectSpoofing(test);
|
|
187
|
-
|
|
188
148
|
// Gathering facts defining the nature of the test,
|
|
189
149
|
// some of which might change after the test is run.
|
|
190
150
|
let result = new TestResult(test);
|
|
191
151
|
result.setNature();
|
|
192
152
|
|
|
193
|
-
// Aspects of the test determine the right test frame,
|
|
194
|
-
// bound back to its class to use private class members.
|
|
195
|
-
let testFrame = this.#frameChooser.supplyTestFrame(test);
|
|
196
|
-
testFrame = testFrame.bind(this.#frameSource);
|
|
197
|
-
|
|
198
|
-
// Done in every test.
|
|
199
|
-
this.#anyMethodSpoofing(test);
|
|
200
|
-
|
|
201
153
|
// Actually running the test.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
catch (thrown) {
|
|
206
|
-
test.anyThrow = thrown;
|
|
207
|
-
test.didPass = false;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Done in every test, whether passed, failed, or thrown.
|
|
211
|
-
this.#anyMethodRestoring(test);
|
|
212
|
-
|
|
154
|
+
this.#frame.run(test);
|
|
155
|
+
|
|
213
156
|
// Gathering facts based on the test run.
|
|
214
157
|
result.setResults();
|
|
215
158
|
|
|
@@ -222,33 +165,6 @@ export class TestRunner {
|
|
|
222
165
|
|
|
223
166
|
// region Dependencies of runOneTest()
|
|
224
167
|
|
|
225
|
-
// Spoofs objects as defined in TestDefinition's
|
|
226
|
-
// .with and .in, which can contain spoofs.
|
|
227
|
-
#anyObjectSpoofing(test) {
|
|
228
|
-
// Spoofing objects may be skipped for self-testing.
|
|
229
|
-
if (typeof test.and === "string") {
|
|
230
|
-
if (test.and.includes("nospoof")) {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Actually spoofing, if any is defined.
|
|
236
|
-
this.#instanceSpoofer.spoof(test);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
#anyMethodSpoofing(test) {
|
|
240
|
-
if (test.plus === undefined) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
this.#classSpoofer.spoof(test);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
#anyMethodRestoring(test) {
|
|
248
|
-
this.#classSpoofer.unspoof();
|
|
249
|
-
this.#classSpoofer.removeDefinitions();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
168
|
#retainTestResults(test) {
|
|
253
169
|
this.#numberRun++;
|
|
254
170
|
|
package/system/TestStages.js
CHANGED
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import TestDef from "./TestDef.js";
|
|
4
|
+
import MethodSpoofer from "./MethodSpoofer.js";
|
|
5
|
+
import PropertySpoofer from "./PropertySpoofer.js";
|
|
6
|
+
import TypeAnalyzer from "./TypeAnalyzer.js";
|
|
7
|
+
import NameAnalyzer from "./NameAnalyzer.js";
|
|
8
|
+
import TotalComparer from "./TotalComparer.js";
|
|
6
9
|
|
|
7
|
-
export class TestStages {
|
|
8
|
-
/* &cruft,
|
|
10
|
+
export default class TestStages {
|
|
11
|
+
/* &cruft, ? factor these to more classes */
|
|
9
12
|
|
|
10
13
|
/* These methods hold operations carried out in the test frame,
|
|
11
14
|
or before / after all test frames, in the test runner.
|
|
12
15
|
Not necessarily all test steps are found here. */
|
|
13
16
|
|
|
17
|
+
static #methodSpoofer = new MethodSpoofer();
|
|
18
|
+
static #propertySpoofer = new PropertySpoofer();
|
|
14
19
|
static #comparer = new TotalComparer();
|
|
15
|
-
static #copier = new TotalCopier();
|
|
16
20
|
|
|
17
|
-
anyPreTargetingGroundwork(test) {
|
|
18
|
-
// Spoofing
|
|
19
|
-
|
|
21
|
+
anyPreTargetingGroundwork(test) /* passed */ {
|
|
22
|
+
// Spoofing methods; properties spoofed later.
|
|
23
|
+
TestStages.#methodSpoofer.spoof(test);
|
|
24
|
+
|
|
25
|
+
// Arbitrary user-defined actions.
|
|
26
|
+
// Only spoofed methods available.
|
|
27
|
+
if (test.doesHaveDoEarly) {
|
|
28
|
+
test.do.early(test);
|
|
29
|
+
}
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
setTarget(test) /* passed */ {
|
|
@@ -25,58 +35,50 @@ export class TestStages {
|
|
|
25
35
|
static or instance properties. */
|
|
26
36
|
|
|
27
37
|
if (test.isConstructorTest) {
|
|
28
|
-
test.target = test.on.prototype;
|
|
38
|
+
test.target = new test.on.prototype.constructor(...test.in);
|
|
29
39
|
return;
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
let callable = TestDefinition.plainNameOf(test.of);
|
|
33
|
-
|
|
34
42
|
let target = test.isInstanceTest
|
|
35
|
-
?
|
|
36
|
-
:
|
|
43
|
+
? new test.on.prototype.constructor(...test.with)
|
|
44
|
+
: test.on;
|
|
37
45
|
|
|
38
46
|
test.target = target;
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
let type = test.on;
|
|
45
|
-
|
|
46
|
-
// Methods are called on class directly.
|
|
47
|
-
// Properties are called on class in nonce.
|
|
48
|
-
let target = test.isMethodTest
|
|
49
|
-
? type
|
|
50
|
-
: { localCallable: () => { return type[callable]; } };
|
|
49
|
+
supplyLocalTarget(test) /* passed */ {
|
|
50
|
+
/* Methods are called on instance / type / prototype directly.
|
|
51
|
+
Properties are called on instance / type in a nonce. */
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
}
|
|
53
|
+
let callable = NameAnalyzer.plainNameOf(test.method);
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let target = test.isMethodTest
|
|
61
|
-
? instance
|
|
62
|
-
: { localCallable: () => { return instance[callable]; } };
|
|
63
|
-
|
|
64
|
-
return target;
|
|
55
|
+
let localTarget = test.isMethodTest
|
|
56
|
+
? test.target
|
|
57
|
+
: { nonce: () => { return test.target[callable]; } };
|
|
58
|
+
|
|
59
|
+
return localTarget;
|
|
65
60
|
}
|
|
66
|
-
|
|
67
|
-
// endregion Dependencies of setTarget()
|
|
68
61
|
|
|
69
|
-
|
|
62
|
+
supplyCallableName(test) /* passed */ {
|
|
70
63
|
/* Method under test is a method named in test,
|
|
71
64
|
or a nonce method for a property test. */
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
?
|
|
75
|
-
:
|
|
66
|
+
let name = test.isMethodTest
|
|
67
|
+
? NameAnalyzer.plainNameOf(test.method)
|
|
68
|
+
: TestDef.nonceLocalCallableName;
|
|
69
|
+
|
|
70
|
+
return name;
|
|
76
71
|
}
|
|
77
72
|
|
|
78
|
-
anyPostTargetingGroundwork(test) {
|
|
79
|
-
//
|
|
73
|
+
anyPostTargetingGroundwork(test) /* passed */ {
|
|
74
|
+
// Spoofing properties; methods spoofed earlier.
|
|
75
|
+
TestStages.#propertySpoofer.spoof(test);
|
|
76
|
+
|
|
77
|
+
// Arbitrary user-defined actions.
|
|
78
|
+
// All spoofs normally available.
|
|
79
|
+
if (test.doesHaveDoLate) {
|
|
80
|
+
test.do.late(test);
|
|
81
|
+
}
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
anyModifyActual(test) /* passed */ {
|
|
@@ -97,52 +99,54 @@ export class TestStages {
|
|
|
97
99
|
return TestStages.#comparer.compare(expected, actual);
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
// Unspoofing.
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
anyGroundworkReversal(test) /* passed */ {
|
|
103
|
+
// Unspoofing methods and properties.
|
|
104
|
+
TestStages.#methodSpoofer.unspoof(test);
|
|
105
|
+
TestStages.#propertySpoofer.unspoof(test);
|
|
106
|
+
|
|
107
|
+
// Arbitrary user-defined operations.
|
|
108
|
+
// No spoofs are available here.
|
|
109
|
+
if (test.doesHaveUndo) {
|
|
110
|
+
test.undo(test);
|
|
111
|
+
}
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
// region Dependencies of test stages
|
|
107
115
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
supplyNonReturnActual(test) {
|
|
113
|
-
// When there is a .from that's a string,
|
|
114
|
-
// the actual is the named target member.
|
|
116
|
+
supplyNonReturnActual(test) /* passed */ {
|
|
117
|
+
// When .from is a string, the actual
|
|
118
|
+
// is the named target or type member.
|
|
115
119
|
if (typeof test.from === "string") {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return host[test.from];
|
|
120
|
+
// Removing any sigil.
|
|
121
|
+
let name = NameAnalyzer.plainNameOf(test.from);
|
|
122
|
+
|
|
123
|
+
// Determining which exposes the property.
|
|
124
|
+
let host = TypeAnalyzer.isInstanceMember(test.type, name)
|
|
125
|
+
? test.target
|
|
126
|
+
: test.type;
|
|
127
|
+
|
|
128
|
+
// Actually supplying value.
|
|
129
|
+
return host[name];
|
|
129
130
|
}
|
|
130
|
-
|
|
131
|
-
/* &cruft, possibly change all .from syntax
|
|
132
|
-
|
|
131
|
+
|
|
132
|
+
/* &cruft, possibly change all .from syntax
|
|
133
|
+
to ? (actual, test), or ? (test) */
|
|
133
134
|
// When there is a .from that's a function,
|
|
134
135
|
// the actual is the result of calling it,
|
|
135
136
|
// given everything that might be needed.
|
|
136
137
|
if (test.from instanceof Function) {
|
|
137
|
-
|
|
138
|
+
// Actually supplying value.
|
|
139
|
+
return test.from(test.target, test);
|
|
138
140
|
}
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// value of the code element provided as .from.
|
|
143
|
-
return test.from;
|
|
142
|
+
// Only property names and functions make sense to support.
|
|
143
|
+
throw new Error("The test.from value was not a usable type. It must be either a property name or a function to work.");
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
#compare(expected, actual) /* verified */ {
|
|
147
|
+
return this.#comparer.compare(expected, actual);
|
|
148
|
+
}
|
|
149
|
+
|
|
146
150
|
// endregion Dependencies of test stages
|
|
147
151
|
|
|
148
152
|
}
|
package/system/TestSummary.js
CHANGED
package/system/TotalComparer.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import ATestSource from "./ATestSource.js";
|
|
4
4
|
|
|
5
|
-
export class TotalComparer
|
|
5
|
+
export default class TotalComparer {
|
|
6
6
|
constructor() {
|
|
7
|
-
|
|
7
|
+
/* No operations. */
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
compare(expected, actual) /* passed */ {
|
|
@@ -20,33 +20,43 @@ export class TotalComparer extends AComparer {
|
|
|
20
20
|
/* Traverse / recurse to leaves. */
|
|
21
21
|
return this.#recurseOverArrays(expected, actual);
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
if (expected instanceof Map) {
|
|
25
25
|
/* Traverse / recurse to leaves. */
|
|
26
26
|
return this.#recurseOverMaps(expected, actual);
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
if (expected instanceof Set) {
|
|
30
30
|
/* Traverse / recurse to leaves. */
|
|
31
31
|
return this.#recurseOverSets(expected, actual);
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
if (expected instanceof Function) {
|
|
35
35
|
/* Leaf to compare directly. */
|
|
36
36
|
return this.#compareFunctions(expected, actual);
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
if (expected instanceof Date) {
|
|
40
40
|
return this.#compareDates(expected, actual);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
if (expected instanceof Error) {
|
|
44
|
+
return this.#compareErrors(expected, actual);
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
if (expected instanceof Object) {
|
|
44
48
|
/* Traverse / recurse to leaves. */
|
|
45
49
|
return this.#recurseOverObjects(expected, actual);
|
|
46
50
|
}
|
|
47
|
-
|
|
48
|
-
/* If
|
|
49
|
-
|
|
51
|
+
|
|
52
|
+
/* If a special symbol to support undefined
|
|
53
|
+
as expected, see if actual is undefined. */
|
|
54
|
+
if (expected === ATestSource.undefSymbol) {
|
|
55
|
+
return this.#compareUndefs(expected, actual);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* If none of the above things,
|
|
59
|
+
compare as a leaf value. */
|
|
50
60
|
return this.#compareValues(expected, actual);
|
|
51
61
|
}
|
|
52
62
|
|
|
@@ -203,6 +213,21 @@ export class TotalComparer extends AComparer {
|
|
|
203
213
|
return false;
|
|
204
214
|
}
|
|
205
215
|
|
|
216
|
+
#compareUndefs(expected, actual) /* verified */ {
|
|
217
|
+
// The rare case of comparing undef symbols.
|
|
218
|
+
if (actual === expected) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// The common case of using the undef symbol
|
|
223
|
+
// to see if the actual is undefined.
|
|
224
|
+
if (actual === undefined) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
206
231
|
#compareFunctions(expected, actual) /* verified */ {
|
|
207
232
|
// `expected` is already known to be a function / method.
|
|
208
233
|
if (!(actual instanceof Function)) {
|
|
@@ -235,6 +260,30 @@ export class TotalComparer extends AComparer {
|
|
|
235
260
|
return expected.valueOf() === actual.valueOf();
|
|
236
261
|
}
|
|
237
262
|
|
|
263
|
+
#compareErrors(expected, actual) /* verified */ {
|
|
264
|
+
/* Compares Errors and subclasses accurately apart from .stack,
|
|
265
|
+
which is ignored because it rarely makes sense to compare it. */
|
|
266
|
+
|
|
267
|
+
// Most common difference.
|
|
268
|
+
if (expected.message != actual.message) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Different Error subtypes.
|
|
273
|
+
if (expected.name !== actual.name) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Recursion for causes, which may not be Errors.
|
|
278
|
+
if (!this.compare(expected.cause, actual.cause)) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// If nothing failed, they are the same
|
|
283
|
+
// (apart, probably from .stack).
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
|
|
238
287
|
// endregion Dependencies of compare()
|
|
239
288
|
|
|
240
289
|
}
|
package/system/TotalDisplayer.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import TypeIdentifier from "./TypeIdentifier.js";
|
|
4
|
+
import Types from "./Types.js";
|
|
5
|
+
import ATestSource from "./ATestSource.js";
|
|
5
6
|
|
|
6
|
-
export class TotalDisplayer {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
constructor() /* verified */ {
|
|
10
|
-
this.#identifier = new TypeIdentifier();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
display(value) /* passed */ {
|
|
14
|
-
let typeId = this.#identifier.identify(value);
|
|
7
|
+
export default class TotalDisplayer {
|
|
8
|
+
display(value) /* good */ {
|
|
9
|
+
let typeId = TypeIdentifier.identify(value);
|
|
15
10
|
|
|
16
11
|
switch (typeId) {
|
|
17
12
|
case Types.isValue: return this.displayValue(value);
|
|
@@ -19,11 +14,13 @@ export class TotalDisplayer {
|
|
|
19
14
|
case Types.isArray: return this.displayArray(value);
|
|
20
15
|
case Types.isMap: return this.displayMap(value);
|
|
21
16
|
case Types.isSet: return this.displaySet(value);
|
|
17
|
+
case Types.isError: return this.displayError(value);
|
|
22
18
|
case Types.isClass: return this.displayClass(value);
|
|
23
19
|
case Types.isFunction: return this.displayFunction(value);
|
|
24
20
|
case Types.isObject: return this.displayObject(value);
|
|
25
21
|
case Types.isDate: return this.displayValue(value);
|
|
26
22
|
case Types.isUndefined: return this.displayUndefined(value);
|
|
23
|
+
case Types.isUndefSymbol: return this.displayUndefined(value);
|
|
27
24
|
case Types.isNull: return this.displayNull(value);
|
|
28
25
|
}
|
|
29
26
|
}
|
|
@@ -85,6 +82,30 @@ export class TotalDisplayer {
|
|
|
85
82
|
return output;
|
|
86
83
|
}
|
|
87
84
|
|
|
85
|
+
displayError(value) /* passed */ {
|
|
86
|
+
// Subtype can't be found with typeof.
|
|
87
|
+
let type = value.name;
|
|
88
|
+
|
|
89
|
+
// No "" when no message.
|
|
90
|
+
let message = value.message !== ""
|
|
91
|
+
? `\"${ value.message }\"`
|
|
92
|
+
: "";
|
|
93
|
+
|
|
94
|
+
// Inter-property punctuation only when needed.
|
|
95
|
+
let inter
|
|
96
|
+
= value.message !== "" && value.cause !== undefined
|
|
97
|
+
? ", "
|
|
98
|
+
: "";
|
|
99
|
+
|
|
100
|
+
// Normally an Error / subtype, but not necessarily.
|
|
101
|
+
let cause = value.cause !== undefined
|
|
102
|
+
? `cause:${ this.display(value.cause) }`
|
|
103
|
+
: "";
|
|
104
|
+
|
|
105
|
+
let output = `${ type }{${ message }${ inter }${ cause }}`;
|
|
106
|
+
return output;
|
|
107
|
+
}
|
|
108
|
+
|
|
88
109
|
displayClass(value) /* passed */ {
|
|
89
110
|
// Class definitions have a .name.
|
|
90
111
|
let output = value.name;
|
|
@@ -124,7 +145,7 @@ export class TotalDisplayer {
|
|
|
124
145
|
}
|
|
125
146
|
|
|
126
147
|
displayUndefined(value) /* passed */ {
|
|
127
|
-
return "
|
|
148
|
+
return "undef";
|
|
128
149
|
}
|
|
129
150
|
|
|
130
151
|
displayNull(value) /* passed */ {
|