risei 1.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/LICENSE.txt +8 -0
- package/README.md +642 -0
- package/index.js +122 -0
- package/package.json +39 -0
- package/public/javascript/AComparer.js +9 -0
- package/public/javascript/ASpoofingFixture.js +84 -0
- package/public/javascript/ATestCaller.js +61 -0
- package/public/javascript/ATestFinder.js +25 -0
- package/public/javascript/ATestFixture.js +44 -0
- package/public/javascript/ATestReporter.js +25 -0
- package/public/javascript/ATestSource.js +8 -0
- package/public/javascript/ChosenTestFinder.js +30 -0
- package/public/javascript/ClassTestGroup.js +8 -0
- package/public/javascript/LocalCaller.js +22 -0
- package/public/javascript/MethodTestGroup.js +8 -0
- package/public/javascript/Moment.js +29 -0
- package/public/javascript/Risei.js +88 -0
- package/public/javascript/SpoofClassMethodsFixture.js +165 -0
- package/public/javascript/SpoofObjectMethodsFixture.js +52 -0
- package/public/javascript/SpoofTuple.js +238 -0
- package/public/javascript/TerminalReporter.js +222 -0
- package/public/javascript/TestFinder.js +140 -0
- package/public/javascript/TestGroup.js +25 -0
- package/public/javascript/TestResult.js +338 -0
- package/public/javascript/TestRunner.js +476 -0
- package/public/javascript/TestSummary.js +37 -0
- package/public/javascript/TestTuple.js +244 -0
- package/public/javascript/TotalComparer.js +229 -0
- package/usage-examples/Output-example.png +0 -0
- package/usage-examples/Summary-example.png +0 -0
- package/usage-examples/Syntax-example.png +0 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**/
|
|
2
|
+
|
|
3
|
+
/* Runs tests one by one as an ATestCaller requests them. */
|
|
4
|
+
|
|
5
|
+
import { TestTuple } from "./TestTuple.js";
|
|
6
|
+
import { TestGroup } from "./TestGroup.js";
|
|
7
|
+
import { ClassTestGroup } from "./ClassTestGroup.js";
|
|
8
|
+
import { MethodTestGroup } from "./MethodTestGroup.js";
|
|
9
|
+
import { TestResult } from "./TestResult.js";
|
|
10
|
+
import { TestSummary } from "./TestSummary.js";
|
|
11
|
+
|
|
12
|
+
import { SpoofClassMethodsFixture } from "./SpoofClassMethodsFixture.js";
|
|
13
|
+
import { SpoofObjectMethodsFixture } from "./SpoofObjectMethodsFixture.js";
|
|
14
|
+
import { SpoofTuple } from "./SpoofTuple.js";
|
|
15
|
+
|
|
16
|
+
import { AComparer } from "./AComparer.js";
|
|
17
|
+
import { TotalComparer } from "./TotalComparer.js";
|
|
18
|
+
|
|
19
|
+
export class TestRunner {
|
|
20
|
+
// region Static definition fields
|
|
21
|
+
|
|
22
|
+
// Used for a special test structural case;
|
|
23
|
+
// classes have one constructor by this name.
|
|
24
|
+
static #constructorName = "constructor";
|
|
25
|
+
|
|
26
|
+
// endregion Static definition fields
|
|
27
|
+
|
|
28
|
+
// region Private fields
|
|
29
|
+
|
|
30
|
+
// Components and state.
|
|
31
|
+
#tests;
|
|
32
|
+
|
|
33
|
+
#classSpoofer;
|
|
34
|
+
#instanceSpoofer;
|
|
35
|
+
|
|
36
|
+
#comparer;
|
|
37
|
+
|
|
38
|
+
// Results summary.
|
|
39
|
+
#numberRun;
|
|
40
|
+
#numberPassed;
|
|
41
|
+
#numberFailed;
|
|
42
|
+
#allDidPass;
|
|
43
|
+
|
|
44
|
+
// endregion Private fields
|
|
45
|
+
|
|
46
|
+
// region Properties
|
|
47
|
+
|
|
48
|
+
get tests() {
|
|
49
|
+
return this.#tests;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
set tests(value) {
|
|
53
|
+
this.#tests = value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// region Spoofing
|
|
57
|
+
|
|
58
|
+
get classSpoofer() {
|
|
59
|
+
return this.#classSpoofer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
set classSpoofer(value) {
|
|
63
|
+
this.#classSpoofer = value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get instanceSpoofer() {
|
|
67
|
+
return this.#instanceSpoofer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
set instanceSpoofer(value) {
|
|
71
|
+
this.#instanceSpoofer = value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// endregion Spoofing
|
|
75
|
+
|
|
76
|
+
get comparer() {
|
|
77
|
+
return this.#comparer;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
set comparer(value) {
|
|
81
|
+
this.#comparer = value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get numberRun() {
|
|
85
|
+
return this.#numberRun;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
set numberRun(value) {
|
|
89
|
+
this.#numberRun = value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get numberPassed() {
|
|
93
|
+
return this.#numberPassed;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
set numberPassed(value) {
|
|
97
|
+
this.#numberPassed = value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get numberFailed() {
|
|
101
|
+
return this.#numberFailed;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
set numberFailed(value) {
|
|
105
|
+
this.#numberFailed = value;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get allDidPass() {
|
|
109
|
+
return this.#allDidPass;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
set allDidPass(value) {
|
|
113
|
+
this.#allDidPass = value;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// endregion Properties
|
|
117
|
+
|
|
118
|
+
constructor() {
|
|
119
|
+
this.#tests = [];
|
|
120
|
+
this.#classSpoofer = new SpoofClassMethodsFixture();
|
|
121
|
+
this.#instanceSpoofer = new SpoofObjectMethodsFixture();
|
|
122
|
+
|
|
123
|
+
this.#numberRun = 0;
|
|
124
|
+
this.#numberPassed = 0;
|
|
125
|
+
this.#numberFailed = 0;
|
|
126
|
+
|
|
127
|
+
this.#allDidPass = true;
|
|
128
|
+
|
|
129
|
+
// Comparer is inited once for all tests, since compare() is reentrant.
|
|
130
|
+
this.#comparer = new TotalComparer();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// region Tests to run
|
|
134
|
+
|
|
135
|
+
useTests(tests) {
|
|
136
|
+
this.#tests = tests;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// endregion Tests to run
|
|
140
|
+
|
|
141
|
+
// region Running tests
|
|
142
|
+
|
|
143
|
+
// Generator for all test results,
|
|
144
|
+
// including groups and summary.
|
|
145
|
+
* [Symbol.iterator]() {
|
|
146
|
+
if (!this.#tests) {
|
|
147
|
+
throw new Error("No tests available to run. TestRunner's .tests must be set before an attempt to run is made.");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Converting so that property names can be relied on.
|
|
151
|
+
this.#tests = TestTuple.fromNonceTuples(this.tests);
|
|
152
|
+
|
|
153
|
+
// Needed for displaying classes and methods as groups.
|
|
154
|
+
let classGroup = new ClassTestGroup();
|
|
155
|
+
let methodGroup = new MethodTestGroup();
|
|
156
|
+
let atFirstClassMethod = false;
|
|
157
|
+
|
|
158
|
+
// Iterative running of all tests in current order.
|
|
159
|
+
for (let test of this.#tests) {
|
|
160
|
+
// Each new class should be a group, and its start
|
|
161
|
+
// should be retained for grouping of methods.
|
|
162
|
+
if (test.on.name !== classGroup.group) {
|
|
163
|
+
classGroup.group = test.on.name;
|
|
164
|
+
atFirstClassMethod = true;
|
|
165
|
+
yield classGroup;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Each new method name should be a group,
|
|
169
|
+
// and so should each method for a class.
|
|
170
|
+
if (test.of !== methodGroup.group || atFirstClassMethod) {
|
|
171
|
+
methodGroup.group = test.of;
|
|
172
|
+
atFirstClassMethod = false;
|
|
173
|
+
yield methodGroup;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let result = this.runOneTest(test);
|
|
177
|
+
yield result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Iterative returning of final results summary.
|
|
181
|
+
yield this.summarize();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
runOneTest(test) {
|
|
185
|
+
// Default outputs. Never left undefined.
|
|
186
|
+
test.didPass = false;
|
|
187
|
+
test.anyThrow = null;
|
|
188
|
+
|
|
189
|
+
// Spoofing based on any spoof
|
|
190
|
+
// definitions in .with and .of.
|
|
191
|
+
this.#spoofObjects(test);
|
|
192
|
+
|
|
193
|
+
// Gathering facts defining the nature of the test,
|
|
194
|
+
// some of which might change after the test is run.
|
|
195
|
+
let result = new TestResult(test);
|
|
196
|
+
result.setNature();
|
|
197
|
+
|
|
198
|
+
// Choosing the right test frame for the definition.
|
|
199
|
+
let testFrame = this.#supplyTestFrame(test);
|
|
200
|
+
testFrame = testFrame.bind(this);
|
|
201
|
+
|
|
202
|
+
// Actually running the test.
|
|
203
|
+
try {
|
|
204
|
+
testFrame(test);
|
|
205
|
+
}
|
|
206
|
+
catch (thrown) {
|
|
207
|
+
test.anyThrow = thrown;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Gathering facts based on the test run.
|
|
211
|
+
result.setResults();
|
|
212
|
+
|
|
213
|
+
// For later summarizing.
|
|
214
|
+
this.#retainTestResults(test);
|
|
215
|
+
|
|
216
|
+
// Results and test back to the caller.
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// region Dependencies of runOneTest()
|
|
221
|
+
|
|
222
|
+
// Spoofs objects as defined in TestTuple's
|
|
223
|
+
// .with and .in, which can contain spoofs.
|
|
224
|
+
#spoofObjects(test) {
|
|
225
|
+
this.#instanceSpoofer.spoof(test);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#supplyTestFrame(test) {
|
|
229
|
+
// If `.and` includes "static", a call of a static member
|
|
230
|
+
// is made, and the actual is its return value.
|
|
231
|
+
if (typeof test.and === "string") {
|
|
232
|
+
if (this.#doesAddressStatics(test)) {
|
|
233
|
+
// If .from, a static call is made, then the actual is
|
|
234
|
+
// retrieved from a named code element or custom code.
|
|
235
|
+
if (test.from !== undefined) {
|
|
236
|
+
return this.testCodeElementAfterStaticCall;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Otherwise, the return value
|
|
240
|
+
// of the static call is used.
|
|
241
|
+
return this.testReturnValueOfStaticCall;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Constructors are a special case; no second call should
|
|
246
|
+
// be made, and actual must be retrieved from property.
|
|
247
|
+
if (test.of === TestRunner.#constructorName) {
|
|
248
|
+
return this.testCodeElementAfterConstruction;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If .from, a call of the target is made, then the actual
|
|
252
|
+
// is retrieved from a named code element or custom code.
|
|
253
|
+
if (this.#isRetrievalTest(test.from)) {
|
|
254
|
+
return this.testCodeElementAfterCall;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// The most common case: the target is called,
|
|
258
|
+
// and the actual is its return value.
|
|
259
|
+
return this.testReturnValueOfCall;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#isRetrievalTest(testFrom) {
|
|
263
|
+
// Falsy code elements never are a retrieval.
|
|
264
|
+
if (!testFrom) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// If a (non-falsy) string, must be a retrieval.
|
|
269
|
+
if (typeof testFrom === "string") {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// If a function, is a retrieval.
|
|
274
|
+
if (testFrom instanceof Function) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Anything else is not a retrieval.
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
#retainTestResults(test) {
|
|
283
|
+
this.#numberRun++;
|
|
284
|
+
|
|
285
|
+
if (test.didPass) {
|
|
286
|
+
this.#numberPassed++;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
this.#numberFailed++;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this.#allDidPass &= test.didPass;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// endregion Dependencies of runOneTest()
|
|
296
|
+
|
|
297
|
+
summarize() {
|
|
298
|
+
let summary = `Ran ${ this.#numberRun } test${ this.#numberRun !== 1 ? "s" : "" }. `
|
|
299
|
+
+ `${ this.#allDidPass ? "All tests passed. " : "" }`
|
|
300
|
+
+ `${ this.#numberPassed } passed. `
|
|
301
|
+
+ `${ this.#numberFailed } failed.`;
|
|
302
|
+
|
|
303
|
+
let anyWereRun = this.#numberRun > 0;
|
|
304
|
+
|
|
305
|
+
return new TestSummary(summary, this.#allDidPass, anyWereRun);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// endregion Running tests
|
|
309
|
+
|
|
310
|
+
// region Test frames
|
|
311
|
+
|
|
312
|
+
// The most basic test: whether a call of
|
|
313
|
+
// a method returns the expected value.
|
|
314
|
+
testReturnValueOfCall(test) {
|
|
315
|
+
/* Groundwork. */
|
|
316
|
+
this.#anyMethodSpoofing(test);
|
|
317
|
+
|
|
318
|
+
let prototype = test.on.prototype;
|
|
319
|
+
let target = new prototype.constructor(...test.with); // Must use `...`.
|
|
320
|
+
|
|
321
|
+
/* Exercising the code. */
|
|
322
|
+
test.actual = target[test.of](...test.in); // Must use `...`.
|
|
323
|
+
|
|
324
|
+
/* Restoring. */
|
|
325
|
+
this.#anyMethodRestoring(test);
|
|
326
|
+
|
|
327
|
+
/* Comparing. */
|
|
328
|
+
test.didPass = this.#compare(test.out, test.actual);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
testReturnValueOfStaticCall(test) {
|
|
332
|
+
/* Groundwork. */
|
|
333
|
+
this.#anyMethodSpoofing(test);
|
|
334
|
+
|
|
335
|
+
/* No instance is constructed. */
|
|
336
|
+
|
|
337
|
+
/* Exercising the code. */
|
|
338
|
+
test.actual = test.on[test.of](...test.in); // Must use `...`.
|
|
339
|
+
|
|
340
|
+
/* Restoring. */
|
|
341
|
+
this.#anyMethodRestoring(test);
|
|
342
|
+
|
|
343
|
+
/* Comparing. */
|
|
344
|
+
test.didPass = this.#compare(test.out, test.actual);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// The second-most basic test: whether a property
|
|
348
|
+
// has the expected value after a method call.
|
|
349
|
+
testCodeElementAfterCall(test) {
|
|
350
|
+
/* Groundwork. */
|
|
351
|
+
this.#anyMethodSpoofing(test);
|
|
352
|
+
|
|
353
|
+
let prototype = test.on.prototype;
|
|
354
|
+
let target = new prototype.constructor(...test.with); // Must use `...`.
|
|
355
|
+
|
|
356
|
+
/* Exercising the code. */
|
|
357
|
+
target[test.of](...test.in); // Must use `...`.
|
|
358
|
+
|
|
359
|
+
/* Retrieving. */
|
|
360
|
+
// The `actual` is a property or other code element somewhere.
|
|
361
|
+
test.actual = this.#supplyNonReturnActual(test, target);
|
|
362
|
+
|
|
363
|
+
/* Restoring. */
|
|
364
|
+
this.#anyMethodRestoring(test);
|
|
365
|
+
|
|
366
|
+
/* Comparing. */
|
|
367
|
+
test.didPass = this.#compare(test.out, test.actual);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
testCodeElementAfterStaticCall(test) {
|
|
371
|
+
/* Groundwork. */
|
|
372
|
+
this.#anyMethodSpoofing(test);
|
|
373
|
+
|
|
374
|
+
/* No instance is constructed. */
|
|
375
|
+
|
|
376
|
+
/* Exercising the code. */
|
|
377
|
+
test.on[test.of](...test.in); // Must use `...`.
|
|
378
|
+
|
|
379
|
+
/* Retrieving. */
|
|
380
|
+
test.actual = this.#supplyNonReturnActual(test); // No `target` here.
|
|
381
|
+
|
|
382
|
+
/* Restoring. */
|
|
383
|
+
this.#anyMethodRestoring(test);
|
|
384
|
+
|
|
385
|
+
/* Comparing. */
|
|
386
|
+
test.didPass = this.#compare(test.out, test.actual);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// The third-most basic test: whether a property has
|
|
390
|
+
// the expected value after an object is constructed.
|
|
391
|
+
testCodeElementAfterConstruction(test) {
|
|
392
|
+
/* Groundwork. */
|
|
393
|
+
this.#anyMethodSpoofing(test);
|
|
394
|
+
|
|
395
|
+
/* Exercising the code. */
|
|
396
|
+
// For definition consistency, initing with test.in, not test.with.
|
|
397
|
+
let prototype = test.on.prototype;
|
|
398
|
+
let target = new prototype.constructor(...test.in); // Must use `...`.
|
|
399
|
+
|
|
400
|
+
/* Retrieving. */
|
|
401
|
+
// The `actual` is a property or other code element somewhere.
|
|
402
|
+
test.actual = this.#supplyNonReturnActual(test, target);
|
|
403
|
+
|
|
404
|
+
/* Restoring. */
|
|
405
|
+
this.#anyMethodRestoring(test);
|
|
406
|
+
|
|
407
|
+
/* Comparing. */
|
|
408
|
+
test.didPass = this.#compare(test.out, test.actual);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// endregion Test frames
|
|
412
|
+
|
|
413
|
+
// region Dependencies of test frames
|
|
414
|
+
|
|
415
|
+
#anyMethodSpoofing(test) {
|
|
416
|
+
if (test.plus === undefined) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
this.#classSpoofer.spoof(test);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
#compare(expected, actual) {
|
|
424
|
+
return this.#comparer.compare(expected, actual);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
#supplyNonReturnActual(test, target) {
|
|
428
|
+
// When there is a .from that's a string,
|
|
429
|
+
// the actual is the named target member.
|
|
430
|
+
if (typeof test.from === "string") {
|
|
431
|
+
// Common member host;
|
|
432
|
+
// undefined if static.
|
|
433
|
+
let host = target;
|
|
434
|
+
|
|
435
|
+
// When .and defines static.
|
|
436
|
+
if (this.#doesAddressStatics(test)) {
|
|
437
|
+
host = test.on;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return host[test.from];
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// When there is a .from that's a function,
|
|
444
|
+
// the actual is the result of calling it,
|
|
445
|
+
// given everything that might be needed.
|
|
446
|
+
if (test.from instanceof Function) {
|
|
447
|
+
return test.from(target, test);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// When there is any other .from, the actual is the
|
|
451
|
+
// value of the code element provided as .from.
|
|
452
|
+
return test.from;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
#anyMethodRestoring(test) {
|
|
456
|
+
this.#classSpoofer.unspoof();
|
|
457
|
+
this.#classSpoofer.removeDefinitions();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// endregion Dependencies of test frames
|
|
461
|
+
|
|
462
|
+
// region Cross-region dependencies
|
|
463
|
+
|
|
464
|
+
#doesAddressStatics(test) {
|
|
465
|
+
if (typeof test.and === "string") {
|
|
466
|
+
if (test.and.includes("static")) { /* &cruft, abstract */
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// endregion Cross-region dependencies
|
|
475
|
+
|
|
476
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**/
|
|
2
|
+
|
|
3
|
+
export class TestSummary {
|
|
4
|
+
#summary;
|
|
5
|
+
#allDidPass;
|
|
6
|
+
#anyWereRun;
|
|
7
|
+
|
|
8
|
+
get summary() {
|
|
9
|
+
return this.#summary;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
set summary(value) {
|
|
13
|
+
this.#summary = value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get allDidPass() {
|
|
17
|
+
return this.#allDidPass;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
set allDidPass(value) {
|
|
21
|
+
this.#allDidPass = value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get anyWereRun() {
|
|
25
|
+
return this.#anyWereRun;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
set anyWereRun(value) {
|
|
29
|
+
this.#anyWereRun = value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
constructor(summary, allDidPass, anyWereRun) {
|
|
33
|
+
this.summary = summary;
|
|
34
|
+
this.allDidPass = allDidPass;
|
|
35
|
+
this.anyWereRun = anyWereRun;
|
|
36
|
+
}
|
|
37
|
+
}
|