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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  import TypeAnalyzer from "./TypeAnalyzer.js";
4
4
  import NameAnalyzer from "./NameAnalyzer.js";
5
+ import TotalDisplayer from "./TotalDisplayer.js";
5
6
 
6
7
  /* Defines what is found in a spoof definition, typically as
7
8
  a nonce object rather than an instance of this class. */
@@ -130,14 +131,14 @@ export default class SpoofDef {
130
131
 
131
132
  // region Initing, including constructor() and statics
132
133
 
133
- /* Constructor can't use param names like `of`, because when not .-prefixed, they are keywords. */
134
-
134
+ /* Long names used, just in case. */
135
135
  constructor(target, method, output) /* passed */ {
136
136
  this.target = target;
137
137
  this.method = method;
138
138
  this.output = output;
139
139
  }
140
140
 
141
+ /* Arg 'type' should be test.type if present. */
141
142
  static fromNonceTuples(nonces, type) /* passed */ {
142
143
  // Throughput and output.
143
144
  let full = new SpoofDef();
@@ -154,82 +155,102 @@ export default class SpoofDef {
154
155
  return output;
155
156
  }
156
157
 
158
+ /* Arg 'type' should be test.type if present. */
157
159
  static fromNonceTuple(nonce, type) /* passed */ {
158
- /* No spoof-def to output. */
160
+ // No spoof-def to output.
159
161
  if (SpoofDef.isNotASpoof(nonce)) {
160
162
  return [ ];
161
163
  }
162
164
 
163
- // Empty, since properties can be set
164
- // by either of two nonce naming styles.
165
+ let defs = [ ];
166
+
167
+ // Nonce defines one spoof.
168
+ if (SpoofDef.#isMonoSpoof(nonce)) {
169
+ let def = SpoofDef.#fromMonoNonce(nonce, type);
170
+ defs.push(def);
171
+ }
172
+
173
+ // Nonce defines more than one spoof: Recursion.
174
+ if (SpoofDef.#isPolySpoof(nonce)) {
175
+ let locals = SpoofDef.#fromPolyNonce(nonce, type);
176
+ defs.push(...locals);
177
+ }
178
+
179
+ return defs;
180
+ }
181
+
182
+ // region Dependencies of nonce-tuple initing methods
183
+
184
+ static #isMonoSpoof(nonce) /* verified */ {
185
+ return SpoofDef.#hasEitherName(nonce, SpoofDef.#ofNames);
186
+ }
187
+
188
+ static #isPolySpoof(nonce) /* verified */ {
189
+ return !SpoofDef.#isMonoSpoof(nonce);
190
+ }
191
+
192
+ /* Arg 'type' should be test.type if present. */
193
+ static #fromMonoNonce(nonce, type) /* verified */ {
194
+ // Empty, since each prop can
195
+ // have either of two names.
165
196
  let def = new SpoofDef();
166
197
 
167
198
  let shortNames = SpoofDef.#longsByShort.keys();
168
199
 
169
- /* &cruft, maybe refactor to put property-setting loop in its own method, and possibly
170
- recurse internally using it without inner arrays, for a cleaner algorithm */
171
-
172
- // Traversing matching pairs of names and applying
173
- // whichever one is present as the tuple property;
200
+ // Traversing name pairs to use whichever one is present;
174
201
  // if neither is present, the property is undefined.
175
202
  for (let shortName of shortNames) {
176
203
  let longName = SpoofDef.#longsByShort.get(shortName);
177
- def[shortName] = shortName in nonce ? nonce[shortName] : nonce[longName];
204
+
205
+ def[shortName]
206
+ = nonce[shortName] !== undefined
207
+ ? nonce[shortName]
208
+ : nonce[longName];
209
+ }
210
+
211
+ // Adding test target's type, if no type provided.
212
+ if (def.target === undefined) {
213
+ def.target = type;
178
214
  }
179
215
 
180
- // No nested partial spoof-defs
181
- // (mono-spoof) is most likely.
182
- let defs = [ def ];
216
+ return def;
217
+ }
183
218
 
