diff --git a/.github/workflows/test-codegen.yml b/.github/workflows/test-codegen.yml index ec24fc73..c7421830 100644 --- a/.github/workflows/test-codegen.yml +++ b/.github/workflows/test-codegen.yml @@ -39,6 +39,17 @@ jobs: - run: composer install working-directory: php + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: '25.1' + elixir-version: '1.14.x' + + - name: Install Protoc + uses: arduino/setup-protoc@v1 + + - name: generate code for all languages run: | make clean-all diff --git a/.github/workflows/test-elixir.yml b/.github/workflows/test-elixir.yml index 6cbab17b..0a6f8405 100644 --- a/.github/workflows/test-elixir.yml +++ b/.github/workflows/test-elixir.yml @@ -18,8 +18,8 @@ jobs: - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 with: - otp-version: '22.2' - elixir-version: '1.10.x' + otp-version: '25.1' + elixir-version: '1.14.x' - name: Install Protoc uses: arduino/setup-protoc@v1 @@ -29,9 +29,5 @@ jobs: mix deps.get working-directory: elixir - - name: compile messages - run: MIX_ENV=prod mix protox.generate --multiple-files --output-path=lib/cucumber_messages/generated messages.proto - working-directory: elixir - - run: mix test working-directory: elixir diff --git a/Makefile b/Makefile index 70cc239a..c03a8486 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ schemas = \ ./jsonschema/UndefinedParameterType.json \ ./jsonschema/Envelope.json -languages = go java javascript perl php ruby +languages = go java javascript perl php ruby elixir .DEFAULT_GOAL = help diff --git a/elixir/.formatter.exs b/elixir/.formatter.exs new file mode 100644 index 00000000..d304ff32 --- /dev/null +++ b/elixir/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/elixir/.gitignore b/elixir/.gitignore old mode 100644 new mode 100755 diff --git a/elixir/LICENSE b/elixir/LICENSE old mode 100644 new mode 100755 diff --git a/elixir/Makefile b/elixir/Makefile old mode 100644 new mode 100755 index b1de9b46..b48844d5 --- a/elixir/Makefile +++ b/elixir/Makefile @@ -1,25 +1,21 @@ -include default.mk +schemas = $(shell find ../jsonschema -name "*.json") -clean: - rm -rf _build deps .deps .tested - rm -rf lib/cucumber_messages/generated/* +.DEFAULT_GOAL = help -.deps: setup_mix_and_get_dependencies update_proto_file compile_messages revert_proto_file - touch .deps +help: ## Show this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \n\nWhere is one of:\n"} /^[$$()% a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) -setup_mix_and_get_dependencies: - mix local.hex --force - mix deps.get -.PHONY: setup_mix_and_get_dependencies +generate: require lib/cucumber_messages/generated/messages.ex ## Generate elixir code based on the schemas found in ../jsonschema and using `mix generate_json_structs` for the generation -update_proto_file: messages.proto - mv $< $<.bak - cat $<.bak | sed "s/package io.cucumber.messages/package cucumber_messages/" > $< +require: ## Check requirements for the code generation (elixir/mix is required) + @mix --version >/dev/null 2>&1 || (echo "ERROR: elixir/mix is required."; exit 1) -compile_messages: - MIX_ENV=prod mix protox.generate --multiple-files --output-path=lib/cucumber_messages/generated messages.proto -.PHONY: compile_messages +clean: ## Remove automatically generated files and related artifacts + rm -rf lib/cucumber_messages/generated/messages.ex -revert_proto_file: messages.proto.bak - mv messages.proto.bak messages.proto -.PHONY: revert_proto_file +lib/cucumber_messages/generated/messages.ex: $(schemas) ../jsonschema/scripts/templates/elixir.erb + ruby ../jsonschema/scripts/codegen.rb Ruby ../jsonschema elixir.erb > $@ + mix local.hex --force + mix deps.get + mix compile + mix format diff --git a/elixir/README.md b/elixir/README.md old mode 100644 new mode 100755 diff --git a/elixir/default.mk b/elixir/default.mk deleted file mode 100644 index 42135dab..00000000 --- a/elixir/default.mk +++ /dev/null @@ -1,79 +0,0 @@ -SHELL := /usr/bin/env bash -ELIXIR_SOURCE_FILES = $(shell find . -name "*.ex") - -default: .tested - -.tested: .deps $(ELIXIR_SOURCE_FILES) - mix test - touch $@ - -.deps: - mix local.hex --force - mix deps.get - touch $@ - -update-dependencies: - @echo -e "\033[0;31mPlease update dependencies for elixir manually in mix.exs!! Check https://hex.pm/ for the latest version.\033[0m" - @echo -e "\033[0;31mSome packages require some options (such as ex_doc), check the package its readme / hexdocs.\033[0m" -.PHONY: update-dependencies - -pre-release: remove-local-dependencies update-version update-dependencies clean default -.PHONY: pre-release - -remove-local-dependencies: comment_local_dependency uncomment_remote_dependency -.PHONY: remove-local-dependencies - -comment_remote_dependency: - cat mix.exs | sed 's|{:cucumber_messages, "\([^"]*\)"}|# {:cucumber_messages, "\1"}|' > mix.exs.tmp - mv mix.exs.tmp mix.exs -.PHONY: comment_remote_dependency - -uncomment_remote_dependency: - cat mix.exs | sed 's|# {:cucumber_messages, "\([^"]*\)"}|{:cucumber_messages, "\1"}|' > mix.exs.tmp - mv mix.exs.tmp mix.exs -.PHONY: uncomment_remote_dependency - -comment_local_dependency: - cat mix.exs | sed 's|{:cucumber_messages, path: "../../messages/elixir"}|# {:cucumber_messages, path: "../../messages/elixir"}|' > mix.exs.tmp - mv mix.exs.tmp mix.exs -.PHONY: comment_local_dependency - -uncomment_local_dependency: - cat mix.exs | sed 's|# {:cucumber_messages, path: "../../messages/elixir"}|{:cucumber_messages, path: "../../messages/elixir"}|' > mix.exs.tmp - mv mix.exs.tmp mix.exs -.PHONY: uncomment_local_dependency - -update-version: -ifdef NEW_VERSION - sed -Ei 's/@vsn "[^"]+"/@vsn "$(NEW_VERSION)"/' mix.exs -else - @echo -e "\033[0;31mNEW_VERSION is not defined. Can't update version :-(\033[0m" - exit 1 -endif -.PHONY: update-version - -publish: .deps -ifdef HEX_API_KEY - mix hex.publish --yes -else - @echo -e "\033[0;31mHEX_API_KEY is not defined. Can't update version :-(\033[0m" - exit 1 -endif - -.PHONY: publish - -post-release: comment_remote_dependency uncomment_local_dependency -.PHONY: post-release - -clean: - rm -rf _build deps .deps .tested - -### COMMON stuff for all platforms - -BERP_VERSION = 1.3.0 -BERP_GRAMMAR = gherkin.berp - -define berp-generate-parser = --! dotnet tool list --tool-path /usr/bin | grep "berp\s*$(BERP_VERSION)" && dotnet tool update Berp --version $(BERP_VERSION) --tool-path /usr/bin -berp -g $(BERP_GRAMMAR) -t $< -o $@ --noBOM -endef diff --git a/elixir/lib/cucumber_message.ex b/elixir/lib/cucumber_message.ex old mode 100644 new mode 100755 index 4d1573cf..60bff605 --- a/elixir/lib/cucumber_message.ex +++ b/elixir/lib/cucumber_message.ex @@ -10,7 +10,6 @@ defmodule CucumberMessages do """ def convert_envelopes_to(envelopes, :ndjson) do Enum.map(envelopes, &CucumberMessages.Writer.envelope_to_ndjson!/1) - |> Enum.map(&Jason.encode!(&1)) |> Enum.join("\n") |> case do "" -> "" diff --git a/elixir/lib/cucumber_messages/generated/.gitignore b/elixir/lib/cucumber_messages/generated/.gitignore deleted file mode 100644 index 7862a056..00000000 --- a/elixir/lib/cucumber_messages/generated/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.ex \ No newline at end of file diff --git a/elixir/lib/cucumber_messages/generated/messages.ex b/elixir/lib/cucumber_messages/generated/messages.ex new file mode 100644 index 00000000..ee8f95ce --- /dev/null +++ b/elixir/lib/cucumber_messages/generated/messages.ex @@ -0,0 +1,1499 @@ +defmodule CucumberMessages.Attachment do + defstruct body: nil, + content_encoding: nil, + file_name: nil, + media_type: nil, + source: nil, + test_case_started_id: nil, + test_step_id: nil, + url: nil + + defimpl Jason.Encoder, for: CucumberMessages.Attachment do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + body: normal_map["body"], + content_encoding: normal_map["contentEncoding"], + file_name: normal_map["fileName"], + media_type: normal_map["mediaType"], + source: CucumberMessages.Source.decode(normal_map["source"]), + test_case_started_id: normal_map["testCaseStartedId"], + test_step_id: normal_map["testStepId"], + url: normal_map["url"] + } + end +end + +defmodule CucumberMessages.Duration do + defstruct seconds: nil, nanos: nil + + defimpl Jason.Encoder, for: CucumberMessages.Duration do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + seconds: normal_map["seconds"], + nanos: normal_map["nanos"] + } + end +end + +defmodule CucumberMessages.Envelope do + defstruct attachment: nil, + gherkin_document: nil, + hook: nil, + meta: nil, + parameter_type: nil, + parse_error: nil, + pickle: nil, + source: nil, + step_definition: nil, + test_case: nil, + test_case_finished: nil, + test_case_started: nil, + test_run_finished: nil, + test_run_started: nil, + test_step_finished: nil, + test_step_started: nil, + undefined_parameter_type: nil + + defimpl Jason.Encoder, for: CucumberMessages.Envelope do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + attachment: CucumberMessages.Attachment.decode(normal_map["attachment"]), + gherkin_document: CucumberMessages.GherkinDocument.decode(normal_map["gherkinDocument"]), + hook: CucumberMessages.Hook.decode(normal_map["hook"]), + meta: CucumberMessages.Meta.decode(normal_map["meta"]), + parameter_type: CucumberMessages.ParameterType.decode(normal_map["parameterType"]), + parse_error: CucumberMessages.ParseError.decode(normal_map["parseError"]), + pickle: CucumberMessages.Pickle.decode(normal_map["pickle"]), + source: CucumberMessages.Source.decode(normal_map["source"]), + step_definition: CucumberMessages.StepDefinition.decode(normal_map["stepDefinition"]), + test_case: CucumberMessages.TestCase.decode(normal_map["testCase"]), + test_case_finished: + CucumberMessages.TestCaseFinished.decode(normal_map["testCaseFinished"]), + test_case_started: CucumberMessages.TestCaseStarted.decode(normal_map["testCaseStarted"]), + test_run_finished: CucumberMessages.TestRunFinished.decode(normal_map["testRunFinished"]), + test_run_started: CucumberMessages.TestRunStarted.decode(normal_map["testRunStarted"]), + test_step_finished: + CucumberMessages.TestStepFinished.decode(normal_map["testStepFinished"]), + test_step_started: CucumberMessages.TestStepStarted.decode(normal_map["testStepStarted"]), + undefined_parameter_type: + CucumberMessages.UndefinedParameterType.decode(normal_map["undefinedParameterType"]) + } + end +end + +defmodule CucumberMessages.GherkinDocument do + defstruct uri: nil, feature: nil, comments: nil + + defimpl Jason.Encoder, for: CucumberMessages.GherkinDocument do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + uri: normal_map["uri"], + feature: CucumberMessages.Feature.decode(normal_map["feature"]), + comments: + Enum.map(normal_map["comments"] || [], fn item -> + CucumberMessages.Comment.decode(item) + end) + } + end +end + +defmodule CucumberMessages.Background do + defstruct location: nil, keyword: nil, name: nil, description: nil, steps: nil, id: nil + + defimpl Jason.Encoder, for: CucumberMessages.Background do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + keyword: normal_map["keyword"], + name: normal_map["name"], + description: normal_map["description"], + steps: + Enum.map(normal_map["steps"] || [], fn item -> CucumberMessages.Step.decode(item) end), + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.Comment do + defstruct location: nil, text: nil + + defimpl Jason.Encoder, for: CucumberMessages.Comment do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + text: normal_map["text"] + } + end +end + +defmodule CucumberMessages.DataTable do + defstruct location: nil, rows: nil + + defimpl Jason.Encoder, for: CucumberMessages.DataTable do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + rows: + Enum.map(normal_map["rows"] || [], fn item -> CucumberMessages.TableRow.decode(item) end) + } + end +end + +defmodule CucumberMessages.DocString do + defstruct location: nil, media_type: nil, content: nil, delimiter: nil + + defimpl Jason.Encoder, for: CucumberMessages.DocString do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + media_type: normal_map["mediaType"], + content: normal_map["content"], + delimiter: normal_map["delimiter"] + } + end +end + +defmodule CucumberMessages.Examples do + defstruct location: nil, + tags: nil, + keyword: nil, + name: nil, + description: nil, + table_header: nil, + table_body: nil, + id: nil + + defimpl Jason.Encoder, for: CucumberMessages.Examples do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + tags: Enum.map(normal_map["tags"] || [], fn item -> CucumberMessages.Tag.decode(item) end), + keyword: normal_map["keyword"], + name: normal_map["name"], + description: normal_map["description"], + table_header: CucumberMessages.TableRow.decode(normal_map["tableHeader"]), + table_body: + Enum.map(normal_map["tableBody"] || [], fn item -> + CucumberMessages.TableRow.decode(item) + end), + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.Feature do + defstruct location: nil, + tags: nil, + language: nil, + keyword: nil, + name: nil, + description: nil, + children: nil + + defimpl Jason.Encoder, for: CucumberMessages.Feature do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + tags: Enum.map(normal_map["tags"] || [], fn item -> CucumberMessages.Tag.decode(item) end), + language: normal_map["language"], + keyword: normal_map["keyword"], + name: normal_map["name"], + description: normal_map["description"], + children: + Enum.map(normal_map["children"] || [], fn item -> + CucumberMessages.FeatureChild.decode(item) + end) + } + end +end + +defmodule CucumberMessages.FeatureChild do + defstruct rule: nil, background: nil, scenario: nil + + defimpl Jason.Encoder, for: CucumberMessages.FeatureChild do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + rule: CucumberMessages.Rule.decode(normal_map["rule"]), + background: CucumberMessages.Background.decode(normal_map["background"]), + scenario: CucumberMessages.Scenario.decode(normal_map["scenario"]) + } + end +end + +defmodule CucumberMessages.Rule do + defstruct location: nil, + tags: nil, + keyword: nil, + name: nil, + description: nil, + children: nil, + id: nil + + defimpl Jason.Encoder, for: CucumberMessages.Rule do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + tags: Enum.map(normal_map["tags"] || [], fn item -> CucumberMessages.Tag.decode(item) end), + keyword: normal_map["keyword"], + name: normal_map["name"], + description: normal_map["description"], + children: + Enum.map(normal_map["children"] || [], fn item -> + CucumberMessages.RuleChild.decode(item) + end), + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.RuleChild do + defstruct background: nil, scenario: nil + + defimpl Jason.Encoder, for: CucumberMessages.RuleChild do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + background: CucumberMessages.Background.decode(normal_map["background"]), + scenario: CucumberMessages.Scenario.decode(normal_map["scenario"]) + } + end +end + +defmodule CucumberMessages.Scenario do + defstruct location: nil, + tags: nil, + keyword: nil, + name: nil, + description: nil, + steps: nil, + examples: nil, + id: nil + + defimpl Jason.Encoder, for: CucumberMessages.Scenario do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + tags: Enum.map(normal_map["tags"] || [], fn item -> CucumberMessages.Tag.decode(item) end), + keyword: normal_map["keyword"], + name: normal_map["name"], + description: normal_map["description"], + steps: + Enum.map(normal_map["steps"] || [], fn item -> CucumberMessages.Step.decode(item) end), + examples: + Enum.map(normal_map["examples"] || [], fn item -> + CucumberMessages.Examples.decode(item) + end), + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.Step do + defstruct location: nil, + keyword: nil, + keyword_type: nil, + text: nil, + doc_string: nil, + data_table: nil, + id: nil + + defimpl Jason.Encoder, for: CucumberMessages.Step do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + keyword: normal_map["keyword"], + keyword_type: normal_map["keywordType"], + text: normal_map["text"], + doc_string: CucumberMessages.DocString.decode(normal_map["docString"]), + data_table: CucumberMessages.DataTable.decode(normal_map["dataTable"]), + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.TableCell do + defstruct location: nil, value: nil + + defimpl Jason.Encoder, for: CucumberMessages.TableCell do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + value: normal_map["value"] + } + end +end + +defmodule CucumberMessages.TableRow do + defstruct location: nil, cells: nil, id: nil + + defimpl Jason.Encoder, for: CucumberMessages.TableRow do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + cells: + Enum.map(normal_map["cells"] || [], fn item -> CucumberMessages.TableCell.decode(item) end), + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.Tag do + defstruct location: nil, name: nil, id: nil + + defimpl Jason.Encoder, for: CucumberMessages.Tag do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + location: CucumberMessages.Location.decode(normal_map["location"]), + name: normal_map["name"], + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.Hook do + defstruct id: nil, name: nil, source_reference: nil, tag_expression: nil + + defimpl Jason.Encoder, for: CucumberMessages.Hook do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + id: normal_map["id"], + name: normal_map["name"], + source_reference: CucumberMessages.SourceReference.decode(normal_map["sourceReference"]), + tag_expression: normal_map["tagExpression"] + } + end +end + +defmodule CucumberMessages.Location do + defstruct line: nil, column: nil + + defimpl Jason.Encoder, for: CucumberMessages.Location do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + line: normal_map["line"], + column: normal_map["column"] + } + end +end + +defmodule CucumberMessages.Meta do + defstruct protocol_version: nil, implementation: nil, runtime: nil, os: nil, cpu: nil, ci: nil + + defimpl Jason.Encoder, for: CucumberMessages.Meta do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + protocol_version: normal_map["protocolVersion"], + implementation: CucumberMessages.Product.decode(normal_map["implementation"]), + runtime: CucumberMessages.Product.decode(normal_map["runtime"]), + os: CucumberMessages.Product.decode(normal_map["os"]), + cpu: CucumberMessages.Product.decode(normal_map["cpu"]), + ci: CucumberMessages.Ci.decode(normal_map["ci"]) + } + end +end + +defmodule CucumberMessages.Ci do + defstruct name: nil, url: nil, build_number: nil, git: nil + + defimpl Jason.Encoder, for: CucumberMessages.Ci do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + name: normal_map["name"], + url: normal_map["url"], + build_number: normal_map["buildNumber"], + git: CucumberMessages.Git.decode(normal_map["git"]) + } + end +end + +defmodule CucumberMessages.Git do + defstruct remote: nil, revision: nil, branch: nil, tag: nil + + defimpl Jason.Encoder, for: CucumberMessages.Git do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + remote: normal_map["remote"], + revision: normal_map["revision"], + branch: normal_map["branch"], + tag: normal_map["tag"] + } + end +end + +defmodule CucumberMessages.Product do + defstruct name: nil, version: nil + + defimpl Jason.Encoder, for: CucumberMessages.Product do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + name: normal_map["name"], + version: normal_map["version"] + } + end +end + +defmodule CucumberMessages.ParameterType do + defstruct name: nil, + regular_expressions: nil, + prefer_for_regular_expression_match: nil, + use_for_snippets: nil, + id: nil + + defimpl Jason.Encoder, for: CucumberMessages.ParameterType do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + name: normal_map["name"], + regular_expressions: normal_map["regularExpressions"], + prefer_for_regular_expression_match: normal_map["preferForRegularExpressionMatch"], + use_for_snippets: normal_map["useForSnippets"], + id: normal_map["id"] + } + end +end + +defmodule CucumberMessages.ParseError do + defstruct source: nil, message: nil + + defimpl Jason.Encoder, for: CucumberMessages.ParseError do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + source: CucumberMessages.SourceReference.decode(normal_map["source"]), + message: normal_map["message"] + } + end +end + +defmodule CucumberMessages.Pickle do + defstruct id: nil, uri: nil, name: nil, language: nil, steps: nil, tags: nil, ast_node_ids: nil + + defimpl Jason.Encoder, for: CucumberMessages.Pickle do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + id: normal_map["id"], + uri: normal_map["uri"], + name: normal_map["name"], + language: normal_map["language"], + steps: + Enum.map(normal_map["steps"] || [], fn item -> + CucumberMessages.PickleStep.decode(item) + end), + tags: + Enum.map(normal_map["tags"] || [], fn item -> CucumberMessages.PickleTag.decode(item) end), + ast_node_ids: normal_map["astNodeIds"] + } + end +end + +defmodule CucumberMessages.PickleDocString do + defstruct media_type: nil, content: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleDocString do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + media_type: normal_map["mediaType"], + content: normal_map["content"] + } + end +end + +defmodule CucumberMessages.PickleStep do + defstruct argument: nil, ast_node_ids: nil, id: nil, type: nil, text: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleStep do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + argument: CucumberMessages.PickleStepArgument.decode(normal_map["argument"]), + ast_node_ids: normal_map["astNodeIds"], + id: normal_map["id"], + type: normal_map["type"], + text: normal_map["text"] + } + end +end + +defmodule CucumberMessages.PickleStepArgument do + defstruct doc_string: nil, data_table: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleStepArgument do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + doc_string: CucumberMessages.PickleDocString.decode(normal_map["docString"]), + data_table: CucumberMessages.PickleTable.decode(normal_map["dataTable"]) + } + end +end + +defmodule CucumberMessages.PickleTable do + defstruct rows: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleTable do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + rows: + Enum.map(normal_map["rows"] || [], fn item -> + CucumberMessages.PickleTableRow.decode(item) + end) + } + end +end + +defmodule CucumberMessages.PickleTableCell do + defstruct value: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleTableCell do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + value: normal_map["value"] + } + end +end + +defmodule CucumberMessages.PickleTableRow do + defstruct cells: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleTableRow do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + cells: + Enum.map(normal_map["cells"] || [], fn item -> + CucumberMessages.PickleTableCell.decode(item) + end) + } + end +end + +defmodule CucumberMessages.PickleTag do + defstruct name: nil, ast_node_id: nil + + defimpl Jason.Encoder, for: CucumberMessages.PickleTag do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + name: normal_map["name"], + ast_node_id: normal_map["astNodeId"] + } + end +end + +defmodule CucumberMessages.Source do + defstruct uri: nil, data: nil, media_type: nil + + defimpl Jason.Encoder, for: CucumberMessages.Source do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + uri: normal_map["uri"], + data: normal_map["data"], + media_type: normal_map["mediaType"] + } + end +end + +defmodule CucumberMessages.SourceReference do + defstruct uri: nil, java_method: nil, java_stack_trace_element: nil, location: nil + + defimpl Jason.Encoder, for: CucumberMessages.SourceReference do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + uri: normal_map["uri"], + java_method: CucumberMessages.JavaMethod.decode(normal_map["javaMethod"]), + java_stack_trace_element: + CucumberMessages.JavaStackTraceElement.decode(normal_map["javaStackTraceElement"]), + location: CucumberMessages.Location.decode(normal_map["location"]) + } + end +end + +defmodule CucumberMessages.JavaMethod do + defstruct class_name: nil, method_name: nil, method_parameter_types: nil + + defimpl Jason.Encoder, for: CucumberMessages.JavaMethod do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + class_name: normal_map["className"], + method_name: normal_map["methodName"], + method_parameter_types: normal_map["methodParameterTypes"] + } + end +end + +defmodule CucumberMessages.JavaStackTraceElement do + defstruct class_name: nil, file_name: nil, method_name: nil + + defimpl Jason.Encoder, for: CucumberMessages.JavaStackTraceElement do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + class_name: normal_map["className"], + file_name: normal_map["fileName"], + method_name: normal_map["methodName"] + } + end +end + +defmodule CucumberMessages.StepDefinition do + defstruct id: nil, pattern: nil, source_reference: nil + + defimpl Jason.Encoder, for: CucumberMessages.StepDefinition do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + id: normal_map["id"], + pattern: CucumberMessages.StepDefinitionPattern.decode(normal_map["pattern"]), + source_reference: CucumberMessages.SourceReference.decode(normal_map["sourceReference"]) + } + end +end + +defmodule CucumberMessages.StepDefinitionPattern do + defstruct source: nil, type: nil + + defimpl Jason.Encoder, for: CucumberMessages.StepDefinitionPattern do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + source: normal_map["source"], + type: normal_map["type"] + } + end +end + +defmodule CucumberMessages.TestCase do + defstruct id: nil, pickle_id: nil, test_steps: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestCase do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + id: normal_map["id"], + pickle_id: normal_map["pickleId"], + test_steps: + Enum.map(normal_map["testSteps"] || [], fn item -> + CucumberMessages.TestStep.decode(item) + end) + } + end +end + +defmodule CucumberMessages.Group do + defstruct children: nil, start: nil, value: nil + + defimpl Jason.Encoder, for: CucumberMessages.Group do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + children: + Enum.map(normal_map["children"] || [], fn item -> CucumberMessages.Group.decode(item) end), + start: normal_map["start"], + value: normal_map["value"] + } + end +end + +defmodule CucumberMessages.StepMatchArgument do + defstruct group: nil, parameter_type_name: nil + + defimpl Jason.Encoder, for: CucumberMessages.StepMatchArgument do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + group: CucumberMessages.Group.decode(normal_map["group"]), + parameter_type_name: normal_map["parameterTypeName"] + } + end +end + +defmodule CucumberMessages.StepMatchArgumentsList do + defstruct step_match_arguments: nil + + defimpl Jason.Encoder, for: CucumberMessages.StepMatchArgumentsList do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + step_match_arguments: + Enum.map(normal_map["stepMatchArguments"] || [], fn item -> + CucumberMessages.StepMatchArgument.decode(item) + end) + } + end +end + +defmodule CucumberMessages.TestStep do + defstruct hook_id: nil, + id: nil, + pickle_step_id: nil, + step_definition_ids: nil, + step_match_arguments_lists: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestStep do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + hook_id: normal_map["hookId"], + id: normal_map["id"], + pickle_step_id: normal_map["pickleStepId"], + step_definition_ids: normal_map["stepDefinitionIds"], + step_match_arguments_lists: + case normal_map["stepMatchArgumentsLists"] do + nil -> + nil + + data -> + Enum.map(data, fn item -> CucumberMessages.StepMatchArgumentsList.decode(item) end) + end + } + end +end + +defmodule CucumberMessages.TestCaseFinished do + defstruct test_case_started_id: nil, timestamp: nil, will_be_retried: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestCaseFinished do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + test_case_started_id: normal_map["testCaseStartedId"], + timestamp: CucumberMessages.Timestamp.decode(normal_map["timestamp"]), + will_be_retried: normal_map["willBeRetried"] + } + end +end + +defmodule CucumberMessages.TestCaseStarted do + defstruct attempt: nil, id: nil, test_case_id: nil, worker_id: nil, timestamp: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestCaseStarted do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + attempt: normal_map["attempt"], + id: normal_map["id"], + test_case_id: normal_map["testCaseId"], + worker_id: normal_map["workerId"], + timestamp: CucumberMessages.Timestamp.decode(normal_map["timestamp"]) + } + end +end + +defmodule CucumberMessages.TestRunFinished do + defstruct message: nil, success: nil, timestamp: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestRunFinished do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + message: normal_map["message"], + success: normal_map["success"], + timestamp: CucumberMessages.Timestamp.decode(normal_map["timestamp"]) + } + end +end + +defmodule CucumberMessages.TestRunStarted do + defstruct timestamp: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestRunStarted do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + timestamp: CucumberMessages.Timestamp.decode(normal_map["timestamp"]) + } + end +end + +defmodule CucumberMessages.TestStepFinished do + defstruct test_case_started_id: nil, test_step_id: nil, test_step_result: nil, timestamp: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestStepFinished do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + test_case_started_id: normal_map["testCaseStartedId"], + test_step_id: normal_map["testStepId"], + test_step_result: CucumberMessages.TestStepResult.decode(normal_map["testStepResult"]), + timestamp: CucumberMessages.Timestamp.decode(normal_map["timestamp"]) + } + end +end + +defmodule CucumberMessages.TestStepResult do + defstruct duration: nil, message: nil, status: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestStepResult do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + duration: CucumberMessages.Duration.decode(normal_map["duration"]), + message: normal_map["message"], + status: normal_map["status"] + } + end +end + +defmodule CucumberMessages.TestStepStarted do + defstruct test_case_started_id: nil, test_step_id: nil, timestamp: nil + + defimpl Jason.Encoder, for: CucumberMessages.TestStepStarted do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + test_case_started_id: normal_map["testCaseStartedId"], + test_step_id: normal_map["testStepId"], + timestamp: CucumberMessages.Timestamp.decode(normal_map["timestamp"]) + } + end +end + +defmodule CucumberMessages.Timestamp do + defstruct seconds: nil, nanos: nil + + defimpl Jason.Encoder, for: CucumberMessages.Timestamp do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + seconds: normal_map["seconds"], + nanos: normal_map["nanos"] + } + end +end + +defmodule CucumberMessages.UndefinedParameterType do + defstruct expression: nil, name: nil + + defimpl Jason.Encoder, for: CucumberMessages.UndefinedParameterType do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + expression: normal_map["expression"], + name: normal_map["name"] + } + end +end diff --git a/elixir/lib/cucumber_messages/helper.ex b/elixir/lib/cucumber_messages/helper.ex new file mode 100755 index 00000000..c0ccfd8a --- /dev/null +++ b/elixir/lib/cucumber_messages/helper.ex @@ -0,0 +1,13 @@ +defmodule CucumberMessages.Helper do + def lower_camelize(atom) when is_atom(atom), do: atom |> Atom.to_string() |> lower_camelize() + + def lower_camelize(string) when is_binary(string) do + {first_letter, rest} = string |> Macro.camelize() |> String.split_at(1) + "#{String.downcase(first_letter)}#{rest}" + end + + def construct_module_name(child) when is_binary(child) do + last_modulename_part = Macro.camelize(child) + "Elixir.CucumberMessages.#{last_modulename_part}" |> String.to_atom() + end +end diff --git a/elixir/lib/cucumber_messages/writer.ex b/elixir/lib/cucumber_messages/writer.ex deleted file mode 100644 index 615923f4..00000000 --- a/elixir/lib/cucumber_messages/writer.ex +++ /dev/null @@ -1,124 +0,0 @@ -defmodule CucumberMessages.Writer do - @moduledoc false - # This is a temporary writer up until Protox supports json encoding - - alias CucumberMessages.Envelope - alias CucumberMessages.Location - alias CucumberMessages.Pickle.PickleStep - alias CucumberMessages.GherkinDocument.Feature.FeatureChild - alias CucumberMessages.PickleStepArgument.{PickleDocString, PickleTable} - - defp unstruct(%Location{column: 0} = map, acc) do - map |> Map.from_struct() |> Map.delete(:column) |> unstruct(acc) - end - - defp unstruct(%{__struct__: _} = map, acc) when is_map(map) do - map |> Map.from_struct() |> unstruct(acc) - end - - defp unstruct(%{__uf__: _} = map, acc) when is_map(map) do - map |> Map.delete(:__uf__) |> unstruct(acc) - end - - defp unstruct(map, acc) when is_map(map) do - Enum.reduce(map, acc, fn - :ignore, acc -> - acc - - {_k, nil}, acc -> - acc - - {_k, ""}, acc -> - acc - - {_k, :ignore}, acc -> - acc - - {_k, []}, acc -> - acc - - {_k, {new_key, v}}, acc when is_map(v) or is_list(v) or new_key == :uri -> - Map.put(acc, lower_camelcase(new_key), unstruct(v, %{})) - - {k, v}, acc when is_map(v) or is_list(v) -> - Map.put(acc, lower_camelcase(k), unstruct(v, %{})) - - {k, data}, acc -> - Map.put(acc, lower_camelcase(k), unstruct(data, %{})) - end) - end - - defp unstruct([], %{}) do - :ignore - end - - defp unstruct(list, acc) when is_list(list) do - list - |> Enum.map(fn - %FeatureChild{} = el -> - el.value - - %PickleStep{argument: %PickleTable{}} = el -> - Map.put(el, :argument, %{dataTable: el.argument}) |> Map.delete(:__struct__) - - %PickleStep{argument: %PickleDocString{}} = el -> - Map.put(el, :argument, %{docString: el.argument}) |> Map.delete(:__struct__) - - other_el -> - other_el - end) - |> Enum.reduce(acc, fn - {_new_key, nil}, acc -> - acc - - {new_key, value}, acc when is_map(acc) -> - # Map.put(acc, lower_camelcase(new_key), unstruct(value, %{})) - [Map.put(acc, lower_camelcase(new_key), unstruct(value, %{}))] - - {new_key, value}, acc when is_list(acc) -> - unstructed = unstruct(value, %{}) - - case unstructed do - :ignore -> acc - _ -> acc ++ [Map.put(%{}, lower_camelcase(new_key), unstructed)] - end - - map, acc when is_map(acc) and acc == %{} -> - unstructed = unstruct(map, %{}) - - case unstructed do - :ignore -> acc - _ -> [unstruct(map, %{})] - end - - map, acc -> - unstructed = unstruct(map, %{}) - - case unstructed do - :ignore -> acc - _ -> acc ++ [unstruct(map, %{})] - end - end) - end - - defp unstruct({k, v}, acc) when not is_tuple(v), - do: Map.put(acc, lower_camelcase(k), unstruct(v, %{})) - - defp unstruct(just_data, _acc) when not is_tuple(just_data), do: just_data - - def envelope_to_ndjson!(%Envelope{} = message) do - # This is sort of a sanity check to see whether the constructed message is - # proto compliant - # As soon as Protox supports json encoding, this is no longer necessary - message |> Protox.Encode.encode!() - - unstruct(message, %{}) - end - - defp lower_camelcase(atom) when is_atom(atom), do: atom |> Atom.to_string() |> lower_camelcase() - - defp lower_camelcase(string) when is_binary(string) do - {to_be_downcased, camelcased} = string |> Macro.camelize() |> String.split_at(1) - String.downcase(to_be_downcased) <> camelcased - end -end diff --git a/elixir/messages.proto b/elixir/messages.proto deleted file mode 100644 index fd2f9403..00000000 --- a/elixir/messages.proto +++ /dev/null @@ -1,696 +0,0 @@ -syntax = "proto3"; -package cucumber_messages; -option go_package = "messages"; - -// When removing a field, replace it with reserved, rather than deleting the line. -// When adding a field, add it to the end and increment the number by one. -// See https://developers.google.com/protocol-buffers/docs/proto#updating for details - -/** - * All the messages that are passed between different components/processes are Envelope - * messages. - */ -message Envelope { - oneof message { - // Gherkin - Source source = 1; - GherkinDocument gherkin_document = 2; - - // Compiler(s) - Pickle pickle = 3; - - // - StepDefinition step_definition = 4; - Hook hook = 5; - ParameterType parameter_type = 6; - TestCase test_case = 7; - UndefinedParameterType undefined_parameter_type = 8; - - // Execution - TestRunStarted test_run_started = 9; - TestCaseStarted test_case_started = 10; - TestStepStarted test_step_started = 11; - Attachment attachment = 12; - TestStepFinished test_step_finished = 13; - TestCaseFinished test_case_finished = 14; - TestRunFinished test_run_finished = 15; - - // Parsing - ParseError parse_error = 16; - - Meta meta = 17; - } -} - -/** - * This message contains meta information about the environment. Consumers can use - * this for various purposes. - */ -message Meta { - /** - * The [SEMVER](https://semver.org/) version number of the protocol - */ - string protocol_version = 1; - - // SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. - Product implementation = 2; - - // Java, Ruby, Node.js etc - Product runtime = 3; - - // Windows, Linux, MacOS etc - Product os = 4; - - // 386, arm, amd64 etc - Product cpu = 5; - - // CI environment - CI ci = 6; - - // A product has a name and a version - message Product { - // The product name - string name = 1; - // The product version - string version = 2; - } - - message CI { - // Name of the CI product, e.g. "Jenkins", "CircleCI" etc. - string name = 1; - - // Link to the build - string url = 2; - - // Information about Git, provided by the Build/CI server as environment - // variables. - Git git = 3; - - message Git { - string remote = 1; - string revision = 2; - string branch = 3; - string tag = 4; - } - } -} - - -////// Common types - -// From https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/timestamp.proto -message Timestamp { - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} - -// The structure is pretty close of the Timestamp one. For clarity, a second type -// of message is used. -message Duration { - int64 seconds = 1; - - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - int32 nanos = 2; -} - -/** - * Points to a line and a column in a text file - */ -message Location { - uint32 line = 1; - uint32 column = 2; -} - -/** - * Points to a [Source](#io.cucumber.messages.Source) identified by `uri` and a - * [Location](#io.cucumber.messages.Location) within that file. - */ -message SourceReference { - oneof reference { - string uri = 1; - JavaMethod java_method = 3; - JavaStackTraceElement java_stack_trace_element = 4; - } - Location location = 2; - - message JavaMethod { - string class_name = 1; - string method_name = 2; - repeated string method_parameter_types = 3; - } - - message JavaStackTraceElement { - string class_name = 1; - string method_name = 2; - string file_name = 3; - } -} - -////// Source - -/** - * A source file, typically a Gherkin document or Java/Ruby/JavaScript source code - */ -message Source { - /** - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) - * of the source, typically a file path relative to the root directory - */ - string uri = 1; - // The contents of the file - string data = 2; - // The media type of the file. Can be used to specify custom types, such as - // text/x.cucumber.gherkin+plain - string media_type = 3; -} - -/** - * The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document. - * Cucumber implementations should *not* depend on `GherkinDocument` or any of its - * children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead. - * - * The only consumers of `GherkinDocument` should only be formatters that produce - * "rich" output, resembling the original Gherkin document. - */ -message GherkinDocument { - /** - * The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) - * of the source, typically a file path relative to the root directory - */ - string uri = 1; - Feature feature = 2; - // All the comments in the Gherkin document - repeated Comment comments = 3; - - /** - * A comment in a Gherkin document - */ - message Comment { - // The location of the comment - Location location = 1; - // The text of the comment - string text = 2; - } - - /** - * The top level node in the AST - */ - message Feature { - // The location of the `Feature` keyword - Location location = 1; - // All the tags placed above the `Feature` keyword - repeated Tag tags = 2; - // The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document - string language = 3; - // The text of the `Feature` keyword (in the language specified by `language`) - string keyword = 4; - // The name of the feature (the text following the `keyword`) - string name = 5; - // The line(s) underneath the line with the `keyword` that are used as description - string description = 6; - // Zero or more children - repeated FeatureChild children = 7; - - /** - * A tag - */ - message Tag { - // Location of the tag - Location location = 1; - // The name of the tag (including the leading `@`) - string name = 2; - // Unique ID to be able to reference the Tag from PickleTag - string id = 3; - } - - /** - * A child node of a `Feature` node - */ - message FeatureChild { - oneof value { - Rule rule = 1; - Background background = 2; - Scenario scenario = 3; - } - - /** - * A `Rule` node - */ - message Rule { - // The location of the `Rule` keyword - Location location = 1; - string keyword = 2; - string name = 3; - string description = 4; - repeated RuleChild children = 5; - string id = 6; - repeated Tag tags = 7; - } - - message RuleChild { - oneof value { - Background background = 1; - Scenario scenario = 2; - } - } - } - - message Background { - // The location of the `Background` keyword - Location location = 1; - string keyword = 2; - string name = 3; - string description = 4; - repeated Step steps = 5; - string id = 6; - } - - message Scenario { - // The location of the `Scenario` keyword - Location location = 1; - repeated Tag tags = 2; - string keyword = 3; - string name = 4; - string description = 5; - repeated Step steps = 6; - repeated Examples examples = 7; - string id = 8; - - message Examples { - // The location of the `Examples` keyword - Location location = 1; - repeated Tag tags = 2; - string keyword = 3; - string name = 4; - string description = 5; - TableRow table_header = 6; - repeated TableRow table_body = 7; - string id = 8; - } - } - - // A row in a table - message TableRow { - // The location of the first cell in the row - Location location = 1; - // Cells in the row - repeated TableCell cells = 2; - string id = 3; - - // A cell in a `TableRow` - message TableCell { - // The location of the cell - Location location = 1; - // The value of the cell - string value = 2; - } - } - - // A step - message Step { - // The location of the steps' `keyword` - Location location = 1; - string keyword = 2; - string text = 3; - oneof argument { - DocString doc_string = 4; - DataTable data_table = 5; - } - // Unique ID to be able to reference the Step from PickleStep - string id = 6; - - message DataTable { - Location location = 1; - repeated TableRow rows = 2; - } - - message DocString { - Location location = 1; - string media_type = 2; - string content = 3; - string delimiter = 4; - } - } - } -} - -////// Attachments (parse errors, execution errors, screenshots, links...) - -/** - * An attachment represents any kind of data associated with a line in a - * [Source](#io.cucumber.messages.Source) file. It can be used for: - * - * * Syntax errors during parse time - * * Screenshots captured and attached during execution - * * Logs captured and attached during execution - * - * It is not to be used for runtime errors raised/thrown during execution. This - * is captured in `TestResult`. - */ -message Attachment { - SourceReference source = 1; - string testStepId = 2; - string test_case_started_id = 3; - - /** - * The body of the attachment. If `content_encoding` is `IDENTITY`, the attachment - * is simply the string. If it's `BASE64`, the string should be Base64 decoded to - * obtain the attachment. - */ - string body = 4; - - /** - * The media type of the data. This can be any valid - * [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) - * as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` - * and `text/x.cucumber.stacktrace+plain` - */ - string media_type = 5; - - /** - * Whether to interpret `body` "as-is" (IDENTITY) or if it needs to be Base64-decoded (BASE64). - * - * Content encoding is *not* determined by the media type, but rather by the type - * of the object being attached: - * - * - string => IDENTITY - * - byte array => BASE64 - * - stream => BASE64 - */ - ContentEncoding content_encoding = 6; - - /** - * Suggested file name of the attachment. (Provided by the user as an argument to `attach`) - */ - string file_name = 7; - - /** - * A URL where the attachment can be retrieved. This field should not be set by Cucumber. - * It should be set by a program that reads a message stream and does the following for - * each Attachment message: - * - * - Writes the body (after base64 decoding if necessary) to a new file. - * - Sets `body` and `content_encoding` to `null` - * - Writes out the new attachment message - * - * This will result in a smaller message stream, which can improve performance and - * reduce bandwidth of message consumers. It also makes it easier to process and download attachments - * separately from reports. - */ - string url = 8; - - enum ContentEncoding { - IDENTITY = 0; - // When this is used, the data field is a single line base64 string - BASE64 = 1; - } -} - -////// Pickles - -/** - * A `Pickle` represents a template for a `TestCase`. It is typically derived - * from another format, such as [GherkinDocument](#io.cucumber.messages.GherkinDocument). - * In the future a `Pickle` may be derived from other formats such as Markdown or - * Excel files. - * - * By making `Pickle` the main data structure Cucumber uses for execution, the - * implementation of Cucumber itself becomes simpler, as it doesn't have to deal - * with the complex structure of a [GherkinDocument](#io.cucumber.messages.GherkinDocument). - * - * Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` - */ -message Pickle { - /** - * A unique id for the pickle - */ - string id = 1; - // The uri of the source file - string uri = 2; - // The name of the pickle - string name = 3; - // The language of the pickle - string language = 4; - // One or more steps - repeated PickleStep steps = 5; - /** - * One or more tags. If this pickle is constructed from a Gherkin document, - * It includes inherited tags from the `Feature` as well. - */ - repeated PickleTag tags = 6; - /** - * Points to the AST node locations of the pickle. The last one represents the unique - * id of the pickle. A pickle constructed from `Examples` will have the first - * id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. - */ - repeated string ast_node_ids = 7; - - /** - * A tag - */ - message PickleTag { - string name = 1; - // Points to the AST node this was created from - string ast_node_id = 2; - } - - /** - * An executable step - */ - message PickleStep { - string text = 1; - // An optional argument - PickleStepArgument argument = 2; - // A unique ID for the PickleStep - string id = 3; - // References the IDs of the source of the step. For Gherkin, this can be - // the ID of a Step, and possibly also the ID of a TableRow - repeated string ast_node_ids = 4; - } -} - -/** - * A wrapper for either a doc string or a table. - */ -message PickleStepArgument { - oneof message { - PickleDocString doc_string = 1; - PickleTable data_table = 2; - } - - message PickleDocString { - string media_type = 1; - string content = 2; - } - - message PickleTable { - repeated PickleTableRow rows = 1; - - message PickleTableRow { - repeated PickleTableCell cells = 1; - - message PickleTableCell { - string value = 1; - } - } - } -} - -////// TestCases - -/** - * A `TestCase` contains a sequence of `TestStep`s. - */ -message TestCase { - string id = 1; - // The ID of the `Pickle` this `TestCase` is derived from. - string pickle_id = 2; - repeated TestStep test_steps = 3; - - /** - * A `TestStep` is derived from either a `PickleStep` - * combined with a `StepDefinition`, or from a `Hook`. - */ - message TestStep { - string id = 1; - // Pointer to the `PickleStep` (if derived from a PickleStep) - string pickle_step_id = 2; - // Pointer to all the matching `StepDefinition`s (if derived from a PickleStep) - repeated string step_definition_ids = 3; - // A list of list of StepMatchArgument (if derived from a `StepDefinition`). - // Each element represents a matching step definition. A size of 0 means `UNDEFINED`, - // and a size of 2+ means `AMBIGUOUS` - repeated StepMatchArgumentsList step_match_arguments_lists = 4; - // Pointer to the `Hook` (if derived from a Hook) - string hook_id = 5; - - message StepMatchArgumentsList { - repeated StepMatchArgument step_match_arguments = 1; - - /** - * Represents a single argument extracted from a step match and passed to a step definition. - * This is used for the following purposes: - * - Construct an argument to pass to a step definition (possibly through a parameter type transform) - * - Highlight the matched parameter in rich formatters such as the HTML formatter - * - * This message closely matches the `Argument` class in the `cucumber-expressions` library. - */ - message StepMatchArgument { - string parameter_type_name = 1; - /** - * Represents the outermost capture group of an argument. This message closely matches the - * `Group` class in the `cucumber-expressions` library. - */ - Group group = 2; - - message Group { - uint32 start = 1; - string value = 2; - repeated Group children = 3; - } - } - } - } -} - -message TestRunStarted { - Timestamp timestamp = 1; -} - -message TestCaseStarted { - Timestamp timestamp = 1; - reserved 2; - /** - * The first attempt should have value 0, and for each retry the value - * should increase by 1. - */ - uint32 attempt = 3; - string test_case_id = 4; - /** - * Because a `TestCase` can be run multiple times (in case of a retry), - * we use this field to group messages relating to the same attempt. - */ - string id = 5; -} - -message TestCaseFinished { - Timestamp timestamp = 1; - reserved 2; - string test_case_started_id = 3; -} - -message TestStepStarted { - Timestamp timestamp = 1; - string testStepId = 2; - string test_case_started_id = 3; -} - -message TestStepFinished { - TestStepResult test_step_result = 1; - Timestamp timestamp = 2; - string testStepId = 3; - string test_case_started_id = 4; - - - message TestStepResult { - Status status = 1; - string message = 2; - Duration duration = 3; - bool will_be_retried = 4; - - /** - * Status of a `TestStep`. - * - * The ordinal values of statuses are significant. The status of a TestCase - * is computed by picking the status with the highest value for all of its steps. - * - * For example, if a TestCase has steps with statuses passed, undefined and skipped, - * then the pickle's status is undefined. - */ - enum Status { - // The step hasn't been matched or executed. - UNKNOWN = 0; - // The step matched one step definition and passed execution. - PASSED = 1; - // The step matched one step definition but was not executed because the - // previous step was not PASSED. - SKIPPED = 2; - // The step matched one step definition and signalled pending during execution. - // This is the default behaviour of generated step definitions, which either - // throw a special PendingException, or return a special value indicating that it's - // pending. How to signal the pending status depends on the Cucumber implementation. - PENDING = 3; - // The step matched no step definitions. - UNDEFINED = 4; - // The step matched two or more step definitions. - AMBIGUOUS = 5; - // The step matched one step definition and failed execution. - FAILED = 6; - } - } -} - -message TestRunFinished { - // success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) - bool success = 1; - // Timestamp when the TestRun is finished - Timestamp timestamp = 2; - // Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. - // If there are undefined parameter types, the message is simply - // "The following parameter type(s() are not defined: xxx, yyy". - // The independent `UndefinedParameterType` messages can be used to generate - // snippets for those parameter types. - string message = 3; -} - -message Hook { - string id = 1; - string tag_expression = 2; - SourceReference source_reference = 3; -} - -message StepDefinition { - string id = 1; - StepDefinitionPattern pattern = 2; - SourceReference source_reference = 3; - - message StepDefinitionPattern { - string source = 1; - StepDefinitionPatternType type = 2; - - enum StepDefinitionPatternType { - CUCUMBER_EXPRESSION = 0; - REGULAR_EXPRESSION = 1; - } - } -} - -message ParameterType { - // The name is unique, so we don't need an id. - string name = 1; - repeated string regular_expressions = 2; - bool prefer_for_regular_expression_match = 3; - bool use_for_snippets = 4; - string id = 5; -} - -message UndefinedParameterType { - string name = 1; - string expression = 2; -} - -message ParseError { - SourceReference source = 1; - string message = 2; -} diff --git a/elixir/mix.exs b/elixir/mix.exs old mode 100644 new mode 100755 index e167bfbd..b212d9d7 --- a/elixir/mix.exs +++ b/elixir/mix.exs @@ -11,7 +11,7 @@ defmodule CucumberMessages.MixProject do name: "CucumberMessages", description: description(), package: package(), - elixir: "~> 1.10", + elixir: "~> 1.13", start_permanent: Mix.env() == :prod, deps: deps() ] @@ -25,14 +25,13 @@ defmodule CucumberMessages.MixProject do defp deps do [ - {:protox, "~> 1.7.0"}, {:jason, "~> 1.2"}, {:ex_doc, "~> 0.29", only: :dev, runtime: false} ] end defp description() do - "Elixir implementation of the cucumber messages protobuf schema" + "Elixir implementation of the cucumber messages json schemas" end defp package() do diff --git a/elixir/mix.lock b/elixir/mix.lock old mode 100644 new mode 100755 index 2c72c4ab..b406b4a7 --- a/elixir/mix.lock +++ b/elixir/mix.lock @@ -1,11 +1,9 @@ %{ - "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, "ex_doc": {:hex, :ex_doc, "0.29.0", "4a1cb903ce746aceef9c1f9ae8a6c12b742a5461e6959b9d3b24d813ffbea146", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "f096adb8bbca677d35d278223361c7792d496b3fc0d0224c9d4bc2f651af5db1"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, + "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, - "protox": {:hex, :protox, "1.7.0", "6da3c691ae90ecd3ac8708eca8aea4e9c47d97ebffa29a596ab55f57b8b0fcba", [:mix], [{:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "7da9c62cd288eade7c8ed501a7f05a9bdf712abdc9b6fca5d1e823028ed41478"}, } diff --git a/elixir/retrieve_all_testdata.sh b/elixir/retrieve_all_testdata.sh new file mode 100755 index 00000000..66728b32 --- /dev/null +++ b/elixir/retrieve_all_testdata.sh @@ -0,0 +1,10 @@ +#! /bin/bash + +rm -rf ./test/testdata/* + +curl -s -H "Accept: application/vnd.github+json" https://api.github.com/repos/cucumber/compatibility-kit/contents/devkit/samples | grep "\"name\"" | awk '{print $2}' | sed 's/"//g' | sed 's/,//g' | while read -r subfolder ; do + + curl -s -H "Accept: application/vnd.github+json" "https://api.github.com/repos/cucumber/compatibility-kit/contents/devkit/samples/$subfolder" | grep "\"name\"" | grep ".feature.ndjson" | awk '{print $2}' | sed 's/"//g' | sed 's/,//g' | while read -r filename; do + curl -s -H "Accept: application/vnd.github+json" "https://api.github.com/repos/cucumber/compatibility-kit/contents/devkit/samples/$subfolder/$filename" | jq -r '.content' | base64 -d > "./test/testdata/$filename" + done +done \ No newline at end of file diff --git a/elixir/test/cucumber_message_test.exs b/elixir/test/cucumber_message_test.exs old mode 100644 new mode 100755 index 754e9f19..444d645a --- a/elixir/test/cucumber_message_test.exs +++ b/elixir/test/cucumber_message_test.exs @@ -1,5 +1,57 @@ defmodule CucumberMessagesTest do use ExUnit.Case - # No testdata for now + @files [".", "test", "testdata", "*.ndjson"] + |> Path.join() + |> Path.wildcard() + # temporarily don't look at this test because Elixir/ Jason automatically uppercases some characters? + |> Enum.filter(fn el -> not String.contains?(el, "attachments.feature.ndjson") end) + + require IEx + + test "serialize en deserialize all messages" do + results = + Enum.map(@files, fn path -> + file_content = path |> File.read!() |> String.trim() + list_of_jsons = String.split(file_content, "\n") + comparisons = Enum.map(list_of_jsons, &compare/1) + all_good? = Enum.all?(comparisons, fn {result, _, _, _} -> result end) + {all_good?, path, comparisons} + end) + + testresult = Enum.all?(results, fn {path_result, _, _} -> path_result end) + + Enum.each(results, &verbose_log/1) + + assert testresult + end + + defp compare(binary) do + {module_name, message_name, decoded_json} = + case Jason.decode(binary) do + {:ok, decoded} -> + [message_name] = Map.keys(decoded) + {CucumberMessages.Helper.construct_module_name(message_name), message_name, decoded} + + {:error, data} -> + IEx.pry() + end + + decoded = module_name.decode(decoded_json[message_name]) + re_encoded = Jason.encode!(%{message_name => decoded}) + the_same? = binary == re_encoded + {the_same?, binary, decoded, re_encoded} + end + + # TODO: extra verbose logging with valid message comparisons for each message + defp verbose_log({true, path, _comparisons}) do + text = IO.ANSI.framed() <> IO.ANSI.green_background() <> path <> IO.ANSI.reset() + IO.puts(text) + end + + # TODO: extra verbose logging with invalid message comparisons for each message + defp verbose_log({false, path, _comparisons}) do + text = IO.ANSI.framed() <> IO.ANSI.red_background() <> path <> IO.ANSI.reset() + IO.puts(text) + end end diff --git a/elixir/test/test_helper.exs b/elixir/test/test_helper.exs old mode 100644 new mode 100755 diff --git a/elixir/test/testdata/attachments.feature.ndjson b/elixir/test/testdata/attachments.feature.ndjson new file mode 100644 index 00000000..a91c9a6d --- /dev/null +++ b/elixir/test/testdata/attachments.feature.ndjson @@ -0,0 +1,77 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Attachments\n It is sometimes useful to take a screenshot while a scenario runs.\n Or capture some logs.\n\n Cucumber lets you `attach` arbitrary files during execution, and you can\n specify a content type for the contents.\n\n Formatters can then render these attachments in reports.\n\n Attachments must have a body and a content type\n\n Scenario: Strings can be attached with a media type\n Beware that some formatters such as @cucumber/react use the media type\n to determine how to display an attachment.\n\n When the string \"hello\" is attached as \"application/octet-stream\"\n\n Scenario: Log JSON\n When the following string is attached as \"application/json\":\n ```\n {\"message\": \"The big question\", \"foo\": \"bar\"}\n ```\n\n Scenario: Log text\n When the string \"hello\" is logged\n\n Scenario: Log ANSI coloured text\n When text with ANSI escapes is logged\n\n Scenario: Byte arrays are base64-encoded regardless of media type\n When an array with 10 bytes is attached as \"text/plain\"\n\n Scenario: Streams are always base64-encoded\n When a JPEG image is attached\n\n Scenario: Attaching images in examples\n When the png is attached\n\n Examples:\n | image |\n | cucumber.png |\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/attachments/attachments.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":" Beware that some formatters such as @cucumber/react use the media type\n to determine how to display an attachment.","examples":[],"id":"10","keyword":"Scenario","location":{"column":3,"line":12},"name":"Strings can be attached with a media type","steps":[{"id":"9","keyword":"When ","keywordType":"Action","location":{"column":5,"line":16},"text":"the string \"hello\" is attached as \"application/octet-stream\""}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"12","keyword":"Scenario","location":{"column":3,"line":18},"name":"Log JSON","steps":[{"docString":{"content":"{\"message\": \"The big question\", \"foo\": \"bar\"}","delimiter":"```","location":{"column":8,"line":20}},"id":"11","keyword":"When ","keywordType":"Action","location":{"column":6,"line":19},"text":"the following string is attached as \"application/json\":"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"14","keyword":"Scenario","location":{"column":3,"line":24},"name":"Log text","steps":[{"id":"13","keyword":"When ","keywordType":"Action","location":{"column":5,"line":25},"text":"the string \"hello\" is logged"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"16","keyword":"Scenario","location":{"column":3,"line":27},"name":"Log ANSI coloured text","steps":[{"id":"15","keyword":"When ","keywordType":"Action","location":{"column":6,"line":28},"text":"text with ANSI escapes is logged"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"18","keyword":"Scenario","location":{"column":3,"line":30},"name":"Byte arrays are base64-encoded regardless of media type","steps":[{"id":"17","keyword":"When ","keywordType":"Action","location":{"column":5,"line":31},"text":"an array with 10 bytes is attached as \"text/plain\""}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"20","keyword":"Scenario","location":{"column":3,"line":33},"name":"Streams are always base64-encoded","steps":[{"id":"19","keyword":"When ","keywordType":"Action","location":{"column":5,"line":34},"text":"a JPEG image is attached"}],"tags":[]}},{"scenario":{"description":"","examples":[{"description":"","id":"24","keyword":"Examples","location":{"column":5,"line":39},"name":"","tableBody":[{"cells":[{"location":{"column":9,"line":41},"value":"cucumber.png"}],"id":"23","location":{"column":7,"line":41}}],"tableHeader":{"cells":[{"location":{"column":9,"line":40},"value":"image"}],"id":"22","location":{"column":7,"line":40}},"tags":[]}],"id":"25","keyword":"Scenario","location":{"column":3,"line":36},"name":"Attaching images in examples","steps":[{"id":"21","keyword":"When ","keywordType":"Action","location":{"column":5,"line":37},"text":"the png is attached"}],"tags":[]}}],"description":" It is sometimes useful to take a screenshot while a scenario runs.\n Or capture some logs.\n\n Cucumber lets you `attach` arbitrary files during execution, and you can\n specify a content type for the contents.\n\n Formatters can then render these attachments in reports.\n\n Attachments must have a body and a content type","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Attachments","tags":[]},"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["10"],"id":"27","language":"en","name":"Strings can be attached with a media type","steps":[{"astNodeIds":["9"],"id":"26","text":"the string \"hello\" is attached as \"application/octet-stream\"","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["12"],"id":"29","language":"en","name":"Log JSON","steps":[{"argument":{"docString":{"content":"{\"message\": \"The big question\", \"foo\": \"bar\"}"}},"astNodeIds":["11"],"id":"28","text":"the following string is attached as \"application/json\":","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["14"],"id":"31","language":"en","name":"Log text","steps":[{"astNodeIds":["13"],"id":"30","text":"the string \"hello\" is logged","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["16"],"id":"33","language":"en","name":"Log ANSI coloured text","steps":[{"astNodeIds":["15"],"id":"32","text":"text with ANSI escapes is logged","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["18"],"id":"35","language":"en","name":"Byte arrays are base64-encoded regardless of media type","steps":[{"astNodeIds":["17"],"id":"34","text":"an array with 10 bytes is attached as \"text/plain\"","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["20"],"id":"37","language":"en","name":"Streams are always base64-encoded","steps":[{"astNodeIds":["19"],"id":"36","text":"a JPEG image is attached","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"pickle":{"astNodeIds":["25","23"],"id":"39","language":"en","name":"Attaching images in examples","steps":[{"astNodeIds":["21","23"],"id":"38","text":"the cucumber.png png is attached","type":"Action"}],"tags":[],"uri":"samples/attachments/attachments.feature"}} +{"stepDefinition":{"id":"1","pattern":{"source":"the string {string} is attached as {string}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"the string {string} is logged","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"text with ANSI escapes is logged","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"4","pattern":{"source":"the following string is attached as {string}:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":22},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"5","pattern":{"source":"an array with {int} bytes is attached as {string}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":26},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"6","pattern":{"source":"a stream with {int} bytes are attached as {string}","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":35},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"7","pattern":{"source":"a JPEG image is attached","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":48},"uri":"samples/attachments/attachments.feature.ts"}}} +{"stepDefinition":{"id":"8","pattern":{"source":"the {word} png is attached","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":52},"uri":"samples/attachments/attachments.feature.ts"}}} +{"hook":{"id":"0","sourceReference":{"location":{"line":6},"uri":"samples/attachments/attachments.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"42","pickleId":"27","testSteps":[{"hookId":"0","id":"40"},{"id":"41","pickleStepId":"26","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[{"children":[]}],"start":12,"value":"hello"},{"children":[{"children":[]}]}],"start":11,"value":"\"hello\""},"parameterTypeName":"string"},{"group":{"children":[{"children":[{"children":[]}],"start":35,"value":"application/octet-stream"},{"children":[{"children":[]}]}],"start":34,"value":"\"application/octet-stream\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"45","pickleId":"29","testSteps":[{"hookId":"0","id":"43"},{"id":"44","pickleStepId":"28","stepDefinitionIds":["4"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[{"children":[]}],"start":37,"value":"application/json"},{"children":[{"children":[]}]}],"start":36,"value":"\"application/json\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"48","pickleId":"31","testSteps":[{"hookId":"0","id":"46"},{"id":"47","pickleStepId":"30","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[{"children":[]}],"start":12,"value":"hello"},{"children":[{"children":[]}]}],"start":11,"value":"\"hello\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"51","pickleId":"33","testSteps":[{"hookId":"0","id":"49"},{"id":"50","pickleStepId":"32","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"54","pickleId":"35","testSteps":[{"hookId":"0","id":"52"},{"id":"53","pickleStepId":"34","stepDefinitionIds":["5"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"10"},"parameterTypeName":"int"},{"group":{"children":[{"children":[{"children":[]}],"start":39,"value":"text/plain"},{"children":[{"children":[]}]}],"start":38,"value":"\"text/plain\""},"parameterTypeName":"string"}]}]}]}} +{"testCase":{"id":"57","pickleId":"37","testSteps":[{"hookId":"0","id":"55"},{"id":"56","pickleStepId":"36","stepDefinitionIds":["7"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"60","pickleId":"39","testSteps":[{"hookId":"0","id":"58"},{"id":"59","pickleStepId":"38","stepDefinitionIds":["8"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":4,"value":"cucumber.png"},"parameterTypeName":"word"}]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"61","testCaseId":"42","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"61","testStepId":"40","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"61","testStepId":"40","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"61","testStepId":"41","timestamp":{"nanos":4000000,"seconds":0}}} +{"attachment":{"body":"hello","contentEncoding":"IDENTITY","mediaType":"application/octet-stream","testCaseStartedId":"61","testStepId":"41"}} +{"testStepFinished":{"testCaseStartedId":"61","testStepId":"41","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"61","timestamp":{"nanos":6000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"62","testCaseId":"45","timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"62","testStepId":"43","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"62","testStepId":"43","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"62","testStepId":"44","timestamp":{"nanos":10000000,"seconds":0}}} +{"attachment":{"body":"{\"message\": \"The big question\", \"foo\": \"bar\"}","contentEncoding":"IDENTITY","mediaType":"application/json","testCaseStartedId":"62","testStepId":"44"}} +{"testStepFinished":{"testCaseStartedId":"62","testStepId":"44","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"62","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"63","testCaseId":"48","timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"63","testStepId":"46","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"63","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"63","testStepId":"47","timestamp":{"nanos":16000000,"seconds":0}}} +{"attachment":{"body":"hello","contentEncoding":"IDENTITY","mediaType":"text/x.cucumber.log+plain","testCaseStartedId":"63","testStepId":"47"}} +{"testStepFinished":{"testCaseStartedId":"63","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"63","timestamp":{"nanos":18000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"64","testCaseId":"51","timestamp":{"nanos":19000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"64","testStepId":"49","timestamp":{"nanos":20000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"64","testStepId":"49","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"64","testStepId":"50","timestamp":{"nanos":22000000,"seconds":0}}} +{"attachment":{"body":"This displays a \u001b[31mr\u001b[0m\u001b[91ma\u001b[0m\u001b[33mi\u001b[0m\u001b[32mn\u001b[0m\u001b[34mb\u001b[0m\u001b[95mo\u001b[0m\u001b[35mw\u001b[0m","contentEncoding":"IDENTITY","mediaType":"text/x.cucumber.log+plain","testCaseStartedId":"64","testStepId":"50"}} +{"testStepFinished":{"testCaseStartedId":"64","testStepId":"50","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"64","timestamp":{"nanos":24000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"65","testCaseId":"54","timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"65","testStepId":"52","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"65","testStepId":"52","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"65","testStepId":"53","timestamp":{"nanos":28000000,"seconds":0}}} +{"attachment":{"body":"AAECAwQFBgcICQ==","contentEncoding":"BASE64","mediaType":"text/plain","testCaseStartedId":"65","testStepId":"53"}} +{"testStepFinished":{"testCaseStartedId":"65","testStepId":"53","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":29000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"65","timestamp":{"nanos":30000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"66","testCaseId":"57","timestamp":{"nanos":31000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"66","testStepId":"55","timestamp":{"nanos":32000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"66","testStepId":"55","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"66","testStepId":"56","timestamp":{"nanos":34000000,"seconds":0}}} +{"attachment":{"body":"iVBORw0KGgoAAAANSUhEUgAAACkAAAAuCAYAAAC1ZTBOAAAABmJLR0QA/wD/AP+gvaeTAAAGgElEQVRYw81ZeWwUVRgfNF4xalDo7Oy92yYmEkm0nZ22olYtM7Pbbu8t24Ntl960Eo0HRCsW5BCIRLyDQK0pFqt/iCdVPIISQvEIVSxg4h8mEhPEqNE/jNLn972dmd1Ztruz3W11kpftdue995vv+H2/7w3DzPBatChwKcvLd7GCvJn1SG+YPNIp+PwFxm8wzrO89CPrEY/A36/keKRuc4F8PTNX18IC700AaAg2/x0GSXN8B8AfNuf7F8wKuBxBXgybHIzdlKvxE2v/MmLf00Kc77QT16ddxH2sh346320nzn1hYtvcSMyhKsIukWPB/sny4iZ2sXhlVsBZiwJXmHh5Gyz8N25gKvES29ogcX3USXJP9RkfE73EMRgiXF1FLNjTbKEoZATwuqJyC+uRj1FwhTKxPrKM5H7Zkx64+HGyjzj2honJV64ChYcX7565e3npDAVY6Seu9zoyAxc33F+tJNZ766JW5eX+9JKjSMpjBfEnnGxpq6ELZhNg7LBta9SAmjzyA4YAssViDkz4ngLsqSW5J3pnDaAGdEeTCvSfHGGpmBokL+3HCebmSpL7zewDVId1Tb0K9NxC3meaHqBHbqNmLy2jVDJXAOkAj3HBCsXt0lBCgAtuqbiKFaSzeJMD+M1Q8E8CrewKEfvzy0nu1xda3THcQiz3B4hjqMXQeq6xDgIYEOhUDi8WJ3Cz3E/jsL3auIse0lwUmXcy+ptzf5uu2jjfakvX7W/rAObleS+DJziHP7oOtBsGyVX79UBGV2i/mcNVut+wKhmy5mddqjXPI8tEOdEjVtFkgfKVVrCvrtcBQdeq1YUtjKnZ8DdubnRdS1cNnQfCZEtMwkij9GlfWJ4eIUNymcSyaC2vr4hY41CnDjyW0XTWdQy3qnNPqBjnwZezaGL3eHfScmZ/uplYVtUS26YG4j4Sudf9cSfh/OU6kFg6FZcRy31g3cn0q5GpKCJIuGKfI1JdMO2r/MmfbqRVL7tA1WiWh8y2P9VM7M9GPWF7vIE4Xw3PmJLMzZGYhixvYkyCWEefuK826SQM/EQa0fFiaHbIXYl3KJUDAFLqxS/W9cGUZIuJobpRq7e3ezNXRomMsl0tlfIwZvajNGmeaDJMuLYNDcRyT4Bymn13iGZz1kEqnoPqcwAzeyMFCTE1p2UwVYYPKuHFS+8zgHQ1pYmtjcYy72g3LXOYNOgSfGL38eRSzvVhJ00q9Jb9mWbi/iS1qne8pOXAQQY7ORqT0KsknQg0YtvYQNhiWZ888D0ZdbkhXjFudXOA3DExkslApDvqbl56naFtqYGa7Xi5NWF2ozU1QN8m3hStnpAZdk3PDNZ1QTVxtjP2JWXzUXWY7vTpBEJKCoIst22JhggmECf5aLWhAgOUFH0ARZOisFUJWgM5OH09x45AKY3dalk8TQXC2PR9DFoJVQ9XX0ksvXW0ZdWIG8NA2zhiHbNSf81Qhdyfr1TKZRdt5hAAVq1pKxH8n73DF5lfKN2sCoytNHlgs7SzcCSckNy5Cq0bJOaW6qReih9oAGXur0x+/iUUJCeI+bROgrvS7WkukGtvRnQjWlAH/rUVxqvNeiUeeXFE38Ly0hc0EXaG0lJBuuoDca0mD7pVp4QGgobVvqqscgSpVq/MBaky0t/4DJc5umC0ySe2J6MFwX24i5hujVJPrPhIGj5DWoKe0Vwdc6FkG6ec+WDAsDUxGdBKtM+JSwRU+bbHgoZ7HJzPVflVK65N3C0W+W6EG/5CejHajGW1Xj+n8enP1wreq5P03eIaVS8abZ6ycuwyDvFd4lWPXFalOB4YuAhu3EtvBq7CujvrICej5A1ePMoEAhcbO8UVpA/Uoz7n6Oy6HoldcfMfJsF7g+FDK2dJyeUAdJ9WAqGZck9k/+AK67cqpGmrMINrHqiQdXiQRK0ql0V4NEuHWFQPRJX+howOUznP0gJY5LhG2kC2qFJcY+1pd4Kai4FTtd5ckHaiQTI/lwZihX4oDAtO6qoMJJe5o4bkGjzDxJChvZK2BkixrACMy35Q82Ug6/fQfl3ZTO3DkwoHOPzHU2PtGDo11WThAqqg5J8CJCp32qJGj15+4Hjxtjl7r5MMJNZvZIWY1yNTMHbPzy+9hpnLKx4k9jSYteaOav2hlUc6nPHrkExBojvNTZXxLcIU9s0Qv6XMf3mpIHWDFydQxcD7GRfzf7hQ90GzdAheqeyAzxC+oMr2Hv8Cf7uNwHUHEgMAAAAASUVORK5CYII=","contentEncoding":"BASE64","mediaType":"image/png","testCaseStartedId":"66","testStepId":"56"}} +{"testStepFinished":{"testCaseStartedId":"66","testStepId":"56","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"66","timestamp":{"nanos":36000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"67","testCaseId":"60","timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"67","testStepId":"58","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"67","testStepId":"58","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"67","testStepId":"59","timestamp":{"nanos":40000000,"seconds":0}}} +{"attachment":{"body":"iVBORw0KGgoAAAANSUhEUgAAACkAAAAuCAYAAAC1ZTBOAAAABmJLR0QA/wD/AP+gvaeTAAAGgElEQVRYw81ZeWwUVRgfNF4xalDo7Oy92yYmEkm0nZ22olYtM7Pbbu8t24Ntl960Eo0HRCsW5BCIRLyDQK0pFqt/iCdVPIISQvEIVSxg4h8mEhPEqNE/jNLn972dmd1Ztruz3W11kpftdue995vv+H2/7w3DzPBatChwKcvLd7GCvJn1SG+YPNIp+PwFxm8wzrO89CPrEY/A36/keKRuc4F8PTNX18IC700AaAg2/x0GSXN8B8AfNuf7F8wKuBxBXgybHIzdlKvxE2v/MmLf00Kc77QT16ddxH2sh346320nzn1hYtvcSMyhKsIukWPB/sny4iZ2sXhlVsBZiwJXmHh5Gyz8N25gKvES29ogcX3USXJP9RkfE73EMRgiXF1FLNjTbKEoZATwuqJyC+uRj1FwhTKxPrKM5H7Zkx64+HGyjzj2honJV64ChYcX7565e3npDAVY6Seu9zoyAxc33F+tJNZ766JW5eX+9JKjSMpjBfEnnGxpq6ELZhNg7LBta9SAmjzyA4YAssViDkz4ngLsqSW5J3pnDaAGdEeTCvSfHGGpmBokL+3HCebmSpL7zewDVId1Tb0K9NxC3meaHqBHbqNmLy2jVDJXAOkAj3HBCsXt0lBCgAtuqbiKFaSzeJMD+M1Q8E8CrewKEfvzy0nu1xda3THcQiz3B4hjqMXQeq6xDgIYEOhUDi8WJ3Cz3E/jsL3auIse0lwUmXcy+ptzf5uu2jjfakvX7W/rAObleS+DJziHP7oOtBsGyVX79UBGV2i/mcNVut+wKhmy5mddqjXPI8tEOdEjVtFkgfKVVrCvrtcBQdeq1YUtjKnZ8DdubnRdS1cNnQfCZEtMwkij9GlfWJ4eIUNymcSyaC2vr4hY41CnDjyW0XTWdQy3qnNPqBjnwZezaGL3eHfScmZ/uplYVtUS26YG4j4Sudf9cSfh/OU6kFg6FZcRy31g3cn0q5GpKCJIuGKfI1JdMO2r/MmfbqRVL7tA1WiWh8y2P9VM7M9GPWF7vIE4Xw3PmJLMzZGYhixvYkyCWEefuK826SQM/EQa0fFiaHbIXYl3KJUDAFLqxS/W9cGUZIuJobpRq7e3ezNXRomMsl0tlfIwZvajNGmeaDJMuLYNDcRyT4Bymn13iGZz1kEqnoPqcwAzeyMFCTE1p2UwVYYPKuHFS+8zgHQ1pYmtjcYy72g3LXOYNOgSfGL38eRSzvVhJ00q9Jb9mWbi/iS1qne8pOXAQQY7ORqT0KsknQg0YtvYQNhiWZ888D0ZdbkhXjFudXOA3DExkslApDvqbl56naFtqYGa7Xi5NWF2ozU1QN8m3hStnpAZdk3PDNZ1QTVxtjP2JWXzUXWY7vTpBEJKCoIst22JhggmECf5aLWhAgOUFH0ARZOisFUJWgM5OH09x45AKY3dalk8TQXC2PR9DFoJVQ9XX0ksvXW0ZdWIG8NA2zhiHbNSf81Qhdyfr1TKZRdt5hAAVq1pKxH8n73DF5lfKN2sCoytNHlgs7SzcCSckNy5Cq0bJOaW6qReih9oAGXur0x+/iUUJCeI+bROgrvS7WkukGtvRnQjWlAH/rUVxqvNeiUeeXFE38Ly0hc0EXaG0lJBuuoDca0mD7pVp4QGgobVvqqscgSpVq/MBaky0t/4DJc5umC0ySe2J6MFwX24i5hujVJPrPhIGj5DWoKe0Vwdc6FkG6ec+WDAsDUxGdBKtM+JSwRU+bbHgoZ7HJzPVflVK65N3C0W+W6EG/5CejHajGW1Xj+n8enP1wreq5P03eIaVS8abZ6ycuwyDvFd4lWPXFalOB4YuAhu3EtvBq7CujvrICej5A1ePMoEAhcbO8UVpA/Uoz7n6Oy6HoldcfMfJsF7g+FDK2dJyeUAdJ9WAqGZck9k/+AK67cqpGmrMINrHqiQdXiQRK0ql0V4NEuHWFQPRJX+howOUznP0gJY5LhG2kC2qFJcY+1pd4Kai4FTtd5ckHaiQTI/lwZihX4oDAtO6qoMJJe5o4bkGjzDxJChvZK2BkixrACMy35Q82Ug6/fQfl3ZTO3DkwoHOPzHU2PtGDo11WThAqqg5J8CJCp32qJGj15+4Hjxtjl7r5MMJNZvZIWY1yNTMHbPzy+9hpnLKx4k9jSYteaOav2hlUc6nPHrkExBojvNTZXxLcIU9s0Qv6XMf3mpIHWDFydQxcD7GRfzf7hQ90GzdAheqeyAzxC+oMr2Hv8Cf7uNwHUHEgMAAAAASUVORK5CYII=","contentEncoding":"BASE64","mediaType":"image/png","testCaseStartedId":"67","testStepId":"59"}} +{"testStepFinished":{"testCaseStartedId":"67","testStepId":"59","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":41000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"67","timestamp":{"nanos":42000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":43000000,"seconds":0}}} diff --git a/elixir/test/testdata/data-tables.feature.ndjson b/elixir/test/testdata/data-tables.feature.ndjson new file mode 100644 index 00000000..9c50cecf --- /dev/null +++ b/elixir/test/testdata/data-tables.feature.ndjson @@ -0,0 +1,15 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Data Tables\n Data Tables can be places underneath a step and will be passed as the last\n argument to the step definition. They can be used to represent richer data\n structures, and can also be transformed to other types.\n\n Scenario: transposed table\n When the following table is transposed:\n | a | b |\n | 1 | 2 |\n Then it should be:\n | a | 1 |\n | b | 2 |\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/data-tables/data-tables.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"8","keyword":"Scenario","location":{"column":3,"line":6},"name":"transposed table","steps":[{"dataTable":{"location":{"column":7,"line":8},"rows":[{"cells":[{"location":{"column":9,"line":8},"value":"a"},{"location":{"column":13,"line":8},"value":"b"}],"id":"2","location":{"column":7,"line":8}},{"cells":[{"location":{"column":9,"line":9},"value":"1"},{"location":{"column":13,"line":9},"value":"2"}],"id":"3","location":{"column":7,"line":9}}]},"id":"4","keyword":"When ","keywordType":"Action","location":{"column":5,"line":7},"text":"the following table is transposed:"},{"dataTable":{"location":{"column":7,"line":11},"rows":[{"cells":[{"location":{"column":9,"line":11},"value":"a"},{"location":{"column":13,"line":11},"value":"1"}],"id":"5","location":{"column":7,"line":11}},{"cells":[{"location":{"column":9,"line":12},"value":"b"},{"location":{"column":13,"line":12},"value":"2"}],"id":"6","location":{"column":7,"line":12}}]},"id":"7","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":10},"text":"it should be:"}],"tags":[]}}],"description":" Data Tables can be places underneath a step and will be passed as the last\n argument to the step definition. They can be used to represent richer data\n structures, and can also be transformed to other types.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Data Tables","tags":[]},"uri":"samples/data-tables/data-tables.feature"}} +{"pickle":{"astNodeIds":["8"],"id":"11","language":"en","name":"transposed table","steps":[{"argument":{"dataTable":{"rows":[{"cells":[{"value":"a"},{"value":"b"}]},{"cells":[{"value":"1"},{"value":"2"}]}]}},"astNodeIds":["4"],"id":"9","text":"the following table is transposed:","type":"Action"},{"argument":{"dataTable":{"rows":[{"cells":[{"value":"a"},{"value":"1"}]},{"cells":[{"value":"b"},{"value":"2"}]}]}},"astNodeIds":["7"],"id":"10","text":"it should be:","type":"Outcome"}],"tags":[],"uri":"samples/data-tables/data-tables.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"the following table is transposed:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":5},"uri":"samples/data-tables/data-tables.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"it should be:","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":9},"uri":"samples/data-tables/data-tables.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"14","pickleId":"11","testSteps":[{"id":"12","pickleStepId":"9","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"13","pickleStepId":"10","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"15","testCaseId":"14","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"15","testStepId":"12","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"15","testStepId":"12","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"15","testStepId":"13","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"15","testStepId":"13","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"15","timestamp":{"nanos":6000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":7000000,"seconds":0}}} diff --git a/elixir/test/testdata/examples-tables.feature.ndjson b/elixir/test/testdata/examples-tables.feature.ndjson new file mode 100644 index 00000000..ef694ade --- /dev/null +++ b/elixir/test/testdata/examples-tables.feature.ndjson @@ -0,0 +1,68 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Examples Tables\n Sometimes it can be desireable to run the same scenario multiple times\n with different data each time. This can be done by placing an Examples\n section with an Examples Table underneath a Scenario, and use \n in the Scenario, matching the table headers.\n\n Scenario Outline: eating cucumbers\n Given there are cucumbers\n When I eat cucumbers\n Then I should have cucumbers\n\n @passing\n Examples: These are passing\n | start | eat | left |\n | 12 | 5 | 7 |\n | 20 | 5 | 15 |\n\n @failing\n Examples: These are failing\n | start | eat | left |\n | 12 | 20 | 0 |\n | 0 | 1 | 0 |\n\n @undefined\n Examples: These are undefined because the value is not an {int}\n | start | eat | left |\n | 12 | banana | 12 |\n | 0 | 1 | apple |\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/examples-tables/examples-tables.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[{"description":"","id":"10","keyword":"Examples","location":{"column":5,"line":13},"name":"These are passing","tableBody":[{"cells":[{"location":{"column":12,"line":15},"value":"12"},{"location":{"column":19,"line":15},"value":"5"},{"location":{"column":26,"line":15},"value":"7"}],"id":"7","location":{"column":7,"line":15}},{"cells":[{"location":{"column":12,"line":16},"value":"20"},{"location":{"column":19,"line":16},"value":"5"},{"location":{"column":25,"line":16},"value":"15"}],"id":"8","location":{"column":7,"line":16}}],"tableHeader":{"cells":[{"location":{"column":9,"line":14},"value":"start"},{"location":{"column":17,"line":14},"value":"eat"},{"location":{"column":23,"line":14},"value":"left"}],"id":"6","location":{"column":7,"line":14}},"tags":[{"id":"9","location":{"column":5,"line":12},"name":"@passing"}]},{"description":"","id":"15","keyword":"Examples","location":{"column":5,"line":19},"name":"These are failing","tableBody":[{"cells":[{"location":{"column":12,"line":21},"value":"12"},{"location":{"column":18,"line":21},"value":"20"},{"location":{"column":26,"line":21},"value":"0"}],"id":"12","location":{"column":7,"line":21}},{"cells":[{"location":{"column":13,"line":22},"value":"0"},{"location":{"column":19,"line":22},"value":"1"},{"location":{"column":26,"line":22},"value":"0"}],"id":"13","location":{"column":7,"line":22}}],"tableHeader":{"cells":[{"location":{"column":9,"line":20},"value":"start"},{"location":{"column":17,"line":20},"value":"eat"},{"location":{"column":23,"line":20},"value":"left"}],"id":"11","location":{"column":7,"line":20}},"tags":[{"id":"14","location":{"column":5,"line":18},"name":"@failing"}]},{"description":"","id":"20","keyword":"Examples","location":{"column":5,"line":25},"name":"These are undefined because the value is not an {int}","tableBody":[{"cells":[{"location":{"column":12,"line":27},"value":"12"},{"location":{"column":17,"line":27},"value":"banana"},{"location":{"column":29,"line":27},"value":"12"}],"id":"17","location":{"column":7,"line":27}},{"cells":[{"location":{"column":13,"line":28},"value":"0"},{"location":{"column":22,"line":28},"value":"1"},{"location":{"column":26,"line":28},"value":"apple"}],"id":"18","location":{"column":7,"line":28}}],"tableHeader":{"cells":[{"location":{"column":9,"line":26},"value":"start"},{"location":{"column":17,"line":26},"value":"eat"},{"location":{"column":26,"line":26},"value":"left"}],"id":"16","location":{"column":7,"line":26}},"tags":[{"id":"19","location":{"column":5,"line":24},"name":"@undefined"}]}],"id":"21","keyword":"Scenario Outline","location":{"column":3,"line":7},"name":"eating cucumbers","steps":[{"id":"3","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":8},"text":"there are cucumbers"},{"id":"4","keyword":"When ","keywordType":"Action","location":{"column":5,"line":9},"text":"I eat cucumbers"},{"id":"5","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":10},"text":"I should have cucumbers"}],"tags":[]}}],"description":" Sometimes it can be desireable to run the same scenario multiple times\n with different data each time. This can be done by placing an Examples\n section with an Examples Table underneath a Scenario, and use \n in the Scenario, matching the table headers.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Examples Tables","tags":[]},"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","7"],"id":"25","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","7"],"id":"22","text":"there are 12 cucumbers","type":"Context"},{"astNodeIds":["4","7"],"id":"23","text":"I eat 5 cucumbers","type":"Action"},{"astNodeIds":["5","7"],"id":"24","text":"I should have 7 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"9","name":"@passing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","8"],"id":"29","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","8"],"id":"26","text":"there are 20 cucumbers","type":"Context"},{"astNodeIds":["4","8"],"id":"27","text":"I eat 5 cucumbers","type":"Action"},{"astNodeIds":["5","8"],"id":"28","text":"I should have 15 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"9","name":"@passing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","12"],"id":"33","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","12"],"id":"30","text":"there are 12 cucumbers","type":"Context"},{"astNodeIds":["4","12"],"id":"31","text":"I eat 20 cucumbers","type":"Action"},{"astNodeIds":["5","12"],"id":"32","text":"I should have 0 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"14","name":"@failing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","13"],"id":"37","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","13"],"id":"34","text":"there are 0 cucumbers","type":"Context"},{"astNodeIds":["4","13"],"id":"35","text":"I eat 1 cucumbers","type":"Action"},{"astNodeIds":["5","13"],"id":"36","text":"I should have 0 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"14","name":"@failing"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","17"],"id":"41","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","17"],"id":"38","text":"there are 12 cucumbers","type":"Context"},{"astNodeIds":["4","17"],"id":"39","text":"I eat banana cucumbers","type":"Action"},{"astNodeIds":["5","17"],"id":"40","text":"I should have 12 cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"19","name":"@undefined"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"pickle":{"astNodeIds":["21","18"],"id":"45","language":"en","name":"eating cucumbers","steps":[{"astNodeIds":["3","18"],"id":"42","text":"there are 0 cucumbers","type":"Context"},{"astNodeIds":["4","18"],"id":"43","text":"I eat 1 cucumbers","type":"Action"},{"astNodeIds":["5","18"],"id":"44","text":"I should have apple cucumbers","type":"Outcome"}],"tags":[{"astNodeId":"19","name":"@undefined"}],"uri":"samples/examples-tables/examples-tables.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"there are {int} cucumbers","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/examples-tables/examples-tables.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"I eat {int} cucumbers","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/examples-tables/examples-tables.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"I should have {int} cucumbers","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/examples-tables/examples-tables.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"49","pickleId":"25","testSteps":[{"id":"46","pickleStepId":"22","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"12"},"parameterTypeName":"int"}]}]},{"id":"47","pickleStepId":"23","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"5"},"parameterTypeName":"int"}]}]},{"id":"48","pickleStepId":"24","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"7"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"53","pickleId":"29","testSteps":[{"id":"50","pickleStepId":"26","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"20"},"parameterTypeName":"int"}]}]},{"id":"51","pickleStepId":"27","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"5"},"parameterTypeName":"int"}]}]},{"id":"52","pickleStepId":"28","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"15"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"57","pickleId":"33","testSteps":[{"id":"54","pickleStepId":"30","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"12"},"parameterTypeName":"int"}]}]},{"id":"55","pickleStepId":"31","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"20"},"parameterTypeName":"int"}]}]},{"id":"56","pickleStepId":"32","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"0"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"61","pickleId":"37","testSteps":[{"id":"58","pickleStepId":"34","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"0"},"parameterTypeName":"int"}]}]},{"id":"59","pickleStepId":"35","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"1"},"parameterTypeName":"int"}]}]},{"id":"60","pickleStepId":"36","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"0"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"65","pickleId":"41","testSteps":[{"id":"62","pickleStepId":"38","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"12"},"parameterTypeName":"int"}]}]},{"id":"63","pickleStepId":"39","stepDefinitionIds":[],"stepMatchArgumentsLists":[]},{"id":"64","pickleStepId":"40","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":14,"value":"12"},"parameterTypeName":"int"}]}]}]}} +{"testCase":{"id":"69","pickleId":"45","testSteps":[{"id":"66","pickleStepId":"42","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"0"},"parameterTypeName":"int"}]}]},{"id":"67","pickleStepId":"43","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":6,"value":"1"},"parameterTypeName":"int"}]}]},{"id":"68","pickleStepId":"44","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCaseStarted":{"attempt":0,"id":"70","testCaseId":"49","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"46","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"47","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"70","testStepId":"48","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"70","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"70","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"71","testCaseId":"53","timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"50","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"50","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"51","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"51","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"71","testStepId":"52","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"71","testStepId":"52","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"71","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"72","testCaseId":"57","timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"54","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"54","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"55","timestamp":{"nanos":20000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"55","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"72","testStepId":"56","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"72","testStepId":"56","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Expected values to be strictly equal:\n\n-8 !== 0\n\nsamples/examples-tables/examples-tables.feature:10\nsamples/examples-tables/examples-tables.feature:21","status":"FAILED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"72","timestamp":{"nanos":24000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"73","testCaseId":"61","timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"58","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"58","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"59","timestamp":{"nanos":28000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"59","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":29000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"73","testStepId":"60","timestamp":{"nanos":30000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"73","testStepId":"60","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Expected values to be strictly equal:\n\n-1 !== 0\n\nsamples/examples-tables/examples-tables.feature:10\nsamples/examples-tables/examples-tables.feature:22","status":"FAILED"},"timestamp":{"nanos":31000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"73","timestamp":{"nanos":32000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"74","testCaseId":"65","timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"62","timestamp":{"nanos":34000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"62","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"63","timestamp":{"nanos":36000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"63","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"74","testStepId":"64","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"74","testStepId":"64","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"74","timestamp":{"nanos":40000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"75","testCaseId":"69","timestamp":{"nanos":41000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"66","timestamp":{"nanos":42000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"66","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":43000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"67","timestamp":{"nanos":44000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"67","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":45000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"75","testStepId":"68","timestamp":{"nanos":46000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"75","testStepId":"68","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":47000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"75","timestamp":{"nanos":48000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":49000000,"seconds":0}}} diff --git a/elixir/test/testdata/hooks.feature.ndjson b/elixir/test/testdata/hooks.feature.ndjson new file mode 100644 index 00000000..59746bb1 --- /dev/null +++ b/elixir/test/testdata/hooks.feature.ndjson @@ -0,0 +1,77 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Hooks\n Hooks are special steps that run before or after each scenario's steps.\n They can also conditionally target specific scenarios, using tag expressions\n\n Scenario: no tags, passed step\n When a step passes\n\n Scenario: no tags, failed step\n When a step throws an exception\n\n Scenario: no tags, undefined step\n When a step throws an exception\n\n @some-tag\n Scenario: with a tag, passed step\n When a step passes\n\n @with-attachment\n Scenario: with an attachment in the hook\n When a step passes","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/hooks/hooks.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"8","keyword":"Scenario","location":{"column":3,"line":5},"name":"no tags, passed step","steps":[{"id":"7","keyword":"When ","keywordType":"Action","location":{"column":5,"line":6},"text":"a step passes"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"10","keyword":"Scenario","location":{"column":3,"line":8},"name":"no tags, failed step","steps":[{"id":"9","keyword":"When ","keywordType":"Action","location":{"column":5,"line":9},"text":"a step throws an exception"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"12","keyword":"Scenario","location":{"column":3,"line":11},"name":"no tags, undefined step","steps":[{"id":"11","keyword":"When ","keywordType":"Action","location":{"column":5,"line":12},"text":"a step throws an exception"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"15","keyword":"Scenario","location":{"column":3,"line":15},"name":"with a tag, passed step","steps":[{"id":"13","keyword":"When ","keywordType":"Action","location":{"column":5,"line":16},"text":"a step passes"}],"tags":[{"id":"14","location":{"column":3,"line":14},"name":"@some-tag"}]}},{"scenario":{"description":"","examples":[],"id":"18","keyword":"Scenario","location":{"column":3,"line":19},"name":"with an attachment in the hook","steps":[{"id":"16","keyword":"When ","keywordType":"Action","location":{"column":5,"line":20},"text":"a step passes"}],"tags":[{"id":"17","location":{"column":3,"line":18},"name":"@with-attachment"}]}}],"description":" Hooks are special steps that run before or after each scenario's steps.\n They can also conditionally target specific scenarios, using tag expressions","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Hooks","tags":[]},"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["8"],"id":"20","language":"en","name":"no tags, passed step","steps":[{"astNodeIds":["7"],"id":"19","text":"a step passes","type":"Action"}],"tags":[],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["10"],"id":"22","language":"en","name":"no tags, failed step","steps":[{"astNodeIds":["9"],"id":"21","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["12"],"id":"24","language":"en","name":"no tags, undefined step","steps":[{"astNodeIds":["11"],"id":"23","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["15"],"id":"26","language":"en","name":"with a tag, passed step","steps":[{"astNodeIds":["13"],"id":"25","text":"a step passes","type":"Action"}],"tags":[{"astNodeId":"14","name":"@some-tag"}],"uri":"samples/hooks/hooks.feature"}} +{"pickle":{"astNodeIds":["18"],"id":"28","language":"en","name":"with an attachment in the hook","steps":[{"astNodeIds":["16"],"id":"27","text":"a step passes","type":"Action"}],"tags":[{"astNodeId":"17","name":"@with-attachment"}],"uri":"samples/hooks/hooks.feature"}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step passes","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":12},"uri":"samples/hooks/hooks.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"a step throws an exception","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"0","sourceReference":{"location":{"line":4},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"1","name":"A named hook","sourceReference":{"location":{"line":8},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"4","sourceReference":{"location":{"line":20},"uri":"samples/hooks/hooks.feature.ts"}}} +{"hook":{"id":"5","sourceReference":{"location":{"line":24},"uri":"samples/hooks/hooks.feature.ts"},"tagExpression":"@some-tag or @some-other-tag"}} +{"hook":{"id":"6","sourceReference":{"location":{"line":28},"uri":"samples/hooks/hooks.feature.ts"},"tagExpression":"@with-attachment"}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"33","pickleId":"20","testSteps":[{"hookId":"0","id":"29"},{"hookId":"1","id":"30"},{"id":"31","pickleStepId":"19","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"4","id":"32"}]}} +{"testCase":{"id":"38","pickleId":"22","testSteps":[{"hookId":"0","id":"34"},{"hookId":"1","id":"35"},{"id":"36","pickleStepId":"21","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"4","id":"37"}]}} +{"testCase":{"id":"43","pickleId":"24","testSteps":[{"hookId":"0","id":"39"},{"hookId":"1","id":"40"},{"id":"41","pickleStepId":"23","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"4","id":"42"}]}} +{"testCase":{"id":"49","pickleId":"26","testSteps":[{"hookId":"0","id":"44"},{"hookId":"1","id":"45"},{"id":"46","pickleStepId":"25","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"5","id":"47"},{"hookId":"4","id":"48"}]}} +{"testCase":{"id":"55","pickleId":"28","testSteps":[{"hookId":"0","id":"50"},{"hookId":"1","id":"51"},{"id":"52","pickleStepId":"27","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"hookId":"6","id":"53"},{"hookId":"4","id":"54"}]}} +{"testCaseStarted":{"attempt":0,"id":"56","testCaseId":"33","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"29","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"29","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"30","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"31","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"31","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"56","testStepId":"32","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"56","testStepId":"32","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in hook\nsamples/hooks/hooks.feature:5","status":"FAILED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"56","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"57","testCaseId":"38","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"34","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"34","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"35","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"35","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"36","timestamp":{"nanos":16000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"36","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/hooks/hooks.feature:9","status":"FAILED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"57","testStepId":"37","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"57","testStepId":"37","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in hook\nsamples/hooks/hooks.feature:8","status":"FAILED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"57","timestamp":{"nanos":20000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"58","testCaseId":"43","timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"39","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"39","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"40","timestamp":{"nanos":24000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"40","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"41","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"41","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/hooks/hooks.feature:12","status":"FAILED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"58","testStepId":"42","timestamp":{"nanos":28000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"58","testStepId":"42","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in hook\nsamples/hooks/hooks.feature:11","status":"FAILED"},"timestamp":{"nanos":29000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"58","timestamp":{"nanos":30000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"59","testCaseId":"49","timestamp":{"nanos":31000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"44","timestamp":{"nanos":32000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"44","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"45","timestamp":{"nanos":34000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"45","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"46","timestamp":{"nanos":36000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"47","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in conditional hook\nsamples/hooks/hooks.feature:15","status":"FAILED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"59","testStepId":"48","timestamp":{"nanos":40000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"59","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in hook\nsamples/hooks/hooks.feature:15","status":"FAILED"},"timestamp":{"nanos":41000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"59","timestamp":{"nanos":42000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"60","testCaseId":"55","timestamp":{"nanos":43000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"50","timestamp":{"nanos":44000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"50","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":45000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"51","timestamp":{"nanos":46000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"51","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":47000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"52","timestamp":{"nanos":48000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"52","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":49000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"53","timestamp":{"nanos":50000000,"seconds":0}}} +{"attachment":{"body":"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJtbC0zIG1sLW1kLTAiIHZpZXdCb3g9IjAgMCA0MC41OSA0Ni4zMSIgd2lkdGg9IjQwLjU5IiBoZWlnaHQ9IjQ2LjMxIj4KICAgIDxnPgogICAgICAgIDxwYXRoIGZpbGw9IiMyM2Q5NmMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTMwLjI4MyAzLjY0NXEtLjUyOC0uMzE3LTEuMDgtLjU5M2ExNi4xNjQgMTYuMTY0IDAgMDAtMS4xNTQtLjUxOGMtLjEyNC0uMDUyLS4yNDctLjEtLjM3Mi0uMTQ5LS4zNDMtLjEyNy0uNjg5LS4yNjgtMS4wNDItLjM3MWExOS40MjcgMTkuNDI3IDAgMTAtOS43OTIgMzcuNTF2NS41NmMxMS42NzYtMS43NTMgMjIuMDE2LTEwLjk3OSAyMi43ODctMjMuMDkzLjQ1OS03LjI4OS0zLjE5My0xNC43My05LjM0Ny0xOC4zNDZ6Ii8+CiAgICAgICAgPHBhdGggZmlsbD0iIzE3MzY0NyIgZD0iTTE1Ljc4NyA0Ni4zMDd2LTUuOTM1QTIwLjQ3MiAyMC40NzIgMCAxMTI2Ljk1OSAxLjAxNWMuMjc0LjA4LjU1Ny4xODcuODMyLjI5MWwuMjQ4LjA5M2MuMTY1LjA2NC4yOTEuMTEzLjQxNy4xNjcuMzQ4LjEzNy43MzkuMzEzIDEuMjA4LjU0M3EuNTg5LjI5NSAxLjE1My42MzNjNi4zOTMgMy43NTYgMTAuMzU0IDExLjUxOCA5Ljg1NyAxOS4zMTYtLjc2MyAxMi0xMC43MjIgMjIuMTIyLTIzLjY3OSAyNC4wNjd6bTQuOC00NC4yMTRoLS4wMjZhMTguMzY2IDE4LjM2NiAwIDAwLTMuNTI0IDM2LjQwOGwuODUuMTY1djUuMThjMTEuMzkyLTIuMjI0IDIwLjAwOS0xMS4yNzIgMjAuNjg2LTIxLjkyMi40NDgtNy4wMzMtMy4xLTE0LjAxOC04LjgzLTE3LjM4M2wtLjAwOC0uMDA1QTE0LjY5MSAxNC42OTEgMCAwMDI3LjY1NCAzLjVhNS43NCA1Ljc0IDAgMDAtLjM0NC0uMTM4bC0uMjctLjFhOS40OSA5LjQ5IDAgMDAtLjcwOC0uMjQ5IDE4LjQyNSAxOC40MjUgMCAwMC01Ljc0My0uOTJ6Ii8+CiAgICAgICAgPHBhdGggZmlsbD0iIzE3MzY0NyIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTYuNjY2IDEwLjU4YTEuOCAxLjggMCAwMTEuNTgzLjYwOCA0LjE4NCA0LjE4NCAwIDAxLjcyOCAxLjEwN2MuNjQ1IDEuNDIyIDEuMDI3IDMuNDYxLjIzIDQuNjA1YTYuMzM0IDYuMzM0IDAgMDEtMy45ODEtMy4wODcgMy4yMzYgMy4yMzYgMCAwMS0uMzQ3LTEuMzM5IDEuOTU3IDEuOTU3IDAgMDExLjc4Ny0xLjg5NHptLTUuNjgzIDguMDI1YTcuNzQyIDcuNzQyIDAgMDAxLjIxOC43MzcgNS43ODkgNS43ODkgMCAwMDQuODgzLS4xMzggNi4xMTYgNi4xMTYgMCAwMC0zLjM0NS0zLjQ1IDMuNjY0IDMuNjY0IDAgMDAtMS40NDItLjMyMSAxLjg4NCAxLjg4NCAwIDAwLS4zMTkgMCAxLjc2NiAxLjc2NiAwIDAwLS45OTUgMy4xNzJ6bTYuMSAzLjQzM2MtLjc3Ny0uNTE4LTIuMzc5LS4zMDktMy4zMTItLjI5MmE0LjQxNiA0LjQxNiAwIDAwLTEuNjY2LjM1MiAzLjUgMy41IDAgMDAtMS4yMTguNzM4IDEuODE3IDEuODE3IDAgMDAxLjQwOSAzLjE3MSAzLjMgMy4zIDAgMDAxLjQ0Mi0uMzIxYzEuNDM2LS42MiAzLjE0MS0yLjMyIDMuMzQ2LTMuNjQ4em0yLjYxIDJhNi41NTYgNi41NTYgMCAwMC0zLjcyNCAzLjUwNiAzLjA5MSAzLjA5MSAwIDAwLS4zMjEgMS4zMTQgMS45MDcgMS45MDcgMCAwMDMuMyAxLjM0NiA3LjQyMiA3LjQyMiAwIDAwLjctMS4yMThjLjYyMS0xLjMzMy44NjYtMy43Mi4wNDYtNC45NDh6bTIuNTU3LTcuMTY3YTUuOTQxIDUuOTQxIDAgMDAzLjctMy4xNjcgMy4yNDMgMy4yNDMgMCAwMC4zMTktMS4zNDYgMS45MTUgMS45MTUgMCAwMC0xLjc5NC0xLjk1NCAxLjgzMiAxLjgzMiAwIDAwLTEuNi42NDEgNy4zODIgNy4zODIgMCAwMC0uNzA1IDEuMjE4Yy0uNjIgMS40MzQtLjg0MiAzLjQ4LjA4MSA0LjYwM3ptNC4yMDggMTIuMTE1YTMuMjQ0IDMuMjQ0IDAgMDAtLjMyMS0xLjM0NSA1Ljg2OSA1Ljg2OSAwIDAwLTMuNTU0LTMuMjY5IDUuMzg2IDUuMzg2IDAgMDAtLjIyNiA0LjcxMSA0LjE0NyA0LjE0NyAwIDAwLjcgMS4xMjFjMS4xMzMgMS4yMyAzLjUwNS4zMiAzLjQwMi0xLjIxOHptNC4yLTYuMjhhNy40NjYgNy40NjYgMCAwMC0xLjIxNy0uNyA0LjQyNSA0LjQyNSAwIDAwLTEuNjY2LS4zNTIgNi40IDYuNCAwIDAwLTMuMTg4LjU1NSA1Ljk1OSA1Ljk1OSAwIDAwMy4zMTYgMy4zODYgMy42NzIgMy42NzIgMCAwMDEuNDQyLjMyIDEuOCAxLjggMCAwMDEuMzEtMy4yMDl6Ii8+CiAgICA8L2c+Cjwvc3ZnPg==","contentEncoding":"BASE64","mediaType":"image/svg+xml","testCaseStartedId":"60","testStepId":"53"}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"53","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":51000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"60","testStepId":"54","timestamp":{"nanos":52000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"60","testStepId":"54","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in hook\nsamples/hooks/hooks.feature:19","status":"FAILED"},"timestamp":{"nanos":53000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"60","timestamp":{"nanos":54000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":55000000,"seconds":0}}} diff --git a/elixir/test/testdata/minimal.feature.ndjson b/elixir/test/testdata/minimal.feature.ndjson new file mode 100644 index 00000000..0e34d963 --- /dev/null +++ b/elixir/test/testdata/minimal.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: minimal\n \n Cucumber doesn't execute this markdown, but @cucumber/react renders it\n \n * This is\n * a bullet\n * list\n \n Scenario: cukes\n Given I have 42 cukes in my belly\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/minimal/minimal.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":9},"name":"cukes","steps":[{"id":"1","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":10},"text":"I have 42 cukes in my belly"}],"tags":[]}}],"description":" Cucumber doesn't execute this markdown, but @cucumber/react renders it\n \n * This is\n * a bullet\n * list","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"minimal","tags":[]},"uri":"samples/minimal/minimal.feature"}} +{"pickle":{"astNodeIds":["2"],"id":"4","language":"en","name":"cukes","steps":[{"astNodeIds":["1"],"id":"3","text":"I have 42 cukes in my belly","type":"Context"}],"tags":[],"uri":"samples/minimal/minimal.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"I have {int} cukes in my belly","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/minimal/minimal.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"6","pickleId":"4","testSteps":[{"id":"5","pickleStepId":"3","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":7,"value":"42"},"parameterTypeName":"int"}]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"7","testCaseId":"6","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"7","testStepId":"5","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"7","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/elixir/test/testdata/parameter-types.feature.ndjson b/elixir/test/testdata/parameter-types.feature.ndjson new file mode 100644 index 00000000..6418fb9c --- /dev/null +++ b/elixir/test/testdata/parameter-types.feature.ndjson @@ -0,0 +1,13 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Parameter Types\n Cucumber lets you define your own parameter types, which can be used\n in Cucumber Expressions. This lets you define a precise domain-specific\n vocabulary which can be used to generate a glossary with examples taken\n from your scenarios. They also let you transform strings and tables into\n rich types.\n\n Scenario: flights\n Given LHR-CDG has been delayed 45 minutes\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/parameter-types/parameter-types.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"3","keyword":"Scenario","location":{"column":3,"line":8},"name":"flights","steps":[{"id":"2","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":9},"text":"LHR-CDG has been delayed 45 minutes"}],"tags":[]}}],"description":" Cucumber lets you define your own parameter types, which can be used\n in Cucumber Expressions. This lets you define a precise domain-specific\n vocabulary which can be used to generate a glossary with examples taken\n from your scenarios. They also let you transform strings and tables into\n rich types.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Parameter Types","tags":[]},"uri":"samples/parameter-types/parameter-types.feature"}} +{"pickle":{"astNodeIds":["3"],"id":"5","language":"en","name":"flights","steps":[{"astNodeIds":["2"],"id":"4","text":"LHR-CDG has been delayed 45 minutes","type":"Context"}],"tags":[],"uri":"samples/parameter-types/parameter-types.feature"}} +{"parameterType":{"id":"0","name":"flight","preferForRegularExpressionMatch":false,"regularExpressions":["([A-Z]{3})-([A-Z]{3})"],"useForSnippets":true}} +{"stepDefinition":{"id":"1","pattern":{"source":"{flight} has been delayed {int} minutes","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/parameter-types/parameter-types.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"7","pickleId":"5","testSteps":[{"id":"6","pickleStepId":"4","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[{"children":[],"start":0,"value":"LHR"},{"children":[],"start":4,"value":"CDG"}],"start":0,"value":"LHR-CDG"},"parameterTypeName":"flight"},{"group":{"children":[],"start":25,"value":"45"},"parameterTypeName":"int"}]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"8","testCaseId":"7","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"8","testStepId":"6","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"8","testStepId":"6","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"8","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/elixir/test/testdata/pending.feature.ndjson b/elixir/test/testdata/pending.feature.ndjson new file mode 100644 index 00000000..bfd4614a --- /dev/null +++ b/elixir/test/testdata/pending.feature.ndjson @@ -0,0 +1,30 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Pending steps\n\n During development, step definitions can signal at runtime that they are\n not yet implemented (or \"pending\") by returning or throwing a particular\n value.\n\n This causes subsequent steps in the scenario to be skipped, and the overall\n result to be treated as a failure.\n\n Scenario: Unimplemented step signals pending status\n Given a step that isnt implemented yet\n\n Scenario: Steps before unimplemented steps are executed\n Given an implemented step\n When a step that isnt implemented yet\n\n Scenario: Steps after unimplemented steps are skipped\n Given a step that isnt implemented yet\n Then a step that we expect to be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/pending/pending.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"4","keyword":"Scenario","location":{"column":3,"line":10},"name":"Unimplemented step signals pending status","steps":[{"id":"3","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"7","keyword":"Scenario","location":{"column":3,"line":13},"name":"Steps before unimplemented steps are executed","steps":[{"id":"5","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":14},"text":"an implemented step"},{"id":"6","keyword":"When ","keywordType":"Action","location":{"column":5,"line":15},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"10","keyword":"Scenario","location":{"column":3,"line":17},"name":"Steps after unimplemented steps are skipped","steps":[{"id":"8","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":18},"text":"a step that isnt implemented yet"},{"id":"9","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":19},"text":"a step that we expect to be skipped"}],"tags":[]}}],"description":" During development, step definitions can signal at runtime that they are\n not yet implemented (or \"pending\") by returning or throwing a particular\n value.\n\n This causes subsequent steps in the scenario to be skipped, and the overall\n result to be treated as a failure.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Pending steps","tags":[]},"uri":"samples/pending/pending.feature"}} +{"pickle":{"astNodeIds":["4"],"id":"12","language":"en","name":"Unimplemented step signals pending status","steps":[{"astNodeIds":["3"],"id":"11","text":"a step that isnt implemented yet","type":"Context"}],"tags":[],"uri":"samples/pending/pending.feature"}} +{"pickle":{"astNodeIds":["7"],"id":"15","language":"en","name":"Steps before unimplemented steps are executed","steps":[{"astNodeIds":["5"],"id":"13","text":"an implemented step","type":"Context"},{"astNodeIds":["6"],"id":"14","text":"a step that isnt implemented yet","type":"Action"}],"tags":[],"uri":"samples/pending/pending.feature"}} +{"pickle":{"astNodeIds":["10"],"id":"18","language":"en","name":"Steps after unimplemented steps are skipped","steps":[{"astNodeIds":["8"],"id":"16","text":"a step that isnt implemented yet","type":"Context"},{"astNodeIds":["9"],"id":"17","text":"a step that we expect to be skipped","type":"Outcome"}],"tags":[],"uri":"samples/pending/pending.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/pending/pending.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"a step that isnt implemented yet","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/pending/pending.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step that we expect to be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":11},"uri":"samples/pending/pending.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"20","pickleId":"12","testSteps":[{"id":"19","pickleStepId":"11","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"23","pickleId":"15","testSteps":[{"id":"21","pickleStepId":"13","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"22","pickleStepId":"14","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"26","pickleId":"18","testSteps":[{"id":"24","pickleStepId":"16","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"25","pickleStepId":"17","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"27","testCaseId":"20","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"27","testStepId":"19","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"27","testStepId":"19","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PENDING"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"27","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"28","testCaseId":"23","timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"21","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"21","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"22","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"22","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PENDING"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"28","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"29","testCaseId":"26","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"29","testStepId":"24","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"29","testStepId":"24","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PENDING"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"29","testStepId":"25","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"29","testStepId":"25","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"29","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":17000000,"seconds":0}}} diff --git a/elixir/test/testdata/retry.feature.ndjson b/elixir/test/testdata/retry.feature.ndjson new file mode 100644 index 00000000..43536b45 --- /dev/null +++ b/elixir/test/testdata/retry.feature.ndjson @@ -0,0 +1,59 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Retry\n\n Some Cucumber implementations support a Retry mechanism, where test cases that fail\n can be retried up to a limited number of attempts in the same test run.\n\n Non-passing statuses other than FAILED don't trigger a retry - they are not going to pass\n however many times we attempt them.\n\n Scenario: test case passes on the first attempt\n Given a step that always passes\n\n Scenario: test case passes on the second attempt\n Given a step that passes the second time\n\n Scenario: test case passes on the final attempt\n Given a step that passes the third time\n\n Scenario: test case fails on every attempt\n Given a step that always fails\n\n Scenario: don't retry on UNDEFINED\n Given a non-existent step\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/retry/retry.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"5","keyword":"Scenario","location":{"column":3,"line":9},"name":"test case passes on the first attempt","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":10},"text":"a step that always passes"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"7","keyword":"Scenario","location":{"column":3,"line":12},"name":"test case passes on the second attempt","steps":[{"id":"6","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":13},"text":"a step that passes the second time"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":15},"name":"test case passes on the final attempt","steps":[{"id":"8","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":16},"text":"a step that passes the third time"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"11","keyword":"Scenario","location":{"column":3,"line":18},"name":"test case fails on every attempt","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":19},"text":"a step that always fails"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"13","keyword":"Scenario","location":{"column":3,"line":21},"name":"don't retry on UNDEFINED","steps":[{"id":"12","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":22},"text":"a non-existent step"}],"tags":[]}}],"description":" Some Cucumber implementations support a Retry mechanism, where test cases that fail\n can be retried up to a limited number of attempts in the same test run.\n\n Non-passing statuses other than FAILED don't trigger a retry - they are not going to pass\n however many times we attempt them.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Retry","tags":[]},"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["5"],"id":"15","language":"en","name":"test case passes on the first attempt","steps":[{"astNodeIds":["4"],"id":"14","text":"a step that always passes","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["7"],"id":"17","language":"en","name":"test case passes on the second attempt","steps":[{"astNodeIds":["6"],"id":"16","text":"a step that passes the second time","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"19","language":"en","name":"test case passes on the final attempt","steps":[{"astNodeIds":["8"],"id":"18","text":"a step that passes the third time","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["11"],"id":"21","language":"en","name":"test case fails on every attempt","steps":[{"astNodeIds":["10"],"id":"20","text":"a step that always fails","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"pickle":{"astNodeIds":["13"],"id":"23","language":"en","name":"don't retry on UNDEFINED","steps":[{"astNodeIds":["12"],"id":"22","text":"a non-existent step","type":"Context"}],"tags":[],"uri":"samples/retry/retry.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"a step that always passes","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/retry/retry.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"a step that passes the second time","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":8},"uri":"samples/retry/retry.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step that passes the third time","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":16},"uri":"samples/retry/retry.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"a step that always fails","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":23},"uri":"samples/retry/retry.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"25","pickleId":"15","testSteps":[{"id":"24","pickleStepId":"14","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"27","pickleId":"17","testSteps":[{"id":"26","pickleStepId":"16","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"29","pickleId":"19","testSteps":[{"id":"28","pickleStepId":"18","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"31","pickleId":"21","testSteps":[{"id":"30","pickleStepId":"20","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"33","pickleId":"23","testSteps":[{"id":"32","pickleStepId":"22","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCaseStarted":{"attempt":0,"id":"34","testCaseId":"25","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"34","testStepId":"24","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"34","testStepId":"24","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"34","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"35","testCaseId":"27","timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"35","testStepId":"26","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"35","testStepId":"26","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/retry/retry.feature:13","status":"FAILED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"35","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":1,"id":"36","testCaseId":"27","timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"36","testStepId":"26","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"36","testStepId":"26","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"36","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"37","testCaseId":"29","timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"37","testStepId":"28","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"37","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/retry/retry.feature:16","status":"FAILED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"37","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":1,"id":"38","testCaseId":"29","timestamp":{"nanos":17000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"38","testStepId":"28","timestamp":{"nanos":18000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"38","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/retry/retry.feature:16","status":"FAILED"},"timestamp":{"nanos":19000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"38","timestamp":{"nanos":20000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":2,"id":"39","testCaseId":"29","timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"39","testStepId":"28","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"39","testStepId":"28","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"39","timestamp":{"nanos":24000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"40","testCaseId":"31","timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"40","testStepId":"30","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"40","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/retry/retry.feature:19","status":"FAILED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"40","timestamp":{"nanos":28000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":1,"id":"41","testCaseId":"31","timestamp":{"nanos":29000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"41","testStepId":"30","timestamp":{"nanos":30000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"41","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/retry/retry.feature:19","status":"FAILED"},"timestamp":{"nanos":31000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"41","timestamp":{"nanos":32000000,"seconds":0},"willBeRetried":true}} +{"testCaseStarted":{"attempt":2,"id":"42","testCaseId":"31","timestamp":{"nanos":33000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"42","testStepId":"30","timestamp":{"nanos":34000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"42","testStepId":"30","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"Exception in step\nsamples/retry/retry.feature:19","status":"FAILED"},"timestamp":{"nanos":35000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"42","timestamp":{"nanos":36000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"43","testCaseId":"33","timestamp":{"nanos":37000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"43","testStepId":"32","timestamp":{"nanos":38000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"43","testStepId":"32","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":39000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"43","timestamp":{"nanos":40000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":41000000,"seconds":0}}} diff --git a/elixir/test/testdata/rules.feature.ndjson b/elixir/test/testdata/rules.feature.ndjson new file mode 100644 index 00000000..0a48a628 --- /dev/null +++ b/elixir/test/testdata/rules.feature.ndjson @@ -0,0 +1,45 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Rules\n You can place scenarios inside rules. This makes is possible to structure\n Gherkin documents in the same way as [example maps](https://cucumber.io/blog/bdd/example-mapping-introduction/).\n You can also use the Examples synonym for Scenario to make them even more similar.\n\n Rule: a sale cannot happen if change cannot be returned\n # sad path\n Example: no change\n Given there are 5 0.20 coins inside\n When the customer tries to buy a 0.85 chocolate with a 1 coin\n Then the sale should not happen\n\n # happy path\n Example: exact change\n Given there are 5 0.20 coins inside\n And there are 3 chocolates inside\n When the customer tries to buy a 0.80 chocolate with a 1 coin\n Then the customer's change should be 1 0.20 coin\n\n @some-tag\n Rule: a sale cannot happen if we're out of stock\n # sad path\n Example: no chocolates left\n Given there are no chocolates inside\n But there are 10 0.5 coins inside\n When the customer tries to buy a 0.85 chocolate with a 1 coin\n Then the sale should not happen\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/rules/rules.feature"}} +{"gherkinDocument":{"comments":[{"location":{"column":1,"line":7},"text":" # sad path"},{"location":{"column":1,"line":13},"text":" # happy path"},{"location":{"column":1,"line":22},"text":" # sad path"}],"feature":{"children":[{"rule":{"children":[{"scenario":{"description":"","examples":[],"id":"9","keyword":"Example","location":{"column":5,"line":8},"name":"no change","steps":[{"id":"6","keyword":"Given ","keywordType":"Context","location":{"column":7,"line":9},"text":"there are 5 0.20 coins inside"},{"id":"7","keyword":"When ","keywordType":"Action","location":{"column":7,"line":10},"text":"the customer tries to buy a 0.85 chocolate with a 1 coin"},{"id":"8","keyword":"Then ","keywordType":"Outcome","location":{"column":7,"line":11},"text":"the sale should not happen"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"14","keyword":"Example","location":{"column":5,"line":14},"name":"exact change","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":7,"line":15},"text":"there are 5 0.20 coins inside"},{"id":"11","keyword":"And ","keywordType":"Conjunction","location":{"column":7,"line":16},"text":"there are 3 chocolates inside"},{"id":"12","keyword":"When ","keywordType":"Action","location":{"column":7,"line":17},"text":"the customer tries to buy a 0.80 chocolate with a 1 coin"},{"id":"13","keyword":"Then ","keywordType":"Outcome","location":{"column":7,"line":18},"text":"the customer's change should be 1 0.20 coin"}],"tags":[]}}],"description":"","id":"15","keyword":"Rule","location":{"column":3,"line":6},"name":"a sale cannot happen if change cannot be returned","tags":[]}},{"rule":{"children":[{"scenario":{"description":"","examples":[],"id":"20","keyword":"Example","location":{"column":5,"line":23},"name":"no chocolates left","steps":[{"id":"16","keyword":"Given ","keywordType":"Context","location":{"column":7,"line":24},"text":"there are no chocolates inside"},{"id":"17","keyword":"But ","keywordType":"Conjunction","location":{"column":7,"line":25},"text":"there are 10 0.5 coins inside"},{"id":"18","keyword":"When ","keywordType":"Action","location":{"column":7,"line":26},"text":"the customer tries to buy a 0.85 chocolate with a 1 coin"},{"id":"19","keyword":"Then ","keywordType":"Outcome","location":{"column":7,"line":27},"text":"the sale should not happen"}],"tags":[]}}],"description":"","id":"22","keyword":"Rule","location":{"column":3,"line":21},"name":"a sale cannot happen if we're out of stock","tags":[{"id":"21","location":{"column":3,"line":20},"name":"@some-tag"}]}}],"description":" You can place scenarios inside rules. This makes is possible to structure\n Gherkin documents in the same way as [example maps](https://cucumber.io/blog/bdd/example-mapping-introduction/).\n You can also use the Examples synonym for Scenario to make them even more similar.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Rules","tags":[]},"uri":"samples/rules/rules.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"26","language":"en","name":"no change","steps":[{"astNodeIds":["6"],"id":"23","text":"there are 5 0.20 coins inside","type":"Context"},{"astNodeIds":["7"],"id":"24","text":"the customer tries to buy a 0.85 chocolate with a 1 coin","type":"Action"},{"astNodeIds":["8"],"id":"25","text":"the sale should not happen","type":"Outcome"}],"tags":[],"uri":"samples/rules/rules.feature"}} +{"pickle":{"astNodeIds":["14"],"id":"31","language":"en","name":"exact change","steps":[{"astNodeIds":["10"],"id":"27","text":"there are 5 0.20 coins inside","type":"Context"},{"astNodeIds":["11"],"id":"28","text":"there are 3 chocolates inside","type":"Context"},{"astNodeIds":["12"],"id":"29","text":"the customer tries to buy a 0.80 chocolate with a 1 coin","type":"Action"},{"astNodeIds":["13"],"id":"30","text":"the customer's change should be 1 0.20 coin","type":"Outcome"}],"tags":[],"uri":"samples/rules/rules.feature"}} +{"pickle":{"astNodeIds":["20"],"id":"36","language":"en","name":"no chocolates left","steps":[{"astNodeIds":["16"],"id":"32","text":"there are no chocolates inside","type":"Context"},{"astNodeIds":["17"],"id":"33","text":"there are 10 0.5 coins inside","type":"Context"},{"astNodeIds":["18"],"id":"34","text":"the customer tries to buy a 0.85 chocolate with a 1 coin","type":"Action"},{"astNodeIds":["19"],"id":"35","text":"the sale should not happen","type":"Outcome"}],"tags":[{"astNodeId":"21","name":"@some-tag"}],"uri":"samples/rules/rules.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"there are {int} {float} coins inside","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":4},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"there are no chocolates inside","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":10},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"there are {int} chocolates inside","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":14},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"the customer tries to buy a {float} chocolate with a {float} coin","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":19},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"4","pattern":{"source":"the sale should not happen","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":25},"uri":"samples/rules/rules.feature.ts"}}} +{"stepDefinition":{"id":"5","pattern":{"source":"the customer's change should be {int} {float} coin(s)","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":29},"uri":"samples/rules/rules.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"40","pickleId":"26","testSteps":[{"id":"37","pickleStepId":"23","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"5"},"parameterTypeName":"int"},{"group":{"children":[],"start":12,"value":"0.20"},"parameterTypeName":"float"}]}]},{"id":"38","pickleStepId":"24","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":28,"value":"0.85"},"parameterTypeName":"float"},{"group":{"children":[],"start":50,"value":"1"},"parameterTypeName":"float"}]}]},{"id":"39","pickleStepId":"25","stepDefinitionIds":["4"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"45","pickleId":"31","testSteps":[{"id":"41","pickleStepId":"27","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"5"},"parameterTypeName":"int"},{"group":{"children":[],"start":12,"value":"0.20"},"parameterTypeName":"float"}]}]},{"id":"42","pickleStepId":"28","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"3"},"parameterTypeName":"int"}]}]},{"id":"43","pickleStepId":"29","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":28,"value":"0.80"},"parameterTypeName":"float"},{"group":{"children":[],"start":50,"value":"1"},"parameterTypeName":"float"}]}]},{"id":"44","pickleStepId":"30","stepDefinitionIds":["5"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":32,"value":"1"},"parameterTypeName":"int"},{"group":{"children":[],"start":34,"value":"0.20"},"parameterTypeName":"float"}]}]}]}} +{"testCase":{"id":"50","pickleId":"36","testSteps":[{"id":"46","pickleStepId":"32","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"47","pickleStepId":"33","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":10,"value":"10"},"parameterTypeName":"int"},{"group":{"children":[],"start":13,"value":"0.5"},"parameterTypeName":"float"}]}]},{"id":"48","pickleStepId":"34","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[{"group":{"children":[],"start":28,"value":"0.85"},"parameterTypeName":"float"},{"group":{"children":[],"start":50,"value":"1"},"parameterTypeName":"float"}]}]},{"id":"49","pickleStepId":"35","stepDefinitionIds":["4"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"51","testCaseId":"40","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"51","testStepId":"37","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"51","testStepId":"37","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"51","testStepId":"38","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"51","testStepId":"38","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"51","testStepId":"39","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"51","testStepId":"39","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"51","timestamp":{"nanos":8000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"52","testCaseId":"45","timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"41","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"41","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"42","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"42","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"43","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"43","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"52","testStepId":"44","timestamp":{"nanos":16000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"52","testStepId":"44","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"52","timestamp":{"nanos":18000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"53","testCaseId":"50","timestamp":{"nanos":19000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"46","timestamp":{"nanos":20000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"46","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":21000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"47","timestamp":{"nanos":22000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"47","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":23000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"48","timestamp":{"nanos":24000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"48","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":25000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"53","testStepId":"49","timestamp":{"nanos":26000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"53","testStepId":"49","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":27000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"53","timestamp":{"nanos":28000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":29000000,"seconds":0}}} diff --git a/elixir/test/testdata/skipped.feature.ndjson b/elixir/test/testdata/skipped.feature.ndjson new file mode 100644 index 00000000..0f58c2be --- /dev/null +++ b/elixir/test/testdata/skipped.feature.ndjson @@ -0,0 +1,33 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Skipping scenarios\n\n Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by returning or throwing a particular value.\n\n This can be useful when e.g. the current environment doesn't have the right conditions\n for running the scenario.\n\n @skip\n Scenario: Skipping from a Before hook\n Given a step that we expect to be skipped\n\n Scenario: Skipping from a step doesn't affect the previous steps\n Given an implemented step\n When a step that skips\n\n Scenario: Skipping from a step causes the rest of the scenario to be skipped\n Given a step that skips\n When a step that we expect to be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/skipped/skipped.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":10},"name":"Skipping from a Before hook","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":11},"text":"a step that we expect to be skipped"}],"tags":[{"id":"5","location":{"column":3,"line":9},"name":"@skip"}]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":13},"name":"Skipping from a step doesn't affect the previous steps","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":14},"text":"an implemented step"},{"id":"8","keyword":"When ","keywordType":"Action","location":{"column":5,"line":15},"text":"a step that skips"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"12","keyword":"Scenario","location":{"column":3,"line":17},"name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"id":"10","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":18},"text":"a step that skips"},{"id":"11","keyword":"When ","keywordType":"Action","location":{"column":5,"line":19},"text":"a step that we expect to be skipped"}],"tags":[]}}],"description":" Hooks and step definitions are able to signal at runtime that the scenario should\n be skipped by returning or throwing a particular value.\n\n This can be useful when e.g. the current environment doesn't have the right conditions\n for running the scenario.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Skipping scenarios","tags":[]},"uri":"samples/skipped/skipped.feature"}} +{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Skipping from a Before hook","steps":[{"astNodeIds":["4"],"id":"13","text":"a step that we expect to be skipped","type":"Context"}],"tags":[{"astNodeId":"5","name":"@skip"}],"uri":"samples/skipped/skipped.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Skipping from a step doesn't affect the previous steps","steps":[{"astNodeIds":["7"],"id":"15","text":"an implemented step","type":"Context"},{"astNodeIds":["8"],"id":"16","text":"a step that skips","type":"Action"}],"tags":[],"uri":"samples/skipped/skipped.feature"}} +{"pickle":{"astNodeIds":["12"],"id":"20","language":"en","name":"Skipping from a step causes the rest of the scenario to be skipped","steps":[{"astNodeIds":["10"],"id":"18","text":"a step that skips","type":"Context"},{"astNodeIds":["11"],"id":"19","text":"a step that we expect to be skipped","type":"Action"}],"tags":[],"uri":"samples/skipped/skipped.feature"}} +{"stepDefinition":{"id":"1","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/skipped/skipped.feature.ts"}}} +{"stepDefinition":{"id":"2","pattern":{"source":"a step that we expect to be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":11},"uri":"samples/skipped/skipped.feature.ts"}}} +{"stepDefinition":{"id":"3","pattern":{"source":"a step that skips","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":15},"uri":"samples/skipped/skipped.feature.ts"}}} +{"hook":{"id":"0","sourceReference":{"location":{"line":3},"uri":"samples/skipped/skipped.feature.ts"},"tagExpression":"@skip"}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"23","pickleId":"14","testSteps":[{"hookId":"0","id":"21"},{"id":"22","pickleStepId":"13","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"26","pickleId":"17","testSteps":[{"id":"24","pickleStepId":"15","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"25","pickleStepId":"16","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCase":{"id":"29","pickleId":"20","testSteps":[{"id":"27","pickleStepId":"18","stepDefinitionIds":["3"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"28","pickleStepId":"19","stepDefinitionIds":["2"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"30","testCaseId":"23","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"30","testStepId":"21","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"30","testStepId":"21","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"30","testStepId":"22","timestamp":{"nanos":4000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"30","testStepId":"22","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":5000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"30","timestamp":{"nanos":6000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"31","testCaseId":"26","timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"31","testStepId":"24","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"31","testStepId":"24","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"31","testStepId":"25","timestamp":{"nanos":10000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"31","testStepId":"25","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":11000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"31","timestamp":{"nanos":12000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"32","testCaseId":"29","timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"32","testStepId":"27","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"32","testStepId":"27","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"32","testStepId":"28","timestamp":{"nanos":16000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"32","testStepId":"28","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":17000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"32","timestamp":{"nanos":18000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":true,"timestamp":{"nanos":19000000,"seconds":0}}} diff --git a/elixir/test/testdata/stack-traces.feature.ndjson b/elixir/test/testdata/stack-traces.feature.ndjson new file mode 100644 index 00000000..5b438c8a --- /dev/null +++ b/elixir/test/testdata/stack-traces.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Stack traces\n Nothing beats stack traces when it comes to diagnosing the source of a bug.\n Cucumber provides helpful stack traces that:\n \n - Include a stack frame from the Gherkin document\n - Remove uninteresting frames by default\n\n The first line of the stack trace must contain the feature file.\n\n Scenario: A failing step\n When a step throws an exception\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/stack-traces/stack-traces.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"2","keyword":"Scenario","location":{"column":3,"line":10},"name":"A failing step","steps":[{"id":"1","keyword":"When ","keywordType":"Action","location":{"column":5,"line":11},"text":"a step throws an exception"}],"tags":[]}}],"description":" Nothing beats stack traces when it comes to diagnosing the source of a bug.\n Cucumber provides helpful stack traces that:\n \n - Include a stack frame from the Gherkin document\n - Remove uninteresting frames by default\n\n The first line of the stack trace must contain the feature file.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Stack traces","tags":[]},"uri":"samples/stack-traces/stack-traces.feature"}} +{"pickle":{"astNodeIds":["2"],"id":"4","language":"en","name":"A failing step","steps":[{"astNodeIds":["1"],"id":"3","text":"a step throws an exception","type":"Action"}],"tags":[],"uri":"samples/stack-traces/stack-traces.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"a step throws an exception","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/stack-traces/stack-traces.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"6","pickleId":"4","testSteps":[{"id":"5","pickleStepId":"3","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"7","testCaseId":"6","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"7","testStepId":"5","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"7","testStepId":"5","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"message":"BOOM\nsamples/stack-traces/stack-traces.feature:11","status":"FAILED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"7","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/elixir/test/testdata/undefined.feature.ndjson b/elixir/test/testdata/undefined.feature.ndjson new file mode 100644 index 00000000..2b852aec --- /dev/null +++ b/elixir/test/testdata/undefined.feature.ndjson @@ -0,0 +1,29 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Undefined steps\n\n At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario cannot run and so the step status\n will be UNDEFINED, with subsequent steps being skipped and the overall result treated\n as a failure.\n\n Scenario: Undefined step causes failure\n Given a step that isnt implemented yet\n\n Scenario: Steps before undefined steps are executed\n Given an implemented step\n When a step that isnt implemented yet\n\n Scenario: Steps after undefined steps are skipped\n Given a step that isnt implemented yet\n Then a step that we expect to be skipped\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/undefined/undefined.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"3","keyword":"Scenario","location":{"column":3,"line":8},"name":"Undefined step causes failure","steps":[{"id":"2","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":9},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"6","keyword":"Scenario","location":{"column":3,"line":11},"name":"Steps before undefined steps are executed","steps":[{"id":"4","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":12},"text":"an implemented step"},{"id":"5","keyword":"When ","keywordType":"Action","location":{"column":5,"line":13},"text":"a step that isnt implemented yet"}],"tags":[]}},{"scenario":{"description":"","examples":[],"id":"9","keyword":"Scenario","location":{"column":3,"line":15},"name":"Steps after undefined steps are skipped","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":16},"text":"a step that isnt implemented yet"},{"id":"8","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":17},"text":"a step that we expect to be skipped"}],"tags":[]}}],"description":" At runtime, Cucumber may encounter a step in a scenario that it cannot match to a\n step definition. In these cases, the scenario cannot run and so the step status\n will be UNDEFINED, with subsequent steps being skipped and the overall result treated\n as a failure.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Undefined steps","tags":[]},"uri":"samples/undefined/undefined.feature"}} +{"pickle":{"astNodeIds":["3"],"id":"11","language":"en","name":"Undefined step causes failure","steps":[{"astNodeIds":["2"],"id":"10","text":"a step that isnt implemented yet","type":"Context"}],"tags":[],"uri":"samples/undefined/undefined.feature"}} +{"pickle":{"astNodeIds":["6"],"id":"14","language":"en","name":"Steps before undefined steps are executed","steps":[{"astNodeIds":["4"],"id":"12","text":"an implemented step","type":"Context"},{"astNodeIds":["5"],"id":"13","text":"a step that isnt implemented yet","type":"Action"}],"tags":[],"uri":"samples/undefined/undefined.feature"}} +{"pickle":{"astNodeIds":["9"],"id":"17","language":"en","name":"Steps after undefined steps are skipped","steps":[{"astNodeIds":["7"],"id":"15","text":"a step that isnt implemented yet","type":"Context"},{"astNodeIds":["8"],"id":"16","text":"a step that we expect to be skipped","type":"Outcome"}],"tags":[],"uri":"samples/undefined/undefined.feature"}} +{"stepDefinition":{"id":"0","pattern":{"source":"an implemented step","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":3},"uri":"samples/undefined/undefined.feature.ts"}}} +{"stepDefinition":{"id":"1","pattern":{"source":"a step that we expect to be skipped","type":"CUCUMBER_EXPRESSION"},"sourceReference":{"location":{"line":7},"uri":"samples/undefined/undefined.feature.ts"}}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"19","pickleId":"11","testSteps":[{"id":"18","pickleStepId":"10","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCase":{"id":"22","pickleId":"14","testSteps":[{"id":"20","pickleStepId":"12","stepDefinitionIds":["0"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]},{"id":"21","pickleStepId":"13","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCase":{"id":"25","pickleId":"17","testSteps":[{"id":"23","pickleStepId":"15","stepDefinitionIds":[],"stepMatchArgumentsLists":[]},{"id":"24","pickleStepId":"16","stepDefinitionIds":["1"],"stepMatchArgumentsLists":[{"stepMatchArguments":[]}]}]}} +{"testCaseStarted":{"attempt":0,"id":"26","testCaseId":"19","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"26","testStepId":"18","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"26","testStepId":"18","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"26","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"27","testCaseId":"22","timestamp":{"nanos":5000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"27","testStepId":"20","timestamp":{"nanos":6000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"27","testStepId":"20","testStepResult":{"duration":{"nanos":1000000,"seconds":0},"status":"PASSED"},"timestamp":{"nanos":7000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"27","testStepId":"21","timestamp":{"nanos":8000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"27","testStepId":"21","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":9000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"27","timestamp":{"nanos":10000000,"seconds":0},"willBeRetried":false}} +{"testCaseStarted":{"attempt":0,"id":"28","testCaseId":"25","timestamp":{"nanos":11000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"23","timestamp":{"nanos":12000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"23","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":13000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"28","testStepId":"24","timestamp":{"nanos":14000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"28","testStepId":"24","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"SKIPPED"},"timestamp":{"nanos":15000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"28","timestamp":{"nanos":16000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":17000000,"seconds":0}}} diff --git a/elixir/test/testdata/unknown-parameter-type.feature.ndjson b/elixir/test/testdata/unknown-parameter-type.feature.ndjson new file mode 100644 index 00000000..0e8c4908 --- /dev/null +++ b/elixir/test/testdata/unknown-parameter-type.feature.ndjson @@ -0,0 +1,12 @@ +{"meta":{"ci":{"buildNumber":"154666429","git":{"remote":"https://github.com/cucumber-ltd/shouty.rb.git","revision":"99684bcacf01d95875834d87903dcb072306c9ad"},"name":"GitHub Actions","url":"https://github.com/cucumber-ltd/shouty.rb/actions/runs/154666429"},"cpu":{"name":"x64"},"implementation":{"name":"fake-cucumber","version":"16.0.0"},"os":{"name":"linux","version":"5.10.102.1-microsoft-standard-WSL2"},"protocolVersion":"19.1.2","runtime":{"name":"node.js","version":"16.4.0"}}} +{"source":{"data":"Feature: Parameter Types\n Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.\n\n Scenario: undefined parameter type\n Given CDG is closed because of a strike","mediaType":"text/x.cucumber.gherkin+plain","uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}} +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[],"id":"1","keyword":"Scenario","location":{"column":3,"line":5},"name":"undefined parameter type","steps":[{"id":"0","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":6},"text":"CDG is closed because of a strike"}],"tags":[]}}],"description":" Cucumber will generate an error message if a step definition registers\n an unknown parameter type, but the suite will run.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Parameter Types","tags":[]},"uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}} +{"pickle":{"astNodeIds":["1"],"id":"3","language":"en","name":"undefined parameter type","steps":[{"astNodeIds":["0"],"id":"2","text":"CDG is closed because of a strike","type":"Context"}],"tags":[],"uri":"samples/unknown-parameter-type/unknown-parameter-type.feature"}} +{"undefinedParameterType":{"expression":"{airport} is closed because of a strike","name":"airport"}} +{"testRunStarted":{"timestamp":{"nanos":0,"seconds":0}}} +{"testCase":{"id":"5","pickleId":"3","testSteps":[{"id":"4","pickleStepId":"2","stepDefinitionIds":[],"stepMatchArgumentsLists":[]}]}} +{"testCaseStarted":{"attempt":0,"id":"6","testCaseId":"5","timestamp":{"nanos":1000000,"seconds":0}}} +{"testStepStarted":{"testCaseStartedId":"6","testStepId":"4","timestamp":{"nanos":2000000,"seconds":0}}} +{"testStepFinished":{"testCaseStartedId":"6","testStepId":"4","testStepResult":{"duration":{"nanos":0,"seconds":0},"status":"UNDEFINED"},"timestamp":{"nanos":3000000,"seconds":0}}} +{"testCaseFinished":{"testCaseStartedId":"6","timestamp":{"nanos":4000000,"seconds":0},"willBeRetried":false}} +{"testRunFinished":{"success":false,"timestamp":{"nanos":5000000,"seconds":0}}} diff --git a/jsonschema/scripts/templates/elixir.erb b/jsonschema/scripts/templates/elixir.erb new file mode 100755 index 00000000..ae9c91d3 --- /dev/null +++ b/jsonschema/scripts/templates/elixir.erb @@ -0,0 +1,42 @@ +<%- @schemas.each do |key, schema| -%> +defmodule CucumberMessages.<%= class_name(key) %> do + defstruct [<%- schema['properties'].each do |property_name, property| -%> <%= underscore(property_name) %>: nil <%= property_name == schema['properties'].keys.last ? '' : ',' %> <%- end -%>] + + defimpl Jason.Encoder, for: CucumberMessages.<%= class_name(key) %> do + def encode(value, opts) do + value + |> Map.filter(fn {k, v} -> v != nil && k != :__struct__ end) + |> Enum.map(fn {k, v} -> {CucumberMessages.Helper.lower_camelize(k), v} end) + |> Enum.into(%{}) + |> Jason.Encode.map(opts) + end + end + + def decode(nil), do: nil + def decode(bin) when is_binary(bin), do: bin |> Jason.decode!() |> decode() + + def decode(normal_map) when is_map(normal_map) do + %__MODULE__{ + <%- schema['properties'].each do |property_name, property| + ref = property['$ref'] + items_ref = property.dig('items', '$ref') -%> + <%= "#{underscore(property_name)}: " -%> + <%- if items_ref -%> + <%- if schema['required'].include? property_name -%> + Enum.map(normal_map["<%= property_name -%>"] || [], fn item -> CucumberMessages.<%= class_name(items_ref) %>.decode(item) end), + <%- else -%> + case normal_map["<%= property_name -%>"] do + nil -> nil + data -> Enum.map(data, fn item -> CucumberMessages.<%= class_name(items_ref) %>.decode(item) end) + end + <%- end -%> + <%- elsif ref -%>CucumberMessages.<%= class_name(ref) %>.decode(normal_map["<%= property_name -%>"]), + <%- else -%> + normal_map["<%= property_name %>"], + <%- end -%> + <%- end -%> + } + end +end + +<%- end -%>