cuke_linter 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -1
  3. data/CHANGELOG.md +13 -2
  4. data/LICENSE.txt +1 -1
  5. data/appveyor.yml +19 -0
  6. data/cuke_linter.gemspec +1 -1
  7. data/environments/rspec_env.rb +4 -0
  8. data/lib/cuke_linter.rb +6 -0
  9. data/lib/cuke_linter/linters/element_with_common_tags_linter.rb +41 -0
  10. data/lib/cuke_linter/linters/element_with_duplicate_tags_linter.rb +44 -0
  11. data/lib/cuke_linter/linters/feature_file_with_mismatched_name_linter.rb +26 -0
  12. data/lib/cuke_linter/version.rb +1 -1
  13. data/testing/cucumber/features/command_line.feature +7 -7
  14. data/testing/cucumber/features/default_linters.feature +3 -0
  15. data/testing/cucumber/features/linters/element_with_common_tags.feature +28 -0
  16. data/testing/cucumber/features/linters/element_with_duplicate_tags.feature +71 -0
  17. data/testing/cucumber/features/linters/feature_file_with_mismatched_name.feature +32 -0
  18. data/testing/cucumber/step_definitions/setup_steps.rb +25 -0
  19. data/testing/gemfiles/cuke_modeler1.gemfile +8 -0
  20. data/testing/gemfiles/cuke_modeler2.gemfile +8 -0
  21. data/testing/model_factory.rb +14 -2
  22. data/testing/rspec/spec/integration/cli_integration_spec.rb +8 -7
  23. data/testing/rspec/spec/integration/cuke_linter_integration_spec.rb +5 -0
  24. data/testing/rspec/spec/integration/linters/element_with_common_tags_linter_integration_spec.rb +8 -0
  25. data/testing/rspec/spec/integration/linters/element_with_duplicate_tags_linter_integration_spec.rb +8 -0
  26. data/testing/rspec/spec/integration/linters/feature_file_with_mismatched_name_integration_spec.rb +8 -0
  27. data/testing/rspec/spec/unit/linters/element_with_common_tags_linter_unit_spec.rb +248 -0
  28. data/testing/rspec/spec/unit/linters/element_with_duplicate_tags_linter_unit_spec.rb +203 -0
  29. data/testing/rspec/spec/unit/linters/element_with_too_many_tags_linter_unit_spec.rb +1 -4
  30. data/testing/rspec/spec/unit/linters/feature_file_with_invalid_name_linter_unit_spec.rb +1 -1
  31. data/testing/rspec/spec/unit/linters/feature_file_with_mismatched_name_linter_unit_spec.rb +124 -0
  32. metadata +24 -4
@@ -29,6 +29,10 @@ Given(/^a linter for features with invalid file names$/) do
29
29
  @linter = CukeLinter::FeatureFileWithInvalidNameLinter.new
30
30
  end
31
31
 
32
+ And(/^a linter for features with mismatched file names$/) do
33
+ @linter = CukeLinter::FeatureFileWithMismatchedNameLinter.new
34
+ end
35
+
32
36
  Given(/^no other linters have been registered or unregistered$/) do
33
37
  CukeLinter.reset_linters
34
38
  end
@@ -89,6 +93,10 @@ Given(/^a linter for elements with too many tags has been registered$/) do
89
93
  CukeLinter.register_linter(linter: CukeLinter::ElementWithTooManyTagsLinter.new, name: 'ElementWithTooManyTagsLinter')
90
94
  end
91
95
 
96
+ Given(/^a linter for elements with duplicate tags has been registered$/) do
97
+ CukeLinter.register_linter(linter: CukeLinter::ElementWithDuplicateTagsLinter.new, name: 'ElementWithDuplicateTagsLinter')
98
+ end
99
+
92
100
  Given(/^a linter for tests with too many steps has been registered$/) do
93
101
  CukeLinter.register_linter(linter: CukeLinter::TestWithTooManyStepsLinter.new, name: 'TestWithTooManyStepsLinter')
