@cucumber/query 13.2.0 → 13.4.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/Query.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  getWorstTestStepResult,
7
7
  GherkinDocument,
8
8
  Hook,
9
+ Location,
9
10
  Meta,
10
11
  Pickle,
11
12
  PickleStep,
@@ -21,11 +22,14 @@ import {
21
22
  TestStepFinished,
22
23
  TestStepResult,
23
24
  TestStepResultStatus,
25
+ TestStepStarted,
24
26
  TimeConversion,
25
27
  } from '@cucumber/messages'
26
- import {ArrayMultimap} from '@teppeis/multimaps'
27
- import {Lineage, NamingStrategy} from "./Lineage";
28
- import { comparatorBy, comparatorById, comparatorByStatus, assert } from './helpers'
28
+ import { ArrayMultimap } from '@teppeis/multimaps'
29
+ import sortBy from 'lodash.sortby'
30
+
31
+ import { assert, statusOrdinal } from './helpers'
32
+ import { Lineage, NamingStrategy } from './Lineage'
29
33
 
30
34
  export default class Query {
31
35
  private readonly testStepResultByPickleId = new ArrayMultimap<string, messages.TestStepResult>()
@@ -51,7 +55,7 @@ export default class Query {
51
55
  private meta: Meta
52
56
  private testRunStarted: TestRunStarted
53
57
  private testRunFinished: TestRunFinished
54
- private readonly testCaseStarted: Array<TestCaseStarted> = []
58
+ private readonly testCaseStartedById: Map<string, TestCaseStarted> = new Map()
55
59
  private readonly lineageById: Map<string, Lineage> = new Map()
56
60
  private readonly stepById: Map<string, Step> = new Map()
57
61
  private readonly pickleById: Map<string, Pickle> = new Map()
@@ -59,10 +63,12 @@ export default class Query {
59
63
  private readonly testCaseById: Map<string, TestCase> = new Map()
60
64
  private readonly testStepById: Map<string, TestStep> = new Map()
61
65
  private readonly testCaseFinishedByTestCaseStartedId: Map<string, TestCaseFinished> = new Map()
66
+ private readonly testStepStartedByTestCaseStartedId: ArrayMultimap<string, TestStepStarted> =
67
+ new ArrayMultimap()
62
68
  private readonly testStepFinishedByTestCaseStartedId: ArrayMultimap<string, TestStepFinished> =
63
- new ArrayMultimap()
69
+ new ArrayMultimap()
64
70
  private readonly attachmentsByTestCaseStartedId: ArrayMultimap<string, Attachment> =
65
- new ArrayMultimap()
71
+ new ArrayMultimap()
66
72
 
67
73
  public update(envelope: messages.Envelope) {
68
74
  if (envelope.meta) {
@@ -86,6 +92,9 @@ export default class Query {
86
92
  if (envelope.testCaseStarted) {
87
93
  this.updateTestCaseStarted(envelope.testCaseStarted)
88
94
  }
95
+ if (envelope.testStepStarted) {
96
+ this.updateTestStepStarted(envelope.testStepStarted)
97
+ }
89
98
  if (envelope.attachment) {
90
99
  this.updateAttachment(envelope.attachment)
91
100
  }
@@ -187,14 +196,14 @@ export default class Query {
187
196
  this.pickleStepIdByTestStepId.set(testStep.id, testStep.pickleStepId)
188
197
  this.testStepIdsByPickleStepId.put(testStep.pickleStepId, testStep.id)
189
198
  this.stepMatchArgumentsListsByPickleStepId.set(
190
- testStep.pickleStepId,
191
- testStep.stepMatchArgumentsLists
199
+ testStep.pickleStepId,
200
+ testStep.stepMatchArgumentsLists
192
201
  )
193
202
  })
194
203
  }
195
204
 
196
205
  private updateTestCaseStarted(testCaseStarted: TestCaseStarted) {
197
- this.testCaseStarted.push(testCaseStarted)
206
+ this.testCaseStartedById.set(testCaseStarted.id, testCaseStarted)
198
207
 
199
208
  /*
200
209
  when a test case attempt starts besides the first one, clear all existing results
@@ -202,14 +211,20 @@ export default class Query {
202
211
  (applies to legacy pickle-oriented query methods only)
203
212
  */
204
213
  const testCase = this.testCaseById.get(testCaseStarted.testCaseId)
205
- this.testStepResultByPickleId.delete(testCase.pickleId)
206
- for (const testStep of testCase.testSteps) {
207
- this.testStepResultsByPickleStepId.delete(testStep.pickleStepId)
208
- this.testStepResultsbyTestStepId.delete(testStep.id)
209
- this.attachmentsByTestStepId.delete(testStep.id)
214
+ if (testCase) {
215
+ this.testStepResultByPickleId.delete(testCase.pickleId)
216
+ for (const testStep of testCase.testSteps) {
217
+ this.testStepResultsByPickleStepId.delete(testStep.pickleStepId)
218
+ this.testStepResultsbyTestStepId.delete(testStep.id)
219
+ this.attachmentsByTestStepId.delete(testStep.id)
220
+ }
210
221
  }
211
222
  }
212
223
 
224
+ private updateTestStepStarted(testStepStarted: TestStepStarted) {
225
+ this.testStepStartedByTestCaseStartedId.put(testStepStarted.testCaseStartedId, testStepStarted)
226
+ }
227
+
213
228
  private updateAttachment(attachment: Attachment) {
214
229
  if (attachment.testStepId) {
215
230
  this.attachmentsByTestStepId.put(attachment.testStepId, attachment)
@@ -221,24 +236,21 @@ export default class Query {
221
236
 
222
237
  private updateTestStepFinished(testStepFinished: TestStepFinished) {
223
238
  this.testStepFinishedByTestCaseStartedId.put(
224
- testStepFinished.testCaseStartedId,
225
- testStepFinished
239
+ testStepFinished.testCaseStartedId,
240
+ testStepFinished
226
241
  )
227
242
 
228
243
  const pickleId = this.pickleIdByTestStepId.get(testStepFinished.testStepId)
229
244
  this.testStepResultByPickleId.put(pickleId, testStepFinished.testStepResult)
230
245
  const testStep = this.testStepById.get(testStepFinished.testStepId)
231
- this.testStepResultsByPickleStepId.put(
232
- testStep.pickleStepId,
233
- testStepFinished.testStepResult
234
- )
246
+ this.testStepResultsByPickleStepId.put(testStep.pickleStepId, testStepFinished.testStepResult)
235
247
  this.testStepResultsbyTestStepId.put(testStep.id, testStepFinished.testStepResult)
236
248
  }
237
249
 
238
250
  private updateTestCaseFinished(testCaseFinished: TestCaseFinished) {
239
251
  this.testCaseFinishedByTestCaseStartedId.set(
240
- testCaseFinished.testCaseStartedId,
241
- testCaseFinished
252
+ testCaseFinished.testCaseStartedId,
253
+ testCaseFinished
242
254
  )
243
255
  }
244
256
 
@@ -353,7 +365,11 @@ export default class Query {
353
365
 
354
366
  for (const step of testCase.testSteps) {
355
367
  if (step.hookId) {
356
- pickleStepFound ? onAfterHookFound(step) : onBeforeHookFound(step)
368
+ if (pickleStepFound) {
369
+ onAfterHookFound(step)
370
+ } else {
371
+ onBeforeHookFound(step)
372
+ }
357
373
  } else {
358
374
  pickleStepFound = true
359
375
  }
@@ -389,10 +405,12 @@ export default class Query {
389
405
  [TestStepResultStatus.UNKNOWN]: 0,
390
406
  }
391
407
  for (const testCaseStarted of this.findAllTestCaseStarted()) {
392
- const mostSevereResult = this.findTestStepFinishedAndTestStepBy(testCaseStarted)
393
- .map(([testStepFinished]) => testStepFinished.testStepResult)
394
- .sort(comparatorByStatus)
395
- .at(-1)
408
+ const mostSevereResult = sortBy(
409
+ this.findTestStepFinishedAndTestStepBy(testCaseStarted).map(
410
+ ([testStepFinished]) => testStepFinished.testStepResult
411
+ ),
412
+ [(testStepResult) => statusOrdinal(testStepResult.status)]
413
+ ).at(-1)
396
414
  if (mostSevereResult) {
397
415
  result[mostSevereResult.status]++
398
416
  }
@@ -406,41 +424,54 @@ export default class Query {
406
424
 
407
425
  public findAllPickles(): ReadonlyArray<Pickle> {
408
426
  const pickles = [...this.pickleById.values()]
409
- return pickles.sort(comparatorById)
427
+ return sortBy(pickles, ['id'])
410
428
  }
411
429
 
412
430
  public findAllPickleSteps(): ReadonlyArray<PickleStep> {
413
431
  const pickleSteps = [...this.pickleStepById.values()]
414
- return pickleSteps.sort(comparatorById)
432
+ return sortBy(pickleSteps, ['id'])
415
433
  }
416
434
 
417
435
  public findAllTestCaseStarted(): ReadonlyArray<TestCaseStarted> {
418
- return this.testCaseStarted.filter((testCaseStarted) => {
419
- const testCaseFinished = this.testCaseFinishedByTestCaseStartedId.get(testCaseStarted.id)
420
- // only include if not yet finished OR won't be retried
421
- return !testCaseFinished?.willBeRetried
422
- })
436
+ return sortBy(
437
+ [...this.testCaseStartedById.values()].filter((testCaseStarted) => {
438
+ const testCaseFinished = this.testCaseFinishedByTestCaseStartedId.get(testCaseStarted.id)
439
+ // only include if not yet finished OR won't be retried
440
+ return !testCaseFinished?.willBeRetried
441
+ }),
442
+ [
443
+ (testCaseStarted) =>
444
+ TimeConversion.timestampToMillisecondsSinceEpoch(testCaseStarted.timestamp),
445
+ 'id',
446
+ ]
447
+ )
423
448
  }
424
449
 
425
- public findAllTestCaseStartedGroupedByFeature(): Map<Feature | undefined, ReadonlyArray<TestCaseStarted>> {
426
- const results = new Map();
427
- this.findAllTestCaseStarted()
428
- .map(testCaseStarted => ([this.findLineageBy(testCaseStarted), testCaseStarted] as const))
429
- .sort(([a], [b]) => comparatorBy(a.gherkinDocument, b.gherkinDocument, "uri"))
430
- .forEach(([{feature}, testCaseStarted]) => {
431
- results.set(feature, [...results.get(feature) ?? [], testCaseStarted])
432
- })
450
+ public findAllTestCaseStartedGroupedByFeature(): Map<
451
+ Feature | undefined,
452
+ ReadonlyArray<TestCaseStarted>
453
+ > {
454
+ const results = new Map()
455
+ sortBy(
456
+ this.findAllTestCaseStarted().map(
457
+ (testCaseStarted) => [this.findLineageBy(testCaseStarted), testCaseStarted] as const
458
+ ),
459
+ [([lineage]) => lineage.gherkinDocument.uri]
460
+ ).forEach(([{ feature }, testCaseStarted]) => {
461
+ results.set(feature, [...(results.get(feature) ?? []), testCaseStarted])
462
+ })
433
463
  return results
434
464
  }
435
465
 
436
466
  public findAllTestSteps(): ReadonlyArray<TestStep> {
437
467
  const testSteps = [...this.testStepById.values()]
438
- return testSteps.sort(comparatorById)
468
+ return sortBy(testSteps, ['id'])
439
469
  }
440
470
 
441
471
  public findAttachmentsBy(testStepFinished: TestStepFinished): ReadonlyArray<Attachment> {
442
- return this.attachmentsByTestCaseStartedId.get(testStepFinished.testCaseStartedId)
443
- .filter(attachment => attachment.testStepId === testStepFinished.testStepId)
472
+ return this.attachmentsByTestCaseStartedId
473
+ .get(testStepFinished.testCaseStartedId)
474
+ .filter((attachment) => attachment.testStepId === testStepFinished.testStepId)
444
475
  }
445
476
 
446
477
  public findFeatureBy(testCaseStarted: TestCaseStarted): Feature | undefined {
@@ -448,21 +479,25 @@ export default class Query {
448
479
  }
449
480
 
450
481
  public findHookBy(testStep: TestStep): Hook | undefined {
451
- if (!testStep.hookId){
482
+ if (!testStep.hookId) {
452
483
  return undefined
453
484
  }
454
485
  return this.hooksById.get(testStep.hookId)
455
486
  }
456
487
 
457
488
  public findMeta(): Meta | undefined {
458
- return this.meta;
489
+ return this.meta
459
490
  }
460
491
 
461
- public findMostSevereTestStepResultBy(testCaseStarted: TestCaseStarted): TestStepResult | undefined {
462
- return this.findTestStepFinishedAndTestStepBy(testCaseStarted)
463
- .map(([testStepFinished]) => testStepFinished.testStepResult)
464
- .sort(comparatorByStatus)
465
- .at(-1)
492
+ public findMostSevereTestStepResultBy(
493
+ testCaseStarted: TestCaseStarted
494
+ ): TestStepResult | undefined {
495
+ return sortBy(
496
+ this.findTestStepFinishedAndTestStepBy(testCaseStarted).map(
497
+ ([testStepFinished]) => testStepFinished.testStepResult
498
+ ),
499
+ [(testStepResult) => statusOrdinal(testStepResult.status)]
500
+ ).at(-1)
466
501
  }
467
502
 
468
503
  public findNameOf(pickle: Pickle, namingStrategy: NamingStrategy): string {
@@ -470,14 +505,22 @@ export default class Query {
470
505
  return lineage ? namingStrategy.reduce(lineage, pickle) : pickle.name
471
506
  }
472
507
 
473
- public findPickleBy(testCaseStarted: TestCaseStarted): Pickle | undefined {
474
- const testCase = this.findTestCaseBy(testCaseStarted)
508
+ public findLocationOf(pickle: Pickle): Location | undefined {
509
+ const lineage = this.findLineageBy(pickle)
510
+ if (lineage?.example) {
511
+ return lineage.example.location
512
+ }
513
+ return lineage?.scenario?.location
514
+ }
515
+
516
+ public findPickleBy(element: TestCaseStarted | TestStepStarted): Pickle | undefined {
517
+ const testCase = this.findTestCaseBy(element)
475
518
  assert.ok(testCase, 'Expected to find TestCase from TestCaseStarted')
476
519
  return this.pickleById.get(testCase.pickleId)
477
520
  }
478
521
 
479
522
  public findPickleStepBy(testStep: TestStep): PickleStep | undefined {
480
- if (!testStep.pickleStepId){
523
+ if (!testStep.pickleStepId) {
481
524
  return undefined
482
525
  }
483
526
  return this.pickleStepById.get(testStep.pickleStepId)
@@ -489,7 +532,10 @@ export default class Query {
489
532
  return this.stepById.get(astNodeId)
490
533
  }
491
534
 
492
- public findTestCaseBy(testCaseStarted: TestCaseStarted): TestCase | undefined {
535
+ public findTestCaseBy(element: TestCaseStarted | TestStepStarted): TestCase | undefined {
536
+ const testCaseStarted =
537
+ 'testCaseStartedId' in element ? this.findTestCaseStartedBy(element) : element
538
+ assert.ok(testCaseStarted, 'Expected to find TestCaseStarted by TestStepStarted')
493
539
  return this.testCaseById.get(testCaseStarted.testCaseId)
494
540
  }
495
541
 
@@ -499,11 +545,15 @@ export default class Query {
499
545
  return undefined
500
546
  }
501
547
  return TimeConversion.millisecondsToDuration(
502
- TimeConversion.timestampToMillisecondsSinceEpoch(testCaseFinished.timestamp) -
548
+ TimeConversion.timestampToMillisecondsSinceEpoch(testCaseFinished.timestamp) -
503
549
  TimeConversion.timestampToMillisecondsSinceEpoch(testCaseStarted.timestamp)
504
550
  )
505
551
  }
506
552
 
553
+ public findTestCaseStartedBy(testStepStarted: TestStepStarted): TestCaseStarted | undefined {
554
+ return this.testCaseStartedById.get(testStepStarted.testCaseStartedId)
555
+ }
556
+
507
557
  public findTestCaseFinishedBy(testCaseStarted: TestCaseStarted): TestCaseFinished | undefined {
508
558
  return this.testCaseFinishedByTestCaseStartedId.get(testCaseStarted.id)
509
559
  }
@@ -513,7 +563,7 @@ export default class Query {
513
563
  return undefined
514
564
  }
515
565
  return TimeConversion.millisecondsToDuration(
516
- TimeConversion.timestampToMillisecondsSinceEpoch(this.testRunFinished.timestamp) -
566
+ TimeConversion.timestampToMillisecondsSinceEpoch(this.testRunFinished.timestamp) -
517
567
  TimeConversion.timestampToMillisecondsSinceEpoch(this.testRunStarted.timestamp)
518
568
  )
519
569
  }
@@ -526,29 +576,36 @@ export default class Query {
526
576
  return this.testRunStarted
527
577
  }
528
578
 
529
- public findTestStepBy(testStepFinished: TestStepFinished): TestStep | undefined {
530
- return this.testStepById.get(testStepFinished.testStepId)
579
+ public findTestStepBy(element: TestStepStarted | TestStepFinished): TestStep | undefined {
580
+ return this.testStepById.get(element.testStepId)
581
+ }
582
+
583
+ public findTestStepsStartedBy(testCaseStarted: TestCaseStarted): ReadonlyArray<TestStepStarted> {
584
+ // multimaps `get` implements `getOrDefault([])` behaviour internally
585
+ return [...this.testStepStartedByTestCaseStartedId.get(testCaseStarted.id)]
531
586
  }
532
587
 
533
- public findTestStepsFinishedBy(testCaseStarted: TestCaseStarted): ReadonlyArray<TestStepFinished> {
588
+ public findTestStepsFinishedBy(
589
+ testCaseStarted: TestCaseStarted
590
+ ): ReadonlyArray<TestStepFinished> {
534
591
  // multimaps `get` implements `getOrDefault([])` behaviour internally
535
592
  return [...this.testStepFinishedByTestCaseStartedId.get(testCaseStarted.id)]
536
593
  }
537
594
 
538
595
  public findTestStepFinishedAndTestStepBy(
539
- testCaseStarted: TestCaseStarted
596
+ testCaseStarted: TestCaseStarted
540
597
  ): ReadonlyArray<[TestStepFinished, TestStep]> {
541
598
  return this.testStepFinishedByTestCaseStartedId
542
- .get(testCaseStarted.id)
543
- .map((testStepFinished) => {
544
- const testStep = this.findTestStepBy(testStepFinished)
545
- assert.ok(testStep, 'Expected to find TestStep by TestStepFinished')
546
- return [testStepFinished, testStep]
547
- })
599
+ .get(testCaseStarted.id)
600
+ .map((testStepFinished) => {
601
+ const testStep = this.findTestStepBy(testStepFinished)
602
+ assert.ok(testStep, 'Expected to find TestStep by TestStepFinished')
603
+ return [testStepFinished, testStep]
604
+ })
548
605
  }
549
606
 
550
- private findLineageBy(element: Pickle | TestCaseStarted) {
551
- const pickle = "testCaseId" in element ? this.findPickleBy(element) : element
607
+ public findLineageBy(element: Pickle | TestCaseStarted): Lineage | undefined {
608
+ const pickle = 'testCaseId' in element ? this.findPickleBy(element) : element
552
609
  const deepestAstNodeId = pickle.astNodeIds.at(-1)
553
610
  assert.ok(deepestAstNodeId, 'Expected Pickle to have at least one astNodeId')
554
611
  return this.lineageById.get(deepestAstNodeId)