├── .github └── workflows │ ├── release.yaml │ └── test.yaml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── RELEASING.md ├── go ├── .gitignore ├── Makefile ├── cmd │ └── main.go ├── default.mk ├── go.mod ├── go.sum ├── go_suite_test.go ├── json.go ├── json_elements.go ├── message_lookup.go ├── message_lookup_test.go ├── scripts │ └── github-release ├── test_case.go ├── test_case_test.go ├── test_helpers.go ├── test_step.go └── test_step_test.go ├── renovate.json └── testdata ├── .gitignore ├── Gemfile ├── Makefile ├── README.md ├── Rakefile ├── cucumber.yml └── neutralize-json /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [release/*] 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-go@v5 14 | - uses: ruby/setup-ruby@v1 15 | with: 16 | ruby-version: '3.1' 17 | - run: make 18 | - uses: cucumber/action-get-versions@v1.0.0 19 | id: versions 20 | - run: gh release create v${{ steps.versions.outputs.changelog-latest-version }} go/dist/* 21 | env: 22 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - fix/working-ci 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-go@v5 19 | - uses: ruby/setup-ruby@v1 20 | with: 21 | ruby-version: "3.1" 22 | - run: make 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGE LOG 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | This project adheres to [Semantic Versioning](http://semver.org). 6 | 7 | This document is formatted according to the principles of [Keep A CHANGELOG](http://keepachangelog.com). 8 | 9 | ---- 10 | ## [Unreleased] 11 | 12 | ### Added 13 | 14 | ### Changed 15 | 16 | ### Deprecated 17 | 18 | ### Removed 19 | 20 | ### Fixed 21 | 22 | ## [19.0.0] - 2021-07-08 23 | 24 | ### Changed 25 | 26 | * Update messages to v17 27 | * [Go] Move module paths to point to monorepo 28 | ([#1550](https://github.com/cucumber/common/issues/1550)) 29 | 30 | ## [18.0.0] - 2021-05-17 31 | 32 | ### Changed 33 | 34 | * Upgrade to messages 16.0.0 35 | 36 | ## [17.0.2] - 2021-04-22 37 | 38 | ### Fixed 39 | 40 | * Handle tagged examples 41 | ([1489](https://github.com/cucumber/cucumber/pull/1489) 42 | [aurelien-reeves]) 43 | 44 | ## [7.0.1] - 2021-04-09 45 | 46 | ### Fixed 47 | 48 | * Handle embeddings when step is issued from a hook 49 | ([#1466](https://github.com/cucumber/cucumber/pull/1466) 50 | [#1172](https://github.com/cucumber/cucumber/issues/1172) 51 | [aurelien-reeves]) 52 | 53 | ## [7.0.0] - 2021-03-29 54 | 55 | ### Changed 56 | 57 | * Update `messages` to 15.0.0 58 | 59 | ## [6.0.0] - 2020-08-07 60 | 61 | ### Changed 62 | 63 | * Update `messages` to 13.0.1 64 | * Handle the `JavaMethod` and `JavaStackTraceElement` fields in `SourceReference`: 65 | - outputs `.(,, etc)` for `JavaMethod` 66 | - outputs `:` for `JavaStackTraceElement` 67 | 68 | ## [5.1.0] - 2020-06-29 69 | 70 | ### Changed 71 | 72 | * Upgrade to messages 12.2.0 73 | * Upgrade to gherkin 14.0.2 74 | 75 | ## [5.0.0] - 2020-04-14 76 | 77 | ### Added 78 | 79 | * Output description for Backgrounds. 80 | 81 | ### Changed 82 | 83 | * Upgrade to messages 12.0.0 84 | * Upgrade to gherkin 13.0.0 85 | 86 | ## [4.0.0] - 2020-04-01 87 | 88 | ### Changed 89 | 90 | * Bump major version of messages 91 | 92 | ## [3.1.1] - 2020-03-02 93 | 94 | ### Fixed 95 | 96 | * Fix 3.1.0 release 97 | 98 | ## [3.1.0] - 2020-03-02 99 | 100 | ### Added 101 | 102 | * Handle text added with `log`. 103 | 104 | ## [3.0.0] - 2020-02-14 105 | 106 | ### Changed 107 | 108 | Upgrade messages 109 | 110 | ### Fixed 111 | 112 | * Process rules 113 | * Output background name 114 | 115 | ## [2.2.0] - 2020-01-10 116 | 117 | ### Changed 118 | 119 | * Upgrade to cucumber-messages 9.0.3 120 | 121 | ## [2.1.0] - 2019-12-10 122 | 123 | ### Changed 124 | 125 | * Upgrade to cucumber-messages 8.0.0 126 | 127 | ### Fixed 128 | 129 | * Support for text/plain attachments (haven't fully implemented support for other types) 130 | * Support for Tags, DataTable and DocStrings 131 | 132 | ## [2.0.0] - 2019-10-17 133 | 134 | ### Changed 135 | 136 | * Upgrade to cucumber-messages v6 137 | 138 | ## [1.0.0] 139 | 140 | ### Added 141 | 142 | * Initial release 143 | 144 | 145 | [Unreleased]: https://github.com/cucumber/json-formatter/compare/v19.0.0...HEAD 146 | [19.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v18.0.0...json-formatter/v19.0.0 147 | [18.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v17.0.2...json-formatter/v18.0.0 148 | [17.0.2]: https://github.com/cucumber/cucumber/compare/json-formatter/v7.0.1...json-formatter/v17.0.2 149 | [7.0.1]: https://github.com/cucumber/cucumber/compare/json-formatter/v7.0.0...json-formatter/v7.0.1 150 | [7.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v6.0.0...json-formatter/v7.0.0 151 | [6.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v5.1.0...json-formatter/v6.0.0 152 | [5.1.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v5.0.0...json-formatter/v5.1.0 153 | [5.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v4.0.0...json-formatter/v5.0.0 154 | [4.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v3.1.1...json-formatter/v4.0.0 155 | [3.1.1]: https://github.com/cucumber/cucumber/compare/json-formatter/v3.1.0...json-formatter/v3.1.1 156 | [3.1.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v3.0.0...json-formatter/v3.1.0 157 | [3.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v2.2.0...json-formatter/v3.0.0 158 | [2.2.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v2.1.0...json-formatter/v2.2.0 159 | [2.1.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v2.0.0...json-formatter/v2.1.0 160 | [2.0.0]: https://github.com/cucumber/cucumber/compare/json-formatter/v1.0.0...json-formatter/v2.0.0 161 | [1.0.0]: https://github.com/cucumber/cucumber/releases/tag/json-formatter/v1.0.0 162 | 163 | 164 | [aslakhellesoy]: https://github.com/aslakhellesoy 165 | [aurelien-reeves]: https://github.com/aurelien-reeves 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Cucumber Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | cd testdata && make 3 | cd go && make 4 | 5 | clean: 6 | cd testdata && make clean 7 | cd go && make clean 8 | 9 | .PHONY: default clean 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build](https://github.com/cucumber/json-formatter/actions/workflows/test.yaml/badge.svg)](https://github.com/cucumber/json-formatter/actions/workflows/test.yaml) 2 | 3 | # Cucumber Standalone JSON Formatter 4 | 5 | This *standalone JSON Formatter* provides a language agnostic tool to convert [cucumber messages](https://github.com/cucumber/common/tree/main/messages#readme) into a JSON document. 6 | 7 | ## Installation 8 | 9 | The Standalone JSON Formatter is a prebuilt executable. (It's written in Go). 10 | It can be downloaded from [GitHub Releases](https://github.com/cucumber/json-formatter/releases/tag/v19.0.0) 11 | where you'll find executables for various operating systems and CPU architectures. 12 | 13 | ### MacOS 14 | 15 | * Download `cucumber-json-formatter-darwin-amd64` and rename it to `cucumber-json-formatter` 16 | * Move it to a directory that's on your `PATH` 17 | * Make it executable with `chmod +x cucumber-json-formatter` 18 | * Verify that you can run it: `cucumber-json-formatter --help` 19 | 20 | At the last step, you may get a security warning from MacOS. If you do, open *System Preferences*. Go to 21 | *Security Settings*. You should see a question asking if you want to open it anyway. Say yes. 22 | 23 | ### Windows 24 | 25 | * Download `cucumber-json-formatter-windows-amd64` and rename it to `cucumber-json-formatter.exe` 26 | * Move it to a directory that's on your `PATH` 27 | * Verify that you can run it: `cucumber-json-formatter --help` 28 | 29 | ### Linux 30 | 31 | * Download `cucumber-json-formatter-linux-amd64` (or one of the other CPU variants) and rename it to `cucumber-json-formatter` 32 | * Move it to a directory that's on your `PATH` 33 | * Make it executable with `chmod +x cucumber-json-formatter` 34 | * Verify that you can run it: `cucumber-json-formatter --help` 35 | 36 | ## Usage 37 | 38 | First, generate Cucumber messages using Cucumber's built-in `message` formatter and make sure it's saved to a file 39 | (e.g. `cucumber-messages.ndjson`). 40 | 41 | Next, generate JSON: 42 | 43 | cat cucumber-messages.ndjson | cucumber-json-formatter > cucumber-results.json 44 | 45 | That's it. If you are the maintainer of a tool that consumes the legacy Cucumber JSON format you should consider 46 | updating your tool to consume Cucumber Messages instead. 47 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | See [.github/RELEASING](https://github.com/cucumber/.github/blob/main/RELEASING.md). 2 | -------------------------------------------------------------------------------- /go/.gitignore: -------------------------------------------------------------------------------- 1 | .built 2 | .compared 3 | .deps 4 | .dist 5 | .dist-compressed 6 | .go-get 7 | .gofmt 8 | .linted 9 | .tested* 10 | acceptance/ 11 | bin/ 12 | dist/ 13 | dist_compressed/ 14 | *.bin 15 | *.iml 16 | # upx dist/cucumber-gherkin-openbsd-386 fails with a core dump 17 | core.*.!usr!bin!upx-ucl 18 | -------------------------------------------------------------------------------- /go/Makefile: -------------------------------------------------------------------------------- 1 | include default.mk 2 | 3 | GOLDEN_JSONS = $(wildcard ../testdata/features/**/*.json) 4 | GENERATED_JSONS = $(patsubst ../testdata/features/%.json,acceptance/%.json,$(GOLDEN_JSONS)) 5 | 6 | .DELETE_ON_ERROR: 7 | 8 | .tested: $(GENERATED_JSONS) 9 | 10 | acceptance/%.json: ../testdata/features/%.ndjson $(EXE) ../testdata/features/%.json 11 | mkdir -p $(@D) 12 | cat $< | \ 13 | $(EXE) | \ 14 | ../testdata/neutralize-json | \ 15 | jq --sort-keys "." > \ 16 | $@ 17 | diff --unified $(word 3, $^) $@ 18 | -------------------------------------------------------------------------------- /go/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | jsonFormatter "github.com/cucumber/common/json-formatter/go/v19" 6 | "log" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | flag.Parse() 12 | 13 | var err error 14 | var file *os.File 15 | jf := &jsonFormatter.Formatter{} 16 | paths := flag.Args() 17 | if len(paths) > 1 { 18 | for _, arg := range paths { 19 | file, err = os.Open(arg) 20 | if err != nil { 21 | log.Fatal("ERROR: ", err) 22 | } 23 | err = jf.ProcessMessages(file, os.Stdout) 24 | log.Fatal("ERROR: ", err) 25 | } 26 | } else { 27 | err = jf.ProcessMessages(os.Stdin, os.Stdout) 28 | if err != nil { 29 | log.Fatal("ERROR: ", err) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /go/default.mk: -------------------------------------------------------------------------------- 1 | # Please update /.templates/go/default.mk and sync: 2 | # source /scripts/functions.sh && rsync_files 3 | 4 | SHELL := /usr/bin/env bash 5 | GOPATH := $(shell go env GOPATH) 6 | PATH := $(PATH):$(GOPATH)/bin 7 | GO_SOURCE_FILES := $(shell find . -name "*.go" | sort) 8 | LIBNAME := $(shell basename $$(dirname $$(pwd))) 9 | EXE_BASE_NAME := cucumber-$(LIBNAME) 10 | LDFLAGS := "-X main.version=${NEW_VERSION}" 11 | 12 | # Enumerating Cross compilation targets 13 | PLATFORMS = darwin-amd64 linux-386 linux-amd64 linux-arm linux-arm64 freebsd-386 freebsd-amd64 openbsd-386 openbsd-amd64 windows-386 windows-amd64 freebsd-arm netbsd-386 netbsd-amd64 netbsd-arm 14 | PLATFORM = $(patsubst dist/$(EXE_BASE_NAME)-%,%,$@) 15 | OS_ARCH = $(subst -, ,$(PLATFORM)) 16 | X-OS = $(word 1, $(OS_ARCH)) 17 | X-ARCH = $(word 2, $(OS_ARCH)) 18 | 19 | OS := $(shell go env GOOS) 20 | ARCH := $(shell go env GOARCH) 21 | EXE := dist/$(EXE_BASE_NAME)-$(OS)-$(ARCH) 22 | 23 | ifndef NO_CROSS_COMPILE 24 | EXES = $(patsubst %,dist/$(EXE_BASE_NAME)-%,$(PLATFORMS)) 25 | else 26 | EXES = $(EXE) 27 | endif 28 | 29 | GO_REPLACEMENTS := $(shell sed -n "/^\s*github.com\/cucumber\/common/p" go.mod | perl -wpe 's/\s*(github.com\/cucumber\/common\/(.*)\/go\/v\d+).*/q{replace } . $$1 . q{ => ..\/..\/} . $$2 . q{\/go}/eg') 30 | CURRENT_MAJOR := $(shell sed -n "/^module/p" go.mod | awk '{ print $$0 "/v1" }' | cut -d'/' -f6 | cut -d'v' -f2) 31 | NEW_MAJOR := $(shell echo ${NEW_VERSION} | awk -F'.' '{print $$1}') 32 | 33 | GO_MAJOR_V = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) 34 | GO_MINOR_V = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) 35 | MIN_SUPPORTED_GO_MAJOR_V = 1 36 | MIN_SUPPORTED_GO_MINOR_V = 13 37 | 38 | # https://stackoverflow.com/questions/2483182/recursive-wildcards-in-gnu-make 39 | rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) 40 | 41 | default: .linted .tested 42 | .PHONY: default 43 | 44 | # Run the .dist target if there is a main file 45 | ifneq (,$(wildcard ./cmd/main.go)) 46 | default: dist 47 | endif 48 | 49 | .deps: 50 | touch $@ 51 | 52 | dist: $(EXES) 53 | 54 | dist/$(EXE_BASE_NAME)-%: .deps $(GO_SOURCE_FILES) go.mod 55 | mkdir -p dist 56 | echo "EXES=$(EXES)" 57 | echo "Building $@" 58 | 59 | # Determine if we're on a supported go platform 60 | @if [ $(GO_MAJOR_V) -gt $(MIN_SUPPORTED_GO_MAJOR_V) ]; then \ 61 | exit 0 ;\ 62 | elif [ $(GO_MAJOR_V) -lt $(MIN_SUPPORTED_GO_MAJOR_V) ]; then \ 63 | echo '$(GO_MAJOR_V).$(GO_MINOR_V) is not a supported version, $(MIN_SUPPORTED_GO_MAJOR_V).$(MIN_SUPPORTED_GO_MINOR_V) is required';\ 64 | exit 1; \ 65 | elif [ $(GO_MINOR_V) -lt $(MIN_SUPPORTED_GO_MINOR_V) ] ; then \ 66 | echo '$(GO_MAJOR_V).$(GO_MINOR_V) is not a supported version, $(MIN_SUPPORTED_GO_MAJOR_V).$(MIN_SUPPORTED_GO_MINOR_V) is required';\ 67 | exit 1; \ 68 | fi 69 | 70 | GOOS=$(X-OS) GOARCH=$(X-ARCH) go build -buildmode=exe -ldflags $(LDFLAGS) -o $@ -a ./cmd 71 | ifndef NO_UPX_COMPRESSION 72 | # requires upx in PATH to compress supported binaries 73 | # may produce an error ARCH not supported 74 | -upx $@ -o $@.upx 75 | 76 | # If the compressed file passes the integrity test, replace the original 77 | # file with the compressed file. Otherwise, preserve the original file 78 | # and remove the compressed file. 79 | if [ -f "$@.upx" ]; then upx -t $@.upx && mv $@.upx $@ || rm -f $@.upx; fi 80 | endif 81 | 82 | update-dependencies: 83 | go get -u && go mod tidy 84 | .PHONY: update-dependencies 85 | 86 | pre-release: remove-replaces update-version update-dependencies clean default 87 | .PHONY: pre-release 88 | 89 | update-version: update-major 90 | # no-op 91 | .PHONY: update-version 92 | 93 | ifneq (,$(wildcard ./cmd/main.go)) 94 | publish: dist 95 | ifdef NEW_VERSION 96 | ./scripts/github-release $(NEW_VERSION) 97 | else 98 | @echo -e "\033[0;31mNEW_VERSION is not defined. Can't publish :-(\033[0m" 99 | exit 1 100 | endif 101 | else 102 | publish: 103 | ifdef NEW_VERSION 104 | git tag --sign "$(LIBNAME)/go/v$(NEW_VERSION)" -m "Release $(LIBNAME)/go v$(NEW_VERSION)" 105 | git push --tags 106 | else 107 | @echo -e "\033[0;31mNEW_VERSION is not defined. Can't publish :-(\033[0m" 108 | exit 1 109 | endif 110 | endif 111 | .PHONY: publish 112 | 113 | .linted: $(GO_SOURCE_FILES) 114 | gofmt -w $^ 115 | touch $@ 116 | 117 | .tested: .deps $(GO_SOURCE_FILES) 118 | go test ./... 119 | touch $@ 120 | 121 | post-release: add-replaces 122 | ifdef NEW_VERSION 123 | pushd ../.. && \ 124 | source scripts/functions.sh && update_go_library_version $(LIBNAME) $(NEW_VERSION) && \ 125 | popd 126 | else 127 | @echo -e "\033[0;31mNEW_VERSION is not defined. Can't post-release :-(\033[0m" 128 | exit 1 129 | endif 130 | .PHONY: post-release 131 | 132 | clean: clean-go 133 | .PHONY: clean 134 | 135 | clean-go: 136 | rm -rf .deps .tested* .linted dist/ acceptance/ 137 | .PHONY: clean-go 138 | 139 | remove-replaces: 140 | sed -i '/^replace/d' go.mod 141 | sed -i 'N;/^\n$$/D;P;D;' go.mod 142 | .PHONY: remove-replaces 143 | 144 | add-replaces: 145 | ifeq ($(shell sed -n "/^\s*github.com\/cucumber/p" go.mod | wc -l), 0) 146 | # No replacements here 147 | else 148 | sed -i '/^go .*/i $(GO_REPLACEMENTS)\n' go.mod 149 | endif 150 | .PHONY: add-replaces 151 | 152 | update-major: 153 | ifeq ($(CURRENT_MAJOR), $(NEW_MAJOR)) 154 | # echo "No major version change" 155 | else 156 | echo "Updating major from $(CURRENT_MAJOR) to $(NEW_MAJOR)" 157 | sed -Ei "s/$(LIBNAME)\/go(\/v$(CURRENT_MAJOR))?/$(LIBNAME)\/go\/v$(NEW_MAJOR)/" go.mod 158 | sed -Ei "s/$(LIBNAME)\/go(\/v$(CURRENT_MAJOR))?/$(LIBNAME)\/go\/v$(NEW_MAJOR)/" $(shell find . -name "*.go") 159 | endif 160 | .PHONY: update-major 161 | 162 | ### COMMON stuff for all platforms 163 | 164 | BERP_VERSION = 1.3.0 165 | BERP_GRAMMAR = gherkin.berp 166 | 167 | define berp-generate-parser = 168 | -! dotnet tool list --tool-path /usr/bin | grep "berp\s*$(BERP_VERSION)" && dotnet tool update Berp --version $(BERP_VERSION) --tool-path /usr/bin 169 | berp -g $(BERP_GRAMMAR) -t $< -o $@ --noBOM 170 | endef 171 | -------------------------------------------------------------------------------- /go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cucumber/common/json-formatter/go/v19 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.3 6 | 7 | require ( 8 | github.com/cucumber/common/messages/go/v18 v18.0.0 9 | github.com/cucumber/common/messages/go/v19 v19.1.2 10 | github.com/onsi/ginkgo v1.16.5 11 | github.com/onsi/ginkgo/v2 v2.23.4 12 | github.com/onsi/gomega v1.37.0 13 | ) 14 | 15 | require ( 16 | github.com/fsnotify/fsnotify v1.4.9 // indirect 17 | github.com/gofrs/uuid v4.2.0+incompatible // indirect 18 | github.com/google/go-cmp v0.7.0 // indirect 19 | github.com/nxadm/tail v1.4.8 // indirect 20 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect 21 | golang.org/x/net v0.37.0 // indirect 22 | golang.org/x/sys v0.32.0 // indirect 23 | golang.org/x/text v0.23.0 // indirect 24 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cucumber/common/messages/go/v18 v18.0.0 h1:kULVfV8id18KOOfzWVrCNIXnkf4TsRPXkgyqr2Dnoec= 2 | github.com/cucumber/common/messages/go/v18 v18.0.0/go.mod h1:Q7nbIcCQXw3gWXVjuNHVTU6uIBIwUsN1mOEbxD+oSvA= 3 | github.com/cucumber/common/messages/go/v19 v19.1.2/go.mod h1:0KLDvMVmmkEZcWUSKxFHSUSLS1gjujBbPN0p41IwwJ4= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 8 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 9 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 10 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 11 | github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= 12 | github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 13 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 14 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 15 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 16 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 17 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 18 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 19 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 20 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 21 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 22 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 23 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 24 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 25 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 26 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 27 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 28 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 29 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 30 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 31 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 32 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 33 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 34 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 35 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 36 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 37 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 38 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 39 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 40 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 41 | github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= 42 | github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= 43 | github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= 44 | github.com/onsi/ginkgo/v2 v2.7.1/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU= 45 | github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU= 46 | github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= 47 | github.com/onsi/ginkgo/v2 v2.8.3/go.mod h1:6OaUA8BCi0aZfmzYT/q9AacwTzDpNbxILUT+TlBq6MY= 48 | github.com/onsi/ginkgo/v2 v2.8.4/go.mod h1:427dEDQZkDKsBvCjc2A/ZPefhKxsTTrsQegMlayL730= 49 | github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= 50 | github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= 51 | github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= 52 | github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= 53 | github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= 54 | github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= 55 | github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE= 56 | github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= 57 | github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= 58 | github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= 59 | github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= 60 | github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= 61 | github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= 62 | github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= 63 | github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= 64 | github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= 65 | github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= 66 | github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= 67 | github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= 68 | github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= 69 | github.com/onsi/ginkgo/v2 v2.18.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= 70 | github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= 71 | github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= 72 | github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= 73 | github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= 74 | github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= 75 | github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 76 | github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 77 | github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= 78 | github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= 79 | github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= 80 | github.com/onsi/ginkgo/v2 v2.23.2/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= 81 | github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= 82 | github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= 83 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 84 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 85 | github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= 86 | github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= 87 | github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM= 88 | github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= 89 | github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= 90 | github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= 91 | github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= 92 | github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= 93 | github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= 94 | github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= 95 | github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= 96 | github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= 97 | github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= 98 | github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= 99 | github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754= 100 | github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= 101 | github.com/onsi/gomega v1.27.2 h1:SKU0CXeKE/WVgIV1T61kSa3+IRE8Ekrv9rdXDwwTqnY= 102 | github.com/onsi/gomega v1.27.2/go.mod h1:5mR3phAHpkAVIDkHEUBY6HGVsU+cpcEscrGPB4oPlZI= 103 | github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk= 104 | github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= 105 | github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= 106 | github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= 107 | github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= 108 | github.com/onsi/gomega v1.27.5/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 109 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 110 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 111 | github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= 112 | github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= 113 | github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= 114 | github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= 115 | github.com/onsi/gomega v1.27.9 h1:qIyVWbOsvQEye2QCqLsNSeH/5L1RS9vS382erEWfT3o= 116 | github.com/onsi/gomega v1.27.9/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 117 | github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= 118 | github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 119 | github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c= 120 | github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8= 121 | github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= 122 | github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 123 | github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= 124 | github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 125 | github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= 126 | github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 127 | github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= 128 | github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= 129 | github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= 130 | github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= 131 | github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= 132 | github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= 133 | github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= 134 | github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= 135 | github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= 136 | github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= 137 | github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= 138 | github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= 139 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 140 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 141 | github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= 142 | github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= 143 | github.com/onsi/gomega v1.35.0 h1:xuM1M/UvMp9BCdS4hojhS9/4jEuVqS9Er3bqupeaoPM= 144 | github.com/onsi/gomega v1.35.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 145 | github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= 146 | github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 147 | github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= 148 | github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 149 | github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= 150 | github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 151 | github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= 152 | github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= 153 | github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= 154 | github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= 155 | github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= 156 | github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= 157 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 158 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 159 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 160 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 161 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 162 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 163 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 164 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 165 | github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 166 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 167 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 168 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 169 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 170 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= 171 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 172 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 173 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 174 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 175 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 176 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 177 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 178 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= 179 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 180 | golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= 181 | golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 182 | golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= 183 | golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 184 | golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= 185 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= 186 | golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= 187 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 188 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= 189 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 190 | golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 191 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 192 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 193 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 194 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 195 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 196 | golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= 197 | golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 198 | golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= 199 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 200 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 201 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 202 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 203 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 204 | golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= 205 | golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 206 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 207 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 208 | golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= 209 | golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 210 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 211 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 212 | golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= 213 | golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 214 | golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= 215 | golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= 216 | golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= 217 | golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 218 | golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= 219 | golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 220 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 221 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 222 | golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= 223 | golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= 224 | golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= 225 | golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 226 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 227 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 228 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 229 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 230 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 231 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 232 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 233 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 234 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 235 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 236 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 237 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 238 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 239 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 240 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 241 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 242 | golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= 243 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 244 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 245 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 246 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 247 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 248 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 249 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 250 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 251 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 252 | golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= 253 | golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 254 | golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= 255 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 256 | golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= 257 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 258 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 259 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 260 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 261 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 262 | golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= 263 | golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 264 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 265 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 266 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 267 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 268 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 269 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 270 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 271 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 272 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 273 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 274 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 275 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 276 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 277 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 278 | golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= 279 | golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 280 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 281 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 282 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 283 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 284 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 285 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 286 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 287 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 288 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 289 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 290 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 291 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 292 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 293 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 294 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 295 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 296 | golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= 297 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 298 | golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= 299 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 300 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 301 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 302 | golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= 303 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 304 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 305 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 306 | golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= 307 | golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 308 | golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= 309 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 310 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 311 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 312 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 313 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 314 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 315 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 316 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 317 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 318 | golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= 319 | golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 320 | golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= 321 | golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 322 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 323 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 324 | golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= 325 | golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= 326 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 327 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 328 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 329 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 330 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 331 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 332 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 333 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 334 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 335 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 336 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 337 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 338 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 339 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 340 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 341 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 342 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 343 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 344 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 345 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 346 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 347 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 348 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 349 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 350 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 351 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 352 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 353 | -------------------------------------------------------------------------------- /go/go_suite_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestGo(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Go Suite") 13 | } 14 | -------------------------------------------------------------------------------- /go/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "strings" 8 | 9 | "github.com/cucumber/common/messages/go/v18" 10 | ) 11 | 12 | type Formatter struct { 13 | lookup *MessageLookup 14 | 15 | jsonFeatures []*jsonFeature 16 | jsonFeaturesByURI map[string]*jsonFeature 17 | testCaseById map[string]*TestCase 18 | verbose bool 19 | } 20 | 21 | // ProcessMessages writes a JSON report to STDOUT 22 | func (self *Formatter) ProcessMessages(reader io.Reader, stdout io.Writer) (err error) { 23 | self.verbose = false 24 | self.lookup = &MessageLookup{} 25 | self.lookup.Initialize(self.verbose) 26 | 27 | self.jsonFeatures = make([]*jsonFeature, 0) 28 | self.jsonFeaturesByURI = make(map[string]*jsonFeature) 29 | self.testCaseById = make(map[string]*TestCase) 30 | 31 | decoder := json.NewDecoder(reader) 32 | for { 33 | envelope := &messages.Envelope{} 34 | err := decoder.Decode(envelope) 35 | if err == io.EOF { 36 | break 37 | } 38 | if err != nil { 39 | return err 40 | } 41 | 42 | err = self.lookup.ProcessMessage(envelope) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | if envelope.TestCaseStarted != nil { 48 | err, testCase := ProcessTestCaseStarted(envelope.TestCaseStarted, self.lookup) 49 | if err != nil { 50 | return err 51 | } 52 | self.testCaseById[testCase.TestCase.Id] = testCase 53 | } 54 | 55 | if envelope.TestStepFinished != nil { 56 | err, testStep := ProcessTestStepFinished(envelope.TestStepFinished, self.lookup) 57 | if err != nil { 58 | return err 59 | } 60 | testCase := self.testCaseById[testStep.TestCaseID] 61 | if testCase == nil { 62 | keys := make([]string, 0, len(self.testCaseById)) 63 | for k := range self.testCaseById { 64 | keys = append(keys, k) 65 | } 66 | panic("No testCase for " + testStep.TestCaseID + strings.Join(keys, ", ")) 67 | } 68 | testCase.appendStep(testStep) 69 | } 70 | 71 | if envelope.TestCaseFinished != nil { 72 | testCaseStarted := self.lookup.LookupTestCaseStarted(envelope.TestCaseFinished.TestCaseStartedId) 73 | testCase, ok := self.testCaseById[testCaseStarted.TestCaseId] 74 | 75 | if ok { 76 | jsonFeature := self.findOrCreateJsonFeature(testCase.Pickle) 77 | for _, jsonElement := range TestCaseToJSON(testCase) { 78 | jsonFeature.Elements = append(jsonFeature.Elements, jsonElement) 79 | } 80 | } 81 | } 82 | } 83 | 84 | output, _ := json.MarshalIndent(self.jsonFeatures, "", " ") 85 | _, err = fmt.Fprintln(stdout, string(output)) 86 | return err 87 | } 88 | 89 | func (self *Formatter) findOrCreateJsonFeature(pickle *messages.Pickle) *jsonFeature { 90 | jFeature, ok := self.jsonFeaturesByURI[pickle.Uri] 91 | if !ok { 92 | gherkinDocumentFeature := self.lookup.LookupGherkinDocument(pickle.Uri).Feature 93 | 94 | jFeature = &jsonFeature{ 95 | Description: gherkinDocumentFeature.Description, 96 | Elements: make([]*jsonFeatureElement, 0), 97 | ID: self.makeId(gherkinDocumentFeature.Name), 98 | Keyword: gherkinDocumentFeature.Keyword, 99 | Line: uint32(gherkinDocumentFeature.Location.Line), 100 | Name: gherkinDocumentFeature.Name, 101 | URI: pickle.Uri, 102 | Tags: make([]*jsonTag, len(gherkinDocumentFeature.Tags)), 103 | } 104 | 105 | for tagIndex, tag := range gherkinDocumentFeature.Tags { 106 | jFeature.Tags[tagIndex] = &jsonTag{ 107 | Line: uint32(tag.Location.Line), 108 | Name: tag.Name, 109 | } 110 | } 111 | 112 | self.jsonFeaturesByURI[pickle.Uri] = jFeature 113 | self.jsonFeatures = append(self.jsonFeatures, jFeature) 114 | } 115 | return jFeature 116 | } 117 | 118 | func (self *Formatter) makeId(s string) string { 119 | return strings.ToLower(strings.Replace(s, " ", "-", -1)) 120 | } 121 | 122 | func (self *Formatter) comment(message string) { 123 | if self.verbose { 124 | fmt.Println(fmt.Sprintf("// Formatter: %s", message)) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /go/json_elements.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | type jsonFeature struct { 4 | Description string `json:"description"` 5 | Elements []*jsonFeatureElement `json:"elements"` 6 | ID string `json:"id"` 7 | Keyword string `json:"keyword"` 8 | Line uint32 `json:"line"` 9 | Name string `json:"name"` 10 | URI string `json:"uri"` 11 | Tags []*jsonTag `json:"tags,omitempty"` 12 | } 13 | 14 | type jsonFeatureElement struct { 15 | Description string `json:"description"` 16 | ID string `json:"id,omitempty"` 17 | Keyword string `json:"keyword"` 18 | Line uint32 `json:"line"` 19 | Name string `json:"name"` 20 | Before []*jsonStep `json:"before,omitempty"` 21 | Steps []*jsonStep `json:"steps"` 22 | After []*jsonStep `json:"after,omitempty"` 23 | Type string `json:"type"` 24 | Tags []*jsonTag `json:"tags,omitempty"` 25 | } 26 | 27 | type jsonStep struct { 28 | Keyword string `json:"keyword,omitempty"` 29 | Line uint32 `json:"line,omitempty"` 30 | Name string `json:"name,omitempty"` 31 | Result *jsonStepResult `json:"result"` 32 | Match *jsonStepMatch `json:"match,omitempty"` 33 | DocString *jsonDocString `json:"doc_string,omitempty"` 34 | Rows []*jsonDatatableRow `json:"rows,omitempty"` 35 | Embeddings []*jsonEmbedding `json:"embeddings,omitempty"` 36 | Output []string `json:"output,omitempty"` 37 | } 38 | 39 | type jsonDocString struct { 40 | ContentType string `json:"content_type"` 41 | Line uint32 `json:"line"` 42 | Value string `json:"value"` 43 | } 44 | 45 | type jsonDatatableRow struct { 46 | Cells []string `json:"cells"` 47 | } 48 | 49 | type jsonStepResult struct { 50 | Duration uint64 `json:"duration,omitempty"` 51 | Status string `json:"status"` 52 | ErrorMessage string `json:"error_message,omitempty"` 53 | } 54 | 55 | type jsonStepMatch struct { 56 | Location string `json:"location"` 57 | } 58 | 59 | type jsonTag struct { 60 | Line uint32 `json:"line"` 61 | Name string `json:"name"` 62 | } 63 | 64 | type jsonEmbedding struct { 65 | Data string `json:"data"` 66 | MimeType string `json:"mime_type"` 67 | } 68 | -------------------------------------------------------------------------------- /go/message_lookup.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cucumber/common/messages/go/v18" 7 | ) 8 | 9 | type MessageLookup struct { 10 | gherkinDocumentByURI map[string]*messages.GherkinDocument 11 | pickleByID map[string]*messages.Pickle 12 | pickleStepByID map[string]*messages.PickleStep 13 | testCaseByID map[string]*messages.TestCase 14 | testStepByID map[string]*messages.TestStep 15 | testCaseStartedByID map[string]*messages.TestCaseStarted 16 | stepByID map[string]*messages.Step 17 | scenarioByID map[string]*messages.Scenario 18 | exampleByRowID map[string]*messages.Examples 19 | exampleRowByID map[string]*messages.TableRow 20 | stepDefinitionByID map[string]*messages.StepDefinition 21 | backgroundByStepID map[string]*messages.Background 22 | tagByID map[string]*messages.Tag 23 | hookByID map[string]*messages.Hook 24 | attachmentsByTestStepID map[string][]*messages.Attachment 25 | verbose bool 26 | } 27 | 28 | func (ml *MessageLookup) Initialize(verbose bool) { 29 | ml.gherkinDocumentByURI = make(map[string]*messages.GherkinDocument) 30 | ml.pickleByID = make(map[string]*messages.Pickle) 31 | ml.pickleStepByID = make(map[string]*messages.PickleStep) 32 | ml.testCaseByID = make(map[string]*messages.TestCase) 33 | ml.testStepByID = make(map[string]*messages.TestStep) 34 | ml.testCaseStartedByID = make(map[string]*messages.TestCaseStarted) 35 | ml.stepByID = make(map[string]*messages.Step) 36 | ml.scenarioByID = make(map[string]*messages.Scenario) 37 | ml.exampleByRowID = make(map[string]*messages.Examples) 38 | ml.exampleRowByID = make(map[string]*messages.TableRow) 39 | ml.stepDefinitionByID = make(map[string]*messages.StepDefinition) 40 | ml.backgroundByStepID = make(map[string]*messages.Background) 41 | ml.tagByID = make(map[string]*messages.Tag) 42 | ml.hookByID = make(map[string]*messages.Hook) 43 | ml.attachmentsByTestStepID = make(map[string][]*messages.Attachment) 44 | 45 | ml.verbose = verbose 46 | } 47 | 48 | func (ml *MessageLookup) ProcessMessage(envelope *messages.Envelope) (err error) { 49 | if envelope.GherkinDocument != nil { 50 | ml.gherkinDocumentByURI[envelope.GherkinDocument.Uri] = envelope.GherkinDocument 51 | ml.comment(fmt.Sprintf("Stored GherkinDocument: %s", envelope.GherkinDocument.Uri)) 52 | for key := range ml.gherkinDocumentByURI { 53 | ml.comment(fmt.Sprintf(" - %s ", key)) 54 | } 55 | if envelope.GherkinDocument.Feature == nil { 56 | return nil 57 | } 58 | ml.processTags(envelope.GherkinDocument.Feature.Tags) 59 | 60 | for _, child := range envelope.GherkinDocument.Feature.Children { 61 | ml.processRule(child.Rule) 62 | ml.processBackground(child.Background) 63 | ml.processScenario(child.Scenario) 64 | } 65 | } 66 | 67 | if envelope.Pickle != nil { 68 | ml.pickleByID[envelope.Pickle.Id] = envelope.Pickle 69 | 70 | for _, step := range envelope.Pickle.Steps { 71 | ml.pickleStepByID[step.Id] = step 72 | } 73 | } 74 | 75 | if envelope.TestCase != nil { 76 | ml.testCaseByID[envelope.TestCase.Id] = envelope.TestCase 77 | 78 | for _, step := range envelope.TestCase.TestSteps { 79 | ml.testStepByID[step.Id] = step 80 | } 81 | } 82 | 83 | if envelope.TestCaseStarted != nil { 84 | ml.testCaseStartedByID[envelope.TestCaseStarted.Id] = envelope.TestCaseStarted 85 | } 86 | 87 | if envelope.Attachment != nil { 88 | attachments, ok := ml.attachmentsByTestStepID[envelope.Attachment.TestStepId] 89 | if !ok { 90 | attachments = make([]*messages.Attachment, 0) 91 | ml.attachmentsByTestStepID[envelope.Attachment.TestStepId] = attachments 92 | } 93 | attachments = append(attachments, envelope.Attachment) 94 | ml.attachmentsByTestStepID[envelope.Attachment.TestStepId] = attachments 95 | } 96 | 97 | if envelope.StepDefinition != nil { 98 | ml.stepDefinitionByID[envelope.StepDefinition.Id] = envelope.StepDefinition 99 | } 100 | 101 | if envelope.Hook != nil { 102 | ml.hookByID[envelope.Hook.Id] = envelope.Hook 103 | } 104 | 105 | return nil 106 | } 107 | 108 | func (ml *MessageLookup) processTags(tags []*messages.Tag) { 109 | for _, tag := range tags { 110 | ml.tagByID[tag.Id] = tag 111 | } 112 | } 113 | 114 | func (ml *MessageLookup) processRule(rule *messages.Rule) { 115 | if rule != nil { 116 | for _, ruleChild := range rule.Children { 117 | ml.processBackground(ruleChild.Background) 118 | ml.processScenario(ruleChild.Scenario) 119 | } 120 | } 121 | } 122 | 123 | func (ml *MessageLookup) processBackground(background *messages.Background) { 124 | if background != nil { 125 | for _, step := range background.Steps { 126 | ml.backgroundByStepID[step.Id] = background 127 | ml.stepByID[step.Id] = step 128 | } 129 | } 130 | } 131 | 132 | func (ml *MessageLookup) processScenario(scenario *messages.Scenario) { 133 | if scenario != nil { 134 | ml.scenarioByID[scenario.Id] = scenario 135 | ml.processTags(scenario.Tags) 136 | 137 | for _, step := range scenario.Steps { 138 | ml.stepByID[step.Id] = step 139 | } 140 | 141 | for _, example := range scenario.Examples { 142 | ml.processTags(example.Tags) 143 | 144 | for _, row := range example.TableBody { 145 | // TODO: we may also need to add IDs to the examples 146 | ml.exampleByRowID[row.Id] = example 147 | ml.exampleRowByID[row.Id] = row 148 | } 149 | } 150 | } 151 | } 152 | 153 | func (ml *MessageLookup) LookupGherkinDocument(uri string) *messages.GherkinDocument { 154 | item, ok := ml.gherkinDocumentByURI[uri] 155 | if ok { 156 | ml.informFoundKey(uri, "gherkinDocumentByURI") 157 | } else { 158 | ml.informMissingKey(uri, "gherkinDocumentByURI") 159 | } 160 | return item 161 | } 162 | 163 | func (ml *MessageLookup) LookupScenario(id string) *messages.Scenario { 164 | item, ok := ml.scenarioByID[id] 165 | if ok { 166 | ml.informFoundKey(id, "scenarioByID") 167 | } else { 168 | ml.informMissingKey(id, "scenarioByID") 169 | } 170 | return item 171 | } 172 | 173 | func (ml *MessageLookup) LookupPickle(id string) *messages.Pickle { 174 | item, ok := ml.pickleByID[id] 175 | if ok { 176 | ml.informFoundKey(id, "pickleByID") 177 | } else { 178 | ml.informMissingKey(id, "pickleByID") 179 | } 180 | return item 181 | } 182 | 183 | func (ml *MessageLookup) LookupStep(id string) *messages.Step { 184 | item, ok := ml.stepByID[id] 185 | if ok { 186 | ml.informFoundKey(id, "stepByID") 187 | } else { 188 | ml.informMissingKey(id, "stepByID") 189 | } 190 | return item 191 | } 192 | 193 | func (ml *MessageLookup) LookupExample(id string) *messages.Examples { 194 | item, ok := ml.exampleByRowID[id] 195 | if ok { 196 | ml.informFoundKey(id, "exampleByRowID") 197 | } else { 198 | ml.informMissingKey(id, "exampleByRowID") 199 | } 200 | return item 201 | } 202 | 203 | func (ml *MessageLookup) LookupExampleRow(id string) *messages.TableRow { 204 | item, ok := ml.exampleRowByID[id] 205 | if ok { 206 | ml.informFoundKey(id, "exampleRowByID") 207 | } else { 208 | ml.informMissingKey(id, "exampleRowByID") 209 | } 210 | return item 211 | } 212 | 213 | func (ml *MessageLookup) LookupBackgroundByStepID(id string) *messages.Background { 214 | item, ok := ml.backgroundByStepID[id] 215 | if ok { 216 | ml.informFoundKey(id, "backgroundByStepID") 217 | } else { 218 | ml.informMissingKey(id, "backgroundByStepID") 219 | } 220 | return item 221 | } 222 | 223 | func (ml *MessageLookup) LookupTag(id string) *messages.Tag { 224 | item, ok := ml.tagByID[id] 225 | if ok { 226 | ml.informFoundKey(id, "tagByID") 227 | } else { 228 | ml.informMissingKey(id, "tagByID") 229 | } 230 | return item 231 | } 232 | 233 | func (ml *MessageLookup) LookupTestCaseStarted(id string) *messages.TestCaseStarted { 234 | item, ok := ml.testCaseStartedByID[id] 235 | if ok { 236 | ml.informFoundKey(id, "testCaseStartedByID") 237 | } else { 238 | ml.informMissingKey(id, "testCaseStartedByID") 239 | } 240 | return item 241 | } 242 | 243 | func (ml *MessageLookup) LookupTestCase(id string) *messages.TestCase { 244 | item, ok := ml.testCaseByID[id] 245 | if ok { 246 | ml.informFoundKey(id, "testCaseByID") 247 | } else { 248 | ml.informMissingKey(id, "testCaseByID") 249 | } 250 | return item 251 | } 252 | 253 | func (ml *MessageLookup) LookupTestStep(id string) *messages.TestStep { 254 | item, ok := ml.testStepByID[id] 255 | if ok { 256 | ml.informFoundKey(id, "testStepByID") 257 | } else { 258 | ml.informMissingKey(id, "testStepByID") 259 | } 260 | return item 261 | } 262 | 263 | func (ml *MessageLookup) LookupPickleStep(id string) *messages.PickleStep { 264 | item, ok := ml.pickleStepByID[id] 265 | if ok { 266 | ml.informFoundKey(id, "pickleStepByID") 267 | } else { 268 | ml.informMissingKey(id, "pickleStepByID") 269 | } 270 | return item 271 | } 272 | 273 | func (ml *MessageLookup) LookupStepDefinitions(ids []string) []*messages.StepDefinition { 274 | stepDefinitions := make([]*messages.StepDefinition, len(ids)) 275 | for index, id := range ids { 276 | stepDefinitions[index] = ml.LookupStepDefinition(id) 277 | } 278 | return stepDefinitions 279 | } 280 | 281 | func (ml *MessageLookup) LookupStepDefinition(id string) *messages.StepDefinition { 282 | item, ok := ml.stepDefinitionByID[id] 283 | if ok { 284 | ml.informFoundKey(id, "stepDefinitionByID") 285 | } else { 286 | ml.informMissingKey(id, "stepDefinitionByID") 287 | } 288 | return item 289 | } 290 | 291 | func (ml *MessageLookup) LookupHook(id string) *messages.Hook { 292 | item, ok := ml.hookByID[id] 293 | if ok { 294 | ml.informFoundKey(id, "hookByID") 295 | } else { 296 | ml.informMissingKey(id, "hookByID") 297 | } 298 | return item 299 | } 300 | 301 | func (ml *MessageLookup) LookupAttachments(testStepId string) []*messages.Attachment { 302 | item, ok := ml.attachmentsByTestStepID[testStepId] 303 | if ok { 304 | ml.informFoundKey(testStepId, "attachmentsByTestStepID") 305 | } else { 306 | ml.informMissingKey(testStepId, "attachmentsByTestStepID") 307 | } 308 | return item 309 | } 310 | 311 | func (ml *MessageLookup) informFoundKey(key string, mapName string) { 312 | ml.comment(fmt.Sprintf("Found item'%s' in %s", key, mapName)) 313 | } 314 | 315 | func (ml *MessageLookup) informMissingKey(key string, mapName string) { 316 | ml.comment(fmt.Sprintf("Unable to find '%s' in %s", key, mapName)) 317 | } 318 | 319 | func (ml *MessageLookup) comment(message string) { 320 | if ml.verbose { 321 | fmt.Println(fmt.Sprintf("// LookUp: %s", message)) 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /go/message_lookup_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/cucumber/common/messages/go/v18" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("MessageLookup", func() { 11 | var ml MessageLookup 12 | 13 | JustBeforeEach(func() { 14 | ml = MessageLookup{} 15 | ml.Initialize(true) 16 | }) 17 | 18 | Context("MessageLookup.processScenario", func() { 19 | var scenario messages.Scenario 20 | 21 | BeforeEach(func() { 22 | scenario = messages.Scenario{} 23 | }) 24 | 25 | When("the scenario contains tagged examples", func() { 26 | var ( 27 | tag messages.Tag 28 | example messages.Examples 29 | ) 30 | 31 | BeforeEach(func() { 32 | tag = messages.Tag{ 33 | Id: "tag-id", 34 | Name: "@the-tag", 35 | } 36 | 37 | example = messages.Examples{ 38 | Tags: []*messages.Tag{ 39 | &tag, 40 | }, 41 | } 42 | 43 | scenario.Examples = append(scenario.Examples, &example) 44 | }) 45 | 46 | It("processes example's tags", func() { 47 | ml.processScenario(&scenario) 48 | Expect(ml.LookupTag("tag-id")).To(Equal(&tag)) 49 | }) 50 | }) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /go/scripts/github-release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Creates a GitHub release and uploads all the executables 4 | # 5 | set -euf -o pipefail 6 | 7 | version=$1 8 | libname=$(basename $(dirname $(pwd))) 9 | git_tag_name=${libname}/go 10 | exe_base_name=cucumber-${libname} 11 | add_args=$(find dist -type f -name "${exe_base_name}-*" | \ 12 | # Replace newline with space 13 | tr '\n' ' ' | \ 14 | # Remove trailing space 15 | sed -e 's/[[:space:]]*$//' | \ 16 | # Insert ' -a ' between all files 17 | sed "s/[[:space:]]/ -a /g") 18 | 19 | eval hub release create \ 20 | --attach ${add_args} \ 21 | --message "${git_tag_name}/v${version}" "${git_tag_name}/v${version}" 22 | -------------------------------------------------------------------------------- /go/test_case.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/cucumber/common/messages/go/v18" 9 | ) 10 | 11 | type TestCase struct { 12 | FeatureName string 13 | Scenario *messages.Scenario 14 | Pickle *messages.Pickle 15 | TestCase *messages.TestCase 16 | Steps []*TestStep 17 | Tags []*messages.Tag 18 | } 19 | 20 | type SortedSteps struct { 21 | BeforeHook []*TestStep 22 | Background []*TestStep 23 | Steps []*TestStep 24 | AfterHook []*TestStep 25 | } 26 | 27 | func ProcessTestCaseStarted(testCaseStarted *messages.TestCaseStarted, lookup *MessageLookup) (error, *TestCase) { 28 | testCase := lookup.LookupTestCase(testCaseStarted.TestCaseId) 29 | if testCase == nil { 30 | return errors.New("No testCase for " + testCaseStarted.TestCaseId), nil 31 | } 32 | 33 | pickle := lookup.LookupPickle(testCase.PickleId) 34 | if pickle == nil || len(pickle.AstNodeIds) == 0 { 35 | return errors.New("No pickle for " + testCase.PickleId), nil 36 | } 37 | 38 | tags := make([]*messages.Tag, len(pickle.Tags)) 39 | for index, tag := range pickle.Tags { 40 | sourceTag := lookup.LookupTag(tag.AstNodeId) 41 | if sourceTag == nil { 42 | return errors.New("No sourceTag for " + tag.AstNodeId), nil 43 | } 44 | tags[index] = sourceTag 45 | } 46 | 47 | scenario := lookup.LookupScenario(pickle.AstNodeIds[0]) 48 | if scenario == nil { 49 | return errors.New(fmt.Sprintf("No scenario for %s", strings.Join(pickle.AstNodeIds, ", "))), nil 50 | } 51 | 52 | feature := lookup.LookupGherkinDocument(pickle.Uri) 53 | if feature == nil { 54 | return errors.New("No feature for " + pickle.Uri), nil 55 | } 56 | featureName := feature.Feature.Name 57 | 58 | return nil, &TestCase{ 59 | FeatureName: featureName, 60 | Scenario: scenario, 61 | Pickle: pickle, 62 | TestCase: testCase, 63 | Steps: make([]*TestStep, 0), 64 | Tags: tags, 65 | } 66 | } 67 | 68 | func TestCaseToJSON(testCase *TestCase) []*jsonFeatureElement { 69 | elements := make([]*jsonFeatureElement, 0) 70 | sortedSteps := testCase.SortedSteps() 71 | 72 | if len(sortedSteps.Background) > 0 { 73 | elements = append(elements, backgroundStepsToJSON(sortedSteps.Background)) 74 | } 75 | elements = append(elements, scenarioStepsToJSON(testCase, sortedSteps.Steps)) 76 | 77 | if len(sortedSteps.BeforeHook) > 0 { 78 | elements[0].Before = makeJSONSteps(sortedSteps.BeforeHook) 79 | } 80 | 81 | if len(sortedSteps.AfterHook) > 0 { 82 | elements[len(elements)-1].After = makeJSONSteps(sortedSteps.AfterHook) 83 | } 84 | 85 | return elements 86 | } 87 | 88 | func backgroundStepsToJSON(steps []*TestStep) *jsonFeatureElement { 89 | background := steps[0].Background 90 | 91 | return &jsonFeatureElement{ 92 | Keyword: background.Keyword, 93 | Name: background.Name, 94 | Description: background.Description, 95 | Line: uint32(background.Location.Line), 96 | Type: "background", 97 | Steps: makeJSONSteps(steps), 98 | } 99 | } 100 | 101 | func scenarioStepsToJSON(testCase *TestCase, steps []*TestStep) *jsonFeatureElement { 102 | line := testCase.Scenario.Location.Line 103 | id := fmt.Sprintf("%s;%s", makeID(testCase.FeatureName), makeID(testCase.Scenario.Name)) 104 | if len(testCase.Pickle.AstNodeIds) > 1 { 105 | exampleName := "" 106 | exampleIndex := 0 107 | 108 | for _, example := range testCase.Scenario.Examples { 109 | for index, row := range example.TableBody { 110 | if row.Id == testCase.Pickle.AstNodeIds[1] { 111 | line = row.Location.Line 112 | exampleName = example.Name 113 | // +2 as the index is a one-based index and the table header is taken into account 114 | exampleIndex = index + 2 115 | } 116 | } 117 | } 118 | id = fmt.Sprintf( 119 | "%s;%s;%s;%d", 120 | makeID(testCase.FeatureName), 121 | makeID(testCase.Scenario.Name), 122 | makeID(exampleName), 123 | exampleIndex, 124 | ) 125 | } 126 | 127 | return &jsonFeatureElement{ 128 | ID: id, 129 | Keyword: testCase.Scenario.Keyword, 130 | Type: "scenario", 131 | Name: testCase.Scenario.Name, 132 | Description: testCase.Scenario.Description, 133 | Line: uint32(line), 134 | Steps: makeJSONSteps(steps), 135 | Tags: makeJSONTags(testCase.Tags), 136 | } 137 | } 138 | 139 | func makeJSONSteps(steps []*TestStep) []*jsonStep { 140 | jsonSteps := make([]*jsonStep, len(steps)) 141 | for index, step := range steps { 142 | jsonSteps[index] = TestStepToJSON(step) 143 | } 144 | return jsonSteps 145 | } 146 | 147 | func makeJSONTags(tags []*messages.Tag) []*jsonTag { 148 | jsonTags := make([]*jsonTag, len(tags)) 149 | for index, tag := range tags { 150 | jsonTags[index] = &jsonTag{ 151 | Name: tag.Name, 152 | Line: uint32(tag.Location.Line), 153 | } 154 | } 155 | return jsonTags 156 | } 157 | 158 | func (self *TestCase) appendStep(step *TestStep) { 159 | self.Steps = append(self.Steps, step) 160 | } 161 | 162 | func (self *TestCase) SortedSteps() *SortedSteps { 163 | sorted := &SortedSteps{} 164 | current := &sorted.BeforeHook 165 | 166 | for _, step := range self.Steps { 167 | if current == &sorted.BeforeHook && step.Hook == nil { 168 | current = &sorted.Background 169 | } 170 | if current == &sorted.Background && step.Background == nil { 171 | current = &sorted.Steps 172 | } 173 | if current == &sorted.Steps && step.Hook != nil { 174 | current = &sorted.AfterHook 175 | } 176 | 177 | *current = append(*current, step) 178 | } 179 | 180 | return sorted 181 | } 182 | 183 | func makeID(s string) string { 184 | return strings.ToLower(strings.Replace(s, " ", "-", -1)) 185 | } 186 | -------------------------------------------------------------------------------- /go/test_case_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/cucumber/common/messages/go/v18" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | ) 8 | 9 | var _ = Describe("TestCase.appendStep", func() { 10 | var ( 11 | testCase *TestCase 12 | hookTestStep *TestStep 13 | pickleTestStep *TestStep 14 | ) 15 | 16 | BeforeEach(func() { 17 | testCase = &TestCase{ 18 | Steps: make([]*TestStep, 0), 19 | } 20 | hookTestStep = &TestStep{ 21 | Hook: &messages.Hook{ 22 | Id: "my-hook", 23 | }, 24 | } 25 | pickleTestStep = &TestStep{ 26 | PickleStep: &messages.PickleStep{ 27 | Id: "pickle-step", 28 | }, 29 | } 30 | }) 31 | 32 | It("appends a TestStep to the list of steps", func() { 33 | Expect(len(testCase.Steps)).To(Equal(0)) 34 | testCase.appendStep(hookTestStep) 35 | Expect(len(testCase.Steps)).To(Equal(1)) 36 | }) 37 | 38 | It("does not lose existing steps", func() { 39 | testCase.appendStep(hookTestStep) 40 | testCase.appendStep(pickleTestStep) 41 | 42 | Expect(testCase.Steps[0].Hook.Id).To(Equal("my-hook")) 43 | Expect(testCase.Steps[1].PickleStep.Id).To(Equal("pickle-step")) 44 | }) 45 | }) 46 | 47 | var _ = Describe("TestCase.SortedSteps", func() { 48 | var ( 49 | firstBeforeHookStep *TestStep 50 | secondBeforeHookStep *TestStep 51 | firstBackgroundStep *TestStep 52 | secondBackgroundStep *TestStep 53 | firstPickleStep *TestStep 54 | secondPickleStep *TestStep 55 | firstAfterHookStep *TestStep 56 | secondAfterHookStep *TestStep 57 | ) 58 | BeforeEach(func() { 59 | firstBeforeHookStep = &TestStep{ 60 | Hook: &messages.Hook{ 61 | Id: "hook-1", 62 | }, 63 | } 64 | secondBeforeHookStep = &TestStep{ 65 | Hook: &messages.Hook{ 66 | Id: "hook-2", 67 | }, 68 | } 69 | 70 | background := &messages.Background{} 71 | firstBackgroundStep = &TestStep{ 72 | Background: background, 73 | PickleStep: &messages.PickleStep{ 74 | Id: "step-1", 75 | }, 76 | } 77 | 78 | secondBackgroundStep = &TestStep{ 79 | Background: background, 80 | PickleStep: &messages.PickleStep{ 81 | Id: "step-2", 82 | }, 83 | } 84 | 85 | firstPickleStep = &TestStep{ 86 | PickleStep: &messages.PickleStep{ 87 | Id: "step-3", 88 | }, 89 | } 90 | 91 | secondPickleStep = &TestStep{ 92 | PickleStep: &messages.PickleStep{ 93 | Id: "step-4", 94 | }, 95 | } 96 | 97 | firstAfterHookStep = &TestStep{ 98 | Hook: &messages.Hook{ 99 | Id: "hook-3", 100 | }, 101 | } 102 | secondAfterHookStep = &TestStep{ 103 | Hook: &messages.Hook{ 104 | Id: "hook-4", 105 | }, 106 | } 107 | }) 108 | 109 | It("returns a SortedSteps", func() { 110 | testCase := &TestCase{} 111 | sorted := testCase.SortedSteps() 112 | 113 | Expect(len(sorted.BeforeHook)).To(Equal(0)) 114 | Expect(len(sorted.Background)).To(Equal(0)) 115 | Expect(len(sorted.Steps)).To(Equal(0)) 116 | Expect(len(sorted.AfterHook)).To(Equal(0)) 117 | }) 118 | 119 | It("adds the steps coming from the Scenario in SortedSteps.Steps", func() { 120 | testCase := &TestCase{ 121 | Steps: []*TestStep{firstPickleStep}, 122 | } 123 | sorted := testCase.SortedSteps() 124 | 125 | Expect(sorted.Steps[0]).To(Equal(firstPickleStep)) 126 | }) 127 | 128 | It("adds the background steps in SortedSteps.Background", func() { 129 | testCase := &TestCase{ 130 | Steps: []*TestStep{firstBackgroundStep}, 131 | } 132 | sorted := testCase.SortedSteps() 133 | 134 | Expect(sorted.Background[0]).To(Equal(firstBackgroundStep)) 135 | }) 136 | 137 | It("adds any Hook step before a PickleStep in SortedSteps.BeforeHook", func() { 138 | testCase := &TestCase{ 139 | Steps: []*TestStep{ 140 | firstBeforeHookStep, 141 | firstPickleStep, 142 | }, 143 | } 144 | sorted := testCase.SortedSteps() 145 | 146 | Expect(sorted.BeforeHook[0]).To(Equal(firstBeforeHookStep)) 147 | Expect(sorted.Steps[0]).To(Equal(firstPickleStep)) 148 | }) 149 | 150 | It("adds any Hook step after a PickleStep in SortedSteps.AfterHook", func() { 151 | testCase := &TestCase{ 152 | Steps: []*TestStep{ 153 | firstPickleStep, 154 | firstAfterHookStep, 155 | }, 156 | } 157 | sorted := testCase.SortedSteps() 158 | 159 | Expect(sorted.Steps[0]).To(Equal(firstPickleStep)) 160 | Expect(sorted.AfterHook[0]).To(Equal(firstAfterHookStep)) 161 | }) 162 | 163 | It("keeps correct order of the steps", func() { 164 | testCase := &TestCase{ 165 | Steps: []*TestStep{ 166 | firstBeforeHookStep, 167 | secondBeforeHookStep, 168 | firstBackgroundStep, 169 | secondBackgroundStep, 170 | firstPickleStep, 171 | secondPickleStep, 172 | firstAfterHookStep, 173 | secondAfterHookStep, 174 | }, 175 | } 176 | sorted := testCase.SortedSteps() 177 | 178 | Expect(sorted.BeforeHook[0]).To(Equal(firstBeforeHookStep)) 179 | Expect(sorted.BeforeHook[1]).To(Equal(secondBeforeHookStep)) 180 | Expect(sorted.Background[0]).To(Equal(firstBackgroundStep)) 181 | Expect(sorted.Background[1]).To(Equal(secondBackgroundStep)) 182 | Expect(sorted.Steps[0]).To(Equal(firstPickleStep)) 183 | Expect(sorted.Steps[1]).To(Equal(secondPickleStep)) 184 | Expect(sorted.AfterHook[0]).To(Equal(firstAfterHookStep)) 185 | Expect(sorted.AfterHook[1]).To(Equal(secondAfterHookStep)) 186 | }) 187 | }) 188 | 189 | var _ = Describe("ProcessTestCaseStarted", func() { 190 | var ( 191 | lookup *MessageLookup 192 | pickle *messages.Pickle 193 | testCase *TestCase 194 | document *messages.GherkinDocument 195 | scenario *messages.Scenario 196 | ) 197 | 198 | BeforeEach(func() { 199 | lookup = &MessageLookup{} 200 | lookup.Initialize(false) 201 | 202 | // This is a bit dirty hack to avoid creating all the AST 203 | document = &messages.GherkinDocument{ 204 | Uri: "feature-uri", 205 | Feature: &messages.Feature{ 206 | Name: "My feature", 207 | }, 208 | } 209 | scenario = makeScenario("scenario-id", []*messages.Step{}) 210 | tag := &messages.Tag{ 211 | Id: "tag-id", 212 | Name: "@scenario-tag", 213 | } 214 | 215 | lookup.gherkinDocumentByURI[document.Uri] = document 216 | lookup.scenarioByID[scenario.Id] = scenario 217 | lookup.tagByID[tag.Id] = tag 218 | 219 | pickle = &messages.Pickle{ 220 | Id: "pickle-id", 221 | Uri: document.Uri, 222 | AstNodeIds: []string{scenario.Id}, 223 | Tags: []*messages.PickleTag{ 224 | { 225 | AstNodeId: tag.Id, 226 | }, 227 | }, 228 | } 229 | lookup.ProcessMessage(makePickleEnvelope(pickle)) 230 | 231 | testCaseMsg := makeTestCase( 232 | "test-case-id", 233 | pickle.Id, 234 | []*messages.TestStep{}, 235 | ) 236 | lookup.ProcessMessage(makeTestCaseEnvelope(testCaseMsg)) 237 | 238 | _, testCase = ProcessTestCaseStarted(&messages.TestCaseStarted{ 239 | TestCaseId: testCaseMsg.Id, 240 | }, lookup) 241 | }) 242 | 243 | It("Has the feature name", func() { 244 | Expect(testCase.FeatureName).To(Equal("My feature")) 245 | }) 246 | 247 | It("has a reference to the TestCase (message)", func() { 248 | Expect(testCase.TestCase.Id).To(Equal("test-case-id")) 249 | }) 250 | 251 | It("has a reference to the Pickle", func() { 252 | Expect(testCase.Pickle.Id).To(Equal("pickle-id")) 253 | }) 254 | 255 | It("has a reference to the scenario", func() { 256 | Expect(testCase.Scenario.Id).To(Equal("scenario-id")) 257 | }) 258 | 259 | It("has the tags", func() { 260 | Expect(testCase.Tags[0].Id).To(Equal("tag-id")) 261 | Expect(testCase.Tags[0].Name).To(Equal("@scenario-tag")) 262 | }) 263 | 264 | Context("with referencing issues", func() { 265 | var ( 266 | testCaseReferencingUnknownPickle *messages.TestCase 267 | testCaseForPickleWithoutSource *messages.TestCase 268 | testCaseForPickleWithWrongScenarioReference *messages.TestCase 269 | testCaseForPickleWithWrongDocumentURI *messages.TestCase 270 | testCaseForPickleWithUnknownTag *messages.TestCase 271 | ) 272 | 273 | BeforeEach(func() { 274 | testCaseReferencingUnknownPickle = makeTestCase( 275 | "wrong-pickle-test-case", 276 | "unknown-pickle-id", 277 | []*messages.TestStep{}, 278 | ) 279 | lookup.ProcessMessage(makeTestCaseEnvelope(testCaseReferencingUnknownPickle)) 280 | 281 | pickleWithoutSource := &messages.Pickle{ 282 | Id: "empty-pickle-id", 283 | AstNodeIds: []string{}, 284 | } 285 | testCaseForPickleWithoutSource = makeTestCase( 286 | "empty-pickle-test-case", 287 | pickleWithoutSource.Id, 288 | []*messages.TestStep{}, 289 | ) 290 | lookup.ProcessMessage(makePickleEnvelope(pickleWithoutSource)) 291 | lookup.ProcessMessage(makeTestCaseEnvelope(testCaseForPickleWithoutSource)) 292 | 293 | pickleWithWrongScenarioReference := &messages.Pickle{ 294 | Id: "unknown-scenario-pickle", 295 | AstNodeIds: []string{"this-is-not-a-scenario"}, 296 | } 297 | testCaseForPickleWithWrongScenarioReference = makeTestCase( 298 | "unknow-scenario-test-case", 299 | pickleWithWrongScenarioReference.Id, 300 | []*messages.TestStep{}, 301 | ) 302 | lookup.ProcessMessage(makePickleEnvelope(pickleWithWrongScenarioReference)) 303 | lookup.ProcessMessage(makeTestCaseEnvelope(testCaseForPickleWithWrongScenarioReference)) 304 | 305 | pickleWithWrongDocumentURI := &messages.Pickle{ 306 | Uri: "some-file-hardly-be-found", 307 | Id: "wrong-document-uri", 308 | AstNodeIds: []string{scenario.Id}, 309 | } 310 | testCaseForPickleWithWrongDocumentURI = makeTestCase( 311 | "wrong-document-uri", 312 | pickleWithWrongDocumentURI.Id, 313 | []*messages.TestStep{}, 314 | ) 315 | lookup.ProcessMessage(makePickleEnvelope(pickleWithWrongDocumentURI)) 316 | lookup.ProcessMessage(makeTestCaseEnvelope(testCaseForPickleWithWrongDocumentURI)) 317 | 318 | pickleWithUnknownTag := &messages.Pickle{ 319 | Id: "wrong-tag-id-pickle", 320 | Uri: document.Uri, 321 | AstNodeIds: []string{scenario.Id}, 322 | Tags: []*messages.PickleTag{ 323 | { 324 | AstNodeId: "tag-id", 325 | }, 326 | { 327 | AstNodeId: "wrong-tag-id", 328 | }, 329 | }, 330 | } 331 | testCaseForPickleWithUnknownTag = makeTestCase( 332 | "wrong-tag-id", 333 | pickleWithUnknownTag.Id, 334 | []*messages.TestStep{}, 335 | ) 336 | lookup.ProcessMessage(makePickleEnvelope(pickleWithUnknownTag)) 337 | lookup.ProcessMessage(makeTestCaseEnvelope(testCaseForPickleWithUnknownTag)) 338 | }) 339 | 340 | It("returns nil if the TestCase has not been defined", func() { 341 | _, testCase := ProcessTestCaseStarted(&messages.TestCaseStarted{ 342 | TestCaseId: "unknown-test-case-id", 343 | }, lookup) 344 | 345 | Expect(testCase).To(BeNil()) 346 | }) 347 | 348 | It("returns nil if the Pickle is Unknown", func() { 349 | _, testCase := ProcessTestCaseStarted(&messages.TestCaseStarted{ 350 | TestCaseId: testCaseReferencingUnknownPickle.Id, 351 | }, lookup) 352 | 353 | Expect(testCase).To(BeNil()) 354 | }) 355 | 356 | It("returns nil if the Pickle has no source", func() { 357 | _, testCase := ProcessTestCaseStarted(&messages.TestCaseStarted{ 358 | TestCaseId: testCaseForPickleWithoutSource.Id, 359 | }, lookup) 360 | 361 | Expect(testCase).To(BeNil()) 362 | }) 363 | 364 | It("returns nil if the Pickle references an unknown scenario", func() { 365 | _, testCase := ProcessTestCaseStarted(&messages.TestCaseStarted{ 366 | TestCaseId: testCaseForPickleWithWrongScenarioReference.Id, 367 | }, lookup) 368 | 369 | Expect(testCase).To(BeNil()) 370 | }) 371 | 372 | It("returns nil if the Pickle does not reference an existing Gherkin document", func() { 373 | _, testCase := ProcessTestCaseStarted(&messages.TestCaseStarted{ 374 | TestCaseId: testCaseForPickleWithWrongDocumentURI.Id, 375 | }, lookup) 376 | 377 | Expect(testCase).To(BeNil()) 378 | }) 379 | 380 | It("returns Nil at least one PickleTag references a non-existing tag", func() { 381 | _, testCase := ProcessTestCaseStarted(&messages.TestCaseStarted{ 382 | TestCaseId: testCaseForPickleWithUnknownTag.Id, 383 | }, lookup) 384 | 385 | Expect(testCase).To(BeNil()) 386 | }) 387 | }) 388 | }) 389 | 390 | var _ = Describe("TestCaseToJSON", func() { 391 | var ( 392 | pickle *messages.Pickle 393 | testCase *TestCase 394 | jsonTestCase []*jsonFeatureElement 395 | ) 396 | 397 | BeforeEach(func() { 398 | scenario := &messages.Scenario{ 399 | Id: "scenario-id", 400 | Keyword: "Eksempel", 401 | Name: "A scenario ()", 402 | Description: "This is a scenario", 403 | Location: &messages.Location{ 404 | Line: 11, 405 | }, 406 | } 407 | 408 | pickle = &messages.Pickle{ 409 | Uri: "some_examples.feature", 410 | Name: "A scenario (2)", 411 | AstNodeIds: []string{scenario.Id}, 412 | } 413 | 414 | testCase = &TestCase{ 415 | FeatureName: "My feature", 416 | Scenario: scenario, 417 | Pickle: pickle, 418 | TestCase: makeTestCase( 419 | "test-case-id", 420 | "pickle-id", 421 | []*messages.TestStep{}, 422 | ), 423 | Tags: []*messages.Tag{ 424 | { 425 | Location: &messages.Location{ 426 | Line: 3, 427 | }, 428 | Name: "@foo", 429 | }, 430 | }, 431 | Steps: make([]*TestStep, 0), 432 | } 433 | 434 | testCase.appendStep(&TestStep{ 435 | Step: &messages.Step{ 436 | Id: "some-id", 437 | Keyword: "Given", 438 | Text: "a step", 439 | Location: &messages.Location{ 440 | Line: 5, 441 | }, 442 | }, 443 | Pickle: pickle, 444 | PickleStep: &messages.PickleStep{ 445 | Text: "a passed step", 446 | }, 447 | Result: &messages.TestStepResult{ 448 | Status: messages.TestStepResultStatus_FAILED, 449 | }, 450 | }) 451 | jsonTestCase = TestCaseToJSON(testCase) 452 | }) 453 | 454 | It("computes the ID from the FeatureName and Scenario name", func() { 455 | Expect(jsonTestCase[0].ID).To(Equal("my-feature;a-scenario-()")) 456 | }) 457 | 458 | It("has the Scenario keyword", func() { 459 | Expect(jsonTestCase[0].Keyword).To(Equal("Eksempel")) 460 | }) 461 | 462 | // It does not sadly: we use Cucumber-ruby for producing the 463 | // acceptance tests, which does not support that (but Cucumber-JVM, 464 | // Cucumber-JS do ...) 465 | XIt("should have the Pickle name", func() {}) 466 | 467 | It("has the Scenario name", func() { 468 | Expect(jsonTestCase[0].Name).To(Equal("A scenario ()")) 469 | }) 470 | 471 | It("has the Scenario description", func() { 472 | Expect(jsonTestCase[0].Description).To(Equal("This is a scenario")) 473 | }) 474 | 475 | It("has the Scenario line", func() { 476 | Expect(jsonTestCase[0].Line).To(Equal(uint32(11))) 477 | }) 478 | 479 | It("has the steps rendered in Steps", func() { 480 | Expect(len(jsonTestCase[0].Steps)).To(Equal(1)) 481 | Expect(jsonTestCase[0].Steps[0].Name).To(Equal("a passed step")) 482 | }) 483 | 484 | It("has the Tags", func() { 485 | Expect(len(jsonTestCase[0].Tags)).To(Equal(1)) 486 | Expect(jsonTestCase[0].Tags[0].Line).To(Equal(uint32(3))) 487 | Expect(jsonTestCase[0].Tags[0].Name).To(Equal("@foo")) 488 | }) 489 | 490 | Context("when there is a Background", func() { 491 | BeforeEach(func() { 492 | testCase.Steps = []*TestStep{ 493 | { 494 | Step: &messages.Step{ 495 | Id: "background-step-id", 496 | Keyword: "Given", 497 | Text: "a passed step", 498 | Location: &messages.Location{ 499 | Line: 3, 500 | }, 501 | }, 502 | Pickle: pickle, 503 | PickleStep: &messages.PickleStep{ 504 | Text: "a passed step", 505 | }, 506 | Result: &messages.TestStepResult{ 507 | Status: messages.TestStepResultStatus_PASSED, 508 | Duration: &messages.Duration{ 509 | Seconds: 123, 510 | Nanos: 456, 511 | }, 512 | }, 513 | Background: &messages.Background{ 514 | Keyword: "Kontext", 515 | Location: &messages.Location{ 516 | Line: 3, 517 | }, 518 | }, 519 | }, 520 | testCase.Steps[0], 521 | } 522 | jsonTestCase = TestCaseToJSON(testCase) 523 | }) 524 | 525 | It("returns two jsonFeatureElements", func() { 526 | Expect(len(jsonTestCase)).To(Equal(2)) 527 | }) 528 | 529 | It("has the Background keyword", func() { 530 | Expect(jsonTestCase[0].Keyword).To(Equal("Kontext")) 531 | }) 532 | 533 | It("has the background line", func() { 534 | Expect(jsonTestCase[0].Line).To(Equal(uint32(3))) 535 | }) 536 | }) 537 | 538 | Context("when pickles come from a Examples row", func() { 539 | BeforeEach(func() { 540 | exampleRow := &messages.TableRow{ 541 | Id: "example-row-id", 542 | Location: &messages.Location{ 543 | Line: 13, 544 | }, 545 | } 546 | 547 | testCase.Scenario.Examples = []*messages.Examples{ 548 | { 549 | Name: "some examples", 550 | TableBody: []*messages.TableRow{ 551 | exampleRow, 552 | }, 553 | }, 554 | } 555 | 556 | testCase.Pickle.AstNodeIds = append(testCase.Pickle.AstNodeIds, exampleRow.Id) 557 | jsonTestCase = TestCaseToJSON(testCase) 558 | }) 559 | 560 | It("has the Examples table name and ExampleRow line in the Id", func() { 561 | Expect(jsonTestCase[0].ID).To(Equal("my-feature;a-scenario-();some-examples;2")) 562 | }) 563 | 564 | It("has the Example row line", func() { 565 | Expect(jsonTestCase[0].Line).To(Equal(uint32(13))) 566 | }) 567 | }) 568 | 569 | Context("when there is Before Hooks", func() { 570 | BeforeEach(func() { 571 | testCase.Steps = []*TestStep{ 572 | { 573 | Hook: &messages.Hook{ 574 | SourceReference: &messages.SourceReference{ 575 | Uri: "some_hooks.rb", 576 | Location: &messages.Location{ 577 | Line: 5, 578 | }, 579 | }, 580 | }, 581 | Result: &messages.TestStepResult{ 582 | Status: messages.TestStepResultStatus_PASSED, 583 | Duration: &messages.Duration{ 584 | Seconds: 123, 585 | Nanos: 456, 586 | }, 587 | }, 588 | }, 589 | testCase.Steps[0], 590 | } 591 | jsonTestCase = TestCaseToJSON(testCase) 592 | 593 | }) 594 | 595 | It("has the Hook steps in the Before field in the first Element", func() { 596 | Expect(jsonTestCase[0].Before[0].Match.Location).To(Equal("some_hooks.rb:5")) 597 | }) 598 | }) 599 | 600 | Context("when there is After Hooks", func() { 601 | BeforeEach(func() { 602 | testCase.Steps = append(testCase.Steps, &TestStep{ 603 | Hook: &messages.Hook{ 604 | SourceReference: &messages.SourceReference{ 605 | Uri: "some_hooks.rb", 606 | Location: &messages.Location{ 607 | Line: 12, 608 | }, 609 | }, 610 | }, 611 | Result: &messages.TestStepResult{ 612 | Status: messages.TestStepResultStatus_PASSED, 613 | Duration: &messages.Duration{ 614 | Seconds: 123, 615 | Nanos: 456, 616 | }, 617 | }, 618 | }) 619 | jsonTestCase = TestCaseToJSON(testCase) 620 | }) 621 | 622 | It("has the hooks in the After section of the last Element", func() { 623 | Expect(jsonTestCase[0].After[0].Match.Location).To(Equal("some_hooks.rb:12")) 624 | }) 625 | }) 626 | }) 627 | -------------------------------------------------------------------------------- /go/test_helpers.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/cucumber/common/messages/go/v18" 5 | ) 6 | 7 | func makeScenario(id string, steps []*messages.Step) *messages.Scenario { 8 | return &messages.Scenario{ 9 | Id: id, 10 | Steps: steps, 11 | } 12 | } 13 | 14 | func makeGherkinStep(id string, keyword string, text string) *messages.Step { 15 | return &messages.Step{ 16 | Id: id, 17 | Keyword: keyword, 18 | Text: text, 19 | } 20 | } 21 | 22 | func makeTestStep(id string, pickleStepId string, stepDefinitionIds []string) *messages.TestStep { 23 | return &messages.TestStep{ 24 | Id: id, 25 | PickleStepId: pickleStepId, 26 | StepDefinitionIds: stepDefinitionIds, 27 | } 28 | } 29 | 30 | func makeHookTestStep(id string, hookId string) *messages.TestStep { 31 | return &messages.TestStep{ 32 | Id: id, 33 | HookId: hookId, 34 | } 35 | } 36 | 37 | func makeTestCase(id string, pickleId string, testSteps []*messages.TestStep) *messages.TestCase { 38 | return &messages.TestCase{ 39 | Id: id, 40 | PickleId: pickleId, 41 | TestSteps: testSteps, 42 | } 43 | } 44 | 45 | func makeTestCaseEnvelope(testCase *messages.TestCase) *messages.Envelope { 46 | return &messages.Envelope{ 47 | TestCase: testCase, 48 | } 49 | } 50 | 51 | func makeTestCaseStartedEnvelope(testCaseStarted *messages.TestCaseStarted) *messages.Envelope { 52 | return &messages.Envelope{ 53 | TestCaseStarted: testCaseStarted, 54 | } 55 | } 56 | 57 | func makeHookEnvelope(testCaseHook *messages.Hook) *messages.Envelope { 58 | return &messages.Envelope{ 59 | Hook: testCaseHook, 60 | } 61 | } 62 | 63 | func makeStepDefinitionEnvelope(stepDefinitionConfig *messages.StepDefinition) *messages.Envelope { 64 | return &messages.Envelope{ 65 | StepDefinition: stepDefinitionConfig, 66 | } 67 | } 68 | 69 | func makePickleEnvelope(pickle *messages.Pickle) *messages.Envelope { 70 | return &messages.Envelope{ 71 | Pickle: pickle, 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /go/test_step.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "fmt" 7 | "strings" 8 | 9 | "github.com/cucumber/common/messages/go/v18" 10 | ) 11 | 12 | type TestStep struct { 13 | TestCaseID string 14 | Hook *messages.Hook 15 | Pickle *messages.Pickle 16 | PickleStep *messages.PickleStep 17 | Step *messages.Step 18 | StepDefinitions []*messages.StepDefinition 19 | Result *messages.TestStepResult 20 | Background *messages.Background 21 | Attachments []*messages.Attachment 22 | ExampleRow *messages.TableRow 23 | } 24 | 25 | func ProcessTestStepFinished(testStepFinished *messages.TestStepFinished, lookup *MessageLookup) (error, *TestStep) { 26 | testCaseStarted := lookup.LookupTestCaseStarted(testStepFinished.TestCaseStartedId) 27 | if testCaseStarted == nil { 28 | return errors.New("No testCaseStarted for " + testStepFinished.TestCaseStartedId), nil 29 | } 30 | 31 | testCase := lookup.LookupTestCase(testCaseStarted.TestCaseId) 32 | if testCase == nil { 33 | return errors.New("No testCase for " + testCaseStarted.TestCaseId), nil 34 | } 35 | 36 | testStep := lookup.LookupTestStep(testStepFinished.TestStepId) 37 | if testStep == nil { 38 | return errors.New("No testStep for " + testStepFinished.TestStepId), nil 39 | } 40 | 41 | if testStep.HookId != "" { 42 | hook := lookup.LookupHook(testStep.HookId) 43 | if hook == nil { 44 | return errors.New("No hook for " + testStep.HookId), nil 45 | } 46 | 47 | return nil, &TestStep{ 48 | TestCaseID: testCase.Id, 49 | Hook: hook, 50 | Result: testStepFinished.TestStepResult, 51 | Attachments: lookup.LookupAttachments(testStepFinished.TestStepId), 52 | } 53 | } 54 | 55 | pickle := lookup.LookupPickle(testCase.PickleId) 56 | if pickle == nil { 57 | return errors.New("No pickle for " + testCase.PickleId), nil 58 | } 59 | 60 | pickleStep := lookup.LookupPickleStep(testStep.PickleStepId) 61 | if pickleStep == nil { 62 | return errors.New("No pickleStep for " + testStep.PickleStepId), nil 63 | } 64 | 65 | var exampleRow *messages.TableRow 66 | if len(pickle.AstNodeIds) > 1 { 67 | exampleRow = lookup.LookupExampleRow(pickle.AstNodeIds[1]) 68 | } 69 | 70 | var background *messages.Background 71 | scenarioStep := lookup.LookupStep(pickleStep.AstNodeIds[0]) 72 | if scenarioStep != nil { 73 | background = lookup.LookupBackgroundByStepID(scenarioStep.Id) 74 | } 75 | 76 | return nil, &TestStep{ 77 | TestCaseID: testCase.Id, 78 | Step: lookup.LookupStep(pickleStep.AstNodeIds[0]), 79 | Pickle: pickle, 80 | PickleStep: pickleStep, 81 | ExampleRow: exampleRow, 82 | Result: testStepFinished.TestStepResult, 83 | StepDefinitions: lookup.LookupStepDefinitions(testStep.StepDefinitionIds), 84 | Background: background, 85 | Attachments: lookup.LookupAttachments(testStepFinished.TestStepId), 86 | } 87 | } 88 | 89 | func TestStepToJSON(step *TestStep) *jsonStep { 90 | status := strings.ToLower(step.Result.Status.String()) 91 | duration := uint64(0) 92 | if step.Result.Duration != nil { 93 | duration = uint64(messages.DurationToGoDuration(*step.Result.Duration)) 94 | } 95 | 96 | if step.Hook != nil { 97 | return &jsonStep{ 98 | Match: &jsonStepMatch{ 99 | Location: makeSourceReferenceLocation(step.Hook.SourceReference), 100 | }, 101 | Result: &jsonStepResult{ 102 | Status: status, 103 | ErrorMessage: step.Result.Message, 104 | Duration: duration, 105 | }, 106 | Embeddings: makeEmbeddings(step.Attachments), 107 | } 108 | } 109 | 110 | location := makeLocation(step.Pickle.Uri, step.Step.Location.Line) 111 | if step.ExampleRow != nil { 112 | location = makeLocation(step.Pickle.Uri, step.ExampleRow.Location.Line) 113 | } 114 | 115 | if len(step.StepDefinitions) == 1 { 116 | location = makeSourceReferenceLocation(step.StepDefinitions[0].SourceReference) 117 | } 118 | 119 | jsonStep := &jsonStep{ 120 | Keyword: step.Step.Keyword, 121 | Name: step.PickleStep.Text, 122 | Line: uint32(step.Step.Location.Line), 123 | Match: &jsonStepMatch{ 124 | Location: location, 125 | }, 126 | Result: &jsonStepResult{ 127 | Status: status, 128 | ErrorMessage: step.Result.Message, 129 | Duration: duration, 130 | }, 131 | Embeddings: makeEmbeddings(step.Attachments), 132 | Output: makeOutput(step.Attachments), 133 | } 134 | 135 | docString := step.Step.DocString 136 | if docString != nil { 137 | jsonStep.DocString = &jsonDocString{ 138 | Line: uint32(docString.Location.Line), 139 | ContentType: docString.MediaType, 140 | Value: docString.Content, 141 | } 142 | } 143 | 144 | datatable := step.Step.DataTable 145 | if datatable != nil { 146 | jsonStep.Rows = make([]*jsonDatatableRow, len(datatable.Rows)) 147 | for rowIndex, row := range datatable.Rows { 148 | cells := make([]string, len(row.Cells)) 149 | for cellIndex, cell := range row.Cells { 150 | cells[cellIndex] = cell.Value 151 | } 152 | 153 | jsonStep.Rows[rowIndex] = &jsonDatatableRow{ 154 | Cells: cells, 155 | } 156 | } 157 | } 158 | 159 | return jsonStep 160 | } 161 | 162 | func makeEmbeddings(attachments []*messages.Attachment) []*jsonEmbedding { 163 | embeddableAttachments := filterAttachments(attachments, isEmbeddable) 164 | jsonEmbeddings := make([]*jsonEmbedding, len(embeddableAttachments)) 165 | 166 | for index, attachment := range embeddableAttachments { 167 | var data string 168 | if attachment.ContentEncoding == messages.AttachmentContentEncoding_BASE64 { 169 | data = attachment.Body 170 | } else { 171 | data = base64.StdEncoding.EncodeToString([]byte(attachment.Body)) 172 | } 173 | jsonEmbeddings[index] = &jsonEmbedding{ 174 | Data: data, 175 | MimeType: attachment.MediaType, 176 | } 177 | } 178 | 179 | return jsonEmbeddings 180 | } 181 | 182 | func makeOutput(attachments []*messages.Attachment) []string { 183 | outputAttachments := filterAttachments(attachments, isOutput) 184 | output := make([]string, len(outputAttachments)) 185 | 186 | for index, attachment := range outputAttachments { 187 | output[index] = attachment.Body 188 | } 189 | 190 | return output 191 | } 192 | 193 | func filterAttachments(attachments []*messages.Attachment, filter func(*messages.Attachment) bool) []*messages.Attachment { 194 | matches := make([]*messages.Attachment, 0) 195 | for _, attachment := range attachments { 196 | if filter(attachment) { 197 | matches = append(matches, attachment) 198 | } 199 | } 200 | return matches 201 | } 202 | 203 | func isEmbeddable(attachment *messages.Attachment) bool { 204 | return !isOutput(attachment) 205 | } 206 | 207 | func isOutput(attachment *messages.Attachment) bool { 208 | return attachment.MediaType == "text/x.cucumber.log+plain" 209 | } 210 | 211 | func makeLocation(file string, line int64) string { 212 | return fmt.Sprintf("%s:%d", file, line) 213 | } 214 | 215 | func makeJavaMethodLocation( 216 | javaMethod *messages.JavaMethod, 217 | ) string { 218 | typeList := strings.Join(javaMethod.MethodParameterTypes, ",") 219 | return fmt.Sprintf("%s.%s(%s)", javaMethod.ClassName, javaMethod.MethodName, typeList) 220 | } 221 | 222 | func makeJavaStackTraceElementLocation( 223 | javaStackTraceElement *messages.JavaStackTraceElement, 224 | location *messages.Location, 225 | ) string { 226 | if location != nil { 227 | return makeLocation(javaStackTraceElement.FileName, location.Line) 228 | } 229 | return javaStackTraceElement.FileName 230 | } 231 | 232 | func makeSourceReferenceLocation(sourceReference *messages.SourceReference) string { 233 | javaMethod := sourceReference.JavaMethod 234 | if javaMethod != nil { 235 | return makeJavaMethodLocation(javaMethod) 236 | } 237 | 238 | location := sourceReference.Location 239 | javaStackTraceElement := sourceReference.JavaStackTraceElement 240 | if javaStackTraceElement != nil { 241 | return makeJavaStackTraceElementLocation(javaStackTraceElement, location) 242 | } 243 | 244 | return makeLocation(sourceReference.Uri, location.Line) 245 | } 246 | -------------------------------------------------------------------------------- /go/test_step_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/cucumber/common/messages/go/v18" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | ) 8 | 9 | var _ = Describe("ProcessTestStepFinished", func() { 10 | var ( 11 | lookup *MessageLookup 12 | ) 13 | 14 | BeforeEach(func() { 15 | lookup = &MessageLookup{} 16 | lookup.Initialize(false) 17 | 18 | pickleStep := &messages.PickleStep{ 19 | Id: "pickle-step-id", 20 | AstNodeIds: []string{"some-id"}, 21 | } 22 | pickle := &messages.Pickle{ 23 | Id: "pickle-id", 24 | Steps: []*messages.PickleStep{pickleStep}, 25 | } 26 | lookup.ProcessMessage(makePickleEnvelope(pickle)) 27 | 28 | testStep := makeTestStep( 29 | "test-step-id", 30 | pickleStep.Id, 31 | []string{}, 32 | ) 33 | 34 | testCase := makeTestCase( 35 | "test-case-id", 36 | pickle.Id, 37 | []*messages.TestStep{testStep}, 38 | ) 39 | lookup.ProcessMessage(makeTestCaseEnvelope(testCase)) 40 | 41 | testCaseStarted := &messages.TestCaseStarted{ 42 | Id: "test-case-started-id", 43 | TestCaseId: testCase.Id, 44 | } 45 | lookup.ProcessMessage(makeTestCaseStartedEnvelope(testCaseStarted)) 46 | }) 47 | 48 | It("has the TestCase ID", func() { 49 | testStepFinished := &messages.TestStepFinished{ 50 | TestCaseStartedId: "test-case-started-id", 51 | TestStepId: "test-step-id", 52 | } 53 | 54 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 55 | Expect(testStep.TestCaseID).To(Equal("test-case-id")) 56 | }) 57 | 58 | Context("with referencing issues", func() { 59 | It("returns nil if the step does not exist", func() { 60 | testStepFinished := &messages.TestStepFinished{ 61 | TestCaseStartedId: "test-case-started-id", 62 | TestStepId: "unknown-step", 63 | } 64 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 65 | Expect(testStep).To(BeNil()) 66 | }) 67 | 68 | It("returns nil if the TestCaseStarted does not exist", func() { 69 | testStepFinished := &messages.TestStepFinished{ 70 | TestCaseStartedId: "unknown-test-case-started", 71 | TestStepId: "test-step-id", 72 | } 73 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 74 | Expect(testStep).To(BeNil()) 75 | }) 76 | 77 | It("returns nil if the TestCaseStarted references an unknown TestCase", func() { 78 | testCaseStarted := &messages.TestCaseStarted{ 79 | Id: "test-case-started-no-test-case", 80 | TestCaseId: "unknown-test-case", 81 | } 82 | lookup.ProcessMessage(makeTestCaseStartedEnvelope(testCaseStarted)) 83 | 84 | testStepFinished := &messages.TestStepFinished{ 85 | TestCaseStartedId: testCaseStarted.Id, 86 | TestStepId: "test-step-id", 87 | } 88 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 89 | Expect(testStep).To(BeNil()) 90 | }) 91 | }) 92 | 93 | Context("When step references a Hook", func() { 94 | BeforeEach(func() { 95 | hook := &messages.Hook{ 96 | Id: "hook-id", 97 | } 98 | lookup.ProcessMessage(makeHookEnvelope(hook)) 99 | 100 | testCase := makeTestCase( 101 | "test-case-id", 102 | "whatever-pickle-id", 103 | []*messages.TestStep{ 104 | makeHookTestStep("hook-step-id", hook.Id), 105 | makeHookTestStep("wrong-hook-step-id", "unknown-hook-id"), 106 | }, 107 | ) 108 | lookup.ProcessMessage(makeTestCaseEnvelope(testCase)) 109 | 110 | testCaseStarted := &messages.TestCaseStarted{ 111 | Id: "hook-test-case-started-id", 112 | TestCaseId: testCase.Id, 113 | } 114 | lookup.ProcessMessage(makeTestCaseStartedEnvelope(testCaseStarted)) 115 | }) 116 | 117 | It("returns a TestStep including the Hook", func() { 118 | testStepFinished := &messages.TestStepFinished{ 119 | TestCaseStartedId: "test-case-started-id", 120 | TestStepId: "hook-step-id", 121 | TestStepResult: &messages.TestStepResult{ 122 | Status: messages.TestStepResultStatus_PASSED, 123 | }, 124 | } 125 | 126 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 127 | 128 | Expect(testStep.Hook.Id).To(Equal("hook-id")) 129 | Expect(testStep.Result.Status).To(Equal(messages.TestStepResultStatus_PASSED)) 130 | }) 131 | 132 | It("returns a TestStep with a nil Step", func() { 133 | testStepFinished := &messages.TestStepFinished{ 134 | TestCaseStartedId: "test-case-started-id", 135 | TestStepId: "hook-step-id", 136 | } 137 | 138 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 139 | 140 | Expect(testStep.Step).To(BeNil()) 141 | }) 142 | 143 | It("returns nil if the Hook does not exist", func() { 144 | testStepFinished := &messages.TestStepFinished{ 145 | TestCaseStartedId: "test-case-started-id", 146 | TestStepId: "wrong-hook-step-id", 147 | } 148 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 149 | 150 | Expect(testStep).To(BeNil()) 151 | }) 152 | }) 153 | 154 | Context("When step references a PickleStep", func() { 155 | var ( 156 | testCaseStarted *messages.TestCaseStarted 157 | background *messages.Background 158 | ) 159 | BeforeEach(func() { 160 | // This is a bit dirty hack to avoid creating all the AST 161 | background = &messages.Background{ 162 | Keyword: "Background", 163 | } 164 | backgroundStep := makeGherkinStep("background-step", "Given", "a passed step") 165 | step := makeGherkinStep("step-id", "Given", "a passed step") 166 | scenario := makeScenario("scenario-id", []*messages.Step{ 167 | step, 168 | }) 169 | lookup.stepByID[backgroundStep.Id] = backgroundStep 170 | lookup.stepByID[step.Id] = step 171 | lookup.scenarioByID[scenario.Id] = scenario 172 | lookup.backgroundByStepID[backgroundStep.Id] = background 173 | 174 | stepDefinitionConfig := &messages.StepDefinition{ 175 | Id: "step-def-id", 176 | Pattern: &messages.StepDefinitionPattern{ 177 | Source: "a passed {word}", 178 | }, 179 | } 180 | lookup.ProcessMessage(makeStepDefinitionEnvelope(stepDefinitionConfig)) 181 | 182 | backgroundPickleStep := &messages.PickleStep{ 183 | Id: "background-pickle-step-id", 184 | AstNodeIds: []string{backgroundStep.Id}, 185 | Text: "a passed step", 186 | } 187 | 188 | pickleStep := &messages.PickleStep{ 189 | Id: "pickle-step-id", 190 | AstNodeIds: []string{step.Id}, 191 | Text: "a passed step", 192 | } 193 | 194 | pickle := &messages.Pickle{ 195 | Id: "pickle-id", 196 | Uri: "some_feature.feature", 197 | AstNodeIds: []string{scenario.Id}, 198 | Steps: []*messages.PickleStep{ 199 | backgroundPickleStep, 200 | pickleStep, 201 | }, 202 | } 203 | lookup.ProcessMessage(makePickleEnvelope(pickle)) 204 | 205 | testCase := makeTestCase( 206 | "test-case-id", 207 | pickle.Id, 208 | []*messages.TestStep{ 209 | makeTestStep("background-step-id", backgroundPickleStep.Id, []string{"step-def-id"}), 210 | makeTestStep("test-step-id", pickleStep.Id, []string{"step-def-id"}), 211 | makeTestStep("unknown-pickle", "unknown-pickle-step-id", []string{}), 212 | }, 213 | ) 214 | lookup.ProcessMessage(makeTestCaseEnvelope(testCase)) 215 | 216 | testCaseStarted = &messages.TestCaseStarted{ 217 | Id: "test-case-started-id", 218 | TestCaseId: testCase.Id, 219 | } 220 | lookup.ProcessMessage(makeTestCaseStartedEnvelope(testCaseStarted)) 221 | }) 222 | 223 | It("returns a TestStep including the FeatureStep", func() { 224 | testStepFinished := &messages.TestStepFinished{ 225 | TestStepId: "test-step-id", 226 | TestCaseStartedId: testCaseStarted.Id, 227 | } 228 | 229 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 230 | Expect(testStep.Step.Id).To(Equal("step-id")) 231 | }) 232 | 233 | It("returns a Step including the StepDefinitions", func() { 234 | testStepFinished := &messages.TestStepFinished{ 235 | TestStepId: "test-step-id", 236 | TestCaseStartedId: testCaseStarted.Id, 237 | } 238 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 239 | Expect(len(testStep.StepDefinitions)).To(Equal(1)) 240 | Expect(testStep.StepDefinitions[0].Pattern.Source).To(Equal("a passed {word}")) 241 | }) 242 | 243 | It("the Step is not marked as a Background step by default", func() { 244 | testStepFinished := &messages.TestStepFinished{ 245 | TestStepId: "test-step-id", 246 | TestCaseStartedId: testCaseStarted.Id, 247 | } 248 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 249 | Expect(testStep.Background).To(BeNil()) 250 | }) 251 | 252 | Context("when the Step is defined in a background", func() { 253 | It("sets Step.IsBackground to the Background message", func() { 254 | testStepFinished := &messages.TestStepFinished{ 255 | TestStepId: "background-step-id", 256 | TestCaseStartedId: testCaseStarted.Id, 257 | } 258 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 259 | Expect(testStep.Background).To(Equal(background)) 260 | }) 261 | }) 262 | 263 | Context("with referencing issues", func() { 264 | It("returns Nil if the Pickle is unknow", func() { 265 | testCase := makeTestCase( 266 | "test-case-id", 267 | "unknown-pickle", 268 | []*messages.TestStep{}, 269 | ) 270 | lookup.ProcessMessage(makeTestCaseEnvelope(testCase)) 271 | 272 | testCaseStarted := &messages.TestCaseStarted{ 273 | Id: "test-case-started-id", 274 | TestCaseId: testCase.Id, 275 | } 276 | lookup.ProcessMessage(makeTestCaseStartedEnvelope(testCaseStarted)) 277 | 278 | testStepFinished := &messages.TestStepFinished{ 279 | TestStepId: "test-step-id", 280 | TestCaseStartedId: testCaseStarted.Id, 281 | } 282 | 283 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 284 | Expect(testStep).To(BeNil()) 285 | }) 286 | 287 | It("Returns Nil if the PickleStep is unknown", func() { 288 | testStepFinished := &messages.TestStepFinished{ 289 | TestStepId: "unknown-pickle-step", 290 | TestCaseStartedId: testCaseStarted.Id, 291 | } 292 | 293 | _, testStep := ProcessTestStepFinished(testStepFinished, lookup) 294 | Expect(testStep).To(BeNil()) 295 | }) 296 | }) 297 | }) 298 | }) 299 | 300 | var _ = Describe("TestStepToJSON", func() { 301 | var ( 302 | step *TestStep 303 | jsonStep *jsonStep 304 | ) 305 | 306 | Context("When TestStep comes from a Hook", func() { 307 | BeforeEach(func() { 308 | step = &TestStep{ 309 | Hook: &messages.Hook{ 310 | SourceReference: &messages.SourceReference{ 311 | Uri: "some/hooks.go", 312 | Location: &messages.Location{ 313 | Column: 3, 314 | Line: 12, 315 | }, 316 | }, 317 | }, 318 | Result: &messages.TestStepResult{ 319 | Status: messages.TestStepResultStatus_PASSED, 320 | Duration: &messages.Duration{ 321 | Seconds: 123, 322 | Nanos: 456, 323 | }, 324 | }, 325 | Attachments: make([]*messages.Attachment, 0), 326 | } 327 | 328 | step.Attachments = append(step.Attachments, &messages.Attachment{ 329 | Body: "Hello", 330 | MediaType: "text/plain", 331 | ContentEncoding: messages.AttachmentContentEncoding_BASE64, 332 | }) 333 | 334 | jsonStep = TestStepToJSON(step) 335 | }) 336 | 337 | It("Has a Match", func() { 338 | Expect(jsonStep.Match.Location).To(Equal("some/hooks.go:12")) 339 | }) 340 | 341 | It("Has a Result", func() { 342 | Expect(jsonStep.Result.Status).To(Equal("passed")) 343 | }) 344 | 345 | It("Has a Duration", func() { 346 | Expect(jsonStep.Result.Duration).To(Equal(uint64(123000000456))) 347 | }) 348 | 349 | It("has an Embedding", func() { 350 | Expect(len(jsonStep.Embeddings)).To(Equal(1)) 351 | Expect(jsonStep.Embeddings[0].MimeType).To(Equal("text/plain")) 352 | Expect(jsonStep.Embeddings[0].Data).To(Equal("Hello")) 353 | }) 354 | }) 355 | 356 | Context("When TestStep comes from a feature step", func() { 357 | BeforeEach(func() { 358 | step = &TestStep{ 359 | Step: &messages.Step{ 360 | Id: "some-id", 361 | Keyword: "Given", 362 | Text: "a step", 363 | Location: &messages.Location{ 364 | Line: 5, 365 | }, 366 | }, 367 | Pickle: &messages.Pickle{ 368 | Uri: "my_feature.feature", 369 | }, 370 | PickleStep: &messages.PickleStep{ 371 | Text: "a passed step", 372 | }, 373 | Result: &messages.TestStepResult{ 374 | Status: messages.TestStepResultStatus_FAILED, 375 | Duration: &messages.Duration{ 376 | Seconds: 123, 377 | Nanos: 456, 378 | }, 379 | }, 380 | Attachments: make([]*messages.Attachment, 0), 381 | } 382 | 383 | step.Attachments = append(step.Attachments, &messages.Attachment{ 384 | Body: "Hello", 385 | MediaType: "text/plain", 386 | ContentEncoding: messages.AttachmentContentEncoding_BASE64, 387 | }) 388 | 389 | jsonStep = TestStepToJSON(step) 390 | }) 391 | 392 | It("gets keyword from Step", func() { 393 | Expect(jsonStep.Keyword).To(Equal("Given")) 394 | }) 395 | 396 | It("should gets name from PickleStep", func() { 397 | Expect(jsonStep.Name).To(Equal("a passed step")) 398 | }) 399 | 400 | It("has a Result", func() { 401 | Expect(jsonStep.Result.Status).To(Equal("failed")) 402 | }) 403 | 404 | It("Has a Duration", func() { 405 | Expect(jsonStep.Result.Duration).To(Equal(uint64(123000000456))) 406 | }) 407 | 408 | It("has a Line", func() { 409 | Expect(jsonStep.Line).To(Equal(uint32(5))) 410 | }) 411 | 412 | It("has an Embedding", func() { 413 | Expect(len(jsonStep.Embeddings)).To(Equal(1)) 414 | Expect(jsonStep.Embeddings[0].MimeType).To(Equal("text/plain")) 415 | Expect(jsonStep.Embeddings[0].Data).To(Equal("Hello")) 416 | }) 417 | 418 | Context("When it does not have a StepDefinition", func() { 419 | It("Has a Match referencing the feature file", func() { 420 | Expect(jsonStep.Match.Location).To(Equal("my_feature.feature:5")) 421 | }) 422 | }) 423 | 424 | Context("When it has a StepDefinition", func() { 425 | It("has a Match referencing the feature file", func() { 426 | step.StepDefinitions = []*messages.StepDefinition{ 427 | &messages.StepDefinition{ 428 | SourceReference: &messages.SourceReference{ 429 | Uri: "support_code.go", 430 | Location: &messages.Location{ 431 | Line: 12, 432 | }, 433 | }, 434 | }, 435 | } 436 | 437 | jsonStep = TestStepToJSON(step) 438 | Expect(jsonStep.Match.Location).To(Equal("support_code.go:12")) 439 | }) 440 | }) 441 | }) 442 | 443 | Context("When SourceReference uses JavaMethod and no Location", func() { 444 | Describe("from a Hook", func() { 445 | BeforeEach(func() { 446 | step = &TestStep{ 447 | Hook: &messages.Hook{ 448 | SourceReference: &messages.SourceReference{ 449 | JavaMethod: &messages.JavaMethod{ 450 | ClassName: "org.cucumber.jvm.Class", 451 | MethodName: "someMethod", 452 | MethodParameterTypes: []string{ 453 | "java.lang.String", 454 | "int", 455 | }, 456 | }, 457 | }, 458 | }, 459 | Result: &messages.TestStepResult{ 460 | Status: messages.TestStepResultStatus_PASSED, 461 | Duration: &messages.Duration{ 462 | Seconds: 123, 463 | Nanos: 456, 464 | }, 465 | }, 466 | } 467 | jsonStep = TestStepToJSON(step) 468 | }) 469 | 470 | It("Has a Match", func() { 471 | Expect(jsonStep.Match.Location).To(Equal("org.cucumber.jvm.Class.someMethod(java.lang.String,int)")) 472 | }) 473 | }) 474 | 475 | Describe("from a feature step", func() { 476 | BeforeEach(func() { 477 | step = &TestStep{ 478 | Step: &messages.Step{ 479 | Id: "some-id", 480 | Keyword: "Given", 481 | Text: "a step", 482 | Location: &messages.Location{ 483 | Line: 5, 484 | }, 485 | }, 486 | Pickle: &messages.Pickle{ 487 | Uri: "my_feature.feature", 488 | }, 489 | PickleStep: &messages.PickleStep{ 490 | Text: "a passed step", 491 | }, 492 | Result: &messages.TestStepResult{ 493 | Status: messages.TestStepResultStatus_FAILED, 494 | Duration: &messages.Duration{ 495 | Seconds: 123, 496 | Nanos: 456, 497 | }, 498 | }, 499 | StepDefinitions: []*messages.StepDefinition{ 500 | &messages.StepDefinition{ 501 | SourceReference: &messages.SourceReference{ 502 | JavaMethod: &messages.JavaMethod{ 503 | ClassName: "org.cucumber.jvm.Class", 504 | MethodName: "someMethod", 505 | MethodParameterTypes: []string{ 506 | "java.lang.String", 507 | "int", 508 | }, 509 | }, 510 | }, 511 | }, 512 | }, 513 | } 514 | jsonStep = TestStepToJSON(step) 515 | }) 516 | 517 | It("Has a Match", func() { 518 | Expect(jsonStep.Match.Location).To(Equal("org.cucumber.jvm.Class.someMethod(java.lang.String,int)")) 519 | }) 520 | }) 521 | }) 522 | 523 | Context("When SourceReference uses JavaStackTraceElement and Location", func() { 524 | Describe("from a Hook", func() { 525 | BeforeEach(func() { 526 | step = &TestStep{ 527 | Hook: &messages.Hook{ 528 | SourceReference: &messages.SourceReference{ 529 | JavaStackTraceElement: &messages.JavaStackTraceElement{ 530 | ClassName: "org.cucumber.jvm.ExceptionClass", 531 | MethodName: "someMethod", 532 | FileName: "ExceptionClass.java", 533 | }, 534 | Location: &messages.Location{ 535 | Line: 123, 536 | }, 537 | }, 538 | }, 539 | Result: &messages.TestStepResult{ 540 | Status: messages.TestStepResultStatus_PASSED, 541 | Duration: &messages.Duration{ 542 | Seconds: 123, 543 | Nanos: 456, 544 | }, 545 | }, 546 | } 547 | jsonStep = TestStepToJSON(step) 548 | }) 549 | 550 | It("Has a Match", func() { 551 | Expect(jsonStep.Match.Location).To(Equal("ExceptionClass.java:123")) 552 | }) 553 | }) 554 | 555 | Describe("from a feature step", func() { 556 | BeforeEach(func() { 557 | step = &TestStep{ 558 | Step: &messages.Step{ 559 | Id: "some-id", 560 | Keyword: "Given", 561 | Text: "a step", 562 | Location: &messages.Location{ 563 | Line: 5, 564 | }, 565 | }, 566 | Pickle: &messages.Pickle{ 567 | Uri: "my_feature.feature", 568 | }, 569 | PickleStep: &messages.PickleStep{ 570 | Text: "a passed step", 571 | }, 572 | Result: &messages.TestStepResult{ 573 | Status: messages.TestStepResultStatus_FAILED, 574 | Duration: &messages.Duration{ 575 | Seconds: 123, 576 | Nanos: 456, 577 | }, 578 | }, 579 | StepDefinitions: []*messages.StepDefinition{ 580 | &messages.StepDefinition{ 581 | SourceReference: &messages.SourceReference{ 582 | JavaStackTraceElement: &messages.JavaStackTraceElement{ 583 | ClassName: "org.cucumber.jvm.ExceptionClass", 584 | MethodName: "someMethod", 585 | FileName: "ExceptionClass.java", 586 | }, 587 | Location: &messages.Location{ 588 | Line: 123, 589 | }, 590 | }, 591 | }, 592 | }, 593 | } 594 | jsonStep = TestStepToJSON(step) 595 | }) 596 | 597 | It("Has a Match", func() { 598 | Expect(jsonStep.Match.Location).To(Equal("ExceptionClass.java:123")) 599 | }) 600 | }) 601 | }) 602 | 603 | Context("When SourceReference uses JavaStackTraceElement and no Location", func() { 604 | Describe("from a Hook", func() { 605 | BeforeEach(func() { 606 | step = &TestStep{ 607 | Hook: &messages.Hook{ 608 | SourceReference: &messages.SourceReference{ 609 | JavaStackTraceElement: &messages.JavaStackTraceElement{ 610 | ClassName: "org.cucumber.jvm.ExceptionClass", 611 | MethodName: "someMethod", 612 | FileName: "ExceptionClass.java", 613 | }, 614 | }, 615 | }, 616 | Result: &messages.TestStepResult{ 617 | Status: messages.TestStepResultStatus_PASSED, 618 | Duration: &messages.Duration{ 619 | Seconds: 123, 620 | Nanos: 456, 621 | }, 622 | }, 623 | } 624 | jsonStep = TestStepToJSON(step) 625 | }) 626 | 627 | It("Has a Match", func() { 628 | Expect(jsonStep.Match.Location).To(Equal("ExceptionClass.java")) 629 | }) 630 | }) 631 | 632 | Describe("from a feature step", func() { 633 | BeforeEach(func() { 634 | step = &TestStep{ 635 | Step: &messages.Step{ 636 | Id: "some-id", 637 | Keyword: "Given", 638 | Text: "a step", 639 | Location: &messages.Location{ 640 | Line: 5, 641 | }, 642 | }, 643 | Pickle: &messages.Pickle{ 644 | Uri: "my_feature.feature", 645 | }, 646 | PickleStep: &messages.PickleStep{ 647 | Text: "a passed step", 648 | }, 649 | Result: &messages.TestStepResult{ 650 | Status: messages.TestStepResultStatus_FAILED, 651 | Duration: &messages.Duration{ 652 | Seconds: 123, 653 | Nanos: 456, 654 | }, 655 | }, 656 | StepDefinitions: []*messages.StepDefinition{ 657 | &messages.StepDefinition{ 658 | SourceReference: &messages.SourceReference{ 659 | JavaStackTraceElement: &messages.JavaStackTraceElement{ 660 | ClassName: "org.cucumber.jvm.ExceptionClass", 661 | MethodName: "someMethod", 662 | FileName: "ExceptionClass.java", 663 | }, 664 | }, 665 | }, 666 | }, 667 | } 668 | jsonStep = TestStepToJSON(step) 669 | }) 670 | 671 | It("Has a Match", func() { 672 | Expect(jsonStep.Match.Location).To(Equal("ExceptionClass.java")) 673 | }) 674 | }) 675 | }) 676 | 677 | }) 678 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>cucumber/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /testdata/.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | features 3 | -------------------------------------------------------------------------------- /testdata/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 6 | 7 | gem "cucumber", "~> 9.2.0" 8 | gem "cucumber-compatibility-kit", "~> 18.0" 9 | gem "json" 10 | gem "rspec" 11 | -------------------------------------------------------------------------------- /testdata/Makefile: -------------------------------------------------------------------------------- 1 | CCK_PATH = $(shell bundle show cucumber-compatibility-kit) 2 | CCK_FEATURES=$(wildcard $(CCK_PATH)/features/*) 3 | FEATURES=$(patsubst $(CCK_PATH)/features/%,%,$(CCK_FEATURES)) 4 | UNSUPPORTED=pending rules skipped unknown-parameter-type markdown retry 5 | SUPPORTED_FEATURES=$(filter-out $(UNSUPPORTED), $(FEATURES)) 6 | JSONS_GOLDEN = $(foreach feature, $(SUPPORTED_FEATURES), features/$(feature)/$(feature).feature.json) 7 | NDJSONS = $(patsubst %.json,%.ndjson,$(JSONS_GOLDEN)) 8 | BUNDLE_GEMFILE=$(shell pwd)/Gemfile 9 | 10 | default: $(JSONS_GOLDEN) $(NDJSONS) Gemfile.lock 11 | .PHONY: default 12 | 13 | Gemfile.lock: Gemfile 14 | bundle install 15 | touch @ 16 | 17 | features/%.json: features/%-unprocessed.json 18 | cat $< | \ 19 | ./neutralize-json | \ 20 | jq --sort-keys "." > $@ 21 | 22 | features/%-unprocessed.json: $(CCK_PATH)/features/% 23 | $(eval feature = $(shell realpath $< --relative-to $(CCK_PATH))) 24 | $(eval stepdef = $<.rb) 25 | mkdir -p $(@D) 26 | -cd $(CCK_PATH) && \ 27 | CUCUMBER_PUBLISH_QUIET=true \ 28 | BUNDLE_GEMFILE=$(BUNDLE_GEMFILE) \ 29 | bundle exec cucumber $(feature) --require $(stepdef) --format=json > $(abspath $@) 30 | 31 | features/%.ndjson: $(CCK_PATH)/features/%.ndjson 32 | mkdir -p $(@D) 33 | cp $(patsubst %,$(CCK_PATH)/%,$@) $@ 34 | 35 | clean: 36 | rm -rf features 37 | -------------------------------------------------------------------------------- /testdata/README.md: -------------------------------------------------------------------------------- 1 | # Approval test data 2 | 3 | In this folder, we use [`cucumber-ruby`](github.com/cucumber/cucumber-ruby) to generate "golden master" test data that we use to verify the JSON formatter's output. 4 | 5 | We take the features from the [CCK](https://github.com/cucumber/common/tree/main/compatibility-kit) and run `cucumber-ruby` on each of them. 6 | 7 | This then gives us, for each CCK feature: 8 | 9 | * An `ndjson` of the messages output by a running CCK-compliant Cucumber (copied over from the CCK) 10 | * A `json` formatter output produced by `cucumber-ruby` 11 | 12 | These files are used to test the Go implementation. 13 | -------------------------------------------------------------------------------- /testdata/Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'rubygems' 3 | require 'bundler' 4 | Bundler::GemHelper.install_tasks 5 | 6 | $:.unshift File.expand_path("../lib", __FILE__) 7 | 8 | Dir['./rake/*.rb'].each do |f| 9 | require f 10 | end 11 | 12 | require "rspec/core/rake_task" 13 | RSpec::Core::RakeTask.new(:spec) 14 | 15 | require_relative 'spec/capture_warnings' 16 | include CaptureWarnings 17 | namespace :spec do 18 | task :warnings do 19 | report_warnings do 20 | Rake::Task['spec'].invoke 21 | end 22 | end 23 | end 24 | 25 | task default: ['spec:warnings'] 26 | -------------------------------------------------------------------------------- /testdata/cucumber.yml: -------------------------------------------------------------------------------- 1 | default: --publish-quiet 2 | -------------------------------------------------------------------------------- /testdata/neutralize-json: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Replaces certain "uncontrollable" properties with fixed values 4 | # This allows for comparison of documents without expecting these 5 | # properties to be equal 6 | # 7 | set -euf -o pipefail 8 | 9 | jq ".[].elements[]?.before[]?.result.duration = 99" | \ 10 | jq ".[].elements[]?.before[]?.result.error_message = \"some before hook error\"" | \ 11 | jq ".[].elements[]?.before[]?.match.location = \"some_before_hook.xyz\"" | 12 | 13 | jq ".[].elements[]?.steps[]?.result.duration = 99" | \ 14 | jq ".[].elements[]?.steps[]?.result.error_message = \"some stepdef error\"" | \ 15 | jq ".[].elements[]?.steps[]?.match.location |= 16 | if test(\"(.*.feature:[^:]+)\") then 17 | match(\"(.*.feature:[^:]+)\").captures[0].string 18 | else 19 | \"some_stepdef.xyz\" 20 | end" | \ 21 | jq ".[].elements[]?.after[]?.result.duration = 99" | \ 22 | jq ".[].elements[]?.after[]?.result.error_message = \"some after hook error\"" | \ 23 | jq ".[].elements[]?.after[]?.match.location = \"some_after_hook.xyz\"" | \ 24 | 25 | jq "." 26 | --------------------------------------------------------------------------------