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,3 @@
1
+ module YardTurnip
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,24 @@
1
+ module YARD::CodeObjects::Cucumber
2
+ module LocationHelper
3
+
4
+ def line_number
5
+ files.first.last
6
+ end
7
+
8
+ def file
9
+ files.first.first if files && !files.empty?
10
+ end
11
+
12
+ def location
13
+ "#{file}:#{line_number}"
14
+ end
15
+ end
16
+
17
+ class Base < YARD::CodeObjects::Base
18
+ include LocationHelper
19
+
20
+ def path
21
+ @value || super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module YARD::CodeObjects::Cucumber
2
+ class Feature < NamespaceObject
3
+ attr_accessor :background, :comments, :description, :keyword, :scenarios, :tags, :value
4
+
5
+ def total_scenarios
6
+ scenarios.count
7
+ end
8
+
9
+ def initialize(namespace, name)
10
+ @comments = ""
11
+ @scenarios = []
12
+ @tags = []
13
+ super(namespace, name.to_s.strip)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ module YARD::CodeObjects::Cucumber
2
+ class NamespaceObject < YARD::CodeObjects::NamespaceObject
3
+ include LocationHelper
4
+
5
+ def value;
6
+ nil;
7
+ end
8
+ end
9
+
10
+ class Requirements < NamespaceObject;
11
+ end
12
+ class FeatureTags < NamespaceObject;
13
+ end
14
+ class StepTransformers < NamespaceObject;
15
+ end
16
+
17
+ class FeatureDirectory < YARD::CodeObjects::NamespaceObject
18
+
19
+ attr_accessor :description
20
+
21
+ def initialize(namespace, name)
22
+ super(namespace, name)
23
+ @description = ""
24
+ end
25
+
26
+ def location
27
+ files.first.first if files && !files.empty?
28
+ end
29
+
30
+ def expanded_path
31
+ to_s.split('::')[1..-1].join('/')
32
+ end
33
+
34
+ def value;
35
+ name;
36
+ end
37
+
38
+ def features
39
+ children.find_all { |d| d.is_a?(Feature) }
40
+ end
41
+
42
+ def subdirectories
43
+ subdirectories = children.find_all { |d| d.is_a?(FeatureDirectory) }
44
+ subdirectories + subdirectories.collect { |s| s.subdirectories }.flatten
45
+ end
46
+
47
+ end
48
+
49
+ CUCUMBER_NAMESPACE = Requirements.new(:root, "requirements") unless defined?(CUCUMBER_NAMESPACE)
50
+
51
+ CUCUMBER_TAG_NAMESPACE = FeatureTags.new(CUCUMBER_NAMESPACE, "tags") unless defined?(CUCUMBER_TAG_NAMESPACE)
52
+
53
+ CUCUMBER_STEPTRANSFORM_NAMESPACE = StepTransformers.new(CUCUMBER_NAMESPACE, "step_transformers") unless defined?(CUCUMBER_STEPTRANSFORM_NAMESPACE)
54
+
55
+ end
@@ -0,0 +1,22 @@
1
+ module YARD::CodeObjects::Cucumber
2
+
3
+ class Scenario < NamespaceObject
4
+
5
+ attr_accessor :value, :comments, :keyword, :description, :steps, :tags, :feature
6
+
7
+ def initialize(namespace, name)
8
+ super(namespace, name.to_s.strip)
9
+ @comments = @description = @keyword = @value = @feature = nil
10
+ @steps = []
11
+ @tags = []
12
+ end
13
+
14
+ def background?
15
+ @keyword == "Background"
16
+ end
17
+
18
+ def outline?
19
+ false
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,68 @@
1
+ module YARD::CodeObjects::Cucumber
2
+ class ScenarioOutline < NamespaceObject
3
+
4
+ attr_accessor :value, :comments, :keyword, :description, :steps, :tags, :feature
5
+ attr_accessor :scenarios, :examples
6
+
7
+ def initialize(namespace, name)
8
+ super(namespace, name.to_s.strip)
9
+ @comments = @description = @value = @feature = nil
10
+ @steps = []
11
+ @tags = []
12
+ @scenarios = []
13
+ @examples = []
14
+ end
15
+
16
+ def background?
17
+ false
18
+ end
19
+
20
+ def outline?
21
+ true
22
+ end
23
+
24
+ def examples?
25
+ @examples.find { |example| example.rows }
26
+ end
27
+
28
+ class Examples
29
+
30
+ attr_accessor :name, :line, :keyword, :comments, :rows, :tags, :scenario
31
+
32
+ # The first row of the rows contains the headers for the table
33
+ def headers
34
+ rows.first
35
+ end
36
+
37
+ # The data of the table starts at the second row. When there is no data then
38
+ # return a empty string.
39
+ def data
40
+ rows ? rows[1..-1] : ""
41
+ end
42
+
43
+ def values_for_row(row)
44
+ hash = {}
45
+
46
+ headers.each_with_index do |header, index|
47
+ hash[header] = data[row][index]
48
+ end
49
+
50
+ hash
51
+ end
52
+
53
+ def to_hash
54
+ hash = {}
55
+
56
+ rows.each_with_index do |header, index|
57
+ hash[header] = rows.collect { |row| row[index] }
58
+ end
59
+
60
+ hash
61
+ end
62
+
63
+ def initialize(parameters = {})
64
+ parameters.each { |key, value| send("#{key.to_sym}=", value) if respond_to? "#{key.to_sym}=" }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,46 @@
1
+ module YARD::CodeObjects::Cucumber
2
+ class Step < Base
3
+
4
+ attr_accessor :comments,
5
+ :definition,
6
+ :examples,
7
+ :keyword,
8
+ :scenario,
9
+ :table,
10
+ :text,
11
+ :placeholders,
12
+ :value
13
+
14
+ def initialize(namespace, name)
15
+ super(namespace, name.to_s.strip)
16
+ @comments = @definition = @description = @keyword = @table = @text = @value = nil
17
+ @examples = {}
18
+ @placeholders = []
19
+ end
20
+
21
+ def has_table?
22
+ !@table.nil?
23
+ end
24
+
25
+ def has_text?
26
+ !@text.nil?
27
+ end
28
+
29
+ def definition=(stepdef)
30
+ @definition = stepdef
31
+
32
+ unless stepdef.steps.map(&:files).include?(files)
33
+ stepdef.steps << self
34
+
35
+ stepdef.placeholders.each do |placeholder|
36
+ placeholders << placeholder
37
+ placeholder.steps << self
38
+ end
39
+ end
40
+ end
41
+
42
+ def transformed?
43
+ !@placeholders.empty?
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ module YARD::CodeObjects::Cucumber
2
+ class Tag < NamespaceObject
3
+
4
+ attr_accessor :value, :owners, :total_scenarios
5
+
6
+ def features
7
+ @owners.find_all { |owner| owner.is_a?(Feature) }
8
+ end
9
+
10
+ def scenarios
11
+ all = @owners.find_all do |owner|
12
+ owner.is_a?(Scenario) || owner.is_a?(ScenarioOutline) || ()
13
+ end
14
+
15
+ @owners.each do |owner|
16
+ if owner.is_a?(ScenarioOutline::Examples) && !all.include?(owner.scenario)
17
+ all << owner.scenario
18
+ end
19
+ end
20
+ all
21
+ end
22
+
23
+ def indirect_scenarios
24
+ @owners.find_all { |owner| owner.is_a?(Feature) }.collect { |feature| feature.scenarios }.flatten
25
+ end
26
+
27
+ def all_scenarios
28
+ scenarios + indirect_scenarios
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,45 @@
1
+ module YARD::CodeObjects
2
+ class Placeholder < StepTransformer
3
+
4
+ CONSTANT_PATTERN = /#\{\s*([^}]+)\s*\}/
5
+
6
+ DEFAULT_PLACE_HOLDER_REGEXP_STRING = "['\"]?((?:(?<=\")[^\"]*)(?=\")|(?:(?<=')[^']*(?='))|(?<!['\"])[[:alnum:]_-]+(?!['\"]))['\"]?"
7
+
8
+ def value
9
+ return if @value.nil?
10
+ unless @processed
11
+ @processed = true
12
+ loop do
13
+ break if substitute_constants.nil?
14
+ end
15
+ end
16
+ @value
17
+ end
18
+
19
+ private
20
+
21
+ def substitute_constants
22
+ @value.gsub!(CONSTANT_PATTERN) do |_|
23
+ find_value_for_constant($1)
24
+ end
25
+ end
26
+
27
+ # Look through the specified data for the escape pattern and return an array
28
+ # of those constants found. This defaults to the @value within step transformer
29
+ # as it is used internally, however, it can be called externally if it's
30
+ # needed somewhere.
31
+ def constants_from_value(data=@value)
32
+ data.scan(CONSTANT_PATTERN).flatten.collect { |value| value.strip }
33
+ end
34
+
35
+ #
36
+ # Looking through all the constants in the registry and returning the value
37
+ # with the regex items replaced from the constnat if present
38
+ #
39
+ def find_value_for_constant(name)
40
+ constant = YARD::Registry.all(:constant).find{|c| c.name == constant.to_sym }
41
+ log.warn "StepTransformer#find_value_for_constant : Could not find the CONSTANT [#{name}] using the string value." unless constant
42
+ constant ? constant.value : DEFAULT_PLACE_HOLDER_REGEXP_STRING
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ module YARD::CodeObjects
2
+ class StepDefinition < StepTransformer
3
+
4
+ attr_accessor :placeholders
5
+
6
+ OPTIONAL_WORD_REGEXP = %r{(\\\s)?\\\((?!:)([^)]+)\\\)(\\s)?}
7
+ PLACEHOLDER_REGEXP = /(:[\w]+)/
8
+ ALTERNATIVE_WORD_REGEXP = /([[:alpha:]]+)((\/[[:alpha:]]+)+)/
9
+
10
+ def value
11
+ unless @processed
12
+ @placeholders = []
13
+ @processed = true
14
+ @value = Regexp.escape(@value)
15
+ @value.gsub!(PLACEHOLDER_REGEXP) do |_|
16
+ find_value_for_placeholder($1)
17
+ end
18
+ @value.gsub!(OPTIONAL_WORD_REGEXP) do |_|
19
+ [$1, $2, $3].compact.map { |m| "(?:#{m})?" }.join
20
+ end
21
+ @value.gsub!(ALTERNATIVE_WORD_REGEXP) do |_|
22
+ "(?:#{$1}#{$2.tr('/', '|')})"
23
+ end
24
+ end
25
+ @value
26
+ end
27
+
28
+
29
+ private
30
+
31
+ #
32
+ # Looking through all the constants in the registry and returning the value
33
+ # with the regex items replaced from the constnat if present
34
+ #
35
+ def find_value_for_placeholder(name)
36
+ placeholder_matches = YARD::Registry.all(:placeholder).select{ |p| p.literal_value == name }
37
+ regex = if placeholder_matches.empty?
38
+ YARD::CodeObjects::Placeholder::DEFAULT_PLACE_HOLDER_REGEXP_STRING
39
+ else
40
+ placeholders.push(*placeholder_matches)
41
+ placeholder_matches.map(&:regex).join('|')
42
+ end
43
+ "(?<placeholder_#{name[1..-1]}>#{regex})"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ module YARD::CodeObjects
2
+ class StepTransformer < Base
3
+
4
+ include Cucumber::LocationHelper
5
+
6
+ attr_reader :constants, :keyword, :source, :value
7
+ attr_accessor :steps, :pending, :substeps, :literal_value
8
+
9
+
10
+
11
+ #
12
+ # Set the literal value and the value of the step definition.
13
+ #
14
+ # The literal value is as it appears in the step definition file with any
15
+ # constants. The value, when retrieved will attempt to replace those
16
+ # constants with their regex or string equivalents to hopefully match more
17
+ # steps and step definitions.
18
+ #
19
+ #
20
+ def value=(value)
21
+ @literal_value ||= format_source(value)
22
+ @value = format_source(value)
23
+
24
+ @steps = []
25
+ end
26
+
27
+ # Generate a regex with the step transformers value
28
+ def regex
29
+ @regex ||= Regexp.new(value)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module YARD
2
+ module Handlers
3
+ module Cucumber
4
+
5
+ class Base < Handlers::Base
6
+ class << self
7
+ include Parser::Cucumber
8
+ def handles?(node)
9
+ handlers.any? do |a_handler|
10
+ #log.debug "YARD::Handlers::Cucumber::Base#handles?(#{node.class})"
11
+ node.class == a_handler
12
+ end
13
+ end
14
+ include Parser::Cucumber
15
+ end
16
+ end
17
+
18
+ Processor.register_handler_namespace :feature, Cucumber
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,96 @@
1
+ module YARD
2
+ module Handlers
3
+ module Cucumber
4
+ class FeatureHandler < Base
5
+
6
+ handles CodeObjects::Cucumber::Feature
7
+
8
+ def process
9
+ #
10
+ # Features have already been created when they were parsed. So there
11
+ # is no need to process the feature further. Previously this is where
12
+ # feature steps were matched to step definitions and step definitions
13
+ # were matched to placeholders. This only worked if the feature
14
+ # files were were assured to be processed last which was accomplished
15
+ # by overriding YARD::SourceParser to make it load file in a similar
16
+ # order as Cucumber.
17
+ #
18
+ # As of YARD 0.7.0 this is no longer necessary as there are callbacks
19
+ # that can be registered after all the files have been loaded. That
20
+ # callback _after_parse_list_ is defined below and performs the
21
+ # functionality described above.
22
+ #
23
+ end
24
+
25
+ #
26
+ # Register, once, when that when all files are finished to perform
27
+ # the final matching of feature steps to step definitions and step
28
+ # definitions to step placeholders.
29
+ #
30
+ YARD::Parser::SourceParser.after_parse_list do |files,globals|
31
+ # For every feature found in the Registry, find their steps and step
32
+ # definitions...
33
+ YARD::Registry.all(:feature).each do |feature|
34
+ log.debug "Finding #{feature.file} - steps, step definitions, and step placeholders"
35
+ FeatureHandler.match_steps_to_step_definitions(feature)
36
+ end
37
+ end
38
+
39
+ class << self
40
+ def match_steps_to_step_definitions(statement)
41
+ if statement
42
+ # For the background and the scenario, find the steps that have definitions
43
+ process_scenario(statement.background) if statement.background
44
+
45
+ statement.scenarios.each do |scenario|
46
+ if scenario.outline?
47
+ #log.info "Scenario Outline: #{scenario.value}"
48
+ scenario.scenarios.each_with_index do |example,index|
49
+ #log.info " * Processing Example #{index + 1}"
50
+ process_scenario(example)
51
+ end
52
+ else
53
+ process_scenario(scenario)
54
+ end
55
+ end
56
+ else
57
+ log.warn "Empty feature file. A feature failed to process correctly or contains no feature"
58
+ end
59
+
60
+ rescue YARD::Handlers::NamespaceMissingError
61
+ rescue Exception => exception
62
+ log.error "Skipping feature because an error has occurred."
63
+ log.error "\n#{exception}\n#{exception.backtrace.join("\n")}\n"
64
+ end
65
+
66
+ # process a scenario
67
+ def process_scenario(scenario)
68
+ scenario.steps.each {|step| process_step(step) }
69
+ end
70
+
71
+ # process a step
72
+ def process_step(step)
73
+ match_step_to_step_definition_and_placeholders(step)
74
+ end
75
+
76
+ #
77
+ # Given a step object, attempt to match that step to a step
78
+ # transformation
79
+ #
80
+ def match_step_to_step_definition_and_placeholders(step)
81
+ YARD::Registry.all(:stepdefinition).each do |stepdef|
82
+ stepdef_matches = stepdef.regex.match(step.value)
83
+
84
+ if stepdef_matches
85
+ step.definition = stepdef
86
+ # Step has been matched to step definition and step placeholders
87
+ # TODO: If the step were to match again then we would be able to display ambigous step definitions
88
+ break
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end