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/README.md
CHANGED
|
@@ -1,51 +1,73 @@
|
|
|
1
1
|
|
|
2
|
-
# Risei
|
|
2
|
+
# Risei Read-Me
|
|
3
3
|
|
|
4
|
-
##
|
|
4
|
+
## Overview
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
* Whip up full test coverage of object-oriented or object-hosted JavaScript and TypeScript in no time.
|
|
8
|
-
* Refactor or replace existing designs without worrying about the cost in past or future test time.
|
|
9
|
-
* Create tests with immediate confidence, because you can't introduce mistakes in test code you write.
|
|
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.
|
|
10
7
|
|
|
8
|
+
Risei does all this by replacing hand-coded tests with simple declarative syntax.
|
|
11
9
|
|
|
10
|
+
- Risei has many convenient and time-saving [features](#features-of-risei).
|
|
11
|
+
- Risei works with object-oriented JavaScript in modules.
|
|
12
|
+
- Risei works with TypeScript after just a few [tweaks](https://deusware.com/risei/index.html#typescript-with-risei).
|
|
12
13
|
|
|
14
|
+
You can find a longer version of this read-me at [https://deusware.com/risei](https://deusware.com/risei/index.html). It expands greatly on the information here.
|
|
13
15
|
|
|
14
|
-
## The Risei approach
|
|
15
16
|
|
|
16
|
-
Risei replaces coded tests with simple declarative syntax that's far easier to draft than any other unit-testing approach.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## Examples
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Here are two example tests, in which `SortModel.countSort()` is being tested using the inputs from `.in` and the expected output from `.out`:
|
|
21
21
|
|
|
22
|
+

|
|
22
23
|
|
|
23
|
-
Here is the terminal output of these two example tests. Tests are grouped by class and method. Inputs, expected, and actual values are displayed for all tests, to make intended usage obvious at a glance:
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Here are the example test results for those two tests:
|
|
26
26
|
|
|
27
|
+

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

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

|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
- This bar is red when any tests fail, much as you'd expect.
|
|
41
44
|
|
|
42
|
-
Risei is under active development. If you've been here before, check out [what's new in Risei](#whats-new-in-risei) for the latest. Also see [known issues and workarounds](#known-issues-and-workarounds) and [limitations in Risei](#limitations-in-risei).
|
|
43
45
|
|
|
44
|
-
There are a few additional or different steps for [using Typescript with Risei](#using-typescript-with-risei).
|
|
45
46
|
|
|
47
|
+
## Status
|
|
46
48
|
|
|
49
|
+
Risei is under active development and new enhancements appear often. The latest release, **2.0.1**, brings many major improvements, including broader capabilities and simpler syntax, and a few minor breaking changes.
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
Check out the [full list of changes](#version-history).
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Features of Risei
|
|
56
|
+
|
|
57
|
+
- #### Declarative syntax to write tests simply and quickly. ► [Writing tests](https://deusware.com/risei/index.html#writing-tests) (basics below)
|
|
58
|
+
- #### Easy-to-read test definitions and test outputs. ► [Test and output examples](https://deusware.com/risei/index.html#examples) (also above)
|
|
59
|
+
- #### Even less to write by stating reused test properties only once. ► [Collapsing forward](https://deusware.com/risei/index.html#collapsing-forward) (basics below)
|
|
60
|
+
- #### Built-in declarative syntax to fake test-time values from dependencies. ► [Spoofing using `.plus`](https://deusware.com/risei/index.html#spoofing) (basics below)
|
|
61
|
+
- #### Testing properties, methods, static and instance members all the same way. ► [Properties and statics](https://deusware.com/risei/index.html#testing-properties-and-static-methods)
|
|
62
|
+
- #### Testing `throw` paths effortlessly. ► [Using `.and: "throws"`](https://deusware.com/risei/index.html#using-and-throws)
|
|
63
|
+
- #### Deriving actual values to test from raw outputs or property retrieval. ► [Using `.from`](https://deusware.com/risei/index.html#using-from)
|
|
64
|
+
- #### Setting up and tearing down arbitrary test state. ► [Using `.do` and `.undo`](https://deusware.com/risei/index.html#using-do-and-undo)
|
|
65
|
+
- #### Testing for `undefined` as output. ► [Using `this.undef`](https://deusware.com/risei/index.html#using-undef)
|
|
66
|
+
|
|
67
|
+
- And more! Check out the full [Risei home page](https://deusware.com/risei).
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
49
71
|
|
|
50
72
|
Install Risei for development time only:
|
|
51
73
|
|
|
@@ -53,7 +75,7 @@ Install Risei for development time only:
|
|
|
53
75
|
npm install --save-dev risei
|
|
54
76
|
```
|
|
55
77
|
|
|
56
|
-
|
|
78
|
+
Ensure that `package.json` specifies ECMAScript modules:
|
|
57
79
|
|
|
58
80
|
```json
|
|
59
81
|
"type": "module"
|
|
@@ -67,105 +89,17 @@ And add Risei's metadata to `package.json`:
|
|
|
67
89
|
}
|
|
68
90
|
```
|
|
69
91
|
|
|
70
|
-
Just a [few extra steps](#using-typescript-with-risei) are required for Risei to work with TypeScript.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
### Siting tests
|
|
75
|
-
|
|
76
|
-
Add tests in files ending in `.rt.js` like this:
|
|
77
|
-
|
|
78
|
-
```javascript
|
|
79
|
-
import { ATestSource } from "risei/ATestSource";
|
|
80
|
-
import { ClassToBeTested } from "somewhere";
|
|
81
|
-
|
|
82
|
-
export class SomeTests extends ATestSource {
|
|
83
|
-
tests = [ ... ]; // This array is where test definitions are written.
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
### Writing tests
|
|
90
|
-
|
|
91
|
-
Write tests with Risei's easy syntax:
|
|
92
|
-
|
|
93
|
-
```javascript
|
|
94
|
-
tests = [ ...
|
|
95
|
-
{ on: ContainerModelClass, with: [ "a", "b", "c" ], // Target class and constructor args.
|
|
96
|
-
of: "doesContain", // Target method.
|
|
97
|
-
for: "When the input arg is present, returns true.", // Description of test.
|
|
98
|
-
in: [ "c" ], // Inputs.
|
|
99
|
-
out: true }, // Expected output.
|
|
100
|
-
...];
|
|
101
|
-
```
|
|
102
|
-
|
|
103
92
|
|
|
104
93
|
|
|
105
|
-
|
|
94
|
+
## Running Tests
|
|
106
95
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
/* All of the following tests are of ContainerModelClass. */
|
|
111
|
-
{ on: ContainerModelClass, with: [ "a", "b", "c", "a", "b" ] },
|
|
112
|
-
|
|
113
|
-
/* Two tests for ContainerModelClass .doesContain(). */
|
|
114
|
-
{ of: "doesContain" },
|
|
115
|
-
{ for: "When the input arg is present, returns true.",
|
|
116
|
-
in: [ "c" ], out: true },
|
|
117
|
-
{ for: "When the input arg is not present, returns false.",
|
|
118
|
-
in: [ "d" ], out: false },
|
|
119
|
-
|
|
120
|
-
/* First test for ContainerModelClass .countOf(). */
|
|
121
|
-
{ of: "countOf", for: "Returns the number present.", in: [ "b" ], out: 2 }
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
- This _collapsing forward_ is reset when you change the class in `.on`, when you give the property a new value, or you erase it with an empty array `[]`.
|
|
125
|
-
|
|
126
|
-
- Tests are isolated in general, but if the code you're testing mutates its arguments, you have to state them in each test so the mutations aren't carried forward.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
#### Spoof away code dependencies:
|
|
131
|
-
|
|
132
|
-
When your tests need certain results from code dependencies, just _spoof_ what you want using a `.plus` test property:
|
|
133
|
-
|
|
134
|
-
```javascript
|
|
135
|
-
{ on: CombinerClass, with: [],
|
|
136
|
-
of: "combineResults",
|
|
137
|
-
plus: [
|
|
138
|
-
{ on: ClassA, of: "someMethod", as: 10 }, // Spoof this ClassA method to return 10.
|
|
139
|
-
{ on: ClassB, of: "someMethod" }, // Spoof this ClassB method not to return (or do) anything.
|
|
140
|
-
{ of: "firstMethod", as: [ 7, 8, 9, 10 ] }, // Spoof a method on the tested class (CombinerClass).
|
|
141
|
-
{ of: "secondMethod" }, // Spoof a method on CombinerClass not to do anything.
|
|
142
|
-
{ on: ClassC, // Spoof this ClassC method to be this nonce code.
|
|
143
|
-
of: "someMethod",
|
|
144
|
-
as: (arg) => { return { color: arg }; } },
|
|
145
|
-
{ on: ClassD, as: [ // Spoof two methods on ClassD at the same time.
|
|
146
|
-
{ of: "firstMethod", as: 11 },
|
|
147
|
-
{ of: "secondMethod", as: 12 }
|
|
148
|
-
] },
|
|
149
|
-
],
|
|
150
|
-
for: "When called, returns an array of values from sources.",
|
|
151
|
-
in: [ "green" ], out: [ 7, 8, 9, 10, 10, 11, 12, { color: "green" } ] }
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
- It's just like _fakes_, _mocks_, or _test doubles_ in other test systems, but much easier to write and read.
|
|
155
|
-
|
|
156
|
-
- Spoofed code is fully isolated within tests, even though spoof definitions collapse forward across tests (and within `.plus`).
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
### Running tests
|
|
161
|
-
|
|
162
|
-
Run your tests by invoking Risei's `index.js` file:
|
|
96
|
+
Once you have some tests written, you can run them manually:
|
|
163
97
|
|
|
164
98
|
```bash
|
|
165
99
|
node ./node_modules/risei/index.js
|
|
166
100
|
```
|
|
167
101
|
|
|
168
|
-
Or write a `package.json`
|
|
102
|
+
Or write a script in `package.json` that does the same:
|
|
169
103
|
|
|
170
104
|
```json
|
|
171
105
|
"scripts": {
|
|
@@ -179,438 +113,158 @@ And then run that script:
|
|
|
179
113
|
npm test
|
|
180
114
|
```
|
|
181
115
|
|
|
182
|
-
Risei can be used alongside other test frameworks, and even run with them from the same test script if you want.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
### Learning more about Risei
|
|
187
|
-
|
|
188
|
-
Read through the rest of this doc to learn more about Risei's many capabilities, and about where Risei is headed.
|
|
189
|
-
|
|
190
|
-
- For instance, learn about using `.and` to test static methods or throws.
|
|
191
|
-
- Or learn about using `.from` to test property results or other state.
|
|
192
|
-
- Don't miss out: Once you've tried the basics, read the rest...!
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
## Risei in depth
|
|
198
|
-
|
|
199
|
-
<details>
|
|
200
|
-
<summary>
|
|
201
|
-
There's plenty more to learn about Risei and how it makes testing easy...
|
|
202
|
-
</summary>
|
|
203
|
-
|
|
204
|
-
### Installation
|
|
205
|
-
|
|
206
|
-
- You can use any file extension you want in Risei's metadata in `package.json`, and Risei will scan those files for tests.
|
|
207
|
-
|
|
208
|
-
- You can install Risei outside of development time the usual way, but as with any test system, this is definitely not recommended.
|
|
209
|
-
|
|
210
|
-
- Risei uses ESM syntax, and may not work in older environments where that's not available or is patched in.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
### Siting tests
|
|
215
|
-
|
|
216
|
-
- Match your Risei test file's extensions to whatever you choose in the metadata in `package.json`.
|
|
217
|
-
|
|
218
|
-
- Test classes must inherit from `ATestSource` in `"risei/ATestSource"`: this type name is looked for specifically, so duck-typing doesn't work.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
### Writing tests
|
|
223
|
-
|
|
224
|
-
- The order and placement of test properties doesn't matter, although the order seen in the examples is probably most readable.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
#### Basic test properties and options for each:
|
|
229
|
-
|
|
230
|
-
| Name | Contents |
|
|
231
|
-
|--------|-----------------------------------------------------------------------------------------------------------|
|
|
232
|
-
| `on` | The class under test, as a symbol / name (already imported into the test file) |
|
|
233
|
-
| `with` | An array of any arguments to pass to the constructor of the tested class, or an empty array `[]` if none |
|
|
234
|
-
| `of` | The name of the method under test, as a string, no `()` needed |
|
|
235
|
-
| `for` | A description of what the test proves, for test output |
|
|
236
|
-
| `in` | An array of the input arguments to pass to the tested method, or an empty array `[]` if none |
|
|
237
|
-
| `out` | The expected return value, not in an array |
|
|
238
|
-
|
|
239
|
-
- There are additional properties for extended functionality, covered later.
|
|
240
|
-
- The property names were chosen to be easy to remember and type, but longer alternatives are available, covered later.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
### Test-property reuse _AKA_ Collapsing forward
|
|
245
|
-
|
|
246
|
-
- To save time and effort, any property you write is collapsed forward to subsequent tests unless you replace it or erase it with an empty array `[]`.
|
|
247
|
-
- Property values are gathered across partial test objects until they add up to a runnable test, which is then run.
|
|
248
|
-
- Changes to properties are combined with previous ones to produce intended new tests.
|
|
249
|
-
- For a rare and avoidable side-effect of this system, see [known issues and workarounds](#known-issues-and-workarounds).
|
|
250
|
-
|
|
251
|
-
- Test contents, reused or not, are automatically reset when it makes the most sense:
|
|
252
|
-
- Changing the tested class in `.on` wipes out all prior test properties, so the new class has a fresh start.
|
|
253
|
-
- Changing the tested method in `.of` wipes out only test properties related to methods, to preserve class values you usually want.
|
|
254
|
-
|
|
255
|
-
- Individual tests remain isolated, except for when args are mutated by the code under test.
|
|
256
|
-
- Restate args for each test when the code mutates them.
|
|
257
|
-
- This limitation is the result of limits on what can be copied reliably in JavaScript.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
### Choosing test-only dependency inputs _AKA_ Spoofing
|
|
261
|
-
|
|
262
|
-
- Spoofing is Risei's way of providing test-only inputs from dependencies the tested code uses, written in simple declarative syntax.
|
|
263
|
-
- It's therefore the equivalent of test doubles, mocks, fakes, and so on.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
- As the earlier examples and this table show, you can spoof in many ways, both on the dependencies, and on the class being tested.
|
|
267
|
-
- All classes whose members are being spoofed have to be imported.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
#### Spoof-definition properties:
|
|
272
|
-
|
|
273
|
-
| Name | Necessary or Optional | Contents |
|
|
274
|
-
|------|-----------------------|----------------------------------------------------------------------------|
|
|
275
|
-
| `on` | Optional | Symbol for a type (class); if omitted, the targeted model class is assumed |
|
|
276
|
-
| `of` | Necessary | Name of the method to spoof, as a string, trailing `()` not needed |
|
|
277
|
-
| `as` | Optional | Value to return or nonce implementation; if omitted, an empty method with no return value is used<br> — or —<br>A list of partial spoof definitions for the same class |
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
- You can spoof multiple methods of a class individually, or list them together with partial definitions as seen in the example, with the same effect either way.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
#### Partial spoof-definition properties:
|
|
284
|
-
|
|
285
|
-
| Name | Necessary or Optional | Contents |
|
|
286
|
-
|------|-----------------------|---------------------------------------------------------------------------------------------------|
|
|
287
|
-
| `of` | Necessary | Name of the method to spoof, as a string, trailing `()` not needed |
|
|
288
|
-
| `as` | Optional | Value to return or nonce implementation; if omitted, an empty method with no return value is used |
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
- Defining new spoofing wipes out all old definitions.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
- Spoofing is done at the start of each test and undone at the end of each test, keeping all tests isolated.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
## Advanced Risei usage
|
|
300
116
|
|
|
301
|
-
### Testing special test conditions with `.and`
|
|
302
117
|
|
|
303
|
-
|
|
304
|
-
- At present, the values available are `"static"` and `"throw"` / `"throws"` (either one works).
|
|
305
|
-
- You can list `static` and `throw` / `throws` together if needed, separated by a space.
|
|
118
|
+
## Writing Tests
|
|
306
119
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
#### Testing static methods
|
|
312
|
-
|
|
313
|
-
To test a static method, use an `.and` of `"static"`:
|
|
314
|
-
|
|
315
|
-
```javascript
|
|
316
|
-
{ on: StaticTextProfileModel, with: [] },
|
|
317
|
-
|
|
318
|
-
{ of: "getTextProfile",
|
|
319
|
-
for: "Returns accurate profile of arg text.",
|
|
320
|
-
and: "static",
|
|
321
|
-
in: [ "This is a short sentence." ],
|
|
322
|
-
out: { isFullSentence: true, characters: 25, words: 5 }
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
#### Testing throws
|
|
329
|
-
|
|
330
|
-
To test a throw, use an `.and` of `"throw"` or `"throws"`:
|
|
120
|
+
You write tests in `.rt.js` files like this:
|
|
331
121
|
|
|
332
122
|
```javascript
|
|
333
|
-
|
|
123
|
+
import ATestSource from "risei/ATestSource";
|
|
124
|
+
import ClassToTest from "ClassToTest.js";
|
|
334
125
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
and: "throws",
|
|
338
|
-
in: [],
|
|
339
|
-
out: "Could not build a profile. Did you forget to provide a text?"
|
|
126
|
+
export class SomeTests extends ATestSource {
|
|
127
|
+
tests = [ ... ];
|
|
340
128
|
}
|
|
341
129
|
```
|
|
342
130
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
### Testing object properties and other non-`return` results
|
|
346
|
-
|
|
347
|
-
To test a value that isn't the exercised code's return value, you use `.out` normally, and you add a `.from` property to your test, which can be one of two things:
|
|
348
|
-
|
|
349
|
-
| Contents of `.from` | Usage |
|
|
350
|
-
|--------------------------------|-------------------------------------------------------------------------|
|
|
351
|
-
| Property name as string | Retrieves the actual from the named property on the test's target class |
|
|
352
|
-
| Specialized function | Retrieves the actual from either its `target` or `test` parameter |
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
- Getting properties, mutated args, and other non-return values is known as _retrieving_.
|
|
356
|
-
- Retrieving definitions collapse forward across tests unless replaced with new definitions, or erased by setting `.from` to an empty array `[]`.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
<details>
|
|
360
|
-
<summary>
|
|
361
|
-
More information
|
|
362
|
-
</summary>
|
|
363
|
-
<br>
|
|
364
|
-
|
|
365
|
-
- Use of `.from` with a property name looks like this:
|
|
131
|
+
Write individual tests as plain JavaScript objects with Risei's simple syntax:
|
|
366
132
|
|
|
367
133
|
```javascript
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
134
|
+
tests = [ ...
|
|
135
|
+
{ on: ContainerClass, with: [ "a", "b", "c" ], // Target class and constructor args.
|
|
136
|
+
of: "doesContain", // Target method name.
|
|
137
|
+
for: "When the arg is present, returns true.", // Description of test.
|
|
138
|
+
in: [ "c" ], // Inputs to method.
|
|
139
|
+
out: true }, // Expected output.
|
|
140
|
+
... ];
|
|
373
141
|
```
|
|
374
142
|
|
|
375
|
-
-
|
|
376
|
-
|
|
377
|
-
When the contents of `.from` are a function, these are the two parameters:
|
|
378
|
-
|
|
379
|
-
| Name | Contents |
|
|
380
|
-
|----------|---------------------------------------------------------------------------------------------------|
|
|
381
|
-
| `target` | The instance of the class being tested |
|
|
382
|
-
| `test` | The test definition itself, whose properties may have been mutated by the tested method |
|
|
143
|
+
- Use empty arrays for `.in` or `.with` when there are no args to pass.
|
|
144
|
+
- You can use [long names](https://deusware.com/risei/index.html#long-names) for properties if you want.
|
|
383
145
|
|
|
384
146
|
|
|
385
|
-
- The `test` parameter to a `.from` function contains all of the properties of your test definition, including those that collapsed forward.
|
|
386
|
-
- These properties are available by both short or long names (covered later).
|
|
387
|
-
- The `test.on` property always references the class under test, rather than an instance of it.
|
|
388
147
|
|
|
148
|
+
## Basic collapsing forward example
|
|
389
149
|
|
|
390
|
-
|
|
150
|
+
You can state test properties once and let them _collapse forward_ across subsequent tests to save time and make tests easier to read.
|
|
391
151
|
|
|
392
|
-
|
|
393
|
-
- Another usage of a `.from` function is to look at an input to see if it was changed as expected:
|
|
152
|
+
Risei collapses values together from partial or full test objects until it has a full test to run. Every property collapses forward until it is changed or wiped out:
|
|
394
153
|
|
|
395
154
|
```javascript
|
|
396
|
-
{ on:
|
|
397
|
-
|
|
398
|
-
{ of: "addHash",
|
|
399
|
-
for: "The content's hash is added to the input arg a new property.",
|
|
400
|
-
in: [ { content: "SecretValue" }, iterations: 8 ],
|
|
401
|
-
out: { content: "SecretValue", hash: "FasBdQ-fljk%asPcdf" },
|
|
402
|
-
from: (target, test) => { return test.in[0]; }
|
|
403
|
-
},
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
</details>
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
### Property long names
|
|
411
|
-
|
|
412
|
-
Property names are short so they're easy to use. Some of them overlap with JavaScript keywords, but this causes no harm.
|
|
413
|
-
|
|
414
|
-
All test properties have long names that you can use instead of the short ones if you prefer, mixed together as much as you want. None of the long names are JavaScript keywords.
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
<details>
|
|
418
|
-
<summary>
|
|
419
|
-
Test properties' short and long names
|
|
420
|
-
</summary>
|
|
421
|
-
|
|
422
|
-
| Short Name | Long Name |
|
|
423
|
-
|------------|-----------|
|
|
424
|
-
| `on` | `type` |
|
|
425
|
-
| `with` | `initors` |
|
|
426
|
-
| `of` | `method` |
|
|
427
|
-
| `for` | `nature` |
|
|
428
|
-
| `in` | `inputs` |
|
|
429
|
-
| `out` | `output` |
|
|
430
|
-
| `plus` | `spoofed` |
|
|
431
|
-
| `from` | `source` |
|
|
432
|
-
| `and` | `factors` |
|
|
433
|
-
|
|
434
|
-
</details>
|
|
435
|
-
|
|
436
|
-
<br>
|
|
437
|
-
|
|
438
|
-
<details>
|
|
439
|
-
<summary>
|
|
440
|
-
Spoof properties' short and long names
|
|
441
|
-
</summary>
|
|
442
|
-
|
|
443
|
-
| Short Name | Long Name |
|
|
444
|
-
|------------|-----------|
|
|
445
|
-
| `on` | `target` |
|
|
446
|
-
| `of` | `method` |
|
|
447
|
-
| `as` | `output` |
|
|
448
|
-
|
|
449
|
-
</details>
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
### Further capabilities of Risei
|
|
454
|
-
|
|
455
|
-
Constructors can be tested with no special test properties or keywords.
|
|
456
|
-
- The method name in `.of` is simply `"constructor"`.
|
|
457
|
-
- Any `.with` args are completely ignored for a constructor test. Only those in the test's `.in` property are used.
|
|
458
|
-
- However, a `.with` must still be provided. It can simply be an empty array `[]`.
|
|
459
|
-
|
|
460
|
-
</details>
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
155
|
+
{ on: ContainerClass, with: [ "a", "b", "c" ] }, // Following tests are of this class.
|
|
464
156
|
|
|
465
|
-
|
|
157
|
+
{ of: "doesContain" }, // Following tests are of this method.
|
|
158
|
+
{ for: "Returns true when arg present.", in: [ "c" ], out: true }, // First test: now Risei has enough to run on.
|
|
159
|
+
{ for: "Returns false when arg absent.", in: [ "d" ], out: false }, // Next test: same method, different test case.
|
|
466
160
|
|
|
467
|
-
|
|
161
|
+
{ of: "countOf" }, // Change of tested method. Method-related props are wiped out.
|
|
162
|
+
...
|
|
468
163
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
If you follow the general Risei set-up, you can just make the following few modifications for TypeScript applications.
|
|
472
|
-
- These steps have worked with both Angular and React.
|
|
473
|
-
- Varying other approaches are also sure to work, depending on the other technical choices in play.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
### In `package.json`:
|
|
478
|
-
|
|
479
|
-
Add transpilation and deletion operations to the `test` script, with each operation conditional on completion of the preceding one:
|
|
480
|
-
|
|
481
|
-
```json
|
|
482
|
-
"scripts": {
|
|
483
|
-
"test": "tsc && node ./node_modules/risei/index.js && rm -r ./dist/out-tsc"
|
|
484
|
-
}
|
|
164
|
+
{ on: SortingClass, with: [ ] }, // Change of tested class. All existing props are wiped out.
|
|
485
165
|
```
|
|
486
166
|
|
|
487
|
-
-
|
|
488
|
-
-
|
|
489
|
-
- The deletion path must match the `outDir` in `tsconfig.json`.
|
|
490
|
-
|
|
491
|
-
> You can run Risei manually with the same sequence of operations.
|
|
167
|
+
- There are more options available, and an exclusion for mutated args.
|
|
168
|
+
- Learn all the details [here](https://deusware.com/risei/index.html#collapsing-forward).
|
|
492
169
|
|
|
493
170
|
|
|
494
171
|
|
|
495
|
-
|
|
172
|
+
## Basic spoofing example
|
|
496
173
|
|
|
497
|
-
You
|
|
174
|
+
You can use declarative _spoofing_ syntax to define what dependencies of your targeted code return for it to use.
|
|
498
175
|
|
|
499
|
-
|
|
500
|
-
{
|
|
501
|
-
"outDir": "dist/out-tsc",
|
|
502
|
-
"noEmit": false
|
|
503
|
-
}
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
- The `outDir` can be any location. It's best not to just use `dist`, where adding or deleting files may interfere with web frameworks' build steps.
|
|
507
|
-
- These settings are normally not near each other in the file (as seen here).
|
|
508
|
-
- You can also set the `target` to `es6` or higher, although Risei works fine with ES5.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
### In test files:
|
|
514
|
-
|
|
515
|
-
All `import` statements have to point to the Javascript (`.js`) files in the `outDir` path or subfolders there, using relative-path syntax:
|
|
176
|
+
The most basic spoofing looks like this:
|
|
516
177
|
|
|
517
178
|
```javascript
|
|
518
|
-
|
|
179
|
+
{
|
|
180
|
+
on: TestedClass,
|
|
181
|
+
...
|
|
182
|
+
plus: [
|
|
183
|
+
{ on: Dependency, of: "someMethod", as: 10 }, // Dependency.someMethod() returns 10 in this test.
|
|
184
|
+
{ of: "testedClassMethod", as: 11 } // TestedClass.testedClassMethod() returns 11 in this test.
|
|
185
|
+
],
|
|
186
|
+
...
|
|
187
|
+
}
|
|
519
188
|
```
|
|
520
189
|
|
|
521
|
-
-
|
|
190
|
+
- Numerous additional capabilities, as well as compressed syntax, can be mixed freely in many ways.
|
|
191
|
+
- Learn more [here](https://deusware.com/risei/index.html#spoofing).
|
|
522
192
|
|
|
523
193
|
|
|
524
194
|
|
|
195
|
+
## TypeScript with Risei
|
|
525
196
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
The following are not supported at present:
|
|
529
|
-
- Use of `async` syntax
|
|
530
|
-
- Code written in ESM `export` modules, but not within classes
|
|
531
|
-
- Standalone functions and other functionality not built into classes, AKA _loose code_
|
|
532
|
-
- CJS syntax using `require()`
|
|
533
|
-
- Spoofing mixes of properties and methods, such as `something.method.property.method`
|
|
534
|
-
- Debugging model code during tests
|
|
535
|
-
- Comparing rare JS object types like `Proxy` or `ArrayBuffer` in test assertions
|
|
536
|
-
|
|
537
|
-
Some of these are on the tentative future-development list. In the meantime, Risei can be used to save development time for most of your JavaScript code, while these can be tested using another framework invoked alongside Risei.
|
|
197
|
+
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.
|
|
538
198
|
|
|
199
|
+
- Learn all about it [here](https://deusware.com/risei/index.html#typescript-with-risei).
|
|
539
200
|
|
|
540
201
|
|
|
541
202
|
|
|
542
203
|
## Troubleshooting
|
|
543
204
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
| Error condition or text | Probable cause |
|
|
547
|
-
|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
548
|
-
| Gold bar appears below the summary, and the number of tests run drops | A syntax error caused a test file not to load; error output in the gold bar explains why |
|
|
549
|
-
| `"Test loading failed for... SyntaxError: Unexpected token ':'"` | Missing comma in your tests in the named file |
|
|
550
|
-
| `"Test loading failed for... SyntaxError: Unexpected token '.'"` | Using `this.tests = []` instead of `tests = []` in the file named |
|
|
551
|
-
| Other `... Unexpected token ...` errors | Some other syntax error in the file named, most likely a missing or extra delimiter |
|
|
552
|
-
| Unexpected extra, failing test/s | Partial test properties in a mid-list object have produced extra tests |
|
|
553
|
-
| Unexpected actual values, or unexpected passes / fails in test runs | Test properties from previous tests not replaced or reset with `[]` <br>— or —<br> Args mutated by tested code were not restated in following tests to isolate them |
|
|
205
|
+
If problems cause test files not to load, a gold bar appears and tests in those files disappear from output:
|
|
554
206
|
|
|
207
|
+

|
|
555
208
|
|
|
209
|
+
- Those and other problems can be solved with the help of this [troubleshooting guide](https://deusware.com/risei/index.html#troubleshooting).
|
|
556
210
|
|
|
557
211
|
|
|
558
|
-
##
|
|
559
|
-
|
|
560
|
-
Two issues are known to exist:
|
|
561
|
-
|
|
562
|
-
- When code mutates its args in test definitions, the mutated forms are used in subsequent tests unless those args are restated.
|
|
563
|
-
- To avoid this problem, just restate the args in each test of this code.
|
|
564
|
-
- The workaround for this — copying and initing objects to bypass mutation — requires too many assumptions to be reliable.
|
|
212
|
+
## Version history
|
|
565
213
|
|
|
214
|
+
- Release **2.0.1** (August, 2024) contains all of these changes:
|
|
215
|
+
- Risei now can determine automatically if tested methods are static.
|
|
216
|
+
- You can now directly test properties just like methods.
|
|
217
|
+
- Risei can also determine automatically if these are static.
|
|
218
|
+
- Directly tested properties appear in the output in the form `.name`.
|
|
219
|
+
- You can now spoof properties on the targeted class instance, and static properties on the target class itself.
|
|
220
|
+
- You can now perform arbitrary operations, if needed, with the two-part `.do` property and one-part `.undo` property.
|
|
221
|
+
- You can now change test properties without accidentally creating a new test using `.just`.
|
|
222
|
+
- You can now directly test for `undefined` in `.out` using `this.undef` / `ATestSource.undefSymbol`.
|
|
223
|
+
- `Error` objects are now compared accurately.
|
|
224
|
+
- You can now identify member types with `.`, `:`, and `()` in `.of`, `.plus`, and `.from`, though they aren't necessary.
|
|
225
|
+
- **(Breaking change)** The actual for throw tests is now the entire thrown object (usually an `Error`), rather than the `Error`'s message text.
|
|
226
|
+
- **(Breaking change)** `ATestSource` is now a default export, changing its imports from `import { ATestSource } from` to `import ATestSource from`.
|
|
566
227
|
|
|
567
|
-
|
|
568
|
-
- To avoid this problem, just change properties as part of whole new tests, or else restate `.of` along with the changed properties.
|
|
569
|
-
- Risei is actually working as intended when this happens, but ways to optionally prevent it are being considered.
|
|
228
|
+
> Risei has been massively re-engineered internally along with these improvements.
|
|
570
229
|
|
|
571
230
|
|
|
572
231
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
- Release **1.3.3** (March, 2024) fixes a major bug related to file paths that prevented any tests from loading or running in Windows environments.
|
|
580
|
-
- Risei is now fully usable in Windows environments again.
|
|
581
|
-
|
|
232
|
+
<details>
|
|
233
|
+
<summary>
|
|
234
|
+
Older releases
|
|
235
|
+
</summary>
|
|
582
236
|
|
|
583
|
-
- Release **
|
|
584
|
-
|
|
585
|
-
|
|
237
|
+
- Release **2.0.0** (August, 2024) is identical to 2.0.1 except for some unneeded extra files.
|
|
238
|
+
- Release **1.3.4** (March, 2024) fixed a bug that caused class display problems in some cases.
|
|
239
|
+
- Release **1.3.3** (March, 2024) fixed a major bug that prevented tests from running in Windows.
|
|
240
|
+
- Release **1.3.2** (February, 2024) only improved this read-me's contents.
|
|
241
|
+
- Release **1.3.1** (February, 2024) reversed some changes in 1.3.0 that did not work as hoped.
|
|
242
|
+
- Release **1.3.0** (February, 2024) added the loading-error gold bar and fixed a test-sorting bug.
|
|
243
|
+
- Release **1.2.0** (January, 2024) changed test sorting to move last-edited tests to the end.
|
|
244
|
+
- Release **1.1.2** of Risei (January, 2024) fixed class displays and inaccurate `Date` comparisons.
|
|
586
245
|
|
|
246
|
+
> The oldest releases are no longer listed here, and old releases are dropped progressively over time. Using the latest release is recommended.
|
|
587
247
|
|
|
588
|
-
|
|
589
|
-
- Supporting built-in total test isolation is not feasible with collapsing forward, nor is it usually necessary given easy usage alternatives.
|
|
590
|
-
- In contrast, collapsing forward is both highly beneficial and frequently used.
|
|
248
|
+
</details>
|
|
591
249
|
|
|
592
250
|
|
|
593
|
-
|
|
594
|
-
- If there are any test-loading errors, their messages now appear in a new gold bar that appears below the summary of the test run.
|
|
595
|
-
- A test-sorting error has also been fixed that occurred when no test files existed yet.
|
|
251
|
+
## Known issues and workarounds
|
|
596
252
|
|
|
253
|
+
The only issue at present is reuse of method args mutated by tested code when collapsing forward.
|
|
597
254
|
|
|
598
|
-
-
|
|
599
|
-
- Previously tests were displayed in the order found, which is always alphabetical (though is not guaranteed to be so).
|
|
600
|
-
- The order for all files except one remains the same.
|
|
601
|
-
- The last-edited file is moved to the bottom, making it easy to see the latest test results.
|
|
255
|
+
- The workaround is just to restate those args for each test.
|
|
602
256
|
|
|
603
257
|
|
|
604
|
-
|
|
605
|
-
- Classes were sometimes displayed in output as their entire definition. Now just the class name is displayed.
|
|
606
|
-
- All `Date` instances were considered equal, regardless of their actual value. Now they only are considered equal when they actually are.
|
|
258
|
+
## Exclusions from Risei
|
|
607
259
|
|
|
260
|
+
At present, there are a few things Risei doesn't support, such as `async` syntax. Some of these may be supported in the future.
|
|
608
261
|
|
|
609
|
-
-
|
|
262
|
+
- You can see the whole list [here](https://deusware.com/risei/index.html#exclusions-from-risei).
|
|
263
|
+
- You can save a lot of time by using Risei for most of your code, and another framework for whatever it doesn't support.
|
|
610
264
|
|
|
611
265
|
|
|
612
266
|
|
|
613
|
-
##
|
|
267
|
+
## Maker
|
|
614
268
|
|
|
615
269
|
Risei is written by myself, Ed Fallin. I'm a longtime software developer who likes to find better ways to do things.
|
|
616
270
|
|
|
@@ -620,8 +274,7 @@ You can get in touch about Risei at **riseimaker@gmail.com**.
|
|
|
620
274
|
|
|
621
275
|
|
|
622
276
|
|
|
623
|
-
|
|
624
|
-
## Risei license
|
|
277
|
+
## License
|
|
625
278
|
|
|
626
279
|
Risei is published for use under the terms of the MIT license:
|
|
627
280
|
|
|
@@ -638,3 +291,5 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
638
291
|
|
|
639
292
|
|
|
640
293
|
|
|
294
|
+
|
|
295
|
+
|