risei 1.1.0 → 1.1.2
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 +195 -338
- package/index.js +9 -9
- package/package.json +7 -7
- package/{public/javascript → system}/ChosenTestFinder.js +2 -2
- package/{public/javascript → system}/SpoofClassMethodsFixture.js +38 -22
- package/system/SpoofObjectMethodsFixture.js +58 -0
- package/{public/javascript → system}/SpoofTuple.js +14 -0
- package/system/TestFrame.js +187 -0
- package/{public/javascript → system}/TestFrameChooser.js +6 -47
- package/{public/javascript → system}/TestFrames.js +232 -180
- package/system/TestResult.js +187 -0
- package/system/TestStages.js +278 -0
- package/{public/javascript → system}/TestTuple.js +126 -18
- package/{public/javascript → system}/TotalComparer.js +12 -0
- package/system/TotalCopier.js +79 -0
- package/system/TotalDisplayer.js +143 -0
- package/system/TypeAnalyzer.js +103 -0
- package/system/TypeIdentifier.js +70 -0
- package/system/Types.js +17 -0
- package/public/javascript/SpoofObjectMethodsFixture.js +0 -52
- package/public/javascript/TestResult.js +0 -338
- /package/{public/javascript → system}/AComparer.js +0 -0
- /package/{public/javascript → system}/ASpoofingFixture.js +0 -0
- /package/{public/javascript → system}/ATestCaller.js +0 -0
- /package/{public/javascript → system}/ATestFinder.js +0 -0
- /package/{public/javascript → system}/ATestFixture.js +0 -0
- /package/{public/javascript → system}/ATestReporter.js +0 -0
- /package/{public/javascript → system}/ATestSource.js +0 -0
- /package/{public/javascript → system}/ClassTestGroup.js +0 -0
- /package/{public/javascript → system}/LocalCaller.js +0 -0
- /package/{public/javascript → system}/MethodTestGroup.js +0 -0
- /package/{public/javascript → system}/Moment.js +0 -0
- /package/{public/javascript → system}/Risei.js +0 -0
- /package/{public/javascript → system}/TerminalReporter.js +0 -0
- /package/{public/javascript → system}/TestFinder.js +0 -0
- /package/{public/javascript → system}/TestGroup.js +0 -0
- /package/{public/javascript → system}/TestRunner.js +0 -0
- /package/{public/javascript → system}/TestSummary.js +0 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**/
|
|
2
|
+
|
|
3
|
+
import { TestTuple } from "./TestTuple.js";
|
|
4
|
+
import { TotalComparer } from "./TotalComparer.js";
|
|
5
|
+
import { TotalCopier } from "./TotalCopier.js";
|
|
6
|
+
|
|
7
|
+
export class TestStages {
|
|
8
|
+
/* &cruft, implement all of these under test, and eventually factor them */
|
|
9
|
+
|
|
10
|
+
/* These methods hold operations carried out in the test frame,
|
|
11
|
+
or before / after all test frames, in the test runner.
|
|
12
|
+
Not necessarily all test steps are found here. */
|
|
13
|
+
|
|
14
|
+
static #comparer = new TotalComparer();
|
|
15
|
+
static #copier = new TotalCopier();
|
|
16
|
+
|
|
17
|
+
setLocalInitorsAndInputs(test) /* passed */ {
|
|
18
|
+
/* Sets local__ of each to a copy of the original,
|
|
19
|
+
so any mutated args aren't collapsed forward. */
|
|
20
|
+
|
|
21
|
+
test.localInitors = TestStages.#copier.copy(test.with);
|
|
22
|
+
test.localInputs = TestStages.#copier.copy(test.in);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
anyPreTargetingGroundwork(test) {
|
|
26
|
+
// Spoofing of class members here.
|
|
27
|
+
// Setting any static properties here.
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setLocalTarget(test) /* passed */ {
|
|
31
|
+
/* Target may be instance, prototype for constructors,
|
|
32
|
+
the class itself for statics, or a nonce for either
|
|
33
|
+
static or instance properties. */
|
|
34
|
+
|
|
35
|
+
if (test.isConstructorTest) {
|
|
36
|
+
test.localTarget = test.on.prototype;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let callable = TestTuple.plainNameOf(test.of);
|
|
41
|
+
|
|
42
|
+
let target = test.isInstanceTest
|
|
43
|
+
? this.#supplyInstanceLocalTarget(test, callable)
|
|
44
|
+
: this.#supplyStaticLocalTarget(test, callable);
|
|
45
|
+
|
|
46
|
+
test.localTarget = target;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// region Dependencies of setLocalTarget()
|
|
50
|
+
|
|
51
|
+
#supplyStaticLocalTarget(test, callable) /* verified */ {
|
|
52
|
+
let type = test.on;
|
|
53
|
+
|
|
54
|
+
// Methods are called on class directly.
|
|
55
|
+
// Properties are called on class in nonce.
|
|
56
|
+
let target = test.isMethodTest
|
|
57
|
+
? type
|
|
58
|
+
: { localCallable: () => { return type[callable]; } };
|
|
59
|
+
|
|
60
|
+
return target;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#supplyInstanceLocalTarget(test, callable) /* verified */ {
|
|
64
|
+
let instance = new test.on.prototype.constructor(...test.localInitors);
|
|
65
|
+
|
|
66
|
+
// Methods are called on instance directly.
|
|
67
|
+
// Properties are called on instance in nonce.
|
|
68
|
+
let target = test.isMethodTest
|
|
69
|
+
? instance
|
|
70
|
+
: { localCallable: () => { return instance[callable]; } };
|
|
71
|
+
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// endregion Dependencies of setLocalTarget()
|
|
76
|
+
|
|
77
|
+
setLocalCallable(test) /* passed */ {
|
|
78
|
+
/* Method under test is a method named in test,
|
|
79
|
+
or a nonce method for a property test. */
|
|
80
|
+
|
|
81
|
+
test.localCallable = test.isMethodTest
|
|
82
|
+
? TestTuple.plainNameOf(test.method)
|
|
83
|
+
: TestTuple.nonceLocalCallableName;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
anyPostTargetingGroundwork(test) {
|
|
87
|
+
// Setting instance properties.
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
anyModifyActual(test) /* passed */ {
|
|
91
|
+
/* Original test.actual is used if a normal test, but replaced with .thrown
|
|
92
|
+
if a throw test, or result of supplier method if a retrieval test. */
|
|
93
|
+
|
|
94
|
+
if (test.isThrowTest) {
|
|
95
|
+
// May be undefined, if no throw.
|
|
96
|
+
test.actual = test.thrown;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (test.isRetrievalTest) {
|
|
100
|
+
test.actual = this.supplyNonReturnActual(test);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
compareResults(expected, actual) /* passed */ {
|
|
105
|
+
return TestStages.#comparer.compare(expected, actual);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
anyGroundworkReversion(test) {
|
|
109
|
+
// Unspoofing.
|
|
110
|
+
// Unsetting static properties.
|
|
111
|
+
// Undoing any other groundwork changes made.
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
// region Dependencies of test stages
|
|
116
|
+
|
|
117
|
+
#anyOriginalPropertyGetting(target, settables) {
|
|
118
|
+
if (!Array.isArray(settables)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let unsettables = [ ];
|
|
123
|
+
|
|
124
|
+
for (let settable of settables) {
|
|
125
|
+
// Not a setter tuple.
|
|
126
|
+
if (!settable.of) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Retaining original.
|
|
131
|
+
let original = target[settable.of];
|
|
132
|
+
let unsettable = { of: settable.of, as: original };
|
|
133
|
+
unsettables.push(unsettable);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return unsettables;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#anyPropertySetting(target, settables) {
|
|
140
|
+
// No properties to set.
|
|
141
|
+
if (!Array.isArray(settables)) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (let settable of settables) {
|
|
146
|
+
// Not a setter tuple.
|
|
147
|
+
if (!settable.of) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Actually setting property.
|
|
152
|
+
target[settable.of] = settable.as;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#anyPropertyUnsetting(target, unsettables) {
|
|
157
|
+
// Unsetting is the same as setting,
|
|
158
|
+
// but with tuples of original values.
|
|
159
|
+
return this.#anyPropertySetting(target, unsettables);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#compare(expected, actual) {
|
|
163
|
+
return this.#comparer.compare(expected, actual);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
supplyNonReturnActual(test) {
|
|
167
|
+
// When there is a .from that's a string,
|
|
168
|
+
// the actual is the named target member.
|
|
169
|
+
if (typeof test.from === "string") {
|
|
170
|
+
/* &cruft, maybe explain how this differs from setting .localTarget,
|
|
171
|
+
which can be a nonce, or else maybe make them the same */
|
|
172
|
+
|
|
173
|
+
// Common member host;
|
|
174
|
+
// undefined if static.
|
|
175
|
+
let host = test.target;
|
|
176
|
+
|
|
177
|
+
// When .and defines static.
|
|
178
|
+
if (test.isStaticTest) {
|
|
179
|
+
host = test.on;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return host[test.from];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* &cruft, possibly change all .from syntax to (actual, test),
|
|
186
|
+
or maybe to (test), or else switch back args here */
|
|
187
|
+
// When there is a .from that's a function,
|
|
188
|
+
// the actual is the result of calling it,
|
|
189
|
+
// given everything that might be needed.
|
|
190
|
+
if (test.from instanceof Function) {
|
|
191
|
+
return test.from(test.actual, test.localTarget);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* &cruft, probably drop this, change maybe to a throw */
|
|
195
|
+
// When there is any other .from, the actual is the
|
|
196
|
+
// value of the code element provided as .from.
|
|
197
|
+
return test.from;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// endregion Dependencies of test stages
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
/* &cruft, remove or use this code... */
|
|
204
|
+
|
|
205
|
+
// anyOriginalPropertyGetting(target, settables) {
|
|
206
|
+
// if (!Array.isArray(settables)) {
|
|
207
|
+
// return;
|
|
208
|
+
// }
|
|
209
|
+
//
|
|
210
|
+
// let unsettables = [ ];
|
|
211
|
+
//
|
|
212
|
+
// for (let settable of settables) {
|
|
213
|
+
// // Not a setter tuple.
|
|
214
|
+
// if (!settable.of) {
|
|
215
|
+
// continue;
|
|
216
|
+
// }
|
|
217
|
+
//
|
|
218
|
+
// // Retaining original.
|
|
219
|
+
// let original = target[settable.of];
|
|
220
|
+
// let unsettable = { of: settable.of, as: original };
|
|
221
|
+
// unsettables.push(unsettable);
|
|
222
|
+
// }
|
|
223
|
+
//
|
|
224
|
+
// return unsettables;
|
|
225
|
+
// }
|
|
226
|
+
//
|
|
227
|
+
// anyPropertySetting(target, settables) {
|
|
228
|
+
// // No properties to set.
|
|
229
|
+
// if (!Array.isArray(settables)) {
|
|
230
|
+
// return;
|
|
231
|
+
// }
|
|
232
|
+
//
|
|
233
|
+
// for (let settable of settables) {
|
|
234
|
+
// // Not a setter tuple.
|
|
235
|
+
// if (!settable.of) {
|
|
236
|
+
// continue;
|
|
237
|
+
// }
|
|
238
|
+
//
|
|
239
|
+
// // Actually setting property.
|
|
240
|
+
// target[settable.of] = settable.as;
|
|
241
|
+
// }
|
|
242
|
+
// }
|
|
243
|
+
//
|
|
244
|
+
// anyPropertyUnsetting(target, unsettables) {
|
|
245
|
+
// // Unsetting is the same as setting,
|
|
246
|
+
// // but with tuples of original values.
|
|
247
|
+
// return this.anyPropertySetting(target, unsettables);
|
|
248
|
+
// }
|
|
249
|
+
//
|
|
250
|
+
// supplyNonReturnActual(test, target) {
|
|
251
|
+
// // When there is a .from that's a string,
|
|
252
|
+
// // the actual is the named target member.
|
|
253
|
+
// if (typeof test.from === "string") {
|
|
254
|
+
// // Common member host;
|
|
255
|
+
// // undefined if static.
|
|
256
|
+
// let host = target;
|
|
257
|
+
//
|
|
258
|
+
// // When .and defines static.
|
|
259
|
+
// if (test.isStaticTest) {
|
|
260
|
+
// host = test.on;
|
|
261
|
+
// }
|
|
262
|
+
//
|
|
263
|
+
// return host[test.from];
|
|
264
|
+
// }
|
|
265
|
+
//
|
|
266
|
+
// // When there is a .from that's a function,
|
|
267
|
+
// // the actual is the result of calling it,
|
|
268
|
+
// // given everything that might be needed.
|
|
269
|
+
// if (test.from instanceof Function) {
|
|
270
|
+
// return test.from(target, test);
|
|
271
|
+
// }
|
|
272
|
+
//
|
|
273
|
+
// // When there is any other .from, the actual is the
|
|
274
|
+
// // value of the code element provided as .from.
|
|
275
|
+
// return test.from;
|
|
276
|
+
// }
|
|
277
|
+
|
|
278
|
+
}
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
+
import { TypeAnalyzer } from "./TypeAnalyzer.js";
|
|
4
|
+
|
|
3
5
|
/* Defines what is found in a test-definition tuple (TDT). Actual TDTs don't need to be instances of this class. */
|
|
4
6
|
|
|
5
7
|
export class TestTuple {
|
|
8
|
+
// region Definitions
|
|
9
|
+
|
|
10
|
+
static staticName = "static";
|
|
11
|
+
static throwName = "throw";
|
|
12
|
+
static constructorName = "constructor";
|
|
13
|
+
static nonceLocalCallableName = "localCallable";
|
|
14
|
+
static dotOperator = ".";
|
|
15
|
+
static colonOperator = ":";
|
|
16
|
+
static parensOperator = "()";
|
|
17
|
+
static operatorsRegExp = /\.|:|\(\)/g;
|
|
18
|
+
|
|
19
|
+
// endregion Definitions
|
|
20
|
+
|
|
6
21
|
// region Static fields
|
|
7
22
|
|
|
8
23
|
static longsByShort = new Map([
|
|
@@ -11,6 +26,7 @@ export class TestTuple {
|
|
|
11
26
|
[ "with", "initors" ],
|
|
12
27
|
[ "of", "method" ],
|
|
13
28
|
[ "plus", "spoofed" ],
|
|
29
|
+
[ "amid", "settables" ],
|
|
14
30
|
[ "in", "inputs" ],
|
|
15
31
|
[ "out", "output" ],
|
|
16
32
|
[ "from", "source" ],
|
|
@@ -19,6 +35,12 @@ export class TestTuple {
|
|
|
19
35
|
|
|
20
36
|
// endregion Static fields
|
|
21
37
|
|
|
38
|
+
// region Private fields
|
|
39
|
+
|
|
40
|
+
#typeAnalyzer;
|
|
41
|
+
|
|
42
|
+
// endregion Private fields
|
|
43
|
+
|
|
22
44
|
// region Fields
|
|
23
45
|
|
|
24
46
|
/* These longer names match the constructor-arg names.
|
|
@@ -29,13 +51,23 @@ export class TestTuple {
|
|
|
29
51
|
initors;
|
|
30
52
|
method;
|
|
31
53
|
spoofed;
|
|
54
|
+
settables;
|
|
32
55
|
inputs;
|
|
33
56
|
output;
|
|
34
57
|
source;
|
|
35
58
|
factors;
|
|
36
59
|
|
|
37
|
-
|
|
38
|
-
|
|
60
|
+
/* &cruft, possibly drop defaults when properties settable in tests */
|
|
61
|
+
// These fields are not part of collapse-forward system:
|
|
62
|
+
// Local copies of args; local test targets; outcome fields.
|
|
63
|
+
localInitors = [ ];
|
|
64
|
+
localInputs = [ ];
|
|
65
|
+
|
|
66
|
+
localTarget;
|
|
67
|
+
localMethod;
|
|
68
|
+
|
|
69
|
+
actual;
|
|
70
|
+
thrown;
|
|
39
71
|
|
|
40
72
|
// endregion Fields
|
|
41
73
|
|
|
@@ -84,6 +116,15 @@ export class TestTuple {
|
|
|
84
116
|
set plus(value) {
|
|
85
117
|
this.spoofed = value;
|
|
86
118
|
}
|
|
119
|
+
|
|
120
|
+
/* &cruft, rename this .amid property pair */
|
|
121
|
+
get amid() {
|
|
122
|
+
return this.settables;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
set amid(value) {
|
|
126
|
+
this.settables = value;
|
|
127
|
+
}
|
|
87
128
|
|
|
88
129
|
get in() {
|
|
89
130
|
return this.inputs;
|
|
@@ -119,7 +160,7 @@ export class TestTuple {
|
|
|
119
160
|
|
|
120
161
|
// endregion Test definition, short names
|
|
121
162
|
|
|
122
|
-
// region Tuple state
|
|
163
|
+
// region Tuple state and dependencies
|
|
123
164
|
|
|
124
165
|
get isRunnable() {
|
|
125
166
|
let isRunnable
|
|
@@ -133,15 +174,71 @@ export class TestTuple {
|
|
|
133
174
|
return isRunnable;
|
|
134
175
|
}
|
|
135
176
|
|
|
136
|
-
get
|
|
137
|
-
return this
|
|
177
|
+
get isInstanceTest() /* passed */ {
|
|
178
|
+
return !this.isStaticTest || this.isConstructorTest;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
get isStaticTest() /* passed */ {
|
|
182
|
+
let does = this.andStringContains(TestTuple.staticName);
|
|
183
|
+
return does;
|
|
138
184
|
}
|
|
139
185
|
|
|
140
|
-
|
|
141
|
-
|
|
186
|
+
get isThrowTest() /* passed */ {
|
|
187
|
+
let is = this.andStringContains(TestTuple.throwName);
|
|
188
|
+
return is;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Needed externally, and dependency
|
|
192
|
+
of all .and-based properties. */
|
|
193
|
+
andStringContains(keyword) /* passed */ {
|
|
194
|
+
let doesContain
|
|
195
|
+
= typeof this.and === "string"
|
|
196
|
+
&& this.and.includes(keyword);
|
|
197
|
+
|
|
198
|
+
return doesContain;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get isConstructorTest() /* passed */ {
|
|
202
|
+
let plainTarget = TestTuple.plainNameOf(this.of);
|
|
203
|
+
return plainTarget === TestTuple.constructorName;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get isMethodTest() /* passed */ {
|
|
207
|
+
return !this.isPropertyTest;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get isPropertyTest() /* passed */ {
|
|
211
|
+
let plainName = TestTuple.plainNameOf(this.of);
|
|
212
|
+
|
|
213
|
+
let isPropertyTest
|
|
214
|
+
= this.of.startsWith(TestTuple.dotOperator)
|
|
215
|
+
|| this.of.endsWith(TestTuple.colonOperator)
|
|
216
|
+
|| this.#typeAnalyzer.memberIsProperty(plainName);
|
|
217
|
+
|
|
218
|
+
return isPropertyTest;
|
|
142
219
|
}
|
|
143
220
|
|
|
144
|
-
|
|
221
|
+
get isRetrievalTest() /* passed */ {
|
|
222
|
+
// Falsy code elements never are a retrieval.
|
|
223
|
+
if (!this.from) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// If a (non-falsy) string, must be a retrieval.
|
|
228
|
+
if (typeof this.from === "string") {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// If a function, is a retrieval.
|
|
233
|
+
if (this.from instanceof Function) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Anything else is not a retrieval.
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// endregion Tuple state and dependencies
|
|
145
242
|
|
|
146
243
|
// endregion Properties
|
|
147
244
|
|
|
@@ -149,16 +246,19 @@ export class TestTuple {
|
|
|
149
246
|
|
|
150
247
|
/* Constructor can't use param names like `for`, because when not .-prefixed, they are keywords. */
|
|
151
248
|
|
|
152
|
-
constructor(nature, type, spoofed, initors, method, inputs, output, source, factors) {
|
|
153
|
-
this.nature = nature;
|
|
154
|
-
this.type = type;
|
|
155
|
-
this.spoofed = spoofed;
|
|
156
|
-
this.initors = initors;
|
|
157
|
-
this.
|
|
158
|
-
this.
|
|
159
|
-
this.
|
|
160
|
-
this.
|
|
161
|
-
this.
|
|
249
|
+
constructor(nature, type, spoofed, initors, settables, method, inputs, output, source, factors) {
|
|
250
|
+
this.nature = nature; // .for
|
|
251
|
+
this.type = type; // .on
|
|
252
|
+
this.spoofed = spoofed; // .plus
|
|
253
|
+
this.initors = initors; // .with
|
|
254
|
+
this.settables = settables; // .amid
|
|
255
|
+
this.method = method; // .of
|
|
256
|
+
this.inputs = inputs; // .in
|
|
257
|
+
this.output = output; // .out
|
|
258
|
+
this.source = source; // .from
|
|
259
|
+
this.factors = factors; // .and
|
|
260
|
+
|
|
261
|
+
this.#typeAnalyzer = new TypeAnalyzer(this.type);
|
|
162
262
|
}
|
|
163
263
|
|
|
164
264
|
static fromNonceTuples(nonces) {
|
|
@@ -252,4 +352,12 @@ export class TestTuple {
|
|
|
252
352
|
|
|
253
353
|
// endregion Initing, including constructor()
|
|
254
354
|
|
|
355
|
+
// region Other statics
|
|
356
|
+
|
|
357
|
+
static plainNameOf(name) /* passed */ {
|
|
358
|
+
let plain = name.replaceAll(TestTuple.operatorsRegExp, "");
|
|
359
|
+
return plain;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// endregion Other statics
|
|
255
363
|
}
|
|
@@ -36,6 +36,10 @@ export class TotalComparer extends AComparer {
|
|
|
36
36
|
return this.#compareFunctions(expected, actual);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
if (expected instanceof Date) {
|
|
40
|
+
return this.#compareDates(expected, actual);
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
if (expected instanceof Object) {
|
|
40
44
|
/* Traverse / recurse to leaves. */
|
|
41
45
|
return this.#recurseOverObjects(expected, actual);
|
|
@@ -223,6 +227,14 @@ export class TotalComparer extends AComparer {
|
|
|
223
227
|
return expectedCode === actualCode;
|
|
224
228
|
}
|
|
225
229
|
|
|
230
|
+
#compareDates(expected, actual) /* verified */ {
|
|
231
|
+
if (!(actual instanceof Date)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return expected.valueOf() === actual.valueOf();
|
|
236
|
+
}
|
|
237
|
+
|
|
226
238
|
// endregion Dependencies of compare()
|
|
227
239
|
|
|
228
240
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**/
|
|
2
|
+
|
|
3
|
+
export class TotalCopier {
|
|
4
|
+
copy(original) /* passed */ {
|
|
5
|
+
return this.recursiveCopy(original);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
recursiveCopy(original) /* verified */ {
|
|
9
|
+
/* &cruft, factor, possibly abstracting similar code,
|
|
10
|
+
and/or return copy only once, or similar */
|
|
11
|
+
|
|
12
|
+
/* Algorithm: First copied at current level by value if a value, by reference if
|
|
13
|
+
not a value; then recursively replaced at next level, and so on. */
|
|
14
|
+
|
|
15
|
+
let copy;
|
|
16
|
+
|
|
17
|
+
if (Array.isArray(original)) {
|
|
18
|
+
copy = [ ];
|
|
19
|
+
|
|
20
|
+
// Traversal construction with recursion.
|
|
21
|
+
for (let item of original) {
|
|
22
|
+
let next = this.recursiveCopy(item);
|
|
23
|
+
copy.push(next);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return copy;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (original instanceof Map) {
|
|
30
|
+
copy = new Map();
|
|
31
|
+
|
|
32
|
+
// Traversal construction with recursion.
|
|
33
|
+
for (let entry of original.entries()) {
|
|
34
|
+
let next = this.recursiveCopy(entry);
|
|
35
|
+
copy.set(next[0], next[1]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return copy;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (original instanceof Set) {
|
|
42
|
+
copy = new Set();
|
|
43
|
+
|
|
44
|
+
// Traversal construction with recursion.
|
|
45
|
+
for (let value of original.values()) {
|
|
46
|
+
let next = this.recursiveCopy(value);
|
|
47
|
+
copy.add(next);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return copy;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (original instanceof Date) {
|
|
54
|
+
copy = new Date(original);
|
|
55
|
+
return copy;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (original instanceof Object) {
|
|
59
|
+
copy = { };
|
|
60
|
+
|
|
61
|
+
let keys = Object.keys(original);
|
|
62
|
+
|
|
63
|
+
// Traversal construction with recursion.
|
|
64
|
+
for (let key of Object.keys(original)) {
|
|
65
|
+
let next = this.recursiveCopy(original[key]);
|
|
66
|
+
copy[key] = next;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return copy;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* &cruft, other object types here */
|
|
73
|
+
|
|
74
|
+
// All object types exhausted, so this is a value,
|
|
75
|
+
// which can be used directly for a copy.
|
|
76
|
+
copy = original;
|
|
77
|
+
return copy;
|
|
78
|
+
}
|
|
79
|
+
}
|