-
Notifications
You must be signed in to change notification settings - Fork 1
Integration Cucumber Messages
Kronikol can read the Cucumber Messages protocol directly. Any runner that emits it — playwright-bdd
(cucumberReporter('message')), cucumber-js --format message, Cucumber-JVM
--plugin message:target/messages.ndjson — becomes a first-class Kronikol producer: the feature description,
rules, backgrounds, Given/When/Then keywords, data tables, doc strings, scenario outlines with their
example values, step outcomes, exceptions, retries and attachments all reach TestRunReport.html and
Specifications.html without a Kronikol adapter in the test process.
kronikol ingest ./captures \
--tests ./captures/tests.ndjson \
--cucumber-messages ./cucumber/messages.ndjson \
-o ./ReportsThe messages file is not an interaction capture. Keep it outside the directory you pass as an input —
kronikol ingest scans input directories recursively for *.ndjson / *.jsonl and would otherwise try to
replay it as traffic. (If it does end up inside one, naming it on --cucumber-messages removes it from the
capture set; the recommendation stands because the intent is clearer.)
Ingesting External Captures already gives an out-of-process runner a way to
supply scenario names, outcomes, steps and assertions: the tests NDJSON. That format is deliberately small.
A BDD runner knows much more than it can express there — which steps came from a Background:, which Rule:
a scenario belongs to, which row of an Examples: table produced it, what the data table under a step held.
Rather than grow the tests format until it becomes a second Gherkin, Kronikol reads the one the BDD world
already standardised on.
The result is a living document that reads like the feature file, with the captured traffic of each scenario underneath it.
| Cucumber Messages | Kronikol |
|---|---|
GherkinDocument.feature.name / .description
|
Feature.DisplayName / Feature.Description
|
| feature tags |
Feature.Labels (minus the conventions below) |
Rule |
Scenario.Rule |
Background steps |
Scenario.BackgroundSteps — explicit, never guessed |
Pickle.name, scenario description
|
Scenario.DisplayName, Scenario.Description
|
Pickle.astNodeIds → outline + example row |
Scenario.OutlineId, ExampleValues, ExampleRawValues
|
Pickle.tags |
Scenario.Labels / Categories / IsHappyPath / Feature.Endpoint
|
Gherkin step keyword (Given , And , But …) |
ScenarioStep.Keyword (as authored) |
PickleStep.type (Context/Action/Outcome) |
the phase an And inherits — used for phase attribution |
PickleStep.text (placeholders expanded) |
ScenarioStep.Text (+ TextSegments highlighting the substituted values) |
step dataTable
|
a tabular StepParameter named table
|
step docString
|
ScenarioStep.DocString / DocStringMediaType
|
TestStepFinished.testStepResult.status |
ScenarioStep.Status — see the table below |
….duration |
ScenarioStep.Duration |
….exception.message / .stackTrace
|
step Comments + Scenario.ErrorMessage / ErrorStackTrace
|
TestStepStarted.timestamp |
the <<stepDelimiter>> bar in the sequence diagram |
| hook test steps | dropped, or sub-steps named by the hook with --include-hooks
|
Attachment |
Scenario.Attachments / ScenarioStep.Attachments
|
TestCaseStarted.attempt |
retries — last attempt wins, earlier ones leave a retry N label |
TestRunStarted / TestRunFinished
|
the run window in the report header |
Statuses map as PASSED → Passed, SKIPPED/PENDING → Skipped, FAILED/UNDEFINED/AMBIGUOUS → Failed.
A scenario's result is its worst step result.
The same conventions the ReqNRoll integration uses, with the leading @ stripped:
| Tag | Effect |
|---|---|
@happy-path (@happy_path, @happypath) |
Scenario.IsHappyPath — the scenario sorts first and is highlighted |
@category:<name> |
Scenario.Categories |
@endpoint:<path> |
Feature.Endpoint |
| anything else |
Scenario.Labels (scenario's own tags) / Feature.Labels (feature's own tags) |
Categories and the endpoint are read from the pickle's tags, so a @category: on the feature applies to
every scenario in it. Labels and happy-path are read from the node's own tags, so a feature-level tag does
not repeat on every scenario row.
Cucumber ids are not Kronikol test ids, so on their own the messages give you the Gherkin structure but no
traffic under it. The join is one attachment: have a hook attach the 32-hex Kronikol test id (the same value
the fixture stamps on the outgoing requests, and the W3C trace id) under the name kronikol-test-id.
// playwright-bdd — steps/hooks.ts
import { createBdd } from 'playwright-bdd';
const { Before } = createBdd(test);
Before(async ({ $testInfo }) => {
await $testInfo.attach('kronikol-test-id', { body: kronikolTestId, contentType: 'text/plain' });
});The importer uses that value as Scenario.Id, so the captured interactions (testId on the wire), the
reporter's ui/assertion events and the Gherkin structure all converge on one scenario.
Without it the importer mints <pickleId>#<attempt> and records a warning that interactions cannot be joined
— the scenario still renders, it just has no traffic.
| Option | Default | |
|---|---|---|
--cucumber-messages <file> |
— | A Cucumber Messages NDJSON. Repeatable — pass one per shard/worker. |
--include-hooks |
off | Keep hook steps (BeforeEach hook, …) as steps in the report. |
Programmatically:
IngestPipeline.Run(new IngestRequest
{
InteractionFiles = Directory.GetFiles(".logs/taps", "*.ndjson"),
TestsFile = ".logs/taps/tests.jsonl",
CucumberMessagesFiles = [".logs/cucumber/messages.ndjson"],
IncludeHooks = false,
Options = options,
});Reading and synthesising can also be driven directly:
var messages = CucumberMessagesReader.ReadFile("messages.ndjson");
var result = CucumberFeatureSynthesizer.Build(messages); // Feature[], markers, warningsWhen both --tests and --cucumber-messages are given, the messages win for structure:
- for every scenario the messages own, the Gherkin model replaces whatever the tests file produced — feature,
description, rule, background, keywords, tables, doc strings, example values and outcomes. The reporter's own
stepevents for those scenarios are dropped, so the diagram never grows a second set of delimiter bars; - the tests file still contributes what only it knows:
assertionevents (nested under the Gherkin step whose time window contains them — the ✓/✗ rows and the notes in the diagram), UI actions, attachments the reporter recorded, the identity, and a failure message when no Gherkin step failed (a runner-level timeout); - scenarios the messages do not own — plain unit tests, the
--fold-unknownbucket — are kept untouched and rendered after the Gherkin features.
Step delimiter bars, assertion notes and interactions all go through the ordinary ingest machinery: the
importer synthesises the same start/step/end records the tests format uses, so nothing about the diagram
is special-cased for Cucumber.
An Attachment envelope carries either a url (the producer kept the file) or an inline body. Inline
base64 bodies are written to a staging folder and then copied into <reports>/attachments/ by the report
generator, exactly like any other attachment; a url is resolved to a local path and
copied the same way. The display name gains an extension derived from the media type when it has none
(end-screenshot → end-screenshot.png), because that extension is what makes the report render a screenshot
inline instead of as a link. Plain-text bodies are not written out by default
(CucumberSynthesisOptions.WriteTextAttachments); the kronikol-test-id attachment never appears in the
report.
The reader never fails an ingest because of the messages file:
- an envelope type this version does not consume (
source,stepDefinition,parameterType, anything a future schema adds) is counted and ignored; - unknown properties inside an envelope it does consume are ignored;
- a line that is not valid JSON is counted and skipped;
- an empty file yields an empty model.
The counts and the human-readable reasons come back as CucumberSynthesisResult.Warnings — malformed lines,
unknown envelopes, scenarios with no kronikol-test-id, duplicate ids, attachments that could not be written.
Producers are not all equally careful with timestamps. playwright-bdd 9.2 stamps a step it never reached
(SKIPPED after a failure) with the test case's start time, and stamps testCaseFinished before the last
step reports back. The importer therefore keeps step starts monotonic within a scenario and takes the scenario
end as the later of testCaseFinished and the last testStepFinished — otherwise a Then bar would sort
before the When that failed.
| Producer | Command |
|---|---|
| playwright-bdd 9.x |
cucumberReporter('message', { outputFile: '../.logs/cucumber/messages.ndjson' }) — see Integration Playwright
|
| cucumber-js | npx cucumber-js --format message:messages.ndjson |
| Cucumber-JVM | --plugin message:target/messages.ndjson |
Kronikol targets the protocol, not a runner: the golden fixture in the test suite comes from playwright-bdd 9.2.0 (Cucumber Messages 32.2.0), and any other producer of the same schema works unchanged.
-
Ingesting External Captures — the interaction and tests NDJSON formats,
kronikol ingest - Integration Playwright — the header-stamping fixture and the playwright-bdd recipe
- Step Tracking, Assertion Tracking — what the bars and ✓/✗ notes mean
Getting Started
Common Tasks
Integration Guides
- Integration xUnit3
- Integration xUnit2
- Integration NUnit
- Integration MSTest
- Integration TUnit
- Integration BDDfy xUnit3
- Integration LightBDD xUnit2
- Integration LightBDD xUnit3
- Integration LightBDD TUnit
- Integration ReqNRoll xUnit2
- Integration ReqNRoll xUnit3
- Integration ReqNRoll TUnit
- Integration Playwright
Uninstrumentable / polyglot backends
- Integration ProxyTap Extension
- Integration TcpTap Extension
- Integration Otlp Extension
- Ingesting External Captures
- Integration Cucumber Messages
- Capture-Time Redaction
Extensions
- Integration AtlasDataApi Extension
- Integration BigQuery Extension
- Integration Bigtable Extension
- Integration BlobStorage Extension
- Integration ClickHouse Extension
- Integration CloudStorage Extension
- Integration CosmosDB Extension
- Integration Dapper Extension
- Integration DynamoDB Extension
- Integration EF Core Relational Extension
- Integration Elasticsearch Extension
- Integration EventBridge Extension
- Integration EventHubs Extension
- Integration Grpc Extension
- Integration Kafka Extension
- Integration MassTransit Extension
- Integration MongoDB Extension
- Integration MySqlConnector Extension
- Integration Npgsql Extension
- Integration Oracle Extension
- Integration PubSub Extension
- Integration Redis Extension
- Integration S3 Extension
- Integration ServiceBus Extension
- Integration SNS Extension
- Integration Spanner Extension
- Integration SqlClient Extension
- Integration Sqlite Extension
- Integration SQS Extension
- Integration StorageQueues Extension
- Integration OpenTelemetry Extension
- Integration DispatchProxy Extension
- Integration MediatR Extension
- Integration PlantUML IKVM
Configuration
- Tracking Dependencies
- Tracking Custom Dependencies
- HTTP Tracking Setup
- Report Configuration
- Diagram Customisation
- Phase-Aware Tracking
- Content Formatting
- PlantUML Server Configuration
Features
- Generated Reports
- Search Syntax
- Component Diagrams
- PlantUML Browser Rendering
- Inline SVG Rendering
- Internal Flow Tracking
- Tags and Attributes
- Excluding Requests
- Excluded Headers
- Multi-Host Test Architectures
- Event-Driven Architecture Testing
- Service Bus Tracking Patterns
- Background Thread Correlation
- Parallel-Safe Background Correlation
- Event & Message Tracking
- Assertion Tracking
- Step Tracking
- Tabular Attributes
- Large Response and Diagram Handling
- Querying Reports
- Diagnostics and Debugging
- CI Summary Integration
- CI Artifact Upload
- Merging Parallel Reports
Reference