184
- // Recursion to handle nested partial spoof-defs (poly-spoofs).
185
- if (def.method === undefined && Array.isArray(def.output)) {
186
- let locals = [ ];
219
+ /* Arg 'type' should be test.type if present. */
220
+ static #fromPolyNonce(nonce, type) /* verified */ {
221
+ let defs = [ ];
187
222
 
188
- for (let ofAsPair of def.output) {
189
- let localOf = ofAsPair.of !== undefined ? ofAsPair.of : ofAsPair.method;
190
- let localAs = ofAsPair.as !== undefined ? ofAsPair.as : ofAsPair.output;
223
+ // Poly-nonce's .on applies to all its partial spoofs.
224
+ let on = nonce.on !== undefined ? nonce.on : nonce.target;
191
225
 
192
- let passer = SpoofDef.fromNonceTuple({ on: def.target, of: localOf, as: localAs });
193
- locals.push(passer[0]);
194
- }
226
+ // Poly-nonce's .as is an array of partial-spoof nonces.
227
+ let pairs = nonce.as !== undefined ? nonce.as : nonce.output;
195
228
 
196
- // Replace default spoof
197
- // with real poly-spoofs.
198
- defs = locals;
199
- }
229
+ // Traversing partial-spoof nonces (.of and .as only).
230
+ for (let pair of pairs) {
231
+ // Either name (or neither) may exist on nonce partial.
232
+ let of = pair.of !== undefined ? pair.of : pair.method;
233
+ let as = pair.as !== undefined ? pair.as : pair.output;
200
234
 
201
- // All defs need a type / target class.
202
- SpoofDef.#setAnyMissingTypes(defs, type);
235
+ // Only one level of recursion.
236
+ let passer = SpoofDef.#fromMonoNonce({ on, of, as }, type);
237
+
238
+ defs.push(passer);
239
+ }
203
240
 
204
- // Back to caller.
205
241
  return defs;
206
242
  }
207
243
 
208
- // region Dependencies of nonce-tuple initing methods
209
-
210
244
  static #hasEitherName(nonce, names) /* verified */ {
211
245
  let hasEither = false;
212
246
 
213
247
  for (let name of names) {
214
- hasEither ||= SpoofDef.#hasName(nonce, name);
248
+ hasEither ||= nonce[name] !== undefined;
215
249
  }
216
250
 
217
251
  return hasEither;
218
252
  }
219
253
 
220
- static #hasName(nonce, name) /* verified */ {
221
- return nonce[name] !== undefined;
222
- }
223
-
224
- static #setAnyMissingTypes(defs, type) /* verified */ {
225
- // Adding test target's type, if no type provided.
226
- for (let def of defs) {
227
- if (def.target === undefined) {
228
- def.target = type;
229
- }
230
- }
231
- }
232
-
233
254
  // endregion Dependencies of nonce-tuple initing methods
234
255
 
235
256
  // endregion Initing, including constructor() and statics
@@ -237,19 +258,13 @@ export default class SpoofDef {
237
258
  // region Overrides and dependencies
238
259
 
239
260
  toString() /* passed */ {
240
- let text = `SpoofDefinition:{ target:${ this.#asRawOrString(this.target) }, `
241
- + `method:${ this.#asRawOrString(this.method) }, `
242
- + `output:${ this.#asRawOrString(this.output) } }`;
261
+ let displayer = new TotalDisplayer();
262
+
263
+ let text = `SpoofDef{ on:${ displayer.display(this.target) }, `
264
+ + `of:${ displayer.display(this.method) }, `
265
+ + `as:${ displayer.display(this.output) } }`;
243
266
  return text;
244
267
  }
245
-
246
- #asRawOrString(value) /* verified */ {
247
- if (typeof value === "string") {
248
- return `"${ value }"`;
249
- }
250
-
251
- return value;
252
- }
253
-
268
+
254
269
  // endregion Overrides and dependencies
255
270
  }
@@ -1,8 +1,8 @@
1
1
  /**/
2
2
 
3
- /* TerminalReporter is an ATestReporter that sends each result to the terminal / console for display. */
3
+ /* TerminalReporter displays each test result in the terminal / console window. */
4
4
 
5
- import ATestReporter from "./ATestReporter.js";
5
+ import Moment from "./Moment.js";
6
6
  import TestGroup from "./TestGroup.js";
7
7
  import ClassTestGroup from "./ClassTestGroup.js";
8
8
  import MethodTestGroup from "./MethodTestGroup.js";
@@ -11,8 +11,12 @@ import TestSummary from "./TestSummary.js";
11
11
 
12
12
  import chalk from "chalk";
13
13
 
