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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -1
- data/CHANGELOG.md +13 -2
- data/LICENSE.txt +1 -1
- data/appveyor.yml +19 -0
- data/cuke_linter.gemspec +1 -1
- data/environments/rspec_env.rb +4 -0
- data/lib/cuke_linter.rb +6 -0
- data/lib/cuke_linter/linters/element_with_common_tags_linter.rb +41 -0
- data/lib/cuke_linter/linters/element_with_duplicate_tags_linter.rb +44 -0
- data/lib/cuke_linter/linters/feature_file_with_mismatched_name_linter.rb +26 -0
- data/lib/cuke_linter/version.rb +1 -1
- data/testing/cucumber/features/command_line.feature +7 -7
- data/testing/cucumber/features/default_linters.feature +3 -0
- data/testing/cucumber/features/linters/element_with_common_tags.feature +28 -0
- data/testing/cucumber/features/linters/element_with_duplicate_tags.feature +71 -0
- data/testing/cucumber/features/linters/feature_file_with_mismatched_name.feature +32 -0
- data/testing/cucumber/step_definitions/setup_steps.rb +25 -0
- data/testing/gemfiles/cuke_modeler1.gemfile +8 -0
- data/testing/gemfiles/cuke_modeler2.gemfile +8 -0
- data/testing/model_factory.rb +14 -2
- data/testing/rspec/spec/integration/cli_integration_spec.rb +8 -7
- data/testing/rspec/spec/integration/cuke_linter_integration_spec.rb +5 -0
- data/testing/rspec/spec/integration/linters/element_with_common_tags_linter_integration_spec.rb +8 -0
- data/testing/rspec/spec/integration/linters/element_with_duplicate_tags_linter_integration_spec.rb +8 -0
- data/testing/rspec/spec/integration/linters/feature_file_with_mismatched_name_integration_spec.rb +8 -0
- data/testing/rspec/spec/unit/linters/element_with_common_tags_linter_unit_spec.rb +248 -0
- data/testing/rspec/spec/unit/linters/element_with_duplicate_tags_linter_unit_spec.rb +203 -0
- data/testing/rspec/spec/unit/linters/element_with_too_many_tags_linter_unit_spec.rb +1 -4
- data/testing/rspec/spec/unit/linters/feature_file_with_invalid_name_linter_unit_spec.rb +1 -1
- data/testing/rspec/spec/unit/linters/feature_file_with_mismatched_name_linter_unit_spec.rb +124 -0
- 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
|
data/testing/model_factory.rb
CHANGED
@@ -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
|
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
|
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:
|
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: '
|
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/
|
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/
|
173
|
-
' <path_to>/
|
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: '
|
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: '
|
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,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
|