@cucumber/query 10.0.0 → 12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cucumber/query",
3
- "version": "10.0.0",
3
+ "version": "12.0.0",
4
4
  "description": "Cucumber Query - query messages",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -22,18 +22,18 @@
22
22
  },
23
23
  "homepage": "https://github.com/cucumber/cucumber#readme",
24
24
  "devDependencies": {
25
- "@cucumber/fake-cucumber": "^12.0.0",
26
- "@cucumber/gherkin": "^19.0.1",
27
- "@cucumber/gherkin-streams": "^2.0.1",
28
- "@cucumber/gherkin-utils": "^5.0.1",
29
- "@types/mocha": "8.2.2",
30
- "@types/node": "15.3.0",
31
- "mocha": "8.4.0",
32
- "ts-node": "9.1.1",
33
- "typescript": "4.2.4"
25
+ "@cucumber/fake-cucumber": "^16.0.0",
26
+ "@cucumber/gherkin": "^24.0.0",
27
+ "@cucumber/gherkin-streams": "^5.0.1",
28
+ "@cucumber/gherkin-utils": "^8.0.0",
29
+ "@types/mocha": "9.1.1",
30
+ "@types/node": "16.11.38",
31
+ "mocha": "10.0.0",
32
+ "ts-node": "10.8.0",
33
+ "typescript": "4.7.2"
34
34
  },
35
35
  "dependencies": {
36
- "@cucumber/messages": "^16.0.0",
36
+ "@cucumber/messages": "^19.0.0",
37
37
  "@teppeis/multimaps": "2.0.0"
38
38
  },
39
39
  "directories": {
package/src/Query.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as messages from '@cucumber/messages'
2
+ import { getWorstTestStepResult } from '@cucumber/messages'
2
3
  import { ArrayMultimap } from '@teppeis/multimaps'
3
4
 
4
5
  export default class Query {
@@ -9,6 +10,7 @@ export default class Query {
9
10
  >()
10
11
  private readonly testStepById = new Map<string, messages.TestStep>()
11
12
  private readonly testCaseByPickleId = new Map<string, messages.TestCase>()
13
+ private readonly testCaseByTestCaseId = new Map<string, messages.TestCase>()
12
14
  private readonly pickleIdByTestStepId = new Map<string, string>()
13
15
  private readonly pickleStepIdByTestStepId = new Map<string, string>()
14
16
  private readonly testStepResultsbyTestStepId = new ArrayMultimap<
@@ -27,6 +29,7 @@ export default class Query {
27
29
 
28
30
  public update(envelope: messages.Envelope) {
29
31
  if (envelope.testCase) {
32
+ this.testCaseByTestCaseId.set(envelope.testCase.id, envelope.testCase)
30
33
  this.testCaseByPickleId.set(envelope.testCase.pickleId, envelope.testCase)
31
34
  for (const testStep of envelope.testCase.testSteps) {
32
35
  this.testStepById.set(testStep.id, testStep)
@@ -40,6 +43,21 @@ export default class Query {
40
43
  }
41
44
  }
42
45
 
46
+ /*
47
+ when a test case attempt starts besides the first one, clear all existing results
48
+ 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
50
+ */
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
+ }
59
+ }
60
+
43
61
  if (envelope.testStepFinished) {
44
62
  const pickleId = this.pickleIdByTestStepId.get(envelope.testStepFinished.testStepId)
45
63
  this.testStepResultByPickleId.put(pickleId, envelope.testStepFinished.testStepResult)
@@ -73,7 +91,6 @@ export default class Query {
73
91
  {
74
92
  status: messages.TestStepResultStatus.UNKNOWN,
75
93
  duration: messages.TimeConversion.millisecondsToDuration(0),
76
- willBeRetried: false,
77
94
  },
78
95
  ]
79
96
  }
@@ -94,7 +111,6 @@ export default class Query {
94
111
  {
95
112
  status: messages.TestStepResultStatus.UNKNOWN,
96
113
  duration: messages.TimeConversion.millisecondsToDuration(0),
97
- willBeRetried: false,
98
114
  },
99
115
  ]
100
116
  }
@@ -184,4 +200,16 @@ export default class Query {
184
200
  public getTestStepResults(testStepId: string): messages.TestStepResult[] {
185
201
  return this.testStepResultsbyTestStepId.get(testStepId)
186
202
  }
203
+
204
+ public getStatusCounts(
205
+ pickleIds: readonly string[]
206
+ ): Partial<Record<messages.TestStepResultStatus, number>> {
207
+ const result: Partial<Record<messages.TestStepResultStatus, number>> = {}
208
+ for (const pickleId of pickleIds) {
209
+ const testStepResult = getWorstTestStepResult(this.getPickleTestStepResults([pickleId]))
210
+ const count = result[testStepResult.status] || 0
211
+ result[testStepResult.status] = count + 1
212
+ }
213
+ return result
214
+ }
187
215
  }
package/test/QueryTest.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  makeTestCase,
11
11
  IncrementClock,
12
12
  IncrementStopwatch,
13
+ RunOptions,
13
14
  } from '@cucumber/fake-cucumber'
14
15
 
15
16
  import { promisify } from 'util'
@@ -381,6 +382,24 @@ describe('Query', () => {
381
382
  assert.deepStrictEqual(results.length, 1)
382
383
  assert.deepStrictEqual(results[0].status, 'PASSED')
383
384
  })
385
+
386
+ it('returns the result from the last attempt only where retry has been used', async () => {
387
+ const emittedMessages: Array<messages.Envelope> = []
388
+ await execute(
389
+ `Feature: hello
390
+ Scenario: hi
391
+ Given a step that passes the second time
392
+ `,
393
+ (message) => emittedMessages.push(message),
394
+ { allowedRetries: 1 }
395
+ )
396
+ const testCase = emittedMessages.find((child) => child.testCase).testCase
397
+ const testStep = testCase.testSteps[0]
398
+ const results = cucumberQuery.getTestStepResults(testStep.id)
399
+
400
+ assert.deepStrictEqual(results.length, 1)
401
+ assert.deepStrictEqual(results[0].status, 'PASSED')
402
+ })
384
403
  })
