cuke_linter 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef55f6c2d957526d16d14a9f9624fe5ea7638ba0f1b7ea2ea52244fe9805e60b
4
- data.tar.gz: df6e881179035f9edb0f3c7b50a2826ff302049c2926399cee1e1570c0bcfad2
3
+ metadata.gz: 064456ffb63c27969a361497f8c1d6c9f9b96f5a59f488506db5611faa695f0f
4
+ data.tar.gz: b31161d84e3bda65527caa854bdcfcb2e1bdaca8a0f94b4dc540d00274d54bdf
5
5
  SHA512:
6
- metadata.gz: a62718d8b395cddf100152b9a6c6b63cc0c4349c2715fc0261ef985000047853753835ff7d341894ad0ce6eef37b032885c75a3a569b2afaf62a2b33809b9a99
7
- data.tar.gz: 9b239647d5f4f45c7454f0cfbe9c31777b8fe56064c7f99b3cab9ac237551dccc71404afa1cc4e462c10295220f1c3bfd6de7cc60d88e71da7961a13fced62c9
6
+ metadata.gz: b83fad28c5cd56da418697695f4333659f8cd16f750e4be51cd796e62a979c125413aa7b9d443ef56a832e1585b7247e9eb07512acb62e9071a074ed3593e079
7
+ data.tar.gz: 414f730e30415505687acc8abc8a197ba7d49e1bdb6ff9337d079d4450e928cb88d3582f85124b047bd554d13a0f2cf6fbb13f6c43e6a58db26b8ffb41e36d8a
data/.travis.yml CHANGED
@@ -3,6 +3,9 @@ os:
3
3
  - osx
4
4
  sudo: false
5
5
 
6
+ # JRuby isn't working on the new default distribution
7
+ dist: trusty
8
+
6
9
  language: ruby
7
10
  rvm:
8
11
  - 2.3.3
data/CHANGELOG.md CHANGED
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
 
9
9
  Nothing yet...
10
10
 
11
+ ## [0.9.0] - 2019-09-11
12
+
13
+ ### Added
14
+ - Linters can now be temporarily enabled/disabled within arbitrary portions of a feature file, in order to allow for exceptions to the otherwise blanket set of linting rules.
15
+
11
16
  ## [0.8.0] - 2019-09-07
12
17
 
13
18
  ### Changed
@@ -85,7 +90,8 @@ Nothing yet...
85
90
  - Custom linters, formatters, and command line usability
86
91
 
87
92
 
88
- [Unreleased]: https://github.com/enkessler/cuke_linter/compare/v0.8.0...HEAD
93
+ [Unreleased]: https://github.com/enkessler/cuke_linter/compare/v0.9.0...HEAD
94
+ [0.9.0]: https://github.com/enkessler/cuke_linter/compare/v0.8.0...v0.9.0
89
95
  [0.8.0]: https://github.com/enkessler/cuke_linter/compare/v0.7.0...v0.8.0
90
96
  [0.7.0]: https://github.com/enkessler/cuke_linter/compare/v0.6.0...v0.7.0
91
97
  [0.6.0]: https://github.com/enkessler/cuke_linter/compare/v0.5.0...v0.6.0
data/lib/cuke_linter.rb CHANGED
@@ -115,7 +115,8 @@ module CukeLinter
115
115
 
116
116
  model_sets.each do |model_tree|
117
117
  model_tree.each_model do |model|
118
- linters.each do |linter|
118
+ applicable_linters = relevant_linters_for_model(linters, model)
119
+ applicable_linters.each do |linter|
119
120
  # TODO: have linters lint only certain types of models
120
121
  # linting_data.concat(linter.lint(model)) if relevant_model?(linter, model)
121
122
 
@@ -147,6 +148,75 @@ module CukeLinter
147
148
  end
148
149
 
149
150
 
