rubocop-minitest 0.15.2 → 0.16.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: e8c46717c1b414495df0bdd66c1733962dc3f1ded4547a671abde61c4b262903
4
- data.tar.gz: d0ef47f1db7c7d665749e90cdf15df05b5be27226828c4108d0b95755b1cffc3
3
+ metadata.gz: ae51cf25ec8713911a7b48130c68450d8cb983cbf280757d65fbad039deb9005
4
+ data.tar.gz: 15ade21076b9c6f31ae2336b1d0dabd7f626aa334a3f36d4fb007021121d73fa
5
5
  SHA512:
6
- metadata.gz: a0f124d91add6696ce7d74104be315156395efb07c3c312acaa767158fd4ac25477e1e70120ace728068e1f484d64d3934cb6578d1fba4e397d53537b1bf2c2d
7
- data.tar.gz: 72c3a8f9c53a837f1c3564e1eba047fac0dfdd900adcbcf35ec234c7202358fcd9b1e8b7208e7e929dfa447fa3de81f5fb731608224bfcf6285567376d921fee
6
+ metadata.gz: 8e2735ab5ec9466052e86755ab05eb739039a4eaf4c3aa25cf22beed6f292251f845561ed7cdb5596094b15835ff67d2f66acb18c5c99ddd66c6ebd23ce83291
7
+ data.tar.gz: 9c352bf7cbd8dc650a57591c9add7d3b11f1cd0f87ec5c3e629befa1149e05987640d50e5f0301b957c308e1bbf43524785e6ed4993c3caf25bdd0b78279958e
data/.circleci/config.yml CHANGED
@@ -54,5 +54,5 @@ workflows:
54
54
  name: Ruby HEAD
55
55
  image: rubocophq/circleci-ruby-snapshot:latest # Nightly snapshot build
56
56
  - rake_default:
57
- name: JRuby 9.2
58
- image: circleci/jruby:9.2
57
+ name: JRuby 9.3
58
+ image: circleci/jruby:9.3
@@ -10,7 +10,7 @@ Before submitting the PR make sure the following are checked:
10
10
  * [ ] Feature branch is up-to-date with `master` (if not - rebase it).
11
11
  * [ ] Squashed related commits together.
12
12
  * [ ] Added tests.
