cucumber-in-the-yard 1.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.
Files changed (55) hide show
  1. data/History.txt +3 -0
  2. data/Manifest +51 -0
  3. data/README.txt +55 -0
  4. data/Rakefile +28 -0
  5. data/cucumber-in-the-yard.gemspec +42 -0
  6. data/example/example.feature +13 -0
  7. data/example/example.step.rb +48 -0
  8. data/example/example.third.feature +30 -0
  9. data/example/second_example.feature +36 -0
  10. data/lib/city.rb +23 -0
  11. data/lib/cucumber/city_builder.rb +130 -0
  12. data/lib/yard/code_objects/cucumber_location_helper.rb +15 -0
  13. data/lib/yard/code_objects/feature.rb +24 -0
  14. data/lib/yard/code_objects/scenario.rb +25 -0
  15. data/lib/yard/code_objects/step.rb +25 -0
  16. data/lib/yard/code_objects/tags.rb +63 -0
  17. data/lib/yard/extensions.rb +38 -0
  18. data/lib/yard/handlers/base.rb +21 -0
  19. data/lib/yard/handlers/feature_handler.rb +41 -0
  20. data/lib/yard/parser/feature.rb +46 -0
  21. data/lib/yard/rake/city_task.rb +12 -0
  22. data/lib/yard/rb_extensions.rb +152 -0
  23. data/lib/yard/templates/default/feature/html/feature.erb +243 -0
  24. data/lib/yard/templates/default/feature/setup.rb +5 -0
  25. data/lib/yard/templates/default/fulldoc/html/css/common.css +182 -0
  26. data/lib/yard/templates/default/fulldoc/html/full_list.erb +34 -0
  27. data/lib/yard/templates/default/fulldoc/html/full_list_features.erb +10 -0
  28. data/lib/yard/templates/default/fulldoc/html/full_list_scenarios.erb +10 -0
  29. data/lib/yard/templates/default/fulldoc/html/full_list_stepdefinitions.erb +11 -0
  30. data/lib/yard/templates/default/fulldoc/html/full_list_steps.erb +13 -0
  31. data/lib/yard/templates/default/fulldoc/html/full_list_tagusages.erb +10 -0
  32. data/lib/yard/templates/default/fulldoc/html/index.erb +24 -0
  33. data/lib/yard/templates/default/fulldoc/html/js/cucumber.js +13 -0
  34. data/lib/yard/templates/default/fulldoc/html/setup.rb +73 -0
  35. data/lib/yard/templates/default/layout/html/headers.erb +14 -0
  36. data/lib/yard/templates/default/layout/html/search.erb +8 -0
  37. data/lib/yard/templates/default/module/html/step_transforms.erb +23 -0
  38. data/lib/yard/templates/default/module/setup.rb +5 -0
  39. data/lib/yard/templates/default/scenario/html/scenario.erb +26 -0
  40. data/lib/yard/templates/default/scenario/setup.rb +5 -0
  41. data/lib/yard/templates/default/steptransformers/html/stepdefinition.erb +38 -0
  42. data/lib/yard/templates/default/steptransformers/setup.rb +5 -0
  43. data/lib/yard/templates/default/tagusage/html/tagusage.erb +65 -0
  44. data/lib/yard/templates/default/tagusage/setup.rb +5 -0
  45. data/spec/city/feature_parser_spec_examples.rb +153 -0
  46. data/spec/city/gherkin_loader_spec.rb +39 -0
  47. data/spec/city/test.feature +36 -0
  48. data/spec/city/yard_handlers_cucumber_spec.rb +24 -0
  49. data/spec/city/yard_namespace_object_spec.rb +8 -0
  50. data/spec/city/yard_parser_cucumber_spec.rb +215 -0
  51. data/spec/city/yard_rb_extensions_spec.rb +128 -0
  52. data/spec/spec_helper.rb +5 -0
  53. data.tar.gz.sig +0 -0
  54. metadata +201 -0
  55. metadata.gz.sig +0 -0
