yard-gherkin-turnip 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +3 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +48 -0
  9. data/History.txt +293 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +182 -0
  12. data/Rakefile +24 -0
  13. data/example/README.md +8 -0
  14. data/example/child_feature/README.md +21 -0
  15. data/example/child_feature/child.feature +11 -0
  16. data/example/child_feature/grandchild_feature/grandchild.feature +12 -0
  17. data/example/empty.feature +2 -0
  18. data/example/placeholder.feature +34 -0
  19. data/example/scenario.feature +63 -0
  20. data/example/scenario_outline.feature +100 -0
  21. data/example/scenario_outline_multi.feature +15 -0
  22. data/example/step_definitions/customer.step.rb +16 -0
  23. data/example/step_definitions/duck.step.rb +6 -0
  24. data/example/step_definitions/placeholder.step.rb +70 -0
  25. data/example/step_definitions/text.step.rb +11 -0
  26. data/example/tags.feature +18 -0
  27. data/lib/cucumber/city_builder.rb +412 -0
  28. data/lib/docserver/default/fulldoc/html/js/cucumber.js +88 -0
  29. data/lib/docserver/default/layout/html/headers.erb +13 -0
  30. data/lib/docserver/doc_server/full_list/html/full_list.erb +39 -0
  31. data/lib/docserver/doc_server/full_list/html/setup.rb +18 -0
  32. data/lib/templates/default/feature/html/feature.erb +39 -0
  33. data/lib/templates/default/feature/html/no_steps_defined.erb +1 -0
  34. data/lib/templates/default/feature/html/outline.erb +59 -0
  35. data/lib/templates/default/feature/html/pystring.erb +3 -0
  36. data/lib/templates/default/feature/html/scenario.erb +55 -0
  37. data/lib/templates/default/feature/html/setup.rb +54 -0
  38. data/lib/templates/default/feature/html/steps.erb +39 -0
  39. data/lib/templates/default/feature/html/table.erb +20 -0
  40. data/lib/templates/default/featuredirectory/html/alpha_table.erb +30 -0
  41. data/lib/templates/default/featuredirectory/html/directory.erb +32 -0
  42. data/lib/templates/default/featuredirectory/html/setup.rb +41 -0
  43. data/lib/templates/default/featuretags/html/namespace.erb +159 -0
  44. data/lib/templates/default/featuretags/html/setup.rb +34 -0
  45. data/lib/templates/default/fulldoc/html/css/cucumber.css +226 -0
  46. data/lib/templates/default/fulldoc/html/directories.erb +27 -0
  47. data/lib/templates/default/fulldoc/html/full_list_featuredirectories.erb +11 -0
  48. data/lib/templates/default/fulldoc/html/full_list_features.erb +37 -0
  49. data/lib/templates/default/fulldoc/html/full_list_stepdefinitions.erb +20 -0
  50. data/lib/templates/default/fulldoc/html/full_list_steps.erb +20 -0
  51. data/lib/templates/default/fulldoc/html/full_list_tags.erb +16 -0
  52. data/lib/templates/default/fulldoc/html/js/cucumber.js +331 -0
  53. data/lib/templates/default/fulldoc/html/setup.rb +208 -0
  54. data/lib/templates/default/layout/html/setup.rb +131 -0
  55. data/lib/templates/default/requirements/html/alpha_table.erb +26 -0
  56. data/lib/templates/default/requirements/html/requirements.erb +50 -0
  57. data/lib/templates/default/requirements/html/setup.rb +51 -0
  58. data/lib/templates/default/steptransformers/html/header.erb +12 -0
  59. data/lib/templates/default/steptransformers/html/index.erb +10 -0
  60. data/lib/templates/default/steptransformers/html/placeholders.erb +79 -0
  61. data/lib/templates/default/steptransformers/html/setup.rb +107 -0
  62. data/lib/templates/default/steptransformers/html/step_definitions.erb +79 -0
  63. data/lib/templates/default/steptransformers/html/undefined_steps.erb +26 -0
  64. data/lib/templates/default/tag/html/alpha_table.erb +33 -0
  65. data/lib/templates/default/tag/html/setup.rb +27 -0
  66. data/lib/templates/default/tag/html/tag.erb +35 -0
  67. data/lib/yard-gherkin-turnip.rb +42 -0
  68. data/lib/yard-gherkin-turnip/version.rb +3 -0
  69. data/lib/yard/code_objects/cucumber/base.rb +24 -0
  70. data/lib/yard/code_objects/cucumber/feature.rb +16 -0
  71. data/lib/yard/code_objects/cucumber/namespace_object.rb +55 -0
  72. data/lib/yard/code_objects/cucumber/scenario.rb +22 -0
  73. data/lib/yard/code_objects/cucumber/scenario_outline.rb +68 -0
  74. data/lib/yard/code_objects/cucumber/step.rb +46 -0
  75. data/lib/yard/code_objects/cucumber/tag.rb +31 -0
  76. data/lib/yard/code_objects/placeholder.rb +45 -0
  77. data/lib/yard/code_objects/step_definition.rb +46 -0
  78. data/lib/yard/code_objects/step_transformer.rb +32 -0
  79. data/lib/yard/handlers/cucumber/base.rb +21 -0
  80. data/lib/yard/handlers/cucumber/feature_handler.rb +96 -0
  81. data/lib/yard/handlers/placeholder_handler.rb +28 -0
  82. data/lib/yard/handlers/placeholder_match_handler.rb +17 -0
  83. data/lib/yard/handlers/step_definition_handler.rb +55 -0
  84. data/lib/yard/parser/cucumber/feature.rb +72 -0
  85. data/lib/yard/server/adapter.rb +43 -0
  86. data/lib/yard/server/commands/list_command.rb +31 -0
  87. data/lib/yard/server/router.rb +31 -0
  88. data/lib/yard/templates/helpers/base_helper.rb +26 -0
  89. data/yard-gherkin-turnip.gemspec +67 -0
  90. metadata +216 -0