13
- * [ ] Added an entry to the [Changelog](https://github.com/rubocop-hq/rubocop-minitest/blob/master/CHANGELOG.md) if the new code introduces user-observable changes. See [changelog entry format](https://github.com/rubocop-hq/rubocop-minitest/blob/master/CONTRIBUTING.md#changelog-entry-format).
13
+ * [ ] Added an entry (file) to the [changelog folder](https://github.com/rubocop/rubocop-minitest/blob/master/changelog/) named `{change_type}_{change_description}.md` if the new code introduces user-observable changes. See [changelog entry format](https://github.com/rubocop/rubocop/blob/master/CONTRIBUTING.md#changelog-entry-format) for details.
14
14
  * [ ] The PR relates to *only* one subject with a clear title
15
15
  and description in grammatically correct, complete sentences.
16
16
  * [ ] Run `bundle exec rake default`. It executes all tests and RuboCop for itself, and generates the documentation.
data/.rubocop.yml CHANGED
@@ -42,6 +42,7 @@ Style/FormatStringToken:
42
42
  Metrics/ClassLength:
43
43
  Exclude:
44
44
  - test/**/*
45
+ - tasks/changelog.rb
45
46
 
46
47
  Layout/EndOfLine:
47
48
  EnforcedStyle: lf
@@ -84,6 +85,7 @@ Metrics/BlockLength:
84
85
  - 'Rakefile'
85
86
  - '**/*.rake'
86
87
  - 'test/**/*.rb'
88
+ - 'rubocop-minitest.gemspec'
87
89
 
88
90
  Naming/FileName:
89
91
  Exclude:
data/.rubocop_todo.yml CHANGED
@@ -10,6 +10,8 @@
10
10
  # Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
11
11
  Metrics/MethodLength:
12
12
  Max: 14
13
+ Exclude:
14
+ - 'test/rubocop/cop/minitest/global_expectations_test.rb'
13
15
 
14
16
  # Offense count: 1
15
17
  Style/DocumentDynamicEvalDefinition:
data/CHANGELOG.md CHANGED
@@ -2,11 +2,22 @@
2
2
 
3
3
  ## master (unreleased)
4
4
 
5
+ ## 0.16.0 (2021-11-14)
6
+
7
+ ### New features
8
+
9
+ * [#147](https://github.com/rubocop/rubocop-minitest/issues/147): Add `EnforcedStyle` config parameter for `Minitest/GlobalExpectations`. ([@gi][])
10
+
11
+ ### Bug fixes
12
+
13
+ * [#142](https://github.com/rubocop/rubocop-minitest/issues/142): Fix `Minitest/GlobalExpectations` autocorrect when receiver is lambda. ([@gi][])
14
+ * [#150](https://github.com/rubocop/rubocop-minitest/issues/150): Fix a false positive for `Minitest/AssertEmpty` and `RefuteEmpty` cops when using `empty` method with any arguments. ([@koic][])
15
+
5
16
  ## 0.15.2 (2021-10-11)
6
17
 
7
18
  ### Bug fixes
8
19
 
9
- * [#144](https://github.com/rubocop/rubocop-minitest/pull/144): Mark `Minitest/AssertEmptyLiteral` as safe auto-correction. ([@koic][])
20
+ * [#145](https://github.com/rubocop/rubocop-minitest/pull/145): Mark `Minitest/AssertEmptyLiteral` as safe auto-correction. ([@koic][])
10
21
 
11
22
  ## 0.15.1 (2021-09-26)
12
23
 
@@ -246,3 +257,4 @@
246
257
  [@tsmmark]: https://github.com/tsmmark
247
258
  [@cstyles]: https://github.com/cstyles
248
259
  [@ghiculescu]: https://github.com/ghiculescu
260
+ [@gi]: https://github.com/gi
data/CONTRIBUTING.md CHANGED
@@ -33,7 +33,7 @@ $ rubocop -V
33
33
  * If your change has a corresponding open GitHub issue, prefix the commit message with `[Fix #github-issue-number]`.
34
34
  * Make sure to add tests for it. This is important so I don't break it
35
35
  in a future version unintentionally.
36
- * Add an entry to the [Changelog](CHANGELOG.md) accordingly. See [changelog entry format](#changelog-entry-format).
36
+ * Add an entry to the [Changelog](CHANGELOG.md) by creating a file `changelog/{type}_{some_description}.md`. See [changelog entry format](#changelog-entry-format) for details.
37
37
  * Please try not to mess with the Rakefile, version, or history. If
38
38
  you want to have your own version, or is otherwise necessary, that
39
39
  is fine, but please isolate to its own commit so I can cherry-pick
@@ -53,12 +53,13 @@ Here are a few examples:
53
53
  * New cop `ElseLayout` checks for odd arrangement of code in the `else` branch of a conditional expression. ([@bbatsov][])
54
54
  ```
55
55
 
56
+ * Create one file `changelog/{type}_{some_description}.md`, where `type` is `new` (New feature), `fix` or `change`, and `some_description` is unique to avoid conflicts. Task `changelog:fix` (or `:new` or `:change`) can help you.
56
57
  * Mark it up in [Markdown syntax][6].
57
58
  * The entry line should start with `* ` (an asterisk and a space).
58
59
  * If the change has a related GitHub issue (e.g. a bug fix for a reported issue), put a link to the issue as `[#123](https://github.com/rubocop/rubocop-minitest/issues/123): `.
59
60
  * Describe the brief of the change. The sentence should end with a punctuation.
60
61
  * At the end of the entry, add an implicit link to your GitHub user page as `([@username][])`.
61
- * If this is your first contribution to RuboCop project, add a link definition for the implicit link to the bottom of the changelog as `[@username]: https://github.com/username`.
62
+ * Alternatively, you may modify the CHANGELOG file directly, but this may result in conflicts later on. Also, if this is your first contribution to RuboCop Minitest project, add a link definition for the implicit link to the bottom of the changelog as `[@username]: https://github.com/username`.
62
63
 
63
64
  [1]: https://github.com/rubocop/rubocop-minitest/issues
64
65
  [2]: https://www.gun.io/blog/how-to-github-fork-branch-and-pull-request
data/Gemfile CHANGED
@@ -9,5 +9,5 @@ gemspec
9
9
  gem 'bump', require: false
10
10
  gem 'rake'
11
11
  gem 'rubocop', github: 'rubocop/rubocop'
12
- gem 'rubocop-performance', '~> 1.11.0'
12
+ gem 'rubocop-performance', '~> 1.12.0'
13
13
  gem 'yard', '~> 0.9'
data/Rakefile CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ task release: 'changelog:check_clean' # Before task is required
4
+
3
5
  require 'bundler'
4
6
  require 'bundler/gem_tasks'
5
7
 
@@ -42,10 +44,7 @@ task :new_cop, [:cop] do |_task, args|
42
44
  exit!
43
45
  end
44
46
 
45
- github_user = `git config github.user`.chop
46
- github_user = 'your_id' if github_user.empty?
47
-
48
- generator = RuboCop::Cop::Generator.new(cop_name, github_user)
47
+ generator = RuboCop::Cop::Generator.new(cop_name)
49
48
 
50
49
  generator.write_source
51
50
  generator.write_test
data/config/default.yml CHANGED
@@ -103,7 +103,19 @@ Minitest/GlobalExpectations:
103
103
  Description: 'This cop checks for deprecated global expectations.'
104
104
  StyleGuide: 'https://minitest.rubystyle.guide#global-expectations'
105
105
  Enabled: true
106
+ EnforcedStyle: any
107
+ Include:
108
+ - '**/test/**/*'
109
+ - '**/*_test.rb'
110
+ - '**/spec/**/*'
111
+ - '**/*_spec.rb'
112
+ SupportedStyles:
113
+ - _
114
+ - any
115
+ - expect
116
+ - value
106
117
  VersionAdded: '0.7'
118
+ VersionChanged: '0.16'
107
119
 
108
120
  Minitest/LiteralAsActualArgument:
109
121
  Description: 'This cop enforces correct order of `expected` and `actual` arguments for `assert_equal`.'
data/docs/antora.yml CHANGED
@@ -2,6 +2,6 @@ name: rubocop-minitest
2
2
  title: RuboCop Minitest
3
3
  # We always provide version without patch here (e.g. 1.1),
4
4
  # as patch versions should not appear in the docs.
5
- version: '0.15'
5
+ version: '0.16'
6
6
  nav:
7
7
  - modules/ROOT/nav.adoc
@@ -519,7 +519,7 @@ end
519
519
  | Yes
520
520
  | Yes
521
521
  | 0.7
522
- | -
522
+ | 0.16
523
523
  |===
524
524
 
525
525
  This cop checks for deprecated global expectations
@@ -527,6 +527,54 @@ and autocorrects them to use expect format.
527
527
 
528
528
  === Examples
529
529
 
530
+ ==== EnforcedStyle: _
531
+
532
+ [source,ruby]
533
+ ----
534
+ # bad
535
+ musts.must_equal expected_musts
536
+ wonts.wont_match expected_wonts
537
+ musts.must_raise TypeError
538
+
539
+ expect(musts).must_equal expected_musts
540
+ expect(wonts).wont_match expected_wonts
541
+ expect { musts }.must_raise TypeError
542
+
543
+ value(musts).must_equal expected_musts
544
+ value(wonts).wont_match expected_wonts
545
+ value { musts }.must_raise TypeError
546
+
547
+ # good
548
+ _(musts).must_equal expected_musts
549
+ _(wonts).wont_match expected_wonts
550
+ _ { musts }.must_raise TypeError
551
+ ----
552
+
553
+ ==== EnforcedStyle: any (default)
554
+
555
+ [source,ruby]
556
+ ----
557
+ # bad
558
+ musts.must_equal expected_musts
559
+ wonts.wont_match expected_wonts
560
+ musts.must_raise TypeError
561
+
562
+ # good
563
+ _(musts).must_equal expected_musts
564
+ _(wonts).wont_match expected_wonts
565
+ _ { musts }.must_raise TypeError
566
+
567
+ expect(musts).must_equal expected_musts
568
+ expect(wonts).wont_match expected_wonts
569
+ expect { musts }.must_raise TypeError
570
+
571
+ value(musts).must_equal expected_musts
572
+ value(wonts).wont_match expected_wonts
573
+ value { musts }.must_raise TypeError
574
+ ----
575
+
576
+ ==== EnforcedStyle: expect
577
+
530
578
  [source,ruby]
531
579
  ----
532
580
  # bad
@@ -534,12 +582,57 @@ musts.must_equal expected_musts
534
582
  wonts.wont_match expected_wonts
535
583
  musts.must_raise TypeError
536
584
 
585
+ _(musts).must_equal expected_musts
586
+ _(wonts).wont_match expected_wonts
587
+ _ { musts }.must_raise TypeError
588
+
589
+ value(musts).must_equal expected_musts
590
+ value(wonts).wont_match expected_wonts
591
+ value { musts }.must_raise TypeError
592
+
537
593
  # good
594
+ expect(musts).must_equal expected_musts
595
+ expect(wonts).wont_match expected_wonts
596
+ expect { musts }.must_raise TypeError
597
+ ----
598
+
599
+ ==== EnforcedStyle: value
600
+
601
+ [source,ruby]
602
+ ----
603
+ # bad
604
+ musts.must_equal expected_musts
605
+ wonts.wont_match expected_wonts
606
+ musts.must_raise TypeError
607
+
538
608
  _(musts).must_equal expected_musts
539
609
  _(wonts).wont_match expected_wonts
540
610
  _ { musts }.must_raise TypeError
611
+
612
+ expect(musts).must_equal expected_musts
613
+ expect(wonts).wont_match expected_wonts
614
+ expect { musts }.must_raise TypeError
615
+
616
+ # good
617
+ value(musts).must_equal expected_musts
618
+ value(wonts).wont_match expected_wonts
619
+ value { musts }.must_raise TypeError
541
620
  ----
542
621
 
622
+ === Configurable attributes
623
+
624
+ |===
625
+ | Name | Default value | Configurable values
626
+
627
+ | EnforcedStyle
628
+ | `any`
629
+ | `_`, `any`, `expect`, `value`
630
+
631
+ | Include
632
+ | `+**/test/**/*+`, `+**/*_test.rb+`, `+**/spec/**/*+`, `+**/*_spec.rb+`
633
+ | Array
634
+ |===
635
+
543
636
  === References
544
637
 
545
638
  * https://minitest.rubystyle.guide#global-expectations
@@ -19,6 +19,18 @@ module RuboCop
19
19
  extend MinitestCopRule
20
20
 
21
21
  define_rule :assert, target_method: :empty?
22
+
23
+ remove_method :on_send
24
+ def on_send(node)
25
+ return unless node.method?(:assert)
26
+ return unless (arguments = peel_redundant_parentheses_from(node.arguments))
27
+ return unless arguments.first.respond_to?(:method?) && arguments.first.method?(:empty?)
28
+ return unless arguments.first.arguments.empty?
29
+
30
+ add_offense(node, message: offense_message(arguments)) do |corrector|
31
+ autocorrect(corrector, node, arguments)
32
+ end
33
+ end
22
34
  end
23
35
  end
24
36
  end
@@ -6,17 +6,83 @@ module RuboCop
6
6
  # This cop checks for deprecated global expectations
7
7
  # and autocorrects them to use expect format.
8
8
  #
9
- # @example
9
+ # @example EnforcedStyle: _
10
10
  # # bad
11
11
  # musts.must_equal expected_musts
12
12
  # wonts.wont_match expected_wonts
13
13
  # musts.must_raise TypeError
14
14
  #
15
+ # expect(musts).must_equal expected_musts
16
+ # expect(wonts).wont_match expected_wonts
17
+ # expect { musts }.must_raise TypeError
18
+ #
19
+ # value(musts).must_equal expected_musts
20
+ # value(wonts).wont_match expected_wonts
21
+ # value { musts }.must_raise TypeError
22
+ #
15
23
  # # good
16
24
  # _(musts).must_equal expected_musts
17
25
  # _(wonts).wont_match expected_wonts
18
26
  # _ { musts }.must_raise TypeError
27
+ #
28
+ # @example EnforcedStyle: any (default)
29
+ # # bad
30
+ # musts.must_equal expected_musts
31
+ # wonts.wont_match expected_wonts
32
+ # musts.must_raise TypeError
33
+ #
34
+ # # good
35
+ # _(musts).must_equal expected_musts
36
+ # _(wonts).wont_match expected_wonts
37
+ # _ { musts }.must_raise TypeError
38
+ #
39
+ # expect(musts).must_equal expected_musts
40
+ # expect(wonts).wont_match expected_wonts
41
+ # expect { musts }.must_raise TypeError
42
+ #
43
+ # value(musts).must_equal expected_musts
44
+ # value(wonts).wont_match expected_wonts
45
+ # value { musts }.must_raise TypeError
46
+ #
47
+ # @example EnforcedStyle: expect
48
+ # # bad
49
+ # musts.must_equal expected_musts
50
+ # wonts.wont_match expected_wonts
51
+ # musts.must_raise TypeError
52
+ #
53
+ # _(musts).must_equal expected_musts
54
+ # _(wonts).wont_match expected_wonts
55
+ # _ { musts }.must_raise TypeError
56
+ #
57
+ # value(musts).must_equal expected_musts
58
+ # value(wonts).wont_match expected_wonts
59
+ # value { musts }.must_raise TypeError
60
+ #
61
+ # # good
62
+ # expect(musts).must_equal expected_musts
63
+ # expect(wonts).wont_match expected_wonts
64
+ # expect { musts }.must_raise TypeError
65
+ #
66
+ # @example EnforcedStyle: value
67
+ # # bad
68
+ # musts.must_equal expected_musts
69
+ # wonts.wont_match expected_wonts
70
+ # musts.must_raise TypeError
71
+ #
72
+ # _(musts).must_equal expected_musts
73
+ # _(wonts).wont_match expected_wonts
74
+ # _ { musts }.must_raise TypeError
75
+ #
76
+ # expect(musts).must_equal expected_musts
77
+ # expect(wonts).wont_match expected_wonts
78
+ # expect { musts }.must_raise TypeError
79
+ #
80
+ # # good
81
+ # value(musts).must_equal expected_musts
82
+ # value(wonts).wont_match expected_wonts
83
+ # value { musts }.must_raise TypeError
19
84
  class GlobalExpectations < Base
85
+ include ConfigurableEnforcedStyle
20
86
  extend AutoCorrector
21
87
 
22
88
  MSG = 'Use `%<preferred>s` instead.'
@@ -34,58 +100,63 @@ module RuboCop
34
100
 
35
101
  RESTRICT_ON_SEND = VALUE_MATCHERS + BLOCK_MATCHERS
36
102
 
37
- VALUE_MATCHERS_STR = VALUE_MATCHERS.map do |m|
38
- ":#{m}"
39
- end.join(' ').freeze
103
+ # There are aliases for the `_` method - `expect` and `value`
104
+ DSL_METHODS = %i[_ expect value].freeze
40
105
 
41
- BLOCK_MATCHERS_STR = BLOCK_MATCHERS.map do |m|
42
- ":#{m}"
43
- end.join(' ').freeze
106
+ def on_send(node)
107
+ receiver = node.receiver
108
+ return unless receiver
44
109
 
45
- # There are aliases for the `_` method - `expect` and `value`
46
- DSL_METHODS_LIST = %w[_ value expect].map do |n|
47
- ":#{n}"
48
- end.join(' ').freeze
110
+ method = block_receiver?(receiver) || value_receiver?(receiver)
111
+ return if method == preferred_method || (method && style == :any)
112
+
113
+ register_offense(node, method)
114
+ end
49
115
 
50
- def_node_matcher :value_global_expectation?, <<~PATTERN
51
- (send !(send nil? {#{DSL_METHODS_LIST}} _) {#{VALUE_MATCHERS_STR}} ...)
116
+ private
117
+
118
+ def_node_matcher :block_receiver?, <<~PATTERN
119
+ (block (send nil? $#method_allowed?) _ _)
52
120
  PATTERN
53
121
 
54
- def_node_matcher :block_global_expectation?, <<~PATTERN
55
- (send
56
- [
57
- !(send nil? {#{DSL_METHODS_LIST}} _)
58
- !(block (send nil? {#{DSL_METHODS_LIST}}) _ _)
59
- ]
60
- {#{BLOCK_MATCHERS_STR}}
61
- _
62
- )
122
+ def_node_matcher :value_receiver?, <<~PATTERN
123
+ (send nil? $#method_allowed? _)
63
124
  PATTERN
64
125
 
65
- def on_send(node)
66
- return unless value_global_expectation?(node) || block_global_expectation?(node)
126
+ def method_allowed?(method)
127
+ DSL_METHODS.include?(method)
128
+ end
67
129
 
68
- message = format(MSG, preferred: preferred_receiver(node))
130
+ def preferred_method
131
+ style == :any ? :_ : style
132
+ end
69
133
 
70
- add_offense(node.receiver.source_range, message: message) do |corrector|
71
- receiver = node.receiver.source_range
134
+ def preferred_receiver(node)
135
+ receiver = node.receiver
72
136
 
73
- if BLOCK_MATCHERS.include?(node.method_name)
74
- corrector.wrap(receiver, '_ { ', ' }')
75
- else
76
- corrector.wrap(receiver, '_(', ')')
77
- end
137
+ if BLOCK_MATCHERS.include?(node.method_name)
138
+ body = receiver.lambda? ? receiver.body : receiver
139
+ "#{preferred_method} { #{body.source} }"
140
+ else
141
+ "#{preferred_method}(#{receiver.source})"
78
142
  end
79
143
  end
80
144
 
81
- private
145
+ def register_offense(node, method)
146
+ receiver = node.receiver
82
147
 
83
- def preferred_receiver(node)
84
- source = node.receiver.source
85
- if BLOCK_MATCHERS.include?(node.method_name)
86
- "_ { #{source} }"
148
+ if method
149
+ preferred = preferred_method
150
+ replacement = receiver.source.sub(method.to_s, preferred_method.to_s)
87
151
  else
88
- "_(#{source})"
152
+ preferred = preferred_receiver(node)
153
+ replacement = preferred
154
+ end
155
+
156
+ message = format(MSG, preferred: preferred)
157
+
158
+ add_offense(receiver, message: message) do |corrector|
159
+ corrector.replace(receiver, replacement)
89
160
  end
90
161
  end
91
162
  end
@@ -19,6 +19,18 @@ module RuboCop
19
19
  extend MinitestCopRule
20
20
 
21
21
  define_rule :refute, target_method: :empty?
22
+
23
+ remove_method :on_send
24
+ def on_send(node)
25
+ return unless node.method?(:refute)
26
+ return unless (arguments = peel_redundant_parentheses_from(node.arguments))
27
+ return unless arguments.first.respond_to?(:method?) && arguments.first.method?(:empty?)
28
+ return unless arguments.first.arguments.empty?
29
+
30
+ add_offense(node, message: offense_message(arguments)) do |corrector|
31
+ autocorrect(corrector, node, arguments)
32
+ end
33
+ end
22
34
  end
23
35
  end
24
36
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Minitest
5
5
  # This module holds the RuboCop Minitest version information.
6
6
  module Version
7
- STRING = '0.15.2'
7
+ STRING = '0.16.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
data/relnotes/v0.15.2.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ### Bug fixes
2
2
 
3
- * [#144](https://github.com/rubocop/rubocop-minitest/pull/144): Mark `Minitest/AssertEmptyLiteral` as safe auto-correction. ([@koic][])
3
+ * [#145](https://github.com/rubocop/rubocop-minitest/pull/145): Mark `Minitest/AssertEmptyLiteral` as safe auto-correction. ([@koic][])
4
4
 
5
5
  [@koic]: https://github.com/koic
@@ -0,0 +1,11 @@
1
+ ### New features
2
+
3
+ * [#147](https://github.com/rubocop/rubocop-minitest/issues/147): Add `EnforcedStyle` config parameter for `Minitest/GlobalExpectations`. ([@gi][])
4
+
5
+ ### Bug fixes
6
+
7
+ * [#142](https://github.com/rubocop/rubocop-minitest/issues/142): Fix `Minitest/GlobalExpectations` autocorrect when receiver is lambda. ([@gi][])
8
+ * [#150](https://github.com/rubocop/rubocop-minitest/issues/150): Fix a false positive for `Minitest/AssertEmpty` and `RefuteEmpty` cops when using `empty` method with any arguments. ([@koic][])
9
+
10
+ [@gi]: https://github.com/gi
11
+ [@koic]: https://github.com/koic
@@ -22,7 +22,8 @@ Gem::Specification.new do |spec|
22
22
  'changelog_uri' => 'https://github.com/rubocop/rubocop-minitest/blob/master/CHANGELOG.md',
23
23
  'source_code_uri' => 'https://github.com/rubocop/rubocop-minitest',
24
24
  'documentation_uri' => "https://docs.rubocop.org/rubocop-minitest/#{RuboCop::Minitest::Version.document_version}",
25
- 'bug_tracker_uri' => 'https://github.com/rubocop/rubocop-minitest/issues'
25
+ 'bug_tracker_uri' => 'https://github.com/rubocop/rubocop-minitest/issues',
26
+ 'rubygems_mfa_required' => 'true'
26
27
  }
27
28
 
28
29
  # Specify which files should be added to the gem when it is released.
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ autoload :Changelog, "#{__dir__}/changelog"
4
+
5
+ namespace :changelog do
6
+ %i[new fix change].each do |type|
7
+ desc "Create a Changelog entry (#{type})"
8
+ task type, [:id] do |_task, args|
9
+ ref_type = :pull if args[:id]
10
+ path = Changelog::Entry.new(type: type, ref_id: args[:id], ref_type: ref_type).write
11
+ cmd = "git add #{path}"
12
+ system cmd
13
+ puts "Entry '#{path}' created and added to git index"
14
+ end
15
+ end
16
+
17
+ desc 'Merge entries and delete them'
18
+ task :merge do
19
+ raise 'No entries!' unless Changelog.pending?
20
+
21
+ Changelog.new.merge!.and_delete!
22
+ cmd = "git commit -a -m 'Update Changelog'"
23
+ puts cmd
24
+ system cmd
25
+ end
26
+
27
+ task :check_clean do
28
+ next unless Changelog.pending?
29
+
30
+ puts '*** Pending changelog entries!'
31
+ puts 'Do `bundle exec rake changelog:merge`'
32
+ exit(1)
33
+ end
34
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ if RUBY_VERSION < '2.6'
4
+ puts 'Changelog utilities available only for Ruby 2.6+'
5
+ exit(1)
6
+ end
7
+
8
+ # Changelog utility
9
+ class Changelog
10
+ ENTRIES_PATH = 'changelog/'
11
+ FIRST_HEADER = /#{Regexp.escape("## master (unreleased)\n")}/m.freeze
12
+ ENTRIES_PATH_TEMPLATE = "#{ENTRIES_PATH}%<type>s_%<name>s.md"
13
+ TYPE_REGEXP = /#{Regexp.escape(ENTRIES_PATH)}([a-z]+)_/.freeze
14
+ TYPE_TO_HEADER = { new: 'New features', fix: 'Bug fixes', change: 'Changes' }.freeze
15
+ HEADER = /### (.*)/.freeze
16
+ PATH = 'CHANGELOG.md'
17
+ REF_URL = 'https://github.com/rubocop/rubocop-minitest'
18
+ MAX_LENGTH = 40
19
+ CONTRIBUTOR = '[@%<user>s]: https://github.com/%<user>s'
20
+ SIGNATURE = Regexp.new(format(Regexp.escape('[@%<user>s][]'), user: '([\w-]+)'))
21
+ EOF = "\n"
22
+
23
+ # New entry
24
+ Entry = Struct.new(:type, :body, :ref_type, :ref_id, :user, keyword_init: true) do
25
+ def initialize(type:, body: last_commit_title, ref_type: nil, ref_id: nil, user: github_user)
26
+ id, body = extract_id(body)
27
+ ref_id ||= id || 'x'
28
+ ref_type ||= id ? :issues : :pull
29
+ super
30
+ end
31
+
32
+ def write
33
+ Dir.mkdir(ENTRIES_PATH) unless Dir.exist?(ENTRIES_PATH)
34
+ File.write(path, content)
35
+ path
36
+ end
37
+
38
+ def path
39
+ format(ENTRIES_PATH_TEMPLATE, type: type, name: str_to_filename(body))
40
+ end
41
+
42
+ def content
43
+ period = '.' unless body.end_with? '.'
44
+ "* #{ref}: #{body}#{period} ([@#{user}][])\n"
45
+ end
46
+
47
+ def ref
48
+ "[##{ref_id}](#{REF_URL}/#{ref_type}/#{ref_id})"
49
+ end
50
+
51
+ def last_commit_title
52
+ `git log -1 --pretty=%B`.lines.first.chomp
53
+ end
54
+
55
+ def extract_id(body)
56
+ /^\[Fix(?:es)? #(\d+)\] (.*)/.match(body)&.captures || [nil, body]
57
+ end
58
+
59
+ def str_to_filename(str)
60
+ str
61
+ .downcase
62
+ .split
63
+ .each { |s| s.gsub!(/\W/, '') }
64
+ .reject(&:empty?)
65
+ .inject do |result, word|
66
+ s = "#{result}_#{word}"
67
+ return result if s.length > MAX_LENGTH
68
+
69
+ s
70
+ end
71
+ end
72
+
73
+ def github_user
74
+ user = `git config --global credential.username`.chomp
75
+ warn 'Set your username with `git config --global credential.username "myusernamehere"`' if user.empty?
76
+
77
+ user
78
+ end
79
+ end
80
+ attr_reader :header, :rest
81
+
82
+ def initialize(content: File.read(PATH), entries: Changelog.read_entries)
83
+ require 'strscan'
84
+
85
+ parse(content)
86
+ @entries = entries
87
+ end
88
+
89
+ def and_delete!
90
+ @entries.each_key { |path| File.delete(path) }
91
+ end
92
+
93
+ def merge!
94
+ File.write(PATH, merge_content)
95
+ self
96
+ end
97
+
98
+ def unreleased_content
99
+ entry_map = parse_entries(@entries)
100
+ merged_map = merge_entries(entry_map)
101
+ merged_map.flat_map { |header, things| ["### #{header}\n", *things, ''] }.join("\n")
102
+ end
103
+
104
+ def merge_content
105
+ merged_content = [@header, unreleased_content, @rest.chomp, *new_contributor_lines].join("\n")
106
+
107
+ merged_content << EOF
108
+ end
109
+
110
+ def self.pending?
111
+ entry_paths.any?
112
+ end
113
+
114
+ def self.entry_paths
115
+ Dir["#{ENTRIES_PATH}*"]
116
+ end
117
+
118
+ def self.read_entries
119
+ entry_paths.to_h { |path| [path, File.read(path)] }
120
+ end
121
+
122
+ def new_contributor_lines
123
+ contributors
124
+ .map { |user| format(CONTRIBUTOR, user: user) }
125
+ .reject { |line| @rest.include?(line) }
126
+ end
127
+
128
+ def contributors
129
+ contributors = @entries.values.flat_map do |entry|
130
+ entry.match(/\. \((?<contributors>.+)\)\n/)[:contributors].split(',')
131
+ end
132
+
133
+ contributors.join.scan(SIGNATURE).flatten
134
+ end
135
+
136
+ private
137
+
138
+ def merge_entries(entry_map)
139
+ all = @unreleased.merge(entry_map) { |_k, v1, v2| v1.concat(v2) }
140
+ canonical = TYPE_TO_HEADER.values.to_h { |v| [v, nil] }
141
+ canonical.merge(all).compact
142
+ end
143
+
144
+ def parse(content)
145
+ ss = StringScanner.new(content)
146
+ @header = ss.scan_until(FIRST_HEADER)
147
+ @unreleased = parse_release(ss.scan_until(/\n(?=## )/m))
148
+ @rest = ss.rest
149
+ end
150
+
151
+ # @return [Hash<type, Array<String>]]
152
+ def parse_release(unreleased)
153
+ unreleased.lines.map(&:chomp).reject(&:empty?).slice_before(HEADER).to_h do |header, *entries|
154
+ [HEADER.match(header)[1], entries]
155
+ end
156
+ end
157
+
158
+ def parse_entries(path_content_map)
159
+ changes = Hash.new { |h, k| h[k] = [] }
160
+ path_content_map.each do |path, content|
161
+ header = TYPE_TO_HEADER.fetch(TYPE_REGEXP.match(path)[1].to_sym)
162
+ changes[header].concat(content.lines.map(&:chomp))
163
+ end
164
+ changes
165
+ end
166
+ end
@@ -4,9 +4,8 @@ require 'bump'
4
4
 
5
5
  namespace :cut_release do
6
6
  %w[major minor patch pre].each do |release_type|
7
- desc "Cut a new #{release_type} release, create release notes " \
8
- 'and update documents.'
9
- task release_type do
7
+ desc "Cut a new #{release_type} release, create release notes and update documents."
8
+ task release_type => 'changelog:check_clean' do
10
9
  run(release_type)
11
10
  end
12
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-minitest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.2
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-10-11 00:00:00.000000000 Z
13
+ date: 2021-11-14 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -148,6 +148,7 @@ files:
148
148
  - relnotes/v0.15.0.md
149
149
  - relnotes/v0.15.1.md
150
150
  - relnotes/v0.15.2.md
151
+ - relnotes/v0.16.0.md
151
152
  - relnotes/v0.2.0.md
152
153
  - relnotes/v0.2.1.md
153
154
  - relnotes/v0.3.0.md
@@ -163,6 +164,8 @@ files:
163
164
  - relnotes/v0.8.1.md
164
165
  - relnotes/v0.9.0.md
165
166
  - rubocop-minitest.gemspec
167
+ - tasks/changelog.rake
168
+ - tasks/changelog.rb
166
169
  - tasks/cops_documentation.rake
167
170
  - tasks/cut_release.rake
168
171
  homepage:
@@ -172,8 +175,9 @@ metadata:
172
175
  homepage_uri: https://docs.rubocop.org/rubocop-minitest/
173
176
  changelog_uri: https://github.com/rubocop/rubocop-minitest/blob/master/CHANGELOG.md
174
177
  source_code_uri: https://github.com/rubocop/rubocop-minitest
175
- documentation_uri: https://docs.rubocop.org/rubocop-minitest/0.15
178
+ documentation_uri: https://docs.rubocop.org/rubocop-minitest/0.16
176
179
  bug_tracker_uri: https://github.com/rubocop/rubocop-minitest/issues
180
+ rubygems_mfa_required: 'true'
177
181
  post_install_message:
178
182
  rdoc_options: []
179
183
  require_paths: