risei 1.3.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +142 -379
  2. package/Read-me reduced.md +294 -0
  3. package/Read-me redux.md +581 -0
  4. package/index.js +5 -5
  5. package/package.json +10 -11
  6. package/system/ASpoofingFixture.js +7 -8
  7. package/system/ATestCaller.js +3 -3
  8. package/system/ATestFinder.js +2 -2
  9. package/system/ATestReporter.js +2 -1
  10. package/system/ATestSource.js +5 -1
  11. package/system/ChosenTestFinder.js +4 -4
  12. package/system/ClassTestGroup.js +2 -2
  13. package/system/LocalCaller.js +5 -5
  14. package/system/{ClassMethodSpoofer.js → MethodSpoofer.js} +54 -48
  15. package/system/MethodTestGroup.js +2 -2
  16. package/system/Moment.js +1 -1
  17. package/system/NameAnalyzer.js +26 -0
  18. package/system/PropertySpoofer.js +156 -0
  19. package/system/Risei.js +5 -5
  20. package/system/SpoofDef.js +260 -0
  21. package/system/TerminalReporter.js +8 -8
  22. package/system/{TestDefinition.js → TestDef.js} +153 -107
  23. package/system/TestFinder.js +3 -3
  24. package/system/TestFrame.js +15 -52
  25. package/system/TestGroup.js +1 -1
  26. package/system/TestResult.js +2 -2
  27. package/system/TestRunner.js +23 -107
  28. package/system/TestStages.js +80 -76
  29. package/system/TestSummary.js +1 -1
  30. package/system/TotalComparer.js +60 -11
  31. package/system/TotalDisplayer.js +33 -12
  32. package/system/TypeAnalyzer.js +41 -79
  33. package/system/TypeIdentifier.js +18 -8
  34. package/system/Types.js +3 -1
  35. package/test-target-objects/ConditionalThrowTarget.js +11 -0
  36. package/test-target-objects/Counter.js +46 -0
  37. package/test-target-objects/DomTarget.js +37 -0
  38. package/test-target-objects/InterSpoofer.js +230 -0
  39. package/test-target-objects/MixedContents.js +33 -0
  40. package/test-target-objects/MutationTarget.js +37 -0
  41. package/test-target-objects/ObjectComposer.js +34 -0
  42. package/test-target-objects/PolySpoofableInner.js +29 -0
  43. package/test-target-objects/PolySpoofableOuter.js +52 -0
  44. package/test-target-objects/PropertiesTarget.js +98 -0
  45. package/test-target-objects/Returner.js +7 -0
  46. package/test-target-objects/Searcher.js +25 -0
  47. package/test-target-objects/Sorter.js +91 -0
  48. package/test-target-objects/Spoofable.js +36 -0
  49. package/test-target-objects/StateTarget.js +34 -0
  50. package/test-target-objects/StaticTarget.js +17 -0
  51. package/test-target-objects/TestableTarget.js +57 -0
  52. package/trial-tests/SelfTests.outward-rt.js +511 -0
  53. package/trial-tests/TopicTests.outward-rt.js +313 -0
  54. package/usage-examples/Gold-bar-example.png +0 -0
  55. package/usage-examples/Title-example.png +0 -0
  56. package/xternal-tests/ASpoofingFixture.tests.js +242 -0
  57. package/xternal-tests/MethodSpoofer.tests.js +130 -0
  58. package/xternal-tests/SpoofDef.tests.js +91 -0
  59. package/xternal-tests/TotalComparer.tests.js +1055 -0
  60. package/xternal-tests/package.json +7 -0
  61. package/system/AComparer.js +0 -9
  62. package/system/ATestFixture.js +0 -44
  63. package/system/ClassPropertySpoofer.js +0 -185
  64. package/system/ObjectMethodSpoofer.js +0 -58
  65. package/system/ObjectPropertySpoofer.js +0 -136
  66. package/system/SpoofDefinition.js +0 -243
  67. package/system/TestFrameChooser.js +0 -54
  68. package/system/TestFrames.js +0 -232
  69. package/system/TotalCopier.js +0 -106
@@ -1,19 +1,28 @@
1
1
  /**/
2
2
 
3
+ import TypeIdentifier from "./TypeIdentifier.js";
4
+ import Types from "./Types.js";
5
+
3
6
  /* Used to analyze classes / members of classes being tested. */
4
7
 
