Cucumber.Messages (Cucumber v1.0.0)
View SourceBuilders and NDJSON encoding for Cucumber Messages, the protocol every Cucumber implementation emits so formatters, report services, and the Cucumber Compatibility Kit can consume runs uniformly.
A message is an envelope: a single-key map whose key names the message
type (mirroring the schema's oneof) and whose value is the payload,
with camelCase keys matching the JSON schema. One envelope per line,
JSON-encoded, makes the NDJSON stream.
Two groups of builders live here:
- Static messages — derivable from source files alone:
meta/0,source/2,gherkin_document/3,pickle/1,step_definition/3,parameter_type/2, andhook/5. - Run-time messages — describing an execution:
test_run_started/2,test_case/4,test_case_started/4,test_step_started/3,test_step_finished/4,test_case_finished/3,attachment/2,test_run_hook_started/4,test_run_hook_finished/3, andtest_run_finished/3.
These builders are pure — id allocation, ordering, and NDJSON output are
owned by Cucumber.RunCoordinator (the run-wide message sink) and driven
by Cucumber.Messages.Emitter. Enable the stream with
config :cucumber, messages: "cucumber-messages.ndjson"The file is written when the test suite finishes, via
ExUnit.after_suite/1. Those callbacks fire on every ExUnit.run/1, so
a project that invokes a nested ExUnit.run/1 mid-suite will flush (and
close) the stream at that point rather than at the end of the real run.
Example
Ids must be unique across the whole stream, so when emitting several
features, thread next_id from one compilation into the next (as
Cucumber.Compiler.compile_features!/1 does):
{lines, _next_id} =
Enum.flat_map_reduce(features, 0, fn feature, start_id ->
compilation = Gherkin.Pickles.compile(feature, start_id)
envelopes = [
Cucumber.Messages.source(feature.file, feature.source),
Cucumber.Messages.gherkin_document(
feature.file,
compilation.document,
compilation.comments
)
| Enum.map(compilation.pickles, &Cucumber.Messages.pickle/1)
]
{Enum.map(envelopes, &Cucumber.Messages.encode!/1), compilation.next_id}
end)Known deltas from the reference stream
testCase.testSteps entries do not carry stepMatchArgumentsLists
(expression matching returns converted values, not source offsets), and
parameterType/hook envelopes have no source line (their macros don't
record one). The CCK approval harness (test/cucumber/cck_approval_test.exs)
normalizes these; its samples table documents the two samples with
further per-sample divergences.
Summary
Types
A single-key envelope map, e.g. %{pickle: %{...}}.
A %{seconds: integer, nanos: integer} map (timestamp or duration).
A step/hook result status accepted by test_step_result/3.
Functions
Builds an attachment envelope from a Cucumber.Attachment.
Converts a monotonic nanosecond difference into a duration.
Encodes an envelope as one NDJSON line (no trailing newline).
Builds a gherkinDocument envelope from a compiled feature node.
Builds a hook envelope.
Builds the meta envelope describing this implementation and its host.
Builds a parameterType envelope from a custom parameter type definition.
Builds a pickle envelope from a Gherkin.Pickle.
Builds a source envelope carrying a feature file's raw text.
Builds a stepDefinition envelope from a step-registry entry.
Builds a testCase envelope binding a pickle to its executable test steps.
Builds a testCaseFinished envelope.
Builds a testCaseStarted envelope.
Builds a testRunFinished envelope.
Builds a testRunHookFinished envelope.
Builds a testRunHookStarted envelope.
Builds a testRunStarted envelope.
Builds a testStepFinished envelope.
Builds a TestStepResult payload.
Builds a testStepStarted envelope.
Converts a System.system_time(:nanosecond) value into a timestamp.
Types
A single-key envelope map, e.g. %{pickle: %{...}}.
A %{seconds: integer, nanos: integer} map (timestamp or duration).
@type status() ::
:passed | :failed | :pending | :skipped | :undefined | :ambiguous | :unknown
A step/hook result status accepted by test_step_result/3.
Functions
@spec attachment(Cucumber.Attachment.t(), map() | nil) :: envelope()
Builds an attachment envelope from a Cucumber.Attachment.
ref carries :test_case_started_id/:test_step_id when the attachment
was recorded during a step (both optional in the schema).
Converts a monotonic nanosecond difference into a duration.
Negative differences are clamped at zero.
Encodes an envelope as one NDJSON line (no trailing newline).
Builds a gherkinDocument envelope from a compiled feature node.
feature_node and comments are the document and comments
produced by Gherkin.Pickles.compile/2.
@spec hook( String.t(), Cucumber.Hooks.hook_type(), String.t() | nil, String.t() | nil, map() ) :: envelope()
Builds a hook envelope.
type is a Cucumber.Hooks hook kind; tag and name may be nil.
@spec meta() :: envelope()
Builds the meta envelope describing this implementation and its host.
Builds a parameterType envelope from a custom parameter type definition.
See Cucumber.ParameterTypes for how custom types are defined.
@spec pickle(Gherkin.Pickle.t()) :: envelope()
Builds a pickle envelope from a Gherkin.Pickle.
Builds a source envelope carrying a feature file's raw text.
The media type follows the file extension: Markdown feature files
(.feature.md) get the MDG media type, everything else plain Gherkin.
@spec step_definition( String.t(), Cucumber.Discovery.DiscoveryResult.registry_key(), map() ) :: envelope()
Builds a stepDefinition envelope from a step-registry entry.
key is a Cucumber.Discovery registry key; source_reference is a
%{uri: ..., location: %{line: ...}} map.
Builds a testCase envelope binding a pickle to its executable test steps.
Each entry in test_steps is either a pickle step
(%{id: ..., pickleStepId: ..., stepDefinitionIds: [...]}) or a
scenario-hook step (%{id: ..., hookId: ...}), in execution order.
Builds a testCaseFinished envelope.
will_be_retried marks a failed attempt that a retry follows.
@spec test_case_started(String.t(), String.t(), non_neg_integer(), instant()) :: envelope()
Builds a testCaseStarted envelope.
attempt is 0-based; retries increment it.
Builds a testRunFinished envelope.
success is false when any scenario failed.
Builds a testRunHookFinished envelope.
@spec test_run_hook_started(String.t(), String.t() | nil, String.t() | nil, instant()) :: envelope()
Builds a testRunHookStarted envelope.
Marks a before_all/after_all hook beginning execution.
Builds a testRunStarted envelope.
Builds a testStepFinished envelope.
Build result with test_step_result/3.
@spec test_step_result(status(), non_neg_integer(), String.t() | nil) :: map()
Builds a TestStepResult payload.
Used by test_step_finished/4 and test_run_hook_finished/3.
Builds a testStepStarted envelope.
Converts a System.system_time(:nanosecond) value into a timestamp.