151
+ def self.relevant_linters_for_model(base_linters, model)
152
+ feature_file_model = model.get_ancestor(:feature_file)
153
+
154
+ # Linter directives are not applicable for directory and feature file models. Every other model type should have a feature file ancestor from which to grab linter directive comments.
155
+ return base_linters if feature_file_model.nil?
156
+
157
+ linter_modifications_for_model = {}
158
+
159
+ linter_directives_for_feature_file(feature_file_model).each do |directive|
160
+ # Assuming that the directives are in the same order that they appear in the file
161
+ break if directive[:source_line] > model.source_line
162
+
163
+ linter_modifications_for_model[directive[:linter_class]] = directive[:enabled_status]
164
+ end
165
+
166
+ disabled_linter_classes = linter_modifications_for_model.reject { |_name, status| status }.keys
167
+ enabled_linter_classes = linter_modifications_for_model.select { |_name, status| status }.keys
168
+
169
+ final_linters = base_linters.reject { |linter| disabled_linter_classes.include?(linter.class) }
170
+ enabled_linter_classes.each do |clazz|
171
+ final_linters << dynamic_linters[clazz] unless final_linters.map(&:class).include?(clazz)
172
+ end
173
+
174
+ final_linters
175
+ end
176
+
177
+ private_class_method(:relevant_linters_for_model)
178
+
179
+
180
+ def self.linter_directives_for_feature_file(feature_file_model)
181
+ # IMPORTANT ASSUMPTION: Models never change during the life of the program, so data only has to be gathered once
182
+ @directives_for_feature_file ||= {}
183
+ return @directives_for_feature_file[feature_file_model.object_id] if @directives_for_feature_file[feature_file_model.object_id]
184
+
185
+
186
+ @directives_for_feature_file[feature_file_model.object_id] = []
187
+
188
+ feature_file_model.comments.each do |comment|
189
+ pieces = comment.text.match(/#\s*cuke_linter:(disable|enable)\s+(.*)/)
190
+ next unless pieces # Skipping non-directive file comments
191
+
192
+ linter_classes = pieces[2].gsub(',', ' ').split(' ')
193
+ linter_classes.each do |clazz|
194
+ @directives_for_feature_file[feature_file_model.object_id] << { linter_class: Kernel.const_get(clazz),
195
+ enabled_status: pieces[1] != 'disable',
196
+ source_line: comment.source_line }
197
+ end
198
+ end
199
+
200
+ # Make sure that the directives are in the same order as they appear in the source file
201
+ @directives_for_feature_file[feature_file_model.object_id] = @directives_for_feature_file[feature_file_model.object_id].sort { |a, b| a[:source_line] <=> b[:source_line] }
202
+
203
+
204
+ @directives_for_feature_file[feature_file_model.object_id]
205
+ end
206
+
207
+ private_class_method(:linter_directives_for_feature_file)
208
+
209
+ def self.dynamic_linters
210
+ # No need to keep making new ones over and over...
211
+ @dynamic_linters ||= Hash.new { |hash, key| hash[key] = key.new }
212
+ # return @dynamic_linters if @dynamic_linters
213
+ #
214
+ # @dynamic_linters = {}
215
+ end
216
+
217
+ private_class_method(:dynamic_linters)
218
+
219
+
150
220
  # # def self.relevant_model?(linter, model)
151
221
  # # model_classes = linter.class.target_model_types.map { |type| CukeModeler.const_get(type.to_s.capitalize.chop) }
152
222
  # # model_classes.any? { |clazz| model.is_a?(clazz) }
@@ -1,4 +1,4 @@
1
1
  module CukeLinter
2
2
  # The release version of this gem
3
- VERSION = "0.8.0"
3
+ VERSION = "0.9.0"
4
4
  end
@@ -0,0 +1,50 @@
1
+ Feature: Locally scoping linters
2
+
3
+ As a writer of documentation
4
+ I want to be able to limit the scope of linters for certain portions of the documentation
5
+ So that exceptions to rules can be made
6
+
7
+ In addition to using configurations to change whether or not a given linter is used when linting, linters can be enabled/disabled for specific portions of a `.feature` file. To enable/disable one (or more) linters, add a comment line before the portion of the file where the change should apply. Add a comment with the opposite change at the point in the file when the default behavior should resume. Unless so countermanded, changes remain in effect for the remainder of the feature file.
8
+
9
+
10
+ Scenario: Enabling/disabling a linter within a feature file
11
+
12
+ Note: This example disables linters that are enabled by default but enabling a linter that is disabled by default works in the same manner
13
+
14
+ Given the default linters are being used
15
+ And a feature file model based on the following text:
16
+ """
17
+ # The comma is optional when listing linters. The following two lines are equivalent (although the second line is redundant in this case).
18
+ # cuke_linter:disable CukeLinter::TestWithNoNameLinter, CukeLinter::FeatureWithoutDescriptionLinter
19
+ # cuke_linter:disable CukeLinter::TestWithNoNameLinter CukeLinter::FeatureWithoutDescriptionLinter
20
+
21
+ Feature: Feature with no description
22
+
23
+ # cuke_linter:disable CukeLinter::ElementWithTooManyTagsLinter
24
+ @tag_1 @tag_2 @tag_3 @tag_4 @tag_5 @tag_one_too_many
25
+ Scenario:
26
+ This scenario has no name and too many tags
27
+
28
+ Given a step
29
+ When a step
30
+ Then a step
31
+ # cuke_linter:enable CukeLinter::ElementWithTooManyTagsLinter
32
+
33
+ @tag_1 @tag_2 @tag_3 @tag_4 @tag_5 @tag_one_too_many
34
+ Scenario:
35
+ This scenario also has no name and too many tags
36
+
37
+ Given a step
38
+ When a step
39
+ Then a step
40
+ """
41
+ When the feature is linted
42
+ Then the following problems are reported:
43
+ | linter | problem | location |
44
+ | ElementWithTooManyTagsLinter | Scenario has too many tags. 6 tags found (max 5). | <path_to_file>:18 |
45
+ And the following problems are not reported:
46
+ | linter | problem | location |
47
+ | FeatureWithoutDescriptionLinter | Feature has no description | <path_to_file>:5 |
48
+ | TestWithNoNameLinter | Test does not have a name. | <path_to_file>:9 |
49
+ | TestWithNoNameLinter | Test does not have a name. | <path_to_file>:18 |
50
+ | ElementWithTooManyTagsLinter | Scenario has too many tags. 6 tags found (max 5). | <path_to_file>:9 |
@@ -148,3 +148,16 @@ Given(/^the following(?: feature)? file "([^"]*)":$/) do |file_path, text|
148
148
  @created_files ||= []
149
149
  @created_files << CukeLinter::FileHelper.create_file(directory: @root_test_directory, name: path, extension: ".#{extension}", text: text)
150
150
  end
151
+
152
+ Given(/^the default linters are being used$/) do
153
+ CukeLinter.reset_linters
154
+ end
155
+
156
+ And(/^a feature file model based on the following text:$/) do |text|
157
+ file_path = CukeLinter::FileHelper.create_file(directory: @root_test_directory, extension: '.feature', text: text)
158
+
159
+ @created_files ||= []
160
+ @created_files << file_path
161
+
162
+ @model = CukeModeler::FeatureFile.new(file_path)
163
+ end
@@ -22,6 +22,24 @@ Then(/^an error is reported:$/) do |table|
22
22
  end
23
23
  end
24
24
 
25
+ Then(/^the following problems are( not)? reported:$/) do |exclude, table|
26
+ assertion_method = exclude ? :to_not : :to
27
+
28
+ if @model.is_a?(CukeModeler::FeatureFile)
29
+ feature_file_model = @model
30
+ source_line = ''
31
+ else
32
+ feature_file_model = @model.get_ancestor(:feature_file)
33
+ source_line = @model.source_line.to_s
34
+ end
35
+
36
+ table.hashes.each do |error_record|
37
+ expect(@results).send(assertion_method, include({ linter: error_record['linter'],
38
+ problem: error_record['problem'],
39
+ location: error_record['location'].sub('<path_to_file>', feature_file_model.path).sub('<model_line_number>', source_line) }))
40
+ end
41
+ end
42
+
25
43
  Then(/^the following linters are registered(?: by default)?$/) do |linter_names|
26
44
  expect(CukeLinter.registered_linters.keys).to match_array(linter_names.raw.flatten)
27
45
  end
@@ -28,5 +28,33 @@ module CukeLinter
28
28
  linter
29
29
  end
30
30
 
31
+ def self.generate_fake_linter_class(module_name: nil, class_name: 'FakeLinter', name: 'Some Name', finds_problems: true)
32
+
33
+ if module_name
34
+ parent_module = Kernel.const_defined?(module_name) ? Kernel.const_get(module_name) : Kernel.const_set(module_name, Module.new)
35
+ end
36
+
37
+ (parent_module || Kernel).const_set(class_name, Class.new do
38
+
39
+ define_method('lint') do |model|
40
+ location = model.respond_to?(:source_line) ? "#{model.get_ancestor(:feature_file).path}:#{model.source_line}" : model.path
41
+ problem = @problem || "#{name} problem"
42
+
43
+ if finds_problems
44
+ { problem: problem,
45
+ location: location }
46
+ else
47
+ nil
48
+ end
49
+ end
50
+
51
+ define_method('name') do
52
+ name
53
+ end
54
+
55
+ end)
56
+
57
+ end
58
+
31
59
  end
32
60
  end
@@ -4,115 +4,806 @@ RSpec.describe CukeLinter do
4
4
 
5
5
  describe 'configuration' do
