rubocop-rspec-guide 0.2.2 → 0.4.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -6
  3. data/.yardopts +9 -0
  4. data/CHANGELOG.md +86 -0
  5. data/CONTRIBUTING.md +358 -0
  6. data/INTEGRATION_TESTING.md +324 -0
  7. data/README.md +443 -16
  8. data/Rakefile +49 -0
  9. data/benchmark/README.md +349 -0
  10. data/benchmark/baseline_v0.3.1.txt +67 -0
  11. data/benchmark/baseline_v0.4.0.txt +167 -0
  12. data/benchmark/benchmark_helper.rb +92 -0
  13. data/benchmark/compare_versions.rb +136 -0
  14. data/benchmark/cops_benchmark.rb +428 -0
  15. data/benchmark/cops_performance.rb +109 -0
  16. data/benchmark/quick_comparison.rb +58 -0
  17. data/benchmark/quick_invariant_bench.rb +52 -0
  18. data/benchmark/rspec_base_integration.rb +86 -0
  19. data/benchmark/save_baseline.rb +18 -0
  20. data/benchmark/scalability_benchmark.rb +181 -0
  21. data/config/default.yml +43 -2
  22. data/config/obsoletion.yml +6 -0
  23. data/lib/rubocop/cop/factory_bot_guide/dynamic_attribute_evaluation.rb +193 -0
  24. data/lib/rubocop/cop/factory_bot_guide/dynamic_attributes_for_time_and_random.rb +10 -106
  25. data/lib/rubocop/cop/rspec_guide/characteristics_and_contexts.rb +13 -78
  26. data/lib/rubocop/cop/rspec_guide/context_setup.rb +81 -30
  27. data/lib/rubocop/cop/rspec_guide/duplicate_before_hooks.rb +89 -22
  28. data/lib/rubocop/cop/rspec_guide/duplicate_let_values.rb +89 -22
  29. data/lib/rubocop/cop/rspec_guide/happy_path_first.rb +52 -21
  30. data/lib/rubocop/cop/rspec_guide/invariant_examples.rb +60 -19
  31. data/lib/rubocop/cop/rspec_guide/minimum_behavioral_coverage.rb +165 -0
  32. data/lib/rubocop/rspec/guide/inject.rb +26 -0
  33. data/lib/rubocop/rspec/guide/plugin.rb +45 -0
  34. data/lib/rubocop/rspec/guide/version.rb +1 -1
  35. data/lib/rubocop-rspec-guide.rb +4 -0
  36. metadata +49 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9093b8623b631c2b9ddaf15d1b72a1638c499d93e9820687dd37ec6e71f27d30
4
- data.tar.gz: '09d4eb92564fcfd55bb9d72b15eaf690a5d1e8ada0621f6f281de369a2c1c683'
3
+ metadata.gz: bcebf8b42eee358b662dc641b3e07c805897742a37cb5dd88464b6423b7ca2fa
4
+ data.tar.gz: 8b903dbd42050fbef4a51e61ed961ac6c1175ac997ca80ed4305b4cdfa529027
5
5
  SHA512:
6
- metadata.gz: beaebca72cb64609567dd9bc687961cfe193d228d1a25a50de8371a1e605f8265cc596ed2695e942b0d5f679a1f23d2581ef8a8aec176d7c26a5174c323146eb
7
- data.tar.gz: 3783cee1af2fa2d784f0ad67ce7bcf08019aab358e7d6f5c2f9d53f7b26d5bf0dbe9dd55566c0bb347e30c331ae26f0f6467d3c51972cf1071cda15cab1e8d4c
6
+ metadata.gz: 30c52761374276952c116c748486a52373b4d8f3158826d1d5ed59e2e1e3a993e460e76e1c9a267a1545cfd5fb21d985a0d947b818ad31d850d24ce2d0fa0ab5
7
+ data.tar.gz: 13833dbdb0ff5094a33e683f248fb4b1378fdb5e57d6afb40906dc78da0a843622a87fa604d3e07eee617eef7425e3fc834c26b42b0d6d2839e6b9d2b2d2afec
data/.rubocop.yml CHANGED
@@ -8,13 +8,13 @@ AllCops:
8
8
  NewCops: enable
