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.
@@ -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: #fcfb98;
185
+ background: #aaaaaa;
186
186
  color: #131313;
187
187
  }
188
188
  .cucumber ol li.message, td ol li.message, th ol li.message {
@@ -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
+ }
@@ -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, nil
9
- set :duration, '?'
10
- set :stats, {:scenarios => '', :steps => ''}
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 = nil
23
- settings.stats = {:scenarios => '', :steps => ''}
24
- settings.duration = '?'
25
- settings.build_status = nil
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['description'] = feature['description'].split("\n")
31
- feature['scenarios'] = []
32
- feature['id'] = settings.features.size + 1
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['steps'] = []
40
- scenario['id'] = "scenario_#{current_feature['id']}_#{current_feature['scenarios'].size + 1}"
41
- current_feature['scenarios'] << scenario
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 '/before_step_result' do
52
+ post '/before_step' do
46
53
  step = read_from_request
47
- step['table'] = []
48
- current_scenario['steps'] << step
54
+ step[:table] = []
55
+ step[:status] = 'undefined'
56
+ current_scenario[:steps] << step
49
57
  'OK'
50
58
  end
51
-
52
- post '/exception' do
53
- current_step['exception'] = read_from_request
59
+
60
+ post '/before_table_row' do
61
+ current_step[:table] << []
54
62
  'OK'
55
63
  end
56
64
 
57
- post '/after_step_result' do
58
- current_step.merge! read_from_request
59
- if current_step['status'] == 'failed'
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 '/after_steps' do
69
- if current_scenario['steps'].all? {|step| step['status'] == 'skipped' }
70
- current_scenario['status'] = 'skipped'
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 '/before_table_row' do
77
- current_step['table'] << []
77
+ post '/exception' do
78
+ current_step[:exception] = read_from_request
78
79
  'OK'
79
80
  end
80
81
 
81
- post '/table_cell_value' do
82
- data = read_from_request
83
- current_step['table'].last << data['value']
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 '/doc_string' do
88
- data = read_from_request
89
- current_step['multiline_string'] = data['multiline_string']
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
- data = read_from_request
95
- min, sec = data['duration'].to_f.divmod(60)
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 snippet(exception)
107
- return '' unless exception['raw_lines']
112
+ def code_snippet_for(exception)
113
+ return '' unless exception[:raw_lines]
108
114
  result = '<pre class="ruby"><code>'
109
- linenum = exception['first_line']
110
- html_lines = htmlize(exception['raw_lines']).split "\n"
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['marked_line']
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 { 'steps' => [{}] } if current_feature['scenarios'].empty?
131
- current_feature['scenarios'].last
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['steps'].last
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['scenarios'] }
161
+ settings.features.each {|feature| scenarios.concat feature[:scenarios] }
141
162
 
142
163
  result = {:scenarios => String.new, :steps => String.new}
143
- result[:scenarios] << dump_count(scenarios.size, "scenario")
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['steps'] }
148
- result[:steps] << dump_count(steps.size, "step")
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['status'] == status }
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 = data.clone
176
- result.each do |k, v|
177
- result[k] = escape_html(v) if v.class == String && k !~ /^raw_/
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
@@ -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 = "Finished in <strong><%= @duration %> seconds</strong>";
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">Feature: <%= feature['short_name'] %></span></h2>
30
- <p class="narrative"><%= feature['description'].join '<br/>' %><bbr/r></p>
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['scenarios'].each do |scenario| %>
31
+ <% feature[:scenarios].each do |scenario| %>
33
32
  <div class="scenario">
34
- <span style="display: block;" class="scenario_file"><%= scenario['file_colon_line'] %></span>
35
- <h3 style="cursor: pointer;" id="<%= scenario['id'] %>">
36
- <span class="keyword"><%= scenario['keyword'] %>: </span><span class="val"><%= scenario['name'] %></span>
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['status'] %>Colors('<%= scenario['id'] %>');</script>
37
+ <script><%= scenario[:status] %>Colors('<%= scenario[:id] %>');</script>
39
38
  <ol style="display: block;">
40
- <% scenario['steps'].each do |step| %>
41
- <li id="features_" class="step <%= step['status'] %>">
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['keyword'] %></span><span class="step val"><%= step['name'] %></span>
42
+ <span class="keyword"><%= step[:keyword] %></span><span class="step val"><%= step[:name] %></span>
44
43
  </div>
45
- <div class="step_file"><span><%= step['file_colon_line'] %></span></div>
46
- <% if step['exception'] %>
47
- <div class="message"><pre><%= step['exception']['message'] %></pre></div>
48
- <div class="backtrace"><pre><%= step['exception']['backtrace'].gsub('\n', '<br/>') %></pre></div>
49
- <%= snippet step['exception'] %>
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['table'].empty? %>
50
+ <% unless step[:table].empty? %>
52
51
  <table>
53
- <% step['table'].each_with_index do |rowdata, row| %>
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['multiline_string'] %>
63
- <pre class="val"><%= step['multiline_string'] %></pre>
61
+ <% if step[:multiline_string] %>
62
+ <pre class="val"><%= step[:multiline_string] %></pre>
64
63
  <% end %>
65
64
  </li>
66
65
  <% end %>
@@ -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
@@ -1,4 +1,4 @@
1
- require 'cuukie/cucumber/formatter/code_snippets'
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