6
6
 
7
- let(:test_model_tree) { CukeLinter::ModelFactory.generate_lintable_model }
8
- let(:test_linters) { [CukeLinter::LinterFactory.generate_fake_linter] }
9
- let(:test_formatters) { [[CukeLinter::FormatterFactory.generate_fake_formatter, "#{CukeLinter::FileHelper::create_directory}/junk_output_file.txt"]] }
10
- let(:linting_options) { { model_trees: [test_model_tree], linters: test_linters, formatters: test_formatters } }
7
+ describe 'blanket linters' do
11
8
 
9
+ let(:test_model_tree) { CukeLinter::ModelFactory.generate_lintable_model }
10
+ let(:test_linters) { [CukeLinter::LinterFactory.generate_fake_linter] }
11
+ let(:test_formatters) { [[CukeLinter::FormatterFactory.generate_fake_formatter, "#{CukeLinter::FileHelper::create_directory}/junk_output_file.txt"]] }
12
+ let(:linting_options) { { model_trees: [test_model_tree], linters: test_linters, formatters: test_formatters } }
12
13
 
13
- it 'unregisters disabled linters' do
14
- config = { 'FakeLinter1' => { 'Enabled' => false } }
15
- configuration_file = CukeLinter::FileHelper.create_file(name: '.cuke_linter', extension: '', text: config.to_yaml)
16
14
 
17
- CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter1'), name: 'FakeLinter1')
18
- expect(subject.registered_linters['FakeLinter1']).to_not be nil
15
+ it 'unregisters disabled linters' do
16
+ config = { 'FakeLinter1' => { 'Enabled' => false } }
17
+ configuration_file = CukeLinter::FileHelper.create_file(name: '.cuke_linter', extension: '', text: config.to_yaml)
19
18
 
20
- subject.load_configuration(config_file_path: configuration_file)
19
+ CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter1'), name: 'FakeLinter1')
20
+ expect(subject.registered_linters['FakeLinter1']).to_not be nil
21
21
 
22
- expect(subject.registered_linters['FakeLinter1']).to be nil
23
- end
22
+ subject.load_configuration(config_file_path: configuration_file)
24
23
 
25
- it 'can apply a property to all linters' do
26
- configuration = { 'AllLinters' => { 'Enabled' => false } }
24
+ expect(subject.registered_linters['FakeLinter1']).to be nil
25
+ end
27
26
 
28
- # Restore the default linters
29
- CukeLinter.reset_linters
27
+ it 'can apply a property to all linters' do
28
+ configuration = { 'AllLinters' => { 'Enabled' => false } }
30
29
 
31
- # Also add some custom ones
32
- CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter, name: 'Foo')
30
+ # Restore the default linters
31
+ CukeLinter.reset_linters
33
32
 
33
+ # Also add some custom ones
34
+ CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter, name: 'Foo')
34
35
 
35
- subject.load_configuration(config: configuration)
36
36
 
37
- expect(subject.registered_linters).to be_empty
38
- end
37
+ subject.load_configuration(config: configuration)
39
38
 
40
- it 'uses linter specific properties over general properties' do
41
- configuration = { 'AllLinters' => { 'Enabled' => false },
42
- 'FakeLinter1' => { 'Enabled' => true } }
39
+ expect(subject.registered_linters).to be_empty
40
+ end
43
41
 
44
- CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter, name: 'FakeLinter1')
45
- expect(subject.registered_linters['FakeLinter1']).to_not be nil
42
+ it 'uses linter specific properties over general properties' do
43
+ configuration = { 'AllLinters' => { 'Enabled' => false },
44
+ 'FakeLinter1' => { 'Enabled' => true } }
46
45
 
47
- subject.load_configuration(config: configuration)
46
+ CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter, name: 'FakeLinter1')
47
+ expect(subject.registered_linters['FakeLinter1']).to_not be nil
48
48
 
49
- expect(subject.registered_linters['FakeLinter1']).to_not be nil
50
- end
49
+ subject.load_configuration(config: configuration)
51
50
 
52
- it 'even unregisters non-configurable disabled linters' do
53
- config = { 'FakeLinter' => { 'Enabled' => false } }
54
- configuration_file = CukeLinter::FileHelper.create_file(name: '.cuke_linter', extension: '', text: config.to_yaml)
55
- non_configurable_linter = CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter')
56
- non_configurable_linter.instance_eval('undef :configure')
51
+ expect(subject.registered_linters['FakeLinter1']).to_not be nil
52
+ end
57
53
 
58
- CukeLinter.register_linter(linter: non_configurable_linter, name: 'FakeLinter')
59
- expect(subject.registered_linters['FakeLinter']).to_not be nil
54
+ it 'even unregisters non-configurable disabled linters' do
55
+ config = { 'FakeLinter' => { 'Enabled' => false } }
56
+ configuration_file = CukeLinter::FileHelper.create_file(name: '.cuke_linter', extension: '', text: config.to_yaml)
57
+ non_configurable_linter = CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter')
58
+ non_configurable_linter.instance_eval('undef :configure')
60
59
 
61
- subject.load_configuration(config_file_path: configuration_file)
60
+ CukeLinter.register_linter(linter: non_configurable_linter, name: 'FakeLinter')
61
+ expect(subject.registered_linters['FakeLinter']).to_not be nil
62
62
 
63
- expect(subject.registered_linters['FakeLinter']).to be nil
64
- end
63
+ subject.load_configuration(config_file_path: configuration_file)
65
64
 
66
- it 'uses the default configuration file in the current directory if no configuration file is provided' do
67
- config = { 'FakeLinter1' => { 'Enabled' => false } }
68
- configuration_file = CukeLinter::FileHelper.create_file(name: '.cuke_linter', extension: '', text: config.to_yaml)
65
+ expect(subject.registered_linters['FakeLinter']).to be nil
66
+ end
69
67
 
70
- CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter1'), name: 'FakeLinter1')
71
- expect(subject.registered_linters['FakeLinter1']).to_not be nil
68
+ it 'uses the default configuration file in the current directory if no configuration file is provided' do
69
+ config = { 'FakeLinter1' => { 'Enabled' => false } }
70
+ configuration_file = CukeLinter::FileHelper.create_file(name: '.cuke_linter', extension: '', text: config.to_yaml)
72
71
 
73
- Dir.chdir(File.dirname(configuration_file)) do
74
- subject.load_configuration
72
+ CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter1'), name: 'FakeLinter1')
73
+ expect(subject.registered_linters['FakeLinter1']).to_not be nil
74
+
75
+ Dir.chdir(File.dirname(configuration_file)) do
76
+ subject.load_configuration
77
+ end
78
+
79
+ expect(subject.registered_linters['FakeLinter1']).to be nil
75
80
  end