9
9
  TargetRubyVersion: 3.0
10
10
  Exclude:
11
- - 'vendor/**/*'
12
- - 'tmp/**/*'
13
- - 'bin/**/*'
14
- - '.devbox/**/*'
11
+ - "vendor/**/*"
12
+ - "tmp/**/*"
13
+ - "bin/**/*"
14
+ - ".devbox/**/*"
15
15
 
16
16
  # Enable our custom cops
17
- RSpecGuide/CharacteristicsAndContexts:
17
+ RSpecGuide/MinimumBehavioralCoverage:
18
18
  Enabled: true
19
19
 
20
20
  RSpecGuide/HappyPathFirst:
@@ -33,5 +33,5 @@ RSpecGuide/InvariantExamples:
33
33
  Enabled: true
34
34
  MinLeafContexts: 3
35
35
 
36
- FactoryBotGuide/DynamicAttributesForTimeAndRandom:
36
+ FactoryBotGuide/DynamicAttributeEvaluation:
37
37
  Enabled: true
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --output-dir doc
2
+ --readme README.md
3
+ --markup markdown
4
+ --no-private
5
+ lib/**/*.rb
6
+ - CHANGELOG.md
7
+ - CONTRIBUTING.md
8
+ - INTEGRATION_TESTING.md
9
+ - LICENSE.txt
data/CHANGELOG.md CHANGED
@@ -1,5 +1,91 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2025-10-30
4
+
5
+ ### Added
6
+ - **Integration with RuboCop::Cop::RSpec::Base**: All 6 RSpec cops now inherit from `RuboCop::Cop::RSpec::Base`
7
+ - Leverages rubocop-rspec Language API for better RSpec DSL detection
8
+ - Native support for `let_it_be` and `let_it_be!` from rspec-rails
9
+ - More accurate detection of RSpec constructs
10
+ - **RSpec Language configuration**: Added comprehensive RSpec/Language config in `config/default.yml`
11
+ - Enables rubocop-rspec API matchers: `example_group?()`, `example?()`, `let?()`, `hook?()`
12
+ - Supports ExampleGroups, Examples, Helpers, Hooks, and Subjects
13
+ - **Configuration injection**: Created `lib/rubocop/rspec/guide/inject.rb` to automatically load gem config
14
+ - **Test helper improvements**: Added `rubocop_config_with_rspec_language` helper for consistent test setup
15
+
16
+ ### Changed
17
+ - **Removed duplicate node matchers**: Eliminated ~30 lines of code that duplicated rubocop-rspec functionality
18
+ - `example_group?()` - now uses API instead of custom matcher
19
+ - `example?()` - now uses API instead of custom matcher
20
+ - `let?()` - now uses API (also recognizes let_it_be/let_it_be!)
21
+ - `hook?()` - now uses API instead of custom matcher
22
+ - **Kept strategic custom matchers**: Retained specific matchers where needed
23
+ - `context_only?()` - for filtering only context blocks
24
+ - `let_with_name_and_value?()` - captures let name and value
25
+ - `before_hook_with_body?()` - captures hook body
26
+ - `example_with_description?()` - captures example description
27
+
28
+ ### Performance
29
+ - **Optimized for production use**: Applied two-level optimization strategy
30
+ - Fast pre-checks: Added `node.method?(:describe)` checks before API calls
31
+ - Local matchers for hot paths: InvariantExamples uses fast local matching in O(n²) loops
32
+ - **InvariantExamples**: **4.25x faster** than v0.3.1 baseline (1,504 → 6,395 i/s) 🚀
33
+ - **Other cops**: 10-25% slower than baseline, acceptable trade-off for correctness and maintainability
34
+ - **Real-world impact**: All cops remain fast enough for CI/CD pipelines (1,200-6,395 i/s)
35
+ - **Detailed analysis**: See `PERFORMANCE_REPORT.md` for comprehensive performance documentation
36
+
37
+ ### Fixed
38
+ - **ContextSetup**: Now correctly recognizes `let_it_be` and `let_it_be!` as valid context setup
39
+
40
+ ## [0.3.1] - 2025-10-30
41
+
42
+ ### Added
43
+ - **Plugin support for RuboCop 1.72+**: Added `lib/rubocop/rspec/guide/plugin.rb` for modern plugin system
44
+ - Now supports both `plugins:` (recommended for RuboCop 1.72+) and `require:` (legacy) configuration
45
+ - Fully backward compatible with older RuboCop versions
46
+ - **Version metadata in config/default.yml**: Added `VersionAdded` and `VersionChanged` fields to all cops
47
+ - Follows RuboCop conventions for tracking cop history
48
+ - Helps users understand when cops were introduced and modified
49
+
50
+ ### Changed
51
+ - **README.md**: Updated with both modern (`plugins:`) and legacy (`require:`) configuration examples
52
+ - Clear documentation for different RuboCop versions
53
+ - Migration guidance for users upgrading RuboCop
54
+
55
+ ### Fixed
56
+ - **Build process**: Added `*.gem` to `.gitignore` to prevent built gems from being committed
57
+
58
+ ## [0.3.0] - 2025-10-30
59
+
60
+ ### Added
61
+ - **RSpecGuide/MinimumBehavioralCoverage**: New cop replacing CharacteristicsAndContexts with enhanced functionality
62
+ - Now supports two patterns for behavioral variations:
63
+ 1. Traditional: 2+ sibling context blocks
64
+ 2. New: it-blocks (default behavior) + context-blocks (edge cases)
65
+ - Better reflects the goal: ensuring minimum behavioral coverage in tests
66
+ - **FactoryBotGuide/DynamicAttributeEvaluation**: New cop replacing DynamicAttributesForTimeAndRandom
67
+ - More accurate name reflecting broader scope: checks ALL method calls, not just Time/Random
68
+ - Covers Time.now, SecureRandom.hex, 1.day.from_now, Array.new, and any other method calls
69
+ - Ensures dynamic evaluation by requiring block syntax for all method-based attributes
70
+ - **config/obsoletion.yml**: Added cop obsoletion configuration for tracking renamed cops
71
+
72
+ ### Changed
73
+ - **RSpecGuide/MinimumBehavioralCoverage**: Enhanced to accept it-blocks + context-blocks pattern
74
+ - Validates that it-blocks appear before context-blocks (strict ordering)
75
+ - Allows tests with before/let setup + it-blocks + context-blocks
76
+ - Updated error messages to explain both valid patterns
77
+ - Improved documentation and examples in README.md
78
+ - Added examples for new it-blocks + context-blocks pattern
79
+ - Clarified that deprecated cop names still work as aliases
80
+
81
+ ### Deprecated
82
+ - **RSpecGuide/CharacteristicsAndContexts**: Deprecated in favor of MinimumBehavioralCoverage
83
+ - Still works as an alias for backward compatibility
84
+ - Will be removed in a future major version
85
+ - **FactoryBotGuide/DynamicAttributesForTimeAndRandom**: Deprecated in favor of DynamicAttributeEvaluation
86
+ - Still works as an alias for backward compatibility
87
+ - Will be removed in a future major version
88
+
3
89
  ## [0.2.2] - 2025-10-29
