daddy 0.3.36 → 0.3.37
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.
- checksums.yaml +4 -4
- data/lib/daddy/cucumber.rb +0 -7
- data/lib/daddy/cucumber/helpers.rb +13 -0
- data/lib/daddy/version.rb +1 -1
- data/lib/tasks/test.rake +0 -1
- metadata +4 -27
- data/lib/daddy/cucumber/helpers/capture.rb +0 -67
- data/lib/daddy/formatter/cucumber.css +0 -285
- data/lib/daddy/formatter/daddy.css +0 -214
- data/lib/daddy/formatter/daddy.js +0 -19
- data/lib/daddy/formatter/daddy_html.rb +0 -87
- data/lib/daddy/formatter/html.rb +0 -720
- data/lib/daddy/formatter/jquery-min.js +0 -4
- data/lib/daddy/formatter/menu.html.erb +0 -49
- data/lib/daddy/formatter/screenshot.js +0 -38
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
#menu {
|
|
2
|
-
float: left;
|
|
3
|
-
background: #FFFFFF;
|
|
4
|
-
color: #000000;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
#menu ol {
|
|
8
|
-
margin: 0;
|
|
9
|
-
padding: 0;
|
|
10
|
-
border-left: 5px solid #65c400;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
#menu ol li.master,
|
|
14
|
-
#menu ol li.sprint {
|
|
15
|
-
margin: 0;
|
|
16
|
-
padding: 0 5px;
|
|
17
|
-
border-bottom: 1px solid #65c400;
|
|
18
|
-
line-height: 40px;
|
|
19
|
-
vertical-align: middle;
|
|
20
|
-
font-size: 1.2em;
|
|
21
|
-
font-weight: bold;
|
|
22
|
-
white-space: nowrap;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
#menu ol li.sub_menu {
|
|
26
|
-
margin: 0;
|
|
27
|
-
padding: 0 5px;
|
|
28
|
-
border-bottom: 1px solid #65c400;
|
|
29
|
-
line-height: 30px;
|
|
30
|
-
text-align: right;
|
|
31
|
-
vertical-align: middle;
|
|
32
|
-
font-size: 1.2em;
|
|
33
|
-
font-weight: bold;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.cucumber .scenario h3, td .scenario h3, th .scenario h3, .background h3 {
|
|
37
|
-
font-size: 1.2em;
|
|
38
|
-
margin: 0;
|
|
39
|
-
background: #FFFFFF;
|
|
40
|
-
color: #000000;
|
|
41
|
-
font-weight: bold;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.cucumber ol li.step, td ol li.step, th ol li.step {
|
|
45
|
-
padding: 3px 3px 3px 10px;
|
|
46
|
-
margin: 5px 0px 5px 1.2em;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.cucumber ol li.passed, td ol li.passed, th ol li.passed {
|
|
50
|
-
border-left: 5px solid #999999;
|
|
51
|
-
border-bottom: 1px solid #999999;
|
|
52
|
-
background: #FFFFFF;
|
|
53
|
-
color: #000000;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.cucumber ol li.expand:hover, td ol li.expand:hover, th ol li.expand:hover {
|
|
57
|
-
background: #F0F8FF;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.cucumber ol li.message, td ol li.message, th ol li.message {
|
|
61
|
-
border-left: 5px solid #999999;
|
|
62
|
-
border-bottom: 1px solid #999999;
|
|
63
|
-
background: #F0F8FF;
|
|
64
|
-
color: #001111;
|
|
65
|
-
margin: 5px 0px 5px 1.2em;
|
|
66
|
-
padding: 3px 15px 3px 10px;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.feature_dir {
|
|
70
|
-
font-size: 1.2em;
|
|
71
|
-
font-weight: bold;
|
|
72
|
-
padding: 2px;
|
|
73
|
-
margin: 10px 10px 0px 10px;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
div.feature_dir span {
|
|
77
|
-
cursor: pointer;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
div.feature h2 {
|
|
81
|
-
font-size: 1.2em;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.step_contents {
|
|
85
|
-
display: none;
|
|
86
|
-
background: #FFFFFF;
|
|
87
|
-
border: solid 1px #999999;
|
|
88
|
-
margin-left: 1.2em;
|
|
89
|
-
padding: 1em;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
div.background {
|
|
93
|
-
margin-left: 20px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
div.background ol {
|
|
97
|
-
margin-left: 20px;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
div.contents {
|
|
101
|
-
padding-top: 10px;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
div.feature .narrative {
|
|
105
|
-
margin: 1.2em;
|
|
106
|
-
line-height: 1.5em;
|
|
107
|
-
font-size: 1.2em;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
div.feature .narrative > div {
|
|
111
|
-
margin-left: 10px;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
div.feature .narrative > pre {
|
|
115
|
-
border: solid 1px #999999;
|
|
116
|
-
border-radius: 5px;
|
|
117
|
-
padding: 1em 0;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
div.scenario {
|
|
121
|
-
margin: 10px 10px 10px 1.2em;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
div.scenario.faild h3,
|
|
125
|
-
div.scenario.faild .scenario_file {
|
|
126
|
-
background: #C40D0D;
|
|
127
|
-
color: #FFFFFF;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
div.scenario.pending h3,
|
|
131
|
-
div.scenario.pending .scenario_file {
|
|
132
|
-
background: #FAF834;
|
|
133
|
-
color: #000000;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
div.scenario ol {
|
|
137
|
-
margin-top: 1em;
|
|
138
|
-
margin-left: 5px;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
div.scenario table {
|
|
142
|
-
margin: 10px 0;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
div.scenario td {
|
|
146
|
-
border: solid 1px #999999;
|
|
147
|
-
padding: 5px;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
span.feature_subtitle {
|
|
151
|
-
margin-left: 20px;
|
|
152
|
-
color: #999999;
|
|
153
|
-
font-size: 0.9em;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
span.scenario_file {
|
|
157
|
-
padding: 3px;
|
|
158
|
-
line-height: 1.2em;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
span.new {
|
|
162
|
-
color: #006400;
|
|
163
|
-
font-weight: bold;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
span.edit {
|
|
167
|
-
color: #006400;
|
|
168
|
-
font-weight: bold;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
span.auto {
|
|
172
|
-
color: #006400;
|
|
173
|
-
font-weight: bold;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
ins.differ {
|
|
177
|
-
text-decoration: none;
|
|
178
|
-
background: #90EE90;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
del.differ {
|
|
182
|
-
text-decoration: none;
|
|
183
|
-
background: #FFB6C1;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.screenshot {
|
|
187
|
-
border: solid 1px #999999;
|
|
188
|
-
border-radius: 5px 5px;
|
|
189
|
-
margin: 5px 0;
|
|
190
|
-
padding: 5px;
|
|
191
|
-
background: #ffffff;
|
|
192
|
-
width: 48%;
|
|
193
|
-
height: 48%;
|
|
194
|
-
max-width: 98%;
|
|
195
|
-
max-height: 98%;
|
|
196
|
-
cursor: pointer;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
#screenshot_viewer img {
|
|
200
|
-
display: block;
|
|
201
|
-
border: solid 3px #555555;
|
|
202
|
-
border-radius: 5px 5px;
|
|
203
|
-
box-shadow: 5px 5px 5px 5px rgba(0,0,0,0.3);
|
|
204
|
-
margin: 4%;
|
|
205
|
-
padding: 1%;
|
|
206
|
-
background: #ffffff;
|
|
207
|
-
max-width: 90%;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
blockquote {
|
|
211
|
-
border: solid 1px #999999;
|
|
212
|
-
border-radius: 5px 5px;
|
|
213
|
-
padding: 5px;
|
|
214
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
function toggle_feature_dir(feature_dir) {
|
|
2
|
-
$(feature_dir).parent('div.feature_dir').nextUntil('div.feature_dir').toggle(250);
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
function toggle_step_file(step_file) {
|
|
6
|
-
$(step_file).closest('li').next('.step_contents').toggle(250);
|
|
7
|
-
event.stopPropagation();
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
$(document).ready(function() {
|
|
11
|
-
$('li.step').each(function() {
|
|
12
|
-
var messages = $(this).nextUntil('li.step').filter('.message');
|
|
13
|
-
if (messages.length > 0) {
|
|
14
|
-
$(this).css('cursor', 'pointer').click(function() {
|
|
15
|
-
messages.toggle(250);
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require 'erb'
|
|
2
|
-
require 'daddy'
|
|
3
|
-
|
|
4
|
-
module Daddy
|
|
5
|
-
module Formatter
|
|
6
|
-
module DaddyHtml
|
|
7
|
-
|
|
8
|
-
def ruby_version_dir
|
|
9
|
-
unless @_ruby_version_dir
|
|
10
|
-
@_ruby_version_dir = RUBY_VERSION.split('.')[0..1].join('.') + '.0'
|
|
11
|
-
end
|
|
12
|
-
@_ruby_version_dir
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def title
|
|
16
|
-
ret = ENV['TITLE']
|
|
17
|
-
ret ||= Daddy.config.cucumber.title if Daddy.config.cucumber.title?
|
|
18
|
-
ret ||= 'Daddy'
|
|
19
|
-
ret
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def before_menu
|
|
23
|
-
if ENV['PUBLISH']
|
|
24
|
-
@builder << "<div>"
|
|
25
|
-
|
|
26
|
-
@builder.div(:id => 'menu') do
|
|
27
|
-
@builder << make_menu_for_publish
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
@builder << "<div class='contents'>"
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def after_menu
|
|
35
|
-
if ENV['PUBLISH']
|
|
36
|
-
@builder << '</div>'
|
|
37
|
-
@builder << '</div>'
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def make_menu_for_publish
|
|
42
|
-
template = File.join(File.dirname(__FILE__), 'menu.html.erb')
|
|
43
|
-
ERB.new(File.read(template), 0, '-').result
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def feature_id
|
|
47
|
-
@feature.file.gsub(/(\/|\.|\\)/, '_')
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def feature_dir(feature, short = false)
|
|
51
|
-
ret = ''
|
|
52
|
-
|
|
53
|
-
split = feature.file.split(File::SEPARATOR)
|
|
54
|
-
split.reverse[1..-2].each_with_index do |dir, i|
|
|
55
|
-
break if dir == '仕様書' or dir == '開発日記'
|
|
56
|
-
|
|
57
|
-
if i == 0
|
|
58
|
-
if short
|
|
59
|
-
ret = dir.split('.').first + '.'
|
|
60
|
-
else
|
|
61
|
-
ret = dir
|
|
62
|
-
end
|
|
63
|
-
else
|
|
64
|
-
ret = dir.split('.').first + '.' + ret
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
ret
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def should_expand
|
|
72
|
-
['t', 'true'].include?(ENV['EXPAND'].to_s.downcase)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def magic_comment?(comment_line)
|
|
76
|
-
comment = comment_line.to_s
|
|
77
|
-
|
|
78
|
-
['language', 'format'].each do |magic|
|
|
79
|
-
return true if /#\s*#{magic}\s*:.*/ =~ comment
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
false
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
data/lib/daddy/formatter/html.rb
DELETED
|
@@ -1,720 +0,0 @@
|
|
|
1
|
-
require 'erb'
|
|
2
|
-
require 'cucumber/formatter/ordered_xml_markup'
|
|
3
|
-
require 'cucumber/formatter/duration'
|
|
4
|
-
require 'cucumber/formatter/io'
|
|
5
|
-
require 'daddy/formatter/daddy_html'
|
|
6
|
-
|
|
7
|
-
module Daddy
|
|
8
|
-
module Formatter
|
|
9
|
-
class Html
|
|
10
|
-
include ERB::Util # for the #h method
|
|
11
|
-
include ::Cucumber::Formatter::Duration
|
|
12
|
-
include ::Cucumber::Formatter::Io
|
|
13
|
-
include Daddy::Formatter::DaddyHtml
|
|
14
|
-
|
|
15
|
-
def initialize(runtime, path_or_io, options)
|
|
16
|
-
@io = ensure_io(path_or_io, "html")
|
|
17
|
-
@runtime = runtime
|
|
18
|
-
@options = options
|
|
19
|
-
@buffer = {}
|
|
20
|
-
@builder = create_builder(@io)
|
|
21
|
-
@feature_number = 0
|
|
22
|
-
@scenario_number = 0
|
|
23
|
-
@step_number = 0
|
|
24
|
-
@header_red = nil
|
|
25
|
-
@delayed_messages = []
|
|
26
|
-
@img_id = 0
|
|
27
|
-
@inside_outline = false
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def embed(src, mime_type, label)
|
|
31
|
-
case(mime_type)
|
|
32
|
-
when /^image\/(png|gif|jpg|jpeg)/
|
|
33
|
-
embed_image(src, label)
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def embed_image(src, label)
|
|
38
|
-
id = "img_#{@img_id}"
|
|
39
|
-
@img_id += 1
|
|
40
|
-
@builder.span(:class => 'embed') do |pre|
|
|
41
|
-
pre << %{<a href="" onclick="img=document.getElementById('#{id}'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false">#{label}</a><br>
|
|
42
|
-
<img id="#{id}" style="display: none" src="#{src}"/>}
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def before_features(features)
|
|
48
|
-
@step_count = features.step_count
|
|
49
|
-
|
|
50
|
-
@builder.declare!(:DOCTYPE, :html)
|
|
51
|
-
|
|
52
|
-
@builder << '<html>'
|
|
53
|
-
@builder.head do
|
|
54
|
-
@builder.meta('http-equiv' => 'Content-Type', :content => 'text/html;charset=utf-8')
|
|
55
|
-
@builder.title title
|
|
56
|
-
inline_css
|
|
57
|
-
inline_js
|
|
58
|
-
end
|
|
59
|
-
@builder << '<body>'
|
|
60
|
-
@builder << "<!-- Step count #{@step_count}-->"
|
|
61
|
-
@builder << '<div class="cucumber">'
|
|
62
|
-
@builder.div(:id => 'cucumber-header') do
|
|
63
|
-
@builder.div(:id => 'label') do
|
|
64
|
-
@builder.h1(title)
|
|
65
|
-
end
|
|
66
|
-
@builder.div(:id => 'summary') do
|
|
67
|
-
@builder.p('',:id => 'totals')
|
|
68
|
-
@builder.p('',:id => 'duration')
|
|
69
|
-
@builder.div(:id => 'expand-collapse') do
|
|
70
|
-
@builder.p('すべて開く', :id => 'expander')
|
|
71
|
-
@builder.p('すべて閉じる', :id => 'collapser')
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
before_menu
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def after_features(features)
|
|
80
|
-
after_menu
|
|
81
|
-
print_stats(features)
|
|
82
|
-
@builder << '</div>'
|
|
83
|
-
@builder << '</body>'
|
|
84
|
-
@builder << '</html>'
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def before_feature(feature)
|
|
88
|
-
dir = feature_dir(feature)
|
|
89
|
-
unless dir.empty?
|
|
90
|
-
if @feature_dir != dir
|
|
91
|
-
@builder << '<div class="feature_dir"><span class="val" onclick="toggle_feature_dir(this);">'
|
|
92
|
-
@builder << dir
|
|
93
|
-
@builder << '</span></div>'
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
@feature_dir = dir
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
@feature = feature
|
|
100
|
-
@exceptions = []
|
|
101
|
-
@builder << "<div id=\"#{feature_id}\" class=\"feature\">"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def after_feature(feature)
|
|
105
|
-
@builder << '</div>'
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def before_comment(comment)
|
|
109
|
-
@builder << '<pre class="comment">' unless magic_comment?(comment)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def after_comment(comment)
|
|
113
|
-
@builder << '</pre>' unless magic_comment?(comment)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def comment_line(comment_line)
|
|
117
|
-
unless magic_comment?(comment_line)
|
|
118
|
-
@builder.text!(comment_line)
|
|
119
|
-
@builder.br
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def after_tags(tags)
|
|
124
|
-
@tag_spacer = nil
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def tag_name(tag_name)
|
|
128
|
-
@builder.text!(@tag_spacer) if @tag_spacer
|
|
129
|
-
@tag_spacer = ' '
|
|
130
|
-
@builder.span(tag_name, :class => 'tag')
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def feature_name(keyword, name)
|
|
134
|
-
title = feature_dir(@feature, true) + @feature.file.split('/').last.gsub(/\.feature/, '')
|
|
135
|
-
lines = name.split(/\r?\n/)
|
|
136
|
-
return if lines.empty?
|
|
137
|
-
@builder.h2 do |h2|
|
|
138
|
-
@builder.span(:class => 'val') do
|
|
139
|
-
@builder << title
|
|
140
|
-
|
|
141
|
-
if Daddy.config.cucumber.use_feature_name?
|
|
142
|
-
subtitle = lines.first.to_s.strip
|
|
143
|
-
unless subtitle.blank?
|
|
144
|
-
@builder.span('(' + subtitle + ')', :class => 'feature_subtitle')
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
if lines.size > 1
|
|
151
|
-
@builder.div(:class => 'narrative') do
|
|
152
|
-
@builder << lines[1..-1].join("\n")
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def before_background(background)
|
|
158
|
-
@in_background = true
|
|
159
|
-
@builder << '<div class="background">'
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def after_background(background)
|
|
163
|
-
@in_background = nil
|
|
164
|
-
@builder << '</div>'
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def background_name(keyword, name, file_colon_line, source_indent)
|
|
168
|
-
@listing_background = true
|
|
169
|
-
@builder.h3 do |h3|
|
|
170
|
-
@builder.span(keyword, :class => 'keyword')
|
|
171
|
-
@builder.text!(' ')
|
|
172
|
-
@builder.span(name, :class => 'val')
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def before_feature_element(feature_element)
|
|
177
|
-
@scenario_number+=1
|
|
178
|
-
@scenario_red = false
|
|
179
|
-
css_class = {
|
|
180
|
-
::Cucumber::Ast::Scenario => 'scenario',
|
|
181
|
-
::Cucumber::Ast::ScenarioOutline => 'scenario outline'
|
|
182
|
-
}[feature_element.class]
|
|
183
|
-
@builder << "<div class='#{css_class}'>"
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def after_feature_element(feature_element)
|
|
187
|
-
@builder << '</div>'
|
|
188
|
-
@open_step_list = true
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def scenario_name(keyword, name, file_colon_line, source_indent)
|
|
192
|
-
@step_number_in_scenario = 0
|
|
193
|
-
|
|
194
|
-
@builder.span(:class => 'scenario_file', :style => 'display: none;') do
|
|
195
|
-
@builder << file_colon_line
|
|
196
|
-
end
|
|
197
|
-
@listing_background = false
|
|
198
|
-
|
|
199
|
-
lines = name.split("\n")
|
|
200
|
-
@builder.h3 do
|
|
201
|
-
@builder.span(lines[0], :class => 'val')
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
if lines.size > 1
|
|
205
|
-
@builder.div(:class => 'narrative', :style => 'display: none;') do
|
|
206
|
-
@builder << lines[1..-1].join("\n")
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def before_outline_table(outline_table)
|
|
212
|
-
@inside_outline = true
|
|
213
|
-
@outline_row = 0
|
|
214
|
-
@builder << '<table>'
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def after_outline_table(outline_table)
|
|
218
|
-
@builder << '</table>'
|
|
219
|
-
@outline_row = nil
|
|
220
|
-
@inside_outline = false
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def before_examples(examples)
|
|
224
|
-
@builder << '<div class="examples">'
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def after_examples(examples)
|
|
228
|
-
@builder << '</div>'
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
def examples_name(keyword, name)
|
|
232
|
-
@builder.h4 do
|
|
233
|
-
@builder.span(keyword, :class => 'keyword')
|
|
234
|
-
@builder.text!(' ')
|
|
235
|
-
@builder.span(name, :class => 'val')
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def before_steps(steps)
|
|
240
|
-
@builder << '<ol style="display: none;">'
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def after_steps(steps)
|
|
244
|
-
@builder << '</ol>'
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def before_step(step)
|
|
248
|
-
@step_id = step.dom_id
|
|
249
|
-
@step_number += 1
|
|
250
|
-
@step = step
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def after_step(step)
|
|
254
|
-
move_progress
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
|
|
258
|
-
@step_match = step_match
|
|
259
|
-
@hide_this_step = false
|
|
260
|
-
if exception
|
|
261
|
-
if @exceptions.include?(exception)
|
|
262
|
-
@hide_this_step = true
|
|
263
|
-
return
|
|
264
|
-
end
|
|
265
|
-
@exceptions << exception
|
|
266
|
-
end
|
|
267
|
-
if status != :failed && @in_background ^ background
|
|
268
|
-
@hide_this_step = true
|
|
269
|
-
return
|
|
270
|
-
end
|
|
271
|
-
@status = status
|
|
272
|
-
return if @hide_this_step
|
|
273
|
-
set_scenario_color(status)
|
|
274
|
-
|
|
275
|
-
if ! @delayed_messages.empty? and status == :passed
|
|
276
|
-
@builder << "<li class='step #{status} expand'>"
|
|
277
|
-
else
|
|
278
|
-
@builder << "<li class='step #{status}'>"
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
|
|
283
|
-
return if @hide_this_step
|
|
284
|
-
# print snippet for undefined steps
|
|
285
|
-
if status == :undefined
|
|
286
|
-
keyword = @step.actual_keyword if @step.respond_to?(:actual_keyword)
|
|
287
|
-
step_multiline_class = @step.multiline_arg ? @step.multiline_arg.class : nil
|
|
288
|
-
@builder.pre do |pre|
|
|
289
|
-
pre << @runtime.snippet_text(keyword,step_match.instance_variable_get("@name") || '',step_multiline_class)
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
@builder << '</li>'
|
|
293
|
-
|
|
294
|
-
unless status == :undefined
|
|
295
|
-
step_file = step_match.file_colon_line
|
|
296
|
-
step_contents = "<div class=\"step_contents\"><pre>"
|
|
297
|
-
step_file.gsub(/^([^:]*\.rb):(\d*)/) do
|
|
298
|
-
line_index = $2.to_i - 1
|
|
299
|
-
|
|
300
|
-
file = $1.force_encoding('UTF-8')
|
|
301
|
-
if file.start_with?('daddy-') or file.start_with?('/daddy-')
|
|
302
|
-
file = File.join('/usr/local/lib/ruby/gems', ruby_version_dir, 'gems', file)
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
File.readlines(File.expand_path(file))[line_index..-1].each do |line|
|
|
306
|
-
step_contents << line
|
|
307
|
-
break if line.chop == 'end' or line.chop.start_with?('end ')
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
step_contents << "</pre></div>"
|
|
311
|
-
@builder << step_contents
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
print_messages
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
|
|
318
|
-
background_in_scenario = background && !@listing_background
|
|
319
|
-
@skip_step = background_in_scenario
|
|
320
|
-
|
|
321
|
-
unless @skip_step
|
|
322
|
-
build_step(keyword, step_match, status)
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
def exception(exception, status)
|
|
327
|
-
return if @hide_this_step
|
|
328
|
-
build_exception_detail(exception)
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
def extra_failure_content(file_colon_line)
|
|
332
|
-
@snippet_extractor ||= SnippetExtractor.new
|
|
333
|
-
"<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def before_multiline_arg(multiline_arg)
|
|
337
|
-
return if @hide_this_step || @skip_step
|
|
338
|
-
if ::Cucumber::Ast::Table === multiline_arg
|
|
339
|
-
@builder << '<table>'
|
|
340
|
-
end
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
def after_multiline_arg(multiline_arg)
|
|
344
|
-
return if @hide_this_step || @skip_step
|
|
345
|
-
if ::Cucumber::Ast::Table === multiline_arg
|
|
346
|
-
@builder << '</table>'
|
|
347
|
-
end
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
def doc_string(string)
|
|
351
|
-
return if @hide_this_step
|
|
352
|
-
@builder.pre(:class => 'val') do |pre|
|
|
353
|
-
@builder << h(string).gsub("\n", '
')
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
def before_table_row(table_row)
|
|
359
|
-
@row_id = table_row.dom_id
|
|
360
|
-
@col_index = 0
|
|
361
|
-
return if @hide_this_step
|
|
362
|
-
@builder << "<tr class='step' id='#{@row_id}'>"
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
def after_table_row(table_row)
|
|
366
|
-
return if @hide_this_step
|
|
367
|
-
print_table_row_messages
|
|
368
|
-
@builder << '</tr>'
|
|
369
|
-
if table_row.exception
|
|
370
|
-
@builder.tr do
|
|
371
|
-
@builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
|
372
|
-
@builder.pre do |pre|
|
|
373
|
-
pre << h(format_exception(table_row.exception))
|
|
374
|
-
end
|
|
375
|
-
end
|
|
376
|
-
end
|
|
377
|
-
if table_row.exception.is_a? ::Cucumber::Pending
|
|
378
|
-
set_scenario_color_pending
|
|
379
|
-
else
|
|
380
|
-
set_scenario_color_failed
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
if @outline_row
|
|
384
|
-
@outline_row += 1
|
|
385
|
-
end
|
|
386
|
-
@step_number += 1
|
|
387
|
-
move_progress
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
def table_cell_value(value, status)
|
|
391
|
-
return if @hide_this_step
|
|
392
|
-
|
|
393
|
-
@cell_type = @outline_row == 0 ? :th : :td
|
|
394
|
-
attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
|
|
395
|
-
attributes[:class] += " #{status}" if status
|
|
396
|
-
build_cell(@cell_type, value, attributes)
|
|
397
|
-
set_scenario_color(status) if @inside_outline
|
|
398
|
-
@col_index += 1
|
|
399
|
-
end
|
|
400
|
-
|
|
401
|
-
def puts(message)
|
|
402
|
-
@delayed_messages << message
|
|
403
|
-
#@builder.pre(message, :class => 'message')
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
def print_messages
|
|
407
|
-
return if @delayed_messages.empty?
|
|
408
|
-
|
|
409
|
-
#@builder.ol do
|
|
410
|
-
@delayed_messages.each do |ann|
|
|
411
|
-
@builder.li(:class => 'message', :style => 'display: none;') do
|
|
412
|
-
@builder << ann
|
|
413
|
-
end
|
|
414
|
-
end
|
|
415
|
-
#end
|
|
416
|
-
empty_messages
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
def print_table_row_messages
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
def empty_messages
|
|
423
|
-
@delayed_messages = []
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
protected
|
|
427
|
-
|
|
428
|
-
def build_exception_detail(exception)
|
|
429
|
-
backtrace = Array.new
|
|
430
|
-
@builder.div(:class => 'message') do
|
|
431
|
-
message = exception.message
|
|
432
|
-
if defined?(RAILS_ROOT) && message.include?('Exception caught')
|
|
433
|
-
matches = message.match(/Showing <i>(.+)<\/i>(?:.+) #(\d+)/)
|
|
434
|
-
backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"] if matches
|
|
435
|
-
matches = message.match(/<code>([^(\/)]+)<\//m)
|
|
436
|
-
message = matches ? matches[1] : ""
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
unless exception.instance_of?(RuntimeError)
|
|
440
|
-
message = "#{message} (#{exception.class})"
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
@builder.pre do
|
|
444
|
-
@builder.text!(message)
|
|
445
|
-
end
|
|
446
|
-
end
|
|
447
|
-
@builder.div(:class => 'backtrace') do
|
|
448
|
-
@builder.pre do
|
|
449
|
-
backtrace = exception.backtrace
|
|
450
|
-
backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
|
|
451
|
-
@builder << backtrace_line(backtrace.join("\n"))
|
|
452
|
-
end
|
|
453
|
-
end
|
|
454
|
-
extra = extra_failure_content(backtrace)
|
|
455
|
-
@builder << extra unless extra == ""
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
def set_scenario_color(status)
|
|
459
|
-
if status.nil? or status == :undefined or status == :pending
|
|
460
|
-
set_scenario_color_pending
|
|
461
|
-
end
|
|
462
|
-
if status == :failed
|
|
463
|
-
set_scenario_color_failed
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
def set_scenario_color_failed
|
|
468
|
-
id = Daddy::Utils::StringUtils.current_time
|
|
469
|
-
style = 'display: none; margin: 0; padding: 0;'
|
|
470
|
-
@builder << "<div id=\"#{id}\" style=\"#{style}\"></div>"
|
|
471
|
-
|
|
472
|
-
@builder.script do
|
|
473
|
-
@builder.text!("makeRed('cucumber-header');") unless @header_red
|
|
474
|
-
@header_red = true
|
|
475
|
-
@builder.text!("$(function() { $('##{id}').closest('.scenario').addClass('faild'); });") unless @scenario_red
|
|
476
|
-
@scenario_red = true
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
def set_scenario_color_pending
|
|
481
|
-
id = Daddy::Utils::StringUtils.current_time
|
|
482
|
-
style = 'display: none; margin: 0; padding: 0;'
|
|
483
|
-
@builder << "<div id=\"#{id}\" style=\"#{style}\"></div>"
|
|
484
|
-
|
|
485
|
-
@builder.script do
|
|
486
|
-
@builder.text!("makeYellow('cucumber-header');") unless @header_red
|
|
487
|
-
@builder.text!("$(function() { $('##{id}').closest('.scenario').addClass('pending'); });") unless @scenario_red
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
def build_step(keyword, step_match, status)
|
|
492
|
-
if @in_background
|
|
493
|
-
display_keyword = keyword.strip + ' '
|
|
494
|
-
else
|
|
495
|
-
if keyword.strip == '*'
|
|
496
|
-
@step_number_in_scenario += 1
|
|
497
|
-
display_keyword = ''
|
|
498
|
-
display_keyword << '0' if @step_number_in_scenario.to_s.size == 1
|
|
499
|
-
display_keyword << @step_number_in_scenario.to_s
|
|
500
|
-
display_keyword << '. '
|
|
501
|
-
else
|
|
502
|
-
display_keyword = keyword.strip + ' '
|
|
503
|
-
end
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
|
|
507
|
-
@builder.div(:class => 'step_name') do |div|
|
|
508
|
-
@builder.span(display_keyword, :class => 'keyword')
|
|
509
|
-
@builder.span(:class => 'step val') do |name|
|
|
510
|
-
name << h(step_name).gsub(/<span class="(.*?)">/, '<span class="\1">').gsub(/<\/span>/, '</span>')
|
|
511
|
-
end
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
step_file = step_match.file_colon_line.force_encoding('UTF-8')
|
|
515
|
-
step_file.gsub(/^([^:]*\.rb):(\d*)/) do
|
|
516
|
-
if index = $1.index('lib/daddy/cucumber/step_definitions/')
|
|
517
|
-
step_file = "daddy: #{$1[index..-1]}:#{$2}"
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
step_file = "<span style=\"cursor: pointer;\" onclick=\"toggle_step_file(this); return false;\">#{step_file}</span>"
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
@builder.div(:class => 'step_file') do |div|
|
|
524
|
-
@builder.span do
|
|
525
|
-
@builder << step_file
|
|
526
|
-
end
|
|
527
|
-
end
|
|
528
|
-
end
|
|
529
|
-
|
|
530
|
-
def build_cell(cell_type, value, attributes)
|
|
531
|
-
@builder.__send__(cell_type, attributes) do
|
|
532
|
-
@builder.div do
|
|
533
|
-
@builder.span(value,:class => 'step param')
|
|
534
|
-
end
|
|
535
|
-
end
|
|
536
|
-
end
|
|
537
|
-
|
|
538
|
-
def inline_css
|
|
539
|
-
@builder.style(:type => 'text/css') do
|
|
540
|
-
@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
|
|
541
|
-
@builder << File.read(File.dirname(__FILE__) + '/daddy.css')
|
|
542
|
-
if File.exist?('features/support/daddy.css')
|
|
543
|
-
@builder << File.read('features/support/daddy.css')
|
|
544
|
-
end
|
|
545
|
-
end
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
def inline_js
|
|
549
|
-
@builder.script(:type => 'text/javascript') do
|
|
550
|
-
@builder << inline_jquery
|
|
551
|
-
@builder << inline_js_content
|
|
552
|
-
@builder << inline_daddy
|
|
553
|
-
end
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
def inline_jquery
|
|
557
|
-
File.read(File.dirname(__FILE__) + '/jquery-min.js')
|
|
558
|
-
end
|
|
559
|
-
|
|
560
|
-
def inline_daddy
|
|
561
|
-
ret = ''
|
|
562
|
-
ret << File.read(File.join(File.dirname(__FILE__), 'daddy.js'))
|
|
563
|
-
ret << File.read(File.join(File.dirname(__FILE__), 'screenshot.js'))
|
|
564
|
-
|
|
565
|
-
if should_expand
|
|
566
|
-
ret << %w{
|
|
567
|
-
$(document).ready(function() {
|
|
568
|
-
$('#expander').click();
|
|
569
|
-
});
|
|
570
|
-
}.join(' ')
|
|
571
|
-
end
|
|
572
|
-
|
|
573
|
-
ret
|
|
574
|
-
end
|
|
575
|
-
|
|
576
|
-
def inline_js_content
|
|
577
|
-
<<-EOF
|
|
578
|
-
|
|
579
|
-
SCENARIOS = "div.scenario h3";
|
|
580
|
-
|
|
581
|
-
$(document).ready(function() {
|
|
582
|
-
$(SCENARIOS).css('cursor', 'pointer');
|
|
583
|
-
$(SCENARIOS).click(function() {
|
|
584
|
-
$(this).siblings().toggle(250);
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
$("#collapser").css('cursor', 'pointer');
|
|
588
|
-
$("#collapser").click(function() {
|
|
589
|
-
$(SCENARIOS).siblings().hide();
|
|
590
|
-
$('li.message').hide();
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
$("#expander").css('cursor', 'pointer');
|
|
594
|
-
$("#expander").click(function() {
|
|
595
|
-
$(SCENARIOS).siblings().show();
|
|
596
|
-
$('li.message').show();
|
|
597
|
-
});
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
function moveProgressBar(percentDone) {
|
|
601
|
-
$("cucumber-header").css('width', percentDone +"%");
|
|
602
|
-
}
|
|
603
|
-
function makeRed(element_id) {
|
|
604
|
-
$('#'+element_id).css('background', '#C40D0D');
|
|
605
|
-
$('#'+element_id).css('color', '#FFFFFF');
|
|
606
|
-
}
|
|
607
|
-
function makeYellow(element_id) {
|
|
608
|
-
$('#'+element_id).css('background', '#FAF834');
|
|
609
|
-
$('#'+element_id).css('color', '#000000');
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
EOF
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
def move_progress
|
|
616
|
-
@builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
|
|
617
|
-
end
|
|
618
|
-
|
|
619
|
-
def percent_done
|
|
620
|
-
result = 100.0
|
|
621
|
-
if @step_count != 0
|
|
622
|
-
result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
|
|
623
|
-
end
|
|
624
|
-
result
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
def format_exception(exception)
|
|
628
|
-
(["#{exception.message}"] + exception.backtrace).join("\n")
|
|
629
|
-
end
|
|
630
|
-
|
|
631
|
-
def backtrace_line(line)
|
|
632
|
-
line.gsub(/\A([^:]*\.(?:rb|feature|haml)):(\d*).*\z/) do
|
|
633
|
-
if ENV['TM_PROJECT_DIRECTORY']
|
|
634
|
-
"<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
|
|
635
|
-
else
|
|
636
|
-
line
|
|
637
|
-
end
|
|
638
|
-
end
|
|
639
|
-
end
|
|
640
|
-
|
|
641
|
-
def print_stats(features)
|
|
642
|
-
@builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
|
|
643
|
-
@builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
|
|
644
|
-
end
|
|
645
|
-
|
|
646
|
-
def print_stat_string(features)
|
|
647
|
-
string = String.new
|
|
648
|
-
string << dump_count(@runtime.scenarios.length, "scenario")
|
|
649
|
-
scenario_count = print_status_counts{|status| @runtime.scenarios(status)}
|
|
650
|
-
string << scenario_count if scenario_count
|
|
651
|
-
string << "<br />"
|
|
652
|
-
string << dump_count(@runtime.steps.length, "step")
|
|
653
|
-
step_count = print_status_counts{|status| @runtime.steps(status)}
|
|
654
|
-
string << step_count if step_count
|
|
655
|
-
end
|
|
656
|
-
|
|
657
|
-
def print_status_counts
|
|
658
|
-
counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
|
|
659
|
-
elements = yield status
|
|
660
|
-
elements.any? ? "#{elements.length} #{status.to_s}" : nil
|
|
661
|
-
end.compact
|
|
662
|
-
return " (#{counts.join(', ')})" if counts.any?
|
|
663
|
-
end
|
|
664
|
-
|
|
665
|
-
def dump_count(count, what, state=nil)
|
|
666
|
-
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
|
|
667
|
-
end
|
|
668
|
-
|
|
669
|
-
def create_builder(io)
|
|
670
|
-
::Cucumber::Formatter::OrderedXmlMarkup.new(:target => io, :indent => 0)
|
|
671
|
-
end
|
|
672
|
-
|
|
673
|
-
class SnippetExtractor #:nodoc:
|
|
674
|
-
class NullConverter; def convert(code, pre); code; end; end #:nodoc:
|
|
675
|
-
begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
|
|
676
|
-
|
|
677
|
-
def snippet(error)
|
|
678
|
-
raw_code, line = snippet_for(error[0])
|
|
679
|
-
highlighted = @@converter.convert(raw_code, false)
|
|
680
|
-
highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
|
|
681
|
-
post_process(highlighted, line)
|
|
682
|
-
end
|
|
683
|
-
|
|
684
|
-
def snippet_for(error_line)
|
|
685
|
-
if error_line =~ /(.*):(\d+)/
|
|
686
|
-
file = $1
|
|
687
|
-
line = $2.to_i
|
|
688
|
-
[lines_around(file, line), line]
|
|
689
|
-
else
|
|
690
|
-
["# Couldn't get snippet for #{error_line}", 1]
|
|
691
|
-
end
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
def lines_around(file, line)
|
|
695
|
-
if File.file?(file)
|
|
696
|
-
lines = File.open(file).read.split("\n")
|
|
697
|
-
min = [0, line-3].max
|
|
698
|
-
max = [line+1, lines.length-1].min
|
|
699
|
-
selected_lines = []
|
|
700
|
-
selected_lines.join("\n")
|
|
701
|
-
lines[min..max].join("\n")
|
|
702
|
-
else
|
|
703
|
-
"# Couldn't get snippet for #{file}"
|
|
704
|
-
end
|
|
705
|
-
end
|
|
706
|
-
|
|
707
|
-
def post_process(highlighted, offending_line)
|
|
708
|
-
new_lines = []
|
|
709
|
-
highlighted.split("\n").each_with_index do |line, i|
|
|
710
|
-
new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
|
|
711
|
-
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
|
|
712
|
-
new_lines << new_line
|
|
713
|
-
end
|
|
714
|
-
new_lines.join("\n")
|
|
715
|
-
end
|
|
716
|
-
|
|
717
|
-
end
|
|
718
|
-
end
|
|
719
|
-
end
|
|
720
|
-
end
|