cuke_linter 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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