4
90
 
5
91
  ### Fixed
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,358 @@
1
+ # Contributing to RuboCop RSpec Guide
2
+
3
+ Thank you for your interest in contributing! This guide will help you get started.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Code of Conduct](#code-of-conduct)
8
+ - [Getting Started](#getting-started)
9
+ - [Development Setup](#development-setup)
10
+ - [Creating a New Cop](#creating-a-new-cop)
11
+ - [Writing Tests](#writing-tests)
12
+ - [Code Style Guidelines](#code-style-guidelines)
13
+ - [Submitting Changes](#submitting-changes)
14
+ - [Commit Message Guidelines](#commit-message-guidelines)
15
+
16
+ ## Code of Conduct
17
+
18
+ This project follows the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). By participating, you are expected to uphold this code.
19
+
20
+ ## Getting Started
21
+
22
+ 1. Fork the repository on GitHub
23
+ 2. Clone your fork locally:
24
+ ```bash
25
+ git clone https://github.com/YOUR-USERNAME/rubocop-rspec-guide.git
26
+ cd rubocop-rspec-guide
27
+ ```
28
+ 3. Add the upstream repository:
29
+ ```bash
30
+ git remote add upstream https://github.com/AlexeyMatskevich/rubocop-rspec-guide.git
31
+ ```
32
+
33
+ ## Development Setup
34
+
35
+ ### Prerequisites
36
+
37
+ - Ruby 3.0 or higher
38
+ - Bundler
39
+
40
+ ### Install Dependencies
41
+
42
+ ```bash
43
+ bundle install
44
+ ```
45
+
46
+ ### Run Tests
47
+
48
+ ```bash
49
+ bundle exec rspec
50
+ ```
51
+
52
+ All tests should pass before you start making changes.
53
+
54
+ ### Run RuboCop
55
+
56
+ ```bash
57
+ bundle exec rubocop
58
+ ```
59
+
60
+ Make sure your code follows the project's style guidelines.
61
+
62
+ ## Creating a New Cop
63
+
64
+ ### 1. Generate Cop File
65
+
66
+ Create a new file in the appropriate directory:
67
+
68
+ - For RSpec cops: `lib/rubocop/cop/rspec_guide/your_cop_name.rb`
69
+ - For FactoryBot cops: `lib/rubocop/cop/factory_bot_guide/your_cop_name.rb`
70
+
71
+ ### 2. Basic Cop Structure
72
+
73
+ ```ruby
74
+ # frozen_string_literal: true
75
+
76
+ module RuboCop
77
+ module Cop
78
+ module RSpecGuide
79
+ # Short description of what the cop checks.
80
+ #
81
+ # Longer explanation of WHY this is important and what problems
82
+ # it prevents or solves.
83
+ #
84
+ # @safety
85
+ # Describe if the cop is safe to run automatically.
86
+ #
87
+ # @example Bad code
88
+ # # bad - explain what's wrong
89
+ # describe 'Something' do
90
+ # # problematic code
91
+ # end
92
+ #
93
+ # @example Good code
94
+ # # good - explain why this is better
95
+ # describe 'Something' do
96
+ # # correct code
97
+ # end
98
+ #
99
+ # @example Edge cases
100
+ # # good - explain edge case
101
+ # describe 'Something' do
102
+ # # edge case example
103
+ # end
104
+ #
105
+ class YourCopName < Base
106
+ MSG = "Your cop's message to the user"
107
+
108
+ # @!method pattern_to_match?(node)
109
+ def_node_matcher :pattern_to_match?, <<~PATTERN
110
+ (block
111
+ (send nil? :describe ...)
112
+ ...)
113
+ PATTERN
114
+
115
+ def on_block(node)
116
+ return unless pattern_to_match?(node)
117
+
118
+ # Your cop logic here
119
+ add_offense(node) if offense_detected?(node)
120
+ end
121
+
122
+ private
123
+
124
+ def offense_detected?(node)
125
+ # Your detection logic
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ ```
132
+
133
+ ### 3. Key Components
134
+
135
+ - **MSG**: The message shown to users when an offense is detected
136
+ - **Node Matchers**: Use `def_node_matcher` to match AST patterns
137
+ - **Callbacks**: Implement `on_block`, `on_send`, etc. to inspect nodes
138
+ - **YARD Documentation**: Add comprehensive examples and explanations
139
+
140
+ ### 4. Register Your Cop
141
+
142
+ Add your cop to `lib/rubocop-rspec-guide.rb`:
143
+
144
+ ```ruby
145
+ require_relative "rubocop/cop/rspec_guide/your_cop_name"
146
+ ```
147
+
148
+ ### 5. Add Default Configuration
149
+
150
+ Add your cop to `config/default.yml`:
151
+
152
+ ```yaml
153
+ RSpecGuide/YourCopName:
154
+ Description: "Short description of your cop"
155
+ Enabled: true
156
+ VersionAdded: 'X.Y.Z'
157
+ StyleGuideUrl: "https://github.com/AlexeyMatskevich/rspec-guide"
158
+ ```
159
+
160
+ ## Writing Tests
161
+
162
+ ### Test File Location
163
+
164
+ Create a test file in `spec/rubocop/cop/`:
165
+
166
+ - For RSpec cops: `spec/rubocop/cop/rspec_guide/your_cop_name_spec.rb`
167
+ - For FactoryBot cops: `spec/rubocop/cop/factory_bot_guide/your_cop_name_spec.rb`
168
+
169
+ ### Test Structure
170
+
171
+ ```ruby
172
+ # frozen_string_literal: true
173
+
174
+ RSpec.describe RuboCop::Cop::RSpecGuide::YourCopName, :config do
175
+ let(:config) { RuboCop::Config.new }
176
+
177
+ context 'when code has offense' do
178
+ it 'registers an offense' do
179
+ expect_offense(<<~RUBY)
180
+ describe 'Something' do
181
+ ^^^^^^^^^^^^^^^^^^^^^^^ Your cop's message
182
+ # problematic code
183
+ end
184
+ RUBY
185
+ end
186
+ end
187
+
188
+ context 'when code is correct' do
189
+ it 'does not register an offense' do
190
+ expect_no_offenses(<<~RUBY)
191
+ describe 'Something' do
192
+ # correct code
193
+ end
194
+ RUBY
195
+ end
196
+ end
197
+
198
+ context 'with edge case' do
199
+ it 'does not register an offense' do
200
+ expect_no_offenses(<<~RUBY)
201
+ describe 'Something' do
202
+ # edge case code
203
+ end
204
+ RUBY
205
+ end
206
+ end
207
+ end
208
+ ```
209
+
210
+ ### Testing Best Practices
211
+
212
+ 1. **Test both offenses and non-offenses**: Ensure your cop correctly identifies problems AND doesn't create false positives
213
+ 2. **Test edge cases**: Cover boundary conditions and unusual patterns
214
+ 3. **Test with configuration options**: If your cop has configuration options, test different settings
215
+ 4. **Use descriptive context names**: Make it clear what scenario each test covers
216
+ 5. **Follow the behavior-first pattern**: Describe WHAT behavior is being tested, not implementation details
217
+
218
+ ### Running Specific Tests
219
+
220
+ ```bash
221
+ # Run all tests
222
+ bundle exec rspec
223
+
224
+ # Run tests for a specific cop
225
+ bundle exec rspec spec/rubocop/cop/rspec_guide/your_cop_name_spec.rb
226
+
227
+ # Run a specific test
228
+ bundle exec rspec spec/rubocop/cop/rspec_guide/your_cop_name_spec.rb:10
229
+ ```
230
+
231
+ ## Code Style Guidelines
232
+
233
+ This project follows standard RuboCop style guidelines:
234
+
235
+ 1. **Use 2 spaces for indentation** (no tabs)
236
+ 2. **Keep lines under 120 characters**
237
+ 3. **Use frozen string literals** (`# frozen_string_literal: true`)
238
+ 4. **Write descriptive variable names**
239
+ 5. **Add YARD documentation** for public methods and classes
240
+ 6. **Follow RuboCop's own style guide**
241
+
242
+ Run `bundle exec rubocop` to check your code style.
243
+
244
+ ## Submitting Changes
245
+
246
+ ### Before Submitting
247
+
248
+ 1. **Run all tests**: `bundle exec rspec`
249
+ 2. **Run RuboCop**: `bundle exec rubocop`
250
+ 3. **Update CHANGELOG.md**: Add a note about your change under `[Unreleased]`
251
+ 4. **Update documentation**: If you added a new cop, update README.md
252
+
253
+ ### Pull Request Process
254
+
255
+ 1. **Create a feature branch**:
256
+ ```bash
257
+ git checkout -b feature/your-feature-name
258
+ ```
259
+
260
+ 2. **Make your changes and commit**:
261
+ ```bash
262
+ git add .
263
+ git commit -m "Add YourCopName to detect X pattern"
264
+ ```
265
+
266
+ 3. **Push to your fork**:
267
+ ```bash
268
+ git push origin feature/your-feature-name
269
+ ```
270
+
271
+ 4. **Open a Pull Request** on GitHub
272
+
273
+ 5. **PR Checklist**:
274
+ - [ ] Tests pass (`bundle exec rspec`)
275
+ - [ ] RuboCop passes (`bundle exec rubocop`)
276
+ - [ ] New cop has comprehensive YARD documentation (3+ examples)
277
+ - [ ] New cop has tests covering offenses, non-offenses, and edge cases
278
+ - [ ] CHANGELOG.md updated
279
+ - [ ] README.md updated (if adding new cop)
280
+ - [ ] config/default.yml updated (if adding new cop)
281
+
282
+ ### PR Title Format
283
+
284
+ - **For new cops**: `Add RSpecGuide/YourCopName cop`
285
+ - **For bug fixes**: `Fix RSpecGuide/YourCopName false positive on X`
286
+ - **For improvements**: `Improve RSpecGuide/YourCopName to handle Y`
287
+ - **For documentation**: `Update documentation for RSpecGuide/YourCopName`
288
+
289
+ ## Commit Message Guidelines
290
+
291
+ Follow these conventions for commit messages:
292
+
293
+ ### Format
294
+
295
+ ```
296
+ Short summary (50 chars or less)
297
+
298
+ Detailed explanation if needed. Wrap at 72 characters.
299
+ Explain WHAT changed and WHY, not HOW (code shows how).
300
+
301
+ - Bullet points are okay
302
+ - Use present tense: "Add feature" not "Added feature"
303
+ - Reference issues: "Fixes #123" or "Closes #456"
304
+ ```
305
+
306
+ ### Examples
307
+
308
+ **Good commit messages:**
309
+
310
+ ```
311
+ Add MinimumBehavioralCoverage cop
312
+
313
+ Checks that describe blocks test at least 2 behavioral variations.
314
+ Supports both traditional (2+ contexts) and new (it-blocks + contexts)
315
+ patterns.
316
+
317
+ Closes #42
318
+ ```
319
+
320
+ ```
321
+ Fix ContextSetup false positive on nested describes
322
+
323
+ The cop was incorrectly flagging contexts inside nested describes
324
+ when setup was present in the parent describe block.
325
+
326
+ Fixes #89
327
+ ```
328
+
329
+ **Bad commit messages:**
330
+
331
+ ```
332
+ fix bug
333
+ ```
334
+
335
+ ```
336
+ updated code
337
+ ```
338
+
339
+ ```
340
+ WIP - will finish later
341
+ ```
342
+
343
+ ## Questions?
344
+
345
+ If you have questions or need help:
346
+
347
+ 1. Check existing [Issues](https://github.com/AlexeyMatskevich/rubocop-rspec-guide/issues)
348
+ 2. Check existing [Pull Requests](https://github.com/AlexeyMatskevich/rubocop-rspec-guide/pulls)
349
+ 3. Open a new issue with the `question` label
350
+
351
+ ## Resources
352
+
353
+ - [RuboCop Development Guide](https://docs.rubocop.org/rubocop/development.html)
354
+ - [RuboCop AST Documentation](https://docs.rubocop.org/rubocop-ast/)
355
+ - [RSpec Style Guide](https://github.com/AlexeyMatskevich/rspec-guide)
356
+ - [Parser AST Explorer](https://ruby-ast-explorer.herokuapp.com/) - Visualize Ruby AST
357
+
358
+ Thank you for contributing! 🎉