betterlint 1.28.0 → 1.29.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/README.md +56 -0
- data/config/default.yml +13 -0
- data/lib/betterlint/version.rb +1 -1
- data/lib/rubocop/cop/betterment/spec_describe_method_name.rb +114 -0
- data/lib/rubocop/cop/betterment.rb +1 -0
- metadata +7 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4be70714f121b7f33abf6c01f95ff109af80785e27d242225d3e4b0dcf3baa2a
|
|
4
|
+
data.tar.gz: 233a10f1796cd42ebdebfa2ef715d7252f9d30abc6498e4f5cb5cec57b3f28a9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7eebfe8e66e6098b8f78a2c5e564b51500bbbb5391f7fb341558495b0a8a8e3124f11be94a0d25ea7a3648a56c7d945d8f39815bdc30d28b8aff5374a7b768f2
|
|
7
|
+
data.tar.gz: d3f7249277cc5268cd7b6182fd4a21f98cf9e8150c6544a10af4476b879bded4675a9851b8b7f9faf541cc1863de573e6c4d6ef4969e8382338fd78620b3a08b
|
data/README.md
CHANGED
|
@@ -367,3 +367,59 @@ the `coder` keyword argument.
|
|
|
367
367
|
- And you've opted into 7.1 defaults (namely, `config.active_record.default_column_serializer = nil`)
|
|
368
368
|
|
|
369
369
|
...you can safely disable this cop, since failing to pass a deserializer will raise an exception.
|
|
370
|
+
|
|
371
|
+
### Betterment/SpecDescribeMethodName
|
|
372
|
+
|
|
373
|
+
This cop requires each spec to name the class under test and the method under test using `describe` blocks. The outermost `describe` blocks should be the class constant, and second level `describe` blocks should be of the form `"#instance_method"` or `".class_method"`. This structure works with [mutant](https://github.com/mbj/mutant) and creates consistently shaped specs
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
# BAD - the outer describe is a string
|
|
377
|
+
RSpec.describe "Invoice" do
|
|
378
|
+
# ...
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# BAD - no method label
|
|
382
|
+
RSpec.describe Invoice do
|
|
383
|
+
it "sums the line items" do
|
|
384
|
+
# ...
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# BAD - the context block breaks the "Invoice#total" label
|
|
389
|
+
RSpec.describe Invoice do
|
|
390
|
+
context "when the account is delinquent" do
|
|
391
|
+
describe "#total" do
|
|
392
|
+
# ...
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# BAD - a second class describe breaks the "Invoice::Item#total" label.
|
|
398
|
+
# Give Invoice::Item its own spec file.
|
|
399
|
+
RSpec.describe Invoice do
|
|
400
|
+
describe Invoice::Item do
|
|
401
|
+
describe "#total" do
|
|
402
|
+
# ...
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# GOOD - shared setup above the method labels
|
|
408
|
+
RSpec.describe Invoice do
|
|
409
|
+
let(:account) { create(:account, :delinquent) }
|
|
410
|
+
|
|
411
|
+
describe "#total" do
|
|
412
|
+
it "adds the late fee" do
|
|
413
|
+
# ...
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
describe ".open" do
|
|
418
|
+
it "excludes paid invoices" do
|
|
419
|
+
# ...
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
This cop is disabled by default, because it reports many offenses in applications with older specs. To use it, enable it in your `.rubocop.yml`. By default it examines all files in `spec/`, except for the `features`, `requests`, `routing`, `system`, and `views` directories, where integration style tests that don't test one class and method are common. Examples in shared example groups are also ignored.
|
data/config/default.yml
CHANGED
|
@@ -114,6 +114,19 @@ Betterment/SitePrismLoaded:
|
|
|
114
114
|
- spec/features/**/*_spec.rb
|
|
115
115
|
- spec/system/**/*_spec.rb
|
|
116
116
|
|
|
117
|
+
Betterment/SpecDescribeMethodName:
|
|
118
|
+
Description: Requires spec `describe` blocks to be labeled with a class constant and method names.
|
|
119
|
+
Enabled: false
|
|
120
|
+
StyleGuide: '#bettermentspecdescribemethodname'
|
|
121
|
+
Include:
|
|
122
|
+
- spec/**/*_spec.rb
|
|
123
|
+
Exclude:
|
|
124
|
+
- spec/features/**/*_spec.rb
|
|
125
|
+
- spec/requests/**/*_spec.rb
|
|
126
|
+
- spec/routing/**/*_spec.rb
|
|
127
|
+
- spec/system/**/*_spec.rb
|
|
128
|
+
- spec/views/**/*_spec.rb
|
|
129
|
+
|
|
117
130
|
Betterment/UnsafeJob:
|
|
118
131
|
Enabled: false
|
|
119
132
|
StyleGuide: '#bettermentunsafejob'
|
data/lib/betterlint/version.rb
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Betterment
|
|
6
|
+
class SpecDescribeMethodName < Base
|
|
7
|
+
MSG_CLASS_LABEL = 'Label the outer `describe` with the class constant, not a string.'
|
|
8
|
+
MSG_METHOD_LABEL = 'Put examples inside a `describe` labeled with a method name, such as `"#instance_method"` or `".class_method"`.'
|
|
9
|
+
MSG_DIRECTLY_INSIDE = 'Put the method `describe` directly inside the class `describe`, and share setup with `let` or `before`.'
|
|
10
|
+
MSG_ONE_CLASS = 'Describe one class in each spec file, and put the method `describe` directly inside the outer `describe`.'
|
|
11
|
+
|
|
12
|
+
METHOD_LABEL = /\A[#.]\S/
|
|
13
|
+
|
|
14
|
+
DESCRIBE_METHODS = %i(describe fdescribe xdescribe).freeze
|
|
15
|
+
EXAMPLE_GROUP_METHODS = %i(describe fdescribe xdescribe context fcontext xcontext).freeze
|
|
16
|
+
SHARED_GROUP_METHODS = %i(shared_examples shared_examples_for shared_context).freeze
|
|
17
|
+
EXAMPLE_METHODS = %i(
|
|
18
|
+
it its specify example scenario
|
|
19
|
+
fit fspecify fexample fscenario
|
|
20
|
+
xit xspecify xexample xscenario
|
|
21
|
+
).freeze
|
|
22
|
+
|
|
23
|
+
RESTRICT_ON_SEND = (DESCRIBE_METHODS + EXAMPLE_METHODS).freeze
|
|
24
|
+
|
|
25
|
+
def on_send(node)
|
|
26
|
+
if DESCRIBE_METHODS.include?(node.method_name)
|
|
27
|
+
check_describe(node) if rspec_receiver?(node)
|
|
28
|
+
elsif node.receiver.nil?
|
|
29
|
+
check_example(node)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
alias on_csend on_send
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def check_describe(node)
|
|
37
|
+
groups = enclosing_groups(node)
|
|
38
|
+
return if groups.any? { |group| shared_group?(group) }
|
|
39
|
+
|
|
40
|
+
check_class_label(node) if groups.empty?
|
|
41
|
+
check_method_describe_placement(node, groups)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def check_example(node)
|
|
45
|
+
groups = enclosing_groups(node)
|
|
46
|
+
return if groups.any? { |group| shared_group?(group) || method_describe?(group) }
|
|
47
|
+
return unless groups.any? { |group| class_describe?(group) }
|
|
48
|
+
|
|
49
|
+
add_offense(node, message: MSG_METHOD_LABEL)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def check_class_label(send_node)
|
|
53
|
+
label = send_node.first_argument
|
|
54
|
+
return if label.nil? || label.const_type?
|
|
55
|
+
|
|
56
|
+
add_offense(label, message: MSG_CLASS_LABEL)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def check_method_describe_placement(send_node, groups)
|
|
60
|
+
return unless method_describe?(send_node)
|
|
61
|
+
|
|
62
|
+
parent = groups.first
|
|
63
|
+
return if parent.nil? || (groups.one? && class_describe?(parent))
|
|
64
|
+
return unless groups.any? { |group| class_describe?(group) }
|
|
65
|
+
|
|
66
|
+
add_offense(send_node, message: class_describe?(parent) ? MSG_ONE_CLASS : MSG_DIRECTLY_INSIDE)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def enclosing_groups(node)
|
|
70
|
+
node.each_ancestor(:block, :numblock).filter_map do |ancestor|
|
|
71
|
+
send_node = ancestor.send_node
|
|
72
|
+
next if send_node.equal?(node)
|
|
73
|
+
|
|
74
|
+
send_node if example_group?(send_node) || shared_group?(send_node)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def example_group?(send_node)
|
|
79
|
+
EXAMPLE_GROUP_METHODS.include?(send_node.method_name) && rspec_receiver?(send_node)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def shared_group?(send_node)
|
|
83
|
+
SHARED_GROUP_METHODS.include?(send_node.method_name) && rspec_receiver?(send_node)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def describe?(send_node)
|
|
87
|
+
DESCRIBE_METHODS.include?(send_node.method_name) && rspec_receiver?(send_node)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def class_describe?(send_node)
|
|
91
|
+
return false unless describe?(send_node)
|
|
92
|
+
|
|
93
|
+
send_node.first_argument&.const_type? || false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def method_describe?(send_node)
|
|
97
|
+
return false unless describe?(send_node)
|
|
98
|
+
|
|
99
|
+
label = send_node.first_argument
|
|
100
|
+
return false unless label&.str_type?
|
|
101
|
+
|
|
102
|
+
METHOD_LABEL.match?(label.value)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def rspec_receiver?(send_node)
|
|
106
|
+
receiver = send_node.receiver
|
|
107
|
+
return true if receiver.nil?
|
|
108
|
+
|
|
109
|
+
receiver.const_type? && receiver.short_name == :RSpec && (receiver.namespace.nil? || receiver.namespace.cbase_type?)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -21,6 +21,7 @@ require 'rubocop/cop/betterment/render_status'
|
|
|
21
21
|
require 'rubocop/cop/betterment/server_error_assertion'
|
|
22
22
|
require 'rubocop/cop/betterment/simple_delegator'
|
|
23
23
|
require 'rubocop/cop/betterment/site_prism_loaded'
|
|
24
|
+
require 'rubocop/cop/betterment/spec_describe_method_name'
|
|
24
25
|
require 'rubocop/cop/betterment/spec_helper_required_outside_spec_dir'
|
|
25
26
|
require 'rubocop/cop/betterment/timeout'
|
|
26
27
|
require 'rubocop/cop/betterment/unsafe_job'
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: betterlint
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Development
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: lint_roller
|
|
@@ -189,6 +188,7 @@ files:
|
|
|
189
188
|
- lib/rubocop/cop/betterment/server_error_assertion.rb
|
|
190
189
|
- lib/rubocop/cop/betterment/simple_delegator.rb
|
|
191
190
|
- lib/rubocop/cop/betterment/site_prism_loaded.rb
|
|
191
|
+
- lib/rubocop/cop/betterment/spec_describe_method_name.rb
|
|
192
192
|
- lib/rubocop/cop/betterment/spec_helper_required_outside_spec_dir.rb
|
|
193
193
|
- lib/rubocop/cop/betterment/timeout.rb
|
|
194
194
|
- lib/rubocop/cop/betterment/unsafe_job.rb
|
|
@@ -203,13 +203,12 @@ licenses:
|
|
|
203
203
|
- MIT
|
|
204
204
|
metadata:
|
|
205
205
|
homepage_uri: https://github.com/Betterment/betterlint
|
|
206
|
-
source_code_uri: https://github.com/Betterment/betterlint/tree/v1.
|
|
207
|
-
changelog_uri: https://github.com/Betterment/betterlint/blob/v1.
|
|
206
|
+
source_code_uri: https://github.com/Betterment/betterlint/tree/v1.29.0
|
|
207
|
+
changelog_uri: https://github.com/Betterment/betterlint/blob/v1.29.0/CHANGELOG.md
|
|
208
208
|
bug_tracker_uri: https://github.com/Betterment/betterlint/issues
|
|
209
|
-
documentation_uri: https://www.rubydoc.info/gems/betterlint/1.
|
|
209
|
+
documentation_uri: https://www.rubydoc.info/gems/betterlint/1.29.0
|
|
210
210
|
rubygems_mfa_required: 'true'
|
|
211
211
|
default_lint_roller_plugin: Betterlint::Plugin
|
|
212
|
-
post_install_message:
|
|
213
212
|
rdoc_options: []
|
|
214
213
|
require_paths:
|
|
215
214
|
- lib
|
|
@@ -224,8 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
224
223
|
- !ruby/object:Gem::Version
|
|
225
224
|
version: '0'
|
|
226
225
|
requirements: []
|
|
227
|
-
rubygems_version:
|
|
228
|
-
signing_key:
|
|
226
|
+
rubygems_version: 4.0.13
|
|
229
227
|
specification_version: 4
|
|
230
228
|
summary: Betterment rubocop configuration
|
|
231
229
|
test_files: []
|