yard-lucid 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/Rakefile +6 -0
- data/bin/console +13 -0
- data/bin/setup +8 -0
- data/lib/docserver/default/fulldoc/html/js/lucid.js +69 -0
- data/lib/docserver/default/layout/html/headers.erb +13 -0
- data/lib/docserver/doc_server/full_list/html/full_list.erb +37 -0
- data/lib/docserver/doc_server/full_list/html/setup.rb +19 -0
- data/lib/lucid/gherkin_repr.rb +307 -0
- data/lib/templates/default/feature/html/docstring.erb +3 -0
- data/lib/templates/default/feature/html/feature.erb +39 -0
- data/lib/templates/default/feature/html/no_steps_defined.erb +1 -0
- data/lib/templates/default/feature/html/outline.erb +52 -0
- data/lib/templates/default/feature/html/scenario.erb +52 -0
- data/lib/templates/default/feature/html/setup.rb +51 -0
- data/lib/templates/default/feature/html/steps.erb +37 -0
- data/lib/templates/default/feature/html/table.erb +19 -0
- data/lib/templates/default/featuredirectory/html/alpha_table.erb +30 -0
- data/lib/templates/default/featuredirectory/html/directory.erb +30 -0
- data/lib/templates/default/featuredirectory/html/setup.rb +41 -0
- data/lib/templates/default/featuretags/html/namespace.erb +127 -0
- data/lib/templates/default/featuretags/html/setup.rb +33 -0
- data/lib/templates/default/fulldoc/html/css/lucid.css +224 -0
- data/lib/templates/default/fulldoc/html/directories.erb +27 -0
- data/lib/templates/default/fulldoc/html/full_list_featuredirectories.erb +11 -0
- data/lib/templates/default/fulldoc/html/full_list_features.erb +36 -0
- data/lib/templates/default/fulldoc/html/full_list_stepdefinitions.erb +20 -0
- data/lib/templates/default/fulldoc/html/full_list_steps.erb +20 -0
- data/lib/templates/default/fulldoc/html/full_list_tags.erb +16 -0
- data/lib/templates/default/fulldoc/html/js/lucid.js +284 -0
- data/lib/templates/default/fulldoc/html/setup.rb +199 -0
- data/lib/templates/default/layout/html/setup.rb +111 -0
- data/lib/templates/default/specifications/html/alpha_table.erb +26 -0
- data/lib/templates/default/specifications/html/setup.rb +49 -0
- data/lib/templates/default/specifications/html/specifications.erb +46 -0
- data/lib/templates/default/steptransformers/html/header.erb +12 -0
- data/lib/templates/default/steptransformers/html/index.erb +9 -0
- data/lib/templates/default/steptransformers/html/setup.rb +92 -0
- data/lib/templates/default/steptransformers/html/transformers.erb +74 -0
- data/lib/templates/default/steptransformers/html/undefinedsteps.erb +26 -0
- data/lib/templates/default/tag/html/alpha_table.erb +32 -0
- data/lib/templates/default/tag/html/setup.rb +26 -0
- data/lib/templates/default/tag/html/tag.erb +31 -0
- data/lib/yard/code_objects/lucid/base.rb +23 -0
- data/lib/yard/code_objects/lucid/feature.rb +12 -0
- data/lib/yard/code_objects/lucid/namespace_object.rb +43 -0
- data/lib/yard/code_objects/lucid/scenario.rb +20 -0
- data/lib/yard/code_objects/lucid/scenario_outline.rb +66 -0
- data/lib/yard/code_objects/lucid/step.rb +32 -0
- data/lib/yard/code_objects/lucid/tag.rb +21 -0
- data/lib/yard/code_objects/step_definition.rb +3 -0
- data/lib/yard/code_objects/step_transform.rb +3 -0
- data/lib/yard/code_objects/step_transformer.rb +85 -0
- data/lib/yard/handlers/lucid/base.rb +19 -0
- data/lib/yard/handlers/lucid/feature_handler.rb +102 -0
- data/lib/yard/handlers/step_definition_handler.rb +67 -0
- data/lib/yard/handlers/step_transform_handler.rb +28 -0
- data/lib/yard/parser/lucid/feature.rb +58 -0
- data/lib/yard/server/adapter.rb +32 -0
- data/lib/yard/server/commands/list_command.rb +25 -0
- data/lib/yard/server/router.rb +23 -0
- data/lib/yard/templates/helpers/base_helper.rb +22 -0
- data/lib/yard-lucid/version.rb +3 -0
- data/lib/yard-lucid.rb +36 -0
- data/yard-lucid.gemspec +38 -0
- 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,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/,' ') }.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"> </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"> </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/,' ') }.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
|