testeranto 0.9.21 → 0.15.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/src/index.mts ADDED
@@ -0,0 +1,1129 @@
1
+ import pkg from 'graphology';
2
+ /* @ts-ignore:next-line */
3
+ const { DirectedGraph, UndirectedGraph } = pkg;
4
+
5
+ export class BaseFeature {
6
+ name: string;
7
+ constructor(name: string) {
8
+ this.name = name;
9
+ }
10
+ }
11
+
12
+ abstract class TesterantoGraph {
13
+ name: string;
14
+ abstract graph;
15
+
16
+ constructor(name: string) {
17
+ this.name = name;
18
+ }
19
+ }
20
+
21
+ export class TesterantoGraphUndirected implements TesterantoGraph {
22
+ name: string;
23
+ graph: typeof UndirectedGraph
24
+ constructor(name: string) {
25
+ this.name = name;
26
+ this.graph = new UndirectedGraph();
27
+ }
28
+ connect(a, b, relation?: string) {
29
+ this.graph.mergeEdge(a, b, { type: relation });
30
+ }
31
+ }
32
+
33
+ export class TesterantoGraphDirected implements TesterantoGraph {
34
+ name: string;
35
+ graph: typeof DirectedGraph;
36
+ constructor(name: string) {
37
+ this.name = name;
38
+ this.graph = new DirectedGraph();
39
+ }
40
+ connect(to, from, relation?: string) {
41
+ this.graph.mergeEdge(to, from, { type: relation });
42
+ }
43
+ }
44
+
45
+ export class TesterantoGraphDirectedAcyclic implements TesterantoGraph {
46
+ name: string;
47
+ graph: typeof DirectedGraph;
48
+ constructor(name: string) {
49
+ this.name = name;
50
+ this.graph = new DirectedGraph();
51
+ }
52
+ connect(to, from, relation?: string) {
53
+ this.graph.mergeEdge(to, from, { type: relation });
54
+ }
55
+ }
56
+
57
+ export class TesterantoFeatures {
58
+ features: Record<string, BaseFeature>;
59
+ graphs: {
60
+ undirected: TesterantoGraphUndirected[],
61
+ directed: TesterantoGraphDirected[],
62
+ dags: TesterantoGraphDirectedAcyclic[]
63
+ }
64
+
65
+ constructor(
66
+ features: Record<string, BaseFeature>,
67
+ graphs: {
68
+ undirected: TesterantoGraphUndirected[],
69
+ directed: TesterantoGraphDirected[],
70
+ dags: TesterantoGraphDirectedAcyclic[]
71
+ }
72
+ ) {
73
+ this.features = features;
74
+ this.graphs = graphs;
75
+ }
76
+
77
+ networks() {
78
+ return [
79
+ ...this.graphs.undirected.values(),
80
+ ...this.graphs.directed.values(),
81
+ ...this.graphs.dags.values()
82
+ ]
83
+ }
84
+
85
+ toObj() {
86
+ return {
87
+ features: Object.entries(this.features).map(([name, feature]) => {
88
+ return {
89
+ ...feature,
90
+ inNetworks: this.networks().filter((network) => {
91
+ return network.graph.hasNode(feature.name);
92
+ }).map((network) => {
93
+ return {
94
+
95
+ network: network.name,
96
+ neighbors: network.graph.neighbors(feature.name)
97
+ }
98
+ })
99
+ }
100
+ }),
101
+ networks: this.networks().map((network) => {
102
+ return {
103
+ ...network
104
+ }
105
+ })
106
+ };
107
+ }
108
+ }
109
+
110
+ const testOutPath = "./dist/results/";
111
+
112
+ export type ITTestResourceRequirement = {
113
+ "ports": number
114
+ };
115
+
116
+ type ITTestResource = {
117
+ "ports": number[]
118
+ };
119
+
120
+ export type IT_FeatureNetwork = {
121
+ name: string,
122
+ // graph: DirectedGraph
123
+ };
124
+
125
+ export type IT = {
126
+ toObj(): object;
127
+ aborter: () => any;
128
+ name: string;
129
+ givens: BaseGiven<unknown, unknown, unknown, unknown, unknown>[];
130
+ checks: BaseCheck<unknown, unknown, unknown, unknown, ITTestShape, unknown>[];
131
+ };
132
+
133
+ export type ITestJob = {
134
+ toObj(): object;
135
+ test: IT;
136
+ runner: (testResource) => Promise<boolean>;
137
+ testResource: ITTestResourceRequirement;
138
+ };
139
+
140
+ export type ITestResults = Promise<{
141
+ test: IT;
142
+ }>[];
143
+
144
+ export type Modify<T, R> = Omit<T, keyof R> & R;
145
+
146
+ export type ITTestShape = {
147
+ suites;
148
+ givens;
149
+ whens;
150
+ thens;
151
+ checks;
152
+ };
153
+
154
+ export type ITestSpecification<ITestShape extends ITTestShape, IFeatureShape> = (
155
+ Suite: {
156
+ [K in keyof ITestShape["suites"]]: (
157
+ name: string,
158
+ givens: BaseGiven<unknown, unknown, unknown, unknown, unknown>[],
159
+ checks: BaseCheck<unknown, unknown, unknown, unknown, ITestShape, IFeatureShape>[]
160
+ ) => BaseSuite<unknown, unknown, unknown, unknown, unknown, ITestShape, IFeatureShape>;
161
+ },
162
+ Given: {
163
+ [K in keyof ITestShape["givens"]]: (
164
+ features: (keyof IFeatureShape)[],
165
+ whens: BaseWhen<unknown, unknown, unknown>[],
166
+ thens: BaseThen<unknown, unknown, unknown>[],
167
+ ...xtras: ITestShape["givens"][K]
168
+ ) => BaseGiven<unknown, unknown, unknown, unknown, unknown>;
169
+ },
170
+ When: {
171
+ [K in keyof ITestShape["whens"]]: (
172
+ ...xtras: ITestShape["whens"][K]
173
+ ) => BaseWhen<unknown, unknown, unknown>;
174
+ },
175
+ Then: {
176
+ [K in keyof ITestShape["thens"]]: (
177
+ ...xtras: ITestShape["thens"][K]
178
+ ) => BaseThen<unknown, unknown, unknown>;
179
+ },
180
+ Check: {
181
+ [K in keyof ITestShape["checks"]]: (
182
+
183
+ name: string,
184
+ features: (keyof IFeatureShape)[],
185
+ callbackA: (
186
+ whens: {
187
+ [K in keyof ITestShape["whens"]]: (...unknown) => BaseWhen<unknown, unknown, unknown>
188
+ },
189
+ thens: {
190
+ [K in keyof ITestShape["thens"]]: (...unknown) => BaseThen<unknown, unknown, unknown>
191
+ },
192
+
193
+ ) => unknown,
194
+ // whens: BaseWhen<unknown, unknown, unknown>[],
195
+ // thens: BaseThen<unknown, unknown, unknown>[],
196
+
197
+ ...xtras: ITestShape["checks"][K]
198
+ ) => BaseCheck<unknown, unknown, unknown, unknown, ITestShape, IFeatureShape>;
199
+ }
200
+ ) => any[];
201
+
202
+ export type ITestImplementation<
203
+ IState,
204
+ ISelection,
205
+ IWhenShape,
206
+ IThenShape,
207
+ ITestShape extends ITTestShape
208
+ > = {
209
+ Suites: {
210
+ [K in keyof ITestShape["suites"]]: string;
211
+ };
212
+ Givens: {
213
+ [K in keyof ITestShape["givens"]]: (
214
+ ...Ig: ITestShape["givens"][K]
215
+ ) => IState;
216
+ };
217
+ Whens: {
218
+ [K in keyof ITestShape["whens"]]: (
219
+ ...Iw: ITestShape["whens"][K]
220
+ ) => (zel: ISelection) => IWhenShape;
221
+ };
222
+ Thens: {
223
+ [K in keyof ITestShape["thens"]]: (
224
+ ...It: ITestShape["thens"][K]
225
+ ) => (ssel: ISelection) => IThenShape;
226
+ };
227
+ Checks: {
228
+ [K in keyof ITestShape["checks"]]: (
229
+ ...Ic: ITestShape["checks"][K]
230
+ ) => IState;
231
+ };
232
+ };
233
+
234
+ class TestArtifact {
235
+ binary: Buffer | string;
236
+
237
+ constructor(binary) {
238
+ this.binary = binary
239
+ }
240
+ }
241
+
242
+
243
+ export abstract class BaseSuite<
244
+ IInput,
245
+ ISubject,
246
+ IStore,
247
+ ISelection,
248
+ IThenShape,
249
+ ITestShape extends ITTestShape,
250
+ IFeatureShape,
251
+ > {
252
+ name: string;
253
+ givens: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[];
254
+ checks: BaseCheck<ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>[];
255
+ store: IStore;
256
+ aborted: boolean;
257
+ fails: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[];
258
+ testResourceConfiguration: ITTestResource;
259
+
260
+ constructor(
261
+ name: string,
262
+ givens: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[] = [],
263
+ checks: BaseCheck<ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>[] = []
264
+ ) {
265
+ this.name = name;
266
+ this.givens = givens;
267
+ this.checks = checks;
268
+ this.fails = [];
269
+ }
270
+
271
+ async aborter() {
272
+ this.aborted = true;
273
+ await Promise.all((this.givens || []).map((g, ndx) => g.aborter(ndx)))
274
+ }
275
+
276
+ public toObj() {
277
+ return {
278
+ name: this.name,
279
+ givens: this.givens.map((g) => g.toObj()),
280
+ fails: this.fails
281
+ }
282
+ }
283
+
284
+ setup(s: IInput): Promise<ISubject> {
285
+ return new Promise((res) => res(s as unknown as ISubject));
286
+ }
287
+
288
+ test(t: IThenShape): unknown {
289
+ return t;
290
+ }
291
+
292
+ async run(input, testResourceConfiguration: ITTestResource): Promise<boolean> {
293
+ this.testResourceConfiguration = Object.keys(testResourceConfiguration).length ? testResourceConfiguration : { ports: [] };
294
+ const subject = await this.setup(input);
295
+ console.log("\nSuite:", this.name, testResourceConfiguration);
296
+ for (const [ndx, giver] of this.givens.entries()) {
297
+ try {
298
+ if (!this.aborted) {
299
+ this.store = await giver.give(subject, ndx, testResourceConfiguration, this.test);
300
+ }
301
+ } catch (e) {
302
+ console.error(e);
303
+ this.fails.push(giver)
304
+ return false;
305
+ }
306
+ }
307
+ for (const [ndx, thater] of this.checks.entries()) {
308
+ await thater.check(subject, ndx, testResourceConfiguration, this.test);
309
+ }
310
+ return true;
311
+ }
312
+ }
313
+
314
+ export abstract class BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape> {
315
+ name: string;
316
+ features: (keyof IFeatureShape)[];
317
+ whens: BaseWhen<IStore, ISelection, IThenShape>[];
318
+ thens: BaseThen<ISelection, IStore, IThenShape>[];
319
+ error: Error;
320
+ abort: boolean;
321
+ store: IStore;
322
+ testArtifacts: Record<string, any[]>
323
+
324
+ constructor(
325
+ name: string,
326
+ features: (keyof IFeatureShape)[],
327
+ whens: BaseWhen<IStore, ISelection, IThenShape>[],
328
+ thens: BaseThen<ISelection, IStore, IThenShape>[],
329
+ ) {
330
+ this.name = name;
331
+ this.features = features;
332
+ this.whens = whens;
333
+ this.thens = thens;
334
+ this.testArtifacts = {};
335
+ }
336
+
337
+ toObj() {
338
+ return {
339
+ name: this.name,
340
+ whens: this.whens.map((w) => w.toObj()),
341
+ thens: this.thens.map((t) => t.toObj()),
342
+ errors: this.error,
343
+ features: this.features,
344
+ testArtifacts: this.testArtifacts,
345
+ }
346
+ }
347
+
348
+ abstract givenThat(
349
+ subject: ISubject,
350
+ testResourceConfiguration?
351
+ ): Promise<IStore>;
352
+
353
+ saveTestArtifact(k: string, testArtifact: TestArtifact) {
354
+ if (!this.testArtifacts[k]) {
355
+ this.testArtifacts[k] = []
356
+ }
357
+ this.testArtifacts[k].push(testArtifact)
358
+ }
359
+
360
+ artifactSaver = {
361
+ png: (testArtifact) => this.saveTestArtifact('afterEach', new TestArtifact(testArtifact))
362
+ }
363
+
364
+ async aborter(ndx: number) {
365
+ this.abort = true;
366
+ return Promise.all([
367
+ ...this.whens.map((w, ndx) => new Promise((res) => res(w.aborter()))),
368
+ ...this.thens.map((t, ndx) => new Promise((res) => res(t.aborter()))),
369
+ ])
370
+ .then(async () => {
371
+ return await this.afterEach(this.store, ndx, this.artifactSaver)
372
+ })
373
+ }
374
+
375
+ async afterEach(store: IStore, ndx: number, cb): Promise<unknown> {
376
+ return;
377
+ }
378
+
379
+ async give(
380
+ subject: ISubject,
381
+ index: number,
382
+ testResourceConfiguration,
383
+ tester
384
+ ) {
385
+ console.log(`\n Given: ${this.name}`);
386
+ try {
387
+ if (!this.abort) {
388
+ this.store = await this.givenThat(subject, testResourceConfiguration);
389
+ }
390
+ for (const whenStep of this.whens) {
391
+ await whenStep.test(this.store, testResourceConfiguration);
392
+ }
393
+ for (const thenStep of this.thens) {
394
+ const t = await thenStep.test(this.store, testResourceConfiguration);
395
+ tester(t);
396
+ }
397
+ } catch (e) {
398
+ this.error = e;
399
+ console.log('\u0007');// bell
400
+ throw e;
401
+ } finally {
402
+
403
+ try {
404
+ await this.afterEach(this.store, index, this.artifactSaver);
405
+ } catch {
406
+ console.error("afterEach failed! no error will be recorded!")
407
+ }
408
+
409
+ }
410
+ return this.store;
411
+ }
412
+ }
413
+
414
+ export abstract class BaseWhen<IStore, ISelection, IThenShape> {
415
+ public name: string;
416
+ actioner: (x: ISelection) => IThenShape;
417
+ error: boolean;
418
+ abort: boolean;
419
+
420
+ constructor(name: string, actioner: (xyz: ISelection) => IThenShape) {
421
+ this.name = name;
422
+ this.actioner = actioner;
423
+ }
424
+
425
+ abstract andWhen(
426
+ store: IStore,
427
+ actioner: (x: ISelection) => IThenShape,
428
+ testResource
429
+ );
430
+
431
+ toObj() {
432
+ return {
433
+ name: this.name,
434
+ error: this.error,
435
+ }
436
+ }
437
+
438
+ aborter() {
439
+ this.abort = true;
440
+ return this.abort;
441
+ }
442
+
443
+ async test(store: IStore, testResourceConfiguration?) {
444
+ console.log(" When:", this.name);
445
+ if (!this.abort) {
446
+ try {
447
+ return await this.andWhen(store, this.actioner, testResourceConfiguration);
448
+ } catch (e) {
449
+ this.error = true;
450
+ throw e
451
+ }
452
+ }
453
+ }
454
+ }
455
+
456
+ export abstract class BaseThen<ISelection, IStore, IThenShape> {
457
+ public name: string;
458
+ thenCB: (storeState: ISelection) => IThenShape;
459
+ error: boolean;
460
+ abort: boolean;
461
+
462
+ constructor(name: string, thenCB: (val: ISelection) => IThenShape) {
463
+ this.name = name;
464
+ this.thenCB = thenCB;
465
+ }
466
+
467
+ toObj() {
468
+ return {
469
+ name: this.name,
470
+ error: this.error,
471
+ }
472
+ }
473
+
474
+ abstract butThen(store: any, testResourceConfiguration?): Promise<ISelection>;
475
+
476
+ aborter() {
477
+ this.abort = true;
478
+ return this.abort;
479
+ }
480
+
481
+ async test(store: IStore, testResourceConfiguration): Promise<IThenShape | undefined> {
482
+ if (!this.abort) {
483
+ console.log(" Then:", this.name);
484
+ try {
485
+ return await this.thenCB(await this.butThen(store, testResourceConfiguration));
486
+ } catch (e) {
487
+ this.error = true;
488
+ throw e
489
+ }
490
+ }
491
+ }
492
+ }
493
+
494
+ export abstract class BaseCheck<
495
+ ISubject,
496
+ IStore,
497
+ ISelection,
498
+ IThenShape,
499
+ ITestShape extends ITTestShape,
500
+ IFeatureShape
501
+ > {
502
+ name: string;
503
+ features: (keyof IFeatureShape)[];
504
+ checkCB: (whens, thens) => any;
505
+ whens: {
506
+ [K in keyof ITestShape["whens"]]: (p, tc) =>
507
+ BaseWhen<IStore, ISelection, IThenShape>
508
+ }
509
+ thens: {
510
+ [K in keyof ITestShape["thens"]]: (p, tc) =>
511
+ BaseThen<ISelection, IStore, IThenShape>
512
+ }
513
+
514
+ constructor(
515
+ name: string,
516
+ features: (keyof IFeatureShape)[],
517
+ checkCB: (
518
+ whens,
519
+ thens
520
+ ) => any,
521
+ whens,
522
+ thens
523
+ ) {
524
+
525
+ this.name = name;
526
+ this.features = features;
527
+ this.checkCB = checkCB;
528
+ this.whens = whens;
529
+ this.thens = thens;
530
+ }
531
+
532
+ abstract checkThat(
533
+ subject: ISubject,
534
+ testResourceConfiguration?
535
+ ): Promise<IStore>;
536
+
537
+ async afterEach(store: IStore, ndx: number, cb?): Promise<unknown> {
538
+ return;
539
+ }
540
+
541
+ async check(
542
+ subject: ISubject,
543
+ ndx: number,
544
+ testResourceConfiguration,
545
+ tester
546
+ ) {
547
+ console.log(`\n Check: ${this.name}`);
548
+ const store = await this.checkThat(subject, testResourceConfiguration);
549
+ await this.checkCB(
550
+
551
+ (
552
+ Object.entries(this.whens)
553
+ .reduce((a, [key, when]) => {
554
+ a[key] = async (payload) => {
555
+ return await when(payload, testResourceConfiguration).test(
556
+ store,
557
+ testResourceConfiguration
558
+ );
559
+ };
560
+ return a;
561
+ }, {}
562
+ )
563
+ ),
564
+
565
+ (
566
+ Object.entries(this.thens)
567
+ .reduce((a, [key, then]) => {
568
+ a[key] = async (payload) => {
569
+ const t = await then(payload, testResourceConfiguration).test(
570
+ store,
571
+ testResourceConfiguration
572
+ );
573
+ tester(t);
574
+ };
575
+ return a;
576
+ }, {}
577
+ )
578
+ )
579
+ );
580
+
581
+ await this.afterEach(store, ndx);
582
+ return;
583
+ }
584
+ }
585
+
586
+ export abstract class TesterantoLevelZero<
587
+ IInput,
588
+ ISubject,
589
+ IStore,
590
+ ISelection,
591
+ SuiteExtensions,
592
+ GivenExtensions,
593
+ WhenExtensions,
594
+ ThenExtensions,
595
+ CheckExtensions,
596
+ IThenShape,
597
+ IFeatureShape,
598
+ > {
599
+ constructorator: IStore;
600
+
601
+ suitesOverrides: Record<
602
+ keyof SuiteExtensions,
603
+ (
604
+ name: string,
605
+ givens: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[],
606
+ checks: BaseCheck<ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>[]
607
+ ) => BaseSuite<IInput, ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>
608
+ >;
609
+
610
+ givenOverides: Record<
611
+ keyof GivenExtensions,
612
+ (
613
+ name: string,
614
+ features: (keyof IFeatureShape)[],
615
+ whens: BaseWhen<IStore, ISelection, IThenShape>[],
616
+ thens: BaseThen<ISelection, IStore, IThenShape>[],
617
+ ...xtraArgs
618
+ ) => BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>
619
+ >;
620
+
621
+ whenOverides: Record<
622
+ keyof WhenExtensions,
623
+ (any) => BaseWhen<IStore, ISelection, IThenShape>
624
+ >;
625
+
626
+ thenOverides: Record<
627
+ keyof ThenExtensions,
628
+ (
629
+ selection: ISelection,
630
+ expectation: any
631
+ ) => BaseThen<ISelection, IStore, IThenShape>
632
+ >;
633
+
634
+ checkOverides: Record<
635
+ keyof CheckExtensions,
636
+ (
637
+ feature: string,
638
+ callback: (whens, thens) => any,
639
+ ...xtraArgs
640
+ ) => BaseCheck<ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>
641
+ >;
642
+
643
+ constructor(
644
+ public readonly cc: IStore,
645
+ suitesOverrides: Record<
646
+ keyof SuiteExtensions,
647
+ (
648
+ name: string,
649
+ givens: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[],
650
+ checks: BaseCheck<ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>[]
651
+ ) => BaseSuite<IInput, ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>
652
+ >,
653
+
654
+ givenOverides: Record<
655
+ keyof GivenExtensions,
656
+ (
657
+ name: string,
658
+ features: (keyof IFeatureShape)[],
659
+ whens: BaseWhen<IStore, ISelection, IThenShape>[],
660
+ thens: BaseThen<ISelection, IStore, IThenShape>[],
661
+ ...xtraArgs
662
+ ) => BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>
663
+ >,
664
+
665
+ whenOverides: Record<
666
+ keyof WhenExtensions,
667
+ (c: any) => BaseWhen<IStore, ISelection, IThenShape>
668
+ >,
669
+
670
+ thenOverides: Record<
671
+ keyof ThenExtensions,
672
+ (
673
+ selection: ISelection,
674
+ expectation: any
675
+ ) => BaseThen<ISelection, IStore, IThenShape>
676
+ >,
677
+
678
+ checkOverides: Record<
679
+ keyof CheckExtensions,
680
+ (
681
+ feature: string,
682
+ callback: (whens, thens) => any,
683
+ ...xtraArgs
684
+ ) => BaseCheck<ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>
685
+ >
686
+ ) {
687
+ this.constructorator = cc;
688
+ this.suitesOverrides = suitesOverrides;
689
+ this.givenOverides = givenOverides;
690
+ this.whenOverides = whenOverides;
691
+ this.thenOverides = thenOverides;
692
+ this.checkOverides = checkOverides;
693
+ }
694
+
695
+ Suites() {
696
+ return this.suitesOverrides;
697
+ }
698
+
699
+ Given(): Record<
700
+ keyof GivenExtensions,
701
+ (
702
+ name: string,
703
+ features: (keyof IFeatureShape)[],
704
+ whens: BaseWhen<IStore, ISelection, IThenShape>[],
705
+ thens: BaseThen<ISelection, IStore, IThenShape>[],
706
+ ...xtraArgs
707
+ ) => BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>
708
+ > {
709
+ return this.givenOverides;
710
+ }
711
+
712
+ When(): Record<
713
+ keyof WhenExtensions,
714
+ (arg0: IStore, ...arg1: any) => BaseWhen<IStore, ISelection, IThenShape>
715
+ > {
716
+ return this.whenOverides;
717
+ }
718
+
719
+ Then(): Record<
720
+ keyof ThenExtensions,
721
+ (
722
+ selection: ISelection,
723
+ expectation: any
724
+ ) => BaseThen<ISelection, IStore, IThenShape>
725
+ > {
726
+ return this.thenOverides;
727
+ }
728
+
729
+ Check(): Record<
730
+ keyof CheckExtensions,
731
+ (
732
+ feature: string,
733
+ callback: (whens, thens) => any,
734
+ whens,
735
+ thens
736
+ ) => BaseCheck<ISubject, IStore, ISelection, IThenShape, ITTestShape, IFeatureShape>
737
+ > {
738
+ return this.checkOverides;
739
+ }
740
+ }
741
+
742
+ export abstract class TesterantoLevelOne<
743
+ ITestShape extends ITTestShape,
744
+ IInitialState,
745
+ ISelection,
746
+ IStore,
747
+ ISubject,
748
+ IWhenShape,
749
+ IThenShape,
750
+ IInput,
751
+ IFeatureShape,
752
+ > {
753
+ constructor(
754
+ testImplementation: ITestImplementation<
755
+ IInitialState,
756
+ ISelection,
757
+ IWhenShape,
758
+ IThenShape,
759
+ ITestShape
760
+ >,
761
+
762
+ testSpecification: (
763
+ Suite: {
764
+ [K in keyof ITestShape["suites"]]: (
765
+ feature: string,
766
+ givens: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[],
767
+ checks: BaseCheck<ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>[]
768
+ ) => BaseSuite<IInput, ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>;
769
+ },
770
+ Given: {
771
+ [K in keyof ITestShape["givens"]]: (
772
+ name: string,
773
+ features: (keyof IFeatureShape)[],
774
+ whens: BaseWhen<IStore, ISelection, IThenShape>[],
775
+ thens: BaseThen<ISelection, IStore, IThenShape>[],
776
+ ...a: ITestShape["givens"][K]
777
+ ) => BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>;
778
+ },
779
+ When: {
780
+ [K in keyof ITestShape["whens"]]: (
781
+ ...a: ITestShape["whens"][K]
782
+ ) => BaseWhen<IStore, ISelection, IThenShape>;
783
+ },
784
+ Then: {
785
+ [K in keyof ITestShape["thens"]]: (
786
+ ...a: ITestShape["thens"][K]
787
+ ) => BaseThen<ISelection, IStore, IThenShape>;
788
+ },
789
+ Check: {
790
+ [K in keyof ITestShape["checks"]]: (
791
+ name: string,
792
+ features: (keyof IFeatureShape)[],
793
+ cbz: (...any) => Promise<void>
794
+ ) => any;
795
+ }
796
+ ) => BaseSuite<IInput, ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>[],
797
+
798
+ input: IInput,
799
+
800
+ suiteKlasser: (
801
+ name: string,
802
+ givens: BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>[],
803
+ checks: BaseCheck<ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>[]
804
+ ) =>
805
+ BaseSuite<IInput, ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>,
806
+ givenKlasser: (n, f, w, t, z?) =>
807
+ BaseGiven<ISubject, IStore, ISelection, IThenShape, IFeatureShape>,
808
+ whenKlasser: (s, o) =>
809
+ BaseWhen<IStore, ISelection, IThenShape>,
810
+ thenKlasser: (s, o) =>
811
+ BaseThen<IStore, ISelection, IThenShape>,
812
+ checkKlasser: (n, f, cb, w, t) =>
813
+ BaseCheck<ISubject, IStore, ISelection, IThenShape, ITestShape, IFeatureShape>,
814
+
815
+ testResource
816
+
817
+ ) {
818
+ const classySuites = Object.entries(testImplementation.Suites)
819
+ .reduce((a, [key]) => {
820
+ a[key] = (somestring, givens, checks) => {
821
+ return new suiteKlasser.prototype.constructor(somestring, givens, checks);
822
+ };
823
+ return a;
824
+ }, {}
825
+ );
826
+
827
+ const classyGivens = Object.entries(testImplementation.Givens)
828
+ .reduce((a, [key, z]) => {
829
+ a[key] = (features, whens, thens, ...xtrasW) => {
830
+ return new givenKlasser.prototype.constructor(z.name, features, whens, thens, z(...xtrasW))
831
+ };
832
+ return a;
833
+ }, {}
834
+ );
835
+
836
+ const classyWhens = Object.entries(testImplementation.Whens)
837
+ .reduce((a, [key, whEn]) => {
838
+ a[key] = (payload?: any) => {
839
+ return new whenKlasser.prototype.constructor(
840
+ `${whEn.name}: ${payload && payload.toString()}`,
841
+ whEn(payload)
842
+ )
843
+ };
844
+ return a;
845
+ }, {}
846
+ );
847
+
848
+ const classyThens = Object.entries(testImplementation.Thens)
849
+ .reduce((a, [key, thEn]) => {
850
+ a[key] = (expected: any, x) => {
851
+ return new thenKlasser.prototype.constructor(
852
+ `${thEn.name}: ${expected && expected.toString()}`,
853
+ thEn(expected)
854
+ );
855
+ };
856
+ return a;
857
+ }, {}
858
+ );
859
+
860
+ const classyChecks = Object.entries(testImplementation.Checks)
861
+ .reduce((a, [key, z]) => {
862
+ a[key] = (somestring, features, callback) => {
863
+ return new checkKlasser.prototype.constructor(somestring, features, callback, classyWhens, classyThens);
864
+ };
865
+ return a;
866
+ }, {}
867
+ );
868
+
869
+
870
+ const classyTesteranto = new (class <
871
+ IInput,
872
+ ISubject,
873
+ IStore,
874
+ ISelection,
875
+ SuiteExtensions,
876
+ GivenExtensions,
877
+ WhenExtensions,
878
+ ThenExtensions,
879
+ ICheckExtensions,
880
+ IThenShape,
881
+ IFeatureShape,
882
+ > extends TesterantoLevelZero<
883
+ IInput,
884
+ ISubject,
885
+ IStore,
886
+ ISelection,
887
+ SuiteExtensions,
888
+ GivenExtensions,
889
+ WhenExtensions,
890
+ ThenExtensions,
891
+ ICheckExtensions,
892
+ IThenShape,
893
+ IFeatureShape
894
+ > { })(
895
+ input,
896
+ classySuites,
897
+ classyGivens,
898
+ classyWhens,
899
+ classyThens,
900
+ classyChecks
901
+ );
902
+
903
+ const suites = testSpecification(
904
+ /* @ts-ignore:next-line */
905
+ classyTesteranto.Suites(),
906
+ classyTesteranto.Given(),
907
+ classyTesteranto.When(),
908
+ classyTesteranto.Then(),
909
+ classyTesteranto.Check()
910
+ );
911
+
912
+ /* @ts-ignore:next-line */
913
+ const toReturn: ITestJob[] = suites.map((suite) => {
914
+ return {
915
+ test: suite,
916
+ testResource,
917
+
918
+ toObj: () => {
919
+ return suite.toObj()
920
+ },
921
+
922
+ runner: async (allocatedPorts: number[]) => {
923
+ return suite.run(input, { ports: allocatedPorts });
924
+ },
925
+
926
+ };
927
+ });
928
+
929
+ return toReturn;
930
+ }
931
+ }
932
+
933
+ export const Testeranto = async <
934
+ TestShape extends ITTestShape,
935
+ Input,
936
+ Subject,
937
+ Store,
938
+ Selection,
939
+ WhenShape,
940
+ ThenShape,
941
+ InitialStateShape,
942
+ IFeatureShape
943
+ >(
944
+ input: Input,
945
+ testSpecification: ITestSpecification<TestShape, IFeatureShape>,
946
+ testImplementation,
947
+ // testImplementation: ITestImplementation<
948
+ // InitialStateShape,
949
+ // Selection,
950
+ // WhenShape,
951
+ // ThenShape,
952
+ // TestShape
953
+ // >,
954
+ testResource: ITTestResourceRequirement,
955
+
956
+ testInterface: {
957
+ actionHandler?: (b: (...any) => any) => any,
958
+ afterEach?: (store: Store, ndx: number, cb) => unknown,
959
+ andWhen: (store: Store, actioner, testResource: ITTestResource) => Promise<Selection>,
960
+ assertioner?: (t: ThenShape) => any,
961
+ beforeAll?: (input: Input) => Promise<Subject>,
962
+ beforeEach?: (subject: Subject, initialValues, testResource: ITTestResource) => Promise<Store>,
963
+ butThen?: (store: Store, callback, testResource: ITTestResource) => Promise<Selection>,
964
+ },
965
+
966
+ ) => {
967
+
968
+ const butThen = testInterface.butThen || (async (a) => a as any);
969
+ const { andWhen } = testInterface;
970
+ const actionHandler = testInterface.actionHandler || function (b: (...any: any[]) => any) {
971
+ return b;
972
+ };
973
+ const assertioner = testInterface.assertioner || (async (t) => t as any);
974
+ const beforeAll = testInterface.beforeAll || (async (input) => input as any);
975
+ const beforeEach = testInterface.beforeEach || async function (subject: Input, initialValues: any, testResource: any) {
976
+ return subject as any;
977
+ }
978
+ const afterEach = testInterface.afterEach || (async (s) => s);
979
+
980
+ class MrT extends TesterantoLevelOne<
981
+ TestShape,
982
+ InitialStateShape,
983
+ Selection,
984
+ Store,
985
+ Subject,
986
+ WhenShape,
987
+ ThenShape,
988
+ Input,
989
+ IFeatureShape
990
+ > {
991
+ constructor() {
992
+ super(
993
+ testImplementation,
994
+ /* @ts-ignore:next-line */
995
+ testSpecification,
996
+ input,
997
+ (class extends BaseSuite<Input, Subject, Store, Selection, ThenShape, TestShape, IFeatureShape> {
998
+ async setup(s: Input): Promise<Subject> {
999
+ return beforeAll(s);
1000
+ }
1001
+ test(t: ThenShape): unknown {
1002
+ return assertioner(t);
1003
+ }
1004
+ }),
1005
+
1006
+ class Given extends BaseGiven<Subject, Store, Selection, ThenShape, IFeatureShape> {
1007
+ initialValues: any;
1008
+ constructor(
1009
+ name: string,
1010
+ features: (keyof IFeatureShape)[],
1011
+ whens: BaseWhen<Store, Selection, ThenShape>[],
1012
+ thens: BaseThen<Selection, Store, ThenShape>[],
1013
+ initialValues: any
1014
+ ) {
1015
+ super(name, features, whens, thens);
1016
+ this.initialValues = initialValues;
1017
+ }
1018
+ async givenThat(subject, testResource) {
1019
+ return beforeEach(subject, this.initialValues, testResource);
1020
+ }
1021
+ afterEach(store: Store, ndx: number, cb): Promise<unknown> {
1022
+ return new Promise((res) => res(afterEach(store, ndx, cb)))
1023
+ }
1024
+ },
1025
+
1026
+ class When extends BaseWhen<Store, Selection, WhenShape> {
1027
+ payload?: any;
1028
+
1029
+ constructor(name: string, actioner: (...any) => any, payload?: any) {
1030
+ super(name, (store) => {
1031
+ return actionHandler(actioner)
1032
+ });
1033
+ this.payload = payload;
1034
+ }
1035
+
1036
+ async andWhen(store, actioner, testResource) {
1037
+ return await andWhen(store, actioner, testResource);
1038
+ }
1039
+ },
1040
+
1041
+ class Then extends BaseThen<Selection, Store, ThenShape> {
1042
+ constructor(
1043
+ name: string,
1044
+ callback: (val: Selection) => ThenShape
1045
+ ) {
1046
+ super(name, callback);
1047
+ }
1048
+
1049
+ async butThen(store: any, testResourceConfiguration?: any): Promise<Selection> {
1050
+ return await butThen(store, this.thenCB, testResourceConfiguration)
1051
+ }
1052
+ },
1053
+
1054
+ class Check extends BaseCheck<Subject, Store, Selection, ThenShape, TestShape, IFeatureShape> {
1055
+ initialValues: any;
1056
+
1057
+ constructor(
1058
+ name: string,
1059
+ features: (keyof IFeatureShape)[],
1060
+ checkCallback: (a, b) => any,
1061
+ whens,
1062
+ thens,
1063
+ initialValues: any,
1064
+ ) {
1065
+ super(name, features, checkCallback, whens, thens);
1066
+ this.initialValues = initialValues;
1067
+ }
1068
+
1069
+ async checkThat(subject, testResource) {
1070
+ return beforeEach(subject, this.initialValues, testResource);
1071
+ }
1072
+
1073
+ afterEach(store: Store, ndx: number, cb): Promise<unknown> {
1074
+ return new Promise((res) => res(afterEach(store, ndx, cb)))
1075
+ }
1076
+ },
1077
+ testResource
1078
+ );
1079
+ }
1080
+ }
1081
+
1082
+ const mrt = new MrT();
1083
+
1084
+ console.log("requesting test resources from mothership...", testResource);
1085
+ /* @ts-ignore:next-line */
1086
+ process.send({
1087
+ type: 'testeranto:hola',
1088
+ data: {
1089
+ testResource
1090
+ }
1091
+ });
1092
+
1093
+ console.log("awaiting test resources from mothership...");
1094
+ process.on('message', async function (packet: { data: { go?: boolean, goWithTestResources?: string[] } }) {
1095
+ await mrt[0].runner(packet.data.goWithTestResources);
1096
+
1097
+
1098
+ /* @ts-ignore:next-line */
1099
+ process.send({
1100
+ type: 'testeranto:adios',
1101
+ data: {
1102
+ testResource: mrt[0].test.testResourceConfiguration.ports,
1103
+ results: mrt[0].toObj()
1104
+ }
1105
+ }, (err) => {
1106
+ if (!err) { console.log(`✅`) }
1107
+ else { console.error(`❗️`, err) }
1108
+ process.exit(0); // :-)
1109
+ });
1110
+
1111
+
1112
+ });
1113
+
1114
+ process.on('SIGINT', function () {
1115
+
1116
+ console.log("SIGINT caught. Releasing test resources back to mothership...", mrt[0].test.testResourceConfiguration);
1117
+ console.log("`❗️`");
1118
+ /* @ts-ignore:next-line */
1119
+ process.send({
1120
+ type: 'testeranto:adios',
1121
+ data: {
1122
+ testResource: mrt[0].test.testResourceConfiguration?.ports || []
1123
+ }
1124
+ });
1125
+
1126
+ process.exit(0); // :-)
1127
+ });
1128
+
1129
+ };