risei 1.3.4 → 2.0.1
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 +134 -479
- 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/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/usage-examples/Output-example.png +0 -0
- package/usage-examples/Summary-example.png +0 -0
- package/usage-examples/Syntax-example.png +0 -0
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
|
}
|
package/system/AComparer.js
DELETED
package/system/ATestFixture.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**/
|
|
2
|
-
|
|
3
|
-
export class ATestFixture {
|
|
4
|
-
// region Private fields
|
|
5
|
-
|
|
6
|
-
#name;
|
|
7
|
-
#kind;
|
|
8
|
-
#fixture;
|
|
9
|
-
|
|
10
|
-
// endregion Private fields
|
|
11
|
-
|
|
12
|
-
// region Properties
|
|
13
|
-
|
|
14
|
-
get name() {
|
|
15
|
-
return this.#name;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
set name(value) {
|
|
19
|
-
this.#name = value;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get kind() {
|
|
23
|
-
return this.#kind;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
set kind(value) {
|
|
27
|
-
this.#kind = value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get fixture() {
|
|
31
|
-
return this.#fixture;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
set fixture(value) {
|
|
35
|
-
this.#fixture = value;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// endregion Properties
|
|
39
|
-
|
|
40
|
-
constructor(name, kind) {
|
|
41
|
-
this.#name = name;
|
|
42
|
-
this.#kind = kind;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**/
|
|
2
|
-
|
|
3
|
-
import { ASpoofingFixture } from "./ASpoofingFixture.js";
|
|
4
|
-
import { SpoofDefinition } from "./SpoofDefinition.js";
|
|
5
|
-
import { TypeAnalyzer } from "./TypeAnalyzer.js";
|
|
6
|
-
|
|
7
|
-
/* &cruft, implement this under test so that anything that can be spoofed
|
|
8
|
-
from the class level is spoofed, but not expecting it to get
|
|
9
|
-
absolutely everything, and supporting poly-spoofing syntax */
|
|
10
|
-
export class ClassPropertySpoofer extends ASpoofingFixture {
|
|
11
|
-
/* Algorithm: Forward method spoof() looks at spoof definitions in .plus and applies them
|
|
12
|
-
to each class' prototypes after first saving the original member definitions.
|
|
13
|
-
Reverse method unspoof() restores each original member definition.
|
|
14
|
-
Fundamentally different from what ObjectMethodSpoofer does. */
|
|
15
|
-
|
|
16
|
-
// region Private fields
|
|
17
|
-
|
|
18
|
-
#spoofsByClass = new Map();
|
|
19
|
-
#originalsByClass = new Map();
|
|
20
|
-
|
|
21
|
-
// endregion Private fields
|
|
22
|
-
|
|
23
|
-
// region Properties
|
|
24
|
-
|
|
25
|
-
get spoofsByClass() {
|
|
26
|
-
return this.#spoofsByClass;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
set spoofsByClass(value) {
|
|
30
|
-
this.#spoofsByClass = value;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get originalsByClass() {
|
|
34
|
-
return this.#originalsByClass;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
set originalsByClass(value) {
|
|
38
|
-
this.#originalsByClass = value;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// endregion Properties
|
|
42
|
-
|
|
43
|
-
constructor() {
|
|
44
|
-
super("ClassPropertySpoofer", "Construction");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/* Spoofs methods of classes as defined in nonce
|
|
48
|
-
spoof-tuple definitions in any test.plus. */
|
|
49
|
-
spoof(test) {
|
|
50
|
-
// Localizing.
|
|
51
|
-
let type = test.on;
|
|
52
|
-
let spoofNonces = test.plus;
|
|
53
|
-
|
|
54
|
-
// Spoofing definitions are always in an array.
|
|
55
|
-
if (!Array.isArray(spoofNonces)) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Converting to scope object type.
|
|
60
|
-
let spoofDefinitions = SpoofDefinition.fromNonceTuples(spoofNonces);
|
|
61
|
-
|
|
62
|
-
// Storing spoofing definitions.
|
|
63
|
-
this.#retainSpoofsByClass(type, spoofDefinitions);
|
|
64
|
-
|
|
65
|
-
// Storing originals for restoring later.
|
|
66
|
-
this.#reserveOriginalsByClass();
|
|
67
|
-
|
|
68
|
-
// Actually spoofing.
|
|
69
|
-
this.#spoofMembersByClass();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// region Dependencies of spoof()
|
|
73
|
-
|
|
74
|
-
#retainSpoofsByClass(type, spoofDefinitions) {
|
|
75
|
-
// Map of Maps: by type, then by method.
|
|
76
|
-
for (let tuple of spoofDefinitions) {
|
|
77
|
-
// If no .target, use `type`.
|
|
78
|
-
if (!tuple.target) {
|
|
79
|
-
tuple.target = type;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
let spoofsByTarget = this.#supplyMemberMap(this.#spoofsByClass, tuple.target);
|
|
83
|
-
|
|
84
|
-
// A single-method spoof is set for later use.
|
|
85
|
-
if (tuple.method) {
|
|
86
|
-
spoofsByTarget.set(tuple.method, tuple.output);
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Two or more method spoofs are set for later use.
|
|
91
|
-
if (tuple.output) {
|
|
92
|
-
let ofAsPairs = tuple.output;
|
|
93
|
-
|
|
94
|
-
for (let pair of ofAsPairs) {
|
|
95
|
-
spoofsByTarget.set(pair.of, pair.as);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
#reserveOriginalsByClass() {
|
|
104
|
-
/* Any spoofing of multiple methods on each class has already been
|
|
105
|
-
split up into Map elements, so this code can use those naively. */
|
|
106
|
-
|
|
107
|
-
let types = this.#spoofsByClass.keys();
|
|
108
|
-
|
|
109
|
-
// For each class.
|
|
110
|
-
for (let type of types) {
|
|
111
|
-
let originalsByTarget = this.#supplyMemberMap(this.#originalsByClass, type);
|
|
112
|
-
|
|
113
|
-
let spoofsByTarget = this.#spoofsByClass.get(type);
|
|
114
|
-
let names = spoofsByTarget.keys();
|
|
115
|
-
|
|
116
|
-
// Saving originals in the Map for later.
|
|
117
|
-
for (let name of names) {
|
|
118
|
-
let original = type.prototype[name];
|
|
119
|
-
originalsByTarget.set(name, original);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/* Dependency of #retainSpoofsByClass()
|
|
125
|
-
and #reserveOriginalsByClass(). */
|
|
126
|
-
#supplyMemberMap(source, target) {
|
|
127
|
-
let mapForType = source.get(target);
|
|
128
|
-
|
|
129
|
-
if (!mapForType) {
|
|
130
|
-
mapForType = new Map();
|
|
131
|
-
source.set(target, mapForType);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return mapForType;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
#spoofMembersByClass() {
|
|
138
|
-
/* Any spoofing of multiple methods on each class has already been
|
|
139
|
-
split up into Map elements, so this code can use those naively. */
|
|
140
|
-
|
|
141
|
-
let types = this.#spoofsByClass.keys();
|
|
142
|
-
|
|
143
|
-
// For each class.
|
|
144
|
-
for (let type of types) {
|
|
145
|
-
let spoofsByTarget = this.#spoofsByClass.get(type);
|
|
146
|
-
let names = spoofsByTarget.keys();
|
|
147
|
-
|
|
148
|
-
// Actually spoofing, one by one.
|
|
149
|
-
for (let name of names) {
|
|
150
|
-
let spoofSource = spoofsByTarget.get(name);
|
|
151
|
-
let spoof = super.spoofMethod(spoofSource);
|
|
152
|
-
type.prototype[name] = spoof;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// endregion Dependencies of spoof()
|
|
158
|
-
|
|
159
|
-
// Spoofed methods on any targeted classes
|
|
160
|
-
// are restored to their original forms.
|
|
161
|
-
unspoof() {
|
|
162
|
-
let types = this.#originalsByClass.keys();
|
|
163
|
-
|
|
164
|
-
// For each class.
|
|
165
|
-
for (let target of types) {
|
|
166
|
-
let prototype = target.prototype;
|
|
167
|
-
|
|
168
|
-
let originalsByTarget = this.#originalsByClass.get(target);
|
|
169
|
-
let names = originalsByTarget.keys();
|
|
170
|
-
|
|
171
|
-
// Actually restoring, one by one.
|
|
172
|
-
for (let name of names) {
|
|
173
|
-
let original = originalsByTarget.get(name);
|
|
174
|
-
target.prototype[name] = original;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Removes definitions, useful if this instance is reused,
|
|
180
|
-
// or else they might be applied when they shouldn't be.
|
|
181
|
-
removeDefinitions() {
|
|
182
|
-
this.#spoofsByClass.clear();
|
|
183
|
-
this.#originalsByClass.clear();
|
|
184
|
-
}
|
|
185
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/* */
|
|
2
|
-
|
|
3
|
-
import { ASpoofingFixture } from "./ASpoofingFixture.js";
|
|
4
|
-
import { SpoofDefinition } from "./SpoofDefinition.js";
|
|
5
|
-
|
|
6
|
-
export class ObjectMethodSpoofer extends ASpoofingFixture {
|
|
7
|
-
/* Algorithm: Forward method spoof() looks at a test's .with and .in, and replaces entire objects
|
|
8
|
-
in each with spoofed object trees, if the originals are spoof definitions.
|
|
9
|
-
No reverse unspoofing method is needed, since objects are nonces tossed after tests.
|
|
10
|
-
Fundamentally different from much of what ClassMethodSpoofer does. */
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
super("ObjectMethodSpoofer", "Construction");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/* Spoofs methods of any objects in test.with and test.in
|
|
17
|
-
for which nonce spoof-tuple definitions are provided. */
|
|
18
|
-
spoof(test) {
|
|
19
|
-
// These array test aspects may contain object-spoof definitions.
|
|
20
|
-
let topics = [ test.with, test.in ];
|
|
21
|
-
|
|
22
|
-
// Spoofing each array of test-aspect elements.
|
|
23
|
-
for (let topic of topics) {
|
|
24
|
-
// Replacing whole element with spoof or with unchanged self.
|
|
25
|
-
for (let at = 0; at < topic.length; at++) {
|
|
26
|
-
let target = topic[at];
|
|
27
|
-
topic[at] = this.spoofTarget(target);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
spoofTarget(target) {
|
|
33
|
-
// Non-spoof targets are returned unchanged.
|
|
34
|
-
if (SpoofDefinition.isNotASpoof(target)) {
|
|
35
|
-
return target;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Spoof definition targets are converted.
|
|
39
|
-
let tuple = SpoofDefinition.fromNonceTuple(target);
|
|
40
|
-
let ofAsPairs = this.#supplySpoofPairs(tuple);
|
|
41
|
-
|
|
42
|
-
// Returning object with one or more spoofed members.
|
|
43
|
-
let spoofedTarget = super.spoofObjectTree(ofAsPairs);
|
|
44
|
-
return spoofedTarget;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
#supplySpoofPairs(tuple) {
|
|
48
|
-
// Single-spoof output.
|
|
49
|
-
let ofAsPairs = [{ of: tuple.method, as: tuple.output }];
|
|
50
|
-
|
|
51
|
-
// Poly-spoof output.
|
|
52
|
-
if (!tuple.method) {
|
|
53
|
-
ofAsPairs = tuple.output;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return ofAsPairs;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**/
|
|
2
|
-
|
|
3
|
-
import { ASpoofingFixture } from "./ASpoofingFixture.js";
|
|
4
|
-
import { SpoofDefinition } from "./SpoofDefinition.js";
|
|
5
|
-
import { TypeAnalyzer } from "./TypeAnalyzer.js";
|
|
6
|
-
|
|
7
|
-
export class ObjectPropertySpoofer extends ASpoofingFixture {
|
|
8
|
-
/* Algorithm: Forward method spoof() looks at property spoof definitions in .plus
|
|
9
|
-
and first saves the original values, then applies the spoofed ones.
|
|
10
|
-
Reverse method unspoof() restores each original property value.
|
|
11
|
-
Fundamentally different from what ObjectMethodSpoofer does. */
|
|
12
|
-
|
|
13
|
-
// region Private fields
|
|
14
|
-
|
|
15
|
-
// Spoofed and original property values.
|
|
16
|
-
#settables = [ ];
|
|
17
|
-
#unsettables = [ ];
|
|
18
|
-
|
|
19
|
-
// endregion Private fields
|
|
20
|
-
|
|
21
|
-
// region Properties
|
|
22
|
-
|
|
23
|
-
get settables() {
|
|
24
|
-
return this.#settables;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
set settables(value) {
|
|
28
|
-
this.#settables = value;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get unsettables() {
|
|
32
|
-
return this.#unsettables;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
set unsettables(value) {
|
|
36
|
-
this.#unsettables = value;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// endregion Properties
|
|
40
|
-
|
|
41
|
-
constructor() {
|
|
42
|
-
super("ObjectPropertySpoofer", "Construction");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/* Spoofs properties of test.target as defined in any test.plus. */
|
|
46
|
-
spoof(test) {
|
|
47
|
-
// Localizing.
|
|
48
|
-
let target = test.target;
|
|
49
|
-
let spoofNonces = test.plus;
|
|
50
|
-
|
|
51
|
-
// Spoofing definitions are always in an array.
|
|
52
|
-
if (!Array.isArray(spoofNonces)) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Converting to scope object type.
|
|
57
|
-
let spoofDefinitions = SpoofDefinition.fromNonceTuples(spoofNonces);
|
|
58
|
-
|
|
59
|
-
// Working only with property spoofs.
|
|
60
|
-
let propertySpoofs = Array.from(spoofDefinitions)
|
|
61
|
-
.filter(spoof => TypeAnalyzer.memberIsProperty(test.on, spoof.of));
|
|
62
|
-
|
|
63
|
-
// Storing new values for setting.
|
|
64
|
-
this.#settables = propertySpoofs;
|
|
65
|
-
|
|
66
|
-
// Storing original values for restoring later.
|
|
67
|
-
this.#unsettables = this.#anyOriginalPropertyGetting(target, this.#settables);
|
|
68
|
-
|
|
69
|
-
// Actually spoofing with new values.
|
|
70
|
-
this.#anyPropertySetting(target, this.#settables);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Spoofed methods on any targeted classes
|
|
74
|
-
// are restored to their original forms.
|
|
75
|
-
unspoof() {
|
|
76
|
-
this.#anyPropertyUnsetting(this.target, this.#unsettables);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// region Dependencies of spoof()
|
|
80
|
-
|
|
81
|
-
#anyOriginalPropertyGetting(target, settables) {
|
|
82
|
-
if (!Array.isArray(settables)) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
let unsettables = [ ];
|
|
87
|
-
|
|
88
|
-
for (let settable of settables) {
|
|
89
|
-
// Not a setter tuple if no settable value.
|
|
90
|
-
if (settable.of === undefined) {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Retaining original.
|
|
95
|
-
let original = target[settable.of];
|
|
96
|
-
let unsettable = { of: settable.of, as: original };
|
|
97
|
-
|
|
98
|
-
unsettables.push(unsettable);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return unsettables;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// endregion Dependencies of spoof()
|
|
105
|
-
|
|
106
|
-
// region Dependencies of unspoof()
|
|
107
|
-
|
|
108
|
-
#anyPropertyUnsetting(target, unsettables) {
|
|
109
|
-
// Unsetting is the same as setting,
|
|
110
|
-
// but with tuples of original values.
|
|
111
|
-
return this.#anyPropertySetting(target, unsettables);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// endregion Dependencies of unspoof()
|
|
115
|
-
|
|
116
|
-
// region Dependencies of both spoof() and unspoof()
|
|
117
|
-
|
|
118
|
-
#anyPropertySetting(target, settables) {
|
|
119
|
-
// No properties to set.
|
|
120
|
-
if (!Array.isArray(settables)) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
for (let settable of settables) {
|
|
125
|
-
// Not a setter tuple if no settable value.
|
|
126
|
-
if (settable.of === undefined) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Actually setting property.
|
|
131
|
-
target[settable.of] = settable.as;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// endregion Dependencies of both spoof() and unspoof()
|
|
136
|
-
}
|