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/Read-me redux.md
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
|
|
2
|
+
# Risei Read-Me
|
|
3
|
+
|
|
4
|
+
## Overview
|
|
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.
|
|
7
|
+
|
|
8
|
+
Risei does all this by replacing coded tests with simple declarative syntax.
|
|
9
|
+
|
|
10
|
+
- Risei works with object-oriented JavaScript in modules.
|
|
11
|
+
|
|
12
|
+
- Risei works with TypeScript after just a few tweaks.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
Here are two example tests, in which `SortModel.countSort()` is being tested using the inputs from `.in` and the expected output from `.out`:
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Here are the example test results for those two tests:
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
- Outputs are always listed, even on passing tests, which makes code usage much clearer.
|
|
28
|
+
- Any failing tests appear in light red.
|
|
29
|
+
- Your latest tests always sort to the bottom so it's easy to find their results.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Test runs have a title bar so the starting point is easy to find:
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
And they have a summary bar at the bottom:
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
- This bar is red when any tests fail, much as you'd expect.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Status
|
|
46
|
+
|
|
47
|
+
Risei is under active development and new enhancements appear often. The latest release, **2.0.0**, has many [major improvements](#version-history), including broader capabilities and simpler syntax, and a few minor breaking changes.
|
|
48
|
+
|
|
49
|
+
Check out the [list of the changes](#version-history).
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
Install Risei for development time only:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install --save-dev risei
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Ensure that `package.json` specifies ECMAScript modules:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
"type": "module"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
And add Risei's metadata to `package.json`:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
"risei": {
|
|
71
|
+
"tests": "**.rt.js"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Running Tests
|
|
78
|
+
|
|
79
|
+
Once you have some tests written, you can run them manually:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
node ./node_modules/risei/index.js
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or write a script in `package.json` that does the same:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
"scripts": {
|
|
89
|
+
"test": "node ./node_modules/risei/index.js"
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
And then run that script:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm test
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
## Writing Tests
|
|
102
|
+
|
|
103
|
+
You write tests in `.rt.js` files like this:
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import ATestSource from "risei/ATestSource";
|
|
107
|
+
import ClassToTest from "ClassToTest.js";
|
|
108
|
+
|
|
109
|
+
export class SomeTests extends ATestSource {
|
|
110
|
+
tests = [ ... ];
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Write individual tests as plain JavaScript objects with Risei's simple syntax:
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
tests = [ ...
|
|
118
|
+
{ on: ContainerClass, with: [ "a", "b", "c" ], // Target class and constructor args.
|
|
119
|
+
of: "doesContain", // Target method name.
|
|
120
|
+
for: "When the arg is present, returns true.", // Description of test.
|
|
121
|
+
in: [ "c" ], // Inputs to method.
|
|
122
|
+
out: true }, // Expected output.
|
|
123
|
+
... ];
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- To run, a test must have `.on`, `.with`, `.of`, `.for`, `.in`, and `.out` defined.
|
|
127
|
+
- When there are no constructor or method args, you can use empty arrays for `.in` or `.with`.
|
|
128
|
+
- Some of these properties' names are harmlessly the same as JS keywords. [Long names](#property-long-names) exist if you want to use them instead.
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
## Collapsing forward for lighter tests
|
|
133
|
+
|
|
134
|
+
You can write more tests with less syntax by stating test properties once and letting them _collapse forward_ across subsequent tests.
|
|
135
|
+
|
|
136
|
+
Risei collapses values together until it has a full test to run, as seen in this typical example:
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
{ on: ContainerClass, with: [ "a", "b", "c" ] }, // Following tests are of this class.
|
|
141
|
+
|
|
142
|
+
{ of: "doesContain" }, // Following tests are of this method.
|
|
143
|
+
{ for: "Returns true when arg present.", in: [ "c" ], out: true }, // First test: now Risei has enough to run on.
|
|
144
|
+
{ for: "Returns false when arg absent.", in: [ "d" ], out: false }, // Next test: same method, different test case.
|
|
145
|
+
|
|
146
|
+
{ of: "countOf" }, // Change of tested method. Method-related props are wiped out.
|
|
147
|
+
...
|
|
148
|
+
|
|
149
|
+
{ on: SortingClass, with: [ ] }, // Change of tested class. All existing props are wiped out.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Every property collapses forward until it is set to something else or wiped out with a target shift:
|
|
153
|
+
|
|
154
|
+
- Changing `.on` (the target class) wipes out all other test properties.
|
|
155
|
+
- Changing `.of` (the target method) wipes out all except `.on`, `.with`, and `.plus` (described [later](#spoofing-for-test-isolation-using-plus)).
|
|
156
|
+
- To drop any optional property starting with the current test, you can set it to an empty array `[]`.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
### Preventing extra tests using `.just`:
|
|
160
|
+
|
|
161
|
+
You may want to pre-set a property for an upcoming group of tests, even when fully defined tests already exist. However, this creates an accidental extra test (which often fails), because Risei always collapses old and new values together to make a new fully defined test.
|
|
162
|
+
|
|
163
|
+
It's easy to avoid this problem with the `.just` property:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
{ on: ContainerClass, with: [ "a", "b", "c", ] }, // A section of tests using these values.
|
|
167
|
+
{ ... },
|
|
168
|
+
{ ... },
|
|
169
|
+
|
|
170
|
+
{ just: true, with: [ "t", "u", "v" ] }, // Changing .with without creating an extra test.
|
|
171
|
+
{ ... }, // Tests using the new .with start here.
|
|
172
|
+
{ ... },
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- The value of `.just` doesn't have to be `true`, or any value in particular. The property just has to be defined.
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
### Avoiding mutable-arg problems by restating them:
|
|
179
|
+
|
|
180
|
+
Collapsing forward reuses test property contents directly. Tests are isolated unless your code mutates its arguments. If so, the mutated values are used, throwing off test results.
|
|
181
|
+
|
|
182
|
+
To prevent this problem, simply restate mutable args in each test to keep them independent.
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
## Testing properties and static methods
|
|
187
|
+
|
|
188
|
+
To test properties of instances, or to test static methods and properties, you use exactly the same syntax you would for instance methods, and Risei figures out the rest:
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
tests = [
|
|
192
|
+
{ on: VariegatedClass, with: [ ] },
|
|
193
|
+
{ of: "instanceMethod", for: "Returns true.", in: [ ], out: true }, // Typical use: instance method.
|
|
194
|
+
{ of: "staticMethod", for: "Returns false.", in: [ ], out: false }, // Static method: same syntax. Empty .with needed.
|
|
195
|
+
{ of: "instanceProperty", for: "Returns 47.", in: [ ], out: 47 }, // Instance prop: same. Empty .in needed.
|
|
196
|
+
{ of: "staticProperty", for: "Returns 98.", in: [ ], out: 98 } // Static prop: same. Empty .in needed.
|
|
197
|
+
];
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- When testing statics or properties, tests still need a `.with` and `.in`. You can set them to empty arrays `[]` if needed.
|
|
201
|
+
|
|
202
|
+
Although Risei figures out what it needs to, you can explicitly state these factors if you want, and Risei will treat them as you specify:
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
{ of: "instanceMethod()", ... }, // Trailing parens specify method.
|
|
206
|
+
{ of: ".someProperty", ... }, { of: "someProperty:", ... } // Leading dot or trailing colon specify property.
|
|
207
|
+
{ of: "something", and: "static", ... }, // You add an .and of "static" for statics.
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- This syntax can be used in `.of`, `.plus`, and `.from` (all covered later).
|
|
211
|
+
- When a property is tested directly, it is listed in test output as `.name`.
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
## Spoofing for test isolation using `.plus`
|
|
216
|
+
|
|
217
|
+
To make code dependencies return what your tests need, just _spoof_ what you want using a `.plus` test property:
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
{
|
|
221
|
+
on: TestedClass,
|
|
222
|
+
...
|
|
223
|
+
plus: [
|
|
224
|
+
{ on: Dependency, of: "someMethod", as: 10 }, // Dependency.someMethod() returns 10 in this test.
|
|
225
|
+
{ of: "testedClassMethod", as: 11 } // TestedClass.testedClassMethod returns 11 in this test.
|
|
226
|
+
],
|
|
227
|
+
...
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
- It's just like _fakes_, _mocks_, or _test doubles_ in other test systems, but easier to write and read.
|
|
232
|
+
|
|
233
|
+
There are many options available for spoofing, all laid out together in this example:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
{ on: CombinerClass,
|
|
237
|
+
...
|
|
238
|
+
plus: [
|
|
239
|
+
/* Mono-spoofing. */
|
|
240
|
+
{ on: ClassA, of: "someMethod", as: 10 }, // Spoof this ClassA method to return 10.
|
|
241
|
+
{ on: ClassB, of: "someMethod" }, // Spoof this ClassB method not to return (or do) anything.
|
|
242
|
+
|
|
243
|
+
{ of: "firstMethod", as: [ 7, 8, 9, 10 ] }, // Spoof a method on the tested class (CombinerClass).
|
|
244
|
+
{ of: "secondMethod" }, // Spoof a method on the tested class not to do anything.
|
|
245
|
+
|
|
246
|
+
{ on: ClassC, // Spoof this ClassC method to be this nonce code using a runtime arg.
|
|
247
|
+
of: "someMethod",
|
|
248
|
+
as: (arg) => { return { color: arg }; } },
|
|
249
|
+
|
|
250
|
+
/* Poly-spoofing. */
|
|
251
|
+
{ on: ClassD, as: [ // Spoof two methods on ClassD at the same time.
|
|
252
|
+
{ of: "firstMethod", as: 11 },
|
|
253
|
+
{ of: "secondMethod", as: 12 }
|
|
254
|
+
] },
|
|
255
|
+
{ as: [ // Spoof two methods on the tested class at the same time.
|
|
256
|
+
{ of: "firstMethod", as: 11 },
|
|
257
|
+
{ of: "secondMethod", as: 12 }
|
|
258
|
+
] },
|
|
259
|
+
],
|
|
260
|
+
...
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- These individual types of spoofing can be mixed however needed.
|
|
265
|
+
- You can spoof multiple class methods at the same time with either mono- or poly-spoofing syntax.
|
|
266
|
+
- A spoof nonce function can use args provided to it, but not `this` of the tested instance.
|
|
267
|
+
- You can spoof properties only for the class under test.
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
## Special test conditions
|
|
271
|
+
|
|
272
|
+
### Testing throws with `.and`:
|
|
273
|
+
|
|
274
|
+
You can test for a value thrown in code by adding an `.and` property of `"throw"` or `"throws"`:
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
{
|
|
278
|
+
on: TestedClass, of: "throwsWhenNoArgsGiven", and: "throws",
|
|
279
|
+
for: "When not given any args, throws an Error stating \"Argless.\".",
|
|
280
|
+
in: [ ], out: new Error("Argless.")
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
- The `.and` property is an extension point for special test conditions.
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
### Testing for `undefined` actuals with `this.undef`:
|
|
288
|
+
|
|
289
|
+
You can test for an `undefined` value by setting `.out` to `this.undef` (or `ATestSource.undefSymbol`):
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
{
|
|
293
|
+
on: TestedClass, of: "returnsArgGiven",
|
|
294
|
+
for: "When the arg is undefined, returns undefined.",
|
|
295
|
+
in: [ undefined ], out: this.undef
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
- You can't use `undefined` itself directly for `.out`, because Risei treats it as a missing prop.
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
## Changing actual with `.from`
|
|
304
|
+
|
|
305
|
+
You can refine the test's actual using the `.from` property, for instance for method side-effects or derivatives of the original actual.
|
|
306
|
+
|
|
307
|
+
The `.from` can be the name of a property on the target class or its instance created for the test:
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
{
|
|
311
|
+
on: TestedClass, of: "makeInstanceReady",
|
|
312
|
+
for: "When method is run, the .ready flag is set.",
|
|
313
|
+
in: [ ], out: true,
|
|
314
|
+
from: "ready"
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
- Both instance and static props can be retrieved this way.
|
|
319
|
+
- You can't retrieve an instance property for a test of a static method, because no instance is created.
|
|
320
|
+
|
|
321
|
+
The `.from` can be a nonce function, which must always return a new actual:
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
{
|
|
325
|
+
on: TestedClass, of: "initDerivedObject",
|
|
326
|
+
for: "When method is run, output's .flag is cleared.",
|
|
327
|
+
in: [ ], out: false,
|
|
328
|
+
from: (target, test) => { return test.actual.flag; }
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
- The `test` parameter is the assemblage of test properties you've provided, plus the test's actual value as `.actual`.
|
|
333
|
+
- The `target` parameter is the instance created for the test, or the tested class itself for statics.
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
## Changing external state with `.do.early`, `.do.late`, and `.undo`
|
|
338
|
+
|
|
339
|
+
If test state you need can't be created declaratively, you can set it up and then tear it down using `.do` and `.undo`. The `.do` property has two stages, `.early` and `.late`.
|
|
340
|
+
|
|
341
|
+
You set these properties to nonce functions:
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
{
|
|
345
|
+
on: TestedClass, for: "When the file to read is not text, returns first ten bytes as hex.",
|
|
346
|
+
do: {
|
|
347
|
+
early: () => { writeRandomTestFile(); }, // Steps before any test target instance is created.
|
|
348
|
+
late: (test) => { test.out = readTestFileHex(10); } // Steps after any instance is created, here altering a test property.
|
|
349
|
+
},
|
|
350
|
+
undo: { () => { eraseRandomTestFile(); }, // Steps after everything else in the test.
|
|
351
|
+
in: [ ... ], out: 0x00, // .out is replaced with varying data in .undo.
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
- You're better off using Risei's declarative syntax whenever it can do what you need.
|
|
356
|
+
- You don't need to include both `do.early` and `do.late` if you only need one, which is normally the case.
|
|
357
|
+
- You can use the `test` parameter to alter the test itself if needed, though this is rare.
|
|
358
|
+
- If you do, it's best not to change any added at runtime by Risei.
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
## Using TypeScript with Risei
|
|
363
|
+
|
|
364
|
+
To test TypeScript code with Risei, you make sure the code is transpiled before the tests are run, and you point Risei to the transpiled `.js` files.
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
### In `tsconfig.json`:
|
|
368
|
+
|
|
369
|
+
You set up the transpiler to output files to a separate directory with these two settings:
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"outDir": "dist/out-tsc",
|
|
374
|
+
"noEmit": false
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- Any folder can be `outDir`, but it's best to avoid `dist`, which can interfere with frameworks' build steps.
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
### In `package.json`:
|
|
382
|
+
|
|
383
|
+
In the `test` script, add transpilation before the test run, and file deletion after it, each one conditional on the previous:
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
"scripts": {
|
|
387
|
+
"test": "tsc && node ./node_modules/risei/index.js && rm -r ./dist/out-tsc"
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
- Using `tsc` or an equivalent produces individually testable JavaScript files.
|
|
392
|
+
- Deleting files at the end prevents interference with web frameworks' bundling.
|
|
393
|
+
- The deletion path must match the `outDir` in `tsconfig.json`.
|
|
394
|
+
|
|
395
|
+
> You can run Risei manually with the same sequence of operations.
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
### In test files:
|
|
399
|
+
|
|
400
|
+
All `import` statements have to point to the JavaScript (`.js`) files in the `outDir` path or its subfolders:
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
import { TestedClass } from "../../dist/out-tsc/SubPath/TestedClass.js";
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
- Output files may be in a folder tree parallel to the TypeScript originals (Angular), or may all be in the `outDir` folder itself (React).
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
## Troubleshooting
|
|
411
|
+
|
|
412
|
+
### When a gold bar appears and some tests disappear:
|
|
413
|
+
|
|
414
|
+
When problems cause test files not to load, you see a gold bar listing the syntax errors at fault:
|
|
415
|
+
|
|
416
|
+

|
|
417
|
+
|
|
418
|
+
- Test files without syntax errors are still loaded and run, so you also see a lower total number of tests.
|
|
419
|
+
|
|
420
|
+
Here are the most common problems:
|
|
421
|
+
|
|
422
|
+
| Error condition | Probable cause |
|
|
423
|
+
|-------------------|------------------|
|
|
424
|
+
| Gold bar text `"Test loading failed for... SyntaxError: Unexpected token ':'"` | Missing comma in your tests in the named file, either between tests or between test props |
|
|
425
|
+
| Gold bar text `"Test loading failed for... SyntaxError: Unexpected token '.'"` | Stating the test list as `this.tests = []` instead of `tests = []` in the file named |
|
|
426
|
+
| Other `... Unexpected token ...` errors in gold bar | Some other syntax error in the file named, most likely a missing or extra delimiter |
|
|
427
|
+
| Many test files failing all at once | Syntax error in the code targeted in those test files, or a dependency of it |
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
## When the listed tests or their results are unexpected:
|
|
431
|
+
|
|
432
|
+
| Error condition | Probable cause |
|
|
433
|
+
|-------------------|------------------|
|
|
434
|
+
| Unexpected extra, failing test/s | An object intended only to change test properties lacks a `.just` / `.only` |
|
|
435
|
+
| Unexpected actual values, or unexpected passes / fails in test runs | Test properties from previous tests not replaced or reset with `[]` <br>— or —<br> Mutated args for tested code not restated in following tests to isolate them |
|
|
436
|
+
| Tests written not appearing in runs | Not all properties needed to run tests stated before or in those tests |
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
## Property long names
|
|
441
|
+
|
|
442
|
+
Test properties have longer names that you can use interchangeably with their short names.
|
|
443
|
+
|
|
444
|
+
<details>
|
|
445
|
+
<summary>
|
|
446
|
+
Test definition names
|
|
447
|
+
</summary>
|
|
448
|
+
|
|
449
|
+
| Short Name | Long Name |
|
|
450
|
+
|------------|-----------|
|
|
451
|
+
| `on` | `type` |
|
|
452
|
+
| `with` | `initors` |
|
|
453
|
+
| `of` | `method` |
|
|
454
|
+
| `for` | `nature` |
|
|
455
|
+
| `in` | `inputs` |
|
|
456
|
+
| `out` | `output` |
|
|
457
|
+
| `plus` | `spoofed` |
|
|
458
|
+
| `from` | `source` |
|
|
459
|
+
| `and` | `factors` |
|
|
460
|
+
| `do` | `enact` |
|
|
461
|
+
| `undo` | `counteract` |
|
|
462
|
+
| `just` | `only` |
|
|
463
|
+
|
|
464
|
+
- There are no long names for the `.early` and `.late` properties of `.do` / `.enact`.
|
|
465
|
+
|
|
466
|
+
</details>
|
|
467
|
+
|
|
468
|
+
<br>
|
|
469
|
+
|
|
470
|
+
<details>
|
|
471
|
+
<summary>
|
|
472
|
+
Spoof definition names
|
|
473
|
+
</summary>
|
|
474
|
+
|
|
475
|
+
| Short Name | Long Name |
|
|
476
|
+
|------|------|
|
|
477
|
+
| `on` | `target` |
|
|
478
|
+
| `of` | `method` |
|
|
479
|
+
| `as` | `output` |
|
|
480
|
+
|
|
481
|
+
</details>
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
## Version history
|
|
486
|
+
|
|
487
|
+
- Release **2.0.0** (August, 2024) contains all of these changes:
|
|
488
|
+
- Risei now can determine automatically if tested methods are static.
|
|
489
|
+
- You can now directly test properties just like methods.
|
|
490
|
+
- Risei can also determine automatically if these are static.
|
|
491
|
+
- Directly tested properties appear in the output in the form `.name`.
|
|
492
|
+
- You can now spoof properties on the targeted class instance, and static properties on the target class itself.
|
|
493
|
+
- You can now perform arbitrary operations, if needed, with the two-part `.do` property and one-part `.undo` property.
|
|
494
|
+
- You can now change test properties without accidentally creating a new test using `.just`.
|
|
495
|
+
- You can now directly test for `undefined` in `.out` using `this.undef` / `ATestSource.undefSymbol`.
|
|
496
|
+
- `Error` objects are now compared accurately.
|
|
497
|
+
- You can now identify member types with sigils (`.` / `:` / `()`) in `.of`, `.plus`, and `.from`, though they aren't necessary.
|
|
498
|
+
- **(Breaking change)** The `actual` for throw tests is now the entire thrown object (usually an `Error`), rather than the `Error`'s message text.
|
|
499
|
+
- **(Breaking change)** `ATestSource` is now a default export, changing its imports from `import { ATestSource } from` to `import ATestSource from`.
|
|
500
|
+
|
|
501
|
+
> Risei has been massively re-engineered internally along with these improvements.
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
<details>
|
|
506
|
+
<summary>
|
|
507
|
+
Older releases
|
|
508
|
+
</summary>
|
|
509
|
+
|
|
510
|
+
- Release **1.3.4** (March, 2024) fixed a bug that caused class display problems in some cases.
|
|
511
|
+
|
|
512
|
+
- Release **1.3.3** (March, 2024) fixed a major bug that prevented tests from running in Windows.
|
|
513
|
+
|
|
514
|
+
- Release **1.3.2** (February, 2024) only improved this read-me's contents.
|
|
515
|
+
|
|
516
|
+
- Release **1.3.1** (February, 2024) reversed some changes in 1.3.0 that did not work as hoped.
|
|
517
|
+
|
|
518
|
+
- Release **1.3.0** (February, 2024) added the loading-error gold bar and fixed a test-sorting bug.
|
|
519
|
+
|
|
520
|
+
- Release **1.2.0** (January, 2024) changed test sorting to move last-edited tests to the end.
|
|
521
|
+
|
|
522
|
+
- Release **1.1.2** of Risei (January, 2024) fixed class displays and inaccurate `Date` comparisons.
|
|
523
|
+
|
|
524
|
+
> The oldest releases are no longer listed here, and old releases are dropped progressively over time. Using the latest release is recommended.
|
|
525
|
+
|
|
526
|
+
</details>
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
## Known issues and workarounds
|
|
530
|
+
|
|
531
|
+
The only issue at present is the use of mutated args when tested code mutates them, when collapsing forward.
|
|
532
|
+
|
|
533
|
+
- The workaround is simply to restate the args for each test.
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
## Exclusions from Risei
|
|
537
|
+
|
|
538
|
+
The following are not supported at present:
|
|
539
|
+
|
|
540
|
+
- Use of `async` syntax
|
|
541
|
+
- Any code not in classes, even in ESM `export` modules, AKA _loose code_
|
|
542
|
+
- CJS syntax using `require()`
|
|
543
|
+
- Performing step-through debugging of tests and tested code
|
|
544
|
+
- Comparing rare JS object types like `Proxy` or `ArrayBuffer` in test assertions
|
|
545
|
+
- Testing for inequality of values
|
|
546
|
+
|
|
547
|
+
Some of these may be supported in the future.
|
|
548
|
+
|
|
549
|
+
- You can save a lot of time by using Risei for most of your code, but another framework for whatever it doesn't support.
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
## Maker
|
|
554
|
+
|
|
555
|
+
Risei is written by myself, Ed Fallin. I'm a longtime software developer who likes to find better ways to do things.
|
|
556
|
+
|
|
557
|
+
If you find Risei useful, consider spreading the word to other devs, making a donation, suggesting enhancements, or proposing sponsorships.
|
|
558
|
+
|
|
559
|
+
You can get in touch about Risei at **riseimaker@gmail.com**.
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
## License
|
|
564
|
+
|
|
565
|
+
Risei is published for use under the terms of the MIT license:
|
|
566
|
+
|
|
567
|
+
<div style="border: solid darkgray 1px; padding: 0.5rem;">
|
|
568
|
+
|
|
569
|
+
<b>Risei Copyright © 2023–2024 Ed Fallin</b>
|
|
570
|
+
|
|
571
|
+
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:
|
|
572
|
+
|
|
573
|
+
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
|
574
|
+
|
|
575
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
576
|
+
</div>
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
package/index.js
CHANGED
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
// region Test-running dependencies
|
|
12
12
|
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
13
|
+
import TestRunner from "./system/TestRunner.js";
|
|
14
|
+
import LocalCaller from "./system/LocalCaller.js";
|
|
15
|
+
import TerminalReporter from "./system/TerminalReporter.js";
|
|
16
|
+
import TestFinder from "./system/TestFinder.js";
|
|
17
17
|
|
|
18
18
|
// endregion Test-running dependencies
|
|
19
19
|
|
|
20
20
|
// region Display dependencies
|
|
21
21
|
|
|
22
22
|
import chalk from "chalk";
|
|
23
|
-
import
|
|
23
|
+
import Moment from "./system/Moment.js";
|
|
24
24
|
|
|
25
25
|
// endregion Display dependencies
|
|
26
26
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "risei",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Risei
|
|
3
|
+
"version": "2.0.0",
|
|
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",
|
|
7
7
|
"test",
|
|
@@ -19,16 +19,17 @@
|
|
|
19
19
|
],
|
|
20
20
|
"author": "Ed Fallin <riseimaker@gmail.com>",
|
|
21
21
|
"license": "MIT",
|
|
22
|
+
"homepage": "https://deusware.com/risei",
|
|
22
23
|
"type": "module",
|
|
23
24
|
"private": false,
|
|
24
25
|
"scripts": {
|
|
25
26
|
"start": "node ./bin/www",
|
|
26
|
-
"rself": "clear
|
|
27
|
-
"rtest": "clear
|
|
28
|
-
"xtest": "clear
|
|
29
|
-
"mixedtest": "clear
|
|
30
|
-
"alltest": "clear
|
|
31
|
-
"test": "clear
|
|
27
|
+
"rself": "clear && node system/Risei.js",
|
|
28
|
+
"rtest": "clear && node ./node_modules/risei/index.js",
|
|
29
|
+
"xtest": "clear && mocha **/*.tests.js",
|
|
30
|
+
"mixedtest": "clear && mocha **/*.tests.js; node ./node_modules/risei/index.js",
|
|
31
|
+
"alltest": "clear && mocha **/*.tests.js; node ./node_modules/risei/index.js; node system/Risei.js",
|
|
32
|
+
"test": "clear && node ./node_modules/risei/index.js"
|
|
32
33
|
},
|
|
33
34
|
"risei": {
|
|
34
35
|
"tests": "**.rt.js"
|
|
@@ -45,12 +46,10 @@
|
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"chai": "^4.3.6",
|
|
47
48
|
"chalk": "^5.0.0",
|
|
48
|
-
"cookie-parser": "~1.4.4",
|
|
49
49
|
"debug": "~2.6.9",
|
|
50
|
-
"express": "^4.18.2",
|
|
51
50
|
"fs": "^0.0.1-security",
|
|
52
51
|
"mocha": "^10.0.0",
|
|
53
52
|
"morgan": "~1.9.1",
|
|
54
|
-
"risei": "1.3.
|
|
53
|
+
"risei": "^1.3.4"
|
|
55
54
|
}
|
|
56
55
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**/
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { SpoofDefinition } from "./SpoofDefinition.js";
|
|
3
|
+
import SpoofDef from "./SpoofDef.js";
|
|
5
4
|
|
|
6
|
-
export class ASpoofingFixture
|
|
7
|
-
constructor(
|
|
8
|
-
|
|
5
|
+
export default class ASpoofingFixture {
|
|
6
|
+
constructor() {
|
|
7
|
+
/* No operations. */
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
/* A spoof might be supplied as a custom function,
|
|
@@ -25,8 +24,8 @@ export class ASpoofingFixture extends ATestFixture {
|
|
|
25
24
|
// Recursive case: Spoof defines a whole object
|
|
26
25
|
// tree to be returned as method output.
|
|
27
26
|
if (Array.isArray(spoofOutput)) {
|
|
28
|
-
if (spoofOutput.every(x =>
|
|
29
|
-
spoofOutput =
|
|
27
|
+
if (spoofOutput.every(x => SpoofDef.isASpoof(x))) {
|
|
28
|
+
spoofOutput = SpoofDef.fromNonceTuples(spoofOutput);
|
|
30
29
|
spoofOutput = this.spoofObjectTree(spoofOutput);
|
|
31
30
|
}
|
|
32
31
|
}
|
|
@@ -40,7 +39,7 @@ export class ASpoofingFixture extends ATestFixture {
|
|
|
40
39
|
possibly with intermediate objects. */
|
|
41
40
|
spoofObjectTree(ofAsPairs) {
|
|
42
41
|
let spoof = {};
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
for (let pair of ofAsPairs) {
|
|
45
44
|
let chain = pair.of;
|
|
46
45
|
let output = pair.as;
|