risei 3.0.0 → 3.1.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.
@@ -3,23 +3,27 @@
3
3
  import TypeIdentifier from "./TypeIdentifier.js";
4
4
  import Types from "./Types.js";
5
5
  import ATestSource from "./ATestSource.js";
6
+ import ObjectAnalyzer from "./ObjectAnalyzer.js";
6
7
 
7
8
  export default class TotalDisplayer {
8
- display(value) /* good */ {
9
+ static defaultToString = "[object Object]";
10
+
11
+ display(value) /* passed */ {
9
12
  let typeId = TypeIdentifier.identify(value);
10
13
 
11
14
  switch (typeId) {
15
+ case Types.isUndefined: return this.displayUndefined(value);
12
16
  case Types.isValue: return this.displayValue(value);
13
17
  case Types.isString: return this.displayString(value);
14
18
  case Types.isArray: return this.displayArray(value);
15
19
  case Types.isMap: return this.displayMap(value);
16
20
  case Types.isSet: return this.displaySet(value);
21
+ case Types.isFile: return this.displayFile(value);
17
22
  case Types.isError: return this.displayError(value);
18
23
  case Types.isClass: return this.displayClass(value);
19
24
  case Types.isFunction: return this.displayFunction(value);
20
25
  case Types.isObject: return this.displayObject(value);
21
26
  case Types.isDate: return this.displayValue(value);
22
- case Types.isUndefined: return this.displayUndefined(value);
23
27
  case Types.isUndefSymbol: return this.displayUndefined(value);
24
28
  case Types.isNull: return this.displayNull(value);
25
29
  }
@@ -82,6 +86,10 @@ export default class TotalDisplayer {
82
86
  return output;
83
87
  }
84
88
 
89
+ displayFile(value) /* passed */ {
90
+ return `File{name:"${ value.name }",size:${ value.size }}`;
91
+ }
92
+
85
93
  displayError(value) /* passed */ {
86
94
  // Subtype can't be found with typeof.
87
95
  let type = value.name;
@@ -125,13 +133,28 @@ export default class TotalDisplayer {
125
133
  }
126
134
 
127
135
  displayObject(value) /* passed */ {
136
+ // If a custom toString() exists, use its output.
137
+ let asString = value.toString();
138
+
139
+ if (asString !== TotalDisplayer.defaultToString) {
140
+ return asString;
141
+ }
142
+
128
143
  // Each property of the object is handled individually.
129
144
  let items = [ ];
130
145
 
131
- for (let p in value) {
132
- // Cross-recursion.
133
- let item = this.display(value[p]);
134
- items.push(`${ p }:${ item }`);
146
+ let props = ObjectAnalyzer.allProperties(value);
147
+
148
+ for (let prop of props) {
149
+ // Cross-recursion. Try-catch because getters can fail.
150
+ try {
151
+ let item = this.display(value[prop]);
152
+ items.push(`${ prop }:${ item }`);
153
+ }
154
+ catch (e)
155
+ {
156
+ items.push(`${ prop }:(threw)`);
157
+ }
135
158
  }
136
159
 
137
160
  // Properties are all listed together.
@@ -59,9 +59,12 @@ export default class TypeAnalyzer {
59
59
  return isProperty;
60
60
  }
61
61
 
62
+ // region Dependencies of isPropertyMember()
63
+
62
64
  static #typeChainIncludesAsProperty(type, name) /* verified */ {
63
65
  let prototype = type.prototype;
64
66
 
67
+ // Rootward traversal of the prototype chain.
65
68
  while (prototype !== null) {
66
69
  // Type is needed, rather than prototype.
67
70
  type = prototype.constructor;
@@ -71,17 +74,13 @@ export default class TypeAnalyzer {
71
74
  = TypeAnalyzer.#typeDefinitionIncludesAsProperty(type, name)
72
75
  || TypeAnalyzer.#typeTextContainsNameAsProperty(type, name);
73
76
 
74
- // Found as static or instance.
77
+ // Was found as static or instance.
75
78
  if (isProperty) {
76
79
  return true;
77
80
  }
78
81
 
79
- // Parent class.
82
+ // Parent class, or null at root.
80
83
  prototype = Object.getPrototypeOf(prototype);
81
-
82
- if (prototype === null) {
83
- break;
84
- }
85
84
  }
86
85
 
87
86
  // Never found.
@@ -120,18 +119,57 @@ export default class TypeAnalyzer {
120
119
  let valueRegex = new RegExp(`^\\s*${ name }\\s*[=;]`, "m");
121
120
  let accessorRegex = new RegExp(`^\\s*get ${ name }\\(\\)`, "m");
122
121
 
123
- let anyIndexOf = text.search(valueRegex);
122
+ // Conditional execution of the second test() call.
123
+ let doesContain = valueRegex.test(text) || accessorRegex.test(text);
124
+ return doesContain;
125
+ }
126
+
127
+ // endregion Dependencies of isPropertyMember()
128
+
129
+ static allGetters(type) /* passed */ {
130
+ return TypeAnalyzer.#allTypeChainGetters(type);
131
+ }
132
+
133
+ // region Dependencies of allGetters()
134
+
135
+ static #allTypeChainGetters(type) /* verified */ {
136
+ let output = [];
124
137
 
125
- if (anyIndexOf >= 0) {
126
- return true;
127
- }
138
+ let prototype = type.prototype;
139
+
140
+ // Rootward traversal of the prototype chain.
141
+ while (prototype !== null) {
142
+ // Type is needed, rather than prototype.
143
+ type = prototype.constructor;
128
144
 
129
- anyIndexOf = text.search(accessorRegex);
145
+ // Any getters at this level.
146
+ let local = TypeAnalyzer.#gettersInTypeText(type);
147
+ output.push(...local);
130
148
 
131
- if (anyIndexOf >= 0) {
132
- return true;
149
+ // Parent class, or null at root.
150
+ prototype = Object.getPrototypeOf(prototype);
133
151
  }
134
152
 
135
- return false;
153
+ return output;
154
+ }
155
+
156
+ static #gettersInTypeText(type) /* verified */ {
157
+ let text = type.toString();
158
+
159
+ // At start of line's text, get + name + ().
160
+ let getterRegex = /^\s*get\s+(\w+)\(\)/gm;
161
+
162
+ // All getters and their names
163
+ // as an array of arrays.
164
+ let raw = text.matchAll(getterRegex);
165
+ raw = [ ...raw ];
166
+
167
+ // First capturing group is name.
168
+ let names = raw.map(x => x[1]);
169
+
170
+ return names;
136
171
  }
172
+
173
+ // endregion Dependencies of allGetters()
174
+
137
175
  }
@@ -14,27 +14,31 @@ export default class TypeIdentifier {
14
14
  if (Array.isArray(value)) {
15
15
  return Types.isArray;
16
16
  }
17
-
17
+
18
18
  if (value instanceof Map) {
19
19
  return Types.isMap;
20
20
  }
21
-
21
+
22
22
  if (value instanceof Set) {
23
23
  return Types.isSet;
24
24
  }
25
-
25
+
26
+ if (value instanceof File) {
27
+ return Types.isFile;
28
+ }
29
+
26
30
  if (value instanceof Date) {
27
31
  return Types.isDate;
28
32
  }
29
-
33
+
30
34
  if (value instanceof Error) {
31
35
  return Types.isError;
32
36
  }
33
-
37
+
34
38
  if (value === null) {
35
39
  return Types.isNull;
36
40
  }
37
-
41
+
38
42
  return Types.isObject;
39
43
  }
40
44
 
package/system/Types.js CHANGED
@@ -9,6 +9,7 @@ export default class Types {
9
9
  static isObject = "object";
10
10
  static isMap = "map";
11
11
  static isSet = "set";
12
+ static isFile = "file";
12
13
  static isFunction = "function";
13
14
  static isClass = "class";
14
15
  static isDate = "date";
@@ -1,61 +0,0 @@
1
- /**/
2
-
3
- /* Defines operations that a test caller (which requests
4
- the running / reporting of tests) must implement.
5
- Uses the ATestRunner to run all the tests via a generator,
6
- and the ATestReporter for out / formatting if needed.
7
- A = abstract. */
8
-
9
- import TestRunner from "./TestRunner.js";
10
- import ATestReporter from "./ATestReporter.js";
11
-
12
- export default class ATestCaller {
13
- // region Private fields
14
-
15
- #finder;
16
- #runner;
17
- #reporter;
18
-
19
- // endregion Private fields
20
-
21
- // region Properties
22
-
23
- get finder() {
24
- return this.#finder;
25
- }
26
-
27
- set finder(value) {
28
- this.#finder = value;
29
- }
30
-
31
- get runner() {
32
- return this.#runner;
33
- }
34
-
35
- set runner(value) {
36
- this.#runner = value;
37
- }
38
-
39
- get reporter() {
40
- return this.#reporter;
41
- }
42
-
43
- set reporter(value) {
44
- this.#reporter = value;
45
- }
46
-
47
- // endregion Properties
48
-
49
- constructor(finder, runner, reporter) {
50
- this.#finder = finder;
51
- this.#runner = runner;
52
- this.#reporter = reporter;
53
- }
54
-
55
- async runAllTests() {
56
- throw new Error(
57
- "Implement runAllTests() on a subclass, " +
58
- "using .reporter for out / formatting if needed."
59
- );
60
- }
61
- }
@@ -1,38 +0,0 @@
1
- /**/
2
-
3
- /* Defines operations that test-finding classes should implement. A = abstract. */
4
-
5
- import ATestSource from "./ATestSource.js";
6
-
7
- export default class ATestFinder {
8
- // region Fields
9
-
10
- #testSources;
11
- #thrown = [];
12
-
13
- // endregion Fields
14
-
15
- // region Properties
16
-
17
- get testSources() {
18
- return this.#testSources;
19
- }
20
-
21
- set testSources(value) {
22
- this.#testSources = value;
23
- }
24
-
25
- get thrown() {
26
- return this.#thrown;
27
- }
28
-
29
- // endregion Properties
30
-
31
- constructor() {
32
- this.#testSources = [];
33
- }
34
-
35
- async findAllTests() {
36
- throw new Error("Implement findAllTests() on a subclass of ATestFinder.");
37
- }
38
- }
@@ -1,26 +0,0 @@
1
- /**/
2
-
3
- /* Defines operations that a test reporter must implement. A = abstract. */
4
-
5
- /* &cruft, ? replace with my own code eventually */
6
- import chalk from "chalk";
7
-
8
- export default class ATestReporter {
9
- // Reports whatever was just provided by the TestRunner.
10
- // Can delegate to the other report-x methods.
11
- reportNext(result) {
12
- }
13
-
14
- reportGroup() {
15
- }
16
-
17
- reportPassed(result) {
18
- }
19
-
20
- reportFailed(result) {
21
- }
22
-
23
- reportSummary(summary) {
24
- }
25
-
26
- }