14
- export default class TerminalReporter extends ATestReporter {
15
- // region Private fields
14
+ export default class TerminalReporter {
15
+ // region Fields
16
+
17
+ /* Display styles for the start title and other general needs. */
18
+ #title = chalk.hex("FFFFFF").bgHex("191970").bold;
19
+ #fails = chalk.hex("000000").bgHex("FFD700").bold;
16
20
 
17
21
  /* Foreground-colors using dependency calls and hex. */
18
22
  #trueWhite = chalk.hex("FFFFFF");
@@ -31,22 +35,53 @@ export default class TerminalReporter extends ATestReporter {
31
35
  #methodGroup = this.#trueWhite.bgHex(this.#aqua).bold;
32
36
  #passed = this.#trueBlack.bgHex(this.#paleGreen);
33
37
  #failed = this.#trueBlack.bgHex(this.#paleRed);
38
+ #problems = chalk.hex("000000").bgHex("FFD700").bold;
39
+
34
40
  #passedSummary = this.#trueBlack.bgHex(this.#paleGreen).bold;
35
41
  #failedSummary = this.#trueBlack.bgHex(this.#paleRed).bold;
36
42
  #summary = chalk.hex("000080").bgHex("FAFAD2").bold;
37
43
 
44
+
38
45
  // Count of characters used for indent
39
46
  // and text to the left of each test.
40
47
  #preUsedWidth = 20;
41
48
 
42
- // endregion PrivateFields
49
+ // endregion Fields
50
+
51
+ reportTitle() {
52
+ // This moment, ready for use in title.
53
+ let now = new Date();
54
+ now = new Moment(now);
55
+
56
+ // Top of screen and leading color stripe.
57
+ console.clear();
58
+ console.log();
59
+ console.log(this.#title(this.#wide()));
43
60
 
44
- // region ATestReporter
61
+ // Actually reporting title.
62
+ console.log(this.#title(this.#wide(` Risei tests run on ${ now.asReadable() } local time.`)));
63
+
64
+ // Trailing color stripe and blank line.
65
+ console.log(this.#title(this.#wide()));
66
+ console.log();
67
+ }
68
+
69
+ // region Dependencies of reportTitle()
70
+
71
+ /* Styling for color stripes. */
72
+ #wide(text) {
73
+ let width = process.stdout.columns;
74
+ text = text || "";
75
+
76
+ return text.padEnd(width, "\u00A0");
77
+ };
78
+
79
+ // endregion Dependencies of reportTitle()
45
80
 
46
81
  reportNext(result) {
47
82
  // The next result introduces a group of tests.
48
83
  if (result instanceof TestGroup) {
49
- this.reportGroup(result);
84
+ this.#reportGroup(result);
50
85
  return;
51
86
  }
52
87
 
@@ -58,12 +93,14 @@ export default class TerminalReporter extends ATestReporter {
58
93
 
59
94
  // The next result is the summary of all tests.
60
95
  if (result instanceof TestSummary) {
61
- this.reportSummary(result);
96
+ this.#reportSummary(result);
62
97
  return;
63
98
  }
64
99
  }
65
100
 
66
- reportGroup(result) {
101
+ // region Direct dependencies of reportNext(), except summary
102
+
103
+ #reportGroup(result) {
67
104
  if (result instanceof ClassTestGroup) {
68
105
  console.log(this.#classGroup(` ${ result.group }: `));
69
106
  }
@@ -73,32 +110,44 @@ export default class TerminalReporter extends ATestReporter {
73
110
  }
74
111
  }
75
112
 
76
- // region Private dependencies of reportNext()
77
-
78
113
  #reportTestResult(result) {
114
+ if (result.doesHaveThrow) {
115
+ this.#reportThrew(result.anyThrow.stack);
116
+ }
117
+
118
+ if (result.doesHaveTrouble) {
119
+ this.#reportThrew(result.anyTrouble.stack);
120
+ }
121
+
79
122
  if (result.didPass) {
80
- this.reportPassed(result);
123
+ this.#reportPassed(result);
81
124
  }
82
125
 
83
126
  if (!result.didPass) {
84
- this.reportFailed(result);
127
+ this.#reportFailed(result);
85
128
  }
86
129
  }
87
130
 
88
- // endregion Private dependencies of reportNext()
131
+ // endregion Direct dependencies of reportNext(), except summary
132
+
133
+ // region Direct dependencies of #reportTestResult()
134
+
135
+ #reportThrew(threw) {
136
+ console.log(this.#problems(threw));
137
+ }
89
138
 
90
- reportPassed(result) {
139
+ #reportPassed(result) {
91
140
  this.#reportOneTestResult(result, "Passed", this.#passed);
92
141
  }
93
142
 
94
- reportFailed(result) {
143
+ #reportFailed(result) {
95
144
  this.#reportOneTestResult(result, "Failed", this.#failed);
96
145
  }
97
146
 
98
- // region Private dependencies of reportPassed() and reportFailed()
147
+ // endregion Direct dependencies of #reportTestResult()
148
+
149
+ // region Dependencies of #reportPassed() and #reportFailed()
99
150
 
100
- /* &cruft, changes here / in dependencies
101
- for better splitting to multiple lines */
102
151
  #reportOneTestResult(result, net, styler) {
103
152
  let { lineOne, lineTwo, lineThree, lineFour } = this.#calculateResultDisplay(result);
104
153
 
@@ -166,7 +215,7 @@ export default class TerminalReporter extends ATestReporter {
166
215
  return this.#oneLineTactic(identityText, inputsText, expectedText, actualText);
167
216
  }
168
217
 
169
- // region Private dependencies of #calculateResultDisplay()
218
+ // region Dependencies of #calculateResultDisplay()
170
219
 
171
220
  #fourLineTactic(identityText, inputsText, expectedText, actualText) {
172
221
  // Each line on its own.
@@ -218,13 +267,17 @@ export default class TerminalReporter extends ATestReporter {
218
267
  return { lineOne, lineTwo, lineThree, lineFour };
219
268
  }
220
269
 
221
- // endregion Private dependencies of #calculateResultDisplay()
270
+ // endregion Dependencies of #calculateResultDisplay()
271
+
272
+ // endregion Dependencies of #reportPassed() and #reportFailed()
222
273
 
223
- // endregion Private dependencies of reportPassed() and reportFailed()
274
+ // region Summary dependency of reportNext()
224
275
 
225
- reportSummary(summary) {
276
+ #reportSummary(summary) {
226
277
  console.log();
227
278
 
279
+ /* Actual summary. */
280
+
228
281
  if (summary.anyWereRun) {
229
282
  if (summary.allDidPass) {
230
283
  console.log(this.#passedSummary(this.#toFullWidth(" Test run succeeded.")));
@@ -235,7 +288,31 @@ export default class TerminalReporter extends ATestReporter {
235
288
  }
236
289
 
237
290
  console.log(this.#summary(this.#toFullWidth(` ${ summary.summary }`)));
238
- }
291
+
292
+ /* Post-summary reporting of any throws. */
293
+
294
+ // Unsought throws in tested code.
295
+ if (summary.anyThrows.length !== 0) {
296
+ console.log();
297
+ console.log(this.#problems(summary.anyThrows.join("\n")));
298
+ }
299
+
300
+ // Throws in code in scope of test frame.
301
+ if (summary.anyTroubles.length !== 0) {
302
+ console.log();
303
+ console.log(this.#problems(summary.anyTroubles.join("\n")));
304
+ }
305
+
306
+ // Throws in code in scope of test runner.
307
+ if (summary.anyCaught.length !== 0) {
308
+ console.log();
309
+ console.log(this.#problems(summary.anyCaught.join("\n")));
310
+ }
311
+ }
312
+
313
+ // endregion Summary dependency of reportNext()
314
+
315
+ // region Dependencies of #reportSummary()
239
316
 
240
317
  #toFullWidth(text) {
241
318
  text = text || "";
@@ -244,6 +321,13 @@ export default class TerminalReporter extends ATestReporter {
244
321
  return text.padEnd(width, "\u00A0");
245
322
  }
246
323
 
247
- // endregion ATestReporter
324
+ // endregion Dependencies of #reportSummary()
248
325
 
326
+ blankLine() {
327
+ console.log();
328
+ }
329
+
330
+ reportLoadFail(fail) {
331
+ console.log(this.#fails(this.#wide(` ${ fail }`)));
332
+ }
249
333
  }
package/system/TestDef.js CHANGED
@@ -4,6 +4,8 @@ import TypeAnalyzer from "./TypeAnalyzer.js";
4
4
  import NameAnalyzer from "./NameAnalyzer.js";
5
5
  import TypeIdentifier from "./TypeIdentifier.js";
6
6
  import Types from "./Types.js";
7
+ import CallTypes from "./CallTypes.js";
8
+ import TotalDisplayer from "./TotalDisplayer.js";
7
9
 
8
10
  /* Defines what is found in a test definition, typically as
9
11
  a nonce object rather than an instance of this class. */
@@ -57,7 +59,8 @@ export default class TestDef {
57
59
  target;
58
60
 
59
61
  actual;
60
- thrown;
62
+ thrown; // Throw in inner scope (tested code).
63
+ trouble; // Throw in outer scope (test system or .do / .undo / .from).
61
64
 
62
65
  // endregion Fields
63
66
 
@@ -187,6 +190,10 @@ export default class TestDef {
187
190
  return is;
188
191
  }
189
192
 
193
+ get isMonoCallTest() /* passed */ {
194
+ return !this.isPolyCallTest;
195
+ }
196
+
190
197
  get isPolyCallTest() /* passed */ {
191
198
  let is = this.andStringContains(TestDef.polyName);
192
199
  return is;
@@ -198,7 +205,7 @@ export default class TestDef {
198
205
  }
199
206
 
200
207
  get isMethodTest() /* passed */ {
201
- return !this.isPropertyTest;
208
+ return !this.isPropertyTest && !this.isConstructorTest;
202
209
  }
203
210
 
204
211
  get isPropertyTest() /* passed */ {
@@ -211,6 +218,22 @@ export default class TestDef {
211
218
  return isPropertyTest;
212
219
  }
213
220
 
221
+ get callType() /* passed */ {
222
+ let type;
223
+
224
+ if (this.isMethodTest) {
225
+ type = this.isMonoCallTest ? CallTypes.methodMono : CallTypes.methodPoly;
226
+ }
227
+ else if (this.isPropertyTest) {
228
+ type = this.isMonoCallTest ? CallTypes.propMono : CallTypes.propPoly;
229
+ }
230
+ else if (this.isConstructorTest) {
231
+ type = this.isMonoCallTest ? CallTypes.conMono : CallTypes.conPoly;
232
+ }
233
+
234
+ return type;
235
+ }
236
+
214
237
  get isRetrievalTest() /* passed */ {
215
238
  // Falsy code elements never are a retrieval.
216
239
  if (!this.from) {
@@ -256,7 +279,7 @@ export default class TestDef {
256
279
  get runName() /* passed */ {
257
280
  let plain = NameAnalyzer.plainNameOf(this.of);
258
281
 
259
- if (this.isMethodTest) {
282
+ if (!this.isPropertyTest) {
260
283
  return `${ plain }()`;
261
284
  }
262
285
  else {
@@ -264,6 +287,23 @@ export default class TestDef {
264
287
  }
265
288
  }
266
289
 
290
+ get anyUnsoughtThrow() /* passed */ {
291
+ // Prop is true when throw is sought.
292
+ if (this.isThrowTest) {
293
+ return;
294
+ }
295
+
296
+ return this.thrown;
297
+ }
298
+
299
+ get doesHaveUnsoughtThrow() /* passed */ {
300
+ return this.anyUnsoughtThrow !== undefined;
301
+ }
302
+
303
+ get doesHaveTrouble() /* passed */ {
304
+ return this.trouble !== undefined;
305
+ }
306
+
267
307
  // endregion State properties
268
308
 
269
309
  // region State methods
@@ -282,8 +322,7 @@ export default class TestDef {
282
322
 
283
323
  // region Initing, including constructor()
284
324
 
285
- /* Constructor can't use parameter names like `for`:
286
- when not .-prefixed, they are purely keywords. */
325
+ /* Long names used, since some short names, like `for`, are parsed as keywords. */
287
326
  constructor(nature, type, spoofed, initors, method, inputs, output, source, factors, enact, counteract) /* ok */ {
288
327
  this.nature = nature; // .for
289
328
  this.type = type; // .on
@@ -305,6 +344,11 @@ export default class TestDef {
305
344
 
306
345
  // Looping over all.
307
346
  for (let nonce of nonces) {
347
+ // No possible test definition to output.
348
+ if (!TestDef.isANonce(nonce)) {
349
+ continue;
350
+ }
351
+
308
352
  // Get latest test definition.
309
353
  let latest = TestDef.fromNonceTuple(nonce);
310
354
 
@@ -359,6 +403,11 @@ export default class TestDef {
359
403
 
360
404
  // region Dependencies of nonce-tuple initing methods
361
405
 
406
+ static isANonce(topic) /* passed */ {
407
+ let type = TypeIdentifier.identify(topic);
408
+ return type === Types.isObject;
409
+ }
410
+
362
411
  /* Replaces some or all of the 'full' that gathers test
363
412
  props for reuse when targeting changes significantly
364
413
  in the middle of the collapsing-forward process. */
@@ -417,4 +466,22 @@ export default class TestDef {
417
466
 
418
467
  // endregion Initing, including constructor()
419
468
 
469
+ toString() /* verified */ {
470
+ let displayer = new TotalDisplayer();
471
+
472
+ let text = `TestDef{ for:${ displayer.display(this.for) }, `
473
+ + `on:${ displayer.display(this.on) }, `
474
+ + `with:${ displayer.display(this.with) }, `
475
+ + `of:${ displayer.display(this.of) }, `
476
+ + `plus:${ displayer.display(this.plus) }, `
477
+ + `in:${ displayer.display(this.in) }, `
478
+ + `out:${ displayer.display(this.out) }, `
479
+ + `from:${ displayer.display(this.from) }, `
480
+ + `and:${ displayer.display(this.and) }, `
481
+ + `do:${ displayer.display(this.do) }, `
482
+ + `undo:${ displayer.display(this.undo) } }`
483
+
484
+ return text;
485
+ }
486
+
420
487
  }
@@ -1,14 +1,14 @@
1
1
  /**/
2
2
 
3
- /* TestFinder is an ATestFinder that finds tests in the places identified in package.json. */
3
+ /* TestFinder retrieves test sources based on .risei.tests in package.json. */
4
4
 
5
- import ATestFinder from "./ATestFinder.js";
6
5
  import ATestSource from "./ATestSource.js";
6
+ import Choices from "./Choices.js";
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
9
  import { minimatch } from "minimatch";
10
10
 
11
- export default class TestFinder extends ATestFinder {
11
+ export default class TestFinder {
12
12
  // region Definitions
13
13
 
14
14
  static NO_SUCH_FILE = "ENOENT";
@@ -21,20 +21,37 @@ export default class TestFinder extends ATestFinder {
21
21
  // endregion Definitions
22
22
 
23
23
  // region Fields
24
-
24
+
25
+ #testSources;
26
+ #thrown = [];
27
+
25
28
  #sought;
26
29
 
27
30
  // endregion Fields
28
31
 
32
+ // region Properties
33
+
34
+ get testSources() {
35
+ return this.#testSources;
36
+ }
37
+
38
+ set testSources(value) {
39
+ this.#testSources = value;
40
+ }
41
+
42
+ get thrown() {
43
+ return this.#thrown;
44
+ }
45
+
46
+ // endregion Properties
47
+
29
48
  constructor() {
30
- super();
49
+ this.#testSources = [];
31
50
  }
32
51
 
33
52
  async findAllTests() {
34
- // Paths for test sources are listed in package.json, possibly with globbing.
35
- let source = this.sourceBySearch();
36
- this.#sought = source.tests;
37
- let testPaths = this.pathsFromTargetingSource();
53
+ this.#sought = this.supplyTestTargeting();
54
+ let testPaths = this.pathsFromTargeting();
38
55
 
39
56
  testPaths = this.sortTestPaths(testPaths);
40
57
 
@@ -54,28 +71,13 @@ export default class TestFinder extends ATestFinder {
54
71
  return tests;
55
72
  }
56
73
 
57
- sourceBySearch() {
58
- // App's package.json always found at process.cwd().
59
- let packageSite = process.cwd();
60
- let pathAndName = path.join(packageSite, TestFinder.METADATA_FILE);
61
-
62
- // Contents of package.json are just a single JSON object.
63
- let packageJson = fs.readFileSync(pathAndName, { encoding: "utf8" });
64
- packageJson = JSON.parse(packageJson);
65
-
66
- // Can't run or reasonably recover if this is not defined.
67
- if (!packageJson.risei) {
68
- throw new Error(
69
- `For Risei tests to run, this app's package.json must contain `
70
- + `a node like this: "risei": { tests: "path-or-glob-here" }. `
71
- + `One glob or path may be used, or an array of them.`);
72
- }
73
-
74
- // Only Risei's own metadata is needed.
75
- return packageJson.risei;
74
+ // Spoofing point for self-testing.
75
+ supplyTestTargeting() {
76
+ // Value retrieved from package.json.
77
+ return Choices.testTargeting;
76
78
  }
77
79
 
78
- pathsFromTargetingSource() {
80
+ pathsFromTargeting() {
79
81
  let paths = [];
80
82
  let root = process.cwd();
81
83