risei 3.3.3 → 3.3.4

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 CHANGED
@@ -13,6 +13,8 @@ Risei does all this by replacing hand-coded tests with simple declarative syntax
13
13
 
14
14
  You can find a longer version of this read-me at [https://deusware.com/risei](https://deusware.com/risei/index.html).  It expands greatly on the information here.
15
15
 
16
+ Risei's major features are now complete, but new enhancements or fixes may appear from time to time.  For release notes, see the [version history](https://deusware.com/risei/index.html#version-history).
17
+
16
18
 
17
19
 
18
20
  ## Examples
@@ -44,31 +46,26 @@ And they have a summary bar at the bottom:
44
46
 
45
47
 
46
48
 
47
- ## Status
48
-
49
- Risei's major features are now complete, but new enhancements or fixes may appear from time to time.
50
-
51
- The latest release, **3.3.3**, adds `async` capabilities to poly-testing, the last place they were missing.
52
-
53
-
54
-
55
49
  ## Features of Risei
56
50
 
57
- - #### Declarative syntax to write tests simply and quickly.    ►  [Writing tests](https://deusware.com/risei/index.html#writing-tests)  (basics below)
58
- - #### Easy-to-read test definitions and test outputs.    ►  [Test and output examples](https://deusware.com/risei/index.html#examples)  (also above)
59
- - #### Even less to write by stating reused test properties only once.    ►  [Collapsing forward](https://deusware.com/risei/index.html#collapsing-forward)  (basics below)
60
- - #### Built-in declarative syntax to fake test-time values from dependencies.    ►  [Spoofing using `.plus`](https://deusware.com/risei/index.html#spoofing)  (basics below)
61
- - #### Full support for `async` code with no special syntax needed.    ►  [Writing tests](https://deusware.com/risei/index.html#writing-tests)  (basics below)
62
- - #### Testing properties and methods, static and instance members all the same way.    ►  [Properties and statics](https://deusware.com/risei/index.html#testing-properties-and-static-methods)
63
- - #### Testing `throw` paths effortlessly.    ►  [Using `.and: "throws"`](https://deusware.com/risei/index.html#using-and-throws)
64
- - #### Deriving actual values to test from raw outputs or property retrieval.    ►  [Using `.from`](https://deusware.com/risei/index.html#using-from)
65
- - #### Setting up and tearing down arbitrary test state.    ►  [Using `.do` and `.undo`](https://deusware.com/risei/index.html#using-do-and-undo)
66
- - #### Testing for `undefined` as output.    ►  [Using `this.undef`](https://deusware.com/risei/index.html#using-undef)
67
- - #### Running a method repeatedly in one test.    ►  [Using `.and: "poly"`](https://deusware.com/risei/index.html#using-and-poly)
51
+ Click ▼ / ▲ to see sections here, or the ► links to see full explanations on Risei's website.
52
+
53
+ - #### Easy declarative syntax to write tests simply and quickly.  [▼](#writing-tests)  [► Writing tests](https://deusware.com/risei/index.html#writing-tests)
54
+ - #### Easy-to-read test definitions and outputs.  [▲](#examples)  [► Examples](https://deusware.com/risei/index.html#examples)
55
+ - #### Simple reuse of test properties.  [▼](#property-reuse-with-collapsing-forward)  [► Collapsing forward](https://deusware.com/risei/index.html#collapsing-forward)
56
+ - #### Built-in declarative dependency faking.  [▼](#test-isolation-with-spoofing)  [► Spoofing using `.plus`](https://deusware.com/risei/index.html#spoofing)
57
+ - #### Full support for `async` code without special syntax.  [▼](#writing-tests)  [► Writing tests](https://deusware.com/risei/index.html#writing-tests)
58
+ - #### Consistent syntax for all callable code.  [▼](#writing-tests)  [► Properties and statics](https://deusware.com/risei/index.html#testing-properties-and-static-methods)
59
+ - #### Testing exception / error paths effortlessly.   [► Using `.and: "throws"`](https://deusware.com/risei/index.html#using-and-throws)
60
+ - #### Deriving values to test indirectly.   [► Using `.from`](https://deusware.com/risei/index.html#using-from)
61
+ - #### Setting up and tearing down arbitrary test state.   [► Using `.do` and `.undo`](https://deusware.com/risei/index.html#using-do-and-undo)
62
+ - #### Testing for `undefined` as output.   [► Using `this.undef`](https://deusware.com/risei/index.html#using-undef)
63
+ - #### Running a method repeatedly in one test.   [► Using `.and: "poly"`](https://deusware.com/risei/index.html#using-and-poly)
68
64
 
69
65
  - And more!  Check out the full [Risei home page](https://deusware.com/risei).
70
66
 
71
67
 
68
+
72
69
  ## Installation
73
70
 
74
71
  Install Risei for development time only:
@@ -91,11 +88,13 @@ And add Risei's metadata to `package.json`:
91
88
  }
92
89
  ```
93
90
 
91
+
94
92
  ## Testing Risei Itself
95
93
 
96
94
  To test Risei itself, you clone it from a parallel [repository](https://gitlab.com/riseimaker/risei-public.git), install its dependencies, and run its self-tests.  See the full explanation [here](https://deusware.com/risei/index.html#self-testing).
97
95
 
98
96
 
97
+
99
98
  ## Writing Tests
100
99
 
101
100
  You write tests in `.rt.js` files like this:
@@ -122,11 +121,13 @@ tests = [ ...
122
121
  ```
123
122
 
124
123
  - **Asynchronous / awaitable code can tested with no changes at all to this syntax.**
125
- - You use `async` and `await` normally if you define functions within tests.
126
- - Use empty arrays for `.in` or `.with` when there are no args to pass.
124
+ - But you use `async` and `await` if you define functions within tests.
125
+ - **Properties can be tested with no changes at all to this syntax.**
126
+ - Use empty arrays for `.in` or `.with` when there are no args to pass.
127
127
  - You can use [long names](https://deusware.com/risei/index.html#long-names) for properties if you want.
128
128
 
129
129
 
130
+
130
131
  ## Running Tests
131
132
 
132
133
  Once you have some tests written, you can run them manually:
@@ -150,7 +151,8 @@ npm test
150
151
  ```
151
152
 
152
153
 
153
- ## Basic collapsing forward example
154
+
155
+ ## Property reuse with collapsing forward
154
156
 
155
157
  You can state repeated test properties just once, and let them _collapse forward_ across subsequent tests to save time and make tests easier to read.
156
158
 
@@ -169,12 +171,13 @@ Risei collapses values together from partial or full test objects until it has a
169
171
  { on: SortingClass, with: [ ] }, // Change of tested class. All existing props are wiped out.
170
172
  ```
171
173
 
174
+ - **To change just one property between tests, restate it along with `just: true`.**
172
175
  - There are more options available, and an exclusion for mutated args.
173
176
  - Learn all the details [here](https://deusware.com/risei/index.html#collapsing-forward).
174
177
 
175
178
 
176
179
 
177
- ## Basic spoofing example
180
+ ## Test isolation with spoofing
178
181
 
179
182
  You can use declarative _spoofing_ syntax to define what dependencies of your targeted code return for it to use.
180
183
 
@@ -221,16 +224,16 @@ If errors are thrown while testing, gold bars listing them appear at the bottom,
221
224
 
222
225
  There are the known minor issues:
223
226
 
224
- - If args for a test are mutated by tested code, the mutated args are used when collapsing forward.
225
- - The workaround is just to restate those args for each test.
227
+ - Any mutated test args are used in mutated form by later tests when collapsing forward.
228
+ - To work around this, just restate those args for each test.
226
229
 
227
230
 
228
231
  - Spoofing accessor properties only works when they have both a getter and a setter.
229
- - There is no specific workaround for this, but you may look for other ways to produce the property values you need.
232
+ - You can use `.do` and `.undo` to set and reverse other properties.
230
233
 
231
234
 
232
- - Custom versions of JavaScript methods like `toString()` may not be recognized, nor any instance methods with the same names as static methods.
233
- - The workaround is to use an `.and` value of `"instance"` for any methods like these that you test.
235
+ - Custom overrides of `toString()` and other built-in methods aren't recognized automatically.  Nor are instance methods with the same names as static methods.
236
+ - You can use an `.and` value of `"instance"` to ensure these are recognized.
234
237
 
235
238
 
236
239
 
@@ -261,7 +264,7 @@ Risei is published for use under the terms of the MIT license:
261
264
 
262
265
  <div style="border: solid darkgray 1px; padding: 0.5rem;">
263
266
 
264
- <b>Risei Copyright &copy; 2023&ndash;2025 Ed Fallin</b>
267
+ <b>Risei Copyright &copy; 2023&ndash;2026 Ed Fallin</b>
265
268
 
266
269
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
267
270
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "risei",
3
- "version": "3.3.3",
3
+ "version": "3.3.4",
4
4
  "description": "Risei allows you to write unit tests as simple JavaScript objects, so it's easy and fast, with no drag on redesign.",
5
5
  "keywords": [
6
6
  "unit test",
@@ -54,6 +54,6 @@
54
54
  "fs": "^0.0.1-security",
55
55
  "mocha": "^10.0.0",
56
56
  "morgan": "~1.9.1",
57
- "risei": "^3.3.2"
57
+ "risei": "^3.3.4"
58
58
  }
59
59
  }
@@ -6,36 +6,44 @@ export default class ASpoofingFixture {
6
6
  constructor() {
7
7
  /* No operations. */
8
8
  }
9
-
10
- /* A spoof might be supplied as a custom function,
9
+
10
+ /* A spoof might be supplied as a custom function,
11
11
  but it might just be defined by a return value. */
12
- spoofMethod(spoofOutput) {
12
+ spoofMethod(spoofOutput) /* passed */ {
13
13
  // No spoof supplied, so spoof is an empty function.
14
14
  if (spoofOutput === undefined) {
15
15
  let empty = () => { };
16
16
  return empty;
17
17
  }
18
-
18
+
19
19
  // Spoof is a custom function to use as-is.
20
20
  if (spoofOutput instanceof Function) {
21
21
  return spoofOutput;
22
22
  }
23
-
24
- // Recursive case: Spoof defines a whole object
23
+
24
+ // Recursive case: Spoof defines a whole object
25
25
  // tree to be returned as method output.
26
- if (Array.isArray(spoofOutput)) {
27
- if (spoofOutput.every(x => SpoofDef.isASpoof(x))) {
28
- spoofOutput = SpoofDef.fromNonceTuples(spoofOutput);
29
- spoofOutput = this.spoofObjectTree(spoofOutput);
30
- }
26
+ if (this.isSpoofArray(spoofOutput)) {
27
+ spoofOutput = SpoofDef.fromNonceTuples(spoofOutput);
28
+ spoofOutput = this.spoofObjectTree(spoofOutput);
31
29
  }
32
-
30
+
33
31
  // Commonest case: Spoof is a value to be returned.
34
32
  let spoof = () => { return spoofOutput; }
35
33
  return spoof;
36
34
  }
37
35
 
38
- /* Returns an object with all spoofed methods,
36
+ isSpoofArray(subject) /* good */ {
37
+ if (!Array.isArray(subject)) { return false; }
38
+
39
+ // Empty array is assumed to be a spoof return value.
40
+ if (subject.length === 0) { return false; }
41
+
42
+ let is = subject.every(x => SpoofDef.isASpoof(x));
43
+ return is;
44
+ }
45
+
46
+ /* Returns an object with all spoofed methods,
39
47
  possibly with intermediate objects. */
40
48
  spoofObjectTree(ofAsPairs) {
41
49
  let spoof = {};
@@ -66,7 +74,7 @@ export default class ASpoofingFixture {
66
74
  next = next[name];
67
75
  }
68
76
 
69
- // Final or only name is the leaf method;
77
+ // Final or only name is the leaf method;
70
78
  // name may mistakenly end with `()`.
71
79
  name = names.shift();
72
80
  name = name.replace("()", "");
@@ -75,7 +83,7 @@ export default class ASpoofingFixture {
75
83
  let method = this.spoofMethod(output);
76
84
  next[name] = method;
77
85
  }
78
-
86
+
79
87
  // And back to caller.
80
88
  return spoof;
81
89
  }
@@ -4,255 +4,255 @@ import TypeAnalyzer from "./TypeAnalyzer.js";
4
4
  import NameAnalyzer from "./NameAnalyzer.js";
5
5
  import TotalDisplayer from "./TotalDisplayer.js";
6
6
 
7
- /* Defines what is found in a spoof definition, typically as
7
+ /* Defines what is found in a spoof definition, typically as
8
8
  a nonce object rather than an instance of this class. */
9
9
 
10
10
  export default class SpoofDef {
11
11
  // region Static fields
12
12
 
13
- /* Properties can have short or long names. */
14
- static #onNames = [ "on", "target" ];
15
- static #ofNames = [ "of", "method" ];
16
- static #asNames = [ "as", "output" ];
13
+ /* Properties can have short or long names. */
14
+ static #onNames = [ "on", "target" ];
15
+ static #ofNames = [ "of", "method" ];
16
+ static #asNames = [ "as", "output" ];
17
17
 
18
- static #longsByShort = new Map([
19
- SpoofDef.#onNames,
20
- SpoofDef.#ofNames,
21
- SpoofDef.#asNames
22
- ]);
23
-
24
- // endregion Static fields
18
+ static #longsByShort = new Map([
19
+ SpoofDef.#onNames,
20
+ SpoofDef.#ofNames,
21
+ SpoofDef.#asNames
22
+ ]);
23
+
24
+ // endregion Static fields
25
25
 
26
26
  // region Public fields (long names)
27
27
 
28
- /* These long names are clearer and match the constructor's parameter names. */
28
+ /* These long names are clearer and match the constructor's parameter names. */
29
29
 
30
- /* The test-definition field names use the longer, possibly clearer forms.
31
- Equivalent short-name and long-name properties use the same fields. */
30
+ /* The test-definition field names use the longer, possibly clearer forms.
31
+ Equivalent short-name and long-name properties use the same fields. */
32
32
 
33
- target;
34
- method;
35
- output;
33
+ target;
34
+ method;
35
+ output;
36
36
 
37
- // endregion Public fields (long names)
37
+ // endregion Public fields (long names)
38
38
 
39
39
  // region Properties
40
40
 
41
41
  // region Spoof definition, short names
42
42
 
43
- /* These short names make JSON-like definitions easier. */
43
+ /* These short names make JSON-like definitions easier. */
44
44
 
45
- get on() { return this.target; }
45
+ get on() { return this.target; }
46
46
 
47
- set on(value) { this.target = value; }
47
+ set on(value) { this.target = value; }
48
48
 
49
- get of() { return this.method; }
49
+ get of() { return this.method; }
50
50
 
51
- set of(value) { this.method = value; }
51
+ set of(value) { this.method = value; }
52
52
 
53
- get as() { return this.output; }
53
+ get as() { return this.output; }
54
54
 
55
- set as(value) { this.output = value; }
55
+ set as(value) { this.output = value; }
56
56
 
57
- // endregion Spoof definition, short names
57
+ // endregion Spoof definition, short names
58
58
 
59
59
  // region Def state
60
60
 
61
- /* Returns true if arg doesn't provide enough to spoof with. */
62
- static isNotASpoof(nonce) /* passed */ {
63
- return !SpoofDef.isASpoof(nonce);
64
- }
65
-
66
- /* Returns true if arg provides enough to spoof with. */
67
- static isASpoof(nonce) /* passed */ {
68
- /* Any nonce that is null or undefined is not a SpoofDef. */
69
- if (nonce === undefined || nonce === null) {
70
- return false;
61
+ /* Returns true if arg doesn't provide enough to spoof with. */
62
+ static isNotASpoof(nonce) /* passed */ {
63
+ return !SpoofDef.isASpoof(nonce);
71
64
  }
72
-
73
- let hasAnOf = SpoofDef.#hasEitherName(nonce, SpoofDef.#ofNames);
74
- let hasAnAs = SpoofDef.#hasEitherName(nonce, SpoofDef.#asNames);
75
65
 
76
- /* Any nonce with at least an .of or an .as is a SpoofDef. */
77
- return hasAnOf || hasAnAs;
78
- }
66
+ /* Returns true if arg provides enough to spoof with. */
67
+ static isASpoof(nonce) /* passed */ {
68
+ /* Any nonce that is null or undefined is not a SpoofDef. */
69
+ if (nonce === undefined || nonce === null) {
70
+ return false;
71
+ }
79
72
 
80
- get isMethodSpoof() /* passed */ {
81
- if (this.target === undefined) {
82
- throw new Error("No .on / .target present. It must be set for .isMethodSpoof to work.");
73
+ let hasAnOf = SpoofDef.#hasEitherName(nonce, SpoofDef.#ofNames);
74
+ let hasAnAs = SpoofDef.#hasEitherName(nonce, SpoofDef.#asNames);
75
+
76
+ /* Any nonce with at least an .of or an .as is a SpoofDef. */
77
+ return hasAnOf || hasAnAs;
83
78
  }
84
79
 
85
- let plainName = NameAnalyzer.plainNameOf(this.method);
80
+ get isMethodSpoof() /* passed */ {
81
+ if (this.target === undefined) {
82
+ throw new Error("No .on / .target present. It must be set for .isMethodSpoof to work.");
83
+ }
86
84
 
87
- if (NameAnalyzer.hasPropertySigil(this.method)) {
88
- return false;
89
- }
85
+ let plainName = NameAnalyzer.plainNameOf(this.method);
90
86
 
91
- let isMethod
92
- = NameAnalyzer.hasMethodSigil(this.method)
93
- || TypeAnalyzer.isMethodMember(this.target, plainName);
87
+ if (NameAnalyzer.hasPropertySigil(this.method)) {
88
+ return false;
89
+ }
94
90
 
95
- return isMethod;
96
- }
91
+ let isMethod
92
+ = NameAnalyzer.hasMethodSigil(this.method)
93
+ || TypeAnalyzer.isMethodMember(this.target, plainName);
97
94
 
98
- get isPropertySpoof() /* passed */ {
99
- if (this.target === undefined) {
100
- throw new Error("No .on / .target present. It must be set for .isPropertySpoof to work.");
95
+ return isMethod;
101
96
  }
102
97
 
103
- let plainName = NameAnalyzer.plainNameOf(this.method);
98
+ get isPropertySpoof() /* passed */ {
99
+ if (this.target === undefined) {
100
+ throw new Error("No .on / .target present. It must be set for .isPropertySpoof to work.");
101
+ }
104
102
 
105
- if (NameAnalyzer.hasMethodSigil(this.method)) {
106
- return false;
107
- }
103
+ let plainName = NameAnalyzer.plainNameOf(this.method);
104
+
105
+ if (NameAnalyzer.hasMethodSigil(this.method)) {
106
+ return false;
107
+ }
108
108
 
109
- let isProperty
110
- = NameAnalyzer.hasPropertySigil(this.method)
111
- || TypeAnalyzer.isPropertyMember(this.target, plainName);
109
+ let isProperty
110
+ = NameAnalyzer.hasPropertySigil(this.method)
111
+ || TypeAnalyzer.isPropertyMember(this.target, plainName);
112
112
 
113
- return isProperty;
114
- }
113
+ return isProperty;
114
+ }
115
115
 
116
- // endregion Def state
116
+ // endregion Def state
117
117
 
118
118
  // endregion Properties
119
119
 
120
120
  // region Initing, including constructor() and statics
121
121
 
122
- /* Long names used, just in case. */
123
- constructor(target, method, output) /* passed */ {
124
- this.target = target;
125
- this.method = method;
126
- this.output = output;
127
- }
128
-
129
- /* Arg 'type' should be test.type if present. */
130
- static fromNonceTuples(nonces, type) /* passed */ {
131
- // Throughput and output.
132
- let full = new SpoofDef();
133
- let output = [ ];
134
-
135
- // Looping over all.
136
- for (let nonce of nonces) {
137
- // Get latest spoof definition/s and retain for output.
138
- let latests = SpoofDef.fromNonceTuple(nonce, type);
139
- output.push(...latests);
122
+ /* Long names used, just in case. */
123
+ constructor(target, method, output) /* passed */ {
124
+ this.target = target;
125
+ this.method = method;
126
+ this.output = output;
140
127
  }
141
128
 
142
- // Back to caller.
143
- return output;
144
- }
145
-
146
- /* Arg 'type' should be test.type if present. */
147
- static fromNonceTuple(nonce, type) /* passed */ {
148
- // No spoof-def to output.
149
- if (SpoofDef.isNotASpoof(nonce)) {
150
- return [ ];
129
+ /* Arg 'type' should be test.type if present. */
130
+ static fromNonceTuples(nonces, type) /* passed */ {
131
+ // Throughput and output.
132
+ let full = new SpoofDef();
133
+ let output = [ ];
134
+
135
+ // Looping over all.
136
+ for (let nonce of nonces) {
137
+ // Get latest spoof definition/s and retain for output.
138
+ let latests = SpoofDef.fromNonceTuple(nonce, type);
139
+ output.push(...latests);
140
+ }
141
+
142
+ // Back to caller.
143
+ return output;
151
144
  }
152
145
 
153
- let defs = [ ];
146
+ /* Arg 'type' should be test.type if present. */
147
+ static fromNonceTuple(nonce, type) /* passed */ {
148
+ // No spoof-def to output.
149
+ if (SpoofDef.isNotASpoof(nonce)) {
150
+ return [ ];
151
+ }
154
152
 
155
- // Nonce defines one spoof.
156
- if (SpoofDef.#isMonoSpoof(nonce)) {
157
- let def = SpoofDef.#fromMonoNonce(nonce, type);
158
- defs.push(def);
159
- }
153
+ let defs = [ ];
160
154
 
161
- // Nonce defines more than one spoof: Recursion.
162
- if (SpoofDef.#isPolySpoof(nonce)) {
163
- let locals = SpoofDef.#fromPolyNonce(nonce, type);
164
- defs.push(...locals);
155
+ // Nonce defines one spoof.
156
+ if (SpoofDef.#isMonoSpoof(nonce)) {
157
+ let def = SpoofDef.#fromMonoNonce(nonce, type);
158
+ defs.push(def);
159
+ }
160
+
161
+ // Nonce defines more than one spoof: Recursion.
162
+ if (SpoofDef.#isPolySpoof(nonce)) {
163
+ let locals = SpoofDef.#fromPolyNonce(nonce, type);
164
+ defs.push(...locals);
165
+ }
166
+
167
+ return defs;
165
168
  }
166
169
 
167
- return defs;
168
- }
170
+ // region Dependencies of nonce-tuple initing methods
169
171
 
170
- // region Dependencies of nonce-tuple initing methods
172
+ static #isMonoSpoof(nonce) /* verified */ {
173
+ return SpoofDef.#hasEitherName(nonce, SpoofDef.#ofNames);
174
+ }
171
175
 
172
- static #isMonoSpoof(nonce) /* verified */ {
173
- return SpoofDef.#hasEitherName(nonce, SpoofDef.#ofNames);
174
- }
176
+ static #isPolySpoof(nonce) /* verified */ {
177
+ return !SpoofDef.#isMonoSpoof(nonce);
178
+ }
175
179
 
176
- static #isPolySpoof(nonce) /* verified */ {
177
- return !SpoofDef.#isMonoSpoof(nonce);
178
- }
180
+ /* Arg 'type' should be test.type if present. */
181
+ static #fromMonoNonce(nonce, type) /* verified */ {
182
+ // Empty, since each prop can
183
+ // have either of two names.
184
+ let def = new SpoofDef();
179
185
 
180
- /* Arg 'type' should be test.type if present. */
181
- static #fromMonoNonce(nonce, type) /* verified */ {
182
- // Empty, since each prop can
183
- // have either of two names.
184
- let def = new SpoofDef();
186
+ let shortNames = SpoofDef.#longsByShort.keys();
185
187
 
186
- let shortNames = SpoofDef.#longsByShort.keys();
188
+ // Traversing name pairs to use whichever one is present;
189
+ // if neither is present, the property is undefined.
190
+ for (let shortName of shortNames) {
191
+ let longName = SpoofDef.#longsByShort.get(shortName);
187
192
 
188
- // Traversing name pairs to use whichever one is present;
189
- // if neither is present, the property is undefined.
190
- for (let shortName of shortNames) {
191
- let longName = SpoofDef.#longsByShort.get(shortName);
193
+ def[shortName]
194
+ = nonce[shortName] !== undefined
195
+ ? nonce[shortName]
196
+ : nonce[longName];
197
+ }
192
198
 
193
- def[shortName]
194
- = nonce[shortName] !== undefined
195
- ? nonce[shortName]
196
- : nonce[longName];
197
- }
199
+ // Adding test target's type, if no type provided.
200
+ if (def.target === undefined) {
201
+ def.target = type;
202
+ }
198
203
 
199
- // Adding test target's type, if no type provided.
200
- if (def.target === undefined) {
201
- def.target = type;
202
- }
204
+ return def;
205
+ }
203
206
 
204
- return def;
205
- }
207
+ /* Arg 'type' should be test.type if present. */
208
+ static #fromPolyNonce(nonce, type) /* verified */ {
209
+ let defs = [ ];
206
210
 
207
- /* Arg 'type' should be test.type if present. */
208
- static #fromPolyNonce(nonce, type) /* verified */ {
209
- let defs = [ ];
211
+ // Poly-nonce's .on applies to all its partial spoofs.
212
+ let on = nonce.on !== undefined ? nonce.on : nonce.target;
210
213
 
211
- // Poly-nonce's .on applies to all its partial spoofs.
212
- let on = nonce.on !== undefined ? nonce.on : nonce.target;
214
+ // Poly-nonce's .as is an array of partial-spoof nonces.
215
+ let pairs = nonce.as !== undefined ? nonce.as : nonce.output;
213
216
 
214
- // Poly-nonce's .as is an array of partial-spoof nonces.
215
- let pairs = nonce.as !== undefined ? nonce.as : nonce.output;
217
+ // Traversing partial-spoof nonces (.of and .as only).
218
+ for (let pair of pairs) {
219
+ // Either name (or neither) may exist on nonce partial.
220
+ let of = pair.of !== undefined ? pair.of : pair.method;
221
+ let as = pair.as !== undefined ? pair.as : pair.output;
216
222
 
217
- // Traversing partial-spoof nonces (.of and .as only).
218
- for (let pair of pairs) {
219
- // Either name (or neither) may exist on nonce partial.
220
- let of = pair.of !== undefined ? pair.of : pair.method;
221
- let as = pair.as !== undefined ? pair.as : pair.output;
223
+ // Only one level of recursion.
224
+ let passer = SpoofDef.#fromMonoNonce({ on, of, as }, type);
222
225
 
223
- // Only one level of recursion.
224
- let passer = SpoofDef.#fromMonoNonce({ on, of, as }, type);
226
+ defs.push(passer);
227
+ }
225
228
 
226
- defs.push(passer);
227
- }
229
+ return defs;
230
+ }
228
231
 
229
- return defs;
230
- }
232
+ static #hasEitherName(nonce, names) /* verified */ {
233
+ let hasEither = false;
231
234
 
