cucumber 0.4.4 → 0.4.5.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|