yard-cucumber 3.1.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.yardopts +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +20 -17
- data/History.txt +13 -0
- data/README.md +10 -10
- data/example/scenario_outline.feature +8 -2
- data/example/step_definitions/example.step.rb +13 -0
- data/example/transform.feature +5 -0
- data/lib/cucumber/city_builder.rb +64 -15
- data/lib/templates/default/featuretags/html/namespace.erb +32 -4
- data/lib/templates/default/fulldoc/html/full_list_features.erb +1 -1
- data/lib/templates/default/fulldoc/html/full_list_tags.erb +1 -1
- data/lib/templates/default/fulldoc/html/js/cucumber.js +9 -0
- data/lib/templates/default/fulldoc/html/setup.rb +26 -4
- data/lib/templates/default/tag/html/alpha_table.erb +2 -1
- data/lib/yard-cucumber.rb +1 -0
- data/lib/yard-cucumber/version.rb +1 -1
- data/lib/yard/code_objects/cucumber/base.rb +1 -9
- data/lib/yard/code_objects/cucumber/feature.rb +7 -9
- data/lib/yard/code_objects/cucumber/namespace_object.rb +55 -49
- data/lib/yard/code_objects/cucumber/scenario.rb +6 -10
- data/lib/yard/code_objects/cucumber/scenario_outline.rb +16 -23
- data/lib/yard/code_objects/cucumber/step.rb +12 -9
- data/lib/yard/code_objects/cucumber/tag.rb +17 -13
- data/lib/yard/code_objects/step_definition.rb +3 -6
- data/lib/yard/code_objects/step_transform.rb +3 -6
- data/lib/yard/code_objects/step_transformer.rb +1 -4
- data/lib/yard/handlers/constant_transform_handler.rb +98 -0
- data/lib/yard/handlers/cucumber/feature_handler.rb +1 -1
- data/lib/yard/handlers/step_transform_handler.rb +22 -3
- data/yard-cucumber.gemspec +2 -2
- metadata +44 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e15515841dcdc314151768f94ca174cc323c9cd40162fa6e475a7764e33a3190
|
4
|
+
data.tar.gz: 895ad012764d4bc979f22c9bc3476a1585496c3b89b071b76d37fefa2c07ae3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c10f928e6030dbc310b4ca093c24c0e96aeb85244ad7b5441fe44af9ce3b77ec552f85f3e7484e289337abc16aebd03e4c6907b46f218122c1b266608e8d9dd
|
7
|
+
data.tar.gz: fe07b11b56211fd1a1c5f82e9ab08ec9dccab4d6ccf5a1dc2365a15164a80c49cf51b94b1ae3e24e3bb259380346d86a6b778f9429b695e6f85a66187e0a9189
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--plugin yard-cucumber
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,42 +1,45 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
yard-cucumber (3.
|
5
|
-
cucumber (
|
6
|
-
gherkin (
|
4
|
+
yard-cucumber (3.1.0)
|
5
|
+
cucumber (>= 2.0, < 4.0)
|
6
|
+
gherkin (>= 4.0, < 6.0)
|
7
7
|
yard (~> 0.8, >= 0.8.1)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
builder (3.2.
|
13
|
-
cucumber (2.
|
12
|
+
builder (3.2.3)
|
13
|
+
cucumber (2.2.0)
|
14
14
|
builder (>= 2.1.2)
|
15
|
-
cucumber-core (~> 1.
|
15
|
+
cucumber-core (~> 1.3.0)
|
16
16
|
cucumber-wire (~> 0.0.1)
|
17
17
|
diff-lcs (>= 1.1.3)
|
18
|
-
|
18
|
+
event-bus (~> 0.1.0)
|
19
|
+
gherkin3 (~> 3.1.0)
|
19
20
|
multi_json (>= 1.7.5, < 2.0)
|
20
21
|
multi_test (>= 0.1.2)
|
21
|
-
cucumber-core (1.
|
22
|
-
|
22
|
+
cucumber-core (1.3.1)
|
23
|
+
gherkin3 (~> 3.1.0)
|
23
24
|
cucumber-wire (0.0.1)
|
24
|
-
diff-lcs (1.
|
25
|
-
|
26
|
-
|
25
|
+
diff-lcs (1.3)
|
26
|
+
event-bus (0.1.0)
|
27
|
+
gherkin (5.0.0)
|
28
|
+
gherkin3 (3.1.2)
|
29
|
+
multi_json (1.13.1)
|
27
30
|
multi_test (0.1.2)
|
28
|
-
rake (10.
|
29
|
-
redcarpet (
|
30
|
-
yard (0.9.
|
31
|
+
rake (10.5.0)
|
32
|
+
redcarpet (3.4.0)
|
33
|
+
yard (0.9.12)
|
31
34
|
|
32
35
|
PLATFORMS
|
33
36
|
ruby
|
34
37
|
|
35
38
|
DEPENDENCIES
|
36
|
-
gherkin (
|
39
|
+
gherkin (>= 4.0, < 6.0)
|
37
40
|
rake (~> 10)
|
38
41
|
redcarpet
|
39
42
|
yard-cucumber!
|
40
43
|
|
41
44
|
BUNDLED WITH
|
42
|
-
1.
|
45
|
+
1.16.1
|
data/History.txt
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
=== 4.0.0 / 2018-02-25
|
2
|
+
|
3
|
+
* @wellavelino: Adjusts the indentation of several project classes
|
4
|
+
(https://github.com/burtlo/yard-cucumber/pull/89)
|
5
|
+
* @elhuang: Adding example tags and scenarios counts
|
6
|
+
(https://github.com/burtlo/yard-cucumber/pull/87)
|
7
|
+
* @weh: Update README.md
|
8
|
+
(https://github.com/burtlo/yard-cucumber/pull/84)
|
9
|
+
* @weh: Fix Gem Load Error
|
10
|
+
(https://github.com/burtlo/yard-cucumber/pull/83)
|
11
|
+
* @Stephen-Kaye: Transforms with CONSTANTS
|
12
|
+
(https://github.com/burtlo/yard-cucumber/pull/79)
|
13
|
+
|
1
14
|
=== 3.1.0/ 2016-12-06
|
2
15
|
|
3
16
|
* Updates to Gherkin 4.0 and Cucumber 2.4.0
|
data/README.md
CHANGED
@@ -92,16 +92,16 @@ This can be configured through the yard configuration file `~/.yard/config` to
|
|
92
92
|
add or remove these search fields.
|
93
93
|
|
94
94
|
```yaml
|
95
|
-
--- !
|
96
|
-
:
|
97
|
-
:
|
98
|
-
|
99
|
-
:autoload_plugins: []
|
100
|
-
|
101
|
-
:
|
102
|
-
|
103
|
-
:
|
104
|
-
|
95
|
+
--- !ruby/hash-with-ivars:SymbolHash
|
96
|
+
elements:
|
97
|
+
:load_plugins: true
|
98
|
+
:ignored_plugins: []
|
99
|
+
:autoload_plugins: []
|
100
|
+
:safe_mode: false
|
101
|
+
:"yard-cucumber":
|
102
|
+
menus: [ 'features', 'directories', 'tags', 'steps', 'step definitions' ]
|
103
|
+
ivars:
|
104
|
+
:@symbolize_value: false
|
105
105
|
```
|
106
106
|
|
107
107
|
By default the configuration, yaml format, that is generate by the `yard config`
|
@@ -1,4 +1,4 @@
|
|
1
|
-
@scenario_outlines @bvt
|
1
|
+
@scenario_outlines @bvt @duplicate
|
2
2
|
Feature: Displaying Scenario Outlines
|
3
3
|
As a reader of the documentation I expect that scenario outlines are documented correctly
|
4
4
|
|
@@ -9,6 +9,7 @@ Feature: Displaying Scenario Outlines
|
|
9
9
|
When the customer has purchased the product
|
10
10
|
Then I expect the customer to be a member of the '<Product>' group
|
11
11
|
|
12
|
+
@tagged_examples
|
12
13
|
Examples:
|
13
14
|
| Customer | Product |
|
14
15
|
| Customer A | Product A |
|
@@ -61,6 +62,7 @@ Feature: Displaying Scenario Outlines
|
|
61
62
|
|
62
63
|
# This is an example of a scenario outline in development.
|
63
64
|
# The example table has not been defined yet
|
65
|
+
@fifth
|
64
66
|
Scenario Outline: Example Table Missing
|
65
67
|
When I click on an example row
|
66
68
|
Then I expect <name> to be replaced by the example name
|
@@ -70,6 +72,7 @@ Feature: Displaying Scenario Outlines
|
|
70
72
|
|
71
73
|
# This is an example of a scenario outline in development.
|
72
74
|
# The examples table has been defined, but is missing data.
|
75
|
+
@sixth
|
73
76
|
Scenario Outline: Empty Example Table
|
74
77
|
When I click on an example row
|
75
78
|
Then I expect <name> to be replaced by the example name
|
@@ -79,16 +82,19 @@ Feature: Displaying Scenario Outlines
|
|
79
82
|
Examples:
|
80
83
|
| name | price | denomination |
|
81
84
|
|
85
|
+
@seventh @duplicate
|
82
86
|
Scenario Outline: Multiple Example Table
|
83
87
|
Given that <Customer> is a valid customer
|
84
88
|
And that the product, named '<Product>', is a valid product
|
85
89
|
When the customer has purchased the product
|
86
90
|
Then I expect the customer to be a member of the '<Product>' group
|
87
91
|
|
92
|
+
@groupA @duplicate
|
88
93
|
Examples: Example group A
|
89
94
|
| Customer | Product |
|
90
95
|
| Customer A | Product A |
|
91
96
|
|
97
|
+
@groupB
|
92
98
|
Examples: Example group B
|
93
99
|
| Customer | Product |
|
94
|
-
| Customer B | Product A |
|
100
|
+
| Customer B | Product A |
|
@@ -26,6 +26,13 @@ Transform /^((?:\d{1,2}[\/-]){2}(?:\d\d){1,2})?\s*(\w{3})?\s*(\d{1,2}:\d{2}\s*(?
|
|
26
26
|
"#{date} #{day} #{time}"
|
27
27
|
end
|
28
28
|
|
29
|
+
#
|
30
|
+
# Assign a trnasform to a variable to be interpolated later in a step definition
|
31
|
+
#
|
32
|
+
SUBSTITUTED_FILE = Transform(/^file '([^']*)'$/) do |filepath|
|
33
|
+
"Contents loaded from file"
|
34
|
+
end
|
35
|
+
|
29
36
|
Given /^that (#{CUSTOMER}) is a valid customer$/ do |customer|
|
30
37
|
pending "Customer #{customer} validation"
|
31
38
|
end
|
@@ -100,6 +107,12 @@ When /^the step definition has HTML escaped characters like: "([^"]+)"$/ do |cha
|
|
100
107
|
pending characters
|
101
108
|
end
|
102
109
|
|
110
|
+
#
|
111
|
+
# Step using interpolated transform to replace file reference with contents of file
|
112
|
+
#
|
113
|
+
Then /^the (#{SUBSTITUTED_FILE}) will be replaced with the file contents$/ do |content|
|
114
|
+
pending "File contained #{content}"
|
115
|
+
end
|
103
116
|
|
104
117
|
#
|
105
118
|
# Some details about the helper method that might be picked up in the documentation.
|
data/example/transform.feature
CHANGED
@@ -11,3 +11,8 @@ Feature: Step Transforms
|
|
11
11
|
Scenario: Step Transform uses a constant
|
12
12
|
Given this first step
|
13
13
|
Then I expect that the step, on the step transformer page, will link to the step transform
|
14
|
+
|
15
|
+
@third
|
16
|
+
Scenario: Step Transform uses an interpolated transform
|
17
|
+
Given this first step
|
18
|
+
Then the file './somelocation/somefile.input' will be replaced with the file contents
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Cucumber
|
2
2
|
module Parser
|
3
|
-
class CityBuilder < Gherkin::AstBuilder
|
3
|
+
class CityBuilder < ::Gherkin::AstBuilder
|
4
4
|
|
5
5
|
#
|
6
6
|
# The Gherkin Parser is going to call the various methods within this
|
@@ -74,7 +74,7 @@ module Cucumber
|
|
74
74
|
def find_or_create_tag(tag_name,parent)
|
75
75
|
#log.debug "Processing tag #{tag_name}"
|
76
76
|
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.value == tag_name } ||
|
77
|
-
YARD::CodeObjects::Cucumber::Tag.new(YARD::CodeObjects::Cucumber::CUCUMBER_TAG_NAMESPACE,tag_name.gsub('@','')) {|t| t.owners = [] ; t.value = tag_name }
|
77
|
+
YARD::CodeObjects::Cucumber::Tag.new(YARD::CodeObjects::Cucumber::CUCUMBER_TAG_NAMESPACE,tag_name.gsub('@','')) {|t| t.owners = [] ; t.value = tag_name ; t.total_scenarios = 0}
|
78
78
|
|
79
79
|
tag_code_object.add_file(@file,parent.line)
|
80
80
|
|
@@ -84,7 +84,7 @@ module Cucumber
|
|
84
84
|
|
85
85
|
#
|
86
86
|
# Each feature found will call this method, generating the feature object.
|
87
|
-
# This is once, as the
|
87
|
+
# This is once, as the gherkin parser does not like multiple feature per
|
88
88
|
# file.
|
89
89
|
#
|
90
90
|
def feature(document)
|
@@ -103,16 +103,25 @@ module Cucumber
|
|
103
103
|
|
104
104
|
feature[:tags].each {|feature_tag| find_or_create_tag(feature_tag[:name],f) }
|
105
105
|
end
|
106
|
-
|
107
|
-
|
106
|
+
|
107
|
+
background(feature[:background]) if feature[:background]
|
108
|
+
|
109
|
+
feature[:children].each do |child|
|
110
|
+
case child[:type]
|
108
111
|
when :Background
|
109
|
-
background(
|
112
|
+
background(child)
|
110
113
|
when :ScenarioOutline
|
111
|
-
scenario_outline(
|
114
|
+
outline = scenario_outline(child)
|
115
|
+
@feature.total_scenarios += outline.scenarios.size
|
112
116
|
when :Scenario
|
113
|
-
scenario(
|
117
|
+
scenario(child)
|
114
118
|
end
|
115
|
-
|
119
|
+
end
|
120
|
+
|
121
|
+
@feature.tags.each do |feature_tag|
|
122
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == feature_tag[:name].to_s }
|
123
|
+
tag_code_object.total_scenarios += @feature.total_scenarios
|
124
|
+
end
|
116
125
|
end
|
117
126
|
|
118
127
|
#
|
@@ -172,6 +181,14 @@ module Cucumber
|
|
172
181
|
statement[:steps].each { |s|
|
173
182
|
step(s)
|
174
183
|
}
|
184
|
+
|
185
|
+
# count scenarios for scenario level tags
|
186
|
+
scenario.tags.uniq.each { |scenario_tag|
|
187
|
+
if !scenario.feature.tags.include?(scenario_tag)
|
188
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == scenario_tag[:name].to_s }
|
189
|
+
tag_code_object.total_scenarios += 1
|
190
|
+
end
|
191
|
+
}
|
175
192
|
end
|
176
193
|
|
177
194
|
#
|
@@ -197,16 +214,41 @@ module Cucumber
|
|
197
214
|
end
|
198
215
|
|
199
216
|
outline.feature = @feature
|
200
|
-
@feature.scenarios << outline
|
201
217
|
@step_container = outline
|
202
218
|
statement[:steps].each { |s|
|
203
219
|
step(s)
|
204
220
|
}
|
221
|
+
|
205
222
|
statement[:examples].each { |e|
|
206
|
-
examples(e)
|
223
|
+
example = examples(e, outline)
|
224
|
+
}
|
225
|
+
|
226
|
+
@feature.scenarios << outline
|
227
|
+
|
228
|
+
# count scenarios for scenario outline level tags
|
229
|
+
outline.tags.uniq.each { |outline_tag|
|
230
|
+
if !outline.feature.tags.include?(outline_tag)
|
231
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == outline_tag[:name].to_s }
|
232
|
+
tag_code_object.total_scenarios += outline.scenarios.size
|
233
|
+
end
|
234
|
+
}
|
235
|
+
|
236
|
+
# count scenarios for example table level tags
|
237
|
+
outline.examples.each { |example|
|
238
|
+
unless !example.tags.any?
|
239
|
+
example.tags.uniq.each { |example_tag|
|
240
|
+
if !outline.feature.tags.include?(example_tag) && !outline.tags.include?(example_tag)
|
241
|
+
tag_code_object = YARD::Registry.all(:tag).find {|tag| tag.name.to_s == example_tag[:name].to_s }
|
242
|
+
tag_code_object.total_scenarios += example.data.size
|
243
|
+
end
|
244
|
+
}
|
245
|
+
end
|
207
246
|
}
|
247
|
+
|
248
|
+
return outline
|
208
249
|
end
|
209
250
|
|
251
|
+
|
210
252
|
#
|
211
253
|
# Examples for a scenario outline are called here. This section differs
|
212
254
|
# from the Cucumber parser because here each of the examples are exploded
|
@@ -214,15 +256,21 @@ module Cucumber
|
|
214
256
|
# later we can ensure that we have all the variations of the scenario
|
215
257
|
# outline defined to be displayed.
|
216
258
|
#
|
217
|
-
def examples(examples)
|
259
|
+
def examples(examples, outline)
|
218
260
|
#log.debug "EXAMPLES"
|
219
|
-
|
261
|
+
return if has_exclude_tags?(examples[:tags].map { |t| t[:name].gsub(/^@/, '') })
|
220
262
|
example = YARD::CodeObjects::Cucumber::ScenarioOutline::Examples.new(:keyword => examples[:keyword],
|
221
263
|
:name => examples[:name],
|
222
264
|
:line => examples[:location][:line],
|
223
265
|
:comments => examples[:comments] ? examples.comments.map{|comment| comment.value}.join("\n") : '',
|
224
|
-
:rows => []
|
225
|
-
|
266
|
+
:rows => [],
|
267
|
+
:tags => [],
|
268
|
+
:scenario => outline )
|
269
|
+
|
270
|
+
unless !examples[:tags].any?
|
271
|
+
examples[:tags].each {|example_tag| find_or_create_tag(example_tag[:name], example)}
|
272
|
+
end
|
273
|
+
|
226
274
|
example.rows = [examples[:tableHeader][:cells].map{ |c| c[:value] }] if examples[:tableHeader]
|
227
275
|
example.rows += matrix(examples[:tableBody]) if examples[:tableBody]
|
228
276
|
|
@@ -282,6 +330,7 @@ module Cucumber
|
|
282
330
|
|
283
331
|
end
|
284
332
|
|
333
|
+
return example
|
285
334
|
end
|
286
335
|
|
287
336
|
#
|
@@ -1,4 +1,7 @@
|
|
1
1
|
<% if @namespace %>
|
2
|
+
<% count = 0 %>
|
3
|
+
<% features.each {|f| count += f.total_scenarios } %>
|
4
|
+
|
2
5
|
<div id="tags" class="requirements">
|
3
6
|
<script type="text/javascript" charset="utf-8">
|
4
7
|
var tag_list = [ <%= tags.collect{|t| "'#{t.value}'" }.join(',') %> ];
|
@@ -18,11 +21,13 @@
|
|
18
21
|
|
19
22
|
$("#tag_search").keyup(function(evt) {
|
20
23
|
updateTagFiltering($("#tag_search")[0].value);
|
24
|
+
updateScenarioCount();
|
21
25
|
});
|
22
26
|
|
23
27
|
|
24
28
|
$("#tag_search").keyup(function(evt) {
|
25
29
|
updateTagFiltering($("#tag_search")[0].value);
|
30
|
+
updateScenarioCount();
|
26
31
|
});
|
27
32
|
|
28
33
|
$(".tag").click(function(evt) {
|
@@ -50,6 +55,7 @@
|
|
50
55
|
|
51
56
|
tagSearchElement.value = (tagSearchElement.value != "" ? tagSearchElement.value + tagModifier : "") + tagToAdd;
|
52
57
|
updateTagFiltering(tagSearchElement.value);
|
58
|
+
updateScenarioCount();
|
53
59
|
|
54
60
|
}
|
55
61
|
});
|
@@ -89,7 +95,7 @@
|
|
89
95
|
|
90
96
|
<div id="features">
|
91
97
|
<div class="title">
|
92
|
-
<span class="name">Features</span>
|
98
|
+
<span class="name">Features<span id="scenario_count"> (<%= count %>)</span></span>
|
93
99
|
</div>
|
94
100
|
<% n = 1 %>
|
95
101
|
<ul style="padding-left: 0px;">
|
@@ -105,9 +111,9 @@
|
|
105
111
|
<% if feature.scenarios %>
|
106
112
|
<ul style="padding-left: 20px;">
|
107
113
|
<% feature.scenarios.each do |scenario| %>
|
108
|
-
<li class="scenario <%= n % 2 == 0 ? 'even' : 'odd' %> <%= feature.tags.collect{|t| t.value }.join(" ") %> <%= scenario.tags.collect{|t| t.value }.join(" ") %>">
|
114
|
+
<li class="scenario <%= n % 2 == 0 ? 'even' : 'odd' %> <%= feature.tags.collect{|t| t.value }.join(" ") %> <%= scenario.tags.collect{|t| t.value }.join(" ") %>" count="<%= scenario.outline? ? 0 : 1 %>">
|
109
115
|
<span class='object_link'>
|
110
|
-
<a href="<%= url_for(scenario.feature,"
|
116
|
+
<a href="<%= url_for(scenario.feature,"scenario_#{scenario.feature.scenarios.index(scenario) }") %>">
|
111
117
|
<%= h scenario.value %>
|
112
118
|
</a>
|
113
119
|
</span>
|
@@ -116,8 +122,30 @@
|
|
116
122
|
- <small><%= stags %></small>
|
117
123
|
<% end %>
|
118
124
|
</li>
|
125
|
+
|
119
126
|
<% n = n == 2 ? 1 : 2 %>
|
120
|
-
|
127
|
+
|
128
|
+
<% if scenario.outline? %>
|
129
|
+
<ul style="padding-left: 40px;">
|
130
|
+
<% scenario.examples.each do |example| %>
|
131
|
+
<li class="scenario <%= n % 2 == 0 ? 'even' : 'odd' %> <%= feature.tags.collect{|t| t.value }.join(" ") %> <%= scenario.tags.collect{|t| t.value }.join(" ") %> <%= example.tags.collect{|t| t.value }.join(" ") %>" count="<%= example.data.size %>">
|
132
|
+
|
133
|
+
<span class='object_link'>
|
134
|
+
<a href="<%= url_for(scenario.feature,"scenario_#{scenario.feature.scenarios.index(scenario) }") %>">
|
135
|
+
<%= example.name.nil? || example.name.empty? ? "Examples" : example.name %>
|
136
|
+
</a>
|
137
|
+
</span>
|
138
|
+
|
139
|
+
<% etags = example.tags.collect{|t| tagify(t) }.join(", ") %>
|
140
|
+
<% if etags && etags != "" %>
|
141
|
+
- <small><%= etags %></small>
|
142
|
+
<% end %>
|
143
|
+
</li>
|
144
|
+
<% n = n == 2 ? 1 : 2 %>
|
145
|
+
<% end %>
|
146
|
+
</ul>
|
147
|
+
<% end %>
|
148
|
+
<% end %>
|
121
149
|
</ul>
|
122
150
|
<% end %>
|
123
151
|
<% end %>
|