232
- static #hasEitherName(nonce, names) /* verified */ {
233
- let hasEither = false;
235
+ for (let name of names) {
236
+ hasEither ||= nonce[name] !== undefined;
237
+ }
234
238
 
235
- for (let name of names) {
236
- hasEither ||= nonce[name] !== undefined;
237
- }
238
-
239
- return hasEither;
240
- }
239
+ return hasEither;
240
+ }
241
241
 
242
- // endregion Dependencies of nonce-tuple initing methods
242
+ // endregion Dependencies of nonce-tuple initing methods
243
243
 
244
- // endregion Initing, including constructor() and statics
244
+ // endregion Initing, including constructor() and statics
245
245
 
246
246
  // region Overrides and dependencies
247
247
 
248
- toString() /* passed */ {
249
- let displayer = new TotalDisplayer();
248
+ toString() /* passed */ {
249
+ let displayer = new TotalDisplayer();
250
250
 
251
- let text = `SpoofDef{ on:${ displayer.display(this.target) }, `
252
- + `of:${ displayer.display(this.method) }, `
253
- + `as:${ displayer.display(this.output) } }`;
254
- return text;
255
- }
251
+ let text = `SpoofDef{ on:${ displayer.display(this.target) }, `
252
+ + `of:${ displayer.display(this.method) }, `
253
+ + `as:${ displayer.display(this.output) } }`;
254
+ return text;
255
+ }
256
256
 
257
- // endregion Overrides and dependencies
257
+ // endregion Overrides and dependencies
258
258
  }