From 4a1329e9808910755f6c04e406bba1537d75ca14 Mon Sep 17 00:00:00 2001 From: Eric Kessler Date: Fri, 8 Dec 2017 19:41:41 -0500 Subject: [PATCH 1/6] Add Gherkin abstraction layer Using the `cukemodeler` gem as an abstraction layer over the `gherkin` gem in order to insulate the project from breaking changes. Most tests passing. --- Gemfile | 6 +-- gherkin_lint.gemspec | 1 + lib/gherkin_lint.rb | 17 ++++++- lib/gherkin_lint/linter.rb | 47 ++++++++++--------- .../avoid_outline_for_single_example.rb | 8 ++-- lib/gherkin_lint/linter/avoid_period.rb | 6 +-- lib/gherkin_lint/linter/avoid_scripting.rb | 8 ++-- .../linter/background_does_more_than_setup.rb | 4 +- .../background_requires_multiple_scenarios.rb | 2 +- lib/gherkin_lint/linter/bad_scenario_name.rb | 4 +- lib/gherkin_lint/linter/be_declarative.rb | 4 +- .../linter/file_name_differs_feature_name.rb | 4 +- lib/gherkin_lint/linter/invalid_step_flow.rb | 10 ++-- .../linter/missing_example_name.rb | 8 ++-- .../linter/missing_feature_description.rb | 2 +- .../linter/missing_feature_name.rb | 2 +- .../linter/missing_scenario_name.rb | 2 +- .../linter/missing_test_action.rb | 2 +- .../linter/missing_verification.rb | 2 +- .../linter/same_tag_for_all_scenarios.rb | 24 +++++----- lib/gherkin_lint/linter/tag_collector.rb | 4 +- lib/gherkin_lint/linter/tag_constraint.rb | 4 +- .../linter/tag_used_multiple_times.rb | 4 +- lib/gherkin_lint/linter/too_clumsy.rb | 2 +- lib/gherkin_lint/linter/too_long_step.rb | 4 +- .../linter/too_many_different_tags.rb | 4 +- lib/gherkin_lint/linter/too_many_steps.rb | 4 +- .../linter/unique_scenario_names.rb | 4 +- lib/gherkin_lint/linter/unknown_variable.rb | 20 ++++---- lib/gherkin_lint/linter/unused_variable.rb | 24 +++++----- lib/gherkin_lint/linter/use_background.rb | 36 +++++++------- lib/gherkin_lint/linter/use_outline.rb | 11 ++--- 32 files changed, 151 insertions(+), 133 deletions(-) diff --git a/Gemfile b/Gemfile index 000b631..8da6eee 100644 --- a/Gemfile +++ b/Gemfile @@ -5,9 +5,9 @@ gemspec gem 'amatch' gem 'aruba', '1.0.0.pre.alpha.2' gem 'engtagger', '>=0.2.1' -gem 'gherkin', '>=4.0.0' -# TODO: update to gherkin4 gem 'gherkin_format' -# TODO: update to gherkin4 gem 'gherkin_language' +gem 'cuke_modeler', '~> 1.3' +# TODO: update to cuke_modeler gem 'gherkin_format' +# TODO: update to cuke_modeler gem 'gherkin_language' gem 'rake' gem 'rspec' gem 'rubocop' diff --git a/gherkin_lint.gemspec b/gherkin_lint.gemspec index 7579d2c..6877100 100644 --- a/gherkin_lint.gemspec +++ b/gherkin_lint.gemspec @@ -10,6 +10,7 @@ Gem::Specification.new do |s| s.files = `git ls-files`.split("\n") s.executables = s.files.grep(%r{^bin/}) { |file| File.basename(file) } s.add_runtime_dependency 'amatch', ['~> 0.3', '>= 0.3.0'] + s.add_runtime_dependency 'cuke_modeler', ['~> 1.3'] s.add_runtime_dependency 'engtagger', ['~> 0.2', '>= 0.2.0'] s.add_runtime_dependency 'gherkin', ['>= 4.0.0', '< 6.0'] s.add_runtime_dependency 'multi_json', ['~> 1.12', '>= 1.12.1'] diff --git a/lib/gherkin_lint.rb b/lib/gherkin_lint.rb index 82bdcb6..4125e34 100644 --- a/lib/gherkin_lint.rb +++ b/lib/gherkin_lint.rb @@ -1,6 +1,6 @@ gem 'gherkin', '>=4.0.0' -require 'gherkin/parser' +require 'cuke_modeler' require 'gherkin_lint/linter' require 'gherkin_lint/linter/avoid_outline_for_single_example' require 'gherkin_lint/linter/avoid_period' @@ -92,9 +92,11 @@ def evaluate_members(linter) end def analyze(file) - @files[file] = parse file + @files[file] = model file end + #TODO: remove this method + # Deprecated def parse(file) to_json File.read(file) end @@ -114,6 +116,8 @@ def disable_tags LINTER.map { |lint| "disable#{lint.new.class.name.split('::').last}" } end + #TODO: remove this method + # Deprecated def to_json(input) parser = Gherkin::Parser.new scanner = Gherkin::TokenScanner.new input @@ -125,5 +129,14 @@ def print(issues) puts 'There are no issues' if issues.empty? && @verbose issues.each { |issue| puts issue.render } end + + + private + + + def model(file) + CukeModeler::FeatureFile.new(file) + end + end end diff --git a/lib/gherkin_lint/linter.rb b/lib/gherkin_lint/linter.rb index eb49a12..71c136b 100644 --- a/lib/gherkin_lint/linter.rb +++ b/lib/gherkin_lint/linter.rb @@ -16,8 +16,8 @@ def initialize end def features - @files.each do |file, content| - feature = content[:feature] + @files.each do |file, model| + feature = model.feature next if feature.nil? yield(file, feature) end @@ -29,39 +29,44 @@ def files def scenarios elements do |file, feature, scenario| - next if scenario[:type] == :Background + next if scenario.is_a?(CukeModeler::Background) yield(file, feature, scenario) end end def filled_scenarios scenarios do |file, feature, scenario| - next unless scenario.include? :steps - next if scenario[:steps].empty? + next unless scenario.respond_to? :steps + next if scenario.steps.empty? yield(file, feature, scenario) end end def steps elements do |file, feature, scenario| - next unless scenario.include? :steps - scenario[:steps].each { |step| yield(file, feature, scenario, step) } + next unless scenario.steps.any? + scenario.steps.each { |step| yield(file, feature, scenario, step) } end end def backgrounds elements do |file, feature, scenario| - next unless scenario[:type] == :Background + next unless scenario.is_a?(CukeModeler::Background) yield(file, feature, scenario) end end def elements - @files.each do |file, content| - feature = content[:feature] + @files.each do |file, model| + feature = model.feature next if feature.nil? - next unless feature.key? :children - feature[:children].each do |scenario| + next unless feature.background || feature.tests.any? + + everything = [] + everything << feature.background if feature.background + everything += feature.tests + + everything.each do |scenario| yield(file, feature, scenario) end end @@ -114,17 +119,17 @@ def lint end def reference(file, feature = nil, scenario = nil, step = nil) - return file if feature.nil? || feature[:name].empty? - result = "#{file} (#{line(feature, scenario, step)}): #{feature[:name]}" - result += ".#{scenario[:name]}" unless scenario.nil? || scenario[:name].empty? - result += " step: #{step[:text]}" unless step.nil? + return file if feature.nil? || feature.name.empty? + result = "#{file} (#{line(feature, scenario, step)}): #{feature.name}" + result += ".#{scenario.name}" unless scenario.nil? || scenario.name.empty? + result += " step: #{step.text}" unless step.nil? result end def line(feature, scenario, step) - line = feature.nil? ? nil : feature[:location][:line] - line = scenario[:location][:line] unless scenario.nil? - line = step[:location][:line] unless step.nil? + line = feature.nil? ? nil : feature.source_line + line = scenario.source_line unless scenario.nil? + line = step.source_line unless step.nil? line end @@ -137,8 +142,8 @@ def add_warning(references, description = nil) end def render_step(step) - value = "#{step[:keyword]}#{step[:text]}" - value += render_step_argument step[:argument] if step.include? :argument + value = "#{step.keyword} #{step.text}" + value += render_step_argument step.block if step.block value end diff --git a/lib/gherkin_lint/linter/avoid_outline_for_single_example.rb b/lib/gherkin_lint/linter/avoid_outline_for_single_example.rb index 5cf70ce..1f1c787 100644 --- a/lib/gherkin_lint/linter/avoid_outline_for_single_example.rb +++ b/lib/gherkin_lint/linter/avoid_outline_for_single_example.rb @@ -5,11 +5,11 @@ module GherkinLint class AvoidOutlineForSingleExample < Linter def lint scenarios do |file, feature, scenario| - next unless scenario[:type] == :ScenarioOutline + next unless scenario.is_a?(CukeModeler::Outline) - next unless scenario.key? :examples - next if scenario[:examples].length > 1 - next if scenario[:examples].first[:tableBody].length > 1 + next unless scenario.examples.any? + next if scenario.examples.length > 1 + next if scenario.examples.first.argument_rows.length > 1 references = [reference(file, feature, scenario)] add_error(references, 'Better write a scenario') diff --git a/lib/gherkin_lint/linter/avoid_period.rb b/lib/gherkin_lint/linter/avoid_period.rb index 87b60e3..091f51a 100644 --- a/lib/gherkin_lint/linter/avoid_period.rb +++ b/lib/gherkin_lint/linter/avoid_period.rb @@ -5,11 +5,11 @@ module GherkinLint class AvoidPeriod < Linter def lint scenarios do |file, feature, scenario| - next unless scenario.key? :steps + next unless scenario.respond_to? :steps - scenario[:steps].each do |step| + scenario.steps.each do |step| references = [reference(file, feature, scenario, step)] - add_error(references) if step[:text].strip.end_with? '.' + add_error(references) if step.text.strip.end_with? '.' end end end diff --git a/lib/gherkin_lint/linter/avoid_scripting.rb b/lib/gherkin_lint/linter/avoid_scripting.rb index 2a2d30d..0bcf2c0 100644 --- a/lib/gherkin_lint/linter/avoid_scripting.rb +++ b/lib/gherkin_lint/linter/avoid_scripting.rb @@ -5,7 +5,7 @@ module GherkinLint class AvoidScripting < Linter def lint filled_scenarios do |file, feature, scenario| - steps = filter_when_steps scenario[:steps] + steps = filter_when_steps scenario.steps next if steps.length <= 1 references = [reference(file, feature, scenario)] @@ -14,9 +14,9 @@ def lint end def filter_when_steps(steps) - steps = steps.drop_while { |step| step[:keyword] != 'When ' } - steps = steps.reverse.drop_while { |step| step[:keyword] != 'Then ' }.reverse - steps.reject { |step| step[:keyword] == 'Then ' } + steps = steps.drop_while { |step| step.keyword != 'When' } + steps = steps.reverse.drop_while { |step| step.keyword != 'Then' }.reverse + steps.reject { |step| step.keyword == 'Then' } end end end diff --git a/lib/gherkin_lint/linter/background_does_more_than_setup.rb b/lib/gherkin_lint/linter/background_does_more_than_setup.rb index d747322..8441021 100644 --- a/lib/gherkin_lint/linter/background_does_more_than_setup.rb +++ b/lib/gherkin_lint/linter/background_does_more_than_setup.rb @@ -5,8 +5,8 @@ module GherkinLint class BackgroundDoesMoreThanSetup < Linter def lint backgrounds do |file, feature, background| - next unless background.key? :steps - invalid_steps = background[:steps].select { |step| step[:keyword] == 'When ' || step[:keyword] == 'Then ' } + next unless background.steps.any? + invalid_steps = background.steps.select { |step| step.keyword == 'When' || step.keyword == 'Then' } next if invalid_steps.empty? references = [reference(file, feature, background, invalid_steps[0])] add_error(references, 'Just Given Steps allowed') diff --git a/lib/gherkin_lint/linter/background_requires_multiple_scenarios.rb b/lib/gherkin_lint/linter/background_requires_multiple_scenarios.rb index cea0d88..fcc17d2 100644 --- a/lib/gherkin_lint/linter/background_requires_multiple_scenarios.rb +++ b/lib/gherkin_lint/linter/background_requires_multiple_scenarios.rb @@ -5,7 +5,7 @@ module GherkinLint class BackgroundRequiresMultipleScenarios < Linter def lint backgrounds do |file, feature, background| - scenarios = feature[:children].reject { |element| element[:type] == :Background } + scenarios = feature.children.reject { |element| element.is_a?(CukeModeler::Background) } next if scenarios.length >= 2 references = [reference(file, feature, background)] diff --git a/lib/gherkin_lint/linter/bad_scenario_name.rb b/lib/gherkin_lint/linter/bad_scenario_name.rb index 280af8c..e4161af 100644 --- a/lib/gherkin_lint/linter/bad_scenario_name.rb +++ b/lib/gherkin_lint/linter/bad_scenario_name.rb @@ -5,12 +5,12 @@ module GherkinLint class BadScenarioName < Linter def lint scenarios do |file, feature, scenario| - next if scenario[:name].empty? + next if scenario.name.empty? references = [reference(file, feature, scenario)] description = 'Prefer to rely just on Given and When steps when name your scenario to keep it stable' bad_words = %w[test verif check] bad_words.each do |bad_word| - add_error(references, description) if scenario[:name].downcase.include? bad_word + add_error(references, description) if scenario.name.downcase.include? bad_word end end end diff --git a/lib/gherkin_lint/linter/be_declarative.rb b/lib/gherkin_lint/linter/be_declarative.rb index a75142b..2f44414 100644 --- a/lib/gherkin_lint/linter/be_declarative.rb +++ b/lib/gherkin_lint/linter/be_declarative.rb @@ -10,7 +10,7 @@ def initialize def lint filled_scenarios do |file, feature, scenario| - scenario[:steps].each do |step| + scenario.steps.each do |step| references = [reference(file, feature, scenario, step)] add_warning(references, 'no verb') unless verb? step end @@ -18,7 +18,7 @@ def lint end def verb?(step) - tagged = tagger.add_tags step[:text] + tagged = tagger.add_tags step.text step_verbs = verbs tagged !step_verbs.empty? diff --git a/lib/gherkin_lint/linter/file_name_differs_feature_name.rb b/lib/gherkin_lint/linter/file_name_differs_feature_name.rb index 0d8ebad..bb081f6 100644 --- a/lib/gherkin_lint/linter/file_name_differs_feature_name.rb +++ b/lib/gherkin_lint/linter/file_name_differs_feature_name.rb @@ -5,9 +5,9 @@ module GherkinLint class FileNameDiffersFeatureName < Linter def lint features do |file, feature| - next unless feature.include? :name + next if feature.name.empty? expected_feature_name = title_case file - next if ignore_whitespaces(feature[:name]).casecmp(ignore_whitespaces(expected_feature_name)) == 0 + next if ignore_whitespaces(feature.name).casecmp(ignore_whitespaces(expected_feature_name)) == 0 references = [reference(file, feature)] add_error(references, "Feature name should be '#{expected_feature_name}'") end diff --git a/lib/gherkin_lint/linter/invalid_step_flow.rb b/lib/gherkin_lint/linter/invalid_step_flow.rb index 2a4378e..7c921f5 100644 --- a/lib/gherkin_lint/linter/invalid_step_flow.rb +++ b/lib/gherkin_lint/linter/invalid_step_flow.rb @@ -5,7 +5,7 @@ module GherkinLint class InvalidStepFlow < Linter def lint filled_scenarios do |file, feature, scenario| - steps = scenario[:steps].select { |step| step[:keyword] != 'And ' && step[:keyword] != 'But ' } + steps = scenario.steps.select { |step| step.keyword != 'And' && step.keyword != 'But' } next if steps.empty? last_step_is_an_action(file, feature, scenario, steps) given_after_non_given(file, feature, scenario, steps) @@ -15,7 +15,7 @@ def lint def last_step_is_an_action(file, feature, scenario, steps) references = [reference(file, feature, scenario, steps.last)] - add_error(references, 'Last step is an action') if steps.last[:keyword] == 'When ' + add_error(references, 'Last step is an action') if steps.last.keyword == 'When' end def given_after_non_given(file, feature, scenario, steps) @@ -23,16 +23,16 @@ def given_after_non_given(file, feature, scenario, steps) steps.each do |step| references = [reference(file, feature, scenario, step)] description = 'Given after Action or Verification' - add_error(references, description) if step[:keyword] == 'Given ' && last_step[:keyword] != 'Given ' + add_error(references, description) if step.keyword == 'Given' && last_step.keyword != 'Given' last_step = step end end def verification_before_action(file, feature, scenario, steps) steps.each do |step| - break if step[:keyword] == 'When ' + break if step.keyword == 'When' references = [reference(file, feature, scenario, step)] - add_error(references, 'Missing Action') if step[:keyword] == 'Then ' + add_error(references, 'Missing Action') if step.keyword == 'Then' end end end diff --git a/lib/gherkin_lint/linter/missing_example_name.rb b/lib/gherkin_lint/linter/missing_example_name.rb index 6d97be7..75b8afc 100644 --- a/lib/gherkin_lint/linter/missing_example_name.rb +++ b/lib/gherkin_lint/linter/missing_example_name.rb @@ -5,10 +5,10 @@ module GherkinLint class MissingExampleName < Linter def lint scenarios do |file, feature, scenario| - next unless scenario.key? :examples - next unless scenario[:examples].length > 1 - scenario[:examples].each do |example| - name = example.key?(:name) ? example[:name].strip : '' + next unless scenario.respond_to? :examples + next unless scenario.examples.length > 1 + scenario.examples.each do |example| + name = example.respond_to?(:name) ? example.name.strip : '' next unless name.empty? references = [reference(file, feature, scenario)] add_error(references, 'No Example Name') diff --git a/lib/gherkin_lint/linter/missing_feature_description.rb b/lib/gherkin_lint/linter/missing_feature_description.rb index a896231..0d04aa6 100644 --- a/lib/gherkin_lint/linter/missing_feature_description.rb +++ b/lib/gherkin_lint/linter/missing_feature_description.rb @@ -5,7 +5,7 @@ module GherkinLint class MissingFeatureDescription < Linter def lint features do |file, feature| - name = feature.key?(:description) ? feature[:description].strip : '' + name = feature.description ? feature.description.strip : '' next unless name.empty? references = [reference(file, feature)] add_error(references, 'Favor a user story as description') diff --git a/lib/gherkin_lint/linter/missing_feature_name.rb b/lib/gherkin_lint/linter/missing_feature_name.rb index a002364..caf589a 100644 --- a/lib/gherkin_lint/linter/missing_feature_name.rb +++ b/lib/gherkin_lint/linter/missing_feature_name.rb @@ -5,7 +5,7 @@ module GherkinLint class MissingFeatureName < Linter def lint features do |file, feature| - name = feature.key?(:name) ? feature[:name].strip : '' + name = feature.name ? feature.name.strip : '' next unless name.empty? references = [reference(file, feature)] add_error(references, 'No Feature Name') diff --git a/lib/gherkin_lint/linter/missing_scenario_name.rb b/lib/gherkin_lint/linter/missing_scenario_name.rb index 96416ad..576e0cf 100644 --- a/lib/gherkin_lint/linter/missing_scenario_name.rb +++ b/lib/gherkin_lint/linter/missing_scenario_name.rb @@ -5,7 +5,7 @@ module GherkinLint class MissingScenarioName < Linter def lint scenarios do |file, feature, scenario| - name = scenario.key?(:name) ? scenario[:name].strip : '' + name = scenario.name ? scenario.name.strip : '' references = [reference(file, feature, scenario)] next unless name.empty? add_error(references, 'No Scenario Name') diff --git a/lib/gherkin_lint/linter/missing_test_action.rb b/lib/gherkin_lint/linter/missing_test_action.rb index 7bb4c39..86f072b 100644 --- a/lib/gherkin_lint/linter/missing_test_action.rb +++ b/lib/gherkin_lint/linter/missing_test_action.rb @@ -5,7 +5,7 @@ module GherkinLint class MissingTestAction < Linter def lint filled_scenarios do |file, feature, scenario| - when_steps = scenario[:steps].select { |step| step[:keyword] == 'When ' } + when_steps = scenario.steps.select { |step| step.keyword == 'When' } next unless when_steps.empty? references = [reference(file, feature, scenario)] add_error(references, 'No \'When\'-Step') diff --git a/lib/gherkin_lint/linter/missing_verification.rb b/lib/gherkin_lint/linter/missing_verification.rb index 33ddf24..8df1255 100644 --- a/lib/gherkin_lint/linter/missing_verification.rb +++ b/lib/gherkin_lint/linter/missing_verification.rb @@ -5,7 +5,7 @@ module GherkinLint class MissingVerification < Linter def lint filled_scenarios do |file, feature, scenario| - then_steps = scenario[:steps].select { |step| step[:keyword] == 'Then ' } + then_steps = scenario.steps.select { |step| step.keyword == 'Then' } next unless then_steps.empty? references = [reference(file, feature, scenario)] add_error(references, 'No verification step') diff --git a/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb b/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb index db3c892..9299b2b 100644 --- a/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb +++ b/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb @@ -5,7 +5,7 @@ module GherkinLint class SameTagForAllScenarios < Linter def lint features do |file, feature| - next unless feature.include? :children + next unless feature.tests.any? lint_scenarios file, feature lint_examples file, feature @@ -16,7 +16,7 @@ def lint_scenarios(file, feature) tags = gather_same_tags feature return if tags.nil? return if tags.empty? - return unless feature[:children].length > 1 + return unless feature.tests.length > 1 references = [reference(file, feature)] tags.each do |tag| next if tag == '@skip' @@ -26,10 +26,10 @@ def lint_scenarios(file, feature) end def lint_examples(file, feature) - feature[:children].each do |scenario| + feature.tests.each do |scenario| tags = gather_same_tags_for_outline scenario next if tags.nil? || tags.empty? - next unless scenario[:examples].length > 1 + next unless (scenario.is_a?(CukeModeler::Outline) && (scenario.examples.length > 1)) references = [reference(file, feature, scenario)] tags.each do |tag| next if tag == '@skip' @@ -41,10 +41,10 @@ def lint_examples(file, feature) def gather_same_tags(feature) result = nil - feature[:children].each do |scenario| - next if scenario[:type] == :Background - return nil unless scenario.include? :tags - tags = scenario[:tags].map { |tag| tag[:name] } + feature.children.each do |scenario| + next if scenario.is_a? CukeModeler::Background + return nil unless scenario.tags.any? + tags = scenario.tags.map { |tag| tag.name } result = tags if result.nil? result &= tags end @@ -53,10 +53,10 @@ def gather_same_tags(feature) def gather_same_tags_for_outline(scenario) result = nil - return result unless scenario.include? :examples - scenario[:examples].each do |example| - return nil unless example.include? :tags - tags = example[:tags].map { |tag| tag[:name] } + return result unless scenario.is_a?(CukeModeler::Outline) + scenario.examples.each do |example| + return nil unless example.tags.any? + tags = example.tags.map { |tag| tag.name } result = tags if result.nil? result &= tags end diff --git a/lib/gherkin_lint/linter/tag_collector.rb b/lib/gherkin_lint/linter/tag_collector.rb index 18b9ea8..67fc34d 100644 --- a/lib/gherkin_lint/linter/tag_collector.rb +++ b/lib/gherkin_lint/linter/tag_collector.rb @@ -2,8 +2,8 @@ module GherkinLint # Mixin to lint for tags based on their relationship to eachother module TagCollector def gather_tags(element) - return [] unless element.include? :tags - element[:tags].map { |tag| tag[:name][1..-1] } + return [] unless element.respond_to? :tags + element.tags.map { |tag| tag.name[1..-1] } end end end diff --git a/lib/gherkin_lint/linter/tag_constraint.rb b/lib/gherkin_lint/linter/tag_constraint.rb index 181f072..39ec7a3 100644 --- a/lib/gherkin_lint/linter/tag_constraint.rb +++ b/lib/gherkin_lint/linter/tag_constraint.rb @@ -11,8 +11,8 @@ def lint end def tags(element) - return [] unless element.include? :tags - element[:tags].map { |a| a[:name] } + return [] unless element.respond_to? :tags + element.tags.map { |a| a.name } end def matcher(pattern) diff --git a/lib/gherkin_lint/linter/tag_used_multiple_times.rb b/lib/gherkin_lint/linter/tag_used_multiple_times.rb index a3609fc..76687ca 100644 --- a/lib/gherkin_lint/linter/tag_used_multiple_times.rb +++ b/lib/gherkin_lint/linter/tag_used_multiple_times.rb @@ -14,8 +14,8 @@ def lint end def tags(element) - return [] unless element.include? :tags - element[:tags].map { |a| a[:name] } + return [] unless element.tags.any? + element.tags.map { |a| a.name } end end end diff --git a/lib/gherkin_lint/linter/too_clumsy.rb b/lib/gherkin_lint/linter/too_clumsy.rb index ec2578c..7603e93 100644 --- a/lib/gherkin_lint/linter/too_clumsy.rb +++ b/lib/gherkin_lint/linter/too_clumsy.rb @@ -5,7 +5,7 @@ module GherkinLint class TooClumsy < Linter def lint filled_scenarios do |file, feature, scenario| - characters = scenario[:steps].map { |step| step[:text].length }.inject(0, :+) + characters = scenario.steps.map { |step| step.text.length }.inject(0, :+) next if characters < 400 references = [reference(file, feature, scenario)] add_error(references, "Used #{characters} Characters") diff --git a/lib/gherkin_lint/linter/too_long_step.rb b/lib/gherkin_lint/linter/too_long_step.rb index f43d099..b7b7e46 100644 --- a/lib/gherkin_lint/linter/too_long_step.rb +++ b/lib/gherkin_lint/linter/too_long_step.rb @@ -5,9 +5,9 @@ module GherkinLint class TooLongStep < Linter def lint steps do |file, feature, scenario, step| - next if step[:text].length < 80 + next if step.text.length < 80 references = [reference(file, feature, scenario, step)] - add_error(references, "Used #{step[:text].length} characters") + add_error(references, "Used #{step.text.length} characters") end end end diff --git a/lib/gherkin_lint/linter/too_many_different_tags.rb b/lib/gherkin_lint/linter/too_many_different_tags.rb index c8cbb79..2659e6b 100644 --- a/lib/gherkin_lint/linter/too_many_different_tags.rb +++ b/lib/gherkin_lint/linter/too_many_different_tags.rb @@ -34,8 +34,8 @@ def warn_across_all_features(references, tags) end def tags_for_feature(feature) - return [] unless feature.include? :children - gather_tags(feature) + feature[:children].map { |scenario| gather_tags(scenario) }.flatten + return [] unless feature.tests.any? + gather_tags(feature) + feature.tests.map { |scenario| gather_tags(scenario) }.flatten end end end diff --git a/lib/gherkin_lint/linter/too_many_steps.rb b/lib/gherkin_lint/linter/too_many_steps.rb index c0df853..2594e4c 100644 --- a/lib/gherkin_lint/linter/too_many_steps.rb +++ b/lib/gherkin_lint/linter/too_many_steps.rb @@ -5,9 +5,9 @@ module GherkinLint class TooManySteps < Linter def lint filled_scenarios do |file, feature, scenario| - next if scenario[:steps].length < 10 + next if scenario.steps.length < 10 references = [reference(file, feature, scenario)] - add_error(references, "Used #{scenario[:steps].length} Steps") + add_error(references, "Used #{scenario.steps.length} Steps") end end end diff --git a/lib/gherkin_lint/linter/unique_scenario_names.rb b/lib/gherkin_lint/linter/unique_scenario_names.rb index aa6d2d3..5aea126 100644 --- a/lib/gherkin_lint/linter/unique_scenario_names.rb +++ b/lib/gherkin_lint/linter/unique_scenario_names.rb @@ -6,8 +6,8 @@ class UniqueScenarioNames < Linter def lint references_by_name = Hash.new [] scenarios do |file, feature, scenario| - next unless scenario.key? :name - scenario_name = "#{feature[:name]}.#{scenario[:name]}" + next unless scenario.name + scenario_name = "#{feature.name}.#{scenario.name}" references_by_name[scenario_name] = references_by_name[scenario_name] + [reference(file, feature, scenario)] end references_by_name.each do |name, references| diff --git a/lib/gherkin_lint/linter/unknown_variable.rb b/lib/gherkin_lint/linter/unknown_variable.rb index 2767a14..7b82c12 100644 --- a/lib/gherkin_lint/linter/unknown_variable.rb +++ b/lib/gherkin_lint/linter/unknown_variable.rb @@ -6,7 +6,7 @@ class UnknownVariable < Linter def lint filled_scenarios do |file, feature, scenario| known_vars = Set.new known_variables scenario - scenario[:steps].each do |step| + scenario.steps.each do |step| step_vars(step).each do |used_var| next if known_vars.include? used_var references = [reference(file, feature, scenario)] @@ -17,15 +17,15 @@ def lint end def step_vars(step) - vars = gather_vars step[:text] - return vars unless step.include? :argument - vars + gather_vars_from_argument(step[:argument]) + vars = gather_vars step.text + return vars unless step.block + vars + gather_vars_from_argument(step.block) end def gather_vars_from_argument(argument) - return gather_vars argument[:content] if argument[:type] == :DocString - (argument[:rows] || []).map do |row| - row[:cells].map { |value| gather_vars value[:value] }.flatten + return gather_vars argument.content if argument.is_a?(CukeModeler::DocString) + (argument.rows || []).map do |row| + row.cells.map { |value| gather_vars value.value }.flatten end.flatten end @@ -34,9 +34,9 @@ def gather_vars(string) end def known_variables(scenario) - (scenario[:examples] || []).map do |example| - next unless example.key? :tableHeader - example[:tableHeader][:cells].map { |cell| cell[:value].strip } + (scenario.respond_to?(:examples) ? scenario.examples : []).map do |example| + next unless example.parameter_row + example.parameter_row.cells.map { |cell| cell.value.strip } end.flatten end end diff --git a/lib/gherkin_lint/linter/unused_variable.rb b/lib/gherkin_lint/linter/unused_variable.rb index 0c05110..d0dee82 100644 --- a/lib/gherkin_lint/linter/unused_variable.rb +++ b/lib/gherkin_lint/linter/unused_variable.rb @@ -5,10 +5,10 @@ module GherkinLint class UnusedVariable < Linter def lint scenarios do |file, feature, scenario| - next unless scenario.key? :examples - scenario[:examples].each do |example| - next unless example.key? :tableHeader - example[:tableHeader][:cells].map { |cell| cell[:value] }.each do |variable| + next unless (scenario.is_a?(CukeModeler::Outline) && scenario.examples.any?) + scenario.examples.each do |example| + next unless example.parameter_row + example.parameter_row.cells.map { |cell| cell.value }.each do |variable| references = [reference(file, feature, scenario)] add_error(references, "'<#{variable}>' is unused") unless used?(variable, scenario) end @@ -18,10 +18,10 @@ def lint def used?(variable, scenario) variable = "<#{variable}>" - return false unless scenario.key? :steps - scenario[:steps].each do |step| - return true if step[:text].include? variable - next unless step.include? :argument + return false unless scenario.steps.any? + scenario.steps.each do |step| + return true if step.text.include? variable + next unless step.block return true if used_in_docstring?(variable, step) return true if used_in_table?(variable, step) end @@ -29,13 +29,13 @@ def used?(variable, scenario) end def used_in_docstring?(variable, step) - step[:argument][:type] == :DocString && step[:argument][:content].include?(variable) + step.block.is_a?(CukeModeler::DocString) && step.block.content.include?(variable) end def used_in_table?(variable, step) - return false unless step[:argument][:type] == :DataTable - step[:argument][:rows].each do |row| - row[:cells].each { |value| return true if value[:value].include?(variable) } + return false unless step.block.is_a?(CukeModeler::Table) + step.block.rows.each do |row| + row.cells.each { |value| return true if value.value.include?(variable) } end false end diff --git a/lib/gherkin_lint/linter/use_background.rb b/lib/gherkin_lint/linter/use_background.rb index 2e453aa..21bb10d 100644 --- a/lib/gherkin_lint/linter/use_background.rb +++ b/lib/gherkin_lint/linter/use_background.rb @@ -17,22 +17,22 @@ def lint def scenarios_with_steps(feature) scenarios = 0 - return 0 unless feature.key? :children - feature[:children].each do |scenario| - next unless scenario.include? :steps - next if scenario[:steps].empty? + return 0 unless feature.tests.any? + feature.tests.each do |scenario| + next unless scenario.steps.any? + next if scenario.steps.empty? scenarios += 1 end scenarios end def gather_givens(feature) - return unless feature.include? :children + return unless feature.tests.any? has_non_given_step = false - feature[:children].each do |scenario| - next unless scenario.include? :steps - next if scenario[:steps].empty? - has_non_given_step = true unless scenario[:steps].first[:keyword] == 'Given ' + feature.tests.each do |scenario| + next unless scenario.respond_to? :steps + next if scenario.steps.empty? + has_non_given_step = true unless scenario.steps.first.keyword == 'Given' end return if has_non_given_step @@ -42,12 +42,12 @@ def gather_givens(feature) end def expanded_steps(feature) - feature[:children].each do |scenario| - next unless scenario[:type] != :Background - next unless scenario.include? :steps - next if scenario[:steps].empty? - prototypes = [render_step(scenario[:steps].first)] - prototypes = expand_examples(scenario[:examples], prototypes) if scenario.key? :examples + feature.children.each do |scenario| + next if scenario.is_a? CukeModeler::Background + next unless scenario.respond_to? :steps + next if scenario.steps.empty? + prototypes = [render_step(scenario.steps.first)] + prototypes = expand_examples(scenario.examples, prototypes) if scenario.respond_to? :examples prototypes.each { |prototype| yield prototype } end end @@ -61,10 +61,10 @@ def expand_examples(examples, prototypes) def expand_outlines(sentence, example) result = [] - headers = example[:tableHeader][:cells].map { |cell| cell[:value] } - example[:tableBody].each do |row| # .slice(1, example[:tableBody].length).each do |row| + headers = example.parameter_row.cells.map { |cell| cell.value } + example.argument_rows.each do |row| # .slice(1, example[:tableBody].length).each do |row| modified_sentence = sentence.dup - headers.zip(row[:cells].map { |cell| cell[:value] }).map do |key, value| + headers.zip(row.cells.map { |cell| cell.value }).map do |key, value| modified_sentence.gsub!("<#{key}>", value) end result.push modified_sentence diff --git a/lib/gherkin_lint/linter/use_outline.rb b/lib/gherkin_lint/linter/use_outline.rb index a938bbc..20e1626 100644 --- a/lib/gherkin_lint/linter/use_outline.rb +++ b/lib/gherkin_lint/linter/use_outline.rb @@ -28,11 +28,10 @@ def determine_similarity(lhs, rhs) def gather_scenarios(file, feature) scenarios = [] - return scenarios unless feature.include? :children - feature[:children].each do |scenario| - next unless scenario[:type] == :Scenario - next unless scenario.include? :steps - next if scenario[:steps].empty? + return scenarios unless feature.tests.any? + feature.tests.each do |scenario| + next unless scenario.is_a? CukeModeler::Scenario + next if scenario.steps.empty? scenarios.push generate_reference(file, feature, scenario) end scenarios @@ -41,7 +40,7 @@ def gather_scenarios(file, feature) def generate_reference(file, feature, scenario) reference = {} reference[:reference] = reference(file, feature, scenario) - reference[:text] = scenario[:steps].map { |step| render_step(step) }.join ' ' + reference[:text] = scenario.steps.map { |step| render_step(step) }.join ' ' reference end end From eb7b154de0552cf3d85591125eeec4cb752b70c7 Mon Sep 17 00:00:00 2001 From: Eric Kessler Date: Sat, 9 Dec 2017 16:02:40 -0500 Subject: [PATCH 2/6] Update another method Updated another spot that was causing test failures. --- lib/gherkin_lint/linter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gherkin_lint/linter.rb b/lib/gherkin_lint/linter.rb index 71c136b..cd946b1 100644 --- a/lib/gherkin_lint/linter.rb +++ b/lib/gherkin_lint/linter.rb @@ -148,9 +148,9 @@ def render_step(step) end def render_step_argument(argument) - return "\n#{argument[:content]}" if argument[:type] == :DocString - result = argument[:rows].map do |row| - "|#{row[:cells].map { |cell| cell[:value] }.join '|'}|" + return "\n#{argument.content}" if argument.is_a?(CukeModeler::DocString) + result = argument.rows.map do |row| + "|#{row.cells.map { |cell| cell.value }.join '|'}|" end.join "\n" "\n#{result}" end From 980140e6e9a3cc8bd939fbf6eb83297405773213 Mon Sep 17 00:00:00 2001 From: Eric Kessler Date: Sat, 9 Dec 2017 16:09:43 -0500 Subject: [PATCH 3/6] Update gemspec Removed `gherkin` as a dependency in the gemspec, now that `cuke_modler` is being used instead. --- gherkin_lint.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/gherkin_lint.gemspec b/gherkin_lint.gemspec index 6877100..5e8c99a 100644 --- a/gherkin_lint.gemspec +++ b/gherkin_lint.gemspec @@ -12,7 +12,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'amatch', ['~> 0.3', '>= 0.3.0'] s.add_runtime_dependency 'cuke_modeler', ['~> 1.3'] s.add_runtime_dependency 'engtagger', ['~> 0.2', '>= 0.2.0'] - s.add_runtime_dependency 'gherkin', ['>= 4.0.0', '< 6.0'] s.add_runtime_dependency 'multi_json', ['~> 1.12', '>= 1.12.1'] s.add_runtime_dependency 'term-ansicolor', ['~> 1.3', '>= 1.3.2'] s.add_development_dependency 'aruba', ['~> 0.6', '>= 0.6.2'] From a29f734171427563eda59975d148a396f4516215 Mon Sep 17 00:00:00 2001 From: Eric Kessler Date: Sun, 10 Dec 2017 14:21:50 -0500 Subject: [PATCH 4/6] Fix RuboCop issues Took care of RuboCop issues introduced during the refactor. --- Gemfile | 2 +- lib/gherkin_lint.rb | 7 ++----- lib/gherkin_lint/linter.rb | 6 ++---- lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb | 6 +++--- lib/gherkin_lint/linter/tag_constraint.rb | 2 +- lib/gherkin_lint/linter/tag_used_multiple_times.rb | 2 +- lib/gherkin_lint/linter/unused_variable.rb | 4 ++-- lib/gherkin_lint/linter/use_background.rb | 4 ++-- 8 files changed, 14 insertions(+), 19 deletions(-) diff --git a/Gemfile b/Gemfile index 8da6eee..13286ec 100644 --- a/Gemfile +++ b/Gemfile @@ -4,8 +4,8 @@ gemspec gem 'amatch' gem 'aruba', '1.0.0.pre.alpha.2' -gem 'engtagger', '>=0.2.1' gem 'cuke_modeler', '~> 1.3' +gem 'engtagger', '>=0.2.1' # TODO: update to cuke_modeler gem 'gherkin_format' # TODO: update to cuke_modeler gem 'gherkin_language' gem 'rake' diff --git a/lib/gherkin_lint.rb b/lib/gherkin_lint.rb index 4125e34..036b092 100644 --- a/lib/gherkin_lint.rb +++ b/lib/gherkin_lint.rb @@ -95,7 +95,7 @@ def analyze(file) @files[file] = model file end - #TODO: remove this method + # TODO: remove this method # Deprecated def parse(file) to_json File.read(file) @@ -116,7 +116,7 @@ def disable_tags LINTER.map { |lint| "disable#{lint.new.class.name.split('::').last}" } end - #TODO: remove this method + # TODO: remove this method # Deprecated def to_json(input) parser = Gherkin::Parser.new @@ -130,13 +130,10 @@ def print(issues) issues.each { |issue| puts issue.render } end - private - def model(file) CukeModeler::FeatureFile.new(file) end - end end diff --git a/lib/gherkin_lint/linter.rb b/lib/gherkin_lint/linter.rb index cd946b1..7c13fa1 100644 --- a/lib/gherkin_lint/linter.rb +++ b/lib/gherkin_lint/linter.rb @@ -62,9 +62,7 @@ def elements next if feature.nil? next unless feature.background || feature.tests.any? - everything = [] - everything << feature.background if feature.background - everything += feature.tests + everything = [feature.background].compact + feature.tests everything.each do |scenario| yield(file, feature, scenario) @@ -150,7 +148,7 @@ def render_step(step) def render_step_argument(argument) return "\n#{argument.content}" if argument.is_a?(CukeModeler::DocString) result = argument.rows.map do |row| - "|#{row.cells.map { |cell| cell.value }.join '|'}|" + "|#{row.cells.map(&:value).join '|'}|" end.join "\n" "\n#{result}" end diff --git a/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb b/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb index 9299b2b..448f920 100644 --- a/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb +++ b/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb @@ -29,7 +29,7 @@ def lint_examples(file, feature) feature.tests.each do |scenario| tags = gather_same_tags_for_outline scenario next if tags.nil? || tags.empty? - next unless (scenario.is_a?(CukeModeler::Outline) && (scenario.examples.length > 1)) + next unless scenario.is_a?(CukeModeler::Outline) && (scenario.examples.length > 1) references = [reference(file, feature, scenario)] tags.each do |tag| next if tag == '@skip' @@ -44,7 +44,7 @@ def gather_same_tags(feature) feature.children.each do |scenario| next if scenario.is_a? CukeModeler::Background return nil unless scenario.tags.any? - tags = scenario.tags.map { |tag| tag.name } + tags = scenario.tags.map(&:name) result = tags if result.nil? result &= tags end @@ -56,7 +56,7 @@ def gather_same_tags_for_outline(scenario) return result unless scenario.is_a?(CukeModeler::Outline) scenario.examples.each do |example| return nil unless example.tags.any? - tags = example.tags.map { |tag| tag.name } + tags = example.tags.map(&:name) result = tags if result.nil? result &= tags end diff --git a/lib/gherkin_lint/linter/tag_constraint.rb b/lib/gherkin_lint/linter/tag_constraint.rb index 39ec7a3..27fc86f 100644 --- a/lib/gherkin_lint/linter/tag_constraint.rb +++ b/lib/gherkin_lint/linter/tag_constraint.rb @@ -12,7 +12,7 @@ def lint def tags(element) return [] unless element.respond_to? :tags - element.tags.map { |a| a.name } + element.tags.map(&:name) end def matcher(pattern) diff --git a/lib/gherkin_lint/linter/tag_used_multiple_times.rb b/lib/gherkin_lint/linter/tag_used_multiple_times.rb index 76687ca..ee82987 100644 --- a/lib/gherkin_lint/linter/tag_used_multiple_times.rb +++ b/lib/gherkin_lint/linter/tag_used_multiple_times.rb @@ -15,7 +15,7 @@ def lint def tags(element) return [] unless element.tags.any? - element.tags.map { |a| a.name } + element.tags.map(&:name) end end end diff --git a/lib/gherkin_lint/linter/unused_variable.rb b/lib/gherkin_lint/linter/unused_variable.rb index d0dee82..b58be43 100644 --- a/lib/gherkin_lint/linter/unused_variable.rb +++ b/lib/gherkin_lint/linter/unused_variable.rb @@ -5,10 +5,10 @@ module GherkinLint class UnusedVariable < Linter def lint scenarios do |file, feature, scenario| - next unless (scenario.is_a?(CukeModeler::Outline) && scenario.examples.any?) + next unless scenario.is_a?(CukeModeler::Outline) && scenario.examples.any? scenario.examples.each do |example| next unless example.parameter_row - example.parameter_row.cells.map { |cell| cell.value }.each do |variable| + example.parameter_row.cells.map(&:value).each do |variable| references = [reference(file, feature, scenario)] add_error(references, "'<#{variable}>' is unused") unless used?(variable, scenario) end diff --git a/lib/gherkin_lint/linter/use_background.rb b/lib/gherkin_lint/linter/use_background.rb index 21bb10d..656673a 100644 --- a/lib/gherkin_lint/linter/use_background.rb +++ b/lib/gherkin_lint/linter/use_background.rb @@ -61,10 +61,10 @@ def expand_examples(examples, prototypes) def expand_outlines(sentence, example) result = [] - headers = example.parameter_row.cells.map { |cell| cell.value } + headers = example.parameter_row.cells.map(&:value) example.argument_rows.each do |row| # .slice(1, example[:tableBody].length).each do |row| modified_sentence = sentence.dup - headers.zip(row.cells.map { |cell| cell.value }).map do |key, value| + headers.zip(row.cells.map(&:value)).map do |key, value| modified_sentence.gsub!("<#{key}>", value) end result.push modified_sentence From fb8db880e45ab146522cdca795ef5524333465c3 Mon Sep 17 00:00:00 2001 From: Eric Kessler Date: Sun, 11 Mar 2018 21:27:29 -0400 Subject: [PATCH 5/6] Get remaining tests working Remaining functionality has been implemented. --- lib/gherkin_lint/linter.rb | 82 ++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/lib/gherkin_lint/linter.rb b/lib/gherkin_lint/linter.rb index 7c13fa1..4fc0e8b 100644 --- a/lib/gherkin_lint/linter.rb +++ b/lib/gherkin_lint/linter.rb @@ -74,44 +74,12 @@ def name self.class.name.split('::').last end - def lint_files(files, tags_to_suppress) + def lint_files(files, _tags_to_suppress) @files = files - @files = filter_tag(@files, "disable#{name}") - @files = suppress_tags(@files, tags_to_suppress) + filter_guarded_models lint end - def filter_tag(data, tag) - return data.reject { |item| tag?(item, tag) }.map { |item| filter_tag(item, tag) } if data.class == Array - return {} if (data.class == Hash) && (data.include? :feature) && tag?(data[:feature], tag) - return data unless data.respond_to? :each_pair - result = {} - data.each_pair { |key, value| result[key] = filter_tag(value, tag) } - result - end - - def tag?(data, tag) - return false if data.class != Hash - return false unless data.include? :tags - data[:tags].map { |item| item[:name] }.include? "@#{tag}" - end - - def suppress_tags(data, tags) - return data.map { |item| suppress_tags(item, tags) } if data.class == Array - return data unless data.class == Hash - result = {} - - data.each_pair do |key, value| - value = suppress(value, tags) if key == :tags - result[key] = suppress_tags(value, tags) - end - result - end - - def suppress(data, tags) - data.reject { |item| tags.map { |tag| "@#{tag}" }.include? item[:name] } - end - def lint raise 'not implemented' end @@ -152,5 +120,51 @@ def render_step_argument(argument) end.join "\n" "\n#{result}" end + + + private + + + def filter_guarded_models + @files.each_value do |file_model| + filter_feature_model(file_model, "@disable#{name}") + end + end + + def filter_feature_model(file_model, linter_tag_to_filter) + feature_model = file_model.feature + + if feature_model + if feature_model.tags.map(&:name).include?(linter_tag_to_filter) + file_model.feature = nil + else + filter_scenario_models(feature_model, linter_tag_to_filter) + filter_outline_models(feature_model, linter_tag_to_filter) + end + end + end + + def filter_scenario_models(feature_model, linter_tag_to_filter) + feature_model.tests.delete_if do |test_model| + test_model.is_a?(CukeModeler::Scenario) && test_model.tags.map(&:name).include?(linter_tag_to_filter) + end + end + + def filter_outline_models(feature_model, linter_tag_to_filter) + feature_model.tests.delete_if do |test_model| + test_model.is_a?(CukeModeler::Outline) && test_model.tags.map(&:name).include?(linter_tag_to_filter) + end + + feature_model.outlines.each do |outline_model| + filter_example_models(outline_model, linter_tag_to_filter) + end + end + + def filter_example_models(outline_model, linter_tag_to_filter) + outline_model.examples.delete_if do |example_model| + example_model.tags.map(&:name).include?(linter_tag_to_filter) + end + end + end end From c83a965ddae1e291897fadc669a28d87ed54c063 Mon Sep 17 00:00:00 2001 From: Eric Kessler Date: Tue, 27 Mar 2018 18:15:25 -0400 Subject: [PATCH 6/6] Make Rubocop happy Resolved current RuboCop issues. --- lib/gherkin_lint/linter.rb | 48 ++------------------------------ lib/gherkin_lint/model_filter.rb | 47 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 lib/gherkin_lint/model_filter.rb diff --git a/lib/gherkin_lint/linter.rb b/lib/gherkin_lint/linter.rb index 4fc0e8b..b528560 100644 --- a/lib/gherkin_lint/linter.rb +++ b/lib/gherkin_lint/linter.rb @@ -1,9 +1,11 @@ require 'gherkin_lint/issue' +require 'gherkin_lint/model_filter' # gherkin utilities module GherkinLint # base class for all linters class Linter + include ModelFilter attr_reader :issues def self.descendants @@ -120,51 +122,5 @@ def render_step_argument(argument) end.join "\n" "\n#{result}" end - - - private - - - def filter_guarded_models - @files.each_value do |file_model| - filter_feature_model(file_model, "@disable#{name}") - end - end - - def filter_feature_model(file_model, linter_tag_to_filter) - feature_model = file_model.feature - - if feature_model - if feature_model.tags.map(&:name).include?(linter_tag_to_filter) - file_model.feature = nil - else - filter_scenario_models(feature_model, linter_tag_to_filter) - filter_outline_models(feature_model, linter_tag_to_filter) - end - end - end - - def filter_scenario_models(feature_model, linter_tag_to_filter) - feature_model.tests.delete_if do |test_model| - test_model.is_a?(CukeModeler::Scenario) && test_model.tags.map(&:name).include?(linter_tag_to_filter) - end - end - - def filter_outline_models(feature_model, linter_tag_to_filter) - feature_model.tests.delete_if do |test_model| - test_model.is_a?(CukeModeler::Outline) && test_model.tags.map(&:name).include?(linter_tag_to_filter) - end - - feature_model.outlines.each do |outline_model| - filter_example_models(outline_model, linter_tag_to_filter) - end - end - - def filter_example_models(outline_model, linter_tag_to_filter) - outline_model.examples.delete_if do |example_model| - example_model.tags.map(&:name).include?(linter_tag_to_filter) - end - end - end end diff --git a/lib/gherkin_lint/model_filter.rb b/lib/gherkin_lint/model_filter.rb new file mode 100644 index 0000000..69287de --- /dev/null +++ b/lib/gherkin_lint/model_filter.rb @@ -0,0 +1,47 @@ +module GherkinLint + # Mixin to filter scenario models during the linting process + module ModelFilter + private + + def filter_guarded_models + @files.each_value do |file_model| + filter_feature_model(file_model, "@disable#{name}") + end + end + + def filter_feature_model(file_model, linter_tag_to_filter) + feature_model = file_model.feature + + return unless feature_model + + if feature_model.tags.map(&:name).include?(linter_tag_to_filter) + file_model.feature = nil + else + filter_scenario_models(feature_model, linter_tag_to_filter) + filter_outline_models(feature_model, linter_tag_to_filter) + end + end + + def filter_scenario_models(feature_model, linter_tag_to_filter) + feature_model.tests.delete_if do |test_model| + test_model.is_a?(CukeModeler::Scenario) && test_model.tags.map(&:name).include?(linter_tag_to_filter) + end + end + + def filter_outline_models(feature_model, linter_tag_to_filter) + feature_model.tests.delete_if do |test_model| + test_model.is_a?(CukeModeler::Outline) && test_model.tags.map(&:name).include?(linter_tag_to_filter) + end + + feature_model.outlines.each do |outline_model| + filter_example_models(outline_model, linter_tag_to_filter) + end + end + + def filter_example_models(outline_model, linter_tag_to_filter) + outline_model.examples.delete_if do |example_model| + example_model.tags.map(&:name).include?(linter_tag_to_filter) + end + end + end +end