Skip to content

Commit 1d2d33c

Browse files
authored
Hide successful hooks by default (#415)
1 parent ba48b22 commit 1d2d33c

File tree

6 files changed

+148
-12
lines changed

6 files changed

+148
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
9+
### Changed
10+
- Hide successful hooks by default ([#415](https://github.com/cucumber/react-components/pull/415))
911

1012
## [24.0.1] - 2025-11-16
1113
### Fixed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"react-dom": "~18"
5858
},
5959
"devDependencies": {
60-
"@cucumber/compatibility-kit": "^23.0.0",
60+
"@cucumber/compatibility-kit": "^25.0.0",
6161
"@cucumber/fake-cucumber": "^18.1.0",
6262
"@cucumber/gherkin": "^35.1.0",
6363
"@cucumber/gherkin-streams": "^6.0.0",

src/components/results/TestCaseOutcome.module.scss

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@use '../../styles/theming';
2+
13
.container {
24
> * + * {
35
margin-top: 0.25rem !important;
@@ -13,3 +15,14 @@
1315
margin-top: 0.25rem !important;
1416
}
1517
}
18+
19+
.expandButton {
20+
background-color: transparent;
21+
color: theming.$exampleNumberColor;
22+
font-size: inherit;
23+
padding: 0;
24+
border: none;
25+
cursor: pointer;
26+
display: flex;
27+
gap: 0.25rem;
28+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { Query } from '@cucumber/query'
2+
import { userEvent } from '@testing-library/user-event'
3+
import { expect } from 'chai'
4+
import React from 'react'
5+
6+
import hooksSample from '../../../acceptance/hooks/hooks.js'
7+
import hooksConditionalSample from '../../../acceptance/hooks-conditional/hooks-conditional.js'
8+
import hooksSkippedSample from '../../../acceptance/hooks-skipped/hooks-skipped.js'
9+
import { render } from '../../../test-utils/index.js'
10+
import { EnvelopesProvider } from '../app/index.js'
11+
import { TestCaseOutcome } from './TestCaseOutcome.js'
12+
13+
describe('TestCaseOutcome', () => {
14+
it('should hide successful hooks by default, then show them on request', async () => {
15+
const cucumberQuery = new Query()
16+
hooksSample.forEach((envelope) => cucumberQuery.update(envelope))
17+
const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted()
18+
19+
const { getByRole, getAllByRole, getByText, queryByRole, queryByText } = render(
20+
<EnvelopesProvider envelopes={hooksSample}>
21+
<TestCaseOutcome testCaseStarted={testCaseStarted} />
22+
</EnvelopesProvider>
23+
)
24+
25+
expect(getAllByRole('listitem')).to.have.lengthOf(1)
26+
expect(queryByText('Before')).not.to.exist
27+
expect(getByText('a step passes')).to.be.visible
28+
expect(queryByText('After')).not.to.exist
29+
expect(getByRole('button', { name: '2 hooks' })).to.be.visible
30+
31+
await userEvent.click(getByRole('button', { name: '2 hooks' }))
32+
33+
expect(getAllByRole('listitem')).to.have.lengthOf(3)
34+
expect(getByText('Before')).to.be.visible
35+
expect(getByText('a step passes')).to.be.visible
36+
expect(getByText('After')).to.be.visible
37+
expect(queryByRole('button', { name: /hooks/ })).not.to.exist
38+
})
39+
40+
it('should always show failed hooks', () => {
41+
const cucumberQuery = new Query()
42+
hooksConditionalSample.forEach((envelope) => cucumberQuery.update(envelope))
43+
const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted()
44+
45+
const { getAllByRole, getByText, queryByRole } = render(
46+
<EnvelopesProvider envelopes={hooksConditionalSample}>
47+
<TestCaseOutcome testCaseStarted={testCaseStarted} />
48+
</EnvelopesProvider>
49+
)
50+
51+
expect(getAllByRole('listitem')).to.have.lengthOf(2)
52+
expect(getByText('Before')).to.be.visible
53+
expect(getByText('a step passes')).to.be.visible
54+
expect(queryByRole('button', { name: /hooks/ })).not.to.exist
55+
})
56+
57+
it('should hide skipped hooks by default when they are not the skipper', () => {
58+
const cucumberQuery = new Query()
59+
hooksSkippedSample.forEach((envelope) => cucumberQuery.update(envelope))
60+
const [skipFromStep] = cucumberQuery.findAllTestCaseStarted()
61+
62+
const { getAllByRole, getByRole, getByText, queryByText } = render(
63+
<EnvelopesProvider envelopes={hooksSkippedSample}>
64+
<TestCaseOutcome testCaseStarted={skipFromStep} />
65+
</EnvelopesProvider>
66+
)
67+
68+
expect(getAllByRole('listitem')).to.have.lengthOf(1)
69+
expect(queryByText('Before')).not.to.exist
70+
expect(getByText('a step that skips')).to.be.visible
71+
expect(queryByText('After')).not.to.exist
72+
expect(getByRole('button', { name: '4 hooks' })).to.be.visible
73+
})
74+
75+
it('should show skipped hooks by default when they are the skipper', () => {
76+
const cucumberQuery = new Query()
77+
hooksSkippedSample.forEach((envelope) => cucumberQuery.update(envelope))
78+
const [, skipFromBefore] = cucumberQuery.findAllTestCaseStarted()
79+
80+
const { getAllByRole, getAllByText, getByRole, getByText } = render(
81+
<EnvelopesProvider envelopes={hooksSkippedSample}>
82+
<TestCaseOutcome testCaseStarted={skipFromBefore} />
83+
</EnvelopesProvider>
84+
)
85+
86+
expect(getAllByRole('listitem')).to.have.lengthOf(2)
87+
expect(getAllByText('Before')).to.have.lengthOf(1)
88+
expect(getByText('a normal step')).to.be.visible
89+
expect(getByRole('button', { name: '4 hooks' })).to.be.visible
90+
})
91+
})

src/components/results/TestCaseOutcome.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import { TestCaseStarted } from '@cucumber/messages'
2-
import React, { FC } from 'react'
1+
import {
2+
TestCaseStarted,
3+
TestStep,
4+
TestStepFinished,
5+
TestStepResultStatus,
6+
} from '@cucumber/messages'
7+
import { faCirclePlus } from '@fortawesome/free-solid-svg-icons'
8+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
9+
import React, { FC, useState } from 'react'
310

411
import { useQueries } from '../../hooks/index.js'
512
import styles from './TestCaseOutcome.module.scss'
@@ -10,12 +17,15 @@ interface Props {
1017
}
1118

1219
export const TestCaseOutcome: FC<Props> = ({ testCaseStarted }) => {
20+
const [showAllSteps, setShowAllSteps] = useState(false)
1321
const { cucumberQuery } = useQueries()
14-
const steps = cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted)
22+
const allSteps = cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted)
23+
const filteredSteps = showAllSteps ? allSteps : filterSteps(allSteps)
24+
const hiddenSteps = allSteps.length - filteredSteps.length
1525
return (
1626
<article className={styles.container}>
1727
<ol className={styles.steps}>
18-
{steps.map(([testStepFinished, testStep]) => {
28+
{filteredSteps.map(([testStepFinished, testStep]) => {
1929
return (
2030
<TestStepOutcome
2131
key={testStep.id}
@@ -25,6 +35,26 @@ export const TestCaseOutcome: FC<Props> = ({ testCaseStarted }) => {
2535
)
2636
})}
2737
</ol>
38+
{hiddenSteps > 0 && (
39+
<button className={styles.expandButton} onClick={() => setShowAllSteps(true)}>
40+
<FontAwesomeIcon aria-hidden="true" icon={faCirclePlus} />
41+
{hiddenSteps} hooks
42+
</button>
43+
)}
2844
</article>
2945
)
3046
}
47+
48+
function filterSteps(allSteps: ReadonlyArray<[TestStepFinished, TestStep]>) {
49+
const statuses = allSteps.map(([testStepFinished]) => testStepFinished.testStepResult.status)
50+
return allSteps.filter(([testStepFinished, testStep], index) => {
51+
if (!testStep.hookId) {
52+
return true
53+
}
54+
if (testStepFinished.testStepResult.status === TestStepResultStatus.SKIPPED) {
55+
const previousStatus = statuses[index - 1] ?? TestStepResultStatus.PASSED
56+
return previousStatus === TestStepResultStatus.PASSED
57+
}
58+
return testStepFinished.testStepResult.status !== TestStepResultStatus.PASSED
59+
})
60+
}

0 commit comments

Comments
 (0)