cuukie 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|