1package godog
2
3import (
4	"fmt"
5	"io"
6	"math"
7	"strings"
8
9	"github.com/cucumber/messages-go/v10"
10)
11
12func init() {
13	Format("progress", "Prints a character per step.", progressFunc)
14}
15
16func progressFunc(suite string, out io.Writer) Formatter {
17	steps := 0
18	return &progress{
19		basefmt:     newBaseFmt(suite, out),
20		stepsPerRow: 70,
21		steps:       &steps,
22	}
23}
24
25type progress struct {
26	*basefmt
27	stepsPerRow int
28	steps       *int
29}
30
31func (f *progress) Summary() {
32	left := math.Mod(float64(*f.steps), float64(f.stepsPerRow))
33	if left != 0 {
34		if *f.steps > f.stepsPerRow {
35			fmt.Fprintf(f.out, s(f.stepsPerRow-int(left))+fmt.Sprintf(" %d\n", *f.steps))
36		} else {
37			fmt.Fprintf(f.out, " %d\n", *f.steps)
38		}
39	}
40
41	var failedStepsOutput []string
42	for _, sr := range f.findStepResults(failed) {
43		if sr.status == failed {
44			sc := f.findScenario(sr.owner.AstNodeIds[0])
45			scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, sr.owner.Name)
46			scenarioLine := fmt.Sprintf("%s:%d", sr.owner.Uri, sc.Location.Line)
47
48			step := f.findStep(sr.step.AstNodeIds[0])
49			stepDesc := strings.TrimSpace(step.Keyword) + " " + sr.step.Text
50			stepLine := fmt.Sprintf("%s:%d", sr.owner.Uri, step.Location.Line)
51
52			failedStepsOutput = append(
53				failedStepsOutput,
54				s(2)+red(scenarioDesc)+blackb(" # "+scenarioLine),
55				s(4)+red(stepDesc)+blackb(" # "+stepLine),
56				s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", sr.err)),
57				"",
58			)
59		}
60	}
61
62	if len(failedStepsOutput) > 0 {
63		fmt.Fprintln(f.out, "\n\n--- "+red("Failed steps:")+"\n")
64		fmt.Fprint(f.out, strings.Join(failedStepsOutput, "\n"))
65	}
66	fmt.Fprintln(f.out, "")
67
68	f.basefmt.Summary()
69}
70
71func (f *progress) step(res *stepResult) {
72	switch res.status {
73	case passed:
74		fmt.Fprint(f.out, green("."))
75	case skipped:
76		fmt.Fprint(f.out, cyan("-"))
77	case failed:
78		fmt.Fprint(f.out, red("F"))
79	case undefined:
80		fmt.Fprint(f.out, yellow("U"))
81	case pending:
82		fmt.Fprint(f.out, yellow("P"))
83	}
84
85	*f.steps++
86
87	if math.Mod(float64(*f.steps), float64(f.stepsPerRow)) == 0 {
88		fmt.Fprintf(f.out, " %d\n", *f.steps)
89	}
90}
91
92func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
93	f.basefmt.Passed(pickle, step, match)
94
95	f.lock.Lock()
96	defer f.lock.Unlock()
97
98	f.step(f.lastStepResult())
99}
100
101func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
102	f.basefmt.Skipped(pickle, step, match)
103
104	f.lock.Lock()
105	defer f.lock.Unlock()
106
107	f.step(f.lastStepResult())
108}
109
110func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
111	f.basefmt.Undefined(pickle, step, match)
112
113	f.lock.Lock()
114	defer f.lock.Unlock()
115
116	f.step(f.lastStepResult())
117}
118
119func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) {
120	f.basefmt.Failed(pickle, step, match, err)
121
122	f.lock.Lock()
123	defer f.lock.Unlock()
124
125	f.step(f.lastStepResult())
126}
127
128func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) {
129	f.basefmt.Pending(pickle, step, match)
130
131	f.lock.Lock()
132	defer f.lock.Unlock()
133
134	f.step(f.lastStepResult())
135}
136
137func (f *progress) Sync(cf ConcurrentFormatter) {
138	if source, ok := cf.(*progress); ok {
139		f.basefmt.Sync(source.basefmt)
140		f.steps = source.steps
141	}
142}
143
144func (f *progress) Copy(cf ConcurrentFormatter) {
145	if source, ok := cf.(*progress); ok {
146		f.basefmt.Copy(source.basefmt)
147	}
148}
149