From 6662b58fc4c07855d78d95429424984f9cfb2ec4 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 15 Nov 2025 10:34:22 +0000 Subject: [PATCH 1/9] filter out successful hook steps --- .../gherkin/GherkinDocument.stories.tsx | 6 +++ .../results/TestCaseOutcome.spec.tsx | 44 +++++++++++++++++++ src/components/results/TestCaseOutcome.tsx | 10 ++++- 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/components/results/TestCaseOutcome.spec.tsx diff --git a/src/components/gherkin/GherkinDocument.stories.tsx b/src/components/gherkin/GherkinDocument.stories.tsx index 3f28236d..7f463bc1 100644 --- a/src/components/gherkin/GherkinDocument.stories.tsx +++ b/src/components/gherkin/GherkinDocument.stories.tsx @@ -8,6 +8,7 @@ import dataTables from '../../../acceptance/data-tables/data-tables.js' import examplesTables from '../../../acceptance/examples-tables/examples-tables.js' import hooks from '../../../acceptance/hooks/hooks.js' import hooksAttachments from '../../../acceptance/hooks-attachment/hooks-attachment.js' +import hooksConditional from '../../../acceptance/hooks-conditional/hooks-conditional.js' import markdown from '../../../acceptance/markdown/markdown.js' import minimal from '../../../acceptance/minimal/minimal.js' import parameterTypes from '../../../acceptance/parameter-types/parameter-types.js' @@ -67,6 +68,11 @@ HooksWithAttachments.args = { envelopes: hooksAttachments, } +export const HooksConditional = Template.bind({}) +HooksConditional.args = { + envelopes: hooksConditional, +} + export const Markdown = Template.bind({}) Markdown.args = { envelopes: markdown, diff --git a/src/components/results/TestCaseOutcome.spec.tsx b/src/components/results/TestCaseOutcome.spec.tsx new file mode 100644 index 00000000..43f7ce07 --- /dev/null +++ b/src/components/results/TestCaseOutcome.spec.tsx @@ -0,0 +1,44 @@ +import { Query } from '@cucumber/query' +import { expect } from 'chai' +import React from 'react' + +import hooksSample from '../../../acceptance/hooks/hooks.js' +import hooksConditionalSample from '../../../acceptance/hooks-conditional/hooks-conditional.js' +import { render } from '../../../test-utils/index.js' +import { EnvelopesProvider } from '../app/index.js' +import { TestCaseOutcome } from './TestCaseOutcome.js' + +describe('TestCaseOutcome', () => { + it('should hide successful hooks by default', () => { + const cucumberQuery = new Query() + hooksSample.forEach((envelope) => cucumberQuery.update(envelope)) + const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() + + const { getAllByRole, getByText, queryByText } = render( + + + + ) + + expect(getAllByRole('listitem')).to.have.lengthOf(1) + expect(queryByText('Before')).not.to.exist + expect(getByText('a step passes')).to.be.visible + expect(queryByText('After')).not.to.exist + }) + + it('should always show non-successful hooks', () => { + const cucumberQuery = new Query() + hooksConditionalSample.forEach((envelope) => cucumberQuery.update(envelope)) + const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() + + const { getAllByRole, getByText } = render( + + + + ) + + expect(getAllByRole('listitem')).to.have.lengthOf(2) + expect(getByText('Before')).to.be.visible + expect(getByText('a step passes')).to.be.visible + }) +}) diff --git a/src/components/results/TestCaseOutcome.tsx b/src/components/results/TestCaseOutcome.tsx index bfacc347..4829d11f 100644 --- a/src/components/results/TestCaseOutcome.tsx +++ b/src/components/results/TestCaseOutcome.tsx @@ -1,4 +1,4 @@ -import { TestCaseStarted } from '@cucumber/messages' +import { TestCaseStarted, TestStepResultStatus } from '@cucumber/messages' import React, { FC } from 'react' import { useQueries } from '../../hooks/index.js' @@ -12,10 +12,16 @@ interface Props { export const TestCaseOutcome: FC = ({ testCaseStarted }) => { const { cucumberQuery } = useQueries() const steps = cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted) + const filtered = steps.filter(([testStepFinished, testStep]) => { + return ( + testStep.pickleStepId || + testStepFinished.testStepResult.status !== TestStepResultStatus.PASSED + ) + }) return (
    - {steps.map(([testStepFinished, testStep]) => { + {filtered.map(([testStepFinished, testStep]) => { return ( Date: Sat, 15 Nov 2025 10:45:25 +0000 Subject: [PATCH 2/9] show hidden steps on request --- src/components/results/TestCaseOutcome.spec.tsx | 14 ++++++++++++-- src/components/results/TestCaseOutcome.tsx | 8 +++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/results/TestCaseOutcome.spec.tsx b/src/components/results/TestCaseOutcome.spec.tsx index 43f7ce07..4c160e3c 100644 --- a/src/components/results/TestCaseOutcome.spec.tsx +++ b/src/components/results/TestCaseOutcome.spec.tsx @@ -1,4 +1,5 @@ import { Query } from '@cucumber/query' +import { userEvent } from '@testing-library/user-event' import { expect } from 'chai' import React from 'react' @@ -9,12 +10,12 @@ import { EnvelopesProvider } from '../app/index.js' import { TestCaseOutcome } from './TestCaseOutcome.js' describe('TestCaseOutcome', () => { - it('should hide successful hooks by default', () => { + it('should hide successful hooks by default, then show them on request', async () => { const cucumberQuery = new Query() hooksSample.forEach((envelope) => cucumberQuery.update(envelope)) const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() - const { getAllByRole, getByText, queryByText } = render( + const { getByRole, getAllByRole, getByText, queryByRole, queryByText } = render( @@ -24,6 +25,15 @@ describe('TestCaseOutcome', () => { expect(queryByText('Before')).not.to.exist expect(getByText('a step passes')).to.be.visible expect(queryByText('After')).not.to.exist + expect(getByRole('button', { name: '+2 hooks' })).to.be.visible + + await userEvent.click(getByRole('button', { name: '+2 hooks' })) + + expect(getAllByRole('listitem')).to.have.lengthOf(3) + expect(getByText('Before')).to.be.visible + expect(getByText('a step passes')).to.be.visible + expect(getByText('After')).to.be.visible + expect(queryByRole('button', { name: '+2 hooks' })).not.to.exist }) it('should always show non-successful hooks', () => { diff --git a/src/components/results/TestCaseOutcome.tsx b/src/components/results/TestCaseOutcome.tsx index 4829d11f..572fcbfa 100644 --- a/src/components/results/TestCaseOutcome.tsx +++ b/src/components/results/TestCaseOutcome.tsx @@ -1,5 +1,5 @@ import { TestCaseStarted, TestStepResultStatus } from '@cucumber/messages' -import React, { FC } from 'react' +import React, { FC, useState } from 'react' import { useQueries } from '../../hooks/index.js' import styles from './TestCaseOutcome.module.scss' @@ -10,14 +10,19 @@ interface Props { } export const TestCaseOutcome: FC = ({ testCaseStarted }) => { + const [showAllSteps, setShowAllSteps] = useState(false) const { cucumberQuery } = useQueries() const steps = cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted) const filtered = steps.filter(([testStepFinished, testStep]) => { + if (showAllSteps) { + return true + } return ( testStep.pickleStepId || testStepFinished.testStepResult.status !== TestStepResultStatus.PASSED ) }) + const hiddenSteps = steps.length - filtered.length return (
      @@ -31,6 +36,7 @@ export const TestCaseOutcome: FC = ({ testCaseStarted }) => { ) })}
    + {!showAllSteps && }
    ) } From abb32c9ae4bbbca27a86d5d6ff6fee35697f4387 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 15 Nov 2025 10:54:10 +0000 Subject: [PATCH 3/9] dont show button if nothing hidden --- src/components/results/TestCaseOutcome.spec.tsx | 3 ++- src/components/results/TestCaseOutcome.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/results/TestCaseOutcome.spec.tsx b/src/components/results/TestCaseOutcome.spec.tsx index 4c160e3c..56a88400 100644 --- a/src/components/results/TestCaseOutcome.spec.tsx +++ b/src/components/results/TestCaseOutcome.spec.tsx @@ -41,7 +41,7 @@ describe('TestCaseOutcome', () => { hooksConditionalSample.forEach((envelope) => cucumberQuery.update(envelope)) const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() - const { getAllByRole, getByText } = render( + const { getAllByRole, getByText, queryByRole } = render( @@ -50,5 +50,6 @@ describe('TestCaseOutcome', () => { expect(getAllByRole('listitem')).to.have.lengthOf(2) expect(getByText('Before')).to.be.visible expect(getByText('a step passes')).to.be.visible + expect(queryByRole('button', { name: /hooks/ })).not.to.exist }) }) diff --git a/src/components/results/TestCaseOutcome.tsx b/src/components/results/TestCaseOutcome.tsx index 572fcbfa..64673b3f 100644 --- a/src/components/results/TestCaseOutcome.tsx +++ b/src/components/results/TestCaseOutcome.tsx @@ -36,7 +36,7 @@ export const TestCaseOutcome: FC = ({ testCaseStarted }) => { ) })}
- {!showAllSteps && } + {hiddenSteps > 0 && }
) } From f9453475d10fc0c20da10f38a88371fbbc616e5a Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 15 Nov 2025 11:29:54 +0000 Subject: [PATCH 4/9] apply styling --- src/components/results/TestCaseOutcome.module.scss | 13 +++++++++++++ src/components/results/TestCaseOutcome.spec.tsx | 6 +++--- src/components/results/TestCaseOutcome.tsx | 9 ++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/results/TestCaseOutcome.module.scss b/src/components/results/TestCaseOutcome.module.scss index d0a59e13..47ee5b75 100644 --- a/src/components/results/TestCaseOutcome.module.scss +++ b/src/components/results/TestCaseOutcome.module.scss @@ -1,3 +1,5 @@ +@use '../../styles/theming'; + .container { > * + * { margin-top: 0.25rem !important; @@ -13,3 +15,14 @@ margin-top: 0.25rem !important; } } + +.expandButton { + background-color: transparent; + color: theming.$exampleNumberColor; + font-size: inherit; + padding: 0; + border: none; + cursor: pointer; + display: flex; + gap: 0.25rem; +} diff --git a/src/components/results/TestCaseOutcome.spec.tsx b/src/components/results/TestCaseOutcome.spec.tsx index 56a88400..626546dd 100644 --- a/src/components/results/TestCaseOutcome.spec.tsx +++ b/src/components/results/TestCaseOutcome.spec.tsx @@ -25,15 +25,15 @@ describe('TestCaseOutcome', () => { expect(queryByText('Before')).not.to.exist expect(getByText('a step passes')).to.be.visible expect(queryByText('After')).not.to.exist - expect(getByRole('button', { name: '+2 hooks' })).to.be.visible + expect(getByRole('button', { name: '2 hooks' })).to.be.visible - await userEvent.click(getByRole('button', { name: '+2 hooks' })) + await userEvent.click(getByRole('button', { name: '2 hooks' })) expect(getAllByRole('listitem')).to.have.lengthOf(3) expect(getByText('Before')).to.be.visible expect(getByText('a step passes')).to.be.visible expect(getByText('After')).to.be.visible - expect(queryByRole('button', { name: '+2 hooks' })).not.to.exist + expect(queryByRole('button', { name: /hooks/ })).not.to.exist }) it('should always show non-successful hooks', () => { diff --git a/src/components/results/TestCaseOutcome.tsx b/src/components/results/TestCaseOutcome.tsx index 64673b3f..bff38f4a 100644 --- a/src/components/results/TestCaseOutcome.tsx +++ b/src/components/results/TestCaseOutcome.tsx @@ -1,4 +1,6 @@ import { TestCaseStarted, TestStepResultStatus } from '@cucumber/messages' +import { faCirclePlus } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import React, { FC, useState } from 'react' import { useQueries } from '../../hooks/index.js' @@ -36,7 +38,12 @@ export const TestCaseOutcome: FC = ({ testCaseStarted }) => { ) })} - {hiddenSteps > 0 && } + {hiddenSteps > 0 && ( + + )} ) } From eece257d232c44af1faca74b199bec53b1ac948a Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 15 Nov 2025 11:46:56 +0000 Subject: [PATCH 5/9] update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a54b6448..eca47137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed +- Hide successful hooks by default ([#415](https://github.com/cucumber/react-components/pull/415)) ## [24.0.0] - 2025-11-11 ### Added From 88d81a06e6a5519ba885ec1c81367ea7da7a0536 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 17 Nov 2025 19:33:17 +0000 Subject: [PATCH 6/9] bump cck --- package-lock.json | 47 ++++++++++++++++++++++++++--------------------- package.json | 2 +- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 44edb1fd..20088490 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "uuid": "9.0.0" }, "devDependencies": { - "@cucumber/compatibility-kit": "^23.0.0", + "@cucumber/compatibility-kit": "^25.0.0", "@cucumber/fake-cucumber": "^18.1.0", "@cucumber/gherkin": "^35.1.0", "@cucumber/gherkin-streams": "^6.0.0", @@ -138,6 +138,7 @@ "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -495,9 +496,9 @@ "license": "MIT" }, "node_modules/@cucumber/compatibility-kit": { - "version": "23.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-23.0.0.tgz", - "integrity": "sha512-+XapEOpxPm2c5iOD+eiWgRQLAN6MMal22dOeicm6Z4Vewp1XpEoPaS/G5l0ODMcIxhKi9GrUlyCsQ1Hd84M45w==", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-25.0.0.tgz", + "integrity": "sha512-DmB6oOWVh+0L7VRa4kq/xHIccrBezdibSHt7RAVdVX/hv1grf5+yzt44a+JTliPg5IKVEqxusmQ556sB8Tlabg==", "dev": true, "license": "MIT" }, @@ -666,6 +667,7 @@ "integrity": "sha512-JtQZLlhclejUb1lmnztMBin2Qu5lvSuet9dCl/nizrP8Icr8ywZk6PaDfcFXwS5iNQAKV+wH9FLsPxhhxME0sQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cucumber/messages": ">=19.1.4 <30" } @@ -676,6 +678,7 @@ "integrity": "sha512-HLSHMmdDH0vCr7vsVEURcDA4WwnRLdjkhqr6a4HQ3i4RFK1wiDGPjBGVdGJLyuXuRdJpJbFc6QxHvT8pU4t6jw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "commander": "14.0.0", "source-map-support": "0.5.21" @@ -758,6 +761,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", "dev": true, + "peer": true, "peerDependencies": { "@cucumber/messages": ">=17.1.1" } @@ -767,6 +771,7 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-29.0.1.tgz", "integrity": "sha512-aAvIYfQD6/aBdF8KFQChC3CQ1Q+GX9orlR6GurGiX6oqaCnBkxA4WU3OQUVepDynEFrPayerqKRFcAMhdcXReQ==", "license": "MIT", + "peer": true, "dependencies": { "class-transformer": "0.5.1", "reflect-metadata": "0.2.2" @@ -1411,6 +1416,7 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz", "integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==", "hasInstallScript": true, + "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "6.2.1" }, @@ -4213,7 +4219,8 @@ "version": "18.11.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -4237,6 +4244,7 @@ "version": "18.0.26", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4349,6 +4357,7 @@ "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/types": "8.24.1", @@ -4599,6 +4608,7 @@ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5188,6 +5198,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001716", "electron-to-chromium": "^1.5.149", @@ -5403,6 +5414,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, + "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -6860,6 +6872,7 @@ "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -10161,6 +10174,7 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, + "peer": true, "dependencies": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -14692,6 +14706,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -14712,6 +14727,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -16390,6 +16406,7 @@ "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -17191,6 +17208,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -17541,6 +17559,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17841,6 +17860,7 @@ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -17951,22 +17971,6 @@ } } }, - "node_modules/vite-tsconfig-paths/node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/vite/node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -17988,6 +17992,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/package.json b/package.json index 1e4312ab..07d5678b 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "react-dom": "~18" }, "devDependencies": { - "@cucumber/compatibility-kit": "^23.0.0", + "@cucumber/compatibility-kit": "^25.0.0", "@cucumber/fake-cucumber": "^18.1.0", "@cucumber/gherkin": "^35.1.0", "@cucumber/gherkin-streams": "^6.0.0", From eae390507606a89d77894aaa9bb5f7c8dfaaf291 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 17 Nov 2025 19:49:19 +0000 Subject: [PATCH 7/9] handle skipped better --- .../results/TestCaseOutcome.spec.tsx | 38 ++++++++++++++++++- src/components/results/TestCaseOutcome.tsx | 37 +++++++++++------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/components/results/TestCaseOutcome.spec.tsx b/src/components/results/TestCaseOutcome.spec.tsx index 626546dd..7de65d61 100644 --- a/src/components/results/TestCaseOutcome.spec.tsx +++ b/src/components/results/TestCaseOutcome.spec.tsx @@ -5,6 +5,7 @@ import React from 'react' import hooksSample from '../../../acceptance/hooks/hooks.js' import hooksConditionalSample from '../../../acceptance/hooks-conditional/hooks-conditional.js' +import hooksSkippedSample from '../../../acceptance/hooks-skipped/hooks-skipped.js' import { render } from '../../../test-utils/index.js' import { EnvelopesProvider } from '../app/index.js' import { TestCaseOutcome } from './TestCaseOutcome.js' @@ -36,7 +37,7 @@ describe('TestCaseOutcome', () => { expect(queryByRole('button', { name: /hooks/ })).not.to.exist }) - it('should always show non-successful hooks', () => { + it('should always show failed hooks', () => { const cucumberQuery = new Query() hooksConditionalSample.forEach((envelope) => cucumberQuery.update(envelope)) const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() @@ -52,4 +53,39 @@ describe('TestCaseOutcome', () => { expect(getByText('a step passes')).to.be.visible expect(queryByRole('button', { name: /hooks/ })).not.to.exist }) + + it('should hide skipped hooks by default when they are not the skipper', () => { + const cucumberQuery = new Query() + hooksSkippedSample.forEach((envelope) => cucumberQuery.update(envelope)) + const [skipFromStep] = cucumberQuery.findAllTestCaseStarted() + + const { getAllByRole, getByRole, getByText, queryByText } = render( + + + + ) + + expect(getAllByRole('listitem')).to.have.lengthOf(1) + expect(queryByText('Before')).not.to.exist + expect(getByText('a step that skips')).to.be.visible + expect(queryByText('After')).not.to.exist + expect(getByRole('button', { name: '4 hooks' })).to.be.visible + }) + + it('should show skipped hooks by default when they are the skipper', () => { + const cucumberQuery = new Query() + hooksSkippedSample.forEach((envelope) => cucumberQuery.update(envelope)) + const [, skipFromBefore] = cucumberQuery.findAllTestCaseStarted() + + const { getAllByRole, getAllByText, getByRole, getByText } = render( + + + + ) + + expect(getAllByRole('listitem')).to.have.lengthOf(2) + expect(getAllByText('Before')).to.have.lengthOf(1) + expect(getByText('a normal step')).to.be.visible + expect(getByRole('button', { name: '4 hooks' })).to.be.visible + }) }) diff --git a/src/components/results/TestCaseOutcome.tsx b/src/components/results/TestCaseOutcome.tsx index bff38f4a..e6fe6568 100644 --- a/src/components/results/TestCaseOutcome.tsx +++ b/src/components/results/TestCaseOutcome.tsx @@ -1,4 +1,9 @@ -import { TestCaseStarted, TestStepResultStatus } from '@cucumber/messages' +import { + TestCaseStarted, + TestStep, + TestStepFinished, + TestStepResultStatus, +} from '@cucumber/messages' import { faCirclePlus } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import React, { FC, useState } from 'react' @@ -14,21 +19,13 @@ interface Props { export const TestCaseOutcome: FC = ({ testCaseStarted }) => { const [showAllSteps, setShowAllSteps] = useState(false) const { cucumberQuery } = useQueries() - const steps = cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted) - const filtered = steps.filter(([testStepFinished, testStep]) => { - if (showAllSteps) { - return true - } - return ( - testStep.pickleStepId || - testStepFinished.testStepResult.status !== TestStepResultStatus.PASSED - ) - }) - const hiddenSteps = steps.length - filtered.length + const allSteps = cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted) + const filteredSteps = showAllSteps ? allSteps : filterSteps(allSteps) + const hiddenSteps = allSteps.length - filteredSteps.length return (
    - {filtered.map(([testStepFinished, testStep]) => { + {filteredSteps.map(([testStepFinished, testStep]) => { return ( = ({ testCaseStarted }) => {
) } + +function filterSteps(allSteps: ReadonlyArray<[TestStepFinished, TestStep]>) { + const statuses = allSteps.map(([testStepFinished]) => testStepFinished.testStepResult.status) + return allSteps.filter(([testStepFinished, testStep], index) => { + if (testStep.pickleStepId) { + return true + } + if (testStepFinished.testStepResult.status === TestStepResultStatus.SKIPPED) { + const previousStatus = statuses[index - 1] ?? TestStepResultStatus.PASSED + return previousStatus === TestStepResultStatus.PASSED + } + return testStepFinished.testStepResult.status !== TestStepResultStatus.PASSED + }) +} From d01a31566484dd95a9308f942585107529c15294 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 17 Nov 2025 21:00:07 +0000 Subject: [PATCH 8/9] 22 compatible lockfile --- package-lock.json | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 20088490..856f066b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -138,7 +138,6 @@ "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -667,7 +666,6 @@ "integrity": "sha512-JtQZLlhclejUb1lmnztMBin2Qu5lvSuet9dCl/nizrP8Icr8ywZk6PaDfcFXwS5iNQAKV+wH9FLsPxhhxME0sQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cucumber/messages": ">=19.1.4 <30" } @@ -678,7 +676,6 @@ "integrity": "sha512-HLSHMmdDH0vCr7vsVEURcDA4WwnRLdjkhqr6a4HQ3i4RFK1wiDGPjBGVdGJLyuXuRdJpJbFc6QxHvT8pU4t6jw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "commander": "14.0.0", "source-map-support": "0.5.21" @@ -761,7 +758,6 @@ "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", "integrity": "sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA==", "dev": true, - "peer": true, "peerDependencies": { "@cucumber/messages": ">=17.1.1" } @@ -771,7 +767,6 @@ "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-29.0.1.tgz", "integrity": "sha512-aAvIYfQD6/aBdF8KFQChC3CQ1Q+GX9orlR6GurGiX6oqaCnBkxA4WU3OQUVepDynEFrPayerqKRFcAMhdcXReQ==", "license": "MIT", - "peer": true, "dependencies": { "class-transformer": "0.5.1", "reflect-metadata": "0.2.2" @@ -1416,7 +1411,6 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz", "integrity": "sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA==", "hasInstallScript": true, - "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "6.2.1" }, @@ -4219,8 +4213,7 @@ "version": "18.11.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -4244,7 +4237,6 @@ "version": "18.0.26", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", - "peer": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4357,7 +4349,6 @@ "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/types": "8.24.1", @@ -4608,7 +4599,6 @@ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5198,7 +5188,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001716", "electron-to-chromium": "^1.5.149", @@ -5414,7 +5403,6 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, - "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -6872,7 +6860,6 @@ "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -10174,7 +10161,6 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, - "peer": true, "dependencies": { "abab": "^2.0.6", "cssstyle": "^3.0.0", @@ -14706,7 +14692,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -14727,7 +14712,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -16406,7 +16390,6 @@ "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -17208,7 +17191,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -17559,7 +17541,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17860,7 +17841,6 @@ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -17971,6 +17951,22 @@ } } }, + "node_modules/vite-tsconfig-paths/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/vite/node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -17992,7 +17988,6 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, From d47e4af3b803412e0cc53d02194faf72f0bd11f1 Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 18 Nov 2025 08:12:55 +0000 Subject: [PATCH 9/9] test for hook id --- src/components/results/TestCaseOutcome.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/results/TestCaseOutcome.tsx b/src/components/results/TestCaseOutcome.tsx index e6fe6568..0ab99d66 100644 --- a/src/components/results/TestCaseOutcome.tsx +++ b/src/components/results/TestCaseOutcome.tsx @@ -48,7 +48,7 @@ export const TestCaseOutcome: FC = ({ testCaseStarted }) => { function filterSteps(allSteps: ReadonlyArray<[TestStepFinished, TestStep]>) { const statuses = allSteps.map(([testStepFinished]) => testStepFinished.testStepResult.status) return allSteps.filter(([testStepFinished, testStep], index) => { - if (testStep.pickleStepId) { + if (!testStep.hookId) { return true } if (testStepFinished.testStepResult.status === TestStepResultStatus.SKIPPED) {