cucumber 0.4.4 → 0.4.5.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -1
- data/Caliper.yml +5 -0
- data/History.txt +24 -1
- data/Rakefile +1 -1
- data/VERSION.yml +2 -2
- data/cucumber.gemspec +18 -27
- data/cucumber.yml +10 -4
- data/examples/i18n/README.textile +1 -1
- data/examples/i18n/bg/features/consecutive_calculations.feature +1 -1
- data/examples/i18n/bg/features/division.feature +1 -1
- data/examples/i18n/en-lol/Rakefile +1 -3
- data/examples/i18n/en/features/division.feature +4 -4
- data/examples/i18n/et/features/jagamine.feature +1 -1
- data/examples/i18n/fr/features/addition2.feature +17 -0
- data/examples/i18n/fr/features/step_definitions/calculatrice_steps.rb +13 -0
- data/examples/i18n/ja/features/step_definitons/calculator_steps.rb +3 -3
- data/examples/i18n/lt/features/addition.feature +2 -2
- data/examples/i18n/lt/features/division.feature +1 -1
- data/examples/i18n/zh-CN/Rakefile +1 -3
- data/examples/i18n/zh-TW/Rakefile +1 -3
- data/examples/tcl/README.textile +11 -0
- data/examples/tcl/Rakefile +6 -0
- data/examples/tcl/features/fibonnacci.feature +17 -0
- data/examples/tcl/features/step_definitions/fib_steps.rb +7 -0
- data/examples/tcl/features/support/env.rb +6 -0
- data/examples/tcl/src/fib.tcl +3 -0
- data/examples/tickets/features/177/1.feature +3 -4
- data/examples/tickets/features/177/2.feature +2 -3
- data/examples/tickets/features/241.feature +2 -3
- data/examples/tickets/features/301/filter_background_tagged_hooks.feature +1 -1
- data/features/cucumber_cli.feature +4 -2
- data/features/custom_formatter.feature +32 -31
- data/features/html_formatter/a.html +319 -102
- data/features/language_help.feature +12 -14
- data/features/support/env.rb +1 -1
- data/features/transform.feature +17 -3
- data/gem_tasks/features.rake +3 -1
- data/lib/cucumber/ast/scenario_outline.rb +1 -1
- data/lib/cucumber/ast/step_invocation.rb +2 -2
- data/lib/cucumber/cli/configuration.rb +2 -17
- data/lib/cucumber/cli/language_help_formatter.rb +1 -1
- data/lib/cucumber/cli/options.rb +5 -4
- data/lib/cucumber/formatter/cucumber.css +198 -82
- data/lib/cucumber/formatter/cucumber.sass +173 -71
- data/lib/cucumber/formatter/html.rb +371 -195
- data/lib/cucumber/formatter/io.rb +33 -0
- data/lib/cucumber/formatter/junit.rb +16 -7
- data/lib/cucumber/formatter/pdf.rb +5 -5
- data/lib/cucumber/formatter/pretty.rb +6 -8
- data/lib/cucumber/formatter/progress.rb +4 -2
- data/lib/cucumber/formatter/rerun.rb +4 -2
- data/lib/cucumber/formatter/steps.rb +2 -2
- data/lib/cucumber/formatter/tag_cloud.rb +6 -2
- data/lib/cucumber/formatter/usage.rb +2 -2
- data/lib/cucumber/languages.yml +205 -281
- data/lib/cucumber/parser/feature.rb +348 -82
- data/lib/cucumber/parser/feature.tt +47 -11
- data/lib/cucumber/parser/i18n.tt +7 -17
- data/lib/cucumber/parser/natural_language.rb +45 -18
- data/lib/cucumber/rb_support/rb_transform.rb +1 -1
- data/lib/cucumber/step_mother.rb +2 -3
- data/lib/cucumber/webrat/element_locator.rb +9 -5
- data/spec/cucumber/cli/options_spec.rb +0 -3
- data/spec/cucumber/formatter/html_spec.rb +15 -2
- data/spec/cucumber/formatter/junit_spec.rb +3 -0
- data/spec/cucumber/treetop_parser/fit_scenario.feature +2 -2
- data/spec/cucumber/treetop_parser/given_scenario.feature +1 -1
- data/spec/cucumber/treetop_parser/multiple_tables.feature +2 -2
- data/spec/cucumber/treetop_parser/test_dos.feature +1 -1
- metadata +17 -26
- data/Manifest.txt +0 -0
- data/lib/cucumber/rails/action_controller.rb +0 -38
- data/lib/cucumber/rails/active_record.rb +0 -30
- data/lib/cucumber/rails/rspec.rb +0 -10
- data/lib/cucumber/rails/test_unit.rb +0 -9
- data/lib/cucumber/rails/world.rb +0 -30
- data/rails_generators/cucumber/USAGE +0 -11
- data/rails_generators/cucumber/cucumber_generator.rb +0 -117
- data/rails_generators/cucumber/templates/cucumber +0 -17
- data/rails_generators/cucumber/templates/cucumber.rake +0 -46
- data/rails_generators/cucumber/templates/cucumber_environment.rb +0 -30
- data/rails_generators/cucumber/templates/env.rb +0 -49
- data/rails_generators/cucumber/templates/paths.rb +0 -27
- data/rails_generators/cucumber/templates/spork_env.rb +0 -57
- data/rails_generators/cucumber/templates/version_check.rb +0 -31
- data/rails_generators/cucumber/templates/webrat_steps.rb +0 -241
- data/rails_generators/feature/USAGE +0 -12
- data/rails_generators/feature/feature_generator.rb +0 -40
- data/rails_generators/feature/templates/feature.erb +0 -31
- data/rails_generators/feature/templates/steps.erb +0 -14
@@ -1,49 +1,70 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
/* cucumber.css is generated from cucumber.sass */
|
2
|
+
/* Regenerate with rake sass */
|
3
3
|
|
4
|
-
!step_left
|
5
|
-
!step_bottom
|
4
|
+
!step_left = 5px "solid"
|
5
|
+
!step_bottom = 1px "solid"
|
6
6
|
|
7
|
-
!failed
|
8
|
-
!
|
9
|
-
!failed_text
|
7
|
+
!failed = #FFFBD3
|
8
|
+
!failed_border = #C20000
|
9
|
+
!failed_text = #C20000
|
10
10
|
|
11
|
-
!passed
|
12
|
-
!
|
13
|
-
!passed_text
|
11
|
+
!passed = #DBFFB4
|
12
|
+
!passed_border = #65C400
|
13
|
+
!passed_text = #3D7700
|
14
14
|
|
15
|
-
!skipped
|
16
|
-
!
|
17
|
-
!skipped_text
|
15
|
+
!skipped = #E0FFFF
|
16
|
+
!skipped_border = #00FFFF
|
17
|
+
!skipped_text = #001111
|
18
18
|
|
19
|
-
!pending
|
20
|
-
!
|
21
|
-
!pending_text
|
19
|
+
!pending = #FCFB98
|
20
|
+
!pending_border = #FAF834
|
21
|
+
!pending_text = #131313
|
22
22
|
|
23
|
-
!undefined
|
24
|
-
!
|
25
|
-
!undefined_text
|
23
|
+
!undefined = #FCFB98
|
24
|
+
!undefined_border = #FAF834
|
25
|
+
!undefined_text = #131313
|
26
26
|
|
27
|
-
|
28
|
-
:font-
|
29
|
-
:
|
27
|
+
body
|
28
|
+
:font-size 0px
|
29
|
+
:color #FFFFFF
|
30
|
+
:margin 0px
|
31
|
+
:padding 0px
|
32
|
+
|
33
|
+
.cucumber,td,th
|
34
|
+
:font normal 11px "Lucida Grande", Helvetica, sans-serif
|
30
35
|
:background white
|
31
|
-
:
|
36
|
+
:color #000000
|
32
37
|
|
33
|
-
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
38
|
+
#cucumber-header
|
39
|
+
:background #65C400
|
40
|
+
:color #FFFFFF
|
41
|
+
:height 5em
|
42
|
+
|
43
|
+
.scenario
|
44
|
+
h3
|
45
|
+
:font-size 11px
|
46
|
+
:padding 3px
|
47
|
+
:margin 0
|
48
|
+
:background #65C400
|
49
|
+
:color #FFFFFF
|
50
|
+
:font-weight bold
|
51
|
+
|
52
|
+
h1
|
53
|
+
:margin 0px 10px 0px 10px
|
54
|
+
:padding 10px
|
55
|
+
:font-family "Lucida Grande", Helvetica, sans-serif
|
56
|
+
:font-size 1.8em
|
57
|
+
:position absolute
|
58
|
+
|
59
|
+
h4
|
60
|
+
:margin-bottom 2px
|
37
61
|
|
38
62
|
div.feature
|
39
|
-
:border 1px solid
|
40
63
|
:padding 2px
|
41
|
-
:margin
|
64
|
+
:margin 0px 10px 5px 10px
|
42
65
|
|
43
|
-
|
44
|
-
|
45
|
-
div.examples
|
46
|
-
:padding 0em 0em 0em 1em
|
66
|
+
div.examples
|
67
|
+
:padding 0em 0em 0em 1em
|
47
68
|
|
48
69
|
.stats
|
49
70
|
:margin 2em
|
@@ -53,14 +74,19 @@
|
|
53
74
|
li
|
54
75
|
:display inline
|
55
76
|
|
77
|
+
.tag
|
78
|
+
:font-weight bold
|
79
|
+
:color #246AC1
|
80
|
+
|
56
81
|
.backtrace
|
57
82
|
:margin-top 0
|
58
83
|
:margin-bottom 0
|
59
84
|
:margin-left 1em
|
85
|
+
:color #000000
|
60
86
|
|
61
87
|
a
|
62
88
|
:text-decoration none
|
63
|
-
:color
|
89
|
+
:color #BE5C00
|
64
90
|
|
65
91
|
&:hover
|
66
92
|
:text-decoration underline
|
@@ -68,72 +94,148 @@
|
|
68
94
|
:font-weight normal
|
69
95
|
|
70
96
|
div.examples
|
71
|
-
:
|
72
|
-
:
|
73
|
-
|
97
|
+
:margin 5px 0px 5px 15px
|
98
|
+
:color #000000
|
99
|
+
|
100
|
+
.outline
|
101
|
+
table
|
102
|
+
:margin 0px 0px 5px 10px
|
74
103
|
|
75
104
|
table
|
76
105
|
:border-collapse collapse
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
:
|
106
|
+
td
|
107
|
+
:padding 3px 3px 3px 5px
|
108
|
+
td.failed, td.passed, td.skipped, td.pending, td.undefined
|
109
|
+
:padding-left 18px
|
110
|
+
:padding-right 10px
|
81
111
|
td.failed
|
82
|
-
:
|
83
|
-
:
|
112
|
+
:border-left= !step_left !failed_border
|
113
|
+
:border-bottom= !step_bottom !failed_border
|
114
|
+
:background= !failed
|
115
|
+
:color= !failed_text
|
84
116
|
td.passed
|
85
|
-
:
|
117
|
+
:border-left= !step_left !passed_border
|
118
|
+
:border-bottom= !step_bottom !passed_border
|
119
|
+
:background= !passed
|
86
120
|
:color= !passed_text
|
87
121
|
td.skipped
|
122
|
+
:border-left= !step_left !skipped_border
|
123
|
+
:border-bottom= !step_bottom !skipped_border
|
88
124
|
:background= !skipped
|
89
125
|
:color= !skipped_text
|
90
126
|
td.pending
|
91
|
-
:
|
92
|
-
:
|
127
|
+
:border-left= !step_left !pending_border
|
128
|
+
:border-bottom= !step_bottom !pending_border
|
129
|
+
:background= !pending
|
130
|
+
:color= !pending_text
|
93
131
|
td.undefined
|
94
|
-
:
|
95
|
-
:
|
132
|
+
:border-left= !step_left !undefined_border
|
133
|
+
:border-bottom= !step_bottom !undefined_border
|
134
|
+
:background= !undefined
|
135
|
+
:color= !undefined_text
|
96
136
|
|
97
137
|
ol
|
98
138
|
:list-style none
|
99
|
-
|
139
|
+
:margin 0px
|
140
|
+
:padding 0px
|
141
|
+
|
142
|
+
li.step
|
143
|
+
:padding 3px 3px 3px 18px
|
144
|
+
:margin 5px 0px 5px 5px
|
100
145
|
li
|
101
146
|
:margin 0em 0em 0em 1em
|
102
|
-
:padding 0em 0em 0em 0.2em
|
147
|
+
:padding 0em 0em 0em 0.2em
|
103
148
|
span.param
|
104
|
-
:font-weight bold
|
149
|
+
:font-weight bold
|
105
150
|
li.failed
|
106
|
-
:border-left= !step_left !
|
107
|
-
:border-bottom= !step_bottom !
|
151
|
+
:border-left= !step_left !failed_border
|
152
|
+
:border-bottom= !step_bottom !failed_border
|
108
153
|
:background= !failed
|
109
154
|
:color= !failed_text
|
110
|
-
span.param
|
111
|
-
:background !failed_dark
|
112
155
|
li.passed
|
113
|
-
:border-left= !step_left !
|
114
|
-
:border-bottom= !step_bottom !
|
156
|
+
:border-left= !step_left !passed_border
|
157
|
+
:border-bottom= !step_bottom !passed_border
|
115
158
|
:background= !passed
|
116
159
|
:color= !passed_text
|
117
|
-
span.param
|
118
|
-
:background= !passed_dark
|
119
160
|
li.skipped
|
120
|
-
:border-left= !step_left !
|
121
|
-
:border-bottom= !step_bottom !
|
161
|
+
:border-left= !step_left !skipped_border
|
162
|
+
:border-bottom= !step_bottom !skipped_border
|
122
163
|
:background= !skipped
|
123
164
|
:color= !skipped_text
|
124
|
-
span.param
|
125
|
-
:background= !skipped_dark
|
126
165
|
li.pending
|
127
|
-
:border-left= !step_left !
|
128
|
-
:border-bottom= !step_bottom !
|
166
|
+
:border-left= !step_left !pending_border
|
167
|
+
:border-bottom= !step_bottom !pending_border
|
129
168
|
:background= !pending
|
130
169
|
:color= !pending_text
|
131
|
-
span.param
|
132
|
-
:background= !pending_dark
|
133
170
|
li.undefined
|
134
|
-
:border-left= !step_left !
|
135
|
-
:border-bottom= !step_bottom !
|
171
|
+
:border-left= !step_left !undefined_border
|
172
|
+
:border-bottom= !step_bottom !undefined_border
|
136
173
|
:background= !undefined
|
137
174
|
:color= !undefined_text
|
138
|
-
|
139
|
-
|
175
|
+
|
176
|
+
#summary
|
177
|
+
:margin 0px
|
178
|
+
:padding 5px 10px
|
179
|
+
:text-align right
|
180
|
+
:top 0px
|
181
|
+
:right 0px
|
182
|
+
:float right
|
183
|
+
|
184
|
+
p
|
185
|
+
:margin 0 0 0 2px
|
186
|
+
|
187
|
+
#totals
|
188
|
+
:font-size 1.2em
|
189
|
+
|
190
|
+
.ruby
|
191
|
+
:font-size 12px
|
192
|
+
:font-family monospace
|
193
|
+
:color white
|
194
|
+
:background black
|
195
|
+
:padding 0.1em 0 0.2em 0
|
196
|
+
|
197
|
+
.keyword
|
198
|
+
:color #FF6600
|
199
|
+
.constant
|
200
|
+
:color #339999
|
201
|
+
.attribute
|
202
|
+
:color white
|
203
|
+
.global
|
204
|
+
:color white
|
205
|
+
.module
|
206
|
+
:color white
|
207
|
+
.class
|
208
|
+
:color white
|
209
|
+
.string
|
210
|
+
:color #66FF00
|
211
|
+
.ident
|
212
|
+
:color white
|
213
|
+
.method
|
214
|
+
:color #FFCC00
|
215
|
+
.number
|
216
|
+
:color white
|
217
|
+
.char
|
218
|
+
:color white
|
219
|
+
.comment
|
220
|
+
:color #9933CC
|
221
|
+
.symbol
|
222
|
+
:color white
|
223
|
+
.regex
|
224
|
+
:color #44B4CC
|
225
|
+
.punct
|
226
|
+
:color white
|
227
|
+
.escape
|
228
|
+
:color white
|
229
|
+
.interp
|
230
|
+
:color white
|
231
|
+
.expr
|
232
|
+
:color white
|
233
|
+
|
234
|
+
.offending
|
235
|
+
:background #333333
|
236
|
+
.linenum
|
237
|
+
:width 75px
|
238
|
+
:padding 0.1em 1em 0.2em 0
|
239
|
+
:color #000000
|
240
|
+
:background #FFFBD3
|
241
|
+
|
@@ -1,203 +1,201 @@
|
|
1
1
|
require 'cucumber/formatter/ordered_xml_markup'
|
2
2
|
require 'cucumber/formatter/duration'
|
3
|
-
require 'cucumber/formatter/
|
3
|
+
require 'cucumber/formatter/io'
|
4
4
|
|
5
5
|
module Cucumber
|
6
6
|
module Formatter
|
7
|
-
# The formatter used for <tt>--format html</tt>
|
8
7
|
class Html
|
9
8
|
include ERB::Util # for the #h method
|
10
9
|
include Duration
|
11
|
-
include
|
10
|
+
include Io
|
12
11
|
|
13
|
-
def initialize(step_mother,
|
14
|
-
@io =
|
12
|
+
def initialize(step_mother, path_or_io, options)
|
13
|
+
@io = ensure_io(path_or_io, "html")
|
14
|
+
@step_mother = step_mother
|
15
15
|
@options = options
|
16
16
|
@buffer = {}
|
17
|
-
@
|
18
|
-
@
|
17
|
+
@builder = create_builder(@io)
|
18
|
+
@feature_number = 0
|
19
|
+
@scenario_number = 0
|
20
|
+
@step_number = 0
|
21
|
+
@header_red = nil
|
19
22
|
end
|
20
|
-
|
23
|
+
|
21
24
|
def before_features(features)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def after_features(features)
|
26
|
-
stop_buffering :features
|
25
|
+
@step_count = get_step_count(features)
|
26
|
+
|
27
27
|
# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
28
|
-
builder.declare!(
|
28
|
+
@builder.declare!(
|
29
29
|
:DOCTYPE,
|
30
30
|
:html,
|
31
31
|
:PUBLIC,
|
32
|
-
'-//W3C//DTD XHTML 1.0 Strict//EN',
|
32
|
+
'-//W3C//DTD XHTML 1.0 Strict//EN',
|
33
33
|
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
|
34
34
|
)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
|
36
|
+
@builder << '<html xmlns ="http://www.w3.org/1999/xhtml">'
|
37
|
+
@builder.head do
|
38
|
+
@builder.meta(:content => 'text/html;charset=utf-8')
|
39
|
+
@builder.title 'Cucumber'
|
40
|
+
inline_css
|
41
|
+
inline_js
|
42
|
+
end
|
43
|
+
@builder << '<body>'
|
44
|
+
@builder << "<!-- Step count #{@step_count}-->"
|
45
|
+
@builder << '<div class="cucumber">'
|
46
|
+
@builder.div(:id => 'cucumber-header') do
|
47
|
+
@builder.div(:id => 'label') do
|
48
|
+
@builder.h1('Cucumber Features')
|
40
49
|
end
|
41
|
-
builder.
|
42
|
-
builder.
|
43
|
-
|
44
|
-
builder.div(scenario_summary(@step_mother) {|status_count, _| status_count}, :class => 'summary')
|
45
|
-
builder.div(step_summary(@step_mother) {|status_count, _| status_count}, :class => 'summary')
|
46
|
-
builder.div(format_duration(features.duration), :class => 'duration')
|
47
|
-
end
|
50
|
+
@builder.div(:id => 'summary') do
|
51
|
+
@builder.p('',:id => 'totals')
|
52
|
+
@builder.p('',:id => 'duration')
|
48
53
|
end
|
49
54
|
end
|
50
55
|
end
|
51
|
-
|
56
|
+
|
57
|
+
def after_features(features)
|
58
|
+
print_stats(features)
|
59
|
+
@builder << '</div>'
|
60
|
+
@builder << '</body>'
|
61
|
+
@builder << '</html>'
|
62
|
+
end
|
63
|
+
|
52
64
|
def before_feature(feature)
|
53
|
-
start_buffering :feature
|
54
65
|
@exceptions = []
|
66
|
+
@builder << '<div class="feature">'
|
55
67
|
end
|
56
|
-
|
68
|
+
|
57
69
|
def after_feature(feature)
|
58
|
-
|
59
|
-
builder.div(:class => 'feature') do
|
60
|
-
builder << buffer(:feature)
|
61
|
-
end
|
70
|
+
@builder << '</div>'
|
62
71
|
end
|
63
|
-
|
72
|
+
|
64
73
|
def before_comment(comment)
|
65
|
-
|
74
|
+
@builder << '<pre class="comment">'
|
66
75
|
end
|
67
76
|
|
68
77
|
def after_comment(comment)
|
69
|
-
|
70
|
-
builder.pre(:class => 'comment') do
|
71
|
-
builder << buffer(:comment)
|
72
|
-
end
|
78
|
+
@builder << '</pre>'
|
73
79
|
end
|
74
|
-
|
80
|
+
|
75
81
|
def comment_line(comment_line)
|
76
|
-
builder.text!(comment_line)
|
77
|
-
builder.br
|
82
|
+
@builder.text!(comment_line)
|
83
|
+
@builder.br
|
78
84
|
end
|
79
|
-
|
85
|
+
|
80
86
|
def after_tags(tags)
|
81
87
|
@tag_spacer = nil
|
82
88
|
end
|
83
|
-
|
89
|
+
|
84
90
|
def tag_name(tag_name)
|
85
|
-
builder.text!(@tag_spacer) if @tag_spacer
|
91
|
+
@builder.text!(@tag_spacer) if @tag_spacer
|
86
92
|
@tag_spacer = ' '
|
87
|
-
builder.span(tag_name, :class => 'tag')
|
93
|
+
@builder.span(tag_name, :class => 'tag')
|
88
94
|
end
|
89
|
-
|
95
|
+
|
90
96
|
def feature_name(name)
|
91
97
|
lines = name.split(/\r?\n/)
|
92
98
|
return if lines.empty?
|
93
|
-
builder.h2 do |h2|
|
94
|
-
builder.span(lines[0], :class => 'val')
|
99
|
+
@builder.h2 do |h2|
|
100
|
+
@builder.span(lines[0], :class => 'val')
|
95
101
|
end
|
96
|
-
builder.p(:class => 'narrative') do
|
102
|
+
@builder.p(:class => 'narrative') do
|
97
103
|
lines[1..-1].each do |line|
|
98
|
-
builder.text!(line.strip)
|
99
|
-
builder.br
|
104
|
+
@builder.text!(line.strip)
|
105
|
+
@builder.br
|
100
106
|
end
|
101
107
|
end
|
102
108
|
end
|
103
|
-
|
109
|
+
|
104
110
|
def before_background(background)
|
105
111
|
@in_background = true
|
106
|
-
|
112
|
+
@builder << '<div class="background">'
|
107
113
|
end
|
108
|
-
|
114
|
+
|
109
115
|
def after_background(background)
|
110
|
-
stop_buffering :background
|
111
116
|
@in_background = nil
|
112
|
-
builder
|
113
|
-
builder << buffer(:background)
|
114
|
-
end
|
117
|
+
@builder << '</div>'
|
115
118
|
end
|
116
|
-
|
119
|
+
|
117
120
|
def background_name(keyword, name, file_colon_line, source_indent)
|
118
121
|
@listing_background = true
|
119
|
-
builder.h3 do |h3|
|
120
|
-
builder.span(keyword, :class => 'keyword')
|
121
|
-
builder.text!(' ')
|
122
|
-
builder.span(name, :class => 'val')
|
122
|
+
@builder.h3 do |h3|
|
123
|
+
@builder.span(keyword, :class => 'keyword')
|
124
|
+
@builder.text!(' ')
|
125
|
+
@builder.span(name, :class => 'val')
|
123
126
|
end
|
124
127
|
end
|
125
128
|
|
126
129
|
def before_feature_element(feature_element)
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
def after_feature_element(feature_element)
|
131
|
-
stop_buffering :feature_element
|
130
|
+
@scenario_number+=1
|
131
|
+
@scenario_red = false
|
132
132
|
css_class = {
|
133
133
|
Ast::Scenario => 'scenario',
|
134
134
|
Ast::ScenarioOutline => 'scenario outline'
|
135
|
-
}[feature_element.class]
|
135
|
+
}[feature_element.class]
|
136
|
+
@builder << "<div class='#{css_class}'>"
|
137
|
+
end
|
136
138
|
|
137
|
-
|
138
|
-
|
139
|
-
end
|
139
|
+
def after_feature_element(feature_element)
|
140
|
+
@builder << '</div>'
|
140
141
|
@open_step_list = true
|
141
142
|
end
|
142
143
|
|
143
144
|
def scenario_name(keyword, name, file_colon_line, source_indent)
|
144
145
|
@listing_background = false
|
145
|
-
builder.h3 do
|
146
|
-
builder.span(keyword, :class => 'keyword')
|
147
|
-
builder.text!(' ')
|
148
|
-
builder.span(name, :class => 'val')
|
146
|
+
@builder.h3(:id => "scenario_#{@scenario_number}") do
|
147
|
+
@builder.span(keyword, :class => 'keyword')
|
148
|
+
@builder.text!(' ')
|
149
|
+
@builder.span(name, :class => 'val')
|
149
150
|
end
|
150
151
|
end
|
151
|
-
|
152
|
+
|
152
153
|
def before_outline_table(outline_table)
|
153
154
|
@outline_row = 0
|
154
|
-
|
155
|
+
@builder << '<table>'
|
155
156
|
end
|
156
|
-
|
157
|
+
|
157
158
|
def after_outline_table(outline_table)
|
158
|
-
|
159
|
-
builder.table do
|
160
|
-
builder << buffer(:outline_table)
|
161
|
-
end
|
159
|
+
@builder << '</table>'
|
162
160
|
@outline_row = nil
|
163
161
|
end
|
164
|
-
|
162
|
+
|
165
163
|
def before_examples(examples)
|
166
|
-
|
164
|
+
@builder << '<div class="examples">'
|
167
165
|
end
|
168
166
|
|
169
167
|
def after_examples(examples)
|
170
|
-
|
171
|
-
builder.div(:class => 'examples') do
|
172
|
-
builder << buffer(:examples)
|
173
|
-
end
|
168
|
+
@builder << '</div>'
|
174
169
|
end
|
175
170
|
|
176
171
|
def examples_name(keyword, name)
|
177
|
-
builder.h4 do
|
178
|
-
builder.span(keyword, :class => 'keyword')
|
179
|
-
builder.text!(' ')
|
180
|
-
builder.span(name, :class => 'val')
|
172
|
+
@builder.h4 do
|
173
|
+
@builder.span(keyword, :class => 'keyword')
|
174
|
+
@builder.text!(' ')
|
175
|
+
@builder.span(name, :class => 'val')
|
181
176
|
end
|
182
177
|
end
|
183
|
-
|
178
|
+
|
184
179
|
def before_steps(steps)
|
185
|
-
|
180
|
+
@builder << '<ol>'
|
186
181
|
end
|
187
|
-
|
182
|
+
|
188
183
|
def after_steps(steps)
|
189
|
-
|
190
|
-
builder.ol do
|
191
|
-
builder << buffer(:steps)
|
192
|
-
end
|
184
|
+
@builder << '</ol>'
|
193
185
|
end
|
194
|
-
|
186
|
+
|
195
187
|
def before_step(step)
|
196
188
|
@step_id = step.dom_id
|
189
|
+
@step_number += 1
|
190
|
+
@step = step
|
191
|
+
end
|
192
|
+
|
193
|
+
def after_step(step)
|
194
|
+
move_progress
|
197
195
|
end
|
198
196
|
|
199
197
|
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
200
|
-
|
198
|
+
@step_match = step_match
|
201
199
|
@hide_this_step = false
|
202
200
|
if exception
|
203
201
|
if @exceptions.include?(exception)
|
@@ -211,14 +209,21 @@ module Cucumber
|
|
211
209
|
return
|
212
210
|
end
|
213
211
|
@status = status
|
212
|
+
return if @hide_this_step
|
213
|
+
set_scenario_color(status)
|
214
|
+
@builder << "<li id='#{@step_id}' class='step #{status}'>"
|
214
215
|
end
|
215
|
-
|
216
|
+
|
216
217
|
def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
|
217
|
-
stop_buffering :step_result
|
218
218
|
return if @hide_this_step
|
219
|
-
|
220
|
-
|
219
|
+
# print snippet for undefined steps
|
220
|
+
if status == :undefined
|
221
|
+
step_multiline_class = @step.multiline_arg ? @step.multiline_arg.class : nil
|
222
|
+
@builder.pre do |pre|
|
223
|
+
pre << @step_mother.snippet_text(keyword,step_match.instance_variable_get("@name") || '',step_multiline_class)
|
224
|
+
end
|
221
225
|
end
|
226
|
+
@builder << '</li>'
|
222
227
|
end
|
223
228
|
|
224
229
|
def step_name(keyword, step_match, status, source_indent, background)
|
@@ -226,144 +231,315 @@ module Cucumber
|
|
226
231
|
background_in_scenario = background && !@listing_background
|
227
232
|
@skip_step = @step_matches.index(step_match) || background_in_scenario
|
228
233
|
@step_matches << step_match
|
229
|
-
|
234
|
+
|
230
235
|
unless @skip_step
|
231
236
|
build_step(keyword, step_match, status)
|
232
237
|
end
|
233
238
|
end
|
234
239
|
|
235
240
|
def exception(exception, status)
|
236
|
-
|
237
|
-
builder.pre(format_exception(exception), :class => status)
|
241
|
+
build_exception_detail(exception)
|
238
242
|
end
|
239
|
-
|
240
|
-
def
|
241
|
-
|
243
|
+
|
244
|
+
def extra_failure_content(file_colon_line)
|
245
|
+
@snippet_extractor ||= SnippetExtractor.new
|
246
|
+
"<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
|
242
247
|
end
|
243
248
|
|
249
|
+
def before_multiline_arg(multiline_arg)
|
250
|
+
return if @hide_this_step || @skip_step
|
251
|
+
if Ast::Table === multiline_arg
|
252
|
+
@builder << '<table>'
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
244
256
|
def after_multiline_arg(multiline_arg)
|
245
|
-
stop_buffering :multiline_arg
|
246
257
|
return if @hide_this_step || @skip_step
|
247
258
|
if Ast::Table === multiline_arg
|
248
|
-
builder
|
249
|
-
builder << buffer(:multiline_arg)
|
250
|
-
end
|
251
|
-
else
|
252
|
-
builder << buffer(:multiline_arg)
|
259
|
+
@builder << '</table>'
|
253
260
|
end
|
254
261
|
end
|
255
262
|
|
256
263
|
def py_string(string)
|
257
264
|
return if @hide_this_step
|
258
|
-
builder.pre(:class => 'val') do |pre|
|
259
|
-
builder << string.gsub("\n", '
')
|
265
|
+
@builder.pre(:class => 'val') do |pre|
|
266
|
+
@builder << string.gsub("\n", '
')
|
260
267
|
end
|
261
268
|
end
|
262
|
-
|
269
|
+
|
270
|
+
|
263
271
|
def before_table_row(table_row)
|
264
272
|
@row_id = table_row.dom_id
|
265
273
|
@col_index = 0
|
266
|
-
|
274
|
+
return if @hide_this_step
|
275
|
+
@builder << "<tr class='step' id='#{@row_id}'>"
|
267
276
|
end
|
268
|
-
|
277
|
+
|
269
278
|
def after_table_row(table_row)
|
270
|
-
stop_buffering :table_row
|
271
279
|
return if @hide_this_step
|
272
|
-
builder
|
273
|
-
builder << buffer(:table_row)
|
274
|
-
end
|
280
|
+
@builder << '</tr>'
|
275
281
|
if table_row.exception
|
276
|
-
builder.tr do
|
277
|
-
builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
278
|
-
builder.pre do |pre|
|
282
|
+
@builder.tr do
|
283
|
+
@builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
284
|
+
@builder.pre do |pre|
|
279
285
|
pre << format_exception(table_row.exception)
|
280
286
|
end
|
281
287
|
end
|
282
288
|
end
|
283
289
|
end
|
284
|
-
|
290
|
+
if @outline_row
|
291
|
+
@outline_row += 1
|
292
|
+
end
|
293
|
+
@step_number += 1
|
294
|
+
move_progress
|
285
295
|
end
|
286
296
|
|
287
297
|
def table_cell_value(value, status)
|
288
298
|
return if @hide_this_step
|
289
299
|
|
290
|
-
cell_type = @outline_row == 0 ? :th : :td
|
291
|
-
attributes = {:id => "#{@row_id}_#{@col_index}", :class => '
|
300
|
+
@cell_type = @outline_row == 0 ? :th : :td
|
301
|
+
attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
|
292
302
|
attributes[:class] += " #{status}" if status
|
293
|
-
build_cell(cell_type, value, attributes)
|
303
|
+
build_cell(@cell_type, value, attributes)
|
304
|
+
set_scenario_color(status)
|
294
305
|
@col_index += 1
|
295
306
|
end
|
296
307
|
|
297
308
|
def announce(announcement)
|
298
|
-
builder.pre(announcement, :class => 'announcement')
|
309
|
+
@builder.pre(announcement, :class => 'announcement')
|
299
310
|
end
|
300
311
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
312
|
+
protected
|
313
|
+
|
314
|
+
def build_exception_detail(exception)
|
315
|
+
backtrace = Array.new
|
316
|
+
@builder.div(:class => 'message') do
|
317
|
+
message = exception.message
|
318
|
+
if message.include?('Exception caught')
|
319
|
+
matches = message.match(/Showing <i>(.+)<\/i>(?:.+)#(\d+)/)
|
320
|
+
backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"]
|
321
|
+
message = message.match(/<code>([^(\/)]+)<\//m)[1]
|
322
|
+
end
|
323
|
+
@builder << "<pre>#{message}</pre>"
|
324
|
+
end
|
325
|
+
@builder.div(:class => 'backtrace') do
|
326
|
+
@builder.pre do
|
327
|
+
# backtrace += (exception.backtrace.size == 1 || exception.backtrace[0].include?('(eval):')) ? ["#{RAILS_ROOT}/#{@step_match.file_colon_line}"] + exception.backtrace : exception.backtrace
|
328
|
+
backtrace = exception.backtrace
|
329
|
+
backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
|
330
|
+
@builder << backtrace_line(backtrace.join("\n"))
|
331
|
+
end
|
332
|
+
end
|
333
|
+
extra = extra_failure_content(backtrace)
|
334
|
+
@builder << extra unless extra == ""
|
305
335
|
end
|
306
|
-
end
|
307
336
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
337
|
+
def set_scenario_color(status)
|
338
|
+
if status == :undefined
|
339
|
+
@builder.script do
|
340
|
+
@builder.text!("makeYellow('cucumber-header');") unless @header_red
|
341
|
+
@builder.text!("makeYellow('scenario_#{@scenario_number}');") unless @scenario_red
|
342
|
+
end
|
343
|
+
end
|
344
|
+
if status == :failed
|
345
|
+
@builder.script do
|
346
|
+
@builder.text!("makeRed('cucumber-header');") unless @header_red
|
347
|
+
@header_red = true
|
348
|
+
@builder.text!("makeRed('scenario_#{@scenario_number}');") unless @scenario_red
|
349
|
+
@scenario_red = true
|
350
|
+
end
|
351
|
+
end
|
315
352
|
end
|
316
|
-
end
|
317
353
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
354
|
+
def get_step_count(features)
|
355
|
+
count = 0
|
356
|
+
features = features.instance_variable_get("@features")
|
357
|
+
features.each do |feature|
|
358
|
+
#get background steps
|
359
|
+
if feature.instance_variable_get("@background")
|
360
|
+
background = feature.instance_variable_get("@background").instance_variable_get("@steps").instance_variable_get("@steps")
|
361
|
+
count += background.size
|
362
|
+
end
|
363
|
+
#get scenarios
|
364
|
+
feature.instance_variable_get("@feature_elements").each do |scenario|
|
365
|
+
#get steps
|
366
|
+
steps = scenario.instance_variable_get("@steps").instance_variable_get("@steps")
|
367
|
+
count += steps.size
|
368
|
+
|
369
|
+
#get example table
|
370
|
+
examples = scenario.instance_variable_get("@examples_array")
|
371
|
+
unless examples.nil?
|
372
|
+
examples.each do |example|
|
373
|
+
example_matrix = example.instance_variable_get("@outline_table").instance_variable_get("@cell_matrix")
|
374
|
+
count += example_matrix.size
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
#get multiline step tables
|
379
|
+
steps.each do |step|
|
380
|
+
multi_arg = step.instance_variable_get("@multiline_arg")
|
381
|
+
next if multi_arg.nil?
|
382
|
+
matrix = multi_arg.instance_variable_get("@cell_matrix")
|
383
|
+
count += matrix.size unless matrix.nil?
|
384
|
+
end
|
385
|
+
end
|
325
386
|
end
|
387
|
+
return count
|
326
388
|
end
|
327
|
-
end
|
328
389
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
390
|
+
def build_step(keyword, step_match, status)
|
391
|
+
step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
|
392
|
+
@builder.div do |div|
|
393
|
+
@builder.span(keyword, :class => 'keyword')
|
394
|
+
@builder.text!(' ')
|
395
|
+
@builder.span(:class => 'step val') do |name|
|
396
|
+
name << h(step_name).gsub(/<span class="(.*?)">/, '<span class="\1">').gsub(/<\/span>/, '</span>')
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def build_cell(cell_type, value, attributes)
|
402
|
+
@builder.__send__(cell_type, attributes) do
|
403
|
+
@builder.div do
|
404
|
+
@builder.span(value,:class => 'step param')
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def inline_css
|
410
|
+
@builder.style(:type => 'text/css') do
|
411
|
+
@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def inline_js
|
416
|
+
@builder.script(:type => 'text/javascript') do
|
417
|
+
@builder << inline_js_content
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
def inline_js_content
|
422
|
+
<<-EOF
|
423
|
+
function moveProgressBar(percentDone) {
|
424
|
+
document.getElementById("cucumber-header").style.width = percentDone +"%";
|
425
|
+
}
|
426
|
+
function makeRed(element_id) {
|
427
|
+
document.getElementById(element_id).style.background = '#C40D0D';
|
428
|
+
document.getElementById(element_id).style.color = '#FFFFFF';
|
429
|
+
}
|
430
|
+
|
431
|
+
function makeYellow(element_id) {
|
432
|
+
document.getElementById(element_id).style.background = '#FAF834';
|
433
|
+
document.getElementById(element_id).style.color = '#000000';
|
434
|
+
}
|
435
|
+
EOF
|
436
|
+
end
|
437
|
+
|
438
|
+
def move_progress
|
439
|
+
@builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
|
440
|
+
end
|
441
|
+
|
442
|
+
def percent_done
|
443
|
+
result = 100.0
|
444
|
+
if @step_count != 0
|
445
|
+
result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
|
446
|
+
end
|
447
|
+
result
|
448
|
+
end
|
449
|
+
|
450
|
+
def format_exception(exception)
|
451
|
+
(["#{exception.message}"] + exception.backtrace).join("\n")
|
452
|
+
end
|
453
|
+
|
454
|
+
def backtrace_line(line)
|
455
|
+
line.gsub(/^([^:]*\.(?:rb|feature|haml)):(\d*)/) do
|
456
|
+
if ENV['TM_PROJECT_DIRECTORY']
|
457
|
+
"<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
|
458
|
+
else
|
459
|
+
line
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def print_stats(features)
|
465
|
+
@builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
|
466
|
+
@builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
|
467
|
+
end
|
468
|
+
|
469
|
+
def print_stat_string(features)
|
470
|
+
string = String.new
|
471
|
+
string << dump_count(@step_mother.scenarios.length, "scenario")
|
472
|
+
scenario_count = print_status_counts{|status| @step_mother.scenarios(status)}
|
473
|
+
string << scenario_count if scenario_count
|
474
|
+
string << "<br />"
|
475
|
+
string << dump_count(@step_mother.steps.length, "step")
|
476
|
+
step_count = print_status_counts{|status| @step_mother.steps(status)}
|
477
|
+
string << step_count if step_count
|
478
|
+
end
|
479
|
+
|
480
|
+
def print_status_counts
|
481
|
+
counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
|
482
|
+
elements = yield status
|
483
|
+
elements.any? ? "#{elements.length} #{status.to_s}" : nil
|
484
|
+
end.compact
|
485
|
+
return " (#{counts.join(', ')})" if counts.any?
|
486
|
+
end
|
487
|
+
|
488
|
+
def dump_count(count, what, state=nil)
|
489
|
+
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
|
490
|
+
end
|
491
|
+
|
492
|
+
def create_builder(io)
|
493
|
+
OrderedXmlMarkup.new(:target => io, :indent => 0)
|
336
494
|
end
|
337
|
-
end
|
338
|
-
|
339
|
-
def format_exception(exception)
|
340
|
-
h((["#{exception.message} (#{exception.class})"] + exception.backtrace).join("\n"))
|
341
|
-
end
|
342
|
-
|
343
|
-
def builder
|
344
|
-
@current_builder
|
345
|
-
end
|
346
|
-
|
347
|
-
def buffer(label)
|
348
|
-
result = @buffer[label]
|
349
|
-
@buffer[label] = ''
|
350
|
-
result
|
351
|
-
end
|
352
|
-
|
353
|
-
def start_buffering(label)
|
354
|
-
@buffer[label] ||= ''
|
355
|
-
@parent_builder ||= {}
|
356
|
-
@parent_builder[label] = @current_builder
|
357
|
-
@current_builder = create_builder(@buffer[label])
|
358
|
-
end
|
359
|
-
|
360
|
-
def stop_buffering(label)
|
361
|
-
@current_builder = @parent_builder[label]
|
362
|
-
end
|
363
|
-
|
364
|
-
def create_builder(io)
|
365
|
-
OrderedXmlMarkup.new(:target => io, :indent => 0)
|
366
|
-
end
|
367
495
|
end
|
368
496
|
end
|
369
497
|
end
|
498
|
+
|
499
|
+
|
500
|
+
class SnippetExtractor #:nodoc:
|
501
|
+
class NullConverter; def convert(code, pre); code; end; end #:nodoc:
|
502
|
+
begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
|
503
|
+
|
504
|
+
def snippet(error)
|
505
|
+
raw_code, line = snippet_for(error[0])
|
506
|
+
highlighted = @@converter.convert(raw_code, false)
|
507
|
+
highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
|
508
|
+
post_process(highlighted, line)
|
509
|
+
end
|
510
|
+
|
511
|
+
def snippet_for(error_line)
|
512
|
+
if error_line =~ /(.*):(\d+)/
|
513
|
+
file = $1
|
514
|
+
line = $2.to_i
|
515
|
+
[lines_around(file, line), line]
|
516
|
+
else
|
517
|
+
return snippet_for()
|
518
|
+
["# Couldn't get snippet for #{error_line}", 1]
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def lines_around(file, line)
|
523
|
+
if File.file?(file)
|
524
|
+
lines = File.open(file).read.split("\n")
|
525
|
+
min = [0, line-3].max
|
526
|
+
max = [line+1, lines.length-1].min
|
527
|
+
selected_lines = []
|
528
|
+
selected_lines.join("\n")
|
529
|
+
lines[min..max].join("\n")
|
530
|
+
else
|
531
|
+
"# Couldn't get snippet for #{file}"
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
def post_process(highlighted, offending_line)
|
536
|
+
new_lines = []
|
537
|
+
highlighted.split("\n").each_with_index do |line, i|
|
538
|
+
new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
|
539
|
+
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
|
540
|
+
new_lines << new_line
|
541
|
+
end
|
542
|
+
new_lines.join("\n")
|
543
|
+
end
|
544
|
+
|
545
|
+
end
|