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
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..878e6657 100644
--- a/src/components/results/TestStepOutcome.spec.tsx
+++ b/src/components/results/TestStepOutcome.spec.tsx
@@ -4,6 +4,7 @@ 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,47 @@ 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'],
+ ])
+ })
})
diff --git a/src/components/results/TestStepOutcome.tsx b/src/components/results/TestStepOutcome.tsx
index a0069607..a8b64459 100644
--- a/src/components/results/TestStepOutcome.tsx
+++ b/src/components/results/TestStepOutcome.tsx
@@ -98,11 +98,22 @@ 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
- }
- if (pickleStep.argument?.dataTable) {
- return
- }
- return null
+ const { dataTable, docString } = pickleStep.argument ?? {}
+ const stepArguments = [dataTable, docString]
+ .filter((stepArgument) => stepArgument !== undefined)
+ .sort(
+ (a, b) =>
+ (a.argumentIndex ?? Number.MAX_SAFE_INTEGER) - (b.argumentIndex ?? Number.MAX_SAFE_INTEGER)
+ )
+ return (
+ <>
+ {stepArguments.map((stepArgument, index) =>
+ 'content' in stepArgument ? (
+
+ ) : (
+
+ )
+ )}
+ >
+ )
}