<fix>(PRETIFIER): Fix s method to not have errors on negative entry

Context:
While trying to create an helper library to manage http rest api testing, I made a system witch allow to pick value from responses, header, cookie, ... and inject then as variables.
Issue:
Doing this, when the inject variable make the line longer than the longest declared step, godog will failed to render test result under pretty formatting cause it will try to write a comment on a negative index
Fix:
Fix s methods so it will not goes to fatal when recieving negative number.
Этот коммит содержится в:
tfreville 2020-03-06 17:16:57 +01:00
родитель 2e1454719a
коммит eb75d692bd
10 изменённых файлов: 5771 добавлений и 5610 удалений

3
.gitignore предоставленный
Просмотреть файл

@ -4,4 +4,5 @@
Gopkg.lock
Gopkg.toml
.DS_Store
.DS_Store
.idea

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

@ -45,6 +45,7 @@ Feature: pretty formatter
No steps
0s
"""
Scenario: Support of Feature Plus Scenario Outline
Given a feature "features/simple.feature" file:
"""
@ -111,6 +112,7 @@ Feature: pretty formatter
No steps
0s
"""
Scenario: Support of Feature Plus Scenario With Steps
Given a feature "features/simple.feature" file:
"""
@ -146,6 +148,7 @@ Feature: pretty formatter
2 steps (1 passed, 1 failed)
0s
"""
Scenario: Support of Feature Plus Scenario Outline With Steps
Given a feature "features/simple.feature" file:
"""
@ -214,6 +217,7 @@ Feature: pretty formatter
No steps
0s
"""
Scenario: Support of Docstrings
Given a feature "features/simple.feature" file:
"""
@ -244,6 +248,7 @@ Feature: pretty formatter
1 steps (1 passed)
0s
"""
Scenario: Support of Undefined, Pending and Skipped status
Given a feature "features/simple.feature" file:
"""
@ -286,3 +291,30 @@ Feature: pretty formatter
s.Step(`^undefined$`, undefined)
}
"""
# Ensure s will not break when injecting data from BeforeStep
Scenario: Support data injection in BeforeStep
Given a feature "features/inject.feature" file:
"""
Feature: inject long value
Scenario: test scenario
Given Ignore I save some value X under key Y
When Ignore I use value {{Y}}
Then Ignore Godog rendering should not break
"""
And I allow variable injection
When I run feature suite with formatter "pretty"
Then the rendered output will be as follows:
"""
Feature: inject long value
Scenario: test scenario # features/inject.feature:3
Given Ignore I save some value X under key Y # suite_context.go:0 -> SuiteContext.func7
When Ignore I use value someverylonginjectionsoweacanbesureitsurpasstheinitiallongeststeplenghtanditwillhelptestsmethodsafety # suite_context.go:0 -> SuiteContext.func7
Then Ignore Godog rendering should not break # suite_context.go:0 -> SuiteContext.func7
1 scenarios (1 passed)
3 steps (3 passed)
0s
"""

Различия файлов не показаны, т.к. их слишком много Показать различия

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

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="succeed" tests="78" skipped="0" failures="0" errors="0" time="0">
<testsuites name="succeed" tests="79" skipped="0" failures="0" errors="0" time="0">
<testsuite name="JUnit XML formatter" tests="9" skipped="0" failures="0" errors="0" time="0">
<testcase name="Support of Feature Plus Scenario Node" status="passed" time="0"></testcase>
<testcase name="Support of Feature Plus Scenario Node With Tags" status="passed" time="0"></testcase>
@ -35,7 +35,7 @@
<testcase name="loaded feature should have a number of scenarios #3" status="passed" time="0"></testcase>
<testcase name="load a number of feature files" status="passed" time="0"></testcase>
</testsuite>
<testsuite name="pretty formatter" tests="9" skipped="0" failures="0" errors="0" time="0">
<testsuite name="pretty formatter" tests="10" skipped="0" failures="0" errors="0" time="0">
<testcase name="Support of Feature Plus Scenario Node" status="passed" time="0"></testcase>
<testcase name="Support of Feature Plus Scenario Node With Tags" status="passed" time="0"></testcase>
<testcase name="Support of Feature Plus Scenario Outline" status="passed" time="0"></testcase>
@ -45,6 +45,7 @@
<testcase name="Support of Comments" status="passed" time="0"></testcase>
<testcase name="Support of Docstrings" status="passed" time="0"></testcase>
<testcase name="Support of Undefined, Pending and Skipped status" status="passed" time="0"></testcase>
<testcase name="Support data injection in BeforeStep" status="passed" time="0"></testcase>
</testsuite>
<testsuite name="run background" tests="3" skipped="0" failures="0" errors="0" time="0">
<testcase name="should run background steps" status="passed" time="0"></testcase>

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

@ -2,9 +2,9 @@
...................................................................... 140
...................................................................... 210
...................................................................... 280
....................... 303
........................... 307
78 scenarios (78 passed)
303 steps (303 passed)
79 scenarios (79 passed)
307 steps (307 passed)
0s

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

@ -124,12 +124,13 @@ func (f *pretty) printUndefinedScenario(sc interface{}) {
case *gherkin.Scenario:
f.commentPos = f.longestStep(t.Steps, f.length(sc))
text := s(f.indent) + keywordAndName(t.Keyword, t.Name)
text += s(f.commentPos-f.length(t)+1) + f.line(t.Location)
text += s(f.commentPos-f.length(t)) + f.line(t.Location)
fmt.Fprintln(f.out, "\n"+text)
case *gherkin.ScenarioOutline:
f.commentPos = f.longestStep(t.Steps, f.length(sc))
text := s(f.indent) + keywordAndName(t.Keyword, t.Name)
text += s(f.commentPos-f.length(t)+1) + f.line(t.Location)
text += s(f.commentPos-f.length(t)) + f.line(t.Location)
fmt.Fprintln(f.out, "\n"+text)
for _, example := range t.Examples {
@ -248,9 +249,9 @@ func (f *pretty) printExampleRow(row *gherkin.TableRow, max []int, clr colors.Co
for i, cell := range row.Cells {
val := clr(cell.Value)
ln := utf8.RuneCountInString(val)
cells[i] = val + s(max[i]-ln)
cells[i] = val + s(max[i]-ln+1)
}
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |")
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, "| ")+"|")
}
func (f *pretty) printExampleHeader(example *gherkin.Examples, max []int) {
@ -262,9 +263,9 @@ func (f *pretty) printExampleHeader(example *gherkin.Examples, max []int) {
for i, cell := range example.TableHeader.Cells {
val := cyan(cell.Value)
ln := utf8.RuneCountInString(val)
cells[i] = val + s(max[i]-ln)
cells[i] = val + s(max[i]-ln+1)
}
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |")
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, "| ")+"|")
}
func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c colors.ColorFunc) {
@ -289,6 +290,7 @@ func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c colors.ColorFunc)
text += c(step.Text)
}
text += s(f.commentPos-f.length(step)+1) + blackb(fmt.Sprintf("# %s", def.definitionID()))
default:
text += c(step.Text)
}
@ -342,6 +344,7 @@ func (f *pretty) printStepKind(res *stepResult) {
}
text := s(f.indent) + keywordAndName(f.scenario.Keyword, f.scenario.Name)
text += s(f.commentPos-f.length(f.scenario)+1) + f.line(f.scenario.Location)
fmt.Fprintln(f.out, "\n"+text)
f.scenarioKeyword = true
}
@ -357,6 +360,7 @@ func (f *pretty) printStepKind(res *stepResult) {
}
text := s(f.indent) + keywordAndName(f.outline.Keyword, f.outline.Name)
text += s(f.commentPos-f.length(f.outline)+1) + f.line(f.outline.Location)
fmt.Fprintln(f.out, "\n"+text)
f.scenarioKeyword = true
}
@ -400,9 +404,9 @@ func (f *pretty) printTable(t *gherkin.DataTable, c colors.ColorFunc) {
for i, cell := range row.Cells {
val := c(cell.Value)
ln := utf8.RuneCountInString(val)
cols[i] = val + s(l[i]-ln)
cols[i] = val + s(l[i]-ln+1)
}
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cols, " | ")+" |")
fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cols, "| ")+"|")
}
}

4
go.mod
Просмотреть файл

@ -3,7 +3,7 @@ module github.com/cucumber/godog
go 1.13
require (
github.com/DATA-DOG/go-txdb v0.1.3
github.com/go-sql-driver/mysql v1.5.0
github.com/DATA-DOG/go-txdb v0.1.3 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/stretchr/testify v1.4.0
)

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

@ -251,14 +251,14 @@ func TestSucceedRun(t *testing.T) {
}
for _, tc := range testCases {
expectedOutput, err := ioutil.ReadFile(tc.filename)
expectedOutputNoConcurrency, err := ioutil.ReadFile(tc.filename)
require.NoError(t, err)
for concurrency := range make([]int, tc.concurrency) {
t.Run(
fmt.Sprintf("%s/concurrency/%d", tc.format, concurrency),
func(t *testing.T) {
testSucceedRun(t, tc.format, concurrency, string(expectedOutput))
testSucceedRun(t, tc.format, concurrency, string(expectedOutputNoConcurrency))
},
)
}

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

@ -16,6 +16,10 @@ import (
"github.com/cucumber/godog/gherkin"
)
var (
allowInjection = true
)
// SuiteContext provides steps for godog suite execution and
// can be used for meta-testing of godog features/steps themselves.
//
@ -43,6 +47,7 @@ func SuiteContext(s *Suite, additionalContextInitializers ...func(suite *Suite))
s.Step(`^I run feature suite$`, c.iRunFeatureSuite)
s.Step(`^I run feature suite with tags "([^"]*)"$`, c.iRunFeatureSuiteWithTags)
s.Step(`^I run feature suite with formatter "([^"]*)"$`, c.iRunFeatureSuiteWithFormatter)
s.Step(`^(?:I )(allow|disable) variable injection`, c.iSetVariableInjectionTo)
s.Step(`^(?:a )?feature "([^"]*)"(?: file)?:$`, c.aFeatureFile)
s.Step(`^the suite should have (passed|failed)$`, c.theSuiteShouldHave)
@ -97,6 +102,44 @@ func SuiteContext(s *Suite, additionalContextInitializers ...func(suite *Suite))
s.Step(`^(?:a )?failing nested multistep$`, func() Steps {
return Steps{"passing step", "passing multistep", "failing multistep"}
})
// Default recovery step
s.Step(`Ignore.*`, func() error {
return nil
})
s.BeforeStep(func(step *gherkin.Step) {
if !allowInjection {
return
}
step.Text = injectAll(step.Text)
args := step.Argument
if args != nil {
switch arg := args.(type) {
case *gherkin.DataTable:
for i := 0; i < len(arg.Rows); i++ {
for n, cell := range arg.Rows[i].Cells {
arg.Rows[i].Cells[n].Value = injectAll(cell.Value)
}
}
case *gherkin.DocString:
arg.Content = injectAll(arg.Content)
}
}
})
}
func injectAll(inTo string) string {
re := regexp.MustCompile(`{{[^{}]+}}`)
return re.ReplaceAllStringFunc(
inTo,
func(key string) string {
injectRegex := regexp.MustCompile(`^{{.+}}$`)
if injectRegex.MatchString(key) {
return "someverylonginjectionsoweacanbesureitsurpasstheinitiallongeststeplenghtanditwillhelptestsmethodsafety"
}
return key
},
)
}
type firedEvent struct {
@ -123,6 +166,11 @@ func (s *suiteContext) ResetBeforeEachScenario(interface{}) {
s.events = []*firedEvent{}
}
func (s *suiteContext) iSetVariableInjectionTo(to string) error {
allowInjection = to == "allow"
return nil
}
func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error {
if err := s.parseFeatures(); err != nil {
return err

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

@ -20,6 +20,9 @@ var (
// repeats a space n times
func s(n int) string {
if n <= 0 {
n = 1
}
return strings.Repeat(" ", n)
}