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.
- package/README.md +142 -379
- package/Read-me reduced.md +294 -0
- package/Read-me redux.md +581 -0
- package/index.js +5 -5
- package/package.json +10 -11
- package/system/ASpoofingFixture.js +7 -8
- package/system/ATestCaller.js +3 -3
- package/system/ATestFinder.js +2 -2
- package/system/ATestReporter.js +2 -1
- package/system/ATestSource.js +5 -1
- package/system/ChosenTestFinder.js +4 -4
- package/system/ClassTestGroup.js +2 -2
- package/system/LocalCaller.js +5 -5
- package/system/{ClassMethodSpoofer.js → MethodSpoofer.js} +54 -48
- package/system/MethodTestGroup.js +2 -2
- package/system/Moment.js +1 -1
- package/system/NameAnalyzer.js +26 -0
- package/system/PropertySpoofer.js +156 -0
- package/system/Risei.js +5 -5
- package/system/SpoofDef.js +260 -0
- package/system/TerminalReporter.js +8 -8
- package/system/{TestDefinition.js → TestDef.js} +153 -107
- package/system/TestFinder.js +3 -3
- package/system/TestFrame.js +15 -52
- package/system/TestGroup.js +1 -1
- package/system/TestResult.js +2 -2
- package/system/TestRunner.js +23 -107
- package/system/TestStages.js +80 -76
- package/system/TestSummary.js +1 -1
- package/system/TotalComparer.js +60 -11
- package/system/TotalDisplayer.js +33 -12
- package/system/TypeAnalyzer.js +41 -79
- package/system/TypeIdentifier.js +18 -8
- package/system/Types.js +3 -1
- package/test-target-objects/ConditionalThrowTarget.js +11 -0
- package/test-target-objects/Counter.js +46 -0
- package/test-target-objects/DomTarget.js +37 -0
- package/test-target-objects/InterSpoofer.js +230 -0
- package/test-target-objects/MixedContents.js +33 -0
- package/test-target-objects/MutationTarget.js +37 -0
- package/test-target-objects/ObjectComposer.js +34 -0
- package/test-target-objects/PolySpoofableInner.js +29 -0
- package/test-target-objects/PolySpoofableOuter.js +52 -0
- package/test-target-objects/PropertiesTarget.js +98 -0
- package/test-target-objects/Returner.js +7 -0
- package/test-target-objects/Searcher.js +25 -0
- package/test-target-objects/Sorter.js +91 -0
- package/test-target-objects/Spoofable.js +36 -0
- package/test-target-objects/StateTarget.js +34 -0
- package/test-target-objects/StaticTarget.js +17 -0
- package/test-target-objects/TestableTarget.js +57 -0
- package/trial-tests/SelfTests.outward-rt.js +511 -0
- package/trial-tests/TopicTests.outward-rt.js +313 -0
- package/usage-examples/Gold-bar-example.png +0 -0
- package/usage-examples/Title-example.png +0 -0
- package/xternal-tests/ASpoofingFixture.tests.js +242 -0
- package/xternal-tests/MethodSpoofer.tests.js +130 -0
- package/xternal-tests/SpoofDef.tests.js +91 -0
- package/xternal-tests/TotalComparer.tests.js +1055 -0
- package/xternal-tests/package.json +7 -0
- package/system/AComparer.js +0 -9
- package/system/ATestFixture.js +0 -44
- package/system/ClassPropertySpoofer.js +0 -185
- package/system/ObjectMethodSpoofer.js +0 -58
- package/system/ObjectPropertySpoofer.js +0 -136
- package/system/SpoofDefinition.js +0 -243
- package/system/TestFrameChooser.js +0 -54
- package/system/TestFrames.js +0 -232
- package/system/TotalCopier.js +0 -106
package/system/TypeAnalyzer.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
/*
|
|
86
|
-
|
|
87
|
-
|
|
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
|
}
|
package/system/TypeIdentifier.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
import
|
|
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
|
|
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,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
|
+
}
|