yard-lucid 0.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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +87 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +13 -0
  11. data/bin/setup +8 -0
  12. data/lib/docserver/default/fulldoc/html/js/lucid.js +69 -0
  13. data/lib/docserver/default/layout/html/headers.erb +13 -0
  14. data/lib/docserver/doc_server/full_list/html/full_list.erb +37 -0
  15. data/lib/docserver/doc_server/full_list/html/setup.rb +19 -0
  16. data/lib/lucid/gherkin_repr.rb +307 -0
  17. data/lib/templates/default/feature/html/docstring.erb +3 -0
  18. data/lib/templates/default/feature/html/feature.erb +39 -0
  19. data/lib/templates/default/feature/html/no_steps_defined.erb +1 -0
  20. data/lib/templates/default/feature/html/outline.erb +52 -0
  21. data/lib/templates/default/feature/html/scenario.erb +52 -0
  22. data/lib/templates/default/feature/html/setup.rb +51 -0
  23. data/lib/templates/default/feature/html/steps.erb +37 -0
  24. data/lib/templates/default/feature/html/table.erb +19 -0
  25. data/lib/templates/default/featuredirectory/html/alpha_table.erb +30 -0
  26. data/lib/templates/default/featuredirectory/html/directory.erb +30 -0
  27. data/lib/templates/default/featuredirectory/html/setup.rb +41 -0
  28. data/lib/templates/default/featuretags/html/namespace.erb +127 -0
  29. data/lib/templates/default/featuretags/html/setup.rb +33 -0
  30. data/lib/templates/default/fulldoc/html/css/lucid.css +224 -0
  31. data/lib/templates/default/fulldoc/html/directories.erb +27 -0
  32. data/lib/templates/default/fulldoc/html/full_list_featuredirectories.erb +11 -0
  33. data/lib/templates/default/fulldoc/html/full_list_features.erb +36 -0
  34. data/lib/templates/default/fulldoc/html/full_list_stepdefinitions.erb +20 -0
  35. data/lib/templates/default/fulldoc/html/full_list_steps.erb +20 -0
  36. data/lib/templates/default/fulldoc/html/full_list_tags.erb +16 -0
  37. data/lib/templates/default/fulldoc/html/js/lucid.js +284 -0
  38. data/lib/templates/default/fulldoc/html/setup.rb +199 -0
  39. data/lib/templates/default/layout/html/setup.rb +111 -0
  40. data/lib/templates/default/specifications/html/alpha_table.erb +26 -0
  41. data/lib/templates/default/specifications/html/setup.rb +49 -0
  42. data/lib/templates/default/specifications/html/specifications.erb +46 -0
  43. data/lib/templates/default/steptransformers/html/header.erb +12 -0
  44. data/lib/templates/default/steptransformers/html/index.erb +9 -0
  45. data/lib/templates/default/steptransformers/html/setup.rb +92 -0
  46. data/lib/templates/default/steptransformers/html/transformers.erb +74 -0
  47. data/lib/templates/default/steptransformers/html/undefinedsteps.erb +26 -0
  48. data/lib/templates/default/tag/html/alpha_table.erb +32 -0
  49. data/lib/templates/default/tag/html/setup.rb +26 -0
  50. data/lib/templates/default/tag/html/tag.erb +31 -0
  51. data/lib/yard/code_objects/lucid/base.rb +23 -0
  52. data/lib/yard/code_objects/lucid/feature.rb +12 -0
  53. data/lib/yard/code_objects/lucid/namespace_object.rb +43 -0
  54. data/lib/yard/code_objects/lucid/scenario.rb +20 -0
  55. data/lib/yard/code_objects/lucid/scenario_outline.rb +66 -0
  56. data/lib/yard/code_objects/lucid/step.rb +32 -0
  57. data/lib/yard/code_objects/lucid/tag.rb +21 -0
  58. data/lib/yard/code_objects/step_definition.rb +3 -0
  59. data/lib/yard/code_objects/step_transform.rb +3 -0
  60. data/lib/yard/code_objects/step_transformer.rb +85 -0
  61. data/lib/yard/handlers/lucid/base.rb +19 -0
  62. data/lib/yard/handlers/lucid/feature_handler.rb +102 -0
  63. data/lib/yard/handlers/step_definition_handler.rb +67 -0
  64. data/lib/yard/handlers/step_transform_handler.rb +28 -0
  65. data/lib/yard/parser/lucid/feature.rb +58 -0
  66. data/lib/yard/server/adapter.rb +32 -0
  67. data/lib/yard/server/commands/list_command.rb +25 -0
  68. data/lib/yard/server/router.rb +23 -0
  69. data/lib/yard/templates/helpers/base_helper.rb +22 -0
  70. data/lib/yard-lucid/version.rb +3 -0
  71. data/lib/yard-lucid.rb +36 -0
  72. data/yard-lucid.gemspec +38 -0
  73. metadata +215 -0
