@cucumber/query 12.1.2 → 13.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/dist/src/Lineage.d.ts +30 -0
- package/dist/src/Lineage.d.ts.map +1 -0
- package/dist/src/Lineage.js +78 -0
- package/dist/src/Lineage.js.map +1 -0
- package/dist/src/Query.d.ts +46 -2
- package/dist/src/Query.d.ts.map +1 -1
- package/dist/src/Query.js +258 -32
- package/dist/src/Query.js.map +1 -1
- package/dist/src/helpers.d.ts +9 -0
- package/dist/src/helpers.d.ts.map +1 -0
- package/dist/src/helpers.js +39 -0
- package/dist/src/helpers.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +30 -24
- package/src/Lineage.ts +110 -0
- package/{test/QueryTest.ts → src/Query.spec.ts} +1 -1
- package/src/Query.ts +348 -42
- package/src/acceptance.spec.ts +164 -0
- package/src/helpers.spec.ts +15 -0
- package/src/helpers.ts +41 -0
- package/tsconfig.build.json +6 -1
- package/LICENSE +0 -21
- package/dist/package.json +0 -71
- package/dist/test/QueryTest.d.ts +0 -2
- package/dist/test/QueryTest.d.ts.map +0 -1
- package/dist/test/QueryTest.js +0 -447
- package/dist/test/QueryTest.js.map +0 -1
package/src/Query.ts
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import * as messages from '@cucumber/messages'
|
|
2
|
-
import {
|
|
3
|
-
|
|
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.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
194
|
+
(applies to legacy pickle-oriented query methods only)
|
|
50
195
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
205
|
+
private updateTestStepFinished(testStepFinished: TestStepFinished) {
|
|
206
|
+
this.testStepFinishedByTestCaseStartedId.put(
|
|
207
|
+
testStepFinished.testCaseStartedId,
|
|
208
|
+
testStepFinished
|
|
209
|
+
)
|
|
64
210
|
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
+
}
|