94
102
  end
@@ -121,6 +129,14 @@ Given(/^a linter for tests with an action step as the final step$/) do
121
129
  @linter = CukeLinter::TestWithActionStepAsFinalStepLinter.new
122
130
  end
123
131
 
132
+ Given(/^a linter for elements with duplicate tags$/) do
133
+ @linter = CukeLinter::ElementWithDuplicateTagsLinter.new
134
+ end
135
+
136
+ Given(/^a linter for elements with common tags$/) do
137
+ @linter = CukeLinter::ElementWithCommonTagsLinter.new
138
+ end
139
+
124
140
  Given(/^a linter for test steps with too many characters has been registered$/) do
125
141
  CukeLinter.register_linter(linter: CukeLinter::StepWithTooManyCharactersLinter.new, name: 'StepWithTooManyCharactersLinter')
126
142
  end
@@ -203,3 +219,12 @@ Given(/^a feature file model named "([^"]*)"$/) do |file_path|
203
219
 
204
220
  @model.path = file_path
205
221
  end
222
+
223
+ Given(/^a feature file model based on the file "([^"]*)" with the following text:$/) do |file_name, text|
224
+ file_path = CukeLinter::FileHelper.create_file(directory: @root_test_directory, extension: '.feature', text: text, name: file_name)
225
+
226
+ @created_files ||= []
227
+ @created_files << file_path
228
+
229
+ @model = CukeModeler::FeatureFile.new(file_path)
230
+ end
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cuke_linter.gemspec
6
+ gemspec :path => "../../"
7
+
8
+ gem 'cuke_modeler', '~> 1.0'
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in cuke_linter.gemspec
6
+ gemspec :path => "../../"
7
+
8
+ gem 'cuke_modeler', '~> 2.0'
@@ -7,6 +7,12 @@ module CukeLinter
7
7
  include CukeModeler::Sourceable
8
8
  include CukeModeler::Taggable
9
9
 
10
+ def initialize
11
+ super
12
+
13
+ @tags = []
14
+ end
15
+
10
16
  end
11
17
  end
12
18
 
@@ -24,11 +30,13 @@ module CukeLinter
24
30
  model = CukeModeler::Feature.new(source_text)
25
31
  model.parent_model = fake_parent_model
26
32
 
33
+ fake_parent_model.feature = model
34
+
27
35
  model
28
36
  end
29
37
 
30
38
  def self.generate_feature_file_model
31
- model = CukeModeler::FeatureFile.new
39
+ model = CukeModeler::FeatureFile.new
32
40
 
33
41
  model
34
42
  end
@@ -42,7 +50,7 @@ module CukeLinter
42
50
  model
43
51
  end
44
52
 
45
- def self.generate_outline_model(source_text: "Scenario Outline:\n*a step\nExamples:\n|param|", parent_file_path: 'path_to_file')
53
+ def self.generate_outline_model(source_text: "Scenario Outline:", parent_file_path: 'path_to_file')
46
54
  fake_parent_model = generate_feature_model(parent_file_path: parent_file_path)
47
55
 
48
56
  model = CukeModeler::Outline.new(source_text)
@@ -78,6 +86,10 @@ module CukeLinter
78
86
  model
79
87
  end
80
88
 
89
+ def self.generate_tag_model(source_text: '@a_tag')
90
+ CukeModeler::Tag.new(source_text)
91
+ end
92
+
81
93
  def self.generate_lintable_model(parent_file_path: 'path_to_file', source_line: 1, children: [])
82
94
  fake_file_model = CukeModeler::FeatureFile.new
83
95
  fake_file_model.path = parent_file_path
@@ -99,7 +99,7 @@ RSpec.describe 'the Command Line Interface' do
99
99
  let!(:linted_file) { CukeLinter::FileHelper.create_file(directory: test_directory,
100
100
  name: 'nothing_wrong',
101
101
  extension: '.feature',
102
- text: 'Feature: A name
102
+ text: 'Feature: Nothing wrong
103
103
  A description
104
104
  Scenario: A scenario
105
105
  When a step
@@ -148,14 +148,14 @@ RSpec.describe 'the Command Line Interface' do
148
148
 
149
149
  context 'with path arguments' do
150
150
  let(:file_1) { CukeLinter::FileHelper.create_file(directory: test_directory,
151
- name: 'some',
151
+ name: 'some_feature',
152
152
  extension: '.feature',
153
153
  text: 'Feature: Some feature
154
154
  Scenario: A scenario
155
155
  When a step
156
156
  Then a step') }
157
157
  let(:file_2) { CukeLinter::FileHelper.create_file(directory: test_directory,
158
- name: 'a_directory/with_a',
158
+ name: 'a_directory/with/some_feature',
159
159
  extension: '.feature',
160
160
  text: 'Feature: Some feature
161
161
  Scenario: A scenario
@@ -165,12 +165,13 @@ RSpec.describe 'the Command Line Interface' do
165
165
  let(:file_2_directory) { File.dirname(file_2) }
166
166
  let(:command) { "bundle exec ruby #{executable_path} #{flag} #{file_1_path} #{flag} #{file_2_directory}" }
167
167
 
168
+ # TODO: add a negative test that makes sure that non-included paths aren't linted when paths are explicitly included
168
169
 
169
170
  it "lints that locations specified by '#{path_flag}'" do
170
171
  expect(results[:std_out]).to eq(['FeatureWithoutDescriptionLinter',
171
172
  ' Feature has no description',
172
- ' <path_to>/a_directory/with_a.feature:1',
173
- ' <path_to>/some.feature:1',
173
+ ' <path_to>/a_directory/with/some_feature.feature:1',
174
+ ' <path_to>/some_feature.feature:1',
174
175
  '',
175
176
  '2 issues found',
176
177
  ''].join("\n").gsub('<path_to>', test_directory))
@@ -209,7 +210,7 @@ RSpec.describe 'the Command Line Interface' do
209
210
  let(:flag) { formatter_flag }
210
211
 
211
212
  context 'with formatter arguments' do
212
- let(:linted_file) { CukeLinter::FileHelper.create_file(name: 'some',
213
+ let(:linted_file) { CukeLinter::FileHelper.create_file(name: 'some_feature',
213
214
  extension: '.feature',
214
215
  text: 'Feature: Some feature
215
216
  Scenario: A scenario
@@ -275,7 +276,7 @@ RSpec.describe 'the Command Line Interface' do
275
276
  context 'with output arguments' do
276
277
  let(:output_location) { "#{CukeLinter::FileHelper.create_directory}/output.txt" }
277
278
  let(:other_output_location) { "#{CukeLinter::FileHelper.create_directory}/other_output.txt" }
278
- let(:linted_file) { CukeLinter::FileHelper.create_file(name: 'some',
279
+ let(:linted_file) { CukeLinter::FileHelper.create_file(name: 'some_feature',
279
280
  extension: '.feature',
280
281
  text: 'Feature: Some feature
281
282
  Scenario: A scenario
@@ -81,6 +81,8 @@ RSpec.describe CukeLinter do
81
81
  linting_options.delete(:model_trees)
82
82
  end
83
83
 
84
+ # TODO: add a negative test that makes sure that non-included paths aren't linted when paths are explicitly included
85
+
84
86
  it 'lints every model in each path' do
85
87
  results = subject.lint(linting_options)
86
88
 
@@ -177,9 +179,12 @@ RSpec.describe CukeLinter do
177
179
  subject.reset_linters
178
180
 
179
181
  default_linter_classes = ['BackgroundDoesMoreThanSetupLinter',
182
+ 'ElementWithCommonTagsLinter',
183
+ 'ElementWithDuplicateTagsLinter',
180
184
  'ElementWithTooManyTagsLinter',
181
185
  'ExampleWithoutNameLinter',
182
186
  'FeatureFileWithInvalidNameLinter',
187
+ 'FeatureFileWithMismatchedNameLinter',
183
188
  'FeatureWithTooManyDifferentTagsLinter',
184
189
  'FeatureWithoutDescriptionLinter',
185
190
  'FeatureWithoutNameLinter',
@@ -0,0 +1,8 @@
1
+ require_relative '../../../../../environments/rspec_env'
2
+
3
+
4
+ RSpec.describe CukeLinter::ElementWithCommonTagsLinter do
5
+
6
+ it_should_behave_like 'a linter at the integration level'
7
+
8
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../../../../../environments/rspec_env'
2
+
3
+
4
+ RSpec.describe CukeLinter::ElementWithDuplicateTagsLinter do
5
+
6
+ it_should_behave_like 'a linter at the integration level'
7
+
8
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../../../../../environments/rspec_env'
2
+
3
+
4
+ RSpec.describe CukeLinter::FeatureFileWithMismatchedNameLinter do
5
+
6
+ it_should_behave_like 'a linter at the integration level'
7
+
8
+ end
@@ -0,0 +1,248 @@
1
+ require_relative '../../../../../environments/rspec_env'
2
+
3
+
4
+ RSpec.describe CukeLinter::ElementWithCommonTagsLinter do
5
+
6
+ let(:model_file_path) { 'some_file_path' }
7
+
8
+ it_should_behave_like 'a linter at the unit level'
9
+
10
+
11
+ it 'has a name' do
12
+ expect(subject.name).to eq('ElementWithCommonTagsLinter')
13
+ end
14
+
15
+ describe 'linting' do
16
+
17
+ ELEMENTS_WITH_TAGGABLE_CHILDREN.each do |model_type|
18
+
19
+ context "with a #{model_type} that has common tags on all of its children" do
20
+
21
+ let(:test_model) do
22
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
23
+
24
+ 2.times do
25
+ child_model = CukeLinter::ModelFactory.generate_lintable_model
26
+ child_model.tags = [CukeLinter::ModelFactory.generate_tag_model(source_text: '@same')]
27
+
28
+ case model_type
29
+ when 'feature'
30
+ model.tests << child_model
31
+ when 'outline'
32
+ model.examples << child_model
33
+ else
34
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
35
+ end
36
+
37
+ end
38
+
39
+ model
40
+ end
41
+
42
+ it_should_behave_like 'a linter linting a bad model'
43
+
44
+
45
+ it 'records a problem' do
46
+ result = subject.lint(test_model)
47
+
48
+ case model_type
49
+ when 'feature'
50
+ expect(result[:problem]).to match(/^All tests in Feature have tag '@\w+'\. Move tag to Feature level\.$/)
51
+ when 'outline'
52
+ expect(result[:problem]).to match(/^All Examples in Outline have tag '@\w+'\. Move tag to Outline level\.$/)
53
+ else
54
+ raise(ArgumentError, "Don't know how to verify a '#{model_type}'. Add a new case?")
55
+ end
56
+ end
57
+
58
+ it 'includes the name of the common tag found in the problem record' do
59
+ common_tag = test_model.children.first.tags.first.name
60
+ result = subject.lint(test_model)
61
+ expect(result[:problem]).to match(/have tag '#{common_tag}'\./)
62
+
63
+ test_model.children.first.tags = [CukeLinter::ModelFactory.generate_tag_model(source_text: '@still_same')]
64
+ test_model.children.last.tags = [CukeLinter::ModelFactory.generate_tag_model(source_text: '@still_same')]
65
+
66
+ common_tag = test_model.children.first.tags.first.name
67
+ result = subject.lint(test_model)
68
+ expect(result[:problem]).to match(/have tag '#{common_tag}'\./)
69
+ end
70
+
71
+ end
72
+
73
+ context "with a #{model_type} that does not have common tags on all of its children" do
74
+
75
+ context 'because none of their tags are common' do
76
+
77
+ let(:test_model) do
78
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
79
+
80
+ 2.times do |count|
81
+ child_model = CukeLinter::ModelFactory.generate_lintable_model
82
+ child_model.tags = [CukeLinter::ModelFactory.generate_tag_model(source_text: "@tag_#{count}")]
83
+
84
+ case model_type
85
+ when 'feature'
86
+ model.tests << child_model
87
+ when 'outline'
88
+ model.examples << child_model
89
+ else
90
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
91
+ end
92
+
93
+ end
94
+
95
+ model
96
+ end
97
+
98
+ it_should_behave_like 'a linter linting a good model'
99
+
100
+ end
101
+
102
+ context 'because some of them have no tags' do
103
+
104
+ context 'because their tags are empty' do
105
+
106
+ let(:test_model) do
107
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
108
+
109
+ 2.times do
110
+ child_model = CukeLinter::ModelFactory.generate_lintable_model
111
+ child_model.tags = []
112
+
113
+ case model_type
114
+ when 'feature'
115
+ model.tests << child_model
116
+ when 'outline'
117
+ model.examples << child_model
118
+ else
119
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
120
+ end
121
+
122
+ end
123
+
124
+ model
125
+ end
126
+
127
+ it_should_behave_like 'a linter linting a good model'
128
+
129
+ end
130
+
131
+ context 'because their tags are nil' do
132
+
133
+ let(:test_model) do
134
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
135
+
136
+ 2.times do
137
+ child_model = CukeLinter::ModelFactory.generate_lintable_model
138
+ child_model.tags = nil
139
+
140
+ case model_type
141
+ when 'feature'
142
+ model.tests << child_model
143
+ when 'outline'
144
+ model.examples << child_model
145
+ else
146
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
147
+ end
148
+
149
+ end
150
+
151
+ model
152
+ end
153
+
154
+ it_should_behave_like 'a linter linting a good model'
155
+
156
+ end
157
+
158
+ end
159
+
160
+ context 'because the model only has one child' do
161
+
162
+ let(:test_model) do
163
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
164
+
165
+ 1.times do
166
+ child_model = CukeLinter::ModelFactory.generate_lintable_model
167
+ child_model.tags = [CukeLinter::ModelFactory.generate_tag_model(source_text: '@a_tag')]
168
+
169
+ case model_type
170
+ when 'feature'
171
+ model.tests << child_model
172
+ when 'outline'
173
+ model.examples << child_model
174
+ else
175
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
176
+ end
177
+
178
+ end
179
+
180
+ model
181
+ end
182
+
183
+ it_should_behave_like 'a linter linting a good model'
184
+
185
+ end
186
+
187
+ context 'because the model has no children' do
188
+
189
+ context 'because the children are empty' do
190
+
191
+ let(:test_model) do
192
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
193
+
194
+ case model_type
195
+ when 'feature'
196
+ model.tests = []
197
+ when 'outline'
198
+ model.examples = []
199
+ else
200
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
201
+ end
202
+
203
+ model
204
+ end
205
+
206
+ it_should_behave_like 'a linter linting a good model'
207
+
208
+ end
209
+
210
+ context 'because the children are nil' do
211
+
212
+ let(:test_model) do
213
+ model = CukeLinter::ModelFactory.send("generate_#{model_type}_model", parent_file_path: model_file_path)
214
+
215
+ case model_type
216
+ when 'feature'
217
+ model.tests = nil
218
+ when 'outline'
219
+ model.examples = nil
220
+ else
221
+ raise(ArgumentError, "Don't know how to setup a '#{model_type}'. Add a new case?")
222
+ end
223
+
224
+ model
225
+ end
226
+
227
+ it_should_behave_like 'a linter linting a good model'
228
+
229
+ end
230
+
231
+ end
232
+
233
+ end
234
+
235
+ end
236
+
237
+
238
+ context 'a model that is not a type that has taggable children' do
239
+
240
+ let(:test_model) { CukeModeler::Model.new }
241
+
242
+ it_should_behave_like 'a linter linting a good model'
243
+
244
+ end
245
+
246
+ end
247
+
248
+ end