@@ -0,0 +1,38 @@
1
+
2
+ module YARD::CodeObjects
3
+
4
+ NamespaceObject.class_eval do
5
+
6
+ attr_accessor :features
7
+
8
+ def features(opts = {})
9
+ children.select {|child| child.type == :feature }
10
+ end
11
+
12
+ attr_accessor :scenarios
13
+
14
+ def scenarios(opts = {})
15
+ children.select {|child| child.type == :scenario }
16
+ end
17
+
18
+ attr_accessor :steps
19
+
20
+ def steps(opts = {})
21
+ children.select {|child| child.type == :step }
22
+ end
23
+
24
+ attr_accessor :step_definitions
25
+
26
+ def step_definitions(opts = {})
27
+ children.select {|child| child.type == :stepdefinition }
28
+ end
29
+
30
+ attr_accessor :step_transforms
31
+
32
+ def step_transforms(opts = {})
33
+ children.select {|child| child.type == :steptransform }
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,21 @@
1
+ module YARD
2
+ module Handlers
3
+ module Cucumber
4
+
5
+ class Base < Handlers::Base
6
+ class << self
7
+ include Parser::Cucumber
8
+ def handles?(node)
9
+ handlers.any? do |a_handler|
10
+ log.debug "YARD::Handlers::Cucumber::Base#handles?"
11
+ end
12
+ end
13
+ include Parser::Cucumber
14
+ end
15
+
16
+ end
17
+
18
+ Processor.register_handler_namespace :feature, Cucumber
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ module YARD
2
+ module Handlers
3
+ module Cucumber
4
+
5
+ class FeatureHandler < Base
6
+
7
+ handles CodeObjects::Cucumber::Feature
8
+
9
+ def process
10
+
11
+ # For the background and the scenario, find the steps that have definitions
12
+ process_scenario(statement.background) if statement.background
13
+
14
+ statement.scenarios.each do |scenario|
15
+ process_scenario(scenario)
16
+ end
17
+
18
+ rescue YARD::Handlers::NamespaceMissingError
19
+ end
20
+
21
+
22
+ def process_scenario(scenario)
23
+ scenario.steps.each do |step|
24
+ owner.step_definitions.each do |stepdef|
25
+ if %r{#{stepdef.compare_value}}.match(step.value)
26
+ step.definition = stepdef
27
+ stepdef.steps << step
28
+ log.info "STEP #{step} has found its definition #{stepdef}"
29
+ break
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ module YARD::Parser::Cucumber
2
+
3
+ class FeatureParser < YARD::Parser::Base
4
+
5
+ def initialize(source, file = '(stdin)')
6
+
7
+ @builder = Cucumber::Parser::CityBuilder.new(file)
8
+ @tag_counts = {}
9
+ @tag_formatter = Gherkin::Formatter::TagCountFormatter.new(@builder, @tag_counts)
10
+ @parser = Gherkin::Parser::Parser.new(@tag_formatter, true, "root", false)
11
+
12
+ @source = source
13
+ @file = file
14
+
15
+ @feature = nil
16
+ end
17
+
18
+ def parse
19
+ begin
20
+ @parser.parse(@source, @file, 0)
21
+ @feature = @builder.ast
22
+ return nil if @feature.nil? # Nothing matched
23
+ @feature.language = @parser.i18n_language
24
+ rescue Gherkin::Lexer::LexingError, Gherkin::Parser::ParseError => e
25
+ e.message.insert(0, "#{@file}: ")
26
+ raise e
27
+ end
28
+
29
+ self
30
+ end
31
+
32
+ def tokenize
33
+
34
+ end
35
+
36
+
37
+ def enumerator
38
+ [@feature]
39
+ end
40
+
41
+ end
42
+
43
+
44
+ YARD::Parser::SourceParser.register_parser_type :feature, FeatureParser, 'feature'
45
+
46
+ end
@@ -0,0 +1,12 @@
1
+ module YARD::Rake
2
+
3
+ class CitydocTask < YardocTask
4
+
5
+ def initialize(name = :yard)
6
+ super
7
+ self.options += [ "-e", "#{ File.dirname(__FILE__)}/../../city.rb", "-p", "#{File.dirname(__FILE__)}/../templates" ]
8
+ end
9
+
10
+ end
11
+
12
+ end
@@ -0,0 +1,152 @@
1
+
2
+ module YARD::CodeObjects
3
+
4
+
5
+ #
6
+ # StepDefinitions, as implemented in a ruby file
7
+ #
8
+ class StepDefinitionObject < Base
9
+
10
+ attr_reader :keyword, :value, :compare_value, :source
11
+ attr_accessor :constants, :steps
12
+
13
+ def value=(value)
14
+ @value = format_source(value)
15
+ @constants = {}
16
+ @steps = []
17
+ end
18
+
19
+ def compare_value
20
+ base_value = value.gsub(/^\/|\/$/,'')
21
+ @constants.each do |name,value|
22
+ base_value.gsub!(/\#\{\s*#{name.to_s}\s*\}/,value.gsub(/^\/|\/$/,''))
23
+ end
24
+ base_value
25
+ end
26
+
27
+ def _value_constants(data=@value)
28
+ #Hash[*data.scan(/\#\{([^\}]+)\}/).flatten.collect {|value| [value.strip,nil]}.flatten]
29
+ data.scan(/\#\{([^\}]+)\}/).flatten.collect { |value| value.strip }
30
+ end
31
+
32
+ def constants=(value)
33
+ value.each do |val|
34
+ @constants[val.name.to_s] = val if val.respond_to?(:name) && val.respond_to?(:value)
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ class StepTransformersObject < Base
41
+
42
+ attr_reader :source
43
+ attr_accessor :definitions, :transforms
44
+
45
+ def push(stepobject)
46
+ if stepobject.is_a?(StepDefinitionObject)
47
+ @definitions = [] unless @definitions
48
+ @definitions << stepobject
49
+ elsif stepobject.ia_a?(StepTransformObject)
50
+ @transforms = [] unless @transforms
51
+ @transforms << stepobject
52
+ end
53
+ end
54
+
55
+ alias_method :<< , :push
56
+
57
+ def filename
58
+ "#{name}.html"
59
+ end
60
+
61
+ end
62
+
63
+
64
+ #
65
+ # Transforms
66
+ #
67
+ class StepTransformObject < Base
68
+ attr_reader :value
69
+
70
+ def value=(value)
71
+ @value = format_source(value)
72
+ end
73
+ end
74
+
75
+
76
+
77
+
78
+ end
79
+
80
+
81
+ class StepDefinitionHandler < YARD::Handlers::Ruby::Legacy::Base
82
+ MATCH = /^((When|Given|And|Then)\s*(\/[^\/]+\/).+)$/
83
+ handles MATCH
84
+
85
+ @@unique_name = 0
86
+
87
+ def process
88
+
89
+ keyword = statement.tokens.to_s[MATCH,2]
90
+ step_definition = statement.tokens.to_s[MATCH,3]
91
+ @@unique_name = @@unique_name + 1
92
+
93
+ stepdef_instance = StepDefinitionObject.new(namespace, "StepDefinition_#{@@unique_name}") {|o| o.source = statement.block.to_s ; o.value = step_definition ; o.keyword = keyword}
94
+
95
+ begin
96
+ # Look for all constants within the step definitions
97
+ stepdef_instance.constants = stepdef_instance._value_constants.each do |stepdef_constant|
98
+ owner.constants.each do |constant|
99
+ if stepdef_constant.to_sym == constant.name
100
+ log.info "Replacing #{constant.name} with its value in the step definition #{stepdef_instance.value}"
101
+ stepdef_instance.constants[constant.name] = unpack_constants(constant.value)
102
+ end
103
+ end
104
+ end
105
+
106
+ rescue Exception => e
107
+ log.error "Failed to link step definition to constants. This will make step definition to step linking impossible if constants are present. #{e}"
108
+ end
109
+
110
+
111
+ obj = register stepdef_instance
112
+
113
+
114
+ parse_block :owner => obj
115
+ rescue YARD::Handlers::NamespaceMissingError
116
+ end
117
+
118
+
119
+ def unpack_constants(constant_value)
120
+ constant_value.scan(/\#\{([^\}]+)\}/).flatten.collect { |value| value.strip }.each do |inner_constant|
121
+ inner_constant_match = owner.constants.find {|constant| constant.name.to_s == inner_constant }
122
+ if inner_constant_match
123
+ constant_value.gsub!(/\#\{#{inner_constant}\}/,unpack_constants(inner_constant_match.value))
124
+ end
125
+ end
126
+
127
+ constant_value.gsub!(/^('|"|\/)|('|"|\/)$/,'')
128
+ constant_value
129
+ end
130
+
131
+ end
132
+
133
+
134
+ class StepTransformHandler < YARD::Handlers::Ruby::Legacy::Base
135
+ MATCH = /^Transform\s*(\/[^\/]+\/).+$/
136
+ handles MATCH
137
+
138
+ @@unique_name = 0
139
+
140
+ def process
141
+ transformDefinition = statement.tokens.to_s[MATCH,1]
142
+ #log.debug "#process - transformDefinition = #{transformDefinition}"
143
+ @@unique_name = @@unique_name + 1
144
+
145
+ obj = register StepTransformObject.new(namespace, "StepTransform_#{@@unique_name}") {|o| o.source = statement.block.to_s ; o.value = transformDefinition }
146
+
147
+ parse_block :owner => obj
148
+
149
+ rescue YARD::Handlers::NamespaceMissingError
150
+ end
151
+ end
152
+
@@ -0,0 +1,243 @@
1
+ <div class="feature">
2
+ <div class="title">
3
+ <span class="pre"><%= @feature.keyword %>:</span>
4
+ <span class="name"><%= @feature.value %></span>
5
+ </div>
6
+
7
+ <div class="meta">
8
+ <div class="file"><%= h(@feature.file) %></div>
9
+ <div class="tags">
10
+ <% @feature.tags.each do |tag| %>
11
+ <a href="tag_<%= tag.value %>.html"><%= tag.value %></a>
12
+ <% end %>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="description">
17
+ <%= @feature.description %>
18
+ </div>
19
+
20
+ <% if @feature.background %>
21
+
22
+ <script type="text/javascript" charset="utf-8">
23
+ $(function() {
24
+ $('#background .toggle').click(function() {
25
+ $("#backgroundSteps").toggle('blind');
26
+
27
+ var stateIndicator = $('#background a.toggle')[0]
28
+
29
+ stateIndicator.innerHTML = (stateIndicator.innerHTML === '+' ? '-' : '+');
30
+ return false;
31
+ });
32
+ });
33
+ </script>
34
+
35
+ <div id="background">
36
+ <div class="title">
37
+ <a class="toggle">-</a>
38
+ <span class="pre">Background</span>
39
+ </div>
40
+ <% unless @feature.background.description.empty? %>
41
+ <div class="description">
42
+ <%= h(@feature.background.description.join("\n<br/>")) %>
43
+ </div>
44
+ <% end %>
45
+
46
+ <!-- Background and scenario should have the same display with some minor changes -->
47
+
48
+ <div id="backgroundSteps" class="steps">
49
+ <% if @feature.background.steps %>
50
+ <% @feature.background.steps.each_with_index do |step,index| %>
51
+ <div <%= "id='backgroundStep#{index}'" %> class="step <%= (index + 1) % 2 == 0 ? 'even' : 'odd' %>">
52
+ <span class="predicate"><%= step.keyword %></span>
53
+
54
+ <% if step.definition %>
55
+ <script type="text/javascript" charset="utf-8">
56
+ $('<%= "#backgroundStep#{index}" %>').hover(
57
+ function () { $('<%= "#backgroundStep#{index} div.details" %>').toggle('slide'); },
58
+ function () { $('<%= "#backgroundStep#{index} div.details" %>').toggle('slide'); }
59
+ );
60
+
61
+ $('<%= "#backgroundStep#{index} div.details" %>').show();
62
+
63
+ </script>
64
+ <span class="defined">
65
+ <a href="steptransformers.html#<%= step.definition.name %>"><%= h(step.value) %></a>
66
+ <div class="details" style="display: none;">
67
+ (<%= h("#{step.definition.files.first.first}:#{step.definition.files.first.last}") %>)
68
+ </div>
69
+ </span>
70
+ <% else %>
71
+ <span class="undefined"><%= h(step.value) %></span>
72
+ <% end %>
73
+
74
+ <% if step.has_text? %>
75
+ <div class="text">
76
+ <%= h(step.text) %>
77
+ </div>
78
+ <% end %>
79
+
80
+ <% if step.has_table? %>
81
+ <div class="multiline">
82
+ <table style="">
83
+
84
+ <thead>
85
+ <tr>
86
+ <% step.table.each_with_index do |column,column_index| %>
87
+ <th class="<%= (column_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.to_s.strip) %></td>
88
+ <% end %>
89
+ </tr>
90
+ </thead>
91
+
92
+ <% step.table[1..-1].each do |row| %>
93
+ <tr>
94
+ <% row.split("|").each_with_index do |column,column_index| %>
95
+ <td class="<%= (column_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.to_s.strip) %></td>
96
+ <% end %>
97
+ </tr>
98
+ <% end %>
99
+ </table>
100
+ </div>
101
+
102
+ <% end %>
103
+
104
+ </div>
105
+
106
+ <% end %>
107
+ <% else %>
108
+ <span>No Steps Defined</span>
109
+ <% end %>
110
+ </div>
111
+
112
+ </div>
113
+ <% end %>
114
+
115
+ <% if @feature.scenarios %>
116
+ <% @feature.scenarios.each_with_index do |scenario,scenario_index| %>
117
+
118
+ <script type="text/javascript" charset="utf-8">
119
+ $(function() {
120
+ $('#scenario<%=scenario_index%> .toggle').click(function() {
121
+ $("#scenario<%=scenario_index%>Steps").toggle('blind');
122
+
123
+ var stateIndicator = $('#scenario<%=scenario_index%> a.toggle')[0]
124
+
125
+ stateIndicator.innerHTML = (stateIndicator.innerHTML === '+' ? '-' : '+');
126
+ return false;
127
+ });
128
+ });
129
+ </script>
130
+
131
+ <div <%= "id='scenario#{scenario_index}'" %> class="scenario">
132
+ <div class="title">
133
+ <a class="toggle">-</a>
134
+ <span class="pre">Scenario<%= !scenario.examples.empty? ? " Outline" : "" %>:</span>
135
+ <span class="name"><%= h(scenario.value) %></span>
136
+ </div>
137
+ <div class="meta">
138
+ <div class="file">Line: <%= scenario.files.first.last %></div>
139
+ <% unless scenario.tags.empty? %>
140
+ <div class="tags">
141
+ <% scenario.tags.each do |tag| %>
142
+ <a href="tag_<%= tag.value %>.html"><%= tag.value %></a>
143
+ <% end %>
144
+ </div>
145
+ <% end%>
146
+ </div>
147
+ <% unless scenario.description.empty? %>
148
+ <div class="description">
149
+ <%= h(scenario.description.join("\n<br/>")) %>
150
+ </div>
151
+ <% end %>
152
+
153
+ <div <%= "id='scenario#{scenario_index}Steps'" %> class="steps">
154
+ <% if scenario.steps %>
155
+ <% scenario.steps.each_with_index do |step,index| %>
156
+ <div <%= "id='scenario#{scenario_index}Step#{index}'" %> class="step <%= (index + 1) % 2 == 0 ? 'even' : 'odd' %>">
157
+ <span class="predicate"><%= step.keyword %></span>
158
+
159
+ <% if step.definition %>
160
+ <script type="text/javascript" charset="utf-8">
161
+ $('<%= "#scenario#{scenario_index}Step#{index}" %>').hover(
162
+ function () { $('<%= "#scenario#{scenario_index}Step#{index} div.details" %>').toggle('slide'); },
163
+ function () { $('<%= "#scenario#{scenario_index}Step#{index} div.details" %>').toggle('slide'); }
164
+ );
165
+
166
+ $('<%= "#scenario#{scenario_index}Step#{index} div.details" %>').show();
167
+
168
+ </script>
169
+ <span class="defined">
170
+ <a href="steptransformers.html#<%= step.definition.name %>"><%= h(step.value) %></a>
171
+ <div class="details" style="display: none;">
172
+ (<%= h("#{step.definition.files.first.first}:#{step.definition.files.first.last}") %>)
173
+ </div>
174
+ </span>
175
+ <% else %>
176
+ <span class="undefined"><%= h(step.value) %></span>
177
+ <% end %>
178
+
179
+ <% if step.has_text? %>
180
+ <div class="text">
181
+ <%= h(step.text) %>
182
+ </div>
183
+ <% end %>
184
+
185
+ <% if step.has_table? %>
186
+ <div class="multiline">
187
+ <table style="">
188
+
189
+ <thead>
190
+ <tr>
191
+ <% step.table.first.each_with_index do |column,column_index| %>
192
+ <th class="<%= (column_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.strip) %></td>
193
+ <% end %>
194
+ </tr>
195
+ </thead>
196
+
197
+ <% step.table[1..-1].each do |row| %>
198
+ <tr>
199
+ <% row.each_with_index do |column,column_index| %>
200
+ <td class="<%= (column_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.strip) %></td>
201
+ <% end %>
202
+ </tr>
203
+ <% end %>
204
+ </table>
205
+ </div>
206
+
207
+ <% end %>
208
+
209
+
210
+ </div>
211
+
212
+ <% end %>
213
+ <% else %>
214
+ <span>No Steps Defined</span>
215
+ <% end %>
216
+ </div>
217
+
218
+ <% if scenario.examples && !scenario.examples.empty? %>
219
+ <div class="outline">
220
+ <table>
221
+ <thead>
222
+ <tr>
223
+ <th class="odd" colspan="10"><%= h(scenario.examples.first.first.to_s.strip) %></td>
224
+ </tr>
225
+ </thead>
226
+ <% scenario.examples.first.find {|example| example.is_a?(Array) }.each_with_index do |row,row_index| %>
227
+ <tr>
228
+ <% row.each do |column| %>
229
+ <td class="<%= (row_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.to_s.strip) %></td>
230
+ <% end %>
231
+ </tr>
232
+ <% end %>
233
+ </table>
234
+ </div>
235
+
236
+ <% end %>
237
+
238
+ </div>
239
+ <% end %>
240
+ <% end %>
241
+
242
+ </div>
243
+
@@ -0,0 +1,5 @@
1
+ def init
2
+ super
3
+ sections.push :feature
4
+ @feature = object
5
+ end