From 74e6a1b3a70944f328e1cd01b995539c1c422fda Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 27 Sep 2026 12:19:35 +0100 Subject: [PATCH 1/3] Render both data table and doc string in source order --- package-lock.json | 24 ++++- package.json | 2 +- .../results/TestStepOutcome.spec.tsx | 95 ++++++++++++++++++- src/components/results/TestStepOutcome.tsx | 25 ++++- 4 files changed, 136 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 997fcbc1..0cd52b0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "devDependencies": { "@biomejs/biome": "^2.4.1", "@cucumber/biome-config": "cucumber/biome-config#v0.6.0", - "@cucumber/compatibility-kit": "^30.0.0", + "@cucumber/compatibility-kit": "^31.0.0", "@cucumber/gherkin": "^42.0.0", "@cucumber/message-streams": "^5.0.1", "@ladle/react": "^5.0.3", @@ -797,9 +797,9 @@ "license": "MIT" }, "node_modules/@cucumber/compatibility-kit": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-30.0.0.tgz", - "integrity": "sha512-R01Sfp3aPx1CWT9x0hUFMJ3xrAqMpSqQDbzVq3kD5fWBRkkBYmlLhM4p4BukT5MGn6BwEsf9saKT4vD7gsKATg==", + "version": "31.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/compatibility-kit/-/compatibility-kit-31.0.0.tgz", + "integrity": "sha512-0UGPMXQiZfy8cNEdcHnu9/Ov+VmjgoW0hwxbnOv8PEjIGxvvSGAwtnzdkMlFS0u0cdU8BfvAzRRervtlbm0GPA==", "dev": true, "license": "MIT" }, @@ -11815,6 +11815,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", diff --git a/package.json b/package.json index 9843f56e..07f0009d 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "devDependencies": { "@biomejs/biome": "^2.4.1", "@cucumber/biome-config": "cucumber/biome-config#v0.6.0", - "@cucumber/compatibility-kit": "^30.0.0", + "@cucumber/compatibility-kit": "^31.0.0", "@cucumber/gherkin": "^42.0.0", "@cucumber/message-streams": "^5.0.1", "@ladle/react": "^5.0.3", diff --git a/src/components/results/TestStepOutcome.spec.tsx b/src/components/results/TestStepOutcome.spec.tsx index d365f1f0..3da7f796 100644 --- a/src/components/results/TestStepOutcome.spec.tsx +++ b/src/components/results/TestStepOutcome.spec.tsx @@ -1,9 +1,10 @@ -import type { GherkinDocument } from '@cucumber/messages' +import type { Envelope, GherkinDocument } from '@cucumber/messages' import { Query as CucumberQuery } from '@cucumber/query' import { render } from '@testing-library/react' import { expect } from 'chai' import ambiguousSample from '../../../acceptance/ambiguous/ambiguous.js' +import dataTablesDocStringsSample from '../../../acceptance/data-tables-doc-strings/data-tables-doc-strings.js' import minimalSample from '../../../acceptance/minimal/minimal.js' import pendingExceptionSample from '../../../acceptance/pending-exception/pending-exception.js' import skippedExceptionSample from '../../../acceptance/skipped-exception/skipped-exception.js' @@ -148,4 +149,96 @@ describe('TestStepOutcome', () => { expect(getByText('cukes in my belly')).to.be.visible }) + + it('should show both a data table and a doc string in the order they were declared', () => { + const cucumberQuery = new CucumberQuery() + for (const envelope of dataTablesDocStringsSample) { + cucumberQuery.update(envelope) + } + + const [tableFirstTestCase, docStringFirstTestCase] = cucumberQuery.findAllTestCaseStarted() + + const [[tableFirstFinished, tableFirstStep]] = + cucumberQuery.findTestStepFinishedAndTestStepBy(tableFirstTestCase) + const tableFirst = render( + + + + ) + expect( + Array.from(tableFirst.container.querySelectorAll('table, pre')).map((element) => [ + element.tagName, + element.textContent, + ]) + ).to.deep.eq([ + ['TABLE', 'hello'], + ['PRE', 'world'], + ]) + + const [[docStringFirstFinished, docStringFirstStep]] = + cucumberQuery.findTestStepFinishedAndTestStepBy(docStringFirstTestCase) + const docStringFirst = render( + + + + ) + expect( + Array.from(docStringFirst.container.querySelectorAll('table, pre')).map((element) => [ + element.tagName, + element.textContent, + ]) + ).to.deep.eq([ + ['PRE', 'hello'], + ['TABLE', 'world'], + ]) + }) + + it('should show only the doc string when both are present without argument indexes', () => { + // messages from older producers do not have argument indexes + const envelopes: ReadonlyArray = dataTablesDocStringsSample.map((envelope) => { + if (envelope.pickle) { + return { + pickle: { + ...envelope.pickle, + steps: envelope.pickle.steps.map((step) => ({ + ...step, + argument: { + dataTable: step.argument?.dataTable && { + ...step.argument.dataTable, + argumentIndex: undefined, + }, + docString: step.argument?.docString && { + ...step.argument.docString, + argumentIndex: undefined, + }, + }, + })), + }, + } + } + return envelope + }) + + const cucumberQuery = new CucumberQuery() + for (const envelope of envelopes) { + cucumberQuery.update(envelope) + } + + const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() + const [[testStepFinished, testStep]] = + cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted) + + const { container } = render( + + + + ) + + expect( + Array.from(container.querySelectorAll('table, pre')).map((element) => [ + element.tagName, + element.textContent, + ]) + ).to.deep.eq([['PRE', 'world']]) + }) }) diff --git a/src/components/results/TestStepOutcome.tsx b/src/components/results/TestStepOutcome.tsx index a0069607..18f106a3 100644 --- a/src/components/results/TestStepOutcome.tsx +++ b/src/components/results/TestStepOutcome.tsx @@ -98,11 +98,28 @@ const PickleStepTitle: FC<{ testStep: TestStep }> = ({ testStep }) => { const PickleStepArgument: FC<{ testStep: TestStep }> = ({ testStep }) => { const { cucumberQuery } = useQueries() const pickleStep = cucumberQuery.findPickleStepBy(testStep) as PickleStep - if (pickleStep.argument?.docString) { - return + const { dataTable, docString } = pickleStep.argument ?? {} + // argument indexes are only present from newer producers that allow both + if (dataTable?.argumentIndex !== undefined && docString?.argumentIndex !== undefined) { + const dataTableElement = + const docStringElement = + return dataTable.argumentIndex < docString.argumentIndex ? ( + <> + {dataTableElement} + {docStringElement} + + ) : ( + <> + {docStringElement} + {dataTableElement} + + ) } - if (pickleStep.argument?.dataTable) { - return + if (docString) { + return + } + if (dataTable) { + return } return null } From b4c38d8be3043f50cbcbc680eba6685bab12a5df Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 28 Sep 2026 08:59:45 +0100 Subject: [PATCH 2/3] Simplify rendering of step arguments --- .../results/TestStepOutcome.spec.tsx | 51 +------------------ src/components/results/TestStepOutcome.tsx | 38 ++++++-------- 2 files changed, 17 insertions(+), 72 deletions(-) diff --git a/src/components/results/TestStepOutcome.spec.tsx b/src/components/results/TestStepOutcome.spec.tsx index 3da7f796..878e6657 100644 --- a/src/components/results/TestStepOutcome.spec.tsx +++ b/src/components/results/TestStepOutcome.spec.tsx @@ -1,4 +1,4 @@ -import type { Envelope, GherkinDocument } from '@cucumber/messages' +import type { GherkinDocument } from '@cucumber/messages' import { Query as CucumberQuery } from '@cucumber/query' import { render } from '@testing-library/react' import { expect } from 'chai' @@ -192,53 +192,4 @@ describe('TestStepOutcome', () => { ['TABLE', 'world'], ]) }) - - it('should show only the doc string when both are present without argument indexes', () => { - // messages from older producers do not have argument indexes - const envelopes: ReadonlyArray = dataTablesDocStringsSample.map((envelope) => { - if (envelope.pickle) { - return { - pickle: { - ...envelope.pickle, - steps: envelope.pickle.steps.map((step) => ({ - ...step, - argument: { - dataTable: step.argument?.dataTable && { - ...step.argument.dataTable, - argumentIndex: undefined, - }, - docString: step.argument?.docString && { - ...step.argument.docString, - argumentIndex: undefined, - }, - }, - })), - }, - } - } - return envelope - }) - - const cucumberQuery = new CucumberQuery() - for (const envelope of envelopes) { - cucumberQuery.update(envelope) - } - - const [testCaseStarted] = cucumberQuery.findAllTestCaseStarted() - const [[testStepFinished, testStep]] = - cucumberQuery.findTestStepFinishedAndTestStepBy(testCaseStarted) - - const { container } = render( - - - - ) - - expect( - Array.from(container.querySelectorAll('table, pre')).map((element) => [ - element.tagName, - element.textContent, - ]) - ).to.deep.eq([['PRE', 'world']]) - }) }) diff --git a/src/components/results/TestStepOutcome.tsx b/src/components/results/TestStepOutcome.tsx index 18f106a3..a8b64459 100644 --- a/src/components/results/TestStepOutcome.tsx +++ b/src/components/results/TestStepOutcome.tsx @@ -99,27 +99,21 @@ const PickleStepArgument: FC<{ testStep: TestStep }> = ({ testStep }) => { const { cucumberQuery } = useQueries() const pickleStep = cucumberQuery.findPickleStepBy(testStep) as PickleStep const { dataTable, docString } = pickleStep.argument ?? {} - // argument indexes are only present from newer producers that allow both - if (dataTable?.argumentIndex !== undefined && docString?.argumentIndex !== undefined) { - const dataTableElement = - const docStringElement = - return dataTable.argumentIndex < docString.argumentIndex ? ( - <> - {dataTableElement} - {docStringElement} - - ) : ( - <> - {docStringElement} - {dataTableElement} - + const stepArguments = [dataTable, docString] + .filter((stepArgument) => stepArgument !== undefined) + .sort( + (a, b) => + (a.argumentIndex ?? Number.MAX_SAFE_INTEGER) - (b.argumentIndex ?? Number.MAX_SAFE_INTEGER) ) - } - if (docString) { - return - } - if (dataTable) { - return - } - return null + return ( + <> + {stepArguments.map((stepArgument, index) => + 'content' in stepArgument ? ( + + ) : ( + + ) + )} + + ) } From 51070dcd797cd759682d6f61ac601986db493bb1 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 28 Sep 2026 09:09:37 +0100 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d75604..df09174a 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] +### Added +- Render both data table and doc string in source order ([#570](https://github.com/cucumber/react-components/pull/570)) ## [24.4.0] - 2026-09-02 ### Changed