github.com/maps90/godog@v0.7.5-0.20170923143419-0093943021d4/fmt_progress_test.go (about) 1 package godog 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/DATA-DOG/godog/colors" 13 "github.com/DATA-DOG/godog/gherkin" 14 ) 15 16 func TestProgressFormatterOutput(t *testing.T) { 17 feat, err := gherkin.ParseFeature(strings.NewReader(sampleGherkinFeature)) 18 if err != nil { 19 t.Fatalf("unexpected error: %v", err) 20 } 21 22 var buf bytes.Buffer 23 w := colors.Uncolored(&buf) 24 r := runner{ 25 fmt: progressFunc("progress", w), 26 features: []*feature{&feature{ 27 Path: "any.feature", 28 Feature: feat, 29 Content: []byte(sampleGherkinFeature), 30 }}, 31 initializer: func(s *Suite) { 32 s.Step(`^passing$`, func() error { return nil }) 33 s.Step(`^failing$`, func() error { return fmt.Errorf("errored") }) 34 s.Step(`^pending$`, func() error { return ErrPending }) 35 }, 36 } 37 38 expected := ` 39 ...F-.P-.UU.....F..P..U 23 40 41 42 --- Failed steps: 43 44 When failing # any.feature:11 45 Error: errored 46 47 When failing # any.feature:24 48 Error: errored 49 50 51 8 scenarios (2 passed, 2 failed, 2 pending, 2 undefined) 52 23 steps (14 passed, 2 failed, 2 pending, 3 undefined, 2 skipped) 53 %s 54 55 Randomized with seed: %s 56 57 You can implement step definitions for undefined steps with these snippets: 58 59 func undefined() error { 60 return godog.ErrPending 61 } 62 63 func nextUndefined() error { 64 return godog.ErrPending 65 } 66 67 func FeatureContext(s *godog.Suite) { 68 s.Step(` + "`^undefined$`" + `, undefined) 69 s.Step(` + "`^next undefined$`" + `, nextUndefined) 70 }` 71 72 var zeroDuration time.Duration 73 expected = fmt.Sprintf(expected, zeroDuration.String(), os.Getenv("GODOG_SEED")) 74 expected = trimAllLines(expected) 75 76 r.run() 77 78 actual := trimAllLines(buf.String()) 79 80 shouldMatchOutput(expected, actual, t) 81 } 82 83 func trimAllLines(s string) string { 84 var lines []string 85 for _, ln := range strings.Split(strings.TrimSpace(s), "\n") { 86 lines = append(lines, strings.TrimSpace(ln)) 87 } 88 return strings.Join(lines, "\n") 89 } 90 91 var basicGherkinFeature = ` 92 Feature: basic 93 94 Scenario: passing scenario 95 When one 96 Then two 97 ` 98 99 func TestProgressFormatterWhenStepPanics(t *testing.T) { 100 feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) 101 if err != nil { 102 t.Fatalf("unexpected error: %v", err) 103 } 104 105 var buf bytes.Buffer 106 w := colors.Uncolored(&buf) 107 r := runner{ 108 fmt: progressFunc("progress", w), 109 features: []*feature{&feature{Feature: feat}}, 110 initializer: func(s *Suite) { 111 s.Step(`^one$`, func() error { return nil }) 112 s.Step(`^two$`, func() error { panic("omg") }) 113 }, 114 } 115 116 if !r.run() { 117 t.Fatal("the suite should have failed") 118 } 119 120 out := buf.String() 121 if idx := strings.Index(out, "github.com/DATA-DOG/godog/fmt_progress_test.go:116"); idx == -1 { 122 t.Fatalf("expected to find panic stacktrace, actual:\n%s", out) 123 } 124 } 125 126 func TestProgressFormatterWithPassingMultisteps(t *testing.T) { 127 feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) 128 if err != nil { 129 t.Fatalf("unexpected error: %v", err) 130 } 131 132 var buf bytes.Buffer 133 w := colors.Uncolored(&buf) 134 r := runner{ 135 fmt: progressFunc("progress", w), 136 features: []*feature{&feature{Feature: feat}}, 137 initializer: func(s *Suite) { 138 s.Step(`^sub1$`, func() error { return nil }) 139 s.Step(`^sub-sub$`, func() error { return nil }) 140 s.Step(`^sub2$`, func() Steps { return Steps{"sub-sub", "sub1", "one"} }) 141 s.Step(`^one$`, func() error { return nil }) 142 s.Step(`^two$`, func() Steps { return Steps{"sub1", "sub2"} }) 143 }, 144 } 145 146 if r.run() { 147 t.Fatal("the suite should have passed") 148 } 149 } 150 151 func TestProgressFormatterWithFailingMultisteps(t *testing.T) { 152 feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) 153 if err != nil { 154 t.Fatalf("unexpected error: %v", err) 155 } 156 157 var buf bytes.Buffer 158 w := colors.Uncolored(&buf) 159 r := runner{ 160 fmt: progressFunc("progress", w), 161 features: []*feature{&feature{Feature: feat, Path: "some.feature"}}, 162 initializer: func(s *Suite) { 163 s.Step(`^sub1$`, func() error { return nil }) 164 s.Step(`^sub-sub$`, func() error { return fmt.Errorf("errored") }) 165 s.Step(`^sub2$`, func() Steps { return Steps{"sub-sub", "sub1", "one"} }) 166 s.Step(`^one$`, func() error { return nil }) 167 s.Step(`^two$`, func() Steps { return Steps{"sub1", "sub2"} }) 168 }, 169 } 170 171 if !r.run() { 172 t.Fatal("the suite should have failed") 173 } 174 175 expected := ` 176 .F 2 177 178 179 --- Failed steps: 180 181 Then two # some.feature:6 182 Error: sub2: sub-sub: errored 183 184 185 1 scenarios (1 failed) 186 2 steps (1 passed, 1 failed) 187 %s 188 189 Randomized with seed: %s 190 ` 191 192 expected = trimAllLines(expected) 193 var zeroDuration time.Duration 194 expected = fmt.Sprintf(expected, zeroDuration.String(), os.Getenv("GODOG_SEED")) 195 actual := trimAllLines(buf.String()) 196 197 shouldMatchOutput(expected, actual, t) 198 } 199 200 func shouldMatchOutput(expected, actual string, t *testing.T) { 201 act := []byte(actual) 202 exp := []byte(expected) 203 204 if len(act) != len(exp) { 205 t.Fatalf("content lengths do not match, expected: %d, actual %d, actual output:\n%s", len(exp), len(act), actual) 206 } 207 208 for i := 0; i < len(exp); i++ { 209 if act[i] == exp[i] { 210 continue 211 } 212 213 cpe := make([]byte, len(exp)) 214 copy(cpe, exp) 215 e := append(exp[:i], '^') 216 e = append(e, cpe[i:]...) 217 218 cpa := make([]byte, len(act)) 219 copy(cpa, act) 220 a := append(act[:i], '^') 221 a = append(a, cpa[i:]...) 222 223 t.Fatalf("expected output does not match:\n%s\n\n%s", string(a), string(e)) 224 } 225 } 226 227 func TestProgressFormatterWithPanicInMultistep(t *testing.T) { 228 feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) 229 if err != nil { 230 t.Fatalf("unexpected error: %v", err) 231 } 232 233 var buf bytes.Buffer 234 w := colors.Uncolored(&buf) 235 r := runner{ 236 fmt: progressFunc("progress", w), 237 features: []*feature{&feature{Feature: feat}}, 238 initializer: func(s *Suite) { 239 s.Step(`^sub1$`, func() error { return nil }) 240 s.Step(`^sub-sub$`, func() error { return nil }) 241 s.Step(`^sub2$`, func() []string { return []string{"sub-sub", "sub1", "one"} }) 242 s.Step(`^one$`, func() error { return nil }) 243 s.Step(`^two$`, func() []string { return []string{"sub1", "sub2"} }) 244 }, 245 } 246 247 if !r.run() { 248 t.Fatal("the suite should have failed") 249 } 250 } 251 252 func TestProgressFormatterMultistepTemplates(t *testing.T) { 253 feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) 254 if err != nil { 255 t.Fatalf("unexpected error: %v", err) 256 } 257 258 var buf bytes.Buffer 259 w := colors.Uncolored(&buf) 260 r := runner{ 261 fmt: progressFunc("progress", w), 262 features: []*feature{&feature{Feature: feat}}, 263 initializer: func(s *Suite) { 264 s.Step(`^sub-sub$`, func() error { return nil }) 265 s.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} }) 266 s.Step(`^one$`, func() error { return nil }) 267 s.Step(`^(t)wo$`, func(s string) Steps { return Steps{"undef", "substep"} }) 268 }, 269 } 270 271 if r.run() { 272 t.Fatal("the suite should have passed") 273 } 274 275 expected := ` 276 .U 2 277 278 279 1 scenarios (1 undefined) 280 2 steps (1 passed, 1 undefined) 281 %s 282 283 Randomized with seed: %s 284 285 You can implement step definitions for undefined steps with these snippets: 286 287 func undef() error { 288 return godog.ErrPending 289 } 290 291 func unavailableCost(arg1 string, arg2 int) error { 292 return godog.ErrPending 293 } 294 295 func three() error { 296 return godog.ErrPending 297 } 298 299 func FeatureContext(s *godog.Suite) { 300 s.Step(` + "`^undef$`" + `, undef) 301 s.Step(` + "`^unavailable \"([^\"]*)\" cost (\\d+)$`" + `, unavailableCost) 302 s.Step(` + "`^three$`" + `, three) 303 } 304 ` 305 306 var zeroDuration time.Duration 307 expected = fmt.Sprintf(expected, zeroDuration.String(), os.Getenv("GODOG_SEED")) 308 expected = trimAllLines(expected) 309 310 actual := trimAllLines(buf.String()) 311 if actual != expected { 312 t.Fatalf("expected output does not match: %s", actual) 313 } 314 } 315 316 func TestProgressFormatterWhenMultiStepHasArgument(t *testing.T) { 317 318 var featureSource = ` 319 Feature: basic 320 321 Scenario: passing scenario 322 When one 323 Then two: 324 """ 325 text 326 """ 327 ` 328 feat, err := gherkin.ParseFeature(strings.NewReader(featureSource)) 329 if err != nil { 330 t.Fatalf("unexpected error: %v", err) 331 } 332 333 r := runner{ 334 fmt: progressFunc("progress", ioutil.Discard), 335 features: []*feature{&feature{Feature: feat}}, 336 initializer: func(s *Suite) { 337 s.Step(`^one$`, func() error { return nil }) 338 s.Step(`^two:$`, func(doc *gherkin.DocString) Steps { return Steps{"one"} }) 339 }, 340 } 341 342 if r.run() { 343 t.Fatal("the suite should have passed") 344 } 345 } 346 347 func TestProgressFormatterWhenMultiStepHasStepWithArgument(t *testing.T) { 348 349 var featureSource = ` 350 Feature: basic 351 352 Scenario: passing scenario 353 When one 354 Then two` 355 356 feat, err := gherkin.ParseFeature(strings.NewReader(featureSource)) 357 if err != nil { 358 t.Fatalf("unexpected error: %v", err) 359 } 360 361 var subStep = `three: 362 """ 363 content 364 """` 365 366 var buf bytes.Buffer 367 w := colors.Uncolored(&buf) 368 r := runner{ 369 fmt: progressFunc("progress", w), 370 features: []*feature{&feature{Feature: feat}}, 371 initializer: func(s *Suite) { 372 s.Step(`^one$`, func() error { return nil }) 373 s.Step(`^two$`, func() Steps { return Steps{subStep} }) 374 s.Step(`^three:$`, func(doc *gherkin.DocString) error { return nil }) 375 }, 376 } 377 378 if !r.run() { 379 t.Fatal("the suite should have failed") 380 } 381 382 expected := ` 383 .F 2 384 385 386 --- Failed steps: 387 388 Then two # :6 389 Error: nested steps cannot be multiline and have table or content body argument 390 391 392 1 scenarios (1 failed) 393 2 steps (1 passed, 1 failed) 394 %s 395 396 Randomized with seed: %s 397 ` 398 399 var zeroDuration time.Duration 400 expected = fmt.Sprintf(expected, zeroDuration.String(), os.Getenv("GODOG_SEED")) 401 expected = trimAllLines(expected) 402 403 actual := trimAllLines(buf.String()) 404 if actual != expected { 405 t.Fatalf("expected output does not match: %s", actual) 406 } 407 }