@cucumber/query 12.2.0 → 13.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/src/Query.ts CHANGED
@@ -1,6 +1,29 @@
1
1
  import * as messages from '@cucumber/messages'
2
- import { getWorstTestStepResult } from '@cucumber/messages'
3
- import { ArrayMultimap } from '@teppeis/multimaps'
2
+ import {
3
+ Duration,
4
+ Feature,
5
+ getWorstTestStepResult,
6
+ GherkinDocument,
7
+ Pickle,
8
+ PickleStep,
9
+ Rule,
10
+ Scenario,
11
+ Step,
12
+ TestCase,
13
+ TestCaseFinished,
14
+ TestCaseStarted,
15
+ TestRunFinished,
16
+ TestRunStarted,
17
+ TestStep,
18
+ TestStepFinished,
19
+ TestStepResult,
20
+ TestStepResultStatus,
21
+ TimeConversion
22
+ } from '@cucumber/messages'
23
+ import {ArrayMultimap} from '@teppeis/multimaps'
24
+ import {Lineage, NamingStrategy} from "./Lineage";
25
+ import assert from 'assert';
26
+ import { comparatorBy, comparatorById, comparatorByStatus } from './helpers'
4
27
 
5
28
  export default class Query {
6
29
  private readonly testStepResultByPickleId = new ArrayMultimap<string, messages.TestStepResult>()
@@ -8,9 +31,7 @@ export default class Query {
8
31
  string,
9
32
  messages.TestStepResult
10
33
  >()
11
- private readonly testStepById = new Map<string, messages.TestStep>()
12
34
  private readonly testCaseByPickleId = new Map<string, messages.TestCase>()
13
- private readonly testCaseByTestCaseId = new Map<string, messages.TestCase>()
14
35
  private readonly pickleIdByTestStepId = new Map<string, string>()
15
36
  private readonly pickleStepIdByTestStepId = new Map<string, string>()
16
37
  private readonly testStepResultsbyTestStepId = new ArrayMultimap<
@@ -19,64 +40,189 @@ export default class Query {
19
40
  >()
20
41
  private readonly testStepIdsByPickleStepId = new ArrayMultimap<string, string>()
21
42
  private readonly hooksById = new Map<string, messages.Hook>()
22
-
23
43
  private readonly attachmentsByTestStepId = new ArrayMultimap<string, messages.Attachment>()
24
-
25
44
  private readonly stepMatchArgumentsListsByPickleStepId = new Map<
26
45
  string,
27
46
  readonly messages.StepMatchArgumentsList[]
28
47
  >()
29
48
 
49
+ private testRunStarted: TestRunStarted
50
+ private testRunFinished: TestRunFinished
51
+ private readonly testCaseStarted: Array<TestCaseStarted> = []
52
+ private readonly lineageById: Map<string, Lineage> = new Map()
53
+ private readonly stepById: Map<string, Step> = new Map()
54
+ private readonly pickleById: Map<string, Pickle> = new Map()
55
+ private readonly pickleStepById: Map<string, PickleStep> = new Map()
56
+ private readonly testCaseById: Map<string, TestCase> = new Map()
57
+ private readonly testStepById: Map<string, TestStep> = new Map()
58
+ private readonly testCaseFinishedByTestCaseStartedId: Map<string, TestCaseFinished> = new Map()
59
+ private readonly testStepFinishedByTestCaseStartedId: ArrayMultimap<string, TestStepFinished> =
60
+ new ArrayMultimap()
61
+
30
62
  public update(envelope: messages.Envelope) {
63
+ if (envelope.gherkinDocument) {
64
+ this.updateGherkinDocument(envelope.gherkinDocument)
65
+ }
66
+ if (envelope.pickle) {
67
+ this.updatePickle(envelope.pickle)
68
+ }
69
+ if (envelope.hook) {
70
+ this.hooksById.set(envelope.hook.id, envelope.hook)
71
+ }
72
+ if (envelope.testRunStarted) {
73
+ this.testRunStarted = envelope.testRunStarted
74
+ }
31
75
  if (envelope.testCase) {
32
- this.testCaseByTestCaseId.set(envelope.testCase.id, envelope.testCase)
33
- this.testCaseByPickleId.set(envelope.testCase.pickleId, envelope.testCase)
34
- for (const testStep of envelope.testCase.testSteps) {
35
- this.testStepById.set(testStep.id, testStep)
36
- this.pickleIdByTestStepId.set(testStep.id, envelope.testCase.pickleId)
37
- this.pickleStepIdByTestStepId.set(testStep.id, testStep.pickleStepId)
38
- this.testStepIdsByPickleStepId.put(testStep.pickleStepId, testStep.id)
39
- this.stepMatchArgumentsListsByPickleStepId.set(
76
+ this.updateTestCase(envelope.testCase)
77
+ }
78
+ if (envelope.testCaseStarted) {
79
+ this.updateTestCaseStarted(envelope.testCaseStarted)
80
+ }
81
+ if (envelope.attachment) {
82
+ this.attachmentsByTestStepId.put(envelope.attachment.testStepId, envelope.attachment)
83
+ }
84
+ if (envelope.testStepFinished) {
85
+ this.updateTestStepFinished(envelope.testStepFinished)
86
+ }
87
+ if (envelope.testCaseFinished) {
88
+ this.updateTestCaseFinished(envelope.testCaseFinished)
89
+ }
90
+ if (envelope.testRunFinished) {
91
+ this.testRunFinished = envelope.testRunFinished
92
+ }
93
+ }
94
+
95
+ private updateGherkinDocument(gherkinDocument: GherkinDocument) {
96
+ if (gherkinDocument.feature) {
97
+ this.updateFeature(gherkinDocument.feature, {
98
+ gherkinDocument,
99
+ })
100
+ }
101
+ }
102
+
103
+ private updateFeature(feature: Feature, lineage: Lineage) {
104
+ feature.children.forEach((featureChild) => {
105
+ if (featureChild.background) {
106
+ this.updateSteps(featureChild.background.steps)
107
+ }
108
+ if (featureChild.scenario) {
109
+ this.updateScenario(featureChild.scenario, {
110
+ ...lineage,
111
+ feature,
112
+ })
113
+ }
114
+ if (featureChild.rule) {
115
+ this.updateRule(featureChild.rule, {
116
+ ...lineage,
117
+ feature,
118
+ })
119
+ }
120
+ })
121
+ }
122
+
123
+ private updateRule(rule: Rule, lineage: Lineage) {
124
+ rule.children.forEach((ruleChild) => {
125
+ if (ruleChild.background) {
126
+ this.updateSteps(ruleChild.background.steps)
127
+ }
128
+ if (ruleChild.scenario) {
129
+ this.updateScenario(ruleChild.scenario, {
130
+ ...lineage,
131
+ rule,
132
+ })
133
+ }
134
+ })
135
+ }
136
+
137
+ private updateScenario(scenario: Scenario, lineage: Lineage) {
138
+ this.lineageById.set(scenario.id, {
139
+ ...lineage,
140
+ scenario,
141
+ })
142
+ scenario.examples.forEach((examples, examplesIndex) => {
143
+ this.lineageById.set(examples.id, {
144
+ ...lineage,
145
+ scenario,
146
+ examples,
147
+ examplesIndex,
148
+ })
149
+ examples.tableBody.forEach((example, exampleIndex) => {
150
+ this.lineageById.set(example.id, {
151
+ ...lineage,
152
+ scenario,
153
+ examples,
154
+ examplesIndex,
155
+ example,
156
+ exampleIndex,
157
+ })
158
+ })
159
+ })
160
+ this.updateSteps(scenario.steps)
161
+ }
162
+
163
+ private updateSteps(steps: ReadonlyArray<Step>) {
164
+ steps.forEach((step) => this.stepById.set(step.id, step))
165
+ }
166
+
167
+ private updatePickle(pickle: Pickle) {
168
+ this.pickleById.set(pickle.id, pickle)
169
+ pickle.steps.forEach((pickleStep) => this.pickleStepById.set(pickleStep.id, pickleStep))
170
+ }
171
+
172
+ private updateTestCase(testCase: TestCase) {
173
+ this.testCaseById.set(testCase.id, testCase)
174
+
175
+ this.testCaseByPickleId.set(testCase.pickleId, testCase)
176
+ testCase.testSteps.forEach((testStep) => {
177
+ this.testStepById.set(testStep.id, testStep)
178
+ this.pickleIdByTestStepId.set(testStep.id, testCase.pickleId)
179
+ this.pickleStepIdByTestStepId.set(testStep.id, testStep.pickleStepId)
180
+ this.testStepIdsByPickleStepId.put(testStep.pickleStepId, testStep.id)
181
+ this.stepMatchArgumentsListsByPickleStepId.set(
40
182
  testStep.pickleStepId,
41
183
  testStep.stepMatchArgumentsLists
42
- )
43
- }
44
- }
184
+ )
185
+ })
186
+ }
187
+
188
+ private updateTestCaseStarted(testCaseStarted: TestCaseStarted) {
189
+ this.testCaseStarted.push(testCaseStarted)
45
190
 
46
191
  /*
47
192
  when a test case attempt starts besides the first one, clear all existing results
48
193
  and attachments for that test case, so we always report on the latest attempt
49
- TODO keep track of results and attachments from all attempts, expand API accordingly
194
+ (applies to legacy pickle-oriented query methods only)
50
195
  */
51
- if (envelope.testCaseStarted && envelope.testCaseStarted.attempt > 0) {
52
- const testCase = this.testCaseByTestCaseId.get(envelope.testCaseStarted.testCaseId)
53
- this.testStepResultByPickleId.delete(testCase.pickleId)
54
- for (const testStep of testCase.testSteps) {
55
- this.testStepResultsByPickleStepId.delete(testStep.pickleStepId)
56
- this.testStepResultsbyTestStepId.delete(testStep.id)
57
- this.attachmentsByTestStepId.delete(testStep.id)
58
- }
196
+ const testCase = this.testCaseById.get(testCaseStarted.testCaseId)
197
+ this.testStepResultByPickleId.delete(testCase.pickleId)
198
+ for (const testStep of testCase.testSteps) {
199
+ this.testStepResultsByPickleStepId.delete(testStep.pickleStepId)
200
+ this.testStepResultsbyTestStepId.delete(testStep.id)
201
+ this.attachmentsByTestStepId.delete(testStep.id)
59
202
  }
203
+ }
60
204
 
61
- if (envelope.testStepFinished) {
62
- const pickleId = this.pickleIdByTestStepId.get(envelope.testStepFinished.testStepId)
63
- this.testStepResultByPickleId.put(pickleId, envelope.testStepFinished.testStepResult)
205
+ private updateTestStepFinished(testStepFinished: TestStepFinished) {
206
+ this.testStepFinishedByTestCaseStartedId.put(
207
+ testStepFinished.testCaseStartedId,
208
+ testStepFinished
209
+ )
64
210
 
65
- const testStep = this.testStepById.get(envelope.testStepFinished.testStepId)
66
- this.testStepResultsByPickleStepId.put(
211
+ const pickleId = this.pickleIdByTestStepId.get(testStepFinished.testStepId)
212
+ this.testStepResultByPickleId.put(pickleId, testStepFinished.testStepResult)
213
+ const testStep = this.testStepById.get(testStepFinished.testStepId)
214
+ this.testStepResultsByPickleStepId.put(
67
215
  testStep.pickleStepId,
68
- envelope.testStepFinished.testStepResult
69
- )
70
- this.testStepResultsbyTestStepId.put(testStep.id, envelope.testStepFinished.testStepResult)
71
- }
72
-
73
- if (envelope.hook) {
74
- this.hooksById.set(envelope.hook.id, envelope.hook)
75
- }
216
+ testStepFinished.testStepResult
217
+ )
218
+ this.testStepResultsbyTestStepId.put(testStep.id, testStepFinished.testStepResult)
219
+ }
76
220
 
77
- if (envelope.attachment) {
78
- this.attachmentsByTestStepId.put(envelope.attachment.testStepId, envelope.attachment)
79
- }
221
+ private updateTestCaseFinished(testCaseFinished: TestCaseFinished) {
222
+ this.testCaseFinishedByTestCaseStartedId.set(
223
+ testCaseFinished.testCaseStartedId,
224
+ testCaseFinished
225
+ )
80
226
  }
81
227
 
82
228
  /**
@@ -212,4 +358,164 @@ export default class Query {
212
358
  }
213
359
  return result
214
360
  }
361
+
362
+ /* new common interface with Java starts here */
363
+
364
+ public countMostSevereTestStepResultStatus(): Record<TestStepResultStatus, number> {
365
+ const result: Record<TestStepResultStatus, number> = {
366
+ [TestStepResultStatus.AMBIGUOUS]: 0,
367
+ [TestStepResultStatus.FAILED]: 0,
368
+ [TestStepResultStatus.PASSED]: 0,
369
+ [TestStepResultStatus.PENDING]: 0,
370
+ [TestStepResultStatus.SKIPPED]: 0,
371
+ [TestStepResultStatus.UNDEFINED]: 0,
372
+ [TestStepResultStatus.UNKNOWN]: 0,
373
+ }
374
+ for (const testCaseStarted of this.findAllTestCaseStarted()) {
375
+ const mostSevereResult = this.findTestStepFinishedAndTestStepBy(testCaseStarted)
376
+ .map(([testStepFinished]) => testStepFinished.testStepResult)
377
+ .sort(comparatorByStatus)
378
+ .at(-1)
379
+ if (mostSevereResult) {
380
+ result[mostSevereResult.status]++
381
+ }
382
+ }
383
+ return result
384
+ }
385
+
386
+ public countTestCasesStarted(): number {
387
+ return this.findAllTestCaseStarted().length
388
+ }
389
+
390
+ public findAllPickles(): ReadonlyArray<Pickle> {
391
+ const pickles = [...this.pickleById.values()]
392
+ return pickles.sort(comparatorById)
393
+ }
394
+
395
+ public findAllPickleSteps(): ReadonlyArray<PickleStep> {
396
+ const pickleSteps = [...this.pickleStepById.values()]
397
+ return pickleSteps.sort(comparatorById)
398
+ }
399
+
400
+ public findAllTestCaseStarted(): ReadonlyArray<TestCaseStarted> {
401
+ return this.testCaseStarted.filter((testCaseStarted) => {
402
+ const testCaseFinished = this.testCaseFinishedByTestCaseStartedId.get(testCaseStarted.id)
403
+ // only include if not yet finished OR won't be retried
404
+ return !testCaseFinished?.willBeRetried
405
+ })
406
+ }
407
+
408
+ public findAllTestCaseStartedGroupedByFeature(): Map<Feature | undefined, ReadonlyArray<TestCaseStarted>> {
409
+ const results = new Map();
410
+ this.findAllTestCaseStarted()
411
+ .map(testCaseStarted => ([this.findLineageBy(testCaseStarted), testCaseStarted] as const))
412
+ .sort(([a], [b]) => comparatorBy(a.gherkinDocument, b.gherkinDocument, "uri"))
413
+ .forEach(([{feature}, testCaseStarted]) => {
414
+ results.set(feature, [...results.get(feature) ?? [], testCaseStarted])
415
+ })
416
+ return results
417
+ }
418
+
419
+ public findAllTestSteps(): ReadonlyArray<TestStep> {
420
+ const testSteps = [...this.testStepById.values()]
421
+ return testSteps.sort(comparatorById)
422
+ }
423
+
424
+ public findFeatureBy(testCaseStarted: TestCaseStarted): Feature | undefined {
425
+ return this.findLineageBy(testCaseStarted)?.feature
426
+ }
427
+
428
+ public findMostSevereTestStepResultBy(testCaseStarted: TestCaseStarted): TestStepResult | undefined {
429
+ return this.findTestStepFinishedAndTestStepBy(testCaseStarted)
430
+ .map(([testStepFinished]) => testStepFinished.testStepResult)
431
+ .sort(comparatorByStatus)
432
+ .at(-1)
433
+ }
434
+
435
+ public findNameOf(pickle: Pickle, namingStrategy: NamingStrategy): string {
436
+ const lineage = this.findLineageBy(pickle)
437
+ return lineage ? namingStrategy.reduce(lineage, pickle) : pickle.name
438
+ }
439
+
440
+ public findPickleBy(testCaseStarted: TestCaseStarted): Pickle | undefined {
441
+ const testCase = this.findTestCaseBy(testCaseStarted)
442
+ assert.ok(testCase, 'Expected to find TestCase from TestCaseStarted')
443
+ return this.pickleById.get(testCase.pickleId)
444
+ }
445
+
446
+ public findPickleStepBy(testStep: TestStep): PickleStep | undefined {
447
+ assert.ok(testStep.pickleStepId, 'Expected TestStep to have a pickleStepId')
448
+ return this.pickleStepById.get(testStep.pickleStepId)
449
+ }
450
+
451
+ public findStepBy(pickleStep: PickleStep): Step | undefined {
452
+ const [astNodeId] = pickleStep.astNodeIds
453
+ assert.ok('Expected PickleStep to have an astNodeId')
454
+ return this.stepById.get(astNodeId)
455
+ }
456
+
457
+ public findTestCaseBy(testCaseStarted: TestCaseStarted): TestCase | undefined {
458
+ return this.testCaseById.get(testCaseStarted.testCaseId)
459
+ }
460
+
461
+ public findTestCaseDurationBy(testCaseStarted: TestCaseStarted): Duration | undefined {
462
+ const testCaseFinished = this.findTestCaseFinishedBy(testCaseStarted)
463
+ if (!testCaseFinished) {
464
+ return undefined
465
+ }
466
+ return TimeConversion.millisecondsToDuration(
467
+ TimeConversion.timestampToMillisecondsSinceEpoch(testCaseFinished.timestamp) -
468
+ TimeConversion.timestampToMillisecondsSinceEpoch(testCaseStarted.timestamp)
469
+ )
470
+ }
471
+
472
+ public findTestCaseFinishedBy(testCaseStarted: TestCaseStarted): TestCaseFinished | undefined {
473
+ return this.testCaseFinishedByTestCaseStartedId.get(testCaseStarted.id)
474
+ }
475
+
476
+ public findTestRunDuration(): Duration | undefined {
477
+ if (!this.testRunStarted || !this.testRunFinished) {
478
+ return undefined
479
+ }
480
+ return TimeConversion.millisecondsToDuration(
481
+ TimeConversion.timestampToMillisecondsSinceEpoch(this.testRunFinished.timestamp) -
482
+ TimeConversion.timestampToMillisecondsSinceEpoch(this.testRunStarted.timestamp)
483
+ )
484
+ }
485
+
486
+ public findTestRunFinished(): TestRunFinished | undefined {
487
+ return this.testRunFinished
488
+ }
489
+
490
+ public findTestRunStarted(): TestRunStarted | undefined {
491
+ return this.testRunStarted
492
+ }
493
+
494
+ public findTestStepBy(testStepFinished: TestStepFinished): TestStep | undefined {
495
+ return this.testStepById.get(testStepFinished.testStepId)
496
+ }
497
+
498
+ public findTestStepsFinishedBy(testCaseStarted: TestCaseStarted): ReadonlyArray<TestStepFinished> {
499
+ // multimaps `get` implements `getOrDefault([])` behaviour internally
500
+ return [...this.testStepFinishedByTestCaseStartedId.get(testCaseStarted.id)]
501
+ }
502
+
503
+ public findTestStepFinishedAndTestStepBy(
504
+ testCaseStarted: TestCaseStarted
505
+ ): ReadonlyArray<[TestStepFinished, TestStep]> {
506
+ return this.testStepFinishedByTestCaseStartedId
507
+ .get(testCaseStarted.id)
508
+ .map((testStepFinished) => {
509
+ const testStep = this.findTestStepBy(testStepFinished)
510
+ assert.ok(testStep, 'Expected to find TestStep by TestStepFinished')
511
+ return [testStepFinished, testStep]
512
+ })
513
+ }
514
+
515
+ private findLineageBy(element: Pickle | TestCaseStarted) {
516
+ const pickle = "testCaseId" in element ? this.findPickleBy(element) : element
517
+ const deepestAstNodeId = pickle.astNodeIds.at(-1)
518
+ assert.ok(deepestAstNodeId, 'Expected Pickle to have at least one astNodeId')
519
+ return this.lineageById.get(deepestAstNodeId)
520
+ }
215
521
  }
@@ -0,0 +1,164 @@
1
+ import assert from 'node:assert'
2
+ import fs from 'node:fs'
3
+ import * as path from 'node:path'
4
+ import {pipeline, Writable} from 'node:stream'
5
+ import util from 'node:util'
6
+
7
+ import {NdjsonToMessageStream} from '@cucumber/message-streams'
8
+ import {Duration, Envelope, TestRunFinished, TestRunStarted, TestStepResultStatus} from '@cucumber/messages'
9
+ import {glob} from 'glob'
10
+
11
+ import Query from './Query'
12
+ import {
13
+ namingStrategy,
14
+ NamingStrategyExampleName,
15
+ NamingStrategyFeatureName,
16
+ NamingStrategyLength
17
+ } from './Lineage'
18
+
19
+ const asyncPipeline = util.promisify(pipeline)
20
+ const TESTDATA_PATH = path.join(__dirname, '..', '..', 'testdata')
21
+
22
+ describe('Acceptance Tests', async () => {
23
+ const fixtureFiles = glob.sync(`*.query-results.json`, {
24
+ cwd: TESTDATA_PATH,
25
+ absolute: true
26
+ })
27
+
28
+ for (const fixtureFile of fixtureFiles) {
29
+ const [suiteName] = path.basename(fixtureFile).split('.')
30
+ const ndjsonFile = fixtureFile.replace('.query-results.json', '.ndjson')
31
+
32
+ it(suiteName, async () => {
33
+ const query = new Query()
34
+
35
+ await asyncPipeline(
36
+ fs.createReadStream(ndjsonFile, { encoding: 'utf-8' }),
37
+ new NdjsonToMessageStream(),
38
+ new Writable({
39
+ objectMode: true,
40
+ write(envelope: Envelope, _: BufferEncoding, callback) {
41
+ query.update(envelope)
42
+ callback()
43
+ },
44
+ })
45
+ )
46
+
47
+ const expectedResults: ResultsFixture = {
48
+ ...defaults,
49
+ ...JSON.parse(fs.readFileSync(fixtureFile, {
50
+ encoding: 'utf-8',
51
+ }))
52
+ }
53
+
54
+ const actualResults: ResultsFixture = JSON.parse(JSON.stringify({
55
+ countMostSevereTestStepResultStatus: query.countMostSevereTestStepResultStatus(),
56
+ countTestCasesStarted: query.countTestCasesStarted(),
57
+ findAllPickles: query.findAllPickles().length,
58
+ findAllPickleSteps: query.findAllPickleSteps().length,
59
+ findAllTestCaseStarted: query.findAllTestCaseStarted().length,
60
+ findAllTestSteps: query.findAllTestSteps().length,
61
+ findAllTestCaseStartedGroupedByFeature: [...query.findAllTestCaseStartedGroupedByFeature().entries()]
62
+ .map(([feature, testCaseStarteds]) => [feature.name, testCaseStarteds.map(testCaseStarted => testCaseStarted.id)]),
63
+ findFeatureBy: query.findAllTestCaseStarted()
64
+ .map(testCaseStarted => query.findFeatureBy(testCaseStarted))
65
+ .map(feature => feature?.name),
66
+ findMostSevereTestStepResultBy: query.findAllTestCaseStarted()
67
+ .map(testCaseStarted => query.findMostSevereTestStepResultBy(testCaseStarted))
68
+ .map(testStepResult => testStepResult?.status),
69
+ findNameOf: {
70
+ long : query.findAllPickles().map(pickle => query.findNameOf(pickle, namingStrategy(NamingStrategyLength.LONG))),
71
+ excludeFeatureName : query.findAllPickles().map(pickle => query.findNameOf(pickle, namingStrategy(NamingStrategyLength.LONG, NamingStrategyFeatureName.EXCLUDE))),
72
+ longPickleName : query.findAllPickles().map(pickle => query.findNameOf(pickle, namingStrategy(NamingStrategyLength.LONG, NamingStrategyFeatureName.INCLUDE, NamingStrategyExampleName.PICKLE))),
73
+ short : query.findAllPickles().map(pickle => query.findNameOf(pickle, namingStrategy(NamingStrategyLength.SHORT))),
74
+ shortPickleName : query.findAllPickles().map(pickle => query.findNameOf(pickle, namingStrategy(NamingStrategyLength.SHORT, NamingStrategyFeatureName.INCLUDE, NamingStrategyExampleName.PICKLE)))
75
+ },
76
+ findPickleBy: query.findAllTestCaseStarted()
77
+ .map(testCaseStarted => query.findPickleBy(testCaseStarted))
78
+ .map(pickle => pickle?.name),
79
+ findPickleStepBy: query.findAllTestSteps()
80
+ .map(testStep => query.findPickleStepBy(testStep))
81
+ .map(pickleStep => pickleStep?.text),
82
+ findStepBy: query.findAllPickleSteps()
83
+ .map(pickleStep => query.findStepBy(pickleStep))
84
+ .map(step => step?.text),
85
+ findTestCaseBy: query.findAllTestCaseStarted()
86
+ .map(testCaseStarted => query.findTestCaseBy(testCaseStarted))
87
+ .map(testCase => testCase?.id),
88
+ findTestCaseDurationBy: query.findAllTestCaseStarted()
89
+ .map(testCaseStarted => query.findTestCaseDurationBy(testCaseStarted)),
90
+ findTestCaseFinishedBy: query.findAllTestCaseStarted()
91
+ .map(testCaseStarted => query.findTestCaseFinishedBy(testCaseStarted))
92
+ .map(testCaseFinished => testCaseFinished?.testCaseStartedId),
93
+ findTestRunDuration: query.findTestRunDuration(),
94
+ findTestRunFinished: query.findTestRunFinished(),
95
+ findTestRunStarted: query.findTestRunStarted(),
96
+ findTestStepBy: query.findAllTestCaseStarted()
97
+ .flatMap(testCaseStarted => query.findTestStepsFinishedBy(testCaseStarted))
98
+ .map(testStepFinished => query.findTestStepBy(testStepFinished))
99
+ .map(testStep => testStep?.id),
100
+ findTestStepsFinishedBy: query.findAllTestCaseStarted()
101
+ .map(testCaseStarted => query.findTestStepsFinishedBy(testCaseStarted))
102
+ .map(testStepFinisheds => testStepFinisheds.map(testStepFinished => testStepFinished?.testStepId)),
103
+ findTestStepFinishedAndTestStepBy: query.findAllTestCaseStarted()
104
+ .flatMap(testCaseStarted => query.findTestStepFinishedAndTestStepBy(testCaseStarted))
105
+ .map(([testStepFinished, testStep]) => ([testStepFinished.testStepId, testStep.id]))
106
+ }))
107
+
108
+ assert.deepStrictEqual(actualResults, expectedResults)
109
+ })
110
+ }
111
+ })
112
+
113
+ interface ResultsFixture {
114
+ countMostSevereTestStepResultStatus: Record<TestStepResultStatus, number>,
115
+ countTestCasesStarted: number,
116
+ findAllPickles: number,
117
+ findAllPickleSteps: number,
118
+ findAllTestCaseStarted: number,
119
+ findAllTestSteps: number,
120
+ findAllTestCaseStartedGroupedByFeature: Array<[string, string[]]>,
121
+ findFeatureBy: Array<string>,
122
+ findMostSevereTestStepResultBy: Array<TestStepResultStatus>,
123
+ findNameOf: {
124
+ long: Array<string>,
125
+ excludeFeatureName: Array<string>,
126
+ longPickleName: Array<string>,
127
+ short: Array<string>,
128
+ shortPickleName: Array<string>
129
+ },
130
+ findPickleBy: Array<string>,
131
+ findPickleStepBy: Array<string>,
132
+ findStepBy: Array<string>,
133
+ findTestCaseBy: Array<string>,
134
+ findTestCaseDurationBy: Array<Duration>,
135
+ findTestCaseFinishedBy: Array<string>,
136
+ findTestRunDuration: Duration,
137
+ findTestRunFinished: TestRunFinished,
138
+ findTestRunStarted: TestRunStarted,
139
+ findTestStepBy: Array<string>,
140
+ findTestStepsFinishedBy: Array<Array<string>>
141
+ findTestStepFinishedAndTestStepBy: Array<[string, string]>
142
+ }
143
+
144
+ const defaults: Partial<ResultsFixture> = {
145
+ findAllTestCaseStartedGroupedByFeature: [],
146
+ findFeatureBy: [],
147
+ findMostSevereTestStepResultBy: [],
148
+ findNameOf: {
149
+ long: [],
150
+ excludeFeatureName: [],
151
+ longPickleName: [],
152
+ short: [],
153
+ shortPickleName: []
154
+ },
155
+ findPickleBy: [],
156
+ findPickleStepBy: [],
157
+ findStepBy: [],
158
+ findTestCaseBy: [],
159
+ findTestCaseDurationBy: [],
160
+ findTestCaseFinishedBy: [],
161
+ findTestStepBy: [],
162
+ findTestStepsFinishedBy: [],
163
+ findTestStepFinishedAndTestStepBy: []
164
+ }
@@ -0,0 +1,15 @@
1
+ import assert from 'node:assert'
2
+ import { comparatorByStatus } from './helpers'
3
+ import { TestStepResult, TestStepResultStatus } from '@cucumber/messages'
4
+
5
+ describe('comparatorByStatus', () => {
6
+ function resultWithStatus(status: TestStepResultStatus) {
7
+ return { status } as TestStepResult
8
+ }
9
+
10
+ it('puts the more severe status after the less severe', () => {
11
+ assert.strictEqual(comparatorByStatus(resultWithStatus(TestStepResultStatus.PASSED), resultWithStatus(TestStepResultStatus.FAILED)), -1)
12
+ assert.strictEqual(comparatorByStatus(resultWithStatus(TestStepResultStatus.FAILED), resultWithStatus(TestStepResultStatus.PASSED)), 1)
13
+ assert.strictEqual(comparatorByStatus(resultWithStatus(TestStepResultStatus.FAILED), resultWithStatus(TestStepResultStatus.FAILED)), 0)
14
+ })
15
+ })
package/src/helpers.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { TestStepResult, TestStepResultStatus } from '@cucumber/messages'
2
+
3
+ interface WithId {
4
+ id: string | number
5
+ }
6
+
7
+ export function comparatorById(a: WithId, b: WithId) {
8
+ return comparatorBy(a, b, "id")
9
+ }
10
+
11
+ export function comparatorBy(a: any, b: any, key: string) {
12
+ if ( a[key] < b[key] ){
13
+ return -1;
14
+ }
15
+ if ( a[key] > b[key] ){
16
+ return 1;
17
+ }
18
+ return 0;
19
+ }
20
+
21
+ export function comparatorByStatus(a: TestStepResult, b: TestStepResult) {
22
+ if (ordinal(a.status) < ordinal(b.status)) {
23
+ return -1;
24
+ }
25
+ if (ordinal(a.status) > ordinal(b.status) ){
26
+ return 1;
27
+ }
28
+ return 0;
29
+ }
30
+
31
+ function ordinal(status: TestStepResultStatus) {
32
+ return [
33
+ TestStepResultStatus.UNKNOWN,
34
+ TestStepResultStatus.PASSED,
35
+ TestStepResultStatus.SKIPPED,
36
+ TestStepResultStatus.PENDING,
37
+ TestStepResultStatus.UNDEFINED,
38
+ TestStepResultStatus.AMBIGUOUS,
39
+ TestStepResultStatus.FAILED,
40
+ ].indexOf(status)
41
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  import Query from './Query'
2
+ export * from './Lineage'
3
+
2
4
  export { Query }
@@ -11,5 +11,10 @@
11
11
  "target": "es6",
12
12
  "module": "commonjs"
13
13
  },
14
- "include": ["src", "test", "package.json"]
14
+ "include": [
15
+ "src"
16
+ ],
17
+ "exclude": [
18
+ "src/**/*.spec.*"
19
+ ]
15
20
  }