5
- export class TypeAnalyzer {
8
+ export default class TypeAnalyzer {
6
9
  // region Definitions
7
10
 
8
11
  static constructorName = "constructor";
9
- static getName = "get";
10
- static setName = "set";
12
+ static accessorOfPrivate = "Cannot read private member";
11
13
 
12
14
  // endregion Definitions
13
15
 
16
+ /* Returns true if member is defined for
17
+ instances. Returns false otherwise. */
18
+ static isInstanceMember(type, name) {
19
+ /* Reusing logic instead of repeating it. */
20
+ return !TypeAnalyzer.isStaticMember(type, name);
21
+ }
22
+
14
23
  /* Returns true if member is static (value or accessor
15
24
  property, or method). Returns false otherwise. */
16
- static memberIsStatic(type, name) /* passed */ {
25
+ static isStaticMember(type, name) /* passed */ {
17
26
  // Constructor of class type, available with `in`,
18
27
  // is not the constructor normally being sought,
19
28
  // which instead is treated as instance-hosted.
@@ -35,88 +44,41 @@ export class TypeAnalyzer {
35
44
 
36
45
  /* Returns true if instance or static member is a method.
37
46
  Returns false if member is any kind of property. */
38
- static memberIsMethod(type, name) /* passed */ {
39
- return !TypeAnalyzer.memberIsProperty(type, name);
40
- }
47
+ static isMethodMember(type, name) /* passed */ {
48
+ /* A method is always defined either on a type or
49
+ its prototype, even if declared on a superclass. */
41
50
 
42
- /* Returns true if instance or static member is a property
43
- (value or accessor). Returns false if member is a method. */
44
- static memberIsProperty(type, name) /* passed */ {
45
- // Constructor is never a property and is irregular,
46
- // so other type-analysis code can't handle it.
47
- if (name === TypeAnalyzer.constructorName) {
48
- return false;
49
- }
51
+ try {
52
+ let anyInstanceType = TypeIdentifier.identify(type.prototype[name]);
53
+ let anyStaticType = TypeIdentifier.identify(type[name]);
50
54
 
51
- // Static methods and properties (value or accessor) are on type.
52
- if (name in type) {
53
- return TypeAnalyzer.#staticMemberIsProperty(type, name);
54
- }
55
-
56
- // Methods and accessor properties are on prototype.
57
- if (name in type.prototype) {
58
- return TypeAnalyzer.#instanceMemberIsProperty(type.prototype, name);
59
- }
55
+ /* Excludes class "functions", which are not methods. */
56
+ let isInstanceMethod = anyInstanceType === Types.isFunction;
57
+ let isStaticMethod = anyStaticType === Types.isFunction;
60
58
 
61
- // Instance value properties are not on type or prototype.
62
- return true;
63
- }
64
-
65
- // region Dependencies of memberIsProperty()
66
-
67
- /* Algorithm specialized with function check for static members. */
68
- static #staticMemberIsProperty(type, name) {
69
- let descriptor = Object.getOwnPropertyDescriptor(type, name);
70
-
71
- // If on type and has accessors, it's a static accessor property.
72
- if (TypeAnalyzer.#doesHaveAccessorProps(descriptor)) {
73
- return true;
59
+ /* Returns net method-ness; if not found at all, a property
60
+ is assumed (true as long as the member exists at all.) */
61
+ return isInstanceMethod || isStaticMethod;
74
62
  }
75
-
76
- // If on type and has non-Function .value, it's a static value property.
77
- if (TypeAnalyzer.#doesNotHaveFunctionValue(descriptor)) {
78
- return true;
63
+ catch (thrown) {
64
+ /* If private fields are used behind public accessor properties,
65
+ there's a characteristic message which shows it's not a method.
66
+ It may be a property returning a function, but not a method. */
67
+ if (thrown.message.startsWith(TypeAnalyzer.accessorOfPrivate)) {
68
+ return false;
69
+ }
70
+
71
+ /* Other throws are unpredictable and mean there's a problem. */
72
+ throw thrown;
79
73
  }
80
74
 
81
- // If on type but not a static property, it's a static method.
82
- return false;
75
+ /* Probably never reached. No previous throw, but some unknown problem. */
76
+ throw new Error("Unable to determine method vs. property state.");
83
77
  }
84
78
 
85
- /* Algorithm specialized for instance members, no function check. */
86
- static #instanceMemberIsProperty(prototype, name) {
87
- let descriptor = Object.getOwnPropertyDescriptor(prototype, name);
88
-
89
- // If on prototype and has accessors, it's an accessor property.
90
- if (TypeAnalyzer.#doesHaveAccessorProps(descriptor)) {
91
- return true;
92
- }
93
-
94
- // If on prototype but not an accessor property, it's a method.
95
- return false;
96
- }
97
-
98
- // region Internally reused dependencies of other memberIsProperty() dependencies
99
-
100
- static #doesHaveAccessorProps(descriptor) /* verified */ {
101
- // Accessor properties have one or both of these in descriptor.
102
- if (TypeAnalyzer.getName in descriptor || TypeAnalyzer.setName in descriptor) {
103
- return true;
104
- }
105
-
106
- return false;
107
- }
108
-
109
- static #doesNotHaveFunctionValue(descriptor) /* verified */ {
110
- // Simple inverter of other method for readability.
111
- return !TypeAnalyzer.#doesHaveFunctionValue(descriptor);
112
- }
113
-
114
- static #doesHaveFunctionValue(descriptor) /* verified */ {
115
- // If no member, .value of undefined.
116
- return descriptor.value instanceof Function;
79
+ /* Returns true if instance or static member is a property
80
+ (value or accessor). Returns false if member is a method. */
81
+ static isPropertyMember(type, name) /* passed */ {
82
+ return !TypeAnalyzer.isMethodMember(type, name);
117
83
  }
118
-
119
- // endregion Internally reused dependencies of other memberIsProperty() dependencies
120
-
121
- // endregion Dependencies of memberIsProperty()
122
84
  }
@@ -1,9 +1,10 @@
1
1
  /**/
2
2
 
3
- import { Types } from "./Types.js";
3
+ import Types from "./Types.js";
4
+ import ATestSource from "./ATestSource.js";
4
5
 
5
- export class TypeIdentifier {
6
- identify(value) /* passed */ {
6
+ export default class TypeIdentifier {
7
+ static identify(value) /* passed */ {
7
8
  // Basis for most top branching.
8
9
  let rawType = typeof value;
9
10
 
@@ -26,13 +27,17 @@ export class TypeIdentifier {
26
27
  return Types.isDate;
27
28
  }
28
29
 
30
+ if (value instanceof Error) {
31
+ return Types.isError;
32
+ }
33
+
29
34
  if (value === null) {
30
35
  return Types.isNull;
31
36
  }
32
37
 
33
38
  return Types.isObject;
34
39
  }
35
-
40
+
36
41
  // Functions and classes are both "function" to Javascript.
37
42
  if (rawType === "function") {
38
43
  // Functions can be anonymous.
@@ -53,17 +58,22 @@ export class TypeIdentifier {
53
58
  // Function but not class is a named function.
54
59
  return Types.isFunction;
55
60
  }
56
-
61
+
57
62
  // Strings may be handled specially.
58
63
  if (typeof value === "string") {
59
64
  return Types.isString;
60
65
  }
61
-
62
- // Undefined is different from the rest.
66
+
67
+ // Undefined is different from most.
63
68
  if (value === undefined) {
64
69
  return Types.isUndefined;
65
70
  }
66
-
71
+
72
+ // Undef Symbol is different from most.
73
+ if (value === ATestSource.undefSymbol) {
74
+ return Types.isUndefSymbol;
75
+ }
76
+
67
77
  // Non-string values all handled the same.
68
78
  return Types.isValue;
69
79
  }
package/system/Types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**/
2
2
 
3
- export class Types {
3
+ export default class Types {
4
4
  /* This class is essentially an enum. */
5
5
 
6
6
  static isValue = "value";
@@ -12,6 +12,8 @@ export class Types {
12
12
  static isFunction = "function";
13
13
  static isClass = "class";
14
14
  static isDate = "date";
15
+ static isError = "error";
15
16
  static isUndefined = "undefined";
17
+ static isUndefSymbol = "undefsym";
16
18
  static isNull = "null";
17
19
  }
@@ -0,0 +1,11 @@
1
+ /**/
2
+
3
+ export default class ConditionalThrowTarget {
4
+ returnArgOrThrow(arg) {
5
+ if (arg === undefined) {
6
+ throw new Error("No arg provided.");
7
+ }
8
+
9
+ return arg;
10
+ }
11
+ }
@@ -0,0 +1,46 @@
1
+ /**/
2
+
3
+ export default class Counter {
4
+ constructor() {
5
+ /* No operations. */
6
+ }
7
+
8
+ howMany(subject, target) /* passed */ {
9
+ // Bad-call edge case.
10
+ if (arguments.length < 2) {
11
+ return 0;
12
+ }
13
+
14
+ // Bad-arg edge cases.
15
+ if (this.#isEdgeCase(subject, target)) {
16
+ return 0;
17
+ }
18
+
19
+ // Initial values and ops reducer.
20
+ let at = 0;
21
+ let count = 0;
22
+ let span = target.length;
23
+
24
+ // Tracking found site and jumping past it each time the target is found.
25
+ while ((at = subject.indexOf(target, at)) !== -1) {
26
+ count++;
27
+ at += span;
28
+ }
29
+
30
+ // Back to caller.
31
+ return count;
32
+ }
33
+
34
+ #isEdgeCase(subject, target) /* verified */ {
35
+ if (subject === null || target === null) {
36
+ return true;
37
+ }
38
+
39
+ if (target === "") {
40
+ return true;
41
+ }
42
+
43
+ return false;
44
+ }
45
+
46
+ }
@@ -0,0 +1,37 @@
1
+ /**/
2
+
3
+ export default class DomTarget {
4
+ // region Private fields
5
+
6
+ #id;
7
+ #node;
8
+
9
+ // endregion Private fields
10
+
11
+ // region Properties
12
+
13
+ get id() {
14
+ return this.#id;
15
+ }
16
+
17
+ get node() {
18
+ return this.#node;
19
+ }
20
+
21
+ // endregion Properties
22
+
23
+ constructor(id) {
24
+ this.#id = id;
25
+ this.#node = this.domNodeFrom(id);
26
+ }
27
+
28
+ domNodeFrom(id) {
29
+ let node = document.getElementById(id);
30
+ return node;
31
+ }
32
+
33
+ changeDomItem(id) {
34
+ this.#id = id;
35
+ this.#node = this.domNodeFrom(id);
36
+ }
37
+ }
@@ -0,0 +1,230 @@
1
+ /**/
2
+
3
+ import MethodSpoofer from "../system/MethodSpoofer.js";
4
+ import PropertySpoofer from "../system/PropertySpoofer.js";
5
+ import PropertiesTarget from "./PropertiesTarget.js";
6
+
7
+ /* &cruft, maybe drop this after direct testing of groundwork
8
+ and reversing it is possible with .do / .undo */
9
+ export default class InterSpoofer {
10
+ // region Components
11
+
12
+ #methodSpoofer = new MethodSpoofer();
13
+ #propSpoofer = new PropertySpoofer();
14
+
15
+ // endregion Components
16
+
17
+ // region Gathered names of all spoofables
18
+
19
+ static staticProps = [ "staticBaseValueProp", "staticBaseAccessorProp", "staticValueProp", "staticAccessorProp" ];
20
+ static staticMethods = [ "staticBaseMethod", "staticMethod" ];
21
+ static props = [ "instanceBaseValueProp", "instanceBaseAccessorProp", "valueProp", "accessorProp" ];
22
+ static methods = [ "instanceBaseMethod", "instanceMethod" ];
23
+
24
+ static allMethods = [ InterSpoofer.staticMethods, InterSpoofer.methods ];
25
+ static allNames = [ InterSpoofer.staticProps, InterSpoofer.staticMethods, InterSpoofer.props, InterSpoofer.methods ];
26
+
27
+ // endregion Gathered names of all spoofables
28
+
29
+ // region Gathered original method defs and values
30
+
31
+ static #origDefs = InterSpoofer.#bundleDefs(
32
+ PropertiesTarget, new PropertiesTarget(), ...InterSpoofer.allMethods
33
+ );
34
+
35
+ static #origValues = InterSpoofer.#bundleValues(
36
+ PropertiesTarget, new PropertiesTarget(), ...InterSpoofer.allNames
37
+ );
38
+
39
+ // endregion Gathered original method defs and values
40
+
41
+ // region Methods to test spoofers
42
+
43
+ /* If these methods all pass, then the spoofers work
44
+ together without breaking each other's changes. */
45
+
46
+ spoofMethodsThenProps(test) /* passed */ {
47
+ this.#methodSpoofer.spoof(test);
48
+ this.#propSpoofer.spoof(test);
49
+
50
+ let type = test.type;
51
+ let target = test.target;
52
+
53
+ let values = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
54
+
55
+ this.reverseAllSpoofs(type, target);
56
+
57
+ return values;
58
+ }
59
+
60
+ spoofPropsThenMethods(test) /* passed */ {
61
+ this.#propSpoofer.spoof(test);
62
+ this.#methodSpoofer.spoof(test);
63
+
64
+ let type = test.type;
65
+ let target = test.target;
66
+
67
+ let values = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
68
+
69
+ this.reverseAllSpoofs(type, target);
70
+
71
+ return values;
72
+ }
73
+
74
+ spoofAndUnspoofMethodsThenProps(test) /* passed */ {
75
+ this.#methodSpoofer.spoof(test);
76
+ this.#propSpoofer.spoof(test);
77
+
78
+ let type = test.type;
79
+ let target = test.target;
80
+
81
+ let before = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
82
+
83
+ this.#methodSpoofer.unspoof(test);
84
+ this.#propSpoofer.unspoof(test);
85
+
86
+ let after = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
87
+
88
+ this.reverseAllSpoofs(type, target);
89
+
90
+ return { before, after };
91
+ }
92
+
93
+ spoofAndUnspoofPropsThenMethods(test) /* passed */ {
94
+ this.#propSpoofer.spoof(test);
95
+ this.#methodSpoofer.spoof(test);
96
+
97
+ let type = test.type;
98
+ let target = test.target;
99
+
100
+ let before = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
101
+
102
+ this.#propSpoofer.unspoof(test);
103
+ this.#methodSpoofer.unspoof(test);
104
+
105
+ let after = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
106
+
107
+ this.reverseAllSpoofs(type, target);
108
+
109
+ return { before, after };
110
+ }
111
+
112
+ spoofMethodsFirstAndUnspoofThemLast(test) /* passed */ {
113
+ this.#methodSpoofer.spoof(test);
114
+ this.#propSpoofer.spoof(test);
115
+
116
+ let type = test.type;
117
+ let target = test.target;
118
+
119
+ let before = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
120
+
121
+ this.#propSpoofer.unspoof(test);
122
+ this.#methodSpoofer.unspoof(test);
123
+
124
+ let after = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
125
+
126
+ this.reverseAllSpoofs(type, target);
127
+
128
+ return { before, after };
129
+ }
130
+
131
+ spoofPropsFirstAndUnspoofThemLast(test) /* passed */ {
132
+ this.#propSpoofer.spoof(test);
133
+ this.#methodSpoofer.spoof(test);
134
+
135
+ let type = test.type;
136
+ let target = test.target;
137
+
138
+ let before = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
139
+
140
+ this.#methodSpoofer.unspoof(test);
141
+ this.#propSpoofer.unspoof(test);
142
+
143
+ let after = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
144
+
145
+ this.reverseAllSpoofs(type, target);
146
+
147
+ return { before, after };
148
+ }
149
+
150
+ // endregion Methods to test spoofers
151
+
152
+ /* To ensure topic class isolated between tests. */
153
+ reverseAllSpoofs(type, target) /* verified */ {
154
+ /* Setting known properties and methods of target type
155
+ back to their stored original definitions / values. */
156
+
157
+ for (let name of InterSpoofer.staticProps) {
158
+ type[name] = InterSpoofer.#origValues[name];
159
+ }
160
+
161
+ for (let name of InterSpoofer.staticMethods) {
162
+ type[name] = InterSpoofer.#origDefs[name];
163
+ }
164
+
165
+ /* Plain instance properties shouldn't need to be reversed. */
166
+
167
+ for (let name of InterSpoofer.methods) {
168
+ type.prototype[name] = InterSpoofer.#origDefs[name];
169
+ }
170
+
171
+ for (let name of InterSpoofer.props) {
172
+ target[name] = InterSpoofer.#origValues[name];
173
+ }
174
+
175
+ let reversed = InterSpoofer.#bundleValues(type, target, ...InterSpoofer.allNames);
176
+ }
177
+
178
+ // region Internals
179
+
180
+ static #bundleDefs(type, target, staticMethods, methods) /* verified */ {
181
+ /* All callables asked for are stored as copies for restoring. */
182
+
183
+ let output = { };
184
+
185
+ for (let name of staticMethods) {
186
+ output[name] = InterSpoofer.#copyMethod(type[name]);
187
+ }
188
+
189
+ for (let name of methods) {
190
+ output[name] = InterSpoofer.#copyMethod(target[name]);
191
+ }
192
+
193
+ return output;
194
+ }
195
+
196
+ static #bundleValues(type, target, staticProps, staticMethods, props, methods) /* verified */ {
197
+ /* Everything asked for is dropped into an object, either
198
+ property values or return values of calling methods. */
199
+
200
+ let output = { };
201
+
202
+ for (let name of staticProps) {
203
+ output[name] = type[name];
204
+ }
205
+
206
+ for (let name of staticMethods) {
207
+ output[name] = type[name]();
208
+ }
209
+
210
+ for (let name of props) {
211
+ output[name] = target[name];
212
+ }
213
+
214
+ for (let name of methods) {
215
+ output[name] = target[name]();
216
+ }
217
+
218
+ return output;
219
+ }
220
+
221
+ static #copyMethod(method) /* verified */ {
222
+ let passer = [ method ];
223
+ passer = [ ...passer ];
224
+ let copy = passer[0];
225
+
226
+ return copy;
227
+ }
228
+
229
+ // endregion Internals
230
+ }
@@ -0,0 +1,33 @@
1
+ /**/
2
+
3
+ export default class MixedContents {
4
+ first = 1;
5
+ second = "2";
6
+ third = { three: 3 };
7
+
8
+ static fourth = 4;
9
+ static fifth = "5";
10
+
11
+ getInstanceValueOf(name) {
12
+ return this[name];
13
+ }
14
+
15
+ getInstanceProduct() {
16
+ let a = this.first;
17
+ let b = Number(this.second);
18
+ let c = this.third.three;
19
+
20
+ return a * b * c;
21
+ }
22
+
23
+ static getStaticValueOf(name) {
24
+ return MixedContents[name];
25
+ }
26
+
27
+ static getStaticProduct() {
28
+ let a = MixedContents.fourth;
29
+ let b = Number(MixedContents.fifth);
30
+
31
+ return a * b;
32
+ }
33
+ }
@@ -0,0 +1,37 @@
1
+ /**/
2
+
3
+ export default class MutationTarget {
4
+ #map;
5
+ #array;
6
+
7
+ constructor(map, array) {
8
+ this.#map = map;
9
+ this.#array = array;
10
+ }
11
+
12
+ mutateInitors() {
13
+ // Map mutation allows for consecutive mutations.
14
+ let rawKey = this.#map.size + 1;
15
+ this.#map.set(rawKey.toString(), "C");
16
+ this.#array.shift();
17
+
18
+ return { map: this.#map, array: this.#array };
19
+ }
20
+
21
+ mutateArgs(array, tuple) {
22
+ array.push(10);
23
+ tuple.changes += 10;
24
+
25
+ return { array, tuple };
26
+ }
27
+
28
+ replaceArg(array, index, replacement) {
29
+ // The previous element is added at the end for comparison.
30
+ array.push(array[index].toString());
31
+
32
+ // The new element is set at the original index.
33
+ array[index] = replacement;
34
+
35
+ return array;
36
+ }
37
+ }
@@ -0,0 +1,34 @@
1
+ /**/
2
+
3
+ export default class ObjectComposer {
4
+ constructor() {
5
+ /* No operations. */
6
+ }
7
+
8
+ /* Target of spoofing. */
9
+ supplySomeObject() {
10
+ throw new Error("supplyObjectA() must be spoofed in tests.");
11
+ }
12
+
13
+ enact(chain, method) {
14
+ // Invoke the method to be spoofed.
15
+ let target = this.supplySomeObject();
16
+
17
+ // Traverse the spoofed return
18
+ // value's object tree.
19
+ for (let link of chain) {
20
+ target = target[link];
21
+ }
22
+
23
+ // Invoke the method at chain end for its return value.
24
+ let output = target[method]();
25
+ return output;
26
+ }
27
+
28
+ doubleEnact(chainOne, methodOne, chainTwo, methodTwo) {
29
+ let outputOne = this.enact(chainOne, methodOne);
30
+ let outputTwo = this.enact(chainTwo, methodTwo);
31
+
32
+ return [ outputOne, outputTwo ];
33
+ }
34
+ }
@@ -0,0 +1,29 @@
1
+ /**/
2
+
3
+ export default class PolySpoofableInner {
4
+ constructor() {
5
+ /* No operations. */
6
+ }
7
+
8
+ /* Spoofing target. */
9
+ addOrTimes(numbers) {
10
+ let a = numbers.supplyA();
11
+ let b = numbers.supplyB();
12
+
13
+ return a + b;
14
+ }
15
+
16
+ /* Spoofing target. */
17
+ minusOrOver(numbers) {
18
+ let a = numbers.supplyA();
19
+ let b = numbers.supplyB();
20
+
21
+ return a - b;
22
+ }
23
+
24
+ /* Spoofing target. Nested object. */
25
+ numberHost = {
26
+ supplyC: () => { return 15; },
27
+ supplyD: () => { return 18; }
28
+ }
29
+ }