@@ -0,0 +1,24 @@
1
+ require 'rake'
2
+
3
+ task :default => :gendoc
4
+
5
+ desc "Clean out any existing documentation"
6
+ task :clean do
7
+ `rm -rf doc`
8
+ `rm -rf .yardoc`
9
+ end
10
+
11
+ desc "Generate documentation from the example data"
12
+ task :gendoc => :clean do
13
+ puts `yardoc -e ./lib/yard-gherkin-turnip.rb 'example/**/*' --debug`
14
+ end
15
+
16
+ desc "Run the YARD Server"
17
+ task :server => :gendoc do
18
+ puts `yard server -e ./lib/yard-gherkin-turnip.rb`
19
+ end
20
+
21
+ desc "Create the yard-gherkin-turnip gem"
22
+ task :gem do
23
+ puts `gem build yard-gherkin-turnip.gemspec`
24
+ end
@@ -0,0 +1,8 @@
1
+ This collection of features are really just to test some of the varied cases
2
+ that CITY may come in contact with while parsing a series of features. All
3
+ of the features, tags, and directories displayed here are contained in this
4
+ directory and all subdirectories.
5
+
6
+ * First, the features which are broken down alphabetically and displayed
7
+ * Second, the tags, used by all the features and scenarios
8
+ * Third, the subdirectories, contained in this directory and in the subdirectories.
@@ -0,0 +1,21 @@
1
+ Child Features
2
+ ==============
3
+
4
+ Synopsis
5
+ --------
6
+
7
+ This collection of features are contained in this folder. The description README
8
+ allows a user to place a description for entire directory of the features and appears
9
+ in the output to assist with understanding about the collection of features.
10
+
11
+ Resources
12
+ ---------
13
+
14
+ Links to particular resources like the links to the stories, tasks, or other areas
15
+ can also be represented.
16
+
17
+ The implemented example has been deployed at [http://recursivegames.com/cukes/](http://recursivegames.com/cukes/).
18
+
19
+ **1. An Item** [example](http://recursivegames.com/cukes/requirements/)
20
+
21
+
@@ -0,0 +1,11 @@
1
+ @scenarios
2
+ Feature: Child Feature
3
+ As a reader of the documentation I expect that scenario are documented correctly
4
+
5
+ Background:
6
+ Given this background step
7
+
8
+ Scenario: Child Scenario
9
+ Given this first step
10
+ When this second step
11
+ Then this third step
@@ -0,0 +1,12 @@
1
+ @scenarios
2
+ Feature: Grandchild Feature
3
+ As a reader of the documentation I expect that scenario are documented correctly
4
+
5
+ Background:
6
+ Given this background step
7
+
8
+ @first
9
+ Scenario: Grandchild Scenario
10
+ Given this first step
11
+ When this second step
12
+ Then this third step
@@ -0,0 +1,2 @@
1
+ # This is a feature that has not been written
2
+ # However, I don't want parser to fail when reaching this file
@@ -0,0 +1,34 @@
1
+ @scenarios @bvt
2
+ Feature: Step Placeholders
3
+ As a developer of the test suite I expect that step placeholders are documented correctly
4
+
5
+ @first
6
+ Scenario: Step with custom placeholders
7
+ Given this scenario step
8
+ Then I expect that the step, on the step transformer page, will link to the step transform
9
+
10
+ @first
11
+ Scenario: Step with custom placeholders
12
+ Given the first step
13
+ Then I expect that the step, on the step transformer page, will link to the step transform
14
+
15
+ @second
16
+ Scenario: Step with simple placeholders
17
+ Given the step called 'bob'
18
+ And the steps called 'pop'
19
+ Then I expect that the step, on the step transformer page, will link to the step transform
20
+
21
+ @third
22
+ Scenario: Step with multiple custom placeholders
23
+ Given the second scenario step on 12/04/2020
24
+ Then I expect that the step, on the step transformer page, will link to the step transform
25
+
26
+ @third
27
+ Scenario: Step with both simple and custom placeholders
28
+ Given the second 'bob' step
29
+ Then I expect that the step, on the step transformer page, will link to the step transform
30
+
31
+ Scenario: Step with complex placeholder reges
32
+ Given the step at the [0 + 1] index
33
+ Then I expect that the step, on the step transformer page, will link to the step transform
34
+
@@ -0,0 +1,63 @@
1
+ # Comments that appear before the feature are associated with the feature
2
+ @scenarios
3
+ Feature: Displaying Scenarios
4
+ As a reader of the documentation I expect that scenario are documented correctly
5
+
6
+ # Comments after the feature description belong to the background or first scenario
7
+ Background:
8
+ Given this background step
9
+
10
+ @first @bvt
11
+ Scenario: No Step Scenario
12
+
13
+ @second @bvt
14
+ Scenario: Scenario With Steps
15
+ Given this first step
16
+ When this second step
17
+ Then this third step
18
+
19
+ @third @optional_parameters
20
+ Scenario: Optional Parameter Step Definition
21
+ # This step definition has some optional parameters
22
+ Given a project
23
+ And an inactive project
24
+ And a project with the name 'optional', start date 10/26/2010, nicknamed 'norman'
25
+
26
+ @fourth @highlight
27
+ Scenario: Matched Term Highlighting
28
+ Given a duck that has a bill
29
+ Then I expect the duck to quack
30
+
31
+ @fifth @table
32
+ Scenario: Scenario With Table
33
+ Given the following table:
34
+ | column 1 | column 2 | column 3 |
35
+ | value 1 | value 2 | value 3 |
36
+
37
+ @sixth @text
38
+ Scenario: Scenario With Text
39
+ Given the following text:
40
+ """
41
+ Oh what a bother!
42
+ That this text has to take up two lines
43
+ This line should be indented 2 spaces
44
+ This line should be indented 4 spaces
45
+ """
46
+
47
+ # Comments before the scenario
48
+ @seventh @comments
49
+ Scenario: Scenario with comments and a description
50
+ There once was a need for information to be displayed alongside all the
51
+ entities that I hoped to test
52
+ # First Comment
53
+ Given this first step
54
+ # Second Comment that
55
+ # spans a few lines
56
+ And this second step
57
+ # Third Comment
58
+ And this third step
59
+ # Comments after the last step, where do they go?
60
+
61
+ Scenario: Step ending with a match with double-quotes
62
+ When searching the log for the exact match of the message "Entering application."
63
+ When the step definition has HTML escaped characters like: "<>&"
@@ -0,0 +1,100 @@
1
+ @scenario_outlines @bvt @duplicate
2
+ Feature: Displaying Scenario Outlines
3
+ As a reader of the documentation I expect that scenario outlines are documented correctly
4
+
5
+ @first
6
+ Scenario Outline: Three Examples
7
+ Given that <Customer> is a valid customer
8
+ And that the product, named '<Product>', is a valid product
9
+ When the customer has purchased the product
10
+ Then I expect the customer to be a member of the '<Product>' group
11
+
12
+ @tagged_examples
13
+ Examples:
14
+ | Customer | Product |
15
+ | Customer A | Product A |
16
+ | Customer A | Product B |
17
+ | Customer A | Product C |
18
+
19
+ @second
20
+ Scenario Outline: Step contains a text block
21
+ Given the following text:
22
+ """
23
+ The <noun> jumped over the <place>
24
+ """
25
+ When I click on an example row
26
+ Then I expect <noun> to be replaced by the example noun
27
+ And I expect <place> to be replaced by the example place
28
+
29
+ Examples:
30
+ | noun | place |
31
+ | cow | moon |
32
+ | horse | spoon |
33
+
34
+ @third
35
+ Scenario Outline: Step contains a table
36
+ Given the following table:
37
+ | name | price | quantity |
38
+ | <name> | <price> | 100000 |
39
+ When I click on an example row
40
+ Then I expect <name> to be replaced by the example name
41
+ And I expect <price> to be replaced by the example price
42
+
43
+ Examples:
44
+ | name | price |
45
+ | toy | $99 |
46
+ | game | $49 |
47
+
48
+ @fourth
49
+ Scenario Outline: Step contains a table; table header uses an example
50
+ Given the following table:
51
+ | name | <denomination> | quantity |
52
+ | <name> | <price> | 100000 |
53
+ When I click on an example row
54
+ Then I expect <name> to be replaced by the example name
55
+ And I expect <price> to be replaced by the example price
56
+ And I expect <denomination> to be replaced by the example denomination
57
+
58
+ Examples:
59
+ | name | price | denomination |
60
+ | toy | 99 | cost in euros |
61
+ | game | 49 | cost in dollars |
62
+
63
+ # This is an example of a scenario outline in development.
64
+ # The example table has not been defined yet
65
+ @fifth
66
+ Scenario Outline: Example Table Missing
67
+ When I click on an example row
68
+ Then I expect <name> to be replaced by the example name
69
+ And I expect <price> to be replaced by the example price
70
+ And I expect <denomination> to be replaced by the example denomination
71
+
72
+
73
+ # This is an example of a scenario outline in development.
74
+ # The examples table has been defined, but is missing data.
75
+ @sixth
76
+ Scenario Outline: Empty Example Table
77
+ When I click on an example row
78
+ Then I expect <name> to be replaced by the example name
79
+ And I expect <price> to be replaced by the example price
80
+ And I expect <denomination> to be replaced by the example denomination
81
+
82
+ Examples:
83
+ | name | price | denomination |
84
+
85
+ @seventh @duplicate
86
+ Scenario Outline: Multiple Example Table
87
+ Given that <Customer> is a valid customer
88
+ And that the product, named '<Product>', is a valid product
89
+ When the customer has purchased the product
90
+ Then I expect the customer to be a member of the '<Product>' group
91
+
92
+ @groupA @duplicate
93
+ Examples: Example group A
94
+ | Customer | Product |
95
+ | Customer A | Product A |
96
+
97
+ @groupB
98
+ Examples: Example group B
99
+ | Customer | Product |
100
+ | Customer B | Product A |
@@ -0,0 +1,15 @@
1
+ Feature: My Feature
2
+
3
+ Scenario Outline: Multiple Example Table
4
+ Given that <Customer> is a valid customer
5
+ And that the product, named '<Product>', is a valid product
6
+ When the customer has purchased the product
7
+ Then I expect the customer to be a member of the '<Product>' group
8
+
9
+ Examples: Example group A
10
+ | Customer | Product |
11
+ | Customer A | Product A |
12
+
13
+ Examples: Example group B
14
+ | Customer | Product |
15
+ | Customer B | Product A |
@@ -0,0 +1,16 @@
1
+ step "that Customer :customer_number is a valid customer" do
2
+ end
3
+
4
+ step "that the product, named ':product', is a valid product" do
5
+ end
6
+
7
+ step "the customer has purchased the product" do
8
+ skip # pending step
9
+ end
10
+
11
+ step "I expect the customer to be a member of the ':product_type' group" do
12
+ end
13
+
14
+ placeholder :product_type do
15
+ match %r{(Product A|Product B|Product C)}
16
+ end
@@ -0,0 +1,6 @@
1
+ step "a duck that "\
2
+ "has a bill" do
3
+ end
4
+
5
+ step "I expect the " + "duck to quack" do
6
+ end
@@ -0,0 +1,70 @@
1
+ #
2
+ # This step definition uses a placeholder custom
3
+ #
4
+ step "the/this :step_type step" do |step_type|
5
+ puts "step type #{step_type}"
6
+ end
7
+
8
+ #
9
+ # This step definition uses a custom placeholder with forward slashes
10
+ #
11
+ step "the :step_number :step_type step on :date" do |step_number, step_type, day, month, year|
12
+ puts "step type #{step_type}"
13
+ end
14
+
15
+ #
16
+ # This step definition uses alternative text syntax
17
+ #
18
+ step "the/this :step_number step" do |step_type|
19
+ puts "step number #{step_type}"
20
+ end
21
+
22
+ #
23
+ # This step definition uses a default placeholder with optional text
24
+ #
25
+ step "the step(s) called :name" do |name|
26
+ puts "name #{name}"
27
+ end
28
+
29
+
30
+ #
31
+ # This step definition uses both default and custom placeholders
32
+ #
33
+ step "the :step_number :name step" do |step_number, name|
34
+ puts "step_number #{step_number} and name #{name}"
35
+ end
36
+
37
+ #
38
+ # This step definition uses a custom placeholder with regex special chars
39
+ #
40
+ step "the step at the [:first_term :arithmetic_operation :first_term] index" do
41
+ end
42
+
43
+ #
44
+ # Shouldn't match our handler as it's not inside a placeholder
45
+ #
46
+ match /blob/ do
47
+ end
48
+
49
+ #
50
+ # This placeholder matches the type of step
51
+ #
52
+ placeholder :step_type do
53
+ match /(?:background|scenario)/
54
+ end
55
+
56
+ #
57
+ # This placeholder matches the step number using 2 separate calls to match
58
+ #
59
+ placeholder :step_number do
60
+ match(/(?:first|second)/)
61
+ match(/third/)
62
+ end
63
+
64
+ placeholder :date do
65
+ match /(\d\d)\/(\d\d)\/(\d\d\d\d)/
66
+ end
67
+
68
+ placeholder :arithmetic_operation do
69
+ match /\+|-|\*|\//
70
+ end
@@ -0,0 +1,11 @@
1
+ step :symbol, "the following text:"
2
+
3
+ step "I click on an example row" do
4
+ end
5
+
6
+ step "I expect :noun to be replaced by the example noun" do
7
+ end
8
+
9
+ step "I expect :place to be replaced by the example place" do
10
+ end
11
+
@@ -0,0 +1,18 @@
1
+ @tags
2
+ Feature: Tags
3
+ As a developer of the test suite I expect that various tags will be supported
4
+
5
+ @tag
6
+ Scenario: Basic Tag
7
+
8
+ @tag123456
9
+ Scenario: Tag With Numbers
10
+
11
+ @tag_with_underscore
12
+ Scenario: Tag With Underscore
13
+
14
+ @tag-with-dash
15
+ Scenario: Tag With Dash
16
+
17
+ @tag+with+plus
18
+ Scenario: Tag With Plus
@@ -0,0 +1,412 @@
1
+ module Cucumber
2
+ module Parser
3
+ class CityBuilder < ::Gherkin::AstBuilder
4
+
5
+ #
6
+ # The Gherkin Parser is going to call the various methods within this
7
+ # class as it finds items. This is similar to how Cucumber generates
8
+ # it's Abstract Syntax Tree (AST). Here instead this generates the
9
+ # various YARD::CodeObjects defined within this template.
10
+ #
11
+ # A namespace is specified and that is the place in the YARD namespacing
12
+ # where all cucumber features generated will reside. The namespace specified
13
+ # is the root namespaces.
14
+ #
15
+ # @param [String] file the name of the file which the content belongs
16
+ #
17
+ def initialize(file)
18
+ super()
19
+ @namespace = YARD::CodeObjects::Cucumber::CUCUMBER_NAMESPACE
20
+ find_or_create_namespace(file)
21
+ @file = file
22
+ end
23
+
24
+ # Return the feature that has been defined. This method is the final
25
+ # method that is called when all the work is done. It is called by
26
+ # the feature parser to return the complete Feature object that was created
27
+ #
28
+ # @return [YARD::CodeObject::Cucumber::Feature] the completed feature
29
+ #
30
+ # @see YARD::Parser::Cucumber::FeatureParser
31
+ def ast
32
+ feature(get_result) unless @feature
33
+ @feature
34
+ end
35
+
36
+ #
37
+ # Feature that are found in sub-directories are considered, in the way
38
+ # that I chose to implement it, in another namespace. This is because
39
+ # when you execute a cucumber test run on a directory any sub-directories
40
+ # of features will be executed with that directory so the file is split
41
+ # and then namespaces are generated if they have not already have been.
42
+ #
43
+ # The other duty that this does is look for a README.md file within the
44
+ # specified directory of the file and loads it as the description for the
45
+ # namespace. This is useful if you want to give a particular directory
46
+ # some flavor or text to describe what is going on.
47
+ #
48
+ def find_or_create_namespace(file)
49
+ @namespace = YARD::CodeObjects::Cucumber::CUCUMBER_NAMESPACE
50
+
51
+ File.dirname(file).split('/').each do |directory|
52
+ @namespace = @namespace.children.find {|child| child.is_a?(YARD::CodeObjects::Cucumber::FeatureDirectory) && child.name.to_s == directory } ||
53
+ @namespace = YARD::CodeObjects::Cucumber::FeatureDirectory.new(@namespace,directory) {|dir| dir.add_file(directory)}
54
+ end
55
+
56
+ if @namespace.description == "" && File.exists?("#{File.dirname(file)}/README.md")
57
+ @namespace.description = File.read("#{File.dirname(file)}/README.md")
58
+ end
59
+ end
60
+
61
+ #
62
+ # Find the tag if it exists within the YARD Registry, if it doesn't then
63
+ # create it.
64
+ #
65
+ # We note that the tag was used in this file at the current line.
66
+ #
67
+ # Then we add the tag to the current scenario or feature. We also add the
68
+ # feature or scenario to the tag.
69
+ #
70
+ # @param [String] tag_name the name of the tag
71
+ # @param [parent] parent the scenario or feature that is going to adopt
72
+ # this tag.
73
+ #
74
+ def find_or_create_tag(tag_name,parent)
75
+ #log.debug "Processing tag #{tag_name}"
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 ; t.total_scenarios = 0}
78
+
79
+ tag_code_object.add_file(@file,parent.line)
80
+
81
+ parent.tags << tag_code_object unless parent.tags.find {|tag| tag == tag_code_object }
82
+ tag_code_object.owners << parent unless tag_code_object.owners.find {|owner| owner == parent}
83
+ end
84
+
85
+ #
86
+ # Each feature found will call this method, generating the feature object.
87
+ # This is once, as the gherkin parser does not like multiple feature per
88
+ # file.
89
+ #
90
+ def feature(document)
91
+ #log.debug "FEATURE"
92
+ feature = document[:feature]
93
+ return unless document[:feature]
94
+ return if has_exclude_tags?(feature[:tags].map { |t| t[:name].gsub(/^@/, '') })
95
+
96
+ @feature = YARD::CodeObjects::Cucumber::Feature.new(@namespace,File.basename(@file.gsub('.feature','').gsub('.','_'))) do |f|
97
+ f.comments = feature[:comments] ? feature[:comments].map{|comment| comment[:text]}.join("\n") : ''
98
+ f.description = feature[:description] || ''
99
+ f.add_file(@file,feature[:location][:line])
100
+ f.keyword = feature[:keyword]
101
+ f.value = feature[:name]
102
+ f.tags = []
103
+
104
+ feature[:tags].each {|feature_tag| find_or_create_tag(feature_tag[:name],f) }
105
+ end
106
+
107
+ background(feature[:background]) if feature[:background]
108
+
109
+ feature[:children].each do |child|
110
+ case child[:type]
111
+ when :Background
112
+ background(child)
113
+ when :ScenarioOutline
114
+ outline = scenario_outline(child)
115
+ @feature.total_scenarios += outline.scenarios.size
116
+ when :Scenario
117
+ scenario(child)
118
+ end
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
125
+ end
126
+
127
+ #
128
+ # Called when a background has been found
129
+ #
130
+ # @see #feature
131
+ def background(background)
132
+ #log.debug "BACKGROUND"
133
+
134
+ @background = YARD::CodeObjects::Cucumber::Scenario.new(@feature,"background") do |b|
135
+ b.comments = background[:comments] ? background[:comments].map{|comment| comment.value}.join("\n") : ''
136
+ b.description = background[:description] || ''
137
+ b.keyword = background[:keyword]
138
+ b.value = background[:name]
139
+ b.add_file(@file,background[:location][:line])
140
+ end
141
+
142
+ @feature.background = @background
143
+ @background.feature = @feature
144
+ @step_container = @background
145
+ background[:steps].each { |s|
146
+ step(s)
147
+ }
148
+ end
149
+
150
+ #
151
+ # Called when a scenario has been found
152
+ # - create a scenario
153
+ # - assign the scenario to the feature
154
+ # - assign the feature to the scenario
155
+ # - find or create tags associated with the scenario
156
+ #
157
+ # The scenario is set as the @step_container, which means that any steps
158
+ # found before another scenario is defined belong to this scenario
159
+ #
160
+ # @param [Scenario] statement is a scenario object returned from Gherkin
161
+ # @see #find_or_create_tag
162
+ #
163
+ def scenario(statement)
164
+ #log.debug "SCENARIO"
165
+
166
+ return if has_exclude_tags?(statement[:tags].map { |t| t[:name].gsub(/^@/, '') })
167
+
168
+ scenario = YARD::CodeObjects::Cucumber::Scenario.new(@feature,"scenario_#{@feature.scenarios.length + 1}") do |s|
169
+ s.comments = statement[:comments] ? statement[:comments].map{|comment| comment.value}.join("\n") : ''
170
+ s.description = statement[:description] || ''
171
+ s.add_file(@file,statement[:location][:line])
172
+ s.keyword = statement[:keyword]
173
+ s.value = statement[:name]
174
+
175
+ statement[:tags].each {|scenario_tag| find_or_create_tag(scenario_tag[:name],s) }
176
+ end
177
+
178
+ scenario.feature = @feature
179
+ @feature.scenarios << scenario
180
+ @step_container = scenario
181
+ statement[:steps].each { |s|
182
+ step(s)
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
+ }
192
+ end
193
+
194
+ #
195
+ # Called when a scenario outline is found. Very similar to a scenario,
196
+ # the ScenarioOutline is still a distinct object as it can contain
197
+ # multiple different example groups that can contain different values.
198
+ #
199
+ # @see #scenario
200
+ #
201
+ def scenario_outline(statement)
202
+ #log.debug "SCENARIO OUTLINE"
203
+
204
+ return if has_exclude_tags?(statement[:tags].map { |t| t[:name].gsub(/^@/, '') })
205
+
206
+ outline = YARD::CodeObjects::Cucumber::ScenarioOutline.new(@feature,"scenario_#{@feature.scenarios.length + 1}") do |s|
207
+ s.comments = statement[:comments] ? statement[:comments].map{|comment| comment.value}.join("\n") : ''
208
+ s.description = statement[:description] || ''
209
+ s.add_file(@file,statement[:location][:line])
210
+ s.keyword = statement[:keyword]
211
+ s.value = statement[:name]
212
+
213
+ statement[:tags].each {|scenario_tag| find_or_create_tag(scenario_tag[:name],s) }
214
+ end
215
+
216
+ outline.feature = @feature
217
+ @step_container = outline
218
+ statement[:steps].each { |s|
219
+ step(s)
220
+ }
221
+
222
+ statement[:examples].each { |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
246
+ }
247
+
248
+ return outline
249
+ end
250
+
251
+
252
+ #
253
+ # Examples for a scenario outline are called here. This section differs
254
+ # from the Cucumber parser because here each of the examples are exploded
255
+ # out here as individual scenarios and step definitions. This is so that
256
+ # later we can ensure that we have all the variations of the scenario
257
+ # outline defined to be displayed.
258
+ #
259
+ def examples(examples, outline)
260
+ #log.debug "EXAMPLES"
261
+ return if has_exclude_tags?(examples[:tags].map { |t| t[:name].gsub(/^@/, '') })
262
+ example = YARD::CodeObjects::Cucumber::ScenarioOutline::Examples.new(:keyword => examples[:keyword],
263
+ :name => examples[:name],
264
+ :line => examples[:location][:line],
265
+ :comments => examples[:comments] ? examples.comments.map{|comment| comment.value}.join("\n") : '',
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
+
274
+ example.rows = [examples[:tableHeader][:cells].map{ |c| c[:value] }] if examples[:tableHeader]
275
+ example.rows += matrix(examples[:tableBody]) if examples[:tableBody]
276
+
277
+ # add the example to the step containers list of examples
278
+
279
+ @step_container.examples << example
280
+
281
+ # For each example data row we want to generate a new scenario using our
282
+ # current scenario as the template.
283
+
284
+ example.data.length.times do |row_index|
285
+
286
+ # Generate a copy of the scenario.
287
+
288
+ scenario = YARD::CodeObjects::Cucumber::Scenario.new(@step_container,"example_#{@step_container.scenarios.length + 1}") do |s|
289
+ s.comments = @step_container.comments
290
+ s.description = @step_container.description
291
+ s.add_file(@file,@step_container.line_number)
292
+ s.keyword = @step_container.keyword
293
+ s.value = "#{@step_container.value} (#{@step_container.scenarios.length + 1})"
294
+ end
295
+
296
+ # Generate a copy of the scenario steps.
297
+
298
+ @step_container.steps.each do |step|
299
+ step_instance = YARD::CodeObjects::Cucumber::Step.new(scenario,step.line_number.to_s) do |s|
300
+ s.keyword = step.keyword.dup
301
+ s.value = step.value.dup
302
+ s.add_file(@file,step.line_number)
303
+
304
+ s.text = step.text.dup if step.has_text?
305
+ s.table = clone_table(step.table) if step.has_table?
306
+ end
307
+
308
+ # Look at the particular data for the example row and do a simple
309
+ # find and replace of the <key> with the associated values.
310
+
311
+ example.values_for_row(row_index).each do |key,text|
312
+ text ||= "" #handle empty cells in the example table
313
+ step_instance.value.gsub!("<#{key}>",text)
314
+ step_instance.text.gsub!("<#{key}>",text) if step_instance.has_text?
315
+ step_instance.table.each{|row| row.each{|col| col.gsub!("<#{key}>",text)}} if step_instance.has_table?
316
+ end
317
+
318
+ # Connect these steps that we created to the scenario we created
319
+ # and then add the steps to the scenario created.
320
+
321
+ step_instance.scenario = scenario
322
+ scenario.steps << step_instance
323
+ end
324
+
325
+ # Add the scenario to the list of scenarios maintained by the feature
326
+ # and add the feature to the scenario
327
+
328
+ scenario.feature = @feature
329
+ @step_container.scenarios << scenario
330
+
331
+ end
332
+
333
+ return example
334
+ end
335
+
336
+ #
337
+ # Called when a step is found. The step is refered to a table owner, though
338
+ # not all steps have a table or multliline arguments associated with them.
339
+ #
340
+ # If a multiline string is present with the step it is included as the text
341
+ # of the step. If the step has a table it is added to the step using the
342
+ # same method used by the Cucumber Gherkin model.
343
+ #
344
+ def step(step)
345
+ #log.debug "STEP"
346
+
347
+ @table_owner = YARD::CodeObjects::Cucumber::Step.new(@step_container,"#{step[:location][:line]}") do |s|
348
+ s.keyword = step[:keyword]
349
+ s.value = step[:text]
350
+ s.add_file(@file,step[:location][:line])
351
+ end
352
+
353
+ @table_owner.comments = step[:comments] ? step[:comments].map{|comment| comment.value}.join("\n") : ''
354
+
355
+ multiline_arg = step[:argument]
356
+
357
+ case(multiline_arg[:type])
358
+ when :DocString
359
+ @table_owner.text = multiline_arg[:content]
360
+ when :DataTable
361
+ #log.info "Matrix: #{matrix(multiline_arg).collect{|row| row.collect{|cell| cell.class } }.flatten.join("\n")}"
362
+ @table_owner.table = matrix(multiline_arg[:rows])
363
+ end if multiline_arg
364
+
365
+ @table_owner.scenario = @step_container
366
+ @step_container.steps << @table_owner
367
+ end
368
+
369
+ # Defined in the cucumber version so left here. No events for the end-of-file
370
+ def eof
371
+ end
372
+
373
+ # When a syntax error were to occurr. This parser is not interested in errors
374
+ def syntax_error(state, event, legal_events, line)
375
+ # raise "SYNTAX ERROR"
376
+ end
377
+
378
+ private
379
+ def matrix(gherkin_table)
380
+ gherkin_table.map {|gherkin_row| gherkin_row[:cells].map{ |cell| cell[:value] } }
381
+ end
382
+
383
+ #
384
+ # This helper method is used to deteremine what class is the current
385
+ # Gherkin class.
386
+ #
387
+ # @return [Class] the class that is the current supported Gherkin Model
388
+ # for multiline strings. Prior to Gherkin 2.4.0 this was the PyString
389
+ # class. As of Gherkin 2.4.0 it is the DocString class.
390
+ def gherkin_multiline_string_class
391
+ if defined?(Gherkin::Formatter::Model::PyString)
392
+ Gherkin::Formatter::Model::PyString
393
+ elsif defined?(Gherkin::Formatter::Model::DocString)
394
+ Gherkin::Formatter::Model::DocString
395
+ else
396
+ raise "Unable to find a suitable class in the Gherkin Library to parse the multiline step data."
397
+ end
398
+ end
399
+
400
+ def clone_table(base)
401
+ base.map {|row| row.map {|cell| cell.dup }}
402
+ end
403
+
404
+ def has_exclude_tags?(tags)
405
+ if YARD::Config.options["yard-gherkin-turnip"] and YARD::Config.options["yard-gherkin-turnip"]["exclude_tags"]
406
+ return true unless (YARD::Config.options["yard-gherkin-turnip"]["exclude_tags"] & tags).empty?
407
+ end
408
+ end
409
+
410
+ end
411
+ end
412
+ end