@@ -0,0 +1,307 @@
1
+ module Lucid
2
+ module Parser
3
+ class GherkinRepr < Gherkin::AstBuilder
4
+ # This class serves as the Gherkin representation for each feature file
5
+ # that is found. Specifically, the top level Feature object of each such
6
+ # file is given a representation. The Gherkin parser calls the various
7
+ # methods within in this class as it finds Gherkin-style elements.
8
+ #
9
+ # This process is similar to how most Gherkin tools generate an Abstract
10
+ # Syntax Tree. Here what gets generated are various YARD::CodeObjects.
11
+ #
12
+ # A namespace is specified and that is the place in the YARD namespacing
13
+ # where all features generated will reside. The namespace specified
14
+ # is the root namespace. This is the equivalent of the top-level
15
+ # directory holding all of the feature files.
16
+ def initialize(file)
17
+ super()
18
+ @namespace = YARD::CodeObjects::Lucid::LUCID_NAMESPACE
19
+ find_or_create_namespace(file)
20
+ @file = file
21
+ end
22
+
23
+ # This method returns the feature that has been defined. This is the
24
+ # final method that is called when all the work is done. What gets
25
+ # returned is the complete Feature object that was built.
26
+ def ast
27
+ feature(get_result) unless @feature
28
+ @feature
29
+ end
30
+
31
+ # Features that are found in sub-directories are considered to be in
32
+ # another namespace. The rationale is that with Gherkin-supporting test
33
+ # tools, when you execute a test run on a directory, any sub-directories
34
+ # of features will be executed with that directory.
35
+ #
36
+ # Part of the process involves the discovery of a README.md file within
37
+ # the specified directory of the feature file and loads that file as the
38
+ # description for the namespace. This is useful if you want to give a
39
+ # particular directory some supporting documentation.
40
+ def find_or_create_namespace(file)
41
+ @namespace = YARD::CodeObjects::Lucid::LUCID_NAMESPACE
42
+
43
+ File.dirname(file).split('/').each do |directory|
44
+ @namespace = @namespace.children.find { |child| child.is_a?(YARD::CodeObjects::Lucid::FeatureDirectory) && child.name.to_s == directory } ||
45
+ @namespace = YARD::CodeObjects::Lucid::FeatureDirectory.new(@namespace,directory) { |dir| dir.add_file(directory) }
46
+ end
47
+
48
+ if @namespace.description == "" && File.exists?("#{File.dirname(file)}/README.md")
49
+ @namespace.description = File.read("#{File.dirname(file)}/README.md")
50
+ end
51
+ end
52
+
53
+ # A given tag can be searched for, within the YARD Registry, to see if it
54
+ # exists and, if it doesn't, to create it. The logic will note that the
55
+ # tag was used in the given file at whatever the current line is and then
56
+ # add the tag to the current scenario or feature. It's also necessary to
57
+ # add the feature or scenario to the tag.
58
+ def find_or_create_tag(tag_name, parent)
59
+ tag_code_object = YARD::Registry.all(:tag).find { |tag| tag.value == tag_name } ||
60
+ YARD::CodeObjects::Lucid::Tag.new(YARD::CodeObjects::Lucid::LUCID_TAG_NAMESPACE,tag_name.gsub('@','')) { |t| t.owners = [] ; t.value = tag_name }
61
+
62
+ tag_code_object.add_file(@file,parent.line)
63
+
64
+ parent.tags << tag_code_object unless parent.tags.find { |tag| tag == tag_code_object }
65
+ tag_code_object.owners << parent unless tag_code_object.owners.find { |owner| owner == parent }
66
+ end
67
+
68
+ # Each feature found will call this method, generating the feature object.
69
+ # This happens only once, as the Gherkin parser does not allow for
70
+ # multiple features per feature file.
71
+ def feature(document)
72
+ #log.debug "FEATURE"
73
+ feature = document[:feature]
74
+ return unless document[:feature]
75
+ return if has_exclude_tags?(feature[:tags].map { |t| t[:name].gsub(/^@/, '') })
76
+
77
+ @feature = YARD::CodeObjects::Lucid::Feature.new(@namespace,File.basename(@file.gsub('.feature','').gsub('.','_'))) do |f|
78
+ f.comments = feature[:comments] ? feature[:comments].map{|comment| comment[:text]}.join("\n") : ''
79
+ f.description = feature[:description] || ''
80
+ f.add_file(@file,feature[:location][:line])
81
+ f.keyword = feature[:keyword]
82
+ f.value = feature[:name]
83
+ f.tags = []
84
+
85
+ feature[:tags].each {|feature_tag| find_or_create_tag(feature_tag[:name],f) }
86
+ end
87
+ feature[:children].each { |s|
88
+ case s[:type]
89
+ when :Background
90
+ background(s)
91
+ when :ScenarioOutline
92
+ scenario_outline(s)
93
+ when :Scenario
94
+ scenario(s)
95
+ end
96
+ }
97
+ end
98
+
99
+ # Called when a Background has been found. Note that to Gherkin a
100
+ # Background is really just another type of Scenario. The difference is
101
+ # that backgrounds get special processing during execution.
102
+ def background(background)
103
+ @background = YARD::CodeObjects::Lucid::Scenario.new(@feature,"background") do |b|
104
+ b.comments = background[:comments] ? background[:comments].map{|comment| comment.value}.join("\n") : ''
105
+ b.description = background[:description] || ''
106
+ b.keyword = background[:keyword]
107
+ b.value = background[:name]
108
+ b.add_file(@file,background[:location][:line])
109
+ end
110
+
111
+ @feature.background = @background
112
+ @background.feature = @feature
113
+ @step_container = @background
114
+ background[:steps].each { |s| step(s) }
115
+ end
116
+
117
+ # Called when a scenario has been found. This will create a scenario
118
+ # object, assign the scenario object to the feature object (and also
119
+ # assigne the feature object to the scenario object), as well as find
120
+ # or create tags that are associated with the scenario.
121
+ #
122
+ # The scenario is set as a type called a @step_container. This means
123
+ # that any steps found before another scenario is defined belong to this
124
+ # scenario.
125
+ def scenario(statement)
126
+ return if has_exclude_tags?(statement[:tags].map { |t| t[:name].gsub(/^@/, '') })
127
+
128
+ scenario = YARD::CodeObjects::Lucid::Scenario.new(@feature,"scenario_#{@feature.scenarios.length + 1}") do |s|
129
+ s.comments = statement[:comments] ? statement[:comments].map{|comment| comment.value}.join("\n") : ''
130
+ s.description = statement[:description] || ''
131
+ s.add_file(@file,statement[:location][:line])
132
+ s.keyword = statement[:keyword]
133
+ s.value = statement[:name]
134
+
135
+ statement[:tags].each {|scenario_tag| find_or_create_tag(scenario_tag[:name],s) }
136
+ end
137
+
138
+ scenario.feature = @feature
139
+ @feature.scenarios << scenario
140
+ @step_container = scenario
141
+ statement[:steps].each { |s| step(s) }
142
+ end
143
+
144
+ # Called when a scenario outline is found. This is very similar to a
145
+ # scenario but, to Gherkin, the ScenarioOutline is still a distinct
146
+ # object. The reason for this is because it can contain multiple
147
+ # different example groups that can contain different values.
148
+ def scenario_outline(statement)
149
+ return if has_exclude_tags?(statement[:tags].map { |t| t[:name].gsub(/^@/, '') })
150
+
151
+ outline = YARD::CodeObjects::Lucid::ScenarioOutline.new(@feature,"scenario_#{@feature.scenarios.length + 1}") do |s|
152
+ s.comments = statement[:comments] ? statement[:comments].map{|comment| comment.value}.join("\n") : ''
153
+ s.description = statement[:description] || ''
154
+ s.add_file(@file,statement[:location][:line])
155
+ s.keyword = statement[:keyword]
156
+ s.value = statement[:name]
157
+
158
+ statement[:tags].each {|scenario_tag| find_or_create_tag(scenario_tag[:name],s) }
159
+ end
160
+
161
+ outline.feature = @feature
162
+ @feature.scenarios << outline
163
+ @step_container = outline
164
+ statement[:steps].each { |s| step(s) }
165
+ statement[:examples].each { |e| examples(e) }
166
+ end
167
+
168
+ # Examples for a scenario outline are found. From a parsing perspective,
169
+ # the logic differs here from how a Gherkin-supporting tool parses for
170
+ # execution. For the needs of being lucid, each of the examples are
171
+ # expanded out as individual scenarios and step definitions. This is done
172
+ # so that it is possible to ensure that all variations of the scenario
173
+ # outline defined are displayed.
174
+ def examples(examples)
175
+ example = YARD::CodeObjects::Lucid::ScenarioOutline::Examples.new(:keyword => examples[:keyword],
176
+ :name => examples[:name],
177
+ :line => examples[:location][:line],
178
+ :comments => examples[:comments] ? examples.comments.map{|comment| comment.value}.join("\n") : '',
179
+ :rows => []
180
+ )
181
+ example.rows = [examples[:tableHeader][:cells].map{ |c| c[:value] }] if examples[:tableHeader]
182
+ example.rows += matrix(examples[:tableBody]) if examples[:tableBody]
183
+
184
+ @step_container.examples << example
185
+
186
+ # For each example data row, a new scenario must be generated using the
187
+ # current scenario as the template.
188
+
189
+ example.data.length.times do |row_index|
190
+
191
+ # Generate a copy of the scenario.
192
+
193
+ scenario = YARD::CodeObjects::Lucid::Scenario.new(@step_container,"example_#{@step_container.scenarios.length + 1}") do |s|
194
+ s.comments = @step_container.comments
195
+ s.description = @step_container.description
196
+ s.add_file(@file,@step_container.line_number)
197
+ s.keyword = @step_container.keyword
198
+ s.value = "#{@step_container.value} (#{@step_container.scenarios.length + 1})"
199
+ end
200
+
201
+ # Generate a copy of the scenario steps.
202
+
203
+ @step_container.steps.each do |step|
204
+ step_instance = YARD::CodeObjects::Lucid::Step.new(scenario,step.line_number) do |s|
205
+ s.keyword = step.keyword.dup
206
+ s.value = step.value.dup
207
+ s.add_file(@file,step.line_number)
208
+
209
+ s.text = step.text.dup if step.has_text?
210
+ s.table = clone_table(step.table) if step.has_table?
211
+ end
212
+
213
+ # Look at the particular data for the example row and do a simple
214
+ # find and replace of the <key> with the associated values. It's
215
+ # necessary ot handle empty cells in an example table.
216
+
217
+ example.values_for_row(row_index).each do |key,text|
218
+ text ||= ""
219
+ step_instance.value.gsub!("<#{key}>",text)
220
+ step_instance.text.gsub!("<#{key}>",text) if step_instance.has_text?
221
+ step_instance.table.each{ |row| row.each { |col| col.gsub!("<#{key}>",text) } } if step_instance.has_table?
222
+ end
223
+
224
+ # Connect the steps that have been created to the scenario that was
225
+ # created and then add the steps to the scenario.
226
+
227
+ step_instance.scenario = scenario
228
+ scenario.steps << step_instance
229
+ end
230
+
231
+ # Add the scenario to the list of scenarios maintained by the feature
232
+ # and add the feature to the scenario.
233
+
234
+ scenario.feature = @feature
235
+ @step_container.scenarios << scenario
236
+ end
237
+ end
238
+
239
+ # Called when a step is found. The logic here is that each step is
240
+ # referred to a table owner. This is the case even though not all steps
241
+ # have a table or multliline arguments associated with them.
242
+ #
243
+ # If a multiline string is present with the step it is included as the
244
+ # text of the step. If the step has a table it is added to the step using
245
+ # the same method used by the standard Gherkin model.
246
+ def step(step)
247
+ @table_owner = YARD::CodeObjects::Lucid::Step.new(@step_container,"#{step[:location][:line]}") do |s|
248
+ s.keyword = step[:keyword]
249
+ s.value = step[:text]
250
+ s.add_file(@file,step[:location][:line])
251
+ end
252
+
253
+ @table_owner.comments = step[:comments] ? step[:comments].map{|comment| comment.value}.join("\n") : ''
254
+
255
+ multiline_arg = step[:argument]
256
+
257
+ case(multiline_arg[:type])
258
+ when :DocString
259
+ @table_owner.text = multiline_arg[:content]
260
+ when :DataTable
261
+ @table_owner.table = matrix(multiline_arg[:rows])
262
+ end if multiline_arg
263
+
264
+ @table_owner.scenario = @step_container
265
+ @step_container.steps << @table_owner
266
+ end
267
+
268
+ # This is necessary because it is defined in many of the tooling that
269
+ # supports Gherkin, but there are no events for the end-of-file.
270
+ def eof
271
+ end
272
+
273
+ # This method exists when there is a syntax error. That matters for
274
+ # Gherkin execution but not for the parsing being done here.
275
+ def syntax_error(state, event, legal_events, line)
276
+ end
277
+
278
+ private
279
+
280
+ def matrix(gherkin_table)
281
+ gherkin_table.map {|gherkin_row| gherkin_row[:cells].map{ |cell| cell[:value] } }
282
+ end
283
+
284
+ # This helper method is used to deteremine what class is the current
285
+ # Gherkin class.
286
+ def gherkin_multiline_string_class
287
+ if defined?(Gherkin::Formatter::Model::PyString)
288
+ Gherkin::Formatter::Model::PyString
289
+ elsif defined?(Gherkin::Formatter::Model::DocString)
290
+ Gherkin::Formatter::Model::DocString
291
+ else
292
+ raise "Unable to find a suitable class in the Gherkin Library to parse the multiline step data."
293
+ end
294
+ end
295
+
296
+ def clone_table(base)
297
+ base.map {|row| row.map {|cell| cell.dup }}
298
+ end
299
+
300
+ def has_exclude_tags?(tags)
301
+ if YARD::Config.options["yard-lucid"] and YARD::Config.options["yard-lucid"]["exclude_tags"]
302
+ return true unless (YARD::Config.options["yard-lucid"]["exclude_tags"] & tags).empty?
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,3 @@
1
+ <div class="text">
2
+ <%= htmlify_with_newlines @step.text %>
3
+ </div>
@@ -0,0 +1,39 @@
1
+ <script type="text/javascript" charset="utf-8">
2
+ $(function() {
3
+ $(".developer").hide();
4
+ $(".scenario .title").css('cursor','pointer');
5
+ });
6
+ </script>
7
+
8
+ <div class="feature">
9
+ <div class="title">
10
+ <a id="view" class="control" href="#">[More Detail]</a>
11
+ <a id="expand" class="control" href="#">[Collapse All]</a>
12
+ <div style="clear: right;"></div>
13
+ <span class="pre"><%= @feature.keyword %>:</span>
14
+ <span class="name"><%= @feature.value %></span>
15
+ </div>
16
+
17
+ <% if @feature.comments.length > 0 %>
18
+ <div class="comments developer">
19
+ <%= htmlify_with_newlines @feature.comments %>
20
+ </div>
21
+ <% end %>
22
+
23
+ <div class="description">
24
+ <%= htmlify_with_newlines @feature.description %>
25
+ </div>
26
+
27
+ <div class="meta">
28
+ <div class="file developer"><%= h(@feature.file) %></div>
29
+ <div style="clear: right;"></div>
30
+ <div class="tags developer">
31
+ <% @feature.tags.each do |tag| %>
32
+ <a href="<%= url_for tag %>"><%= tag.value %></a>
33
+ <% end %>
34
+ </div>
35
+ </div>
36
+
37
+ <%= yieldall %>
38
+ </div>
39
+
@@ -0,0 +1 @@
1
+ <div class="none">No Steps Defined</div>
@@ -0,0 +1,52 @@
1
+ <% sc = 0 %>
2
+ <% @scenario.examples.each_with_index do |example, example_index| %>
3
+ <% example.data.each_with_index do |row, row_index| %>
4
+ <div style="display: none;" class="steps <%= "example#{example_index + 1}-#{row_index +1}" %>">
5
+ <% scenario = @scenario.scenarios[sc] %>
6
+ <% @scenario_outline = @scenario ; @scenario = scenario ; @steps = scenario.steps %>
7
+ <%= erb(:steps) %>
8
+ <% @scenario = @scenario_outline %>
9
+ <% sc += 1 %>
10
+ </div>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <div class="outline">
15
+ <% if @scenario.examples? %>
16
+ <% @scenario.examples.each_with_index do |example,example_index| %>
17
+ <div class="keyword">
18
+ <%= h example.keyword %><%= example.name != "" ? ':' : '' %>
19
+ </div>
20
+ <div class="example-group-name"><%= h example.name %></div>
21
+ <br style="clear: both;"/>
22
+ <table>
23
+ <thead>
24
+ <tr>
25
+ <% example.headers.each_with_index do |header,header_index| %>
26
+ <th><%= h(header) %></th>
27
+ <% end %>
28
+ </tr>
29
+ </thead>
30
+ <% unless example.data.empty? %>
31
+ <% example.data.each_with_index do |row,row_index| %>
32
+ <tr class="<%= (row_index + 1) % 2 == 0 ? "even example#{example_index+1}-#{row_index +1}" : "odd example#{example_index+1}-#{row_index +1}" %>">
33
+ <% row.each_with_index do |column,column_index| %>
34
+ <td><%= h(column.to_s.strip) %></td>
35
+ <% end %>
36
+ </tr>
37
+ <% end %>
38
+ <% else %>
39
+ <!-- Scenario Outline example table is empty -->
40
+ <tr class="odd">
41
+ <td colspan="<%= example.headers.length %>" style="text-align: center;">
42
+ No Examples Defined
43
+ </td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+ <% end %>
48
+ <% else %>
49
+ <div class="keyword">No Example Table Defined</div>
50
+ <div class="keyword suggestion developer">[!] Did you mean to create a Scenario?</div>
51
+ <% end %>
52
+ </div>
@@ -0,0 +1,52 @@
1
+ <div class="scenario <%= @id %>">
2
+ <a name="<%= @id %>" />
3
+ <div class="title">
4
+ <div style="float: left;">
5
+ <a class="toggle"> - </a>
6
+ <span class="pre"><%= @scenario.keyword %>:</span>
7
+ <span class="name"><%= h @scenario.value %></span>
8
+ </div>
9
+ <a class="link" style="float:right; clear:right;" href="<%= url_for(@scenario.feature, @id) %>">link</a>
10
+ </div>
11
+
12
+ <% if @scenario.description.length > 0 %>
13
+ <div class="description">
14
+ <%= htmlify_with_newlines @scenario.description %>
15
+ </div>
16
+ <% end %>
17
+
18
+ <% if @scenario.comments.length > 0 %>
19
+ <div class="comments developer">
20
+ <%= htmlify_with_newlines @scenario.comments %>
21
+ </div>
22
+ <% end %>
23
+
24
+ <div class="details">
25
+ <div class="meta developer">
26
+ <div class="file"><%= @scenario.location %></div>
27
+ <% unless @scenario.tags.empty? %>
28
+ <div style="clear:right;"></div>
29
+ <div class="tags">
30
+ <% @scenario.tags.each do |tag| %>
31
+ <a href="<%= url_for tag %>"><%= tag.value %></a>
32
+ <% end %>
33
+ </div>
34
+ <% end%>
35
+ <div style="clear: both;"></div>
36
+ </div>
37
+
38
+ <div class="steps">
39
+ <% if @scenario.steps.empty? %>
40
+ <%= erb(:no_steps_defined) %>
41
+ <% else %>
42
+ <%= @steps = @scenario.steps ; erb(:steps) %>
43
+ <% end %>
44
+ </div>
45
+
46
+ <%= erb(:outline) if @scenario.outline? %>
47
+ </div>
48
+
49
+ <div class="attributes" style="display:none;">
50
+ <input type="hidden" name="collapsed" value="false">
51
+ </div>
52
+ </div>
@@ -0,0 +1,51 @@
1
+ def init
2
+ super
3
+ @feature = object
4
+ sections.push :feature
5
+ sections.push :scenarios if object.scenarios
6
+ end
7
+
8
+ def background
9
+ @scenario = @feature.background
10
+ @id = "background"
11
+ erb(:scenario)
12
+ end
13
+
14
+ def scenarios
15
+ scenarios = ""
16
+
17
+ if @feature.background
18
+ @scenario = @feature.background
19
+ @id = "background"
20
+ scenarios += erb(:scenario)
21
+ end
22
+
23
+ @feature.scenarios.each_with_index do |scenario,index|
24
+ @scenario = scenario
25
+ @id = "scenario_#{index}"
26
+ scenarios += erb(:scenario)
27
+ end
28
+
29
+ scenarios
30
+ end
31
+
32
+ def highlight_matches(step)
33
+ value = step.value.dup
34
+
35
+ if step.definition
36
+ matches = step.value.match(step.definition.regex)
37
+
38
+ if matches
39
+ matches[1..-1].reverse.each_with_index do |match,index|
40
+ next if match == nil
41
+ value[matches.begin((matches.size - 1) - index)..(matches.end((matches.size - 1) - index) - 1)] = "<span class='match'>#{h(match)}</span>"
42
+ end
43
+ end
44
+ end
45
+
46
+ value
47
+ end
48
+
49
+ def htmlify_with_newlines(text)
50
+ text.split("\n").collect {|c| h(c).gsub(/\s/,'&nbsp;') }.join("<br/>")
51
+ end
@@ -0,0 +1,37 @@
1
+ <% @steps.each_with_index do |step,index| %>
2
+ <% @step = step %>
3
+
4
+ <% if step.comments && step.comments.length > 0 %>
5
+ <div class="comments developer"><%= htmlify_with_newlines step.comments %></div>
6
+ <% end %>
7
+ <div class="step <%= (index + 1) % 2 == 0 ? 'even' : 'odd' %>">
8
+ <span class="predicate"><%= step.keyword %></span>
9
+
10
+ <% if @scenario.outline? %>
11
+ <%= h step.value %>
12
+ <% else %>
13
+
14
+ <% if step.definition %>
15
+ <span class="defined">
16
+ <%= highlight_matches(step) %>
17
+ <div class="definition developer">
18
+ <a href="<%= url_for step.definition %>"><div class="valid">&nbsp;</div></a>
19
+ </div>
20
+ </span>
21
+ <% else %>
22
+ <span class="undefined">
23
+ <%= h step.value %>
24
+ <div class="definition developer">
25
+ <a href="<%= url_for YARD::CodeObjects::Lucid::LUCID_STEPTRANSFORM_NAMESPACE %>#undefined_steps">
26
+ <div class="invalid">&nbsp;</div>
27
+ </a>
28
+ </div>
29
+ </span>
30
+ <% end %>
31
+
32
+ <% end %>
33
+ </div>
34
+
35
+ <%= erb(:table) if step.has_table? %>
36
+ <%= erb(:docstring) if step.has_text? %>
37
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <div class="multiline">
2
+ <table style="">
3
+ <thead>
4
+ <tr>
5
+ <% @step.table.first.each_with_index do |column, column_index| %>
6
+ <th class="<%= (column_index + 1) % 2 == 0 ? 'even' : 'odd' %>"><%= h(column.strip) %></th>
7
+ <% end %>
8
+ </tr>
9
+ </thead>
10
+
11
+ <% @step.table[1..-1].each_with_index do |row, row_index| %>
12
+ <tr class="<%= (row_index + 1) % 2 == 0 ? 'even' : 'odd' %>">
13
+ <% row.each_with_index do |column, column_index| %>
14
+ <td><%= h(column.strip) %></td>
15
+ <% end %>
16
+ </tr>
17
+ <% end %>
18
+ </table>
19
+ </div>
@@ -0,0 +1,30 @@
1
+ <% if @elements && !@elements.empty? %>
2
+ <% i = (@elements.length % 2 == 0 ? 1 : 0) %>
3
+ <table style="margin-left: 10px; width: 100%;">
4
+ <tr>
5
+ <td valign='top' width="50%">
6
+ <% @elements.each do |letter, objects| %>
7
+ <% if (i += 1) > (@elements.length / 2 + 1) %>
8
+ </td><td valign='top' width="50%">
9
+ <% i = 0 %>
10
+ <% end %>
11
+ <ul id="alpha_<%= letter %>" class="alpha">
12
+ <li class="letter"><%= letter %></li>
13
+ <ul>
14
+ <% objects.each do |obj| %>
15
+ <li>
16
+ <%= linkify obj, obj.value %>
17
+ <% if obj.is_a? YARD::CodeObjects::Lucid::FeatureDirectory %>
18
+ <small>(<%= obj.expanded_path %>)</small>
19
+ <% else %>
20
+ <small>(<%= obj.file %>)</small>
21
+ <% end %>
22
+ </li>
23
+ <% end %>
24
+ </ul>
25
+ </ul>
26
+ <% end %>
27
+ </td>
28
+ </tr>
29
+ </table>
30
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <% if @directory %>
2
+ <div class="specifications">
3
+ <div class="title">
4
+ <span class="pre">Directory:</span>
5
+ <span class="name"><%= h @directory.name.to_s.capitalize %></span>
6
+ </div>
7
+
8
+ <div class="readme">
9
+ <%= markdown @directory.description %>
10
+ </div>
11
+
12
+ <% if features && !features.empty? %>
13
+ <%= alpha_table(features) %>
14
+ <% end %>
15
+
16
+ <div id="directory">
17
+ <div class="title"><span class="name">Tags</span></div>
18
+ </div>
19
+ <div class="tags">
20
+ <%= tags.collect {|tag| linkify(tag,tag.value) }.join(",\n") %>
21
+ </div>
22
+
23
+ <% if directories && !directories.empty? %>
24
+ <div id="directory">
25
+ <div class="title"><span class="name">Subdirectories</span></div>
26
+ </div>
27
+ <%= alpha_table(directories) %>
28
+ <% end %>
29
+ </div>
30
+ <% end %>
@@ -0,0 +1,41 @@
1
+ def init
2
+ super
3
+ sections.push :directory
4
+ @directory = object
5
+ end
6
+
7
+ def markdown(text)
8
+ htmlify(text,:markdown) rescue h(text)
9
+ end
10
+
11
+ def htmlify_with_newlines(text)
12
+ text.split("\n").collect { |c| h(c).gsub(/\s/,'&nbsp;') }.join("<br/>")
13
+ end
14
+
15
+ def directories
16
+ @directories ||= @directory.subdirectories
17
+ end
18
+
19
+ def features
20
+ @features ||= @directory.features + directories.collect { |d| d.features }.flatten
21
+ end
22
+
23
+ def scenarios
24
+ @scenarios ||= features.collect { |feature| feature.scenarios }.flatten
25
+ end
26
+
27
+ def tags
28
+ @tags ||= (features.collect { |feature| feature.tags } + scenarios.collect { |scenario| scenario.tags }).flatten.uniq.sort_by { |l| l.value.to_s }
29
+ end
30
+
31
+ def alpha_table(objects)
32
+ @elements = Hash.new
33
+
34
+ objects = run_verifier(objects)
35
+ objects.each { |o| (@elements[o.value.to_s[0,1].upcase] ||= []) << o }
36
+ @elements.values.each { |v| v.sort! {|a,b| b.value.to_s <=> a.value.to_s } }
37
+ @elements = @elements.sort_by { |l,o| l.to_s }
38
+
39
+ @elements.each {|letter,objects| objects.sort! { |a,b| b.value.to_s <=> a.value.to_s } }
40
+ erb(:alpha_table)
41
+ end