@cucumber/query 14.6.0 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Query.ts CHANGED
@@ -3,7 +3,6 @@ import {
3
3
  Attachment,
4
4
  Duration,
5
5
  Feature,
6
- getWorstTestStepResult,
7
6
  GherkinDocument,
8
7
  Hook,
9
8
  Location,
@@ -37,26 +36,6 @@ import { assert, statusOrdinal } from './helpers'
37
36
  import { Lineage } from './Lineage'
38
37
 
39
38
  export default class Query {
40
- private readonly testStepResultByPickleId = new ArrayMultimap<string, messages.TestStepResult>()
41
- private readonly testStepResultsByPickleStepId = new ArrayMultimap<
42
- string,
43
- messages.TestStepResult
44
- >()
45
- private readonly testCaseByPickleId = new Map<string, messages.TestCase>()
46
- private readonly pickleIdByTestStepId = new Map<string, string>()
47
- private readonly pickleStepIdByTestStepId = new Map<string, string>()
48
- private readonly testStepResultsbyTestStepId = new ArrayMultimap<
49
- string,
50
- messages.TestStepResult
51
- >()
52
- private readonly testStepIdsByPickleStepId = new ArrayMultimap<string, string>()
53
- private readonly hooksById = new Map<string, messages.Hook>()
54
- private readonly attachmentsByTestStepId = new ArrayMultimap<string, messages.Attachment>()
55
- private readonly stepMatchArgumentsListsByPickleStepId = new Map<
56
- string,
57
- readonly messages.StepMatchArgumentsList[]
58
- >()
59
-
60
39
  private meta: Meta
61
40
  private testRunStarted: TestRunStarted
62
41
  private testRunFinished: TestRunFinished
@@ -65,6 +44,7 @@ export default class Query {
65
44
  private readonly stepById: Map<string, Step> = new Map()
66
45
  private readonly pickleById: Map<string, Pickle> = new Map()
67
46
  private readonly pickleStepById: Map<string, PickleStep> = new Map()
47
+ private readonly hookById: Map<string, Hook> = new Map()
68
48
  private readonly stepDefinitionById: Map<string, StepDefinition> = new Map()
69
49
  private readonly testCaseById: Map<string, TestCase> = new Map()
70
50
  private readonly testStepById: Map<string, TestStep> = new Map()
@@ -95,7 +75,7 @@ export default class Query {
95
75
  this.updatePickle(envelope.pickle)
96
76
  }
97
77
  if (envelope.hook) {
98
- this.hooksById.set(envelope.hook.id, envelope.hook)
78
+ this.hookById.set(envelope.hook.id, envelope.hook)
99
79
  }
100
80
  if (envelope.stepDefinition) {
101
81
  this.stepDefinitionById.set(envelope.stepDefinition.id, envelope.stepDefinition)
@@ -149,6 +129,7 @@ export default class Query {
149
129
  private updateFeature(feature: Feature, lineage: Lineage) {
150
130
  feature.children.forEach((featureChild) => {
151
131
  if (featureChild.background) {
132
+ lineage.background = featureChild.background
152
133
  this.updateSteps(featureChild.background.steps)
153
134
  }
154
135
  if (featureChild.scenario) {
@@ -169,6 +150,7 @@ export default class Query {
169
150
  private updateRule(rule: Rule, lineage: Lineage) {
170
151
  rule.children.forEach((ruleChild) => {
171
152
  if (ruleChild.background) {
153
+ lineage.ruleBackground = ruleChild.background
172
154
  this.updateSteps(ruleChild.background.steps)
173
155
  }
174
156
  if (ruleChild.scenario) {
@@ -228,37 +210,13 @@ export default class Query {
228
210
 
229
211
  private updateTestCase(testCase: TestCase) {
230
212
  this.testCaseById.set(testCase.id, testCase)
231
-
232
- this.testCaseByPickleId.set(testCase.pickleId, testCase)
233
213
  testCase.testSteps.forEach((testStep) => {
234
214
  this.testStepById.set(testStep.id, testStep)
235
- this.pickleIdByTestStepId.set(testStep.id, testCase.pickleId)
236
- this.pickleStepIdByTestStepId.set(testStep.id, testStep.pickleStepId)
237
- this.testStepIdsByPickleStepId.put(testStep.pickleStepId, testStep.id)
238
- this.stepMatchArgumentsListsByPickleStepId.set(
239
- testStep.pickleStepId,
240
- testStep.stepMatchArgumentsLists
241
- )
242
215
  })
243
216
  }
244
217
 
245
218
  private updateTestCaseStarted(testCaseStarted: TestCaseStarted) {
246
219
  this.testCaseStartedById.set(testCaseStarted.id, testCaseStarted)
247
-
248
- /*
249
- when a test case attempt starts besides the first one, clear all existing results
250
- and attachments for that test case, so we always report on the latest attempt
251
- (applies to legacy pickle-oriented query methods only)
252
- */
253
- const testCase = this.testCaseById.get(testCaseStarted.testCaseId)
254
- if (testCase) {
255
- this.testStepResultByPickleId.delete(testCase.pickleId)
256
- for (const testStep of testCase.testSteps) {
257
- this.testStepResultsByPickleStepId.delete(testStep.pickleStepId)
258
- this.testStepResultsbyTestStepId.delete(testStep.id)
259
- this.attachmentsByTestStepId.delete(testStep.id)
260
- }
261
- }
262
220
  }
263
221
 
264
222
  private updateTestStepStarted(testStepStarted: TestStepStarted) {
@@ -266,9 +224,6 @@ export default class Query {
266
224
  }
267
225
 
268
226
  private updateAttachment(attachment: Attachment) {
269
- if (attachment.testStepId) {
270
- this.attachmentsByTestStepId.put(attachment.testStepId, attachment)
271
- }
272
227
  if (attachment.testCaseStartedId) {
273
228
  this.attachmentsByTestCaseStartedId.put(attachment.testCaseStartedId, attachment)
274
229
  }
@@ -282,12 +237,6 @@ export default class Query {
282
237
  testStepFinished.testCaseStartedId,
283
238
  testStepFinished
284
239
  )
285
-
286
- const pickleId = this.pickleIdByTestStepId.get(testStepFinished.testStepId)
287
- this.testStepResultByPickleId.put(pickleId, testStepFinished.testStepResult)
288
- const testStep = this.testStepById.get(testStepFinished.testStepId)
289
- this.testStepResultsByPickleStepId.put(testStep.pickleStepId, testStepFinished.testStepResult)
290
- this.testStepResultsbyTestStepId.put(testStep.id, testStepFinished.testStepResult)
291
240
  }
292
241
 
293
242
  private updateTestCaseFinished(testCaseFinished: TestCaseFinished) {
@@ -305,146 +254,6 @@ export default class Query {
305
254
  this.undefinedParameterTypes.push(undefinedParameterType)
306
255
  }
307
256
 
308
- /**
309
- * Gets all the results for multiple pickle steps
310
- * @param pickleStepIds
311
- */
312
- public getPickleStepTestStepResults(
313
- pickleStepIds: readonly string[]
314
- ): readonly messages.TestStepResult[] {
315
- if (pickleStepIds.length === 0) {
316
- return [
317
- {
318
- status: messages.TestStepResultStatus.UNKNOWN,
319
- duration: messages.TimeConversion.millisecondsToDuration(0),
320
- },
321
- ]
322
- }
323
- return pickleStepIds.reduce((testStepResults: messages.TestStepResult[], pickleId) => {
324
- return testStepResults.concat(this.testStepResultsByPickleStepId.get(pickleId))
325
- }, [])
326
- }
327
-
328
- /**
329
- * Gets all the results for multiple pickles
330
- * @param pickleIds
331
- */
332
- public getPickleTestStepResults(
333
- pickleIds: readonly string[]
334
- ): readonly messages.TestStepResult[] {
335
- if (pickleIds.length === 0) {
336
- return [
337
- {
338
- status: messages.TestStepResultStatus.UNKNOWN,
339
- duration: messages.TimeConversion.millisecondsToDuration(0),
340
- },
341
- ]
342
- }
343
- return pickleIds.reduce((testStepResults: messages.TestStepResult[], pickleId) => {
344
- return testStepResults.concat(this.testStepResultByPickleId.get(pickleId))
345
- }, [])
346
- }
347
-
348
- /**
349
- * Gets all the attachments for multiple pickle steps
350
- * @param pickleStepIds
351
- */
352
- public getPickleStepAttachments(
353
- pickleStepIds: readonly string[]
354
- ): readonly messages.Attachment[] {
355
- return this.getTestStepsAttachments(
356
- pickleStepIds.reduce((testStepIds: string[], pickleStepId: string) => {
357
- return testStepIds.concat(this.testStepIdsByPickleStepId.get(pickleStepId))
358
- }, [])
359
- )
360
- }
361
-
362
- public getTestStepsAttachments(testStepIds: readonly string[]): readonly messages.Attachment[] {
363
- return testStepIds.reduce((attachments: messages.Attachment[], testStepId) => {
364
- return attachments.concat(this.attachmentsByTestStepId.get(testStepId))
365
- }, [])
366
- }
367
-
368
- /**
369
- * Get StepMatchArguments for a pickle step
370
- * @param pickleStepId
371
- */
372
- public getStepMatchArgumentsLists(
373
- pickleStepId: string
374
- ): readonly messages.StepMatchArgumentsList[] | undefined {
375
- return this.stepMatchArgumentsListsByPickleStepId.get(pickleStepId)
376
- }
377
-
378
- public getHook(hookId: string): messages.Hook {
379
- return this.hooksById.get(hookId)
380
- }
381
-
382
- public getBeforeHookSteps(pickleId: string): readonly messages.TestStep[] {
383
- const hookSteps: messages.TestStep[] = []
384
-
385
- this.identifyHookSteps(
386
- pickleId,
387
- (hook) => hookSteps.push(hook),
388
- () => null
389
- )
390
- return hookSteps
391
- }
392
-
393
- public getAfterHookSteps(pickleId: string): readonly messages.TestStep[] {
394
- const hookSteps: messages.TestStep[] = []
395
-
396
- this.identifyHookSteps(
397
- pickleId,
398
- () => null,
399
- (hook) => hookSteps.push(hook)
400
- )
401
- return hookSteps
402
- }
403
-
404
- private identifyHookSteps(
405
- pickleId: string,
406
- onBeforeHookFound: (hook: messages.TestStep) => void,
407
- onAfterHookFound: (hook: messages.TestStep) => void
408
- ): void {
409
- const testCase = this.testCaseByPickleId.get(pickleId)
410
-
411
- if (!testCase) {
412
- return
413
- }
414
-
415
- let pickleStepFound = false
416
-
417
- for (const step of testCase.testSteps) {
418
- if (step.hookId) {
419
- if (pickleStepFound) {
420
- onAfterHookFound(step)
421
- } else {
422
- onBeforeHookFound(step)
423
- }
424
- } else {
425
- pickleStepFound = true
426
- }
427
- }
428
- }
429
-
430
- public getTestStepResults(testStepId: string): messages.TestStepResult[] {
431
- return this.testStepResultsbyTestStepId.get(testStepId)
432
- }
433
-
434
- public getStatusCounts(
435
- pickleIds: readonly string[]
436
- ): Partial<Record<messages.TestStepResultStatus, number>> {
437
- const result: Partial<Record<messages.TestStepResultStatus, number>> = {}
438
- for (const pickleId of pickleIds) {
439
- const testStepResult = getWorstTestStepResult(this.getPickleTestStepResults([pickleId]))
440
- const count = result[testStepResult.status] || 0
441
- result[testStepResult.status] = count + 1
442
- }
443
- return result
444
- }
445
-
446
- /* new common interface with Java starts here */
447
-
448
257
  public countMostSevereTestStepResultStatus(): Record<TestStepResultStatus, number> {
449
258
  const result: Record<TestStepResultStatus, number> = {
450
259
  [TestStepResultStatus.AMBIGUOUS]: 0,
@@ -514,6 +323,44 @@ export default class Query {
514
323
  )
515
324
  }
516
325
 
326
+ public findAllTestCaseStartedOrderBy<T>(
327
+ findOrderBy: (query: Query, testCaseStarted: TestCaseStarted) => T | undefined,
328
+ order: (a: T, b: T) => number
329
+ ): ReadonlyArray<TestCaseStarted> {
330
+ const withOrderBy = this.findAllTestCaseStarted().map((testCaseStarted) => ({
331
+ testCaseStarted,
332
+ orderBy: findOrderBy(this, testCaseStarted),
333
+ }))
334
+
335
+ const sorted = withOrderBy.sort((a, b) => {
336
+ if (a.orderBy === undefined && b.orderBy === undefined) return 0
337
+ if (a.orderBy === undefined) return 1
338
+ if (b.orderBy === undefined) return -1
339
+ return order(a.orderBy, b.orderBy)
340
+ })
341
+
342
+ return sorted.map((item) => item.testCaseStarted)
343
+ }
344
+
345
+ public findAllTestCaseFinishedOrderBy<T>(
346
+ findOrderBy: (query: Query, testCaseFinished: TestCaseFinished) => T | undefined,
347
+ order: (a: T, b: T) => number
348
+ ): ReadonlyArray<TestCaseFinished> {
349
+ const withOrderBy = this.findAllTestCaseFinished().map((testCaseFinished) => ({
350
+ testCaseFinished,
351
+ orderBy: findOrderBy(this, testCaseFinished),
352
+ }))
353
+
354
+ const sorted = withOrderBy.sort((a, b) => {
355
+ if (a.orderBy === undefined && b.orderBy === undefined) return 0
356
+ if (a.orderBy === undefined) return 1
357
+ if (b.orderBy === undefined) return -1
358
+ return order(a.orderBy, b.orderBy)
359
+ })
360
+
361
+ return sorted.map((item) => item.testCaseFinished)
362
+ }
363
+
517
364
  public findAllTestSteps(): ReadonlyArray<TestStep> {
518
365
  return [...this.testStepById.values()]
519
366
  }
@@ -559,7 +406,7 @@ export default class Query {
559
406
  if (!item.hookId) {
560
407
  return undefined
561
408
  }
562
- return this.hooksById.get(item.hookId)
409
+ return this.hookById.get(item.hookId)
563
410
  }
564
411
 
565
412
  public findMeta(): Meta | undefined {
@@ -1,16 +1,23 @@
1
1
  import assert from 'node:assert'
2
2
  import fs from 'node:fs'
3
3
  import * as path from 'node:path'
4
- import { pipeline, Writable } from 'node:stream'
5
- import util from 'node:util'
4
+ import { Writable } from 'node:stream'
5
+ import { pipeline } from 'node:stream/promises'
6
6
 
7
- // eslint-disable-next-line n/no-extraneous-import
8
7
  import { NdjsonToMessageStream } from '@cucumber/message-streams'
9
- import { Envelope } from '@cucumber/messages'
8
+ import { Envelope, Pickle } from '@cucumber/messages'
10
9
 
11
10
  import Query from './Query'
12
11
 
13
- const asyncPipeline = util.promisify(pipeline)
12
+ const reversePickleComparator = (a: Pickle, b: Pickle): number => {
13
+ if (a.uri !== b.uri) {
14
+ return b.uri.localeCompare(a.uri)
15
+ }
16
+ if (a.location.line !== b.location.line) {
17
+ return b.location.line - a.location.line
18
+ }
19
+ return b.location.column - a.location.column
20
+ }
14
21
 
15
22
  describe('Acceptance Tests', async () => {
16
23
  const sources = [
@@ -32,6 +39,21 @@ describe('Acceptance Tests', async () => {
32
39
  findAllPickleSteps: (query: Query) => query.findAllPickleSteps().length,
33
40
  findAllStepDefinitions: (query: Query) => query.findAllStepDefinitions().length,
34
41
  findAllTestCaseStarted: (query: Query) => query.findAllTestCaseStarted().length,
42
+ findAllTestCaseStartedOrderBy: (query: Query) =>
43
+ query
44
+ .findAllTestCaseStartedOrderBy(
45
+ (q, testCaseStarted) => q.findPickleBy(testCaseStarted),
46
+ reversePickleComparator
47
+ )
48
+ .map((testCaseStarted) => testCaseStarted.id),
49
+ findAllTestCaseFinished: (query: Query) => query.findAllTestCaseFinished().length,
50
+ findAllTestCaseFinishedOrderBy: (query: Query) =>
51
+ query
52
+ .findAllTestCaseFinishedOrderBy(
53
+ (q, testCaseFinished) => q.findPickleBy(testCaseFinished),
54
+ reversePickleComparator
55
+ )
56
+ .map((testCaseFinished) => testCaseFinished.testCaseStartedId),
35
57
  findAllTestRunHookStarted: (query: Query) => query.findAllTestRunHookStarted().length,
36
58
  findAllTestRunHookFinished: (query: Query) => query.findAllTestRunHookFinished().length,
37
59
  findTestRunHookStartedBy: (query: Query) =>
@@ -281,7 +303,7 @@ describe('Acceptance Tests', async () => {
281
303
  it(suiteName + ' -> ' + methodName, async () => {
282
304
  const query = new Query()
283
305
 
284
- await asyncPipeline(
306
+ await pipeline(
285
307
  fs.createReadStream(source, { encoding: 'utf-8' }),
286
308
  new NdjsonToMessageStream(),
287
309
  new Writable({