Этот коммит содержится в:
Levi Noecker 2021-08-10 04:27:17 -05:00 коммит произвёл GitHub
родитель ad7feb3298
коммит c6c2a0885b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 584 добавлений и 304 удалений

Просмотреть файл

@ -13,11 +13,12 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
### Added
- Support for step definitions without return ([364](https://github.com/cucumber/godog/pull/364) - [titouanfreville])
- Contextualized hooks for scenarios and steps ([409](https://github.com/cucumber/godog/pull/409)) - [vearutop])
- Step result status in After hook ([409](https://github.com/cucumber/godog/pull/409)) - [vearutop])
- Support auto converting doc strings to plain strings ([380](https://github.com/cucumber/godog/pull/380)) - [chirino])
- Use multiple formatters in the same test run ([392](https://github.com/cucumber/godog/pull/392)) - [vearutop])
- Added `RetrieveFeatures()` method to `godog.TestSuite` ([276](https://github.com/cucumber/godog/pull/276)) - [radtriste])
- Contextualized hooks for scenarios and steps ([409](https://github.com/cucumber/godog/pull/409) - [vearutop])
- Step result status in After hook ([409](https://github.com/cucumber/godog/pull/409) - [vearutop])
- Support auto converting doc strings to plain strings ([380](https://github.com/cucumber/godog/pull/380) - [chirino])
- Use multiple formatters in the same test run ([392](https://github.com/cucumber/godog/pull/392) - [vearutop])
- Added `RetrieveFeatures()` method to `godog.TestSuite` ([276](https://github.com/cucumber/godog/pull/276) - [radtriste])
- Added support to create custom formatters ([372](https://github.com/cucumber/godog/pull/372) - [leviable])
### Changed

19
_examples/custom-formatter/README.md Обычный файл
Просмотреть файл

@ -0,0 +1,19 @@
# Custom Formatter Example
This example custom formatter demonstrates some ways to build and use custom formatters with godog
## Emoji Progress
The first example is the Emoji formatter, built on top of the Progress formatter that is included with godog.
To run it:
```
$ godog -f emoji
```
Which would output step progress as emojis rather than text:
![](imgs/emoji-output-example.png)

Просмотреть файл

@ -1,32 +0,0 @@
package customformatter
import (
"io"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
)
func init() {
godog.Format("custom", "Custom formatter", customFormatterFunc)
}
func customFormatterFunc(suite string, out io.Writer) godog.Formatter {
return &customFmt{suiteName: suite, out: out}
}
type customFmt struct {
suiteName string
out io.Writer
}
func (f *customFmt) TestRunStarted() {}
func (f *customFmt) Feature(*messages.GherkinDocument, string, []byte) {}
func (f *customFmt) Pickle(*godog.Scenario) {}
func (f *customFmt) Defined(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Passed(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Skipped(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Undefined(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Failed(*godog.Scenario, *godog.Step, *godog.StepDefinition, error) {}
func (f *customFmt) Pending(*godog.Scenario, *godog.Step, *godog.StepDefinition) {}
func (f *customFmt) Summary() {}

122
_examples/custom-formatter/emoji.go Обычный файл
Просмотреть файл

@ -0,0 +1,122 @@
package main
import (
"fmt"
"io"
"math"
"github.com/cucumber/godog"
)
const (
passedEmoji = "✅"
skippedEmoji = ""
failedEmoji = "❌"
undefinedEmoji = "❓"
pendingEmoji = "🚧"
)
func init() {
godog.Format("emoji", "Progress formatter with emojis", emojiFormatterFunc)
}
func emojiFormatterFunc(suite string, out io.Writer) godog.Formatter {
return newEmojiFmt(suite, out)
}
func newEmojiFmt(suite string, out io.Writer) *emojiFmt {
return &emojiFmt{
ProgressFmt: godog.NewProgressFmt(suite, out),
out: out,
}
}
type emojiFmt struct {
*godog.ProgressFmt
out io.Writer
}
func (f *emojiFmt) TestRunStarted() {}
func (f *emojiFmt) Passed(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Passed(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Skipped(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Skipped(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Undefined(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Undefined(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Failed(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition, err error) {
f.ProgressFmt.Base.Failed(scenario, step, match, err)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Pending(scenario *godog.Scenario, step *godog.Step, match *godog.StepDefinition) {
f.ProgressFmt.Base.Pending(scenario, step, match)
f.ProgressFmt.Base.Lock.Lock()
defer f.ProgressFmt.Base.Lock.Unlock()
f.step(step.Id)
}
func (f *emojiFmt) Summary() {
f.printSummaryLegend()
f.ProgressFmt.Summary()
}
func (f *emojiFmt) printSummaryLegend() {
fmt.Fprint(f.out, "\n\nOutput Legend:\n")
fmt.Fprint(f.out, fmt.Sprintf("\t%s Passed\n", passedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Failed\n", failedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Skipped\n", skippedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Undefined\n", undefinedEmoji))
fmt.Fprint(f.out, fmt.Sprintf("\t%s Pending\n", pendingEmoji))
}
func (f *emojiFmt) step(pickleStepID string) {
pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status {
case godog.StepPassed:
fmt.Fprint(f.out, fmt.Sprintf(" %s", passedEmoji))
case godog.StepSkipped:
fmt.Fprint(f.out, fmt.Sprintf(" %s", skippedEmoji))
case godog.StepFailed:
fmt.Fprint(f.out, fmt.Sprintf(" %s", failedEmoji))
case godog.StepUndefined:
fmt.Fprint(f.out, fmt.Sprintf(" %s", undefinedEmoji))
case godog.StepPending:
fmt.Fprint(f.out, fmt.Sprintf(" %s", pendingEmoji))
}
*f.Steps++
if math.Mod(float64(*f.Steps), float64(f.StepsPerRow)) == 0 {
fmt.Fprintf(f.out, " %d\n", *f.Steps)
}
}

Просмотреть файл

@ -0,0 +1,26 @@
# file: $GOPATH/godogs/features/godogs.feature
Feature: Custom emoji formatter examples
In order to be happy
As a hungry gopher
I need to be able to eat godogs
Scenario: Passing test
Given there are 12 godogs
When I eat 5
Then there should be 7 remaining
Scenario: Failing and Skipped test
Given there are 12 godogs
When I eat 5
Then there should be 6 remaining
And there should be 4 remaining
Scenario: Undefined steps
Given there are 12 godogs
When I eat 5
Then this step is not defined
Scenario: Pending step
Given there are 12 godogs
When I eat 5
Then this step is pending

Просмотреть файл

@ -3,10 +3,11 @@ module custom-formatter
go 1.14
require (
github.com/cucumber/gherkin-go/v11 v11.0.0 // indirect
github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b
github.com/cucumber/messages-go/v10 v10.0.3 // indirect
github.com/cucumber/messages-go/v16 v16.0.1
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-memdb v1.3.2 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/spf13/pflag v1.0.5
)
replace github.com/cucumber/godog => ../..

Просмотреть файл

@ -19,7 +19,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@ -33,19 +32,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cucumber/gherkin-go/v11 v11.0.0 h1:cwVwN1Qn2VRSfHZNLEh5x00tPBmZcjATBWDpxsR5Xug=
github.com/cucumber/gherkin-go/v11 v11.0.0/go.mod h1:CX33k2XU2qog4e+TFjOValoq6mIUq0DmVccZs238R9w=
github.com/cucumber/gherkin-go/v19 v19.0.3 h1:mMSKu1077ffLbTJULUfM5HPokgeBcIGboyeNUof1MdE=
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b h1:TthfOM7c3wz2M5kUvktCU27WXufvUSV/YtcxQk2yaq8=
github.com/cucumber/godog v0.10.1-0.20210705192606-df8c6e49b40b/go.mod h1:ql2U1OH5nlLZ2UDD/3fDJ1+0vkib0XGgEn8NYXCwDZQ=
github.com/cucumber/godog v0.11.0 h1:xgaWyJuAD6A+aW4TfVGNDBhuMyKW0jjl0cvY3KNxEak=
github.com/cucumber/godog v0.11.0/go.mod h1:GyxCIrsg1sgEgpL2GD/rMr3fIoNHpgkjm9nANw/89XY=
github.com/cucumber/messages-go/v10 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg=
github.com/cucumber/messages-go/v10 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY=
github.com/cucumber/messages-go/v16 v10.0.1/go.mod h1:kA5T38CBlBbYLU12TIrJ4fk4wSkVVOgyh7Enyy8WnSg=
github.com/cucumber/messages-go/v16 v10.0.3 h1:m/9SD/K/A15WP7i1aemIv7cwvUw+viS51Ui5HBw1cdE=
github.com/cucumber/messages-go/v16 v10.0.3/go.mod h1:9jMZ2Y8ZxjLY6TG2+x344nt5rXstVVDYSdS5ySfI1WY=
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
github.com/cucumber/messages-go/v16 v16.0.1 h1:fvkpwsLgnIm0qugftrw2YwNlio+ABe2Iu94Ap8GMYIY=
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
@ -62,14 +50,10 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -88,6 +72,7 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@ -98,11 +83,9 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE=
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.3.0 h1:xdXq34gBOMEloa9rlGStLxmfX/dyIK8htOv36dQUwHU=
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
github.com/hashicorp/go-memdb v1.3.2 h1:RBKHOsnSszpU6vxq80LzC2BaQjuuvoyaQbkLTf7V7g8=
github.com/hashicorp/go-memdb v1.3.2/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
@ -115,7 +98,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -130,15 +112,14 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -161,7 +142,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -186,7 +166,9 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@ -202,9 +184,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
@ -281,7 +260,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -322,8 +300,6 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@ -331,7 +307,6 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

6
_examples/custom-formatter/godogs.go Обычный файл
Просмотреть файл

@ -0,0 +1,6 @@
package main
// Godogs available to eat
var Godogs int
func main() { /* usual main func */ }

75
_examples/custom-formatter/godogs_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,75 @@
package main
import (
"context"
"fmt"
"os"
"testing"
"github.com/cucumber/godog"
"github.com/cucumber/godog/colors"
flag "github.com/spf13/pflag"
)
var opts = godog.Options{
Output: colors.Colored(os.Stdout),
Format: "emoji",
}
func init() {
godog.BindCommandLineFlags("godog.", &opts)
}
func TestMain(m *testing.M) {
flag.Parse()
opts.Paths = flag.Args()
status := godog.TestSuite{
Name: "godogs",
TestSuiteInitializer: InitializeTestSuite,
ScenarioInitializer: InitializeScenario,
Options: &opts,
}.Run()
os.Exit(status)
}
func thereAreGodogs(available int) error {
Godogs = available
return nil
}
func iEat(num int) error {
if Godogs < num {
return fmt.Errorf("you cannot eat %d godogs, there are %d available", num, Godogs)
}
Godogs -= num
return nil
}
func thereShouldBeRemaining(remaining int) error {
if Godogs != remaining {
return fmt.Errorf("expected %d godogs to be remaining, but there is %d", remaining, Godogs)
}
return nil
}
func thisStepIsPending() error {
return godog.ErrPending
}
func InitializeTestSuite(ctx *godog.TestSuiteContext) {
ctx.BeforeSuite(func() { Godogs = 0 })
}
func InitializeScenario(ctx *godog.ScenarioContext) {
ctx.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
Godogs = 0 // clean the state before every scenario
return ctx, nil
})
ctx.Step(`^there are (\d+) godogs$`, thereAreGodogs)
ctx.Step(`^I eat (\d+)$`, iEat)
ctx.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining)
ctx.Step(`^this step is pending$`, thisStepIsPending)
}

Двоичные данные
_examples/custom-formatter/imgs/emoji-output-example.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 87 КиБ

48
fmt.go
Просмотреть файл

@ -74,3 +74,51 @@ func printStepDefinitions(steps []*models.StepDefinition, w io.Writer) {
fmt.Fprintln(w, "there were no contexts registered, could not find any step definition..")
}
}
// NewBaseFmt creates a new base formatter.
func NewBaseFmt(suite string, out io.Writer) *BaseFmt {
return internal_fmt.NewBase(suite, out)
}
// NewProgressFmt creates a new progress formatter.
func NewProgressFmt(suite string, out io.Writer) *ProgressFmt {
return internal_fmt.NewProgress(suite, out)
}
// NewPrettyFmt creates a new pretty formatter.
func NewPrettyFmt(suite string, out io.Writer) *PrettyFmt {
return &PrettyFmt{Base: NewBaseFmt(suite, out)}
}
// NewEventsFmt creates a new event streaming formatter.
func NewEventsFmt(suite string, out io.Writer) *EventsFmt {
return &EventsFmt{Base: NewBaseFmt(suite, out)}
}
// NewCukeFmt creates a new Cucumber JSON formatter.
func NewCukeFmt(suite string, out io.Writer) *CukeFmt {
return &CukeFmt{Base: NewBaseFmt(suite, out)}
}
// NewJUnitFmt creates a new JUnit formatter.
func NewJUnitFmt(suite string, out io.Writer) *JUnitFmt {
return &JUnitFmt{Base: NewBaseFmt(suite, out)}
}
// BaseFmt exports Base formatter.
type BaseFmt = internal_fmt.Base
// ProgressFmt exports Progress formatter.
type ProgressFmt = internal_fmt.Progress
// PrettyFmt exports Pretty formatter.
type PrettyFmt = internal_fmt.Pretty
// EventsFmt exports Events formatter.
type EventsFmt = internal_fmt.Events
// CukeFmt exports Cucumber JSON formatter.
type CukeFmt = internal_fmt.Cuke
// JUnitFmt exports JUnit formatter.
type JUnitFmt = internal_fmt.JUnit

Просмотреть файл

@ -20,82 +20,82 @@ import (
"github.com/cucumber/godog/internal/utils"
)
// BaseFormatterFunc implements the FormatterFunc for the base formatter
// BaseFormatterFunc implements the FormatterFunc for the base formatter.
func BaseFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return NewBaseFmt(suite, out)
return NewBase(suite, out)
}
// NewBaseFmt creates a new base formatter
func NewBaseFmt(suite string, out io.Writer) *Basefmt {
return &Basefmt{
// NewBase creates a new base formatter.
func NewBase(suite string, out io.Writer) *Base {
return &Base{
suiteName: suite,
indent: 2,
out: out,
lock: new(sync.Mutex),
Lock: new(sync.Mutex),
}
}
// Basefmt ...
type Basefmt struct {
// Base is a base formatter.
type Base struct {
suiteName string
out io.Writer
indent int
storage *storage.Storage
lock *sync.Mutex
Storage *storage.Storage
Lock *sync.Mutex
}
// SetStorage ...
func (f *Basefmt) SetStorage(st *storage.Storage) {
f.lock.Lock()
defer f.lock.Unlock()
// SetStorage assigns gherkin data storage.
func (f *Base) SetStorage(st *storage.Storage) {
f.Lock.Lock()
defer f.Lock.Unlock()
f.storage = st
f.Storage = st
}
// TestRunStarted ...
func (f *Basefmt) TestRunStarted() {}
// TestRunStarted is triggered on test start.
func (f *Base) TestRunStarted() {}
// Feature ...
func (f *Basefmt) Feature(*messages.GherkinDocument, string, []byte) {}
// Feature receives gherkin document.
func (f *Base) Feature(*messages.GherkinDocument, string, []byte) {}
// Pickle ...
func (f *Basefmt) Pickle(*messages.Pickle) {}
// Pickle receives scenario.
func (f *Base) Pickle(*messages.Pickle) {}
// Defined ...
func (f *Basefmt) Defined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
// Defined receives step definition.
func (f *Base) Defined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
}
// Passed ...
func (f *Basefmt) Passed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {}
// Passed captures passed step.
func (f *Base) Passed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {}
// Skipped ...
func (f *Basefmt) Skipped(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
// Skipped captures skipped step.
func (f *Base) Skipped(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
}
// Undefined ...
func (f *Basefmt) Undefined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
// Undefined captures undefined step.
func (f *Base) Undefined(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
}
// Failed ...
func (f *Basefmt) Failed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition, error) {
// Failed captures failed step.
func (f *Base) Failed(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition, error) {
}
// Pending ...
func (f *Basefmt) Pending(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
// Pending captures pending step.
func (f *Base) Pending(*messages.Pickle, *messages.PickleStep, *formatters.StepDefinition) {
}
// Summary ...
func (f *Basefmt) Summary() {
// Summary renders summary information.
func (f *Base) Summary() {
var totalSc, passedSc, undefinedSc int
var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int
pickleResults := f.storage.MustGetPickleResults()
pickleResults := f.Storage.MustGetPickleResults()
for _, pr := range pickleResults {
var prStatus models.StepResultStatus
totalSc++
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pr.PickleID)
pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pr.PickleID)
if len(pickleStepResults) == 0 {
prStatus = undefined
@ -156,7 +156,7 @@ func (f *Basefmt) Summary() {
}
scenarios = append(scenarios, parts...)
testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt
testRunStartedAt := f.Storage.MustGetTestRunStarted().StartedAt
elapsed := utils.TimeNowFunc().Sub(testRunStartedAt)
fmt.Fprintln(f.out, "")
@ -194,9 +194,9 @@ func (f *Basefmt) Summary() {
}
}
// Snippets ...
func (f *Basefmt) Snippets() string {
undefinedStepResults := f.storage.MustGetPickleStepResultsByStatus(undefined)
// Snippets returns code suggestions for undefined steps.
func (f *Base) Snippets() string {
undefinedStepResults := f.Storage.MustGetPickleStepResultsByStatus(undefined)
if len(undefinedStepResults) == 0 {
return ""
}
@ -205,7 +205,7 @@ func (f *Basefmt) Snippets() string {
var snips []undefinedSnippet
// build snippets
for _, u := range undefinedStepResults {
pickleStep := f.storage.MustGetPickleStep(u.PickleStepID)
pickleStep := f.Storage.MustGetPickleStep(u.PickleStepID)
steps := []string{pickleStep.Text}
arg := pickleStep.Argument

Просмотреть файл

@ -30,15 +30,17 @@ func init() {
// CucumberFormatterFunc implements the FormatterFunc for the cucumber formatter
func CucumberFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &cukefmt{Basefmt: NewBaseFmt(suite, out)}
return &Cuke{Base: NewBase(suite, out)}
}
type cukefmt struct {
*Basefmt
// Cuke ...
type Cuke struct {
*Base
}
func (f *cukefmt) Summary() {
features := f.storage.MustGetFeatures()
// Summary renders test result as Cucumber JSON.
func (f *Cuke) Summary() {
features := f.Storage.MustGetFeatures()
res := f.buildCukeFeatures(features)
@ -50,7 +52,7 @@ func (f *cukefmt) Summary() {
fmt.Fprintf(f.out, "%s\n", string(dat))
}
func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatureJSON) {
func (f *Cuke) buildCukeFeatures(features []*models.Feature) (res []CukeFeatureJSON) {
sort.Sort(sortFeaturesByName(features))
res = make([]CukeFeatureJSON, len(features))
@ -58,7 +60,7 @@ func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatu
for idx, feat := range features {
cukeFeature := buildCukeFeature(feat)
pickles := f.storage.MustGetPickles(feat.Uri)
pickles := f.Storage.MustGetPickles(feat.Uri)
sort.Sort(sortPicklesByID(pickles))
cukeFeature.Elements = f.buildCukeElements(pickles)
@ -75,12 +77,12 @@ func (f *cukefmt) buildCukeFeatures(features []*models.Feature) (res []CukeFeatu
return res
}
func (f *cukefmt) buildCukeElements(pickles []*messages.Pickle) (res []cukeElement) {
func (f *Cuke) buildCukeElements(pickles []*messages.Pickle) (res []cukeElement) {
res = make([]cukeElement, len(pickles))
for idx, pickle := range pickles {
pickleResult := f.storage.MustGetPickleResult(pickle.Id)
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
pickleResult := f.Storage.MustGetPickleResult(pickle.Id)
pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
cukeElement := f.buildCukeElement(pickle)
@ -201,8 +203,8 @@ func buildCukeFeature(feat *models.Feature) CukeFeatureJSON {
return cukeFeature
}
func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) {
feature := f.storage.MustGetFeature(pickle.Uri)
func (f *Cuke) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeElement) {
feature := f.Storage.MustGetFeature(pickle.Uri)
scenario := feature.FindScenario(pickle.AstNodeIds[0])
cukeElement.Name = pickle.Name
@ -245,9 +247,9 @@ func (f *cukefmt) buildCukeElement(pickle *messages.Pickle) (cukeElement cukeEle
return cukeElement
}
func (f *cukefmt) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleStepResult) (cukeStep cukeStep) {
feature := f.storage.MustGetFeature(pickle.Uri)
pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID)
func (f *Cuke) buildCukeStep(pickle *messages.Pickle, stepResult models.PickleStepResult) (cukeStep cukeStep) {
feature := f.Storage.MustGetFeature(pickle.Uri)
pickleStep := f.Storage.MustGetPickleStep(stepResult.PickleStepID)
step := feature.FindStep(pickleStep.AstNodeIds[0])
line := step.Location.Line

Просмотреть файл

@ -20,14 +20,15 @@ func init() {
// EventsFormatterFunc implements the FormatterFunc for the events formatter
func EventsFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &eventsFormatter{Basefmt: NewBaseFmt(suite, out)}
return &Events{Base: NewBase(suite, out)}
}
type eventsFormatter struct {
*Basefmt
// Events - Events formatter
type Events struct {
*Base
}
func (f *eventsFormatter) event(ev interface{}) {
func (f *Events) event(ev interface{}) {
data, err := json.Marshal(ev)
if err != nil {
panic(fmt.Sprintf("failed to marshal stream event: %+v - %v", ev, err))
@ -35,11 +36,12 @@ func (f *eventsFormatter) event(ev interface{}) {
fmt.Fprintln(f.out, string(data))
}
func (f *eventsFormatter) Pickle(pickle *messages.Pickle) {
f.Basefmt.Pickle(pickle)
// Pickle receives scenario.
func (f *Events) Pickle(pickle *messages.Pickle) {
f.Base.Pickle(pickle)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.event(&struct {
Event string `json:"event"`
@ -68,11 +70,12 @@ func (f *eventsFormatter) Pickle(pickle *messages.Pickle) {
}
}
func (f *eventsFormatter) TestRunStarted() {
f.Basefmt.TestRunStarted()
// TestRunStarted is triggered on test start.
func (f *Events) TestRunStarted() {
f.Base.TestRunStarted()
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.event(&struct {
Event string `json:"event"`
@ -87,11 +90,12 @@ func (f *eventsFormatter) TestRunStarted() {
})
}
func (f *eventsFormatter) Feature(ft *messages.GherkinDocument, p string, c []byte) {
f.Basefmt.Feature(ft, p, c)
// Feature receives gherkin document.
func (f *Events) Feature(ft *messages.GherkinDocument, p string, c []byte) {
f.Base.Feature(ft, p, c)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.event(&struct {
Event string `json:"event"`
@ -104,16 +108,17 @@ func (f *eventsFormatter) Feature(ft *messages.GherkinDocument, p string, c []by
})
}
func (f *eventsFormatter) Summary() {
// Summary pushes summary information to JSON stream.
func (f *Events) Summary() {
// @TODO: determine status
status := passed
f.storage.MustGetPickleStepResultsByStatus(failed)
f.Storage.MustGetPickleStepResultsByStatus(failed)
if len(f.storage.MustGetPickleStepResultsByStatus(failed)) > 0 {
if len(f.Storage.MustGetPickleStepResultsByStatus(failed)) > 0 {
status = failed
} else if len(f.storage.MustGetPickleStepResultsByStatus(passed)) == 0 {
if len(f.storage.MustGetPickleStepResultsByStatus(undefined)) > len(f.storage.MustGetPickleStepResultsByStatus(pending)) {
} else if len(f.Storage.MustGetPickleStepResultsByStatus(passed)) == 0 {
if len(f.Storage.MustGetPickleStepResultsByStatus(undefined)) > len(f.Storage.MustGetPickleStepResultsByStatus(pending)) {
status = undefined
} else {
status = pending
@ -140,9 +145,9 @@ func (f *eventsFormatter) Summary() {
})
}
func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.storage.MustGetFeature(pickle.Uri)
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id)
func (f *Events) step(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.Storage.MustGetFeature(pickle.Uri)
pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStep.Id)
step := feature.FindStep(pickleStep.AstNodeIds[0])
var errMsg string
@ -166,7 +171,7 @@ func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.Pic
if isLastStep(pickle, pickleStep) {
var status string
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults {
switch stepResult.Status {
case passed, failed, undefined, pending:
@ -188,17 +193,18 @@ func (f *eventsFormatter) step(pickle *messages.Pickle, pickleStep *messages.Pic
}
}
func (f *eventsFormatter) Defined(pickle *messages.Pickle, pickleStep *messages.PickleStep, def *formatters.StepDefinition) {
f.Basefmt.Defined(pickle, pickleStep, def)
// Defined receives step definition.
func (f *Events) Defined(pickle *messages.Pickle, pickleStep *messages.PickleStep, def *formatters.StepDefinition) {
f.Base.Defined(pickle, pickleStep, def)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
feature := f.storage.MustGetFeature(pickle.Uri)
feature := f.Storage.MustGetFeature(pickle.Uri)
step := feature.FindStep(pickleStep.AstNodeIds[0])
if def != nil {
matchedDef := f.storage.MustGetStepDefintionMatch(pickleStep.AstNodeIds[0])
matchedDef := f.Storage.MustGetStepDefintionMatch(pickleStep.AstNodeIds[0])
m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:]
var args [][2]int
@ -238,53 +244,58 @@ func (f *eventsFormatter) Defined(pickle *messages.Pickle, pickleStep *messages.
})
}
func (f *eventsFormatter) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Passed(pickle, step, match)
// Passed captures passed step.
func (f *Events) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(pickle, step)
}
func (f *eventsFormatter) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Skipped(pickle, step, match)
// Skipped captures skipped step.
func (f *Events) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(pickle, step)
}
func (f *eventsFormatter) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Undefined(pickle, step, match)
// Undefined captures undefined step.
func (f *Events) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(pickle, step)
}
func (f *eventsFormatter) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Basefmt.Failed(pickle, step, match, err)
// Failed captures failed step.
func (f *Events) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Base.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(pickle, step)
}
func (f *eventsFormatter) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Pending(pickle, step, match)
// Pending captures pending step.
func (f *Events) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(pickle, step)
}
func (f *eventsFormatter) scenarioLocation(pickle *messages.Pickle) string {
feature := f.storage.MustGetFeature(pickle.Uri)
func (f *Events) scenarioLocation(pickle *messages.Pickle) string {
feature := f.Storage.MustGetFeature(pickle.Uri)
scenario := feature.FindScenario(pickle.AstNodeIds[0])
line := scenario.Location.Line

Просмотреть файл

@ -19,14 +19,16 @@ func init() {
// JUnitFormatterFunc implements the FormatterFunc for the junit formatter
func JUnitFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &junitFormatter{Basefmt: NewBaseFmt(suite, out)}
return &JUnit{Base: NewBase(suite, out)}
}
type junitFormatter struct {
*Basefmt
// JUnit renders test results in JUnit format.
type JUnit struct {
*Base
}
func (f *junitFormatter) Summary() {
// Summary renders summary information.
func (f *JUnit) Summary() {
suite := f.buildJUNITPackageSuite()
_, err := io.WriteString(f.out, xml.Header)
@ -45,11 +47,11 @@ func junitTimeDuration(from, to time.Time) string {
return strconv.FormatFloat(to.Sub(from).Seconds(), 'f', -1, 64)
}
func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
features := f.storage.MustGetFeatures()
func (f *JUnit) buildJUNITPackageSuite() JunitPackageSuite {
features := f.Storage.MustGetFeatures()
sort.Sort(sortFeaturesByName(features))
testRunStartedAt := f.storage.MustGetTestRunStarted().StartedAt
testRunStartedAt := f.Storage.MustGetTestRunStarted().StartedAt
suite := JunitPackageSuite{
Name: f.suiteName,
@ -58,7 +60,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
}
for idx, feature := range features {
pickles := f.storage.MustGetPickles(feature.Uri)
pickles := f.Storage.MustGetPickles(feature.Uri)
sort.Sort(sortPicklesByID(pickles))
ts := junitTestSuite{
@ -78,7 +80,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
for idx, pickle := range pickles {
tc := junitTestCase{}
pickleResult := f.storage.MustGetPickleResult(pickle.Id)
pickleResult := f.Storage.MustGetPickleResult(pickle.Id)
if idx == 0 {
firstPickleStartedAt = pickleResult.StartedAt
@ -88,7 +90,7 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
if len(pickle.Steps) > 0 {
lastStep := pickle.Steps[len(pickle.Steps)-1]
lastPickleStepResult := f.storage.MustGetPickleStepResult(lastStep.Id)
lastPickleStepResult := f.Storage.MustGetPickleStepResult(lastStep.Id)
lastPickleFinishedAt = lastPickleStepResult.FinishedAt
}
@ -103,9 +105,9 @@ func (f *junitFormatter) buildJUNITPackageSuite() JunitPackageSuite {
ts.Tests++
suite.Tests++
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
for _, stepResult := range pickleStepResults {
pickleStep := f.storage.MustGetPickleStep(stepResult.PickleStepID)
pickleStep := f.Storage.MustGetPickleStep(stepResult.PickleStepID)
switch stepResult.Status {
case passed:

Просмотреть файл

@ -20,50 +20,52 @@ func init() {
// PrettyFormatterFunc implements the FormatterFunc for the pretty formatter
func PrettyFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return &pretty{Basefmt: NewBaseFmt(suite, out)}
return &Pretty{Base: NewBase(suite, out)}
}
var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>")
// a built in default pretty formatter
type pretty struct {
*Basefmt
// Pretty is a formatter for readable output.
type Pretty struct {
*Base
firstFeature *bool
}
func (f *pretty) TestRunStarted() {
f.Basefmt.TestRunStarted()
// TestRunStarted is triggered on test start.
func (f *Pretty) TestRunStarted() {
f.Base.TestRunStarted()
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
firstFeature := true
f.firstFeature = &firstFeature
}
func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
f.lock.Lock()
// Feature receives gherkin document.
func (f *Pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) {
f.Lock.Lock()
if !*f.firstFeature {
fmt.Fprintln(f.out, "")
}
*f.firstFeature = false
f.lock.Unlock()
f.Lock.Unlock()
f.Basefmt.Feature(gd, p, c)
f.Base.Feature(gd, p, c)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.printFeature(gd.Feature)
}
// Pickle takes a gherkin node for formatting
func (f *pretty) Pickle(pickle *messages.Pickle) {
f.Basefmt.Pickle(pickle)
// Pickle takes a gherkin node for formatting.
func (f *Pretty) Pickle(pickle *messages.Pickle) {
f.Base.Pickle(pickle)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
if len(pickle.Steps) == 0 {
f.printUndefinedPickle(pickle)
@ -71,52 +73,57 @@ func (f *pretty) Pickle(pickle *messages.Pickle) {
}
}
func (f *pretty) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Passed(pickle, step, match)
// Passed captures passed step.
func (f *Pretty) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.printStep(pickle, step)
}
func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Skipped(pickle, step, match)
// Skipped captures skipped step.
func (f *Pretty) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.printStep(pickle, step)
}
func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Undefined(pickle, step, match)
// Undefined captures undefined step.
func (f *Pretty) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.printStep(pickle, step)
}
func (f *pretty) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Basefmt.Failed(pickle, step, match, err)
// Failed captures failed step.
func (f *Pretty) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Base.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.printStep(pickle, step)
}
func (f *pretty) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Pending(pickle, step, match)
// Pending captures pending step.
func (f *Pretty) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.printStep(pickle, step)
}
func (f *pretty) printFeature(feature *messages.Feature) {
func (f *Pretty) printFeature(feature *messages.Feature) {
fmt.Fprintln(f.out, keywordAndName(feature.Keyword, feature.Name))
if strings.TrimSpace(feature.Description) != "" {
for _, line := range strings.Split(feature.Description, "\n") {
@ -133,8 +140,8 @@ func keywordAndName(keyword, name string) string {
return title
}
func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) {
feature := f.storage.MustGetFeature(pickle.Uri)
func (f *Pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength int, maxLength int) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
@ -148,15 +155,15 @@ func (f *pretty) scenarioLengths(pickle *messages.Pickle) (scenarioHeaderLength
return scenarioHeaderLength, maxLength
}
func (f *pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.Scenario, spaceFilling int) {
feature := f.storage.MustGetFeature(pickle.Uri)
func (f *Pretty) printScenarioHeader(pickle *messages.Pickle, astScenario *messages.Scenario, spaceFilling int) {
feature := f.Storage.MustGetFeature(pickle.Uri)
text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name)
text += s(spaceFilling) + line(feature.Uri, astScenario.Location)
fmt.Fprintln(f.out, "\n"+text)
}
func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
feature := f.storage.MustGetFeature(pickle.Uri)
func (f *Pretty) printUndefinedPickle(pickle *messages.Pickle) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
@ -197,18 +204,18 @@ func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) {
}
}
// Summary sumarize the feature formatter output
func (f *pretty) Summary() {
failedStepResults := f.storage.MustGetPickleStepResultsByStatus(failed)
// Summary renders summary information.
func (f *Pretty) Summary() {
failedStepResults := f.Storage.MustGetPickleStepResultsByStatus(failed)
if len(failedStepResults) > 0 {
fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n")
sort.Sort(sortPickleStepResultsByPickleStepID(failedStepResults))
for _, fail := range failedStepResults {
pickle := f.storage.MustGetPickle(fail.PickleID)
pickleStep := f.storage.MustGetPickleStep(fail.PickleStepID)
feature := f.storage.MustGetFeature(pickle.Uri)
pickle := f.Storage.MustGetPickle(fail.PickleID)
pickleStep := f.Storage.MustGetPickleStep(fail.PickleStepID)
feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, pickle.Name)
@ -222,14 +229,14 @@ func (f *pretty) Summary() {
}
}
f.Basefmt.Summary()
f.Base.Summary()
}
func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) {
func (f *Pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) {
var errorMsg string
var clr = green
feature := f.storage.MustGetFeature(pickle.Uri)
feature := f.Storage.MustGetFeature(pickle.Uri)
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
scenarioHeaderLength, maxLength := f.scenarioLengths(pickle)
@ -237,7 +244,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id
firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line
pickleStepResults := f.storage.MustGetPickleStepResultsByPickleID(pickle.Id)
pickleStepResults := f.Storage.MustGetPickleStepResultsByPickleID(pickle.Id)
firstExecutedScenarioStep := len(pickleStepResults) == backgroundSteps+1
if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep {
@ -270,7 +277,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
if firstExamplesTable && printExampleHeader {
// in first example, we need to print steps
pickleStep := f.storage.MustGetPickleStep(result.PickleStepID)
pickleStep := f.Storage.MustGetPickleStep(result.PickleStepID)
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
var text = ""
@ -327,7 +334,7 @@ func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps in
}
}
func (f *pretty) printTableRow(row *messages.TableRow, max []int, clr colors.ColorFunc) {
func (f *Pretty) printTableRow(row *messages.TableRow, max []int, clr colors.ColorFunc) {
cells := make([]string, len(row.Cells))
for i, cell := range row.Cells {
@ -339,12 +346,12 @@ func (f *pretty) printTableRow(row *messages.TableRow, max []int, clr colors.Col
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |")
}
func (f *pretty) printTableHeader(row *messages.TableRow, max []int) {
func (f *Pretty) printTableHeader(row *messages.TableRow, max []int) {
f.printTableRow(row, max, cyan)
}
func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.storage.MustGetFeature(pickle.Uri)
func (f *Pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleStep) {
feature := f.Storage.MustGetFeature(pickle.Uri)
astBackground := feature.FindBackground(pickle.AstNodeIds[0])
astScenario := feature.FindScenario(pickle.AstNodeIds[0])
astStep := feature.FindStep(pickleStep.AstNodeIds[0])
@ -387,7 +394,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
f.printScenarioHeader(pickle, astScenario, maxLength-scenarioHeaderLength)
}
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStep.Id)
pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStep.Id)
text := s(f.indent*2) + pickleStepResult.Status.Color()(strings.TrimSpace(astStep.Keyword)) + " " + pickleStepResult.Status.Color()(pickleStep.Text)
if pickleStepResult.Def != nil {
text += s(maxLength - stepLength + 1)
@ -414,7 +421,7 @@ func (f *pretty) printStep(pickle *messages.Pickle, pickleStep *messages.PickleS
}
}
func (f *pretty) printDocString(docString *messages.DocString) {
func (f *Pretty) printDocString(docString *messages.DocString) {
var ct string
if len(docString.MediaType) > 0 {
@ -432,7 +439,7 @@ func (f *pretty) printDocString(docString *messages.DocString) {
// print table with aligned table cells
// @TODO: need to make example header cells bold
func (f *pretty) printTable(t *messages.PickleTable, c colors.ColorFunc) {
func (f *Pretty) printTable(t *messages.PickleTable, c colors.ColorFunc) {
maxColLengths := maxColLengths(t, c)
var cols = make([]string, len(t.Rows[0].Cells))
@ -512,7 +519,7 @@ func longestExampleRow(t *messages.Examples, clrs ...colors.ColorFunc) []int {
return longest
}
func (f *pretty) longestStep(steps []*messages.Step, pickleLength int) int {
func (f *Pretty) longestStep(steps []*messages.Step, pickleLength int) int {
max := pickleLength
for _, step := range steps {
@ -533,10 +540,10 @@ func line(path string, loc *messages.Location) string {
return " " + blackb(fmt.Sprintf("# %s:%d", path, loc.Line))
}
func (f *pretty) lengthPickleStep(keyword, text string) int {
func (f *Pretty) lengthPickleStep(keyword, text string) int {
return f.indent*2 + utf8.RuneCountInString(strings.TrimSpace(keyword)+" "+text)
}
func (f *pretty) lengthPickle(keyword, name string) int {
func (f *Pretty) lengthPickle(keyword, name string) int {
return f.indent + utf8.RuneCountInString(strings.TrimSpace(keyword)+": "+name)
}

Просмотреть файл

@ -16,42 +16,49 @@ func init() {
formatters.Format("progress", "Prints a character per step.", ProgressFormatterFunc)
}
// ProgressFormatterFunc implements the FormatterFunc for the progress formatter
// ProgressFormatterFunc implements the FormatterFunc for the progress formatter.
func ProgressFormatterFunc(suite string, out io.Writer) formatters.Formatter {
return NewProgress(suite, out)
}
// NewProgress creates a new progress formatter.
func NewProgress(suite string, out io.Writer) *Progress {
steps := 0
return &progress{
Basefmt: NewBaseFmt(suite, out),
stepsPerRow: 70,
steps: &steps,
return &Progress{
Base: NewBase(suite, out),
StepsPerRow: 70,
Steps: &steps,
}
}
type progress struct {
*Basefmt
stepsPerRow int
steps *int
// Progress is a minimalistic formatter.
type Progress struct {
*Base
StepsPerRow int
Steps *int
}
func (f *progress) Summary() {
left := math.Mod(float64(*f.steps), float64(f.stepsPerRow))
// Summary renders summary information.
func (f *Progress) Summary() {
left := math.Mod(float64(*f.Steps), float64(f.StepsPerRow))
if left != 0 {
if *f.steps > f.stepsPerRow {
fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", *f.steps))
if *f.Steps > f.StepsPerRow {
fmt.Fprintf(f.out, s(f.StepsPerRow-int(left))+fmt.Sprintf(" %d\n", *f.Steps))
} else {
fmt.Fprintf(f.out, " %d\n", *f.steps)
fmt.Fprintf(f.out, " %d\n", *f.Steps)
}
}
var failedStepsOutput []string
failedSteps := f.storage.MustGetPickleStepResultsByStatus(failed)
failedSteps := f.Storage.MustGetPickleStepResultsByStatus(failed)
sort.Sort(sortPickleStepResultsByPickleStepID(failedSteps))
for _, sr := range failedSteps {
if sr.Status == failed {
pickle := f.storage.MustGetPickle(sr.PickleID)
pickleStep := f.storage.MustGetPickleStep(sr.PickleStepID)
feature := f.storage.MustGetFeature(pickle.Uri)
pickle := f.Storage.MustGetPickle(sr.PickleID)
pickleStep := f.Storage.MustGetPickleStep(sr.PickleStepID)
feature := f.Storage.MustGetFeature(pickle.Uri)
sc := feature.FindScenario(pickle.AstNodeIds[0])
scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, pickle.Name)
@ -77,11 +84,11 @@ func (f *progress) Summary() {
}
fmt.Fprintln(f.out, "")
f.Basefmt.Summary()
f.Base.Summary()
}
func (f *progress) step(pickleStepID string) {
pickleStepResult := f.storage.MustGetPickleStepResult(pickleStepID)
func (f *Progress) step(pickleStepID string) {
pickleStepResult := f.Storage.MustGetPickleStepResult(pickleStepID)
switch pickleStepResult.Status {
case passed:
@ -96,54 +103,59 @@ func (f *progress) step(pickleStepID string) {
fmt.Fprint(f.out, yellow("P"))
}
*f.steps++
*f.Steps++
if math.Mod(float64(*f.steps), float64(f.stepsPerRow)) == 0 {
fmt.Fprintf(f.out, " %d\n", *f.steps)
if math.Mod(float64(*f.Steps), float64(f.StepsPerRow)) == 0 {
fmt.Fprintf(f.out, " %d\n", *f.Steps)
}
}
func (f *progress) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Passed(pickle, step, match)
// Passed captures passed step.
func (f *Progress) Passed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Passed(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(step.Id)
}
func (f *progress) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Skipped(pickle, step, match)
// Skipped captures skipped step.
func (f *Progress) Skipped(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Skipped(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(step.Id)
}
func (f *progress) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Undefined(pickle, step, match)
// Undefined captures undefined step.
func (f *Progress) Undefined(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Undefined(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(step.Id)
}
func (f *progress) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Basefmt.Failed(pickle, step, match, err)
// Failed captures failed step.
func (f *Progress) Failed(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition, err error) {
f.Base.Failed(pickle, step, match, err)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(step.Id)
}
func (f *progress) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Basefmt.Pending(pickle, step, match)
// Pending captures pending step.
func (f *Progress) Pending(pickle *messages.Pickle, step *messages.PickleStep, match *formatters.StepDefinition) {
f.Base.Pending(pickle, step, match)
f.lock.Lock()
defer f.lock.Unlock()
f.Lock.Lock()
defer f.Lock.Unlock()
f.step(step.Id)
}

Просмотреть файл

@ -14,6 +14,11 @@ Now `godog` is able to use multiple formatters simultaneously with comma-separat
`--format pretty,junit:report.xml,cucumber:report.json` will write `pretty` format to stdout, `junit` to report.xml
and `cucumber` to report.json.
### Extensible formatters
Standard formatters are now exported with type aliases so that a custom formatter can be built on top of it.
Please check [an example](../_examples/custom-formatter).
### Contextualized hooks
Scenario and Step hooks are now passing context to allow custom state communication. Returned context should generally

Просмотреть файл

@ -324,9 +324,9 @@ func (tc *godogFeaturesScenario) cleanupSnippet(snip string) string {
}
func (tc *godogFeaturesScenario) theUndefinedStepSnippetsShouldBe(body *DocString) error {
f, ok := tc.testedSuite.fmt.(*formatters.Basefmt)
f, ok := tc.testedSuite.fmt.(*formatters.Base)
if !ok {
return fmt.Errorf("this step requires *formatters.Basefmt, but there is: %T", tc.testedSuite.fmt)
return fmt.Errorf("this step requires *formatters.Base, but there is: %T", tc.testedSuite.fmt)
}
actual := tc.cleanupSnippet(f.Snippets())