risei 3.3.2 → 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
@@ -3,7 +3,7 @@
3
3
 
4
4
  ## Overview
5
5
 
6
- Risei is a new way to write unit tests that's easier and faster, more dependable, and keeps your tests from standing in the way of redesigns.
6
+ Risei is a new way to write unit tests that's easier, faster, and more readable.  Tests made with Risei aren't a source of errors or a drag on redesigns.
7
7
 
8
8
  Risei does all this by replacing hand-coded tests with simple declarative syntax.
9
9
 
@@ -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
@@ -40,36 +42,30 @@ And they have a summary bar at the bottom:
40
42
 
41
43
  ![https://deusware.com/risei/images/Summary-example.png](https://deusware.com/risei/images/Summary-example.png)
42
44
 
43
- - This bar is red when any tests fail, much as you'd expect.
44
-
45
-
46
-
47
- ## Status
48
-
49
- Risei's major features are now complete, but new enhancements or fixes may appear from time to time. \
50
- The latest release, **3.3.2**, adds `async` capabilities formerly missing from the `.from` feature.
51
-
52
- Check out the [full list of changes](#version-history).
45
+ - This bar is red when any tests fail, just as you'd expect.
53
46
 
54
47
 
55
48
 
56
49
  ## Features of Risei
57
50
 
58
- - #### Declarative syntax to write tests simply and quickly.    ►  [Writing tests](https://deusware.com/risei/index.html#writing-tests)  (basics below)
59
- - #### Easy-to-read test definitions and test outputs.    ►  [Test and output examples](https://deusware.com/risei/index.html#examples)  (also above)
60
- - #### Even less to write by stating reused test properties only once.    ►  [Collapsing forward](https://deusware.com/risei/index.html#collapsing-forward)  (basics below)
61
- - #### Built-in declarative syntax to fake test-time values from dependencies.    ►  [Spoofing using `.plus`](https://deusware.com/risei/index.html#spoofing)  (basics below)
62
- - #### Full support for `async` code with no special syntax needed.    ►  [Writing tests](https://deusware.com/risei/index.html#writing-tests)  (basics below)
63
- - #### 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)
64
- - #### Testing `throw` paths effortlessly.    ►  [Using `.and: "throws"`](https://deusware.com/risei/index.html#using-and-throws)
65
- - #### Deriving actual values to test from raw outputs or property retrieval.    ►  [Using `.from`](https://deusware.com/risei/index.html#using-from)
66
- - #### Setting up and tearing down arbitrary test state.    ►  [Using `.do` and `.undo`](https://deusware.com/risei/index.html#using-do-and-undo)
67
- - #### Testing for `undefined` as output.    ►  [Using `this.undef`](https://deusware.com/risei/index.html#using-undef)
68
- - #### 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)
69
64
 
70
65
  - And more!  Check out the full [Risei home page](https://deusware.com/risei).
71
66
 
72
67
 
68
+
73
69
  ## Installation
74
70
 
75
71
  Install Risei for development time only:
@@ -92,11 +88,13 @@ And add Risei's metadata to `package.json`:
92
88
  }
93
89
  ```
94
90
 
91
+
95
92
  ## Testing Risei Itself
96
93
 
97
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).
98
95
 
99
96
 
97
+
100
98
  ## Writing Tests
101
99
 
102
100
  You write tests in `.rt.js` files like this:
@@ -123,11 +121,13 @@ tests = [ ...
123
121
  ```
124
122
 
125
123
  - **Asynchronous / awaitable code can tested with no changes at all to this syntax.**
126
- - And you use `async` and `await` normally if you define functions within tests.
127
- - 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.
128
127
  - You can use [long names](https://deusware.com/risei/index.html#long-names) for properties if you want.
129
128
 
130
129
 
130
+
131
131
  ## Running Tests
132
132
 
133
133
  Once you have some tests written, you can run them manually:
@@ -151,7 +151,8 @@ npm test
151
151
  ```
152
152
 
153
153
 
154
- ## Basic collapsing forward example
154
+
155
+ ## Property reuse with collapsing forward
155
156
 
156
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.
157
158
 
@@ -170,12 +171,13 @@ Risei collapses values together from partial or full test objects until it has a
170
171
  { on: SortingClass, with: [ ] }, // Change of tested class. All existing props are wiped out.
171
172
  ```
172
173
 
174
+ - **To change just one property between tests, restate it along with `just: true`.**
173
175
  - There are more options available, and an exclusion for mutated args.
174
176
  - Learn all the details [here](https://deusware.com/risei/index.html#collapsing-forward).
175
177
 
176
178
 
177
179
 
178
- ## Basic spoofing example
180
+ ## Test isolation with spoofing
179
181
 
180
182
  You can use declarative _spoofing_ syntax to define what dependencies of your targeted code return for it to use.
181
183
 
@@ -218,70 +220,20 @@ If errors are thrown while testing, gold bars listing them appear at the bottom,
218
220
 
219
221
 
220
222
 
221
- ## Version history
222
-
223
- - Release **3.3.2** (September, 2025) contains this change:
224
- - You can now use asynchronous / awaitable code in `.from` using `async` and `await` normally.
225
- - You already could use awaitable code with the normal syntax in `.plus`, `.do`, and `.undo`.
226
-
227
-
228
- - Release **3.3.1** (February, 2025) contains this change:
229
- - You can now spoof and otherwise address value properties (formally _data descriptors_) that don't have an initial value, whether they are static or instance class members.
230
-
231
-
232
- - Release **3.3.0** (January, 2025) adds this change to those of other recent releases:
233
- - You can now test instance members with the same names as static members using a new `.and` option of `"instance"`.
234
-
235
-
236
-
237
- <details>
238
- <summary>
239
- Older releases
240
- </summary>
241
-
242
- - Release **3.2.1** (January, 2025) contains all the changes from **3.2.0**, **3.1.1**, and **3.1.0**, plus this change:
243
- - Risei's mistaken nominal dependency on **npm** has been removed.
244
-
245
-
246
- - Release **3.2.0** (January, 2025) contains all the changes from **3.1.1** and **3.1.0**, plus this change:
247
- - Risei's self-dependency for its own self-testing has been moved back to the development-only scope.
248
-
249
-
250
- - Release **3.1.1** (January, 2025) contains these changes:
251
- - Extra commas and non-objects in arrays of test objects are disregarded, and no longer cause a throw.
252
- - Throws in wider scopes are now listed at the end, like others already found there.
253
-
254
-
255
- - Release **3.1.0** (January, 2025) contains these changes:
256
- - Accessor properties (formally _accessor descriptors_) are now compared for test results and displayed in test outputs.
257
- - Any accessor properties that throw errors during display are displayed as `(threw)`.
258
- - Throws in tested code and in test framing code are now listed at the end, and also displayed amid the tests.
259
- - `File` objects now have a succinct custom display in outputs.
260
- - **(Breaking change)**&nbsp; In `constructor` tests, constructed instances are available in `.from` functions only as `actual` / `test.actual`.
261
- - **(Breaking change)**&nbsp; In `constructor` tests, `test.target` is now the `prototype` of the tested class.
262
- - Widespread internal reengineering of other kinds.
263
-
264
-
265
- > Older releases are dropped from this list progressively over time.&nbsp; Using the latest release is recommended.
266
-
267
- </details>
268
-
269
-
270
-
271
223
  ## Known issues and workarounds
272
224
 
273
225
  There are the known minor issues:
274
226
 
275
- - If args for a test are mutated by tested code, the mutated args are used when collapsing forward.
276
- - 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.
277
229
 
278
230
 
279
231
  - Spoofing accessor properties only works when they have both a getter and a setter.
280
- - The workaround is to find another way to produce the property values you need.
232
+ - You can use `.do` and `.undo` to set and reverse other properties.
281
233
 
282
234
 
283
- - Predefined JavaScript methods like `toString()` may not be recognized, nor any instance method that has the same name as a static method.
284
- - 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.&nbsp; 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.
285
237
 
286
238
 
287
239
 
@@ -312,7 +264,7 @@ Risei is published for use under the terms of the MIT license:
312
264
 
313
265
  <div style="border: solid darkgray 1px; padding: 0.5rem;">
314
266
 
315
- <b>Risei Copyright &copy; 2023&ndash;2025 Ed Fallin</b>
267
+ <b>Risei Copyright &copy; 2023&ndash;2026 Ed Fallin</b>
316
268
 
317
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:
318
270
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "risei",
3
- "version": "3.3.2",
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.1"
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
  }
@@ -66,11 +66,11 @@ export default class TestTargets {
66
66
  // region Dependencies of targeting / naming methods
67
67
 
68
68
  supplyPolyMethodNonce(test, callable) /* verified */ {
69
- let nonce = (...inputs) => {
69
+ let nonce = async (...inputs) => {
70
70
  let actuals = [];
71
71
 
72
72
  for (let args of inputs) {
73
- let local = test.target[callable](...args);
73
+ let local = await test.target[callable](...args);
74
74
  actuals.push(local);
75
75
  }
76
76