385
404
 
386
405
  describe('#getHook(HookId)', () => {
@@ -423,14 +442,84 @@ describe('Query', () => {
423
442
 
424
443
  assert.strictEqual(attachments[0].body, 'Hello')
425
444
  })
445
+
446
+ it('returns attachments from the last attempt only where retry has been used', async () => {
447
+ const testCases: messages.TestCase[] = []
448
+ await execute(
449
+ `Feature: hello
450
+ Scenario: ok
451
+ Given a passed step with attachment
452
+ And a step that passes the second time
453
+ `,
454
+ (envelope) => {
455
+ if (envelope.testCase) {
456
+ testCases.push(envelope.testCase)
457
+ }
458
+ },
459
+ { allowedRetries: 1 }
460
+ )
461
+
462
+ const attachments = cucumberQuery.getTestStepsAttachments([testCases[0].testSteps[0].id])
463
+ assert.strictEqual(attachments.length, 1)
464
+
465
+ assert.strictEqual(attachments[0].body, 'Hello')
466
+ })
467
+ })
468
+
469
+ describe('#getStatusCounts', () => {
470
+ it('returns the number of pickles for each status', async () => {
471
+ await execute(
472
+ `Feature: hello
473
+ Scenario: ok
474
+ Given a passed step
475
+ `,
476
+ () => undefined
477
+ )
478
+
479
+ await execute(
480
+ `Feature: hello
481
+ Scenario: ok
482
+ Given a passed step
483
+ `,
484
+ () => undefined
485
+ )
486
+
487
+ await execute(
488
+ `Feature: hello
489
+ Scenario: ok
490
+ Given a failed step
491
+ `,
492
+ () => undefined
493
+ )
494
+
495
+ await execute(
496
+ `Feature: hello
497
+ Scenario: ok
498
+ Given an undefined step
499
+ `,
500
+ () => undefined
501
+ )
502
+
503
+ const statuses = cucumberQuery.getStatusCounts(
504
+ gherkinQuery.getPickles().map((pickle) => pickle.id)
505
+ )
506
+
507
+ const expectedStatuses: Partial<Record<messages.TestStepResultStatus, number>> = {
508
+ PASSED: 2,
509
+ FAILED: 1,
510
+ UNDEFINED: 1,
511
+ }
512
+ assert.deepStrictEqual(statuses, expectedStatuses)
513
+ })
426
514
  })
427
515
  })
428
516
 
429
517
  async function execute(
430
518
  gherkinSource: string,
431
- messagesHandler: (envelope: messages.Envelope) => void = () => null
519
+ messagesHandler: (envelope: messages.Envelope) => void = () => null,
520
+ runOptions: RunOptions = { allowedRetries: 0 }
432
521
  ): Promise<void> {
433
- const newId = messages.IdGenerator.incrementing()
522
+ const newId = messages.IdGenerator.uuid()
434
523
  const clock = new IncrementClock()
435
524
  const stopwatch = new IncrementStopwatch()
436
525
  const makeErrorMessage = withFullStackTrace()
@@ -453,6 +542,13 @@ describe('Query', () => {
453
542
  supportCode.defineStepDefinition(null, 'I have {int} cukes in my {word}', (cukes: number) => {
454
543
  assert.ok(cukes)
455
544
  })
545
+ let passesSecondTime = 0
546
+ supportCode.defineStepDefinition(null, 'a step that passes the second time', () => {
547
+ passesSecondTime++
548
+ if (passesSecondTime < 2) {
549
+ throw new Error(`This step failed.`)
550
+ }
551
+ })
456
552
 
457
553
  const queryUpdateStream = new Writable({
458
554
  objectMode: true,
@@ -473,7 +569,7 @@ describe('Query', () => {
473
569
  })
474
570
  await pipelinePromise(gherkinMessages(gherkinSource, 'test.feature', newId), queryUpdateStream)
475
571
 
476
- const testPlan = makeTestPlan(gherkinQuery, supportCode, makeTestCase)
572
+ const testPlan = makeTestPlan(gherkinQuery, supportCode, runOptions, makeTestCase)
477
573
  await testPlan.execute((envelope: messages.Envelope) => {
478
574
  messagesHandler(envelope)
479
575
  cucumberQuery.update(envelope)