From c6c2a0885b63bcef11433114f83dcc13ae887666 Mon Sep 17 00:00:00 2001 From: Levi Noecker <11619428+leviable@users.noreply.github.com> Date: Tue, 10 Aug 2021 04:27:17 -0500 Subject: [PATCH] Export internal formatters (#372) --- CHANGELOG.md | 11 +- _examples/custom-formatter/README.md | 19 +++ _examples/custom-formatter/custom.go | 32 ---- _examples/custom-formatter/emoji.go | 122 +++++++++++++++ .../custom-formatter/features/emoji.feature | 26 ++++ _examples/custom-formatter/go.mod | 7 +- _examples/custom-formatter/go.sum | 33 +--- _examples/custom-formatter/godogs.go | 6 + _examples/custom-formatter/godogs_test.go | 75 ++++++++++ .../imgs/emoji-output-example.png | Bin 0 -> 88693 bytes fmt.go | 48 ++++++ internal/formatters/fmt_base.go | 84 +++++------ internal/formatters/fmt_cucumber.go | 32 ++-- internal/formatters/fmt_events.go | 117 ++++++++------- internal/formatters/fmt_junit.go | 26 ++-- internal/formatters/fmt_pretty.go | 141 +++++++++--------- internal/formatters/fmt_progress.go | 100 +++++++------ release-notes/v0.12.0.md | 5 + suite_context_test.go | 4 +- 19 files changed, 584 insertions(+), 304 deletions(-) create mode 100644 _examples/custom-formatter/README.md delete mode 100644 _examples/custom-formatter/custom.go create mode 100644 _examples/custom-formatter/emoji.go create mode 100644 _examples/custom-formatter/features/emoji.feature create mode 100644 _examples/custom-formatter/godogs.go create mode 100644 _examples/custom-formatter/godogs_test.go create mode 100644 _examples/custom-formatter/imgs/emoji-output-example.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e985d..6d485f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/_examples/custom-formatter/README.md b/_examples/custom-formatter/README.md new file mode 100644 index 0000000..31fa7be --- /dev/null +++ b/_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) \ No newline at end of file diff --git a/_examples/custom-formatter/custom.go b/_examples/custom-formatter/custom.go deleted file mode 100644 index 9e364c3..0000000 --- a/_examples/custom-formatter/custom.go +++ /dev/null @@ -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() {} diff --git a/_examples/custom-formatter/emoji.go b/_examples/custom-formatter/emoji.go new file mode 100644 index 0000000..50cc5d5 --- /dev/null +++ b/_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) + } +} diff --git a/_examples/custom-formatter/features/emoji.feature b/_examples/custom-formatter/features/emoji.feature new file mode 100644 index 0000000..8b27e53 --- /dev/null +++ b/_examples/custom-formatter/features/emoji.feature @@ -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 diff --git a/_examples/custom-formatter/go.mod b/_examples/custom-formatter/go.mod index b46595f..5a88259 100644 --- a/_examples/custom-formatter/go.mod +++ b/_examples/custom-formatter/go.mod @@ -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 => ../.. diff --git a/_examples/custom-formatter/go.sum b/_examples/custom-formatter/go.sum index 57ee6fb..fd61262 100644 --- a/_examples/custom-formatter/go.sum +++ b/_examples/custom-formatter/go.sum @@ -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= diff --git a/_examples/custom-formatter/godogs.go b/_examples/custom-formatter/godogs.go new file mode 100644 index 0000000..e71f308 --- /dev/null +++ b/_examples/custom-formatter/godogs.go @@ -0,0 +1,6 @@ +package main + +// Godogs available to eat +var Godogs int + +func main() { /* usual main func */ } diff --git a/_examples/custom-formatter/godogs_test.go b/_examples/custom-formatter/godogs_test.go new file mode 100644 index 0000000..9523929 --- /dev/null +++ b/_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) +} diff --git a/_examples/custom-formatter/imgs/emoji-output-example.png b/_examples/custom-formatter/imgs/emoji-output-example.png new file mode 100644 index 0000000000000000000000000000000000000000..cf597e2d8b8942a4b83c0cdf24ce4f96456eb0ca GIT binary patch literal 88693 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 literal 0 HcmV?d00001 diff --git a/fmt.go b/fmt.go index 5934ddb..f30f9f8 100644 --- a/fmt.go +++ b/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 diff --git a/internal/formatters/fmt_base.go b/internal/formatters/fmt_base.go index 9088968..c95a97f 100644 --- a/internal/formatters/fmt_base.go +++ b/internal/formatters/fmt_base.go @@ -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 diff --git a/internal/formatters/fmt_cucumber.go b/internal/formatters/fmt_cucumber.go index 04b55ad..fee855a 100644 --- a/internal/formatters/fmt_cucumber.go +++ b/internal/formatters/fmt_cucumber.go @@ -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 diff --git a/internal/formatters/fmt_events.go b/internal/formatters/fmt_events.go index 955b653..879a3c2 100644 --- a/internal/formatters/fmt_events.go +++ b/internal/formatters/fmt_events.go @@ -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 diff --git a/internal/formatters/fmt_junit.go b/internal/formatters/fmt_junit.go index 8beab0d..bc6ed27 100644 --- a/internal/formatters/fmt_junit.go +++ b/internal/formatters/fmt_junit.go @@ -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: diff --git a/internal/formatters/fmt_pretty.go b/internal/formatters/fmt_pretty.go index ab0c3d0..0860dda 100644 --- a/internal/formatters/fmt_pretty.go +++ b/internal/formatters/fmt_pretty.go @@ -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) } diff --git a/internal/formatters/fmt_progress.go b/internal/formatters/fmt_progress.go index a869a02..86dbdf6 100644 --- a/internal/formatters/fmt_progress.go +++ b/internal/formatters/fmt_progress.go @@ -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) } diff --git a/release-notes/v0.12.0.md b/release-notes/v0.12.0.md index b25e3c3..6321ef2 100644 --- a/release-notes/v0.12.0.md +++ b/release-notes/v0.12.0.md @@ -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 diff --git a/suite_context_test.go b/suite_context_test.go index 57b65bf..f904fa8 100644 --- a/suite_context_test.go +++ b/suite_context_test.go @@ -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())