76
81
 
77
- expect(subject.registered_linters['FakeLinter1']).to be nil
78
- end
82
+ it 'raises an exception if no default configuration file is found and no configuration or file is provided' do
83
+ some_empty_directory = CukeLinter::FileHelper.create_directory
84
+
85
+ Dir.chdir(File.dirname(some_empty_directory)) do
86
+ expect { subject.load_configuration }.to raise_error('No configuration or configuration file given and no .cuke_linter file found')
87
+ end
88
+ end
89
+
90
+ it 'configures every linter for which it has a configuration' do
91
+ config = { 'FakeLinter1' => { 'Problem' => 'My custom message for FakeLinter1' },
92
+ 'FakeLinter2' => { 'Problem' => 'My custom message for FakeLinter2' } }
93
+
94
+ CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter1'), name: 'FakeLinter1')
95
+ CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter2'), name: 'FakeLinter2')
96
+ linting_options.delete(:linters)
79
97
 
80
- it 'raises an exception if no default configuration file is found and no configuration or file is provided' do
81
- some_empty_directory = CukeLinter::FileHelper.create_directory
98
+ subject.load_configuration(config: config)
99
+ results = subject.lint(linting_options)
82
100
 
83
- Dir.chdir(File.dirname(some_empty_directory)) do
84
- expect { subject.load_configuration }.to raise_error('No configuration or configuration file given and no .cuke_linter file found')
101
+ expect(results).to match_array([{ linter: 'FakeLinter1', location: 'path_to_file:1', problem: 'My custom message for FakeLinter1' },
102
+ { linter: 'FakeLinter2', location: 'path_to_file:1', problem: 'My custom message for FakeLinter2' }])
85
103
  end
86
- end
87
104
 
88
- it 'configures every linter for which it has a configuration' do
89
- config = { 'FakeLinter1' => { 'Problem' => 'My custom message for FakeLinter1' },
90
- 'FakeLinter2' => { 'Problem' => 'My custom message for FakeLinter2' } }
105
+ it "does not try to configure linters that don't know how to be configured" do
106
+ config = { 'FakeLinter' => { 'Problem' => 'My custom message for FakeLinter' } }
107
+ non_configurable_linter = CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter')
108
+ non_configurable_linter.instance_eval('undef :configure')
109
+
110
+ CukeLinter.clear_registered_linters
111
+ CukeLinter.register_linter(linter: non_configurable_linter, name: 'FakeLinter')
112
+ linting_options.delete(:linters)
91
113
 
92
- CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter1'), name: 'FakeLinter1')
93
- CukeLinter.register_linter(linter: CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter2'), name: 'FakeLinter2')
94
- linting_options.delete(:linters)
114
+ subject.load_configuration(config: config)
115
+ results = subject.lint(linting_options)
95
116
 
96
- subject.load_configuration(config: config)
97
- results = subject.lint(linting_options)
117
+ expect(results).to match_array([{ linter: 'FakeLinter', location: 'path_to_file:1', problem: 'FakeLinter problem' }])
118
+ end
98
119
 
99
- expect(results).to match_array([{ linter: 'FakeLinter1', location: 'path_to_file:1', problem: 'My custom message for FakeLinter1' },
100
- { linter: 'FakeLinter2', location: 'path_to_file:1', problem: 'My custom message for FakeLinter2' }])
101
120
  end
102
121
 
103
- it "does not try to configure linters that don't know how to be configured" do
104
- config = { 'FakeLinter' => { 'Problem' => 'My custom message for FakeLinter' } }
105
- non_configurable_linter = CukeLinter::LinterFactory.generate_fake_linter(name: 'FakeLinter')
106
- non_configurable_linter.instance_eval('undef :configure')
122
+ describe 'targeted linters' do
123
+
124
+ before(:all) do
125
+ @targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(class_name: 'ATargetedLinterClass', name: 'ATargetedLinter')
126
+ @another_targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(class_name: 'AnotherTargetedLinterClass', name: 'AnotherTargetedLinter')
127
+ @yet_another_targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(class_name: 'YetAnotherTargetedLinterClass', name: 'YetAnotherTargetedLinter')
128
+
129
+ @a_non_nested_targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(module_name: nil, class_name: 'ANonNestedTargetedLinterClass', name: 'ANonNestedTargetedLinter')
130
+ @another_non_nested_targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(module_name: nil, class_name: 'AnotherNonNestedTargetedLinterClass', name: 'AnotherNonNestedTargetedLinter')
131
+
132
+ @a_nested_targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(module_name: 'Foo', class_name: 'ANestedTargetedLinterClass', name: 'ANestedTargetedLinter')
133
+ @another_nested_targeted_linter_class = CukeLinter::LinterFactory.generate_fake_linter_class(module_name: 'Foo', class_name: 'AnotherNestedTargetedLinterClass', name: 'AnotherNestedTargetedLinter')
134
+ end
135
+
136
+
137
+ let(:linter_name) { 'ATargetedLinter' }
138
+ let(:another_linter_name) { 'AnotherTargetedLinter' }
139
+ let(:yet_another_linter_name) { 'YetAnotherTargetedLinter' }
140
+
141
+ let(:non_nested_linter_name) { 'ANonNestedTargetedLinter' }
142
+ let(:another_non_nested_linter_name) { 'AnotherNonNestedTargetedLinter' }
143
+ let(:nested_linter_name) { 'ANestedTargetedLinter' }
144
+ let(:another_nested_linter_name) { 'AnotherNestedTargetedLinter' }
145
+
146
+ let(:linter_class_name) { 'ATargetedLinterClass' }
147
+ let(:another_linter_class_name) { 'AnotherTargetedLinterClass' }
148
+ let(:yet_another_linter_class_name) { 'YetAnotherTargetedLinterClass' }
149
+ let(:non_nested_linter_class_name) { 'ANonNestedTargetedLinterClass' }
150
+ let(:another_non_nested_linter_class_name) { 'AnotherNonNestedTargetedLinterClass' }
151
+ let(:nested_linter_class_name) { 'Foo::ANestedTargetedLinterClass' }
152
+ let(:another_nested_linter_class_name) { 'Foo::AnotherNestedTargetedLinterClass' }
153
+
154
+ let(:targeted_linter) { @targeted_linter_class.new }
155
+ let(:another_targeted_linter) { @another_targeted_linter_class.new }
156
+ let(:yet_another_targeted_linter) { @yet_another_targeted_linter_class.new }
157
+ let(:non_nested_targeted_linter) { @a_non_nested_targeted_linter_class.new }
158
+ let(:nested_targeted_linter) { @a_nested_targeted_linter_class.new }
159
+
160
+ let(:test_linters) { [targeted_linter] }
161
+ let(:test_linter_names) { [linter_name] }
162
+ let(:test_formatters) { [[CukeLinter::FormatterFactory.generate_fake_formatter, "#{CukeLinter::FileHelper::create_directory}/junk_output_file.txt"]] }
163
+ let(:test_model_trees) { [CukeModeler::FeatureFile.new(linted_file)] }
164
+
165
+ let(:test_directory) { CukeLinter::FileHelper.create_directory }
166
+ let(:linted_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
167
+ extension: '.feature',
168
+ text: file_text) }
169
+
170
+
171
+ [:provided, :registered].each do |linter_type|
172
+
173
+ context "using #{linter_type} linters" do
174
+
175
+ if linter_type == :provided
176
+ let(:linting_options) { { model_trees: test_model_trees, linters: test_linters, formatters: test_formatters } }
177
+ else
178
+ let(:linting_options) { { model_trees: test_model_trees, formatters: test_formatters } }
179
+
180
+ before(:each) do
181
+ subject.clear_registered_linters
182
+
183
+ test_linters.each_with_index { |linter, index| subject.register_linter(linter: linter, name: test_linter_names[index]) }
184
+ end
185
+ end
186
+
187
+
188
+ context 'with non-nested class names' do
189
+
190
+ let(:test_linters) { [non_nested_targeted_linter] }
191
+ let(:test_linter_names) { [non_nested_linter_name] }
192
+
193
+ let(:file_text) { "Feature:
194
+
195
+ # cuke_linter:disable #{non_nested_linter_class_name}
196
+ # cuke_linter:enable #{another_non_nested_linter_class_name}
197
+ Scenario:" }
198
+
199
+
200
+ it 'handles the directives correctly' do
201
+ results = subject.lint(linting_options)
202
+
203
+ expect(results).to match_array([{ linter: non_nested_linter_name, location: linted_file, problem: "#{non_nested_linter_name} problem" },
204
+ { linter: non_nested_linter_name, location: "#{linted_file}:1", problem: "#{non_nested_linter_name} problem" },
205
+ { linter: another_non_nested_linter_name, location: "#{linted_file}:5", problem: "#{another_non_nested_linter_name} problem" }])
206
+ end
207
+
208
+ end
209
+
210
+ context 'with nested class names' do
211
+
212
+ let(:test_linters) { [nested_targeted_linter] }
213
+ let(:test_linter_names) { [nested_linter_name] }
214
+
215
+ let(:file_text) { "Feature:
216
+
217
+ # cuke_linter:disable #{nested_linter_class_name}
218
+ # cuke_linter:enable #{another_nested_linter_class_name}
219
+ Scenario:" }
220
+
221
+
222
+ it 'handles the directives correctly' do
223
+ results = subject.lint(linting_options)
224
+
225
+ expect(results).to match_array([{ linter: nested_linter_name, location: linted_file, problem: "#{nested_linter_name} problem" },
226
+ { linter: nested_linter_name, location: "#{linted_file}:1", problem: "#{nested_linter_name} problem" },
227
+ { linter: another_nested_linter_name, location: "#{linted_file}:5", problem: "#{another_nested_linter_name} problem" }])
228
+ end
229
+
230
+ end
231
+
232
+ context 'with multiple linters in the directive' do
233
+
234
+ let(:commas_text) { "Feature:
235
+
236
+ # cuke_linter:disable #{linter_class_name}, #{another_linter_class_name}, #{yet_another_linter_class_name}
237
+ Scenario:" }
238
+ let(:commas_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
239
+ extension: '.feature',
240
+ text: commas_text,
241
+ name: 'commas_text') }
242
+
243
+ let(:spaces_text) { "Feature:
244
+
245
+ # cuke_linter:disable #{linter_class_name} #{another_linter_class_name} #{yet_another_linter_class_name}
246
+ Scenario:" }
247
+ let(:spaces_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
248
+ extension: '.feature',
249
+ text: spaces_text,
250
+ name: 'spaces_text') }
251
+
252
+ let(:test_model_trees) { [CukeModeler::FeatureFile.new(commas_file),
253
+ CukeModeler::FeatureFile.new(spaces_file)] }
254
+
255
+ let(:test_linters) { [targeted_linter, another_targeted_linter, yet_another_targeted_linter] }
256
+ let(:test_linter_names) { [linter_name, another_linter_name, yet_another_linter_name] }
257
+
258
+
259
+ it 'handles the directives correctly' do
260
+ results = subject.lint(linting_options)
261
+
262
+ expect(results).to match_array([{ linter: linter_name, location: spaces_file, problem: "#{linter_name} problem" },
263
+ { linter: linter_name, location: "#{spaces_file}:1", problem: "#{linter_name} problem" },
264
+ { linter: another_linter_name, location: spaces_file, problem: "#{another_linter_name} problem" },
265
+ { linter: another_linter_name, location: "#{spaces_file}:1", problem: "#{another_linter_name} problem" },
266
+ { linter: yet_another_linter_name, location: spaces_file, problem: "#{yet_another_linter_name} problem" },
267
+ { linter: yet_another_linter_name, location: "#{spaces_file}:1", problem: "#{yet_another_linter_name} problem" },
268
+
269
+ { linter: linter_name, location: commas_file, problem: "#{linter_name} problem" },
270
+ { linter: linter_name, location: "#{commas_file}:1", problem: "#{linter_name} problem" },
271
+ { linter: another_linter_name, location: commas_file, problem: "#{another_linter_name} problem" },
272
+ { linter: another_linter_name, location: "#{commas_file}:1", problem: "#{another_linter_name} problem" },
273
+ { linter: yet_another_linter_name, location: commas_file, problem: "#{yet_another_linter_name} problem" },
274
+ { linter: yet_another_linter_name, location: "#{commas_file}:1", problem: "#{yet_another_linter_name} problem" }])
275
+ end
276
+
277
+ end
278
+
279
+ context 'with multiple files' do
280
+
281
+ let(:modified_text) { "Feature:
282
+
283
+ # cuke_linter:disable #{linter_class_name}
284
+ Scenario:" }
285
+ let(:modified_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
286
+ extension: '.feature',
287
+ text: modified_text) }
288
+ let(:unmodified_text) { "Feature:
289
+
290
+ Scenario:" }
291
+ let(:unmodified_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
292
+ extension: '.feature',
293
+ text: unmodified_text) }
294
+
295
+ let(:test_model_trees) { [CukeModeler::FeatureFile.new(modified_file),
296
+ CukeModeler::FeatureFile.new(unmodified_file)] }
297
+
298
+ it 'does not use targeted linting changes outside of the file in which they occur' do
299
+ results = subject.lint(linting_options)
300
+
301
+ expect(results).to match_array([{ linter: linter_name, location: modified_file, problem: "#{linter_name} problem" },
302
+ { linter: linter_name, location: "#{modified_file}:1", problem: "#{linter_name} problem" },
303
+ { linter: linter_name, location: unmodified_file, problem: "#{linter_name} problem" },
304
+ { linter: linter_name, location: "#{unmodified_file}:1", problem: "#{linter_name} problem" },
305
+ { linter: linter_name, location: "#{unmodified_file}:3", problem: "#{linter_name} problem" }])
306
+ end
307
+
308
+ end
309
+
310
+ context 'with other comments in the file' do
311
+
312
+ let(:file_text) { "# I'm just a comment
313
+ Feature:
314
+
315
+ # cuke_linter:disable #{linter_class_name}
316
+ #Me too
317
+ Scenario:" }
318
+
319
+
320
+ it 'handles the directive correctly' do
321
+ results = subject.lint(linting_options)
322
+
323
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
324
+ { linter: linter_name, location: "#{linted_file}:2", problem: "#{linter_name} problem" }])
325
+ end
326
+
327
+ end
328
+
329
+ context 'with varying whitespace' do
330
+
331
+ let(:extra_whitespace_text) { "Feature:
332
+
333
+ # cuke_linter:disable #{linter_class_name}
334
+ Scenario:" }
335
+ let(:extra_whitespace_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
336
+ extension: '.feature',
337
+ text: extra_whitespace_text) }
338
+ let(:minimal_whitespace_text) { "Feature:
339
+
340
+ #cuke_linter:disable #{linter_class_name}
341
+ Scenario:" }
342
+ let(:minimal_whitespace_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
343
+ extension: '.feature',
344
+ text: minimal_whitespace_text) }
345
+
346
+ let(:test_model_trees) { [CukeModeler::FeatureFile.new(extra_whitespace_file),
347
+ CukeModeler::FeatureFile.new(minimal_whitespace_file)] }
348
+
349
+
350
+ it 'handles the directives correctly' do
351
+ results = subject.lint(linting_options)
352
+
353
+ expect(results).to match_array([{ linter: linter_name, location: extra_whitespace_file, problem: "#{linter_name} problem" },
354
+ { linter: linter_name, location: "#{extra_whitespace_file}:1", problem: "#{linter_name} problem" },
355
+ { linter: linter_name, location: minimal_whitespace_file, problem: "#{linter_name} problem" },
356
+ { linter: linter_name, location: "#{minimal_whitespace_file}:1", problem: "#{linter_name} problem" }])
357
+ end
358
+
359
+
360
+ context 'and multiple targeted linters' do
361
+
362
+ let(:spaced_commas_text) { "Feature:
363
+
364
+ # cuke_linter:disable #{linter_class_name} , #{another_linter_class_name} , #{yet_another_linter_class_name}
365
+ Scenario:" }
366
+ let(:spaced_commas_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
367
+ extension: '.feature',
368
+ text: spaced_commas_text,
369
+ name: 'spaced_commas_text') }
370
+
371
+ let(:compact_commas_text) { "Feature:
372
+
373
+ # cuke_linter:disable #{linter_class_name},#{another_linter_class_name},#{yet_another_linter_class_name}
374
+ Scenario:" }
375
+ let(:compact_commas_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
376
+ extension: '.feature',
377
+ text: compact_commas_text,
378
+ name: 'compact_commas_text') }
379
+
380
+ let(:spaced_space_text) { "Feature:
381
+
382
+ # cuke_linter:disable #{linter_class_name} #{another_linter_class_name} #{yet_another_linter_class_name}
383
+ Scenario:" }
384
+ let(:spaced_space_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
385
+ extension: '.feature',
386
+ text: spaced_space_text,
387
+ name: 'spaced_space_text') }
388
+
389
+ let(:compact_space_text) { "Feature:
390
+
391
+ # cuke_linter:disable #{linter_class_name} #{another_linter_class_name} #{yet_another_linter_class_name}
392
+ Scenario:" }
393
+ let(:compact_space_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
394
+ extension: '.feature',
395
+ text: compact_space_text,
396
+ name: 'compact_space_text') }
397
+
398
+ let(:test_model_trees) { [CukeModeler::FeatureFile.new(spaced_commas_file),
399
+ CukeModeler::FeatureFile.new(compact_commas_file),
400
+ CukeModeler::FeatureFile.new(spaced_space_file),
401
+ CukeModeler::FeatureFile.new(compact_space_file)] }
402
+
403
+ let(:test_linters) { [targeted_linter, another_targeted_linter, yet_another_targeted_linter] }
404
+ let(:test_linter_names) { [linter_name, another_linter_name, yet_another_linter_name] }
405
+
406
+
407
+ it 'handles the directives correctly' do
408
+ results = subject.lint(linting_options)
409
+
410
+ expect(results).to match_array([{ linter: linter_name, location: spaced_commas_file, problem: "#{linter_name} problem" },
411
+ { linter: linter_name, location: "#{spaced_commas_file}:1", problem: "#{linter_name} problem" },
412
+ { linter: another_linter_name, location: spaced_commas_file, problem: "#{another_linter_name} problem" },
413
+ { linter: another_linter_name, location: "#{spaced_commas_file}:1", problem: "#{another_linter_name} problem" },
414
+ { linter: yet_another_linter_name, location: spaced_commas_file, problem: "#{yet_another_linter_name} problem" },
415
+ { linter: yet_another_linter_name, location: "#{spaced_commas_file}:1", problem: "#{yet_another_linter_name} problem" },
416
+
417
+ { linter: linter_name, location: compact_commas_file, problem: "#{linter_name} problem" },
418
+ { linter: linter_name, location: "#{compact_commas_file}:1", problem: "#{linter_name} problem" },
419
+ { linter: another_linter_name, location: compact_commas_file, problem: "#{another_linter_name} problem" },
420
+ { linter: another_linter_name, location: "#{compact_commas_file}:1", problem: "#{another_linter_name} problem" },
421
+ { linter: yet_another_linter_name, location: compact_commas_file, problem: "#{yet_another_linter_name} problem" },
422
+ { linter: yet_another_linter_name, location: "#{compact_commas_file}:1", problem: "#{yet_another_linter_name} problem" },
423
+
424
+ { linter: linter_name, location: spaced_space_file, problem: "#{linter_name} problem" },
425
+ { linter: linter_name, location: "#{spaced_space_file}:1", problem: "#{linter_name} problem" },
426
+ { linter: another_linter_name, location: spaced_space_file, problem: "#{another_linter_name} problem" },
427
+ { linter: another_linter_name, location: "#{spaced_space_file}:1", problem: "#{another_linter_name} problem" },
428
+ { linter: yet_another_linter_name, location: spaced_space_file, problem: "#{yet_another_linter_name} problem" },
429
+ { linter: yet_another_linter_name, location: "#{spaced_space_file}:1", problem: "#{yet_another_linter_name} problem" },
430
+
431
+ { linter: linter_name, location: compact_space_file, problem: "#{linter_name} problem" },
432
+ { linter: linter_name, location: "#{compact_space_file}:1", problem: "#{linter_name} problem" },
433
+ { linter: another_linter_name, location: compact_space_file, problem: "#{another_linter_name} problem" },
434
+ { linter: another_linter_name, location: "#{compact_space_file}:1", problem: "#{another_linter_name} problem" },
435
+ { linter: yet_another_linter_name, location: compact_space_file, problem: "#{yet_another_linter_name} problem" },
436
+ { linter: yet_another_linter_name, location: "#{compact_space_file}:1", problem: "#{yet_another_linter_name} problem" }])
437
+ end
438
+
439
+ end
440
+
441
+ end
442
+
443
+ context 'with a disabled(i.e. unregistered)/not provided linter' do
444
+
445
+ if linter_type == :provided
446
+ before(:each) do
447
+ test_linters.delete(targeted_linter)
448
+ end
449
+ else
450
+ before(:each) do
451
+ subject.unregister_linter(linter_name)
452
+ end
453
+ end
454
+
455
+
456
+ context 'that is explicitly disabled' do
457
+
458
+ # Used so that the linting process is not entirely bypassed due to no other linters being registered/provided
459
+ let(:baseline_linter_name) { 'BaselineLinter' }
460
+ let(:baseline_linter) { CukeLinter::LinterFactory.generate_fake_linter(name: baseline_linter_name) }
461
+
462
+ let(:test_linters) { [baseline_linter, targeted_linter] }
463
+ let(:test_linter_names) { [baseline_linter_name, linter_name] }
464
+
465
+
466
+ let(:file_text) { "Feature:
467
+
468
+ # cuke_linter:disable #{linter_class_name}
469
+ Scenario:" }
470
+
471
+ let(:baseline_linter_results) { [{ linter: baseline_linter_name, location: linted_file, problem: "#{baseline_linter_name} problem" },
472
+ { linter: baseline_linter_name, location: "#{linted_file}:1", problem: "#{baseline_linter_name} problem" },
473
+ { linter: baseline_linter_name, location: "#{linted_file}:4", problem: "#{baseline_linter_name} problem" }] }
107
474
 
108
- CukeLinter.clear_registered_linters
109
- CukeLinter.register_linter(linter: non_configurable_linter, name: 'FakeLinter')
110
- linting_options.delete(:linters)
111
475
 
112
- subject.load_configuration(config: config)
113
- results = subject.lint(linting_options)
476
+ it 'does not use the linter' do
477
+ results = subject.lint(linting_options)
478
+
479
+ expect(results).to match_array(baseline_linter_results + [])
480
+ end
481
+
482
+
483
+ context 'multiple times' do
484
+
485
+ context 'with separate targetings' do
486
+
487
+ let(:file_text) { "Feature:
488
+ # cuke_linter:disable #{linter_class_name}
489
+ # cuke_linter:disable #{linter_class_name}
490
+ Scenario:" }
491
+
492
+
493
+ it 'does not use the linter' do
494
+ results = subject.lint(linting_options)
495
+
496
+ expect(results).to match_array(baseline_linter_results + [])
497
+ end
498
+
499
+ end
500
+
501
+ context 'with the same targeting' do
502
+
503
+ let(:file_text) { "Feature:
504
+
505
+ # cuke_linter:disable #{linter_class_name}, #{linter_class_name}
506
+ Scenario:" }
507
+
508
+ it 'does not use the linter' do
509
+ results = subject.lint(linting_options)
510
+
511
+ expect(results).to match_array(baseline_linter_results + [])
512
+ end
513
+
514
+ end
515
+
516
+ end
517
+
518
+ end
519
+
520
+ context 'that is explicitly enabled' do
521
+
522
+ let(:file_text) { "Feature:
523
+
524
+ # cuke_linter:enable #{linter_class_name}
525
+ Scenario:" }
526
+
527
+ it 'uses the linter' do
528
+ results = subject.lint(linting_options)
529
+
530
+ expect(results).to match_array([{ linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
531
+ end
532
+
533
+ context 'multiple times' do
534
+
535
+ context 'with separate targetings' do
536
+
537
+ let(:file_text) { "Feature:
538
+ # cuke_linter:enable #{linter_class_name}
539
+ # cuke_linter:enable #{linter_class_name}
540
+ Scenario:" }
541
+
542
+ it 'uses the linter' do
543
+ results = subject.lint(linting_options)
544
+
545
+ expect(results).to match_array([{ linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
546
+ end
547
+
548
+ it 'does not include redundant linting results' do
549
+ results = subject.lint(linting_options)
550
+
551
+ expect(results).to match_array([{ linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
552
+ end
553
+
554
+ end
555
+
556
+ context 'with the same targeting' do
557
+
558
+ let(:file_text) { "Feature:
559
+
560
+ # cuke_linter:enable #{linter_class_name}, #{linter_class_name}
561
+ Scenario:" }
562
+
563
+ it 'uses the linter' do
564
+ results = subject.lint(linting_options)
565
+
566
+ expect(results).to match_array([{ linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
567
+ end
568
+
569
+ it 'does not include redundant linting results' do
570
+ results = subject.lint(linting_options)
571
+
572
+ expect(results).to match_array([{ linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
573
+ end
574
+
575
+ end
576
+
577
+ end
578
+
579
+ context 'and then re-disabled' do
580
+
581
+ let(:file_text) { "Feature:
582
+
583
+ # cuke_linter:enable #{linter_class_name}
584
+ Scenario:
585
+ # cuke_linter:disable #{linter_class_name}
586
+ Scenario:" }
587
+
588
+ it 'ceases using the linter' do
589
+ results = subject.lint(linting_options)
590
+
591
+ expect(results).to match_array([{ linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
592
+ end
593
+
594
+ end
595
+
596
+ end
597
+
598
+ end
599
+
600
+ context 'with an enabled(i.e. registered)/provided linter' do
601
+
602
+ if linter_type == :provided
603
+ before(:each) do
604
+ test_linters << targeted_linter unless test_linters.include?(targeted_linter)
605
+ end
606
+ else
607
+ before(:each) do
608
+ subject.register_linter(linter: targeted_linter, name: linter_name)
609
+ end
610
+ end
611
+
612
+
613
+ context 'that is explicitly disabled' do
614
+
615
+ let(:file_text) { "Feature:
616
+
617
+ # cuke_linter:disable #{linter_class_name}
618
+ Scenario:" }
619
+
620
+
621
+ it 'does not use the linter' do
622
+ results = subject.lint(linting_options)
623
+
624
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
625
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" }])
626
+ end
627
+
628
+ context 'multiple times' do
629
+
630
+ context 'with separate targetings' do
631
+
632
+ let(:file_text) { "Feature:
633
+ # cuke_linter:disable #{linter_class_name}
634
+ # cuke_linter:disable #{linter_class_name}
635
+ Scenario:" }
636
+
637
+
638
+ it 'does not use the linter' do
639
+ results = subject.lint(linting_options)
640
+
641
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
642
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" }])
643
+ end
644
+
645
+ end
646
+
647
+ context 'with the same targeting' do
648
+
649
+ let(:file_text) { "Feature:
650
+
651
+ # cuke_linter:disable #{linter_class_name}, #{linter_class_name}
652
+ Scenario:" }
653
+
654
+ it 'does not use the linter' do
655
+ results = subject.lint(linting_options)
656
+
657
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
658
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" }])
659
+ end
660
+ end
661
+
662
+ end
663
+
664
+ context 'and then re-enabled' do
665
+
666
+ let(:file_text) { "Feature:
667
+
668
+ # cuke_linter:disable #{linter_class_name}
669
+ Scenario:
670
+ # cuke_linter:enable #{linter_class_name}
671
+ Scenario:" }
672
+
673
+
674
+ it 'resumes using the linter' do
675
+ results = subject.lint(linting_options)
676
+
677
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
678
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
679
+ { linter: linter_name, location: "#{linted_file}:6", problem: "#{linter_name} problem" }])
680
+ end
681
+
682
+ it 'does not include redundant linting results' do
683
+ results = subject.lint(linting_options)
684
+
685
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
686
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
687
+ { linter: linter_name, location: "#{linted_file}:6", problem: "#{linter_name} problem" }])
688
+ end
689
+
690
+ end
691
+
692
+ end
693
+
694
+ context 'that is explicitly enabled' do
695
+
696
+ let(:file_text) { "Feature:
697
+
698
+ # cuke_linter:enable #{linter_class_name}
699
+ Scenario:" }
700
+
701
+
702
+ it 'uses the linter' do
703
+ results = subject.lint(linting_options)
704
+
705
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
706
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
707
+ { linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
708
+ end
709
+
710
+ it 'does not include redundant linting results' do
711
+ results = subject.lint(linting_options)
712
+
713
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
714
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
715
+ { linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
716
+ end
717
+
718
+ it 'prefers the provided (or registered) linter over having to make a new one' do
719
+
720
+ # Tweaking the linter objects that will already exist so that any linter created from the same class will be noticeably different
721
+ provided_linter = test_linters.find { |linter| linter.is_a?(Kernel.const_get(linter_class_name)) }
722
+ registered_linter = CukeLinter.registered_linters.values.find { |linter| linter.is_a?(Kernel.const_get(linter_class_name)) }
723
+
724
+ if provided_linter
725
+ def provided_linter.name
726
+ 'Pre-existing Linter'
727
+ end
728
+ end
729
+
730
+ if registered_linter
731
+ def registered_linter.name
732
+ 'Pre-existing Linter'
733
+ end
734
+ end
735
+
736
+
737
+ results = subject.lint(linting_options)
738
+
739
+ expect(results).to match_array([{ linter: 'Pre-existing Linter', location: linted_file, problem: "#{linter_name} problem" },
740
+ { linter: 'Pre-existing Linter', location: "#{linted_file}:1", problem: "#{linter_name} problem" },
741
+ { linter: 'Pre-existing Linter', location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
742
+ end
743
+
744
+
745
+ context 'multiple times' do
746
+
747
+ context 'with separate targetings' do
748
+
749
+ let(:file_text) { "Feature:
750
+ # cuke_linter:enable #{linter_class_name}
751
+ # cuke_linter:enable #{linter_class_name}
752
+ Scenario:" }
753
+
754
+ it 'uses the linter' do
755
+ results = subject.lint(linting_options)
756
+
757
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
758
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
759
+ { linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
760
+ end
761
+
762
+ it 'does not include redundant linting results' do
763
+ results = subject.lint(linting_options)
764
+
765
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
766
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
767
+ { linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
768
+ end
769
+
770
+ end
771
+
772
+ context 'with the same targeting' do
773
+
774
+ let(:file_text) { "Feature:
775
+
776
+ # cuke_linter:enable #{linter_class_name}, #{linter_class_name}
777
+ Scenario:" }
778
+
779
+ it 'uses the linter' do
780
+ results = subject.lint(linting_options)
781
+
782
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
783
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
784
+ { linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
785
+ end
786
+
787
+ it 'does not include redundant linting results' do
788
+ results = subject.lint(linting_options)
789
+
790
+ expect(results).to match_array([{ linter: linter_name, location: linted_file, problem: "#{linter_name} problem" },
791
+ { linter: linter_name, location: "#{linted_file}:1", problem: "#{linter_name} problem" },
792
+ { linter: linter_name, location: "#{linted_file}:4", problem: "#{linter_name} problem" }])
793
+ end
794
+
795
+ end
796
+
797
+ end
798
+
799
+ end
800
+
801
+ end
802
+
803
+ end
804
+
805
+ end
114
806
 
115
- expect(results).to match_array([{ linter: 'FakeLinter', location: 'path_to_file:1', problem: 'FakeLinter problem' }])
116
807
  end
117
808
 
118
809
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuke_linter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Kessler
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-07 00:00:00.000000000 Z
11
+ date: 2019-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cuke_modeler
@@ -194,11 +194,12 @@ files:
194
194
  - lib/cuke_linter/version.rb
195
195
  - testing/cucumber/features/command_line.feature
196
196
  - testing/cucumber/features/configuration/configuring_linters.feature
197
+ - testing/cucumber/features/configuration/locally_scoping_linters.feature
197
198
  - testing/cucumber/features/configuration/using_configurations.feature
199
+ - testing/cucumber/features/custom_linters.feature
200
+ - testing/cucumber/features/default_linters.feature
198
201
  - testing/cucumber/features/formatters/pretty_formatter.feature
199
202
  - testing/cucumber/features/linters/background_does_more_than_setup.feature
200
- - testing/cucumber/features/linters/custom_linters.feature
201
- - testing/cucumber/features/linters/default_linters.feature
202
203
  - testing/cucumber/features/linters/element_with_too_many_tags.feature
203
204
  - testing/cucumber/features/linters/example_without_name.feature
204
205
  - testing/cucumber/features/linters/feature_without_description.feature