cuukie 0.1.4 → 0.2.0
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/Gemfile +4 -3
- data/README.markdown +20 -20
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/bin/cuukie +42 -0
- data/cuukie.gemspec +21 -16
- data/doc/backlog.txt +10 -7
- data/doc/pomodoro.txt +22 -0
- data/lib/cuukie.rb +1 -1
- data/lib/cuukie/cli.rb +48 -0
- data/lib/cuukie/code_snippets.rb +25 -0
- data/lib/cuukie/formatter.rb +88 -0
- data/lib/cuukie/public/cucumber.css +1 -1
- data/lib/cuukie/public/cuukie.js +4 -0
- data/lib/cuukie/server.rb +98 -67
- data/lib/cuukie/views/index.erb +20 -21
- data/spec/cli_spec.rb +86 -0
- data/spec/code_snippets_spec.rb +4 -4
- data/spec/commands_spec.rb +73 -22
- data/spec/cuukie_spec.rb +109 -21
- data/spec/spec_helper.rb +7 -2
- data/spec/test_project/features/1_show_scenarios.feature +3 -3
- data/spec/test_project/features/2_show_multiline_args.feature +1 -1
- data/spec/test_project/features/3_failed_background.feature +5 -7
- data/spec/test_project/features/step_definitions/main_steps.rb +5 -4
- metadata +39 -25
- data/bin/cuukie_server +0 -9
- data/lib/cuukie/cucumber/formatter/code_snippets.rb +0 -23
- data/lib/cuukie/cucumber/formatter/cuukie.rb +0 -90
@@ -182,7 +182,7 @@ body {
|
|
182
182
|
.cucumber ol li.undefined, td ol li.undefined, th ol li.undefined {
|
183
183
|
border-left: 5px solid #faf834;
|
184
184
|
border-bottom: 1px solid #faf834;
|
185
|
-
background: #
|
185
|
+
background: #aaaaaa;
|
186
186
|
color: #131313;
|
187
187
|
}
|
188
188
|
.cucumber ol li.message, td ol li.message, th ol li.message {
|
data/lib/cuukie/public/cuukie.js
CHANGED
@@ -37,3 +37,7 @@ function skippedColors(element_id) {
|
|
37
37
|
$('#'+element_id).css('background', '#E0FFFF');
|
38
38
|
$('#'+element_id).css('color', '#000000');
|
39
39
|
}
|
40
|
+
function undefinedColors(element_id) {
|
41
|
+
$('#'+element_id).css('background', '#AAAAAA');
|
42
|
+
$('#'+element_id).css('color', '#000000');
|
43
|
+
}
|
data/lib/cuukie/server.rb
CHANGED
@@ -5,96 +5,102 @@ require 'syntax/convertors/html'
|
|
5
5
|
module Cuukie
|
6
6
|
class Server < Sinatra::Base
|
7
7
|
set :features, []
|
8
|
-
set :build_status,
|
9
|
-
set :
|
10
|
-
set :
|
8
|
+
set :build_status, 'undefined'
|
9
|
+
set :start_time, nil
|
10
|
+
set :duration, nil
|
11
|
+
set :stats, Hash.new('')
|
11
12
|
|
12
13
|
get '/' do
|
13
14
|
@features = settings.features
|
14
15
|
@build_status = settings.build_status
|
15
|
-
@duration = settings.duration
|
16
16
|
@stats = settings.stats
|
17
17
|
erb :index
|
18
18
|
end
|
19
19
|
|
20
20
|
post '/before_features' do
|
21
21
|
settings.features.clear
|
22
|
-
settings.build_status =
|
23
|
-
settings.
|
24
|
-
settings.duration =
|
25
|
-
settings.
|
22
|
+
settings.build_status = 'undefined'
|
23
|
+
settings.start_time = Time.now
|
24
|
+
settings.duration = nil
|
25
|
+
settings.stats = Hash.new('')
|
26
26
|
end
|
27
27
|
|
28
28
|
post '/before_feature' do
|
29
29
|
feature = read_from_request
|
30
|
-
feature[
|
31
|
-
feature[
|
32
|
-
feature[
|
30
|
+
feature[:keyword] = '...'
|
31
|
+
feature[:description] = feature[:description].split("\n")
|
32
|
+
feature[:scenarios] = []
|
33
|
+
feature[:id] = settings.features.size + 1
|
33
34
|
settings.features << feature
|
34
35
|
'OK'
|
35
36
|
end
|
36
37
|
|
38
|
+
post '/feature_name' do
|
39
|
+
current_feature.merge! read_from_request
|
40
|
+
'OK'
|
41
|
+
end
|
42
|
+
|
37
43
|
post '/scenario_name' do
|
38
44
|
scenario = read_from_request
|
39
|
-
scenario[
|
40
|
-
scenario[
|
41
|
-
|
45
|
+
scenario[:steps] = []
|
46
|
+
scenario[:id] = "scenario_#{current_feature[:id]}_#{current_feature[:scenarios].size + 1}"
|
47
|
+
scenario[:status] = 'undefined'
|
48
|
+
current_feature[:scenarios] << scenario
|
42
49
|
'OK'
|
43
50
|
end
|
44
51
|
|
45
|
-
post '/
|
52
|
+
post '/before_step' do
|
46
53
|
step = read_from_request
|
47
|
-
step[
|
48
|
-
|
54
|
+
step[:table] = []
|
55
|
+
step[:status] = 'undefined'
|
56
|
+
current_scenario[:steps] << step
|
49
57
|
'OK'
|
50
58
|
end
|
51
|
-
|
52
|
-
post '/
|
53
|
-
current_step[
|
59
|
+
|
60
|
+
post '/before_table_row' do
|
61
|
+
current_step[:table] << []
|
54
62
|
'OK'
|
55
63
|
end
|
56
64
|
|
57
|
-
post '/
|
58
|
-
|
59
|
-
|
60
|
-
current_scenario['status'] = settings.build_status = 'failed'
|
61
|
-
elsif current_step['status'] == 'pending'
|
62
|
-
current_scenario['status'] = 'pending'
|
63
|
-
settings.build_status ||= 'pending'
|
64
|
-
end
|
65
|
+
post '/table_cell_value' do
|
66
|
+
data = read_from_request
|
67
|
+
current_step[:table].last << data[:value]
|
65
68
|
'OK'
|
66
69
|
end
|
67
|
-
|
68
|
-
post '/
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
current_scenario['status'] ||= 'passed'
|
70
|
+
|
71
|
+
post '/doc_string' do
|
72
|
+
data = read_from_request
|
73
|
+
current_step[:multiline_string] = data[:multiline_string]
|
73
74
|
'OK'
|
74
75
|
end
|
75
76
|
|
76
|
-
post '/
|
77
|
-
current_step[
|
77
|
+
post '/exception' do
|
78
|
+
current_step[:exception] = read_from_request
|
78
79
|
'OK'
|
79
80
|
end
|
80
81
|
|
81
|
-
post '/
|
82
|
-
|
83
|
-
current_step[
|
82
|
+
post '/after_step_result' do
|
83
|
+
current_step.merge! read_from_request
|
84
|
+
if current_step[:status] == 'failed'
|
85
|
+
current_scenario[:status] = settings.build_status = 'failed'
|
86
|
+
elsif current_step[:status] == 'pending'
|
87
|
+
current_scenario[:status] = 'pending'
|
88
|
+
settings.build_status = 'pending' if settings.build_status == 'undefined'
|
89
|
+
end
|
84
90
|
'OK'
|
85
91
|
end
|
86
|
-
|
87
|
-
post '/
|
88
|
-
|
89
|
-
|
92
|
+
|
93
|
+
post '/after_steps' do
|
94
|
+
if current_scenario[:steps].all? {|step| step[:status] == 'skipped' }
|
95
|
+
current_scenario[:status] = 'skipped'
|
96
|
+
end
|
97
|
+
current_scenario[:status] = 'passed' if current_scenario[:status] == 'undefined'
|
90
98
|
'OK'
|
91
99
|
end
|
92
100
|
|
93
101
|
post '/after_features' do
|
94
|
-
|
95
|
-
|
96
|
-
settings.duration = "#{min}m#{'%.3f' % sec}s"
|
97
|
-
settings.build_status ||= 'passed'
|
102
|
+
settings.duration = read_from_request[:duration]
|
103
|
+
settings.build_status = 'passed' if settings.build_status == 'undefined'
|
98
104
|
settings.stats = stats
|
99
105
|
'OK'
|
100
106
|
end
|
@@ -103,19 +109,28 @@ module Cuukie
|
|
103
109
|
delete('/') { exit! }
|
104
110
|
|
105
111
|
helpers do
|
106
|
-
def
|
107
|
-
return '' unless exception[
|
112
|
+
def code_snippet_for(exception)
|
113
|
+
return '' unless exception[:raw_lines]
|
108
114
|
result = '<pre class="ruby"><code>'
|
109
|
-
linenum = exception[
|
110
|
-
html_lines = htmlize(exception[
|
115
|
+
linenum = exception[:first_line]
|
116
|
+
html_lines = htmlize(exception[:raw_lines]).split "\n"
|
111
117
|
html_lines.each do |html_line|
|
112
118
|
line = "<span class=\"linenum\">#{linenum}</span>#{html_line}"
|
113
|
-
line = "<span class=\"offending\">#{line}</span>" if linenum == exception[
|
119
|
+
line = "<span class=\"offending\">#{line}</span>" if linenum == exception[:marked_line]
|
114
120
|
result << "#{line}<br/>"
|
115
121
|
linenum += 1
|
116
122
|
end
|
117
123
|
result << '</code></pre>'
|
118
124
|
end
|
125
|
+
|
126
|
+
def time_label
|
127
|
+
settings.duration ? "Duration" : "Running time"
|
128
|
+
end
|
129
|
+
|
130
|
+
def format_time
|
131
|
+
min, sec = time.to_i.divmod(60)
|
132
|
+
"#{min}':#{sec}''"
|
133
|
+
end
|
119
134
|
end
|
120
135
|
|
121
136
|
def current_feature
|
@@ -127,41 +142,47 @@ module Cuukie
|
|
127
142
|
# don't have a scenario yet. this is useful to eliminate steps
|
128
143
|
# coming from backgrounds (which will be re-sent during the
|
129
144
|
# following scenarios anyway)
|
130
|
-
return {
|
131
|
-
current_feature[
|
145
|
+
return { :steps => [{}] } if current_feature[:scenarios].empty?
|
146
|
+
current_feature[:scenarios].last
|
132
147
|
end
|
133
148
|
|
134
149
|
def current_step
|
135
|
-
current_scenario[
|
150
|
+
current_scenario[:steps].last
|
151
|
+
end
|
152
|
+
|
153
|
+
def time
|
154
|
+
return settings.duration if settings.duration
|
155
|
+
return 0 unless settings.start_time
|
156
|
+
return Time.now - settings.start_time
|
136
157
|
end
|
137
158
|
|
138
159
|
def stats
|
139
160
|
scenarios = []
|
140
|
-
settings.features.each {|feature| scenarios.concat feature[
|
161
|
+
settings.features.each {|feature| scenarios.concat feature[:scenarios] }
|
141
162
|
|
142
163
|
result = {:scenarios => String.new, :steps => String.new}
|
143
|
-
result[:scenarios] <<
|
164
|
+
result[:scenarios] << pluralize(scenarios.size, "scenario")
|
144
165
|
result[:scenarios] << counts(scenarios)
|
145
166
|
|
146
167
|
steps = []
|
147
|
-
scenarios.each {|scenario| steps.concat scenario[
|
148
|
-
result[:steps] <<
|
168
|
+
scenarios.each {|scenario| steps.concat scenario[:steps] }
|
169
|
+
result[:steps] << pluralize(steps.size, "step")
|
149
170
|
step_count = counts steps
|
150
171
|
result[:steps] << step_count if step_count
|
151
172
|
result
|
152
173
|
end
|
174
|
+
|
175
|
+
def pluralize(count, what)
|
176
|
+
"#{count} #{what}#{count == 1 ? '' : 's'}"
|
177
|
+
end
|
153
178
|
|
154
179
|
def counts(elements)
|
155
180
|
counts = ['failed', 'skipped', 'undefined', 'pending', 'passed'].map do |status|
|
156
|
-
selected = elements.find_all {|element| element[
|
181
|
+
selected = elements.find_all {|element| element[:status] == status }
|
157
182
|
selected.any? ? "#{selected.size} #{status}" : nil
|
158
183
|
end.compact
|
159
184
|
counts.any? ? " (#{counts.join(', ')})" : ''
|
160
185
|
end
|
161
|
-
|
162
|
-
def dump_count(count, what)
|
163
|
-
"#{count} #{what}#{count == 1 ? '' : 's'}"
|
164
|
-
end
|
165
186
|
|
166
187
|
def htmlize(ruby)
|
167
188
|
convertor = Syntax::Convertors::HTML.for_syntax("ruby")
|
@@ -172,10 +193,20 @@ module Cuukie
|
|
172
193
|
|
173
194
|
def read_from_request
|
174
195
|
data = JSON.parse request.body.read
|
175
|
-
result =
|
176
|
-
|
177
|
-
|
196
|
+
result = {}
|
197
|
+
data.each do |k, v|
|
198
|
+
if v.class == String && k !~ /^raw_/
|
199
|
+
result[k.to_sym] = escape_html(v)
|
200
|
+
else
|
201
|
+
result[k.to_sym] = v
|
202
|
+
end
|
178
203
|
end
|
204
|
+
result
|
179
205
|
end
|
180
206
|
end
|
181
207
|
end
|
208
|
+
|
209
|
+
if __FILE__ == $0
|
210
|
+
Cuukie::Server.set :port, ARGV[0] if ARGV[0]
|
211
|
+
Cuukie::Server.run!
|
212
|
+
end
|
data/lib/cuukie/views/index.erb
CHANGED
@@ -8,7 +8,6 @@
|
|
8
8
|
</head>
|
9
9
|
|
10
10
|
<body>
|
11
|
-
<!-- header -->
|
12
11
|
<div class="cucumber"><div id="cucumber-header"><div id="label"><h1>Cucumber Features</h1></div>
|
13
12
|
<div id="summary">
|
14
13
|
<p id="stats"></p>
|
@@ -18,7 +17,7 @@
|
|
18
17
|
</div>
|
19
18
|
<script><%= @build_status %>Colors('cucumber-header')</script>
|
20
19
|
<script type="text/javascript">
|
21
|
-
document.getElementById('duration').innerHTML = "
|
20
|
+
document.getElementById('duration').innerHTML = "<%= time_label %>: <strong><%= format_time %></strong>";
|
22
21
|
</script>
|
23
22
|
<script type="text/javascript">
|
24
23
|
document.getElementById('stats').innerHTML = "<%= @stats[:scenarios] %><br/><%= @stats[:steps] %>";
|
@@ -26,31 +25,31 @@
|
|
26
25
|
|
27
26
|
<% @features.each do |feature| %>
|
28
27
|
<div class="feature">
|
29
|
-
<h2><span class="val"
|
30
|
-
<p class="narrative"><%= feature[
|
28
|
+
<h2><span class="val"><%= feature[:keyword] %>: <%= feature[:short_name] %></span></h2>
|
29
|
+
<p class="narrative"><%= feature[:description].join '<br/>' %><bbr/r></p>
|
31
30
|
|
32
|
-
<% feature[
|
31
|
+
<% feature[:scenarios].each do |scenario| %>
|
33
32
|
<div class="scenario">
|
34
|
-
<span style="display: block;" class="scenario_file"><%= scenario[
|
35
|
-
<h3 style="cursor: pointer;" id="<%= scenario[
|
36
|
-
<span class="keyword"><%= scenario[
|
33
|
+
<span style="display: block;" class="scenario_file"><%= scenario[:file_colon_line] %></span>
|
34
|
+
<h3 style="cursor: pointer;" id="<%= scenario[:id] %>">
|
35
|
+
<span class="keyword"><%= scenario[:keyword] %>: </span><span class="val"><%= scenario[:name] %></span>
|
37
36
|
</h3>
|
38
|
-
<script><%= scenario[
|
37
|
+
<script><%= scenario[:status] %>Colors('<%= scenario[:id] %>');</script>
|
39
38
|
<ol style="display: block;">
|
40
|
-
<% scenario[
|
41
|
-
<li id="features_" class="step <%= step[
|
39
|
+
<% scenario[:steps].each do |step| %>
|
40
|
+
<li id="features_" class="step <%= step[:status] %>">
|
42
41
|
<div class="step_name">
|
43
|
-
<span class="keyword"><%= step[
|
42
|
+
<span class="keyword"><%= step[:keyword] %></span><span class="step val"><%= step[:name] %></span>
|
44
43
|
</div>
|
45
|
-
<div class="step_file"><span><%= step[
|
46
|
-
<% if step[
|
47
|
-
<div class="message"><pre><%= step[
|
48
|
-
<div class="backtrace"><pre><%= step[
|
49
|
-
<%=
|
44
|
+
<div class="step_file"><span><%= step[:file_colon_line] %></span></div>
|
45
|
+
<% if step[:exception] %>
|
46
|
+
<div class="message"><pre><%= step[:exception][:message] %></pre></div>
|
47
|
+
<div class="backtrace"><pre><%= step[:exception][:backtrace].gsub('\n', '<br/>') %></pre></div>
|
48
|
+
<%= code_snippet_for step[:exception] %>
|
50
49
|
<% end %>
|
51
|
-
<% unless step[
|
50
|
+
<% unless step[:table].empty? %>
|
52
51
|
<table>
|
53
|
-
<% step[
|
52
|
+
<% step[:table].each_with_index do |rowdata, row| %>
|
54
53
|
<tr class='step' id='row_-<%= row %>'>
|
55
54
|
<% rowdata.each_with_index do |value, col| %>
|
56
55
|
<td class="step" id="row_-<%= row %>_<%= col %>"><div><span class="step param"><%= value %></span></div></td>
|
@@ -59,8 +58,8 @@
|
|
59
58
|
<% end %>
|
60
59
|
</table>
|
61
60
|
<% end %>
|
62
|
-
<% if step[
|
63
|
-
<pre class="val"><%= step[
|
61
|
+
<% if step[:multiline_string] %>
|
62
|
+
<pre class="val"><%= step[:multiline_string] %></pre>
|
64
63
|
<% end %>
|
65
64
|
</li>
|
66
65
|
<% end %>
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'cuukie/cli'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe "The parse_options method" do
|
5
|
+
include Cuukie::Cli
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@out = ''
|
9
|
+
def @out.write(data); self << data; end
|
10
|
+
@old_stdout, $stdout = $stdout, @out
|
11
|
+
end
|
12
|
+
|
13
|
+
after :each do
|
14
|
+
$stdout = @old_stdout
|
15
|
+
end
|
16
|
+
|
17
|
+
it "recognizes --server" do
|
18
|
+
parse_options(['--server'])[:server].should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "defaults to --server = false" do
|
22
|
+
parse_options([])[:server].should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "recognizes --cuukieport" do
|
26
|
+
parse_options(['--cuukieport', '4570'])[:cuukieport].should == 4570
|
27
|
+
end
|
28
|
+
|
29
|
+
it "defaults to --cuukieport 4569" do
|
30
|
+
parse_options([])[:cuukieport].should == 4569
|
31
|
+
end
|
32
|
+
|
33
|
+
it "raises error on bad --cuukieport" do
|
34
|
+
lambda { parse_options ['--cuukieport', 'abc'] }.should raise_error
|
35
|
+
lambda { parse_options ['--cuukieport'] }.should raise_error
|
36
|
+
end
|
37
|
+
|
38
|
+
it "recognizes --showpage" do
|
39
|
+
parse_options(['--showpage'])[:showpage].should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "defaults to --showpage = false" do
|
43
|
+
parse_options([])[:showpage].should be_false
|
44
|
+
end
|
45
|
+
|
46
|
+
it "recognizes --nowait" do
|
47
|
+
parse_options(['--nowait'])[:nowait].should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "defaults to --nowait = false" do
|
51
|
+
parse_options([])[:nowait].should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "recognizes --keepserver" do
|
55
|
+
parse_options(['--keepserver'])[:keepserver].should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "defaults to --keepserver = false" do
|
59
|
+
parse_options(['--nowait'])[:keepserver].should be_false
|
60
|
+
end
|
61
|
+
|
62
|
+
it "shows the help with -h" do
|
63
|
+
parse_options ['-h']
|
64
|
+
@out.should match /Usage: cuukie \[/
|
65
|
+
end
|
66
|
+
|
67
|
+
it "shows the help with --help" do
|
68
|
+
parse_options ['--help']
|
69
|
+
@out.should match /Usage: cuukie \[/
|
70
|
+
end
|
71
|
+
|
72
|
+
it "the help text includes the version of Cuukie" do
|
73
|
+
parse_options ['-h']
|
74
|
+
@out.should match /cuukie \d+\.\d+\.\d+/
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns no result after showing help" do
|
78
|
+
parse_options(['-h']).should be_empty
|
79
|
+
end
|
80
|
+
|
81
|
+
it "consumes recognized options" do
|
82
|
+
options = ['--showpage', '--invalid', '--cuukieport', '4570']
|
83
|
+
parse_options options
|
84
|
+
options.should == ['--invalid']
|
85
|
+
end
|
86
|
+
end
|
data/spec/code_snippets_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'cuukie/
|
1
|
+
require 'cuukie/code_snippets'
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
4
|
describe "The code_snippet method" do
|
@@ -31,12 +31,12 @@ SOURCE
|
|
31
31
|
snippet['lines'].should be_nil
|
32
32
|
end
|
33
33
|
|
34
|
-
it "returns nil if the file is not valid" do
|
34
|
+
it "returns a nil snippet if the file is not valid" do
|
35
35
|
snippet = code_snippet '', 4
|
36
36
|
snippet['lines'].should be_nil
|
37
37
|
end
|
38
38
|
|
39
|
-
it "returns nil if it cannot find the line" do
|
39
|
+
it "returns a nil snippet if it cannot find the line" do
|
40
40
|
snippet = code_snippet @source.path, 7
|
41
41
|
snippet['lines'].should be_nil
|
42
42
|
end
|
@@ -87,7 +87,7 @@ SOURCE
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
it "returns nil if the extraction fails" do
|
90
|
+
it "returns a nil snippet if the extraction fails" do
|
91
91
|
backtrace_to_snippet(['abcd'])['lines'].should be_nil
|
92
92
|
end
|
93
93
|
end
|