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.
Files changed (38) hide show
  1. package/README.md +195 -338
  2. package/index.js +9 -9
  3. package/package.json +7 -7
  4. package/{public/javascript → system}/ChosenTestFinder.js +2 -2
  5. package/{public/javascript → system}/SpoofClassMethodsFixture.js +38 -22
  6. package/system/SpoofObjectMethodsFixture.js +58 -0
  7. package/{public/javascript → system}/SpoofTuple.js +14 -0
  8. package/system/TestFrame.js +187 -0
  9. package/{public/javascript → system}/TestFrameChooser.js +6 -47
  10. package/{public/javascript → system}/TestFrames.js +232 -180
  11. package/system/TestResult.js +187 -0
  12. package/system/TestStages.js +278 -0
  13. package/{public/javascript → system}/TestTuple.js +126 -18
  14. package/{public/javascript → system}/TotalComparer.js +12 -0
  15. package/system/TotalCopier.js +79 -0
  16. package/system/TotalDisplayer.js +143 -0
  17. package/system/TypeAnalyzer.js +103 -0
  18. package/system/TypeIdentifier.js +70 -0
  19. package/system/Types.js +17 -0
  20. package/public/javascript/SpoofObjectMethodsFixture.js +0 -52
  21. package/public/javascript/TestResult.js +0 -338
  22. /package/{public/javascript → system}/AComparer.js +0 -0
  23. /package/{public/javascript → system}/ASpoofingFixture.js +0 -0
  24. /package/{public/javascript → system}/ATestCaller.js +0 -0
  25. /package/{public/javascript → system}/ATestFinder.js +0 -0
  26. /package/{public/javascript → system}/ATestFixture.js +0 -0
  27. /package/{public/javascript → system}/ATestReporter.js +0 -0
  28. /package/{public/javascript → system}/ATestSource.js +0 -0
  29. /package/{public/javascript → system}/ClassTestGroup.js +0 -0
  30. /package/{public/javascript → system}/LocalCaller.js +0 -0
  31. /package/{public/javascript → system}/MethodTestGroup.js +0 -0
  32. /package/{public/javascript → system}/Moment.js +0 -0
  33. /package/{public/javascript → system}/Risei.js +0 -0
  34. /package/{public/javascript → system}/TerminalReporter.js +0 -0
  35. /package/{public/javascript → system}/TestFinder.js +0 -0
  36. /package/{public/javascript → system}/TestGroup.js +0 -0
  37. /package/{public/javascript → system}/TestRunner.js +0 -0
  38. /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
- // Outcome field is not part of general system.
38
- #actual = null;
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 actual() {
137
- return this.#actual;
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
- set actual(value) {
141
- this.#actual = value;
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
- // endregion Tuple state
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.method = method;
158
- this.inputs = inputs;
159
- this.output = output;
160
- this.source = source;
161
- this.factors = factors;
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
+ }