diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 5bfb6ba55..bc91901da 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,7 @@ # .git-blame-ignore-revs # Add mdformat to the workflow in GitHub Actions 4874a5a4a2a58e76d343aaa02279cd93b16f5a30 +# Move node patterns into private scope +089491fb1fa173145f8e6eb1b511d4a7a1bf28ff +# Don't always define node patterns in private scope +ce09cb2b25b9f3778bf032d0caaa862da6635b54 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..e356c939e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,48 @@ +--- +name: Bug Report +about: Report an issue with RuboCop RSpec you've discovered. +--- + +*Be clear, concise and precise in your description of the problem. +Open an issue with a descriptive title and a summary in grammatically correct, +complete sentences.* + +*Use the template below when reporting bugs. Please, make sure that +you're running the latest stable RuboCop RSpec and that the problem you're reporting +hasn't been reported (and potentially fixed) already.* + +*Before filing the ticket you should replace all text above the horizontal +rule with your own words.* + +*In the case of false positive or false negative, please add the +corresponding cop name.* + +______________________________________________________________________ + +## Expected behavior + +Describe here how you expected RuboCop RSpec to behave in this particular situation. + +## Actual behavior + +Describe here what actually happened. +Please use `rubocop --debug` when pasting rubocop output as it contains additional information. + +## Steps to reproduce the problem + +This is extremely important! Providing us with a reliable way to reproduce +a problem will expedite its solution. + +## RuboCop RSpec version + +Include the output of `rubocop -V` or `bundle exec rubocop -V` if using Bundler. +If you see extension cop versions (e.g. `rubocop-performance`, `rubocop-rake`, and others) +output by `rubocop -V`, include them as well. Here's an example: + +```shell +$ [bundle exec] rubocop -V +1.67.0 (using Parser 3.3.5.0, rubocop-ast 1.32.3, analyzing as Ruby 2.7, running on ruby 3.4.0) [arm64-darwin23] + - rubocop-performance 1.22.1 + - rubocop-rake 0.6.0 + - rubocop-rspec 3.1.0 +``` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..5c8eefd93 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest new RuboCop RSpec features or improvements to existing features. +--- + +## Is your feature request related to a problem? Please describe. + +A clear and concise description of what the problem is. Ex. I'm always frustrated when \[...\] + +## Describe the solution you'd like + +A clear and concise description of what you want to happen. + +## Describe alternatives you've considered + +A clear and concise description of any alternative solutions or features you've considered. + +## Additional context + +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml deleted file mode 100644 index 79bff9970..000000000 --- a/.github/workflows/danger.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Danger -on: - - pull_request -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true -jobs: - danger: - name: Danger - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - ruby-version: "3.3" - - name: Run Danger - run: bundle exec danger - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 486665e0d..785a677df 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -69,11 +69,34 @@ jobs: - uses: actions/checkout@v4 - name: Use latest RuboCop from `master` run: | - echo "gem 'rubocop', github: 'rubocop-hq/rubocop'" > Gemfile.local + echo "gem 'rubocop', github: 'rubocop/rubocop'" > Gemfile.local + cat Gemfile.local - uses: ruby/setup-ruby@v1 with: ruby-version: "3.3" bundler-cache: true + - run: bundle exec rubocop -V + - run: NO_COVERAGE=true bundle exec rake ${{ matrix.task }} + + oldest-rubocop: + runs-on: ubuntu-latest + strategy: + matrix: + task: + - spec + name: "Oldest RuboCop: ${{ matrix.task }}" + steps: + - uses: actions/checkout@v4 + - name: Use oldest RuboCop allowed by gemspec + run: | + sed -nr "s/ *spec.add_dependency 'rubocop', '~> ([0-9\.]+)'/gem 'rubocop', '= \1'/p" \ + rubocop-rspec.gemspec > Gemfile.local + cat Gemfile.local + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + - run: bundle exec rubocop -V - run: NO_COVERAGE=true bundle exec rake ${{ matrix.task }} rspec4: @@ -85,7 +108,7 @@ jobs: run: | sed -e '/rspec/d' -i Gemfile cat << EOF > Gemfile.local - gem 'rspec', github: 'rspec/rspec', branch: '4-0-dev' + gem 'rspec', github: 'rspec/rspec-metagem', branch: '4-0-dev' gem 'rspec-core', github: 'rspec/rspec-core', branch: '4-0-dev' gem 'rspec-expectations', github: 'rspec/rspec-expectations', branch: '4-0-dev' gem 'rspec-mocks', github: 'rspec/rspec-mocks', branch: '4-0-dev' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..de3c2d7ae --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,44 @@ +name: Publish +on: + push: + branches: master + paths: lib/rubocop/rspec/version.rb +jobs: + publish: + name: Publish to RubyGems + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ruby + - uses: rubygems/release-gem@v1 + - name: Create a GitHub release + env: + GH_TOKEN: ${{ github.token }} + run: | + bundle exec rake create_release_notes + gh release create $(git tag --points-at @) \ + --title "RuboCop RSpec $(git tag --points-at @)" \ + --notes-file relnotes.md + - name: Replace version in Antora config + env: + GH_TOKEN: ${{ github.token }} + run: | + sed -i 's/version:.*$/version: ~/' docs/antora.yml + if ! git diff --exit-code docs/antora.yml; then + branch=switch-docs-version-$(git tag --points-at @) + git config user.name "${GITHUB_ACTOR}" + git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" + git checkout -b "$branch" + git add docs/antora.yml + git commit -m "Switch docs version back" + git push -u origin "$branch" + gh pr create --fill --head "$branch" + fi diff --git a/.gitignore b/.gitignore index f7286ff5b..9669bf146 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,8 @@ pkg # simplecov generated coverage + +# vscode generated +.vscode + +/relnotes.md diff --git a/.rubocop.yml b/.rubocop.yml index 8ae0a9f6e..6fb427aef 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -47,6 +47,11 @@ Lint/InterpolationCheck: Lint/RedundantCopDisableDirective: Enabled: false +Lint/UselessAccessModifier: + MethodCreatingMethods: + - def_node_matcher + - def_node_search + Metrics/BlockLength: Exclude: - rubocop-rspec.gemspec @@ -85,6 +90,9 @@ Naming/InclusiveLanguage: violation: Suggestions: - offense + 'register no offense': + Suggestions: + - registers no offense RSpec: Language: @@ -93,101 +101,157 @@ RSpec: - expect_no_offenses - expect_offense +RSpec/DescribeClass: + Exclude: + - spec/project/**/*.rb + RSpec/ExampleLength: CountAsOne: - heredoc Max: 11 -RSpec/FilePath: - Enabled: false - -RSpec/DescribeClass: - Exclude: - - spec/project/**/*.rb - RSpec/MultipleExpectations: Max: 2 +RSpec/SpecFilePathFormat: + Exclude: + - spec/rubocop/cop/rspec/mixin/**/*.rb + +# `expect_offense` does not use Kernel#format or String#% Style/FormatStringToken: Exclude: - spec/rubocop/**/*.rb -Style/RequireOrder: +Style/NumberedParameters: Enabled: true + EnforcedStyle: disallow -RSpec/SpecFilePathFormat: +Style/RequireOrder: Enabled: true - Exclude: - - spec/rubocop/cop/rspec/mixin/**/*.rb -# Enable some of RuboCop's pending cops. +# Enable RuboCop's pending cops up to v1.63 -Layout/LineContinuationSpacing: - Enabled: true -Layout/LineEndStringConcatenationIndentation: - Enabled: true -Lint/AmbiguousOperatorPrecedence: - Enabled: true -Lint/NonAtomicFileOperation: - Enabled: true -Style/EmptyHeredoc: - Enabled: true -Style/RedundantHeredocDelimiterQuotes: - Enabled: true -Style/RedundantStringEscape: - Enabled: true -Style/ReturnNilInPredicateMethodDefinition: - Enabled: true +Gemspec/DeprecatedAttributeAssignment: {Enabled: true} +Gemspec/DevelopmentDependencies: {Enabled: true} +Gemspec/RequireMFA: {Enabled: true} +Layout/LineContinuationLeadingSpace: {Enabled: true} +Layout/LineContinuationSpacing: {Enabled: true} +Layout/LineEndStringConcatenationIndentation: {Enabled: true} +Layout/SpaceBeforeBrackets: {Enabled: true} +Lint/AmbiguousAssignment: {Enabled: true} +Lint/AmbiguousOperatorPrecedence: {Enabled: true} +Lint/AmbiguousRange: {Enabled: true} +Lint/ConstantOverwrittenInRescue: {Enabled: true} +Lint/DeprecatedConstants: {Enabled: true} +Lint/DuplicateBranch: {Enabled: true} +Lint/DuplicateMagicComment: {Enabled: true} +Lint/DuplicateMatchPattern: {Enabled: true} +Lint/DuplicateRegexpCharacterClassElement: {Enabled: true} +Lint/EmptyBlock: {Enabled: true} +Lint/EmptyClass: {Enabled: true} +Lint/EmptyInPattern: {Enabled: true} +Lint/IncompatibleIoSelectWithFiberScheduler: {Enabled: true} +Lint/ItWithoutArgumentsInBlock: {Enabled: true} +Lint/LambdaWithoutLiteralBlock: {Enabled: true} +Lint/LiteralAssignmentInCondition: {Enabled: true} +Lint/MixedCaseRange: {Enabled: true} +Lint/NonAtomicFileOperation: {Enabled: true} +Lint/NoReturnInBeginEndBlocks: {Enabled: true} +Lint/NumberedParameterAssignment: {Enabled: true} +Lint/OrAssignmentToConstant: {Enabled: true} +Lint/RedundantDirGlobSort: {Enabled: true} +Lint/RedundantRegexpQuantifiers: {Enabled: true} +Lint/RefinementImportMethods: {Enabled: true} +Lint/RequireRangeParentheses: {Enabled: true} +Lint/RequireRelativeSelfPath: {Enabled: true} +Lint/SymbolConversion: {Enabled: true} +Lint/ToEnumArguments: {Enabled: true} +Lint/TripleQuotes: {Enabled: true} +Lint/UnexpectedBlockArity: {Enabled: true} +Lint/UnmodifiedReduceAccumulator: {Enabled: true} +Lint/UselessRescue: {Enabled: true} +Lint/UselessRuby2Keywords: {Enabled: true} +Metrics/CollectionLiteralLength: {Enabled: true} +Naming/BlockForwarding: {Enabled: true} +Performance/AncestorsInclude: {Enabled: true} +Performance/BigDecimalWithNumericArgument: {Enabled: true} +Performance/BlockGivenWithExplicitBlock: {Enabled: true} +Performance/CollectionLiteralInLoop: {Enabled: true} +Performance/ConcurrentMonotonicTime: {Enabled: true} +Performance/ConstantRegexp: {Enabled: true} +Performance/MapCompact: {Enabled: true} +Performance/MapMethodChain: {Enabled: true} +Performance/MethodObjectAsBlock: {Enabled: true} +Performance/RedundantEqualityComparisonBlock: {Enabled: true} +Performance/RedundantSortBlock: {Enabled: true} +Performance/RedundantSplitRegexpArgument: {Enabled: true} +Performance/RedundantStringChars: {Enabled: true} +Performance/ReverseFirst: {Enabled: true} +Performance/SortReverse: {Enabled: true} +Performance/Squeeze: {Enabled: true} +Performance/StringIdentifierArgument: {Enabled: true} +Performance/StringInclude: {Enabled: true} +Performance/Sum: {Enabled: true} +Security/CompoundHash: {Enabled: true} +Security/IoMethods: {Enabled: true} +Style/ArgumentsForwarding: {Enabled: true} +Style/ArrayIntersect: {Enabled: true} +Style/CollectionCompact: {Enabled: true} +Style/ComparableClamp: {Enabled: true} +Style/ConcatArrayLiterals: {Enabled: true} +Style/DataInheritance: {Enabled: true} +Style/DirEmpty: {Enabled: true} +Style/DocumentDynamicEvalDefinition: {Enabled: true} +Style/EmptyHeredoc: {Enabled: true} +Style/EndlessMethod: {Enabled: true} +Style/EnvHome: {Enabled: true} +Style/ExactRegexpMatch: {Enabled: true} +Style/FetchEnvVar: {Enabled: true} +Style/FileEmpty: {Enabled: true} +Style/FileRead: {Enabled: true} +Style/FileWrite: {Enabled: true} +Style/HashConversion: {Enabled: true} +Style/HashExcept: {Enabled: true} +Style/IfWithBooleanLiteralBranches: {Enabled: true} +Style/InPatternThen: {Enabled: true} +Style/MagicCommentFormat: {Enabled: true} +Style/MapCompactWithConditionalBlock: {Enabled: true} +Style/MapIntoArray: {Enabled: true} +Style/MapToHash: {Enabled: true} +Style/MapToSet: {Enabled: true} +Style/MinMaxComparison: {Enabled: true} +Style/MultilineInPatternThen: {Enabled: true} +Style/NegatedIfElseCondition: {Enabled: true} +Style/NestedFileDirname: {Enabled: true} +Style/NilLambda: {Enabled: true} +Style/NumberedParametersLimit: {Enabled: true} +Style/ObjectThen: {Enabled: true} +Style/OpenStructUse: {Enabled: true} +Style/OperatorMethodCall: {Enabled: true} +Style/QuotedSymbols: {Enabled: true} +Style/RedundantArgument: {Enabled: true} +Style/RedundantArrayConstructor: {Enabled: true} +Style/RedundantConstantBase: {Enabled: true} +Style/RedundantCurrentDirectoryInPath: {Enabled: true} +Style/RedundantDoubleSplatHashBraces: {Enabled: true} +Style/RedundantEach: {Enabled: true} +Style/RedundantFilterChain: {Enabled: true} +Style/RedundantHeredocDelimiterQuotes: {Enabled: true} +Style/RedundantInitialize: {Enabled: true} +Style/RedundantLineContinuation: {Enabled: true} +Style/RedundantRegexpArgument: {Enabled: true} +Style/RedundantRegexpConstructor: {Enabled: true} +Style/RedundantSelfAssignmentBranch: {Enabled: true} +Style/RedundantStringEscape: {Enabled: true} +Style/ReturnNilInPredicateMethodDefinition: {Enabled: true} +Style/SelectByRegexp: {Enabled: true} +Style/SingleLineDoEndBlock: {Enabled: true} +Style/StringChars: {Enabled: true} +Style/SuperWithArgsParentheses: {Enabled: true} +Style/SwapValues: {Enabled: true} +Style/YAMLFileRead: {Enabled: true} # Enable our own pending cops. - -RSpec/BeEmpty: - Enabled: true -RSpec/BeEq: - Enabled: true -RSpec/BeNil: - Enabled: true -RSpec/ChangeByZero: - Enabled: true -RSpec/ClassCheck: - Enabled: true -RSpec/ContainExactly: - Enabled: true -RSpec/DuplicatedMetadata: - Enabled: true -RSpec/EmptyMetadata: - Enabled: true -RSpec/Eq: - Enabled: true -RSpec/ExcessiveDocstringSpacing: - Enabled: true -RSpec/IdenticalEqualityAssertion: - Enabled: true -RSpec/IndexedLet: - Enabled: true -RSpec/MatchArray: - Enabled: true -RSpec/MetadataStyle: - Enabled: true -RSpec/NoExpectationExample: - Enabled: true -RSpec/PendingWithoutReason: - Enabled: true -RSpec/ReceiveMessages: - Enabled: true -RSpec/RedundantAround: - Enabled: true -RSpec/RedundantPredicateMatcher: - Enabled: true -RSpec/RemoveConst: - Enabled: true -RSpec/SkipBlockInsideExample: - Enabled: true -RSpec/SortMetadata: - Enabled: true -RSpec/SpecFilePathSuffix: - Enabled: true -RSpec/SubjectDeclaration: - Enabled: true -RSpec/VerifiedDoubleReference: +# +RSpec/StringAsInstanceDoubleConstant: Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9cb073ce1..eef920c77 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,11 +1,15 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.36.0. +# using RuboCop version 1.63.4. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. +Lint/ToEnumArguments: + Exclude: + - 'lib/rubocop/cop/rspec/multiple_expectations.rb' + Rake/MethodDefinitionInTask: Exclude: - 'tasks/cut_release.rake' diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ae5337ea..73cb05892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,99 @@ ## Master (Unreleased) +## 3.2.0 (2024-10-26) + +- Fix `RSpec/VoidExpect` to only operate inside an example block. ([@corsonknowles]) +- Change `RSpec/ContextWording` cop to always report an offense when both `Prefixes` and `AllowedPatterns` are empty. ([@ydah]) +- Add support for `and` and `or` compound matchers to `RSpec/ChangeByZero` cop. ([@ydah]) + +## 3.1.0 (2024-10-01) + +- Add `RSpec/StringAsInstanceDoubleConstant` to check for and correct strings used as instance_doubles. ([@corsonknowles]) +- Fix false-positive for `RSpec/UnspecifiedException` when a method is literally named `raise_exception`. ([@aarestad]) +- Fix false-positive for `RSpec/UnspecifiedException` when `not_to raise_error` is used within a block. ([@aarestad], [@G-Rath]) + +## 3.0.5 (2024-09-07) + +- Fix false-negative and error for `RSpec/MetadataStyle` when non-literal args are used in metadata in `EnforceStyle: hash`. ([@cbliard]) +- Improve offense message for `RSpec/IndexedLet`. ([@earlopain]) + +## 3.0.4 (2024-08-05) + +- Fix false-negative for `UnspecifiedException` when matcher is chained. ([@r7kamura]) + +## 3.0.3 (2024-07-12) + +- Add support for Unicode RIGHT SINGLE QUOTATION MARK in `RSpec/ExampleWording`. ([@jdufresne]) +- Suppress deprecation warning for `RSpec/MultipleExpectations`, `RSpec/MultipleMemoizedHelpers`, and `RSpec/NestedGroups` cops. ([@koic]) + +## 3.0.2 (2024-07-02) + +- Fix wrong autocorrect for `RSpec/ScatteredSetup` when hook contains heredoc. ([@earlopain]) +- Fix false negative for `RSpec/PredicateMatcher` when expectation contains custom failure message. ([@earlopain]) +- Facilitate the 3.0 upgrade flow with proper extracted cop messages. ([@jeppester]) + +## 3.0.1 (2024-06-11) + +- Bump RuboCop requirement to +1.61. ([@ydah]) + +## 3.0.0 (2024-06-11) + +- Remove extracted cops in `Capybara`, `FactoryBot` and `Rails` departments. ([@ydah]) +- Remove `RuboCop::RSpec::Language::NodePattern`. ([@ydah]) +- Remove `RSpec/FilePath` cop. ([@ydah]) +- Remove `RSpec/Capybara/FeatureMethods` cop. If you are using this cop, change it to use `RSpec/Dialect`. ([@ydah]) +- Add new `RSpec/MissingExpectationTargetMethod` cop. ([@krororo]) +- Fix an error for `RSpec/ScatteredSetup` when one of the hooks is an empty block. ([@earlopain]) + +These previously pending cops are now enabled by default: `RSpec/BeEmpty`, `RSpec/BeEq`, `RSpec/BeNil`, `RSpec/ChangeByZero`, `RSpec/ClassCheck`, `RSpec/ContainExactly`, `RSpec/DuplicatedMetadata`, `RSpec/EmptyMetadata`, `RSpec/EmptyOutput`, `RSpec/Eq`, `RSpec/ExcessiveDocstringSpacing`, `RSpec/ExpectInLet`, `RSpec/IdenticalEqualityAssertion`, `RSpec/IndexedLet`, `RSpec/IsExpectedSpecify`, `RSpec/MatchArray`, `RSpec/MetadataStyle`, `RSpec/NoExpectationExample`, `RSpec/PendingWithoutReason`, `RSpec/ReceiveMessages`, `RSpec/RedundantAround`, `RSpec/RedundantPredicateMatcher`, `RSpec/RemoveConst`, `RSpec/RepeatedSubjectCall`, `RSpec/SkipBlockInsideExample`, `RSpec/SortMetadata`, `RSpec/SpecFilePathFormat`, `RSpec/SpecFilePathSuffix`, `RSpec/SubjectDeclaration`, `RSpec/UndescriptiveLiteralsDescription`, and `RSpec/VerifiedDoubleReference`. + +Read more about how to upgrade in https://docs.rubocop.org/rubocop-rspec/upgrade_to_version_3.html + +## 2.31.0 (2024-06-07) + +- Support `AutoCorrect: contextual` option for LSP. ([@ydah]) + +## 2.30.0 (2024-06-03) + +- Add new `RSpec/ExpectInLet` cop. ([@yasu551]) + +## 2.29.2 (2024-05-02) + +- Fix beginless and endless range bug for RepeatedIncludeExample cop. ([@hasghari]) +- Fix a false positive for `RSpec/RepeatedSubjectCall` when subject is used as argument to function call. ([@K-S-A]) + +## 2.29.1 (2024-04-05) + +- Fix an error in the default configuration. ([@ydah]) + +## 2.29.0 (2024-04-04) + +- Fix an autocorrect error for `RSpec/ExpectActual`. ([@bquorning]) +- Add new `RSpec/UndescriptiveLiteralsDescription` cop. ([@ydah]) +- Add new `RSpec/EmptyOutput` cop. ([@bquorning]) + +## 2.28.0 (2024-03-30) + +- Extract RSpec Rails cops to a separate repository, [`rubocop-rspec_rails`](https://github.com/rubocop/rubocop-rspec_rails). The `rubocop-rspec_rails` repository is a dependency of `rubocop-rspec` and the cops related to rspec-rails are aliased (`RSpec/Rails/Foo` == `RSpecRails/Foo`) until v3.0 is released, so the change will be invisible to users until then. ([@ydah]) + +## 2.27.1 (2024-03-03) + +- Fix a false positive for `RSpec/RepeatedSubjectCall` when `subject.method_call`. ([@ydah]) +- Add configuration option `OnlyStaticConstants` to `RSpec/DescribedClass`. ([@ydah]) + +## 2.27.0 (2024-03-01) + +- Add new `RSpec/IsExpectedSpecify` cop. ([@ydah]) +- Add new `RSpec/RepeatedSubjectCall` cop. ([@drcapulet]) +- Add support for `assert_true`, `assert_false`, `assert_not_equal`, `assert_not_nil`, `*_empty`, `*_predicate`, `*_kind_of`, `*_in_delta`, `*_match`, `*_instance_of` and `*_includes` assertions in `RSpec/Rails/MinitestAssertions`. ([@ydah], [@G-Rath]) +- Support asserts with messages in `Rspec/BeEmpty`. ([@G-Rath]) +- Fix a false positive for `RSpec/ExpectActual` when used with rspec-rails routing matchers. ([@naveg]) +- Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah]) +- Fix a false negative for `RSpec/DescribedClass` when class with constant. ([@ydah]) +- Fix a false positive for `RSpec/ExampleWithoutDescription` when `specify` with multi-line block and missing description. ([@ydah]) +- Fix an incorrect autocorrect for `RSpec/ChangeByZero` when compound expectations with line break before `.by(0)`. ([@ydah]) + ## 2.26.1 (2024-01-05) - Fix an error for `RSpec/SharedExamples` when using examples without argument. ([@ydah]) @@ -818,6 +911,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. +[@aarestad]: https://github.com/aarestad [@abrom]: https://github.com/abrom [@ahukkanen]: https://github.com/ahukkanen [@akiomik]: https://github.com/akiomik @@ -835,9 +929,11 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@bquorning]: https://github.com/bquorning [@brentwheeldon]: https://github.com/BrentWheeldon [@brianhawley]: https://github.com/BrianHawley +[@cbliard]: https://github.com/cbliard [@cfabianski]: https://github.com/cfabianski [@clupprich]: https://github.com/clupprich [@composerinteralia]: https://github.com/composerinteralia +[@corsonknowles]: https://github.com/corsonknowles [@corydiamand]: https://github.com/corydiamand [@darhazer]: https://github.com/Darhazer [@daveworth]: https://github.com/daveworth @@ -845,9 +941,11 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@deivid-rodriguez]: https://github.com/deivid-rodriguez [@dgollahon]: https://github.com/dgollahon [@dmitrytsepelev]: https://github.com/dmitrytsepelev +[@drcapulet]: https://github.com/drcapulet [@drowze]: https://github.com/Drowze [@dswij]: https://github.com/dswij [@dvandersluis]: https://github.com/dvandersluis +[@earlopain]: https://github.com/earlopain [@edgibbs]: https://github.com/edgibbs [@eikes]: https://github.com/eikes [@eitoball]: https://github.com/eitoball @@ -863,20 +961,24 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@gsamokovarov]: https://github.com/gsamokovarov [@harry-graham]: https://github.com/harry-graham [@harrylewis]: https://github.com/harrylewis +[@hasghari]: https://github.com/hasghari [@hosamaly]: https://github.com/hosamaly [@ignaciovillaverde]: https://github.com/ignaciovillaverde [@jaredbeck]: https://github.com/jaredbeck [@jaredmoody]: https://github.com/jaredmoody [@jdufresne]: https://github.com/jdufresne [@jeffreyc]: https://github.com/jeffreyc +[@jeppester]: https://github.com/jeppester [@jessieay]: https://github.com/jessieay [@jfragoulis]: https://github.com/jfragoulis [@johnny-miyake]: https://github.com/johnny-miyake [@jojos003]: https://github.com/jojos003 [@jonatas]: https://github.com/jonatas [@jtannas]: https://github.com/jtannas +[@k-s-a]: https://github.com/K-S-A [@kellysutton]: https://github.com/kellysutton [@koic]: https://github.com/koic +[@krororo]: https://github.com/krororo [@kuahyeow]: https://github.com/kuahyeow [@lazycoder9]: https://github.com/lazycoder9 [@leoarnold]: https://github.com/leoarnold @@ -892,6 +994,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@mockdeep]: https://github.com/mockdeep [@mothonmars]: https://github.com/MothOnMars [@mvz]: https://github.com/mvz +[@naveg]: https://github.com/naveg [@nc-holodakg]: https://github.com/nc-holodakg [@nevir]: https://github.com/nevir [@ngouy]: https://github.com/ngouy @@ -936,6 +1039,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@twalpole]: https://github.com/twalpole [@vzvu3k6k]: https://github.com/vzvu3k6k [@walf443]: https://github.com/walf443 +[@yasu551]: https://github.com/yasu551 [@ybiquitous]: https://github.com/ybiquitous [@ydah]: https://github.com/ydah [@yevhene]: https://github.com/yevhene diff --git a/Dangerfile b/Dangerfile deleted file mode 100644 index 1e2572354..000000000 --- a/Dangerfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -diff = git.diff_for_file('config/default.yml') -if diff && diff.patch =~ /^\+\s*Enabled: true$/ - warn(<<~MESSAGE) - There is a cop that became `Enabled: true` due to this pull request. - Please review the diff and make sure there are no issues. - MESSAGE -end diff --git a/Gemfile b/Gemfile index 04687217d..f6fddf529 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,6 @@ source 'https://rubygems.org' gemspec gem 'bump' -gem 'danger' gem 'rack' gem 'rake' gem 'rspec', '~> 3.11' diff --git a/README.md b/README.md index a29f7f243..61b5ff355 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,15 @@ [RSpec](https://rspec.info/)-specific analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop/rubocop). +- [Installation](#installation) + - [Upgrading to RuboCop RSpec v3.x](#upgrading-to-rubocop-rspec-v3x) + - [Upgrading to RuboCop RSpec v2.x](#upgrading-to-rubocop-rspec-v2x) +- [Usage](#usage) +- [Documentation](#documentation) +- [The Cops](#the-cops) +- [Contributing](#contributing) +- [License](#license) + ## Installation Just install the `rubocop-rspec` gem @@ -21,6 +30,10 @@ or if you use bundler put this in your `Gemfile` gem 'rubocop-rspec', require: false ``` +### Upgrading to RuboCop RSpec v3.x + +Read all the details in our [Upgrade to Version 3.x](https://docs.rubocop.org/rubocop-rspec/3.0/upgrade_to_version_3.html) document. + ### Upgrading to RuboCop RSpec v2.x Read all the details in our [Upgrade to Version 2.x](https://docs.rubocop.org/rubocop-rspec/2.0/upgrade_to_version_2.html) document. @@ -81,7 +94,7 @@ In your `.rubocop.yml`, you may treat the RSpec cops just like any other cop. For example: ```yaml -RSpec/FilePath: +RSpec/SpecFilePathFormat: Exclude: - spec/my_poorly_named_spec_file.rb ``` diff --git a/Rakefile b/Rakefile index c11b55cd6..23d584ed0 100644 --- a/Rakefile +++ b/Rakefile @@ -33,8 +33,7 @@ task :build_config do require 'rubocop/rspec/config_formatter' require 'rubocop/rspec/description_extractor' - glob = File.join('lib', 'rubocop', 'cop', 'rspec', - '{,capybara,factory_bot,rails}', '*.rb') + glob = File.join('lib', 'rubocop', 'cop', 'rspec', '*.rb') # Due to YARD's sensitivity to file require order (as of 0.9.25), # we have to prepend the list with our base cop, RuboCop::Cop::RSpec::Base. # Otherwise, cop's parent class for cops loaded before our base cop class @@ -73,12 +72,15 @@ end desc 'Confirm documentation is up to date' task confirm_documentation: :generate_cops_documentation do - _, _, _, process = - Open3.popen3('git diff --exit-code docs/') - - unless process.value.success? - raise 'Please run `rake generate_cops_documentation` ' \ - 'and add docs/ to the commit.' + stdout, _stderr, status = + Open3.capture3('git diff --exit-code docs/') + + unless status.success? + warn 'Documentation is out of sync:' + warn stdout + warn 'Please run `rake generate_cops_documentation` ' \ + 'and add docs/ to the commit.' + exit 1 end end @@ -91,13 +93,14 @@ task default: %i[build_config spec desc 'Generate a new cop template' task :new_cop, [:cop] do |_task, args| require 'rubocop' + require_relative 'lib/rubocop/rspec/cop/generator' cop_name = args.fetch(:cop) do warn "usage: bundle exec rake 'new_cop[Department/Name]'" exit! end - generator = RuboCop::Cop::Generator.new(cop_name) + generator = RuboCop::RSpec::Cop::Generator.new(cop_name) generator.write_source generator.write_spec generator.inject_require(root_file_path: 'lib/rubocop/cop/rspec_cops.rb') diff --git a/config/default.yml b/config/default.yml index ba24f8b5e..6c09b7d72 100644 --- a/config/default.yml +++ b/config/default.yml @@ -3,10 +3,10 @@ RSpec: Enabled: true StyleGuideBaseURL: https://rspec.rubystyle.guide DocumentationBaseURL: https://docs.rubocop.org/rubocop-rspec - Include: &1 + Include: - "**/*_spec.rb" - "**/spec/**/*" - Language: &2 + Language: inherit_mode: merge: - Expectations @@ -146,13 +146,15 @@ RSpec/Be: RSpec/BeEmpty: Description: Prefer using `be_empty` when checking for an empty array. - Enabled: pending + Enabled: true + AutoCorrect: contextual VersionAdded: '2.20' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEmpty RSpec/BeEq: Description: Check for expectations where `be(...)` can replace `eq(...)`. - Enabled: pending + Enabled: true Safe: false VersionAdded: 2.9.0 VersionChanged: '2.16' @@ -168,7 +170,7 @@ RSpec/BeEql: RSpec/BeNil: Description: Ensures a consistent style is used when matching `nil`. - Enabled: pending + Enabled: true EnforcedStyle: be_nil SupportedStyles: - be @@ -191,7 +193,7 @@ RSpec/BeforeAfterAll: RSpec/ChangeByZero: Description: Prefer negated matchers over `to change.by(0)`. - Enabled: pending + Enabled: true VersionAdded: '2.11' VersionChanged: '2.14' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ChangeByZero @@ -200,7 +202,7 @@ RSpec/ChangeByZero: RSpec/ClassCheck: Description: Enforces consistent use of `be_a` or `be_kind_of`. StyleGuide: "#is-a-vs-kind-of" - Enabled: pending + Enabled: true VersionAdded: '2.13' EnforcedStyle: be_a SupportedStyles: @@ -210,7 +212,7 @@ RSpec/ClassCheck: RSpec/ContainExactly: Description: Checks where `contain_exactly` is used. - Enabled: pending + Enabled: true VersionAdded: '2.19' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContainExactly @@ -283,9 +285,10 @@ RSpec/DescribedClass: SupportedStyles: - described_class - explicit + OnlyStaticConstants: true SafeAutoCorrect: false VersionAdded: '1.0' - VersionChanged: '1.11' + VersionChanged: '2.27' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribedClass RSpec/DescribedClassModuleWrapping: @@ -303,22 +306,25 @@ RSpec/Dialect: RSpec/DuplicatedMetadata: Description: Avoid duplicated metadata. - Enabled: pending + Enabled: true VersionAdded: '2.16' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DuplicatedMetadata RSpec/EmptyExampleGroup: Description: Checks if an example group does not include any tests. Enabled: true + AutoCorrect: contextual SafeAutoCorrect: false VersionAdded: '1.7' - VersionChanged: '2.13' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup RSpec/EmptyHook: Description: Checks for empty before and after hooks. Enabled: true + AutoCorrect: contextual VersionAdded: '1.39' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook RSpec/EmptyLineAfterExample: @@ -361,13 +367,21 @@ RSpec/EmptyLineAfterSubject: RSpec/EmptyMetadata: Description: Avoid empty metadata hash. - Enabled: pending + Enabled: true + AutoCorrect: contextual VersionAdded: '2.24' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyMetadata +RSpec/EmptyOutput: + Description: Check that the `output` matcher is not called with an empty string. + Enabled: true + VersionAdded: '2.29' + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyOutput + RSpec/Eq: Description: Use `eq` instead of `be ==` to compare objects. - Enabled: pending + Enabled: true VersionAdded: '2.24' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Eq @@ -389,6 +403,7 @@ RSpec/ExampleWithoutDescription: - single_line_only - disallow VersionAdded: '1.22' + StyleGuide: https://rspec.rubystyle.guide/#it-and-specify Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWithoutDescription RSpec/ExampleWording: @@ -409,7 +424,7 @@ RSpec/ExampleWording: RSpec/ExcessiveDocstringSpacing: Description: Checks for excessive whitespace in example descriptions. - Enabled: pending + Enabled: true VersionAdded: '2.5' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExcessiveDocstringSpacing @@ -440,32 +455,24 @@ RSpec/ExpectInHook: VersionAdded: '1.16' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectInHook +RSpec/ExpectInLet: + Description: Do not use `expect` in let. + Enabled: true + VersionAdded: '2.30' + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectInLet + RSpec/ExpectOutput: Description: Checks for opportunities to use `expect { ... }.to output`. Enabled: true VersionAdded: '1.10' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectOutput -RSpec/FilePath: - Description: Checks that spec file paths are consistent and well-formed. - Enabled: true - Include: - - "**/*_spec*rb*" - - "**/spec/**/*" - CustomTransform: - RuboCop: rubocop - RSpec: rspec - IgnoreMethods: false - SpecSuffixOnly: false - VersionAdded: '1.2' - VersionChanged: '2.24' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FilePath - RSpec/Focus: Description: Checks if examples are focused. Enabled: true + AutoCorrect: contextual VersionAdded: '1.5' - VersionChanged: '2.1' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Focus RSpec/HookArgument: @@ -483,12 +490,14 @@ RSpec/HookArgument: RSpec/HooksBeforeExamples: Description: Checks for before/around/after hooks that come after an example. Enabled: true + AutoCorrect: contextual VersionAdded: '1.29' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/HooksBeforeExamples RSpec/IdenticalEqualityAssertion: Description: Checks for equality assertions with identical expressions on both sides. - Enabled: pending + Enabled: true VersionAdded: '2.4' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IdenticalEqualityAssertion @@ -525,7 +534,7 @@ RSpec/ImplicitSubject: RSpec/IndexedLet: Description: Do not set up test data using indexes (e.g., `item_1`, `item_2`). - Enabled: pending + Enabled: true VersionAdded: '2.20' VersionChanged: '2.23' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IndexedLet @@ -548,6 +557,13 @@ RSpec/InstanceVariable: StyleGuide: https://rspec.rubystyle.guide/#instance-variables Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable +RSpec/IsExpectedSpecify: + Description: Check for `specify` with `is_expected` and one-liner expectations. + Enabled: true + VersionAdded: '2.27' + StyleGuide: https://rspec.rubystyle.guide/#it-and-specify + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IsExpectedSpecify + RSpec/ItBehavesLike: Description: Checks that only one `it_behaves_like` style is used. Enabled: true @@ -582,8 +598,9 @@ RSpec/LeakyConstantDeclaration: RSpec/LetBeforeExamples: Description: Checks for `let` definitions that come after an example. Enabled: true + AutoCorrect: contextual VersionAdded: '1.16' - VersionChanged: '1.22' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetBeforeExamples RSpec/LetSetup: @@ -594,7 +611,7 @@ RSpec/LetSetup: RSpec/MatchArray: Description: Checks where `match_array` is used. - Enabled: pending + Enabled: true VersionAdded: '2.19' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MatchArray @@ -627,7 +644,7 @@ RSpec/MessageSpies: RSpec/MetadataStyle: Description: Use consistent metadata style. - Enabled: pending + Enabled: true EnforcedStyle: symbol SupportedStyles: - hash @@ -641,6 +658,12 @@ RSpec/MissingExampleGroupArgument: VersionAdded: '1.28' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument +RSpec/MissingExpectationTargetMethod: + Description: Checks if `.to`, `not_to` or `to_not` are used. + Enabled: true + VersionAdded: '3.0' + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExpectationTargetMethod + RSpec/MultipleDescribes: Description: Checks for multiple top-level example groups. Enabled: true @@ -695,7 +718,7 @@ RSpec/NestedGroups: RSpec/NoExpectationExample: Description: Checks if an example contains any expectation. - Enabled: pending + Enabled: true Safe: false VersionAdded: '2.13' VersionChanged: '2.14' @@ -728,7 +751,7 @@ RSpec/Pending: RSpec/PendingWithoutReason: Description: Checks for pending or skipped examples without reason. - Enabled: pending + Enabled: true VersionAdded: '2.16' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/PendingWithoutReason @@ -754,7 +777,7 @@ RSpec/ReceiveCounts: RSpec/ReceiveMessages: Description: Checks for multiple messages stubbed on the same object. - Enabled: pending + Enabled: true SafeAutoCorrect: false VersionAdded: '2.23' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveMessages @@ -767,19 +790,19 @@ RSpec/ReceiveNever: RSpec/RedundantAround: Description: Remove redundant `around` hook. - Enabled: pending + Enabled: true VersionAdded: '2.19' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantAround RSpec/RedundantPredicateMatcher: Description: Checks for redundant predicate matcher. - Enabled: pending + Enabled: true VersionAdded: '2.26' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantPredicateMatcher RSpec/RemoveConst: Description: Checks that `remove_const` is not used in specs. - Enabled: pending + Enabled: true VersionAdded: '2.26' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RemoveConst @@ -813,6 +836,12 @@ RSpec/RepeatedIncludeExample: VersionAdded: '1.44' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedIncludeExample +RSpec/RepeatedSubjectCall: + Description: Checks for repeated calls to subject missing that it is memoized. + Enabled: true + VersionAdded: '2.27' + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedSubjectCall + RSpec/ReturnFromStub: Description: Checks for consistent style of stub's return setting. Enabled: true @@ -827,14 +856,17 @@ RSpec/ReturnFromStub: RSpec/ScatteredLet: Description: Checks for let scattered across the example group. Enabled: true + AutoCorrect: contextual VersionAdded: '1.14' - VersionChanged: '1.39' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredLet RSpec/ScatteredSetup: Description: Checks for setup scattered across multiple hooks in an example group. Enabled: true + AutoCorrect: contextual VersionAdded: '1.10' + VersionChanged: '2.31' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredSetup RSpec/SharedContext: @@ -863,19 +895,19 @@ RSpec/SingleArgumentMessageChain: RSpec/SkipBlockInsideExample: Description: Checks for passing a block to `skip` within examples. - Enabled: pending + Enabled: true VersionAdded: '2.19' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SkipBlockInsideExample RSpec/SortMetadata: Description: Sort RSpec metadata alphabetically. - Enabled: pending + Enabled: true VersionAdded: '2.14' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SortMetadata RSpec/SpecFilePathFormat: Description: Checks that spec file paths are consistent and well-formed. - Enabled: pending + Enabled: true Include: - "**/*_spec.rb" Exclude: @@ -891,13 +923,20 @@ RSpec/SpecFilePathFormat: RSpec/SpecFilePathSuffix: Description: Checks that spec file paths suffix are consistent and well-formed. - Enabled: pending + Enabled: true VersionAdded: '2.24' Include: - "**/*_spec*rb*" - "**/spec/**/*" Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathSuffix +RSpec/StringAsInstanceDoubleConstant: + Description: Do not use a string as `instance_double` constant. + Enabled: pending + Safe: false + VersionAdded: '3.1' + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/StringAsInstanceDoubleConstant + RSpec/StubbedMock: Description: Checks that message expectations do not have a configured response. Enabled: true @@ -906,7 +945,7 @@ RSpec/StubbedMock: RSpec/SubjectDeclaration: Description: Ensure that subject is defined using subject helper. - Enabled: pending + Enabled: true VersionAdded: '2.5' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SubjectDeclaration @@ -918,6 +957,12 @@ RSpec/SubjectStub: StyleGuide: https://rspec.rubystyle.guide/#dont-stub-subject Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SubjectStub +RSpec/UndescriptiveLiteralsDescription: + Description: Description should be descriptive. + Enabled: true + VersionAdded: '2.29' + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UndescriptiveLiteralsDescription + RSpec/UnspecifiedException: Description: Checks for a specified error in checking raised errors. Enabled: true @@ -948,7 +993,7 @@ RSpec/VariableName: RSpec/VerifiedDoubleReference: Description: Checks for consistent verified double reference style. - Enabled: pending + Enabled: true SafeAutoCorrect: false EnforcedStyle: constant SupportedStyles: @@ -979,212 +1024,3 @@ RSpec/Yield: Enabled: true VersionAdded: '1.32' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Yield - -RSpec/Capybara: - Enabled: true - Include: *1 - Language: *2 - -RSpec/Capybara/CurrentPathExpectation: - Description: Checks that no expectations are set on Capybara's `current_path`. - Enabled: true - VersionAdded: '1.18' - VersionChanged: '2.0' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/CurrentPathExpectation - -RSpec/Capybara/FeatureMethods: - Description: Checks for consistent method usage in feature specs. - Enabled: true - EnabledMethods: [] - VersionAdded: '1.17' - VersionChanged: '2.0' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods - -RSpec/Capybara/MatchStyle: - Description: Checks for usage of deprecated style methods. - Enabled: pending - VersionAdded: '2.17' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/MatchStyle - -RSpec/Capybara/NegationMatcher: - Description: Enforces use of `have_no_*` or `not_to` for negated expectations. - Enabled: pending - VersionAdded: '2.14' - EnforcedStyle: not_to - SupportedStyles: - - have_no - - not_to - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/NegationMatcher - -RSpec/Capybara/SpecificActions: - Description: Checks for there is a more specific actions offered by Capybara. - Enabled: pending - VersionAdded: '2.14' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificActions - -RSpec/Capybara/SpecificFinders: - Description: Checks if there is a more specific finder offered by Capybara. - Enabled: pending - VersionAdded: '2.13' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificFinders - -RSpec/Capybara/SpecificMatcher: - Description: Checks for there is a more specific matcher offered by Capybara. - Enabled: pending - VersionAdded: '2.12' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificMatcher - -RSpec/Capybara/VisibilityMatcher: - Description: Checks for boolean visibility in Capybara finders. - Enabled: true - VersionAdded: '1.39' - VersionChanged: '2.0' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher - -RSpec/FactoryBot: - Enabled: true - Include: *1 - Language: *2 - -RSpec/FactoryBot/AttributeDefinedStatically: - Description: Always declare attribute values as blocks. - Enabled: true - Include: - - "**/spec/factories.rb" - - "**/spec/factories/**/*.rb" - - "**/features/support/factories/**/*.rb" - VersionAdded: '1.28' - VersionChanged: '2.23' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically - -RSpec/FactoryBot/ConsistentParenthesesStyle: - Description: Use a consistent style for parentheses in factory bot calls. - Enabled: pending - EnforcedStyle: require_parentheses - SupportedStyles: - - require_parentheses - - omit_parentheses - VersionAdded: '2.14' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/ConsistentParenthesesStyle - -RSpec/FactoryBot/CreateList: - Description: Checks for create_list usage. - Enabled: true - Include: - - "**/*_spec.rb" - - "**/spec/**/*" - - "**/spec/factories.rb" - - "**/spec/factories/**/*.rb" - - "**/features/support/factories/**/*.rb" - EnforcedStyle: create_list - SupportedStyles: - - create_list - - n_times - VersionAdded: '1.25' - VersionChanged: '2.23' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList - -RSpec/FactoryBot/FactoryClassName: - Description: Use string value when setting the class attribute explicitly. - Enabled: true - Include: - - "**/spec/factories.rb" - - "**/spec/factories/**/*.rb" - - "**/features/support/factories/**/*.rb" - VersionAdded: '1.37' - VersionChanged: '2.23' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName - -RSpec/FactoryBot/FactoryNameStyle: - Description: Checks for name style for argument of FactoryBot::Syntax::Methods. - Enabled: pending - VersionAdded: '2.16' - EnforcedStyle: symbol - SupportedStyles: - - symbol - - string - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryNameStyle - -RSpec/FactoryBot/SyntaxMethods: - Description: Use shorthands from `FactoryBot::Syntax::Methods` in your specs. - Enabled: pending - SafeAutoCorrect: false - VersionAdded: '2.7' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/SyntaxMethods - -RSpec/Rails: - Enabled: true - Include: *1 - Language: *2 - -RSpec/Rails/AvoidSetupHook: - Description: Checks that tests use RSpec `before` hook over Rails `setup` method. - Enabled: pending - VersionAdded: '2.4' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/AvoidSetupHook - -RSpec/Rails/HaveHttpStatus: - Description: Checks that tests use `have_http_status` instead of equality matchers. - Enabled: pending - SafeAutoCorrect: false - VersionAdded: '2.12' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus - -RSpec/Rails/HttpStatus: - Description: Enforces use of symbolic or numeric value to describe HTTP status. - Enabled: true - EnforcedStyle: symbolic - SupportedStyles: - - numeric - - symbolic - - be_status - VersionAdded: '1.23' - VersionChanged: '2.20' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus - -RSpec/Rails/InferredSpecType: - Description: Identifies redundant spec type. - Enabled: pending - Safe: false - VersionAdded: '2.14' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/InferredSpecType - Inferences: - channels: channel - controllers: controller - features: feature - generator: generator - helpers: helper - jobs: job - mailboxes: mailbox - mailers: mailer - models: model - requests: request - integration: request - api: request - routing: routing - system: system - views: view - -RSpec/Rails/MinitestAssertions: - Description: Check if using Minitest matchers. - Enabled: pending - VersionAdded: '2.17' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/MinitestAssertions - -RSpec/Rails/NegationBeValid: - Description: Enforces use of `be_invalid` or `not_to` for negated be_valid. - Safe: false - EnforcedStyle: not_to - SupportedStyles: - - not_to - - be_invalid - Enabled: pending - VersionAdded: '2.23' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/NegationBeValid - -RSpec/Rails/TravelAround: - Description: Prefer to travel in `before` rather than `around`. - Enabled: pending - Safe: false - VersionAdded: '2.19' - Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/TravelAround diff --git a/config/obsoletion.yml b/config/obsoletion.yml index d38b0a973..530bd0ed4 100644 --- a/config/obsoletion.yml +++ b/config/obsoletion.yml @@ -13,17 +13,18 @@ changed_parameters: alternative: AllowedPatterns severity: warning -renamed: - RSpec/Capybara/CurrentPathExpectation: Capybara/CurrentPathExpectation - RSpec/Capybara/MatchStyle: Capybara/MatchStyle - RSpec/Capybara/NegationMatcher: Capybara/NegationMatcher - RSpec/Capybara/SpecificActions: Capybara/SpecificActions - RSpec/Capybara/SpecificFinders: Capybara/SpecificFinders - RSpec/Capybara/SpecificMatcher: Capybara/SpecificMatcher - RSpec/Capybara/VisibilityMatcher: Capybara/VisibilityMatcher - RSpec/FactoryBot/AttributeDefinedStatically: FactoryBot/AttributeDefinedStatically - RSpec/FactoryBot/ConsistentParenthesesStyle: FactoryBot/ConsistentParenthesesStyle - RSpec/FactoryBot/CreateList: FactoryBot/CreateList - RSpec/FactoryBot/FactoryClassName: FactoryBot/FactoryClassName - RSpec/FactoryBot/FactoryNameStyle: FactoryBot/FactoryNameStyle - RSpec/FactoryBot/SyntaxMethods: FactoryBot/SyntaxMethods +split: + RSpec/FilePath: + alternatives: + - RSpec/SpecFilePathFormat + - RSpec/SpecFilePathSuffix + +removed: + RSpec/Capybara/FeatureMethods: + reason: > + this cop has migrated to `RSpec/Dialect`. Please use `RSpec/Dialect` instead + +extracted: + RSpec/Rails/*: rubocop-rspec_rails + RSpec/FactoryBot/*: rubocop-factory_bot + RSpec/Capybara/*: rubocop-capybara diff --git a/docs/antora.yml b/docs/antora.yml index 51a533a15..7cca04801 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,5 +1,5 @@ name: rubocop-rspec title: RuboCop RSpec -version: ~ +version: '3.2' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 0487d48aa..2ab1a89d1 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -3,10 +3,8 @@ * xref:usage.adoc[Usage] * xref:cops.adoc[Cops] * xref:upgrade_to_version_2.adoc[Upgrade to 2.x] +* xref:upgrade_to_version_3.adoc[Upgrade to 3.x] * xref:third_party_rspec_syntax_extensions.adoc[RSpec syntax extensions in third-party gems] * xref:development.adoc[Development] * Cops Documentation -** xref:cops_rspec_capybara.adoc[Capybara] -** xref:cops_rspec_factorybot.adoc[FactoryBot] -** xref:cops_rspec_rails.adoc[Rails] ** xref:cops_rspec.adoc[RSpec] diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 41faf557f..116d2a714 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -32,6 +32,7 @@ * xref:cops_rspec.adoc#rspecemptylineafterhook[RSpec/EmptyLineAfterHook] * xref:cops_rspec.adoc#rspecemptylineaftersubject[RSpec/EmptyLineAfterSubject] * xref:cops_rspec.adoc#rspecemptymetadata[RSpec/EmptyMetadata] +* xref:cops_rspec.adoc#rspecemptyoutput[RSpec/EmptyOutput] * xref:cops_rspec.adoc#rspeceq[RSpec/Eq] * xref:cops_rspec.adoc#rspecexamplelength[RSpec/ExampleLength] * xref:cops_rspec.adoc#rspecexamplewithoutdescription[RSpec/ExampleWithoutDescription] @@ -40,8 +41,8 @@ * xref:cops_rspec.adoc#rspecexpectactual[RSpec/ExpectActual] * xref:cops_rspec.adoc#rspecexpectchange[RSpec/ExpectChange] * xref:cops_rspec.adoc#rspecexpectinhook[RSpec/ExpectInHook] +* xref:cops_rspec.adoc#rspecexpectinlet[RSpec/ExpectInLet] * xref:cops_rspec.adoc#rspecexpectoutput[RSpec/ExpectOutput] -* xref:cops_rspec.adoc#rspecfilepath[RSpec/FilePath] * xref:cops_rspec.adoc#rspecfocus[RSpec/Focus] * xref:cops_rspec.adoc#rspechookargument[RSpec/HookArgument] * xref:cops_rspec.adoc#rspechooksbeforeexamples[RSpec/HooksBeforeExamples] @@ -52,6 +53,7 @@ * xref:cops_rspec.adoc#rspecindexedlet[RSpec/IndexedLet] * xref:cops_rspec.adoc#rspecinstancespy[RSpec/InstanceSpy] * xref:cops_rspec.adoc#rspecinstancevariable[RSpec/InstanceVariable] +* xref:cops_rspec.adoc#rspecisexpectedspecify[RSpec/IsExpectedSpecify] * xref:cops_rspec.adoc#rspecitbehaveslike[RSpec/ItBehavesLike] * xref:cops_rspec.adoc#rspeciteratedexpectation[RSpec/IteratedExpectation] * xref:cops_rspec.adoc#rspecleadingsubject[RSpec/LeadingSubject] @@ -64,6 +66,7 @@ * xref:cops_rspec.adoc#rspecmessagespies[RSpec/MessageSpies] * xref:cops_rspec.adoc#rspecmetadatastyle[RSpec/MetadataStyle] * xref:cops_rspec.adoc#rspecmissingexamplegroupargument[RSpec/MissingExampleGroupArgument] +* xref:cops_rspec.adoc#rspecmissingexpectationtargetmethod[RSpec/MissingExpectationTargetMethod] * xref:cops_rspec.adoc#rspecmultipledescribes[RSpec/MultipleDescribes] * xref:cops_rspec.adoc#rspecmultipleexpectations[RSpec/MultipleExpectations] * xref:cops_rspec.adoc#rspecmultiplememoizedhelpers[RSpec/MultipleMemoizedHelpers] @@ -87,6 +90,7 @@ * xref:cops_rspec.adoc#rspecrepeatedexamplegroupbody[RSpec/RepeatedExampleGroupBody] * xref:cops_rspec.adoc#rspecrepeatedexamplegroupdescription[RSpec/RepeatedExampleGroupDescription] * xref:cops_rspec.adoc#rspecrepeatedincludeexample[RSpec/RepeatedIncludeExample] +* xref:cops_rspec.adoc#rspecrepeatedsubjectcall[RSpec/RepeatedSubjectCall] * xref:cops_rspec.adoc#rspecreturnfromstub[RSpec/ReturnFromStub] * xref:cops_rspec.adoc#rspecscatteredlet[RSpec/ScatteredLet] * xref:cops_rspec.adoc#rspecscatteredsetup[RSpec/ScatteredSetup] @@ -97,9 +101,11 @@ * xref:cops_rspec.adoc#rspecsortmetadata[RSpec/SortMetadata] * xref:cops_rspec.adoc#rspecspecfilepathformat[RSpec/SpecFilePathFormat] * xref:cops_rspec.adoc#rspecspecfilepathsuffix[RSpec/SpecFilePathSuffix] +* xref:cops_rspec.adoc#rspecstringasinstancedoubleconstant[RSpec/StringAsInstanceDoubleConstant] * xref:cops_rspec.adoc#rspecstubbedmock[RSpec/StubbedMock] * xref:cops_rspec.adoc#rspecsubjectdeclaration[RSpec/SubjectDeclaration] * xref:cops_rspec.adoc#rspecsubjectstub[RSpec/SubjectStub] +* xref:cops_rspec.adoc#rspecundescriptiveliteralsdescription[RSpec/UndescriptiveLiteralsDescription] * xref:cops_rspec.adoc#rspecunspecifiedexception[RSpec/UnspecifiedException] * xref:cops_rspec.adoc#rspecvariabledefinition[RSpec/VariableDefinition] * xref:cops_rspec.adoc#rspecvariablename[RSpec/VariableName] @@ -108,34 +114,4 @@ * xref:cops_rspec.adoc#rspecvoidexpect[RSpec/VoidExpect] * xref:cops_rspec.adoc#rspecyield[RSpec/Yield] -=== Department xref:cops_rspec_capybara.adoc[RSpec/Capybara] - -* xref:cops_rspec_capybara.adoc#rspeccapybara/currentpathexpectation[RSpec/Capybara/CurrentPathExpectation] -* xref:cops_rspec_capybara.adoc#rspeccapybara/featuremethods[RSpec/Capybara/FeatureMethods] -* xref:cops_rspec_capybara.adoc#rspeccapybara/matchstyle[RSpec/Capybara/MatchStyle] -* xref:cops_rspec_capybara.adoc#rspeccapybara/negationmatcher[RSpec/Capybara/NegationMatcher] -* xref:cops_rspec_capybara.adoc#rspeccapybara/specificactions[RSpec/Capybara/SpecificActions] -* xref:cops_rspec_capybara.adoc#rspeccapybara/specificfinders[RSpec/Capybara/SpecificFinders] -* xref:cops_rspec_capybara.adoc#rspeccapybara/specificmatcher[RSpec/Capybara/SpecificMatcher] -* xref:cops_rspec_capybara.adoc#rspeccapybara/visibilitymatcher[RSpec/Capybara/VisibilityMatcher] - -=== Department xref:cops_rspec_factorybot.adoc[RSpec/FactoryBot] - -* xref:cops_rspec_factorybot.adoc#rspecfactorybot/attributedefinedstatically[RSpec/FactoryBot/AttributeDefinedStatically] -* xref:cops_rspec_factorybot.adoc#rspecfactorybot/consistentparenthesesstyle[RSpec/FactoryBot/ConsistentParenthesesStyle] -* xref:cops_rspec_factorybot.adoc#rspecfactorybot/createlist[RSpec/FactoryBot/CreateList] -* xref:cops_rspec_factorybot.adoc#rspecfactorybot/factoryclassname[RSpec/FactoryBot/FactoryClassName] -* xref:cops_rspec_factorybot.adoc#rspecfactorybot/factorynamestyle[RSpec/FactoryBot/FactoryNameStyle] -* xref:cops_rspec_factorybot.adoc#rspecfactorybot/syntaxmethods[RSpec/FactoryBot/SyntaxMethods] - -=== Department xref:cops_rspec_rails.adoc[RSpec/Rails] - -* xref:cops_rspec_rails.adoc#rspecrails/avoidsetuphook[RSpec/Rails/AvoidSetupHook] -* xref:cops_rspec_rails.adoc#rspecrails/havehttpstatus[RSpec/Rails/HaveHttpStatus] -* xref:cops_rspec_rails.adoc#rspecrails/httpstatus[RSpec/Rails/HttpStatus] -* xref:cops_rspec_rails.adoc#rspecrails/inferredspectype[RSpec/Rails/InferredSpecType] -* xref:cops_rspec_rails.adoc#rspecrails/minitestassertions[RSpec/Rails/MinitestAssertions] -* xref:cops_rspec_rails.adoc#rspecrails/negationbevalid[RSpec/Rails/NegationBeValid] -* xref:cops_rspec_rails.adoc#rspecrails/travelaround[RSpec/Rails/TravelAround] - // END_COP_LIST diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index ff7bc9e6e..20015bc0a 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -1,5 +1,12 @@ +//// + Do NOT edit this file by hand directly, as it is automatically generated. + + Please make any necessary changes to the cop documentation within the source files themselves. +//// + = RSpec +[#rspecalignleftletbrace] == RSpec/AlignLeftLetBrace |=== @@ -7,13 +14,14 @@ | Disabled | Yes -| Yes +| Always | 1.16 | - |=== Checks that left braces for adjacent single line lets are aligned. +[#examples-rspecalignleftletbrace] === Examples [source,ruby] @@ -29,10 +37,12 @@ let(:baz) { bar } let(:a) { b } ---- +[#references-rspecalignleftletbrace] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/AlignLeftLetBrace +[#rspecalignrightletbrace] == RSpec/AlignRightLetBrace |=== @@ -40,13 +50,14 @@ let(:a) { b } | Disabled | Yes -| Yes +| Always | 1.16 | - |=== Checks that right braces for adjacent single line lets are aligned. +[#examples-rspecalignrightletbrace] === Examples [source,ruby] @@ -62,10 +73,12 @@ let(:baz) { bar } let(:a) { b } ---- +[#references-rspecalignrightletbrace] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/AlignRightLetBrace +[#rspecanyinstance] == RSpec/AnyInstance |=== @@ -82,6 +95,7 @@ Check that instances are not being stubbed globally. Prefer instance doubles over stubbing any instance of a class +[#examples-rspecanyinstance] === Examples [source,ruby] @@ -102,11 +116,13 @@ describe MyClass do end ---- +[#references-rspecanyinstance] === References * https://rspec.rubystyle.guide/#any_instance_of * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/AnyInstance +[#rspecaroundblock] == RSpec/AroundBlock |=== @@ -121,6 +137,7 @@ end Checks that around blocks actually run the test. +[#examples-rspecaroundblock] === Examples [source,ruby] @@ -146,10 +163,12 @@ around do |test| end ---- +[#references-rspecaroundblock] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/AroundBlock +[#rspecbe] == RSpec/Be |=== @@ -168,6 +187,7 @@ The `be` matcher is too generic, as it pass on everything that is not nil or false. If that is the exact intend, use `be_truthy`. In all other cases it's better to specify what exactly is the expected value. +[#examples-rspecbe] === Examples [source,ruby] @@ -181,25 +201,28 @@ expect(foo).to be 1.0 expect(foo).to be(true) ---- +[#references-rspecbe] === References * https://rspec.rubystyle.guide/#be-matcher * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Be +[#rspecbeempty] == RSpec/BeEmpty |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Command-line only | 2.20 -| - +| 2.31 |=== Prefer using `be_empty` when checking for an empty array. +[#examples-rspecbeempty] === Examples [source,ruby] @@ -212,18 +235,20 @@ expect(array).to match_array([]) expect(array).to be_empty ---- +[#references-rspecbeempty] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEmpty +[#rspecbeeq] == RSpec/BeEq |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | No -| Yes (Unsafe) +| Always (Unsafe) | 2.9.0 | 2.16 |=== @@ -234,10 +259,12 @@ The `be` matcher compares by identity while the `eq` matcher compares using `==`. Booleans and nil can be compared by identity and therefore the `be` matcher is preferable as it is a more strict test. +[#safety-rspecbeeq] === Safety This cop is unsafe because it changes how values are compared. +[#examples-rspecbeeq] === Examples [source,ruby] @@ -253,10 +280,12 @@ expect(foo).to be(false) expect(foo).to be(nil) ---- +[#references-rspecbeeq] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEq +[#rspecbeeql] == RSpec/BeEql |=== @@ -264,7 +293,7 @@ expect(foo).to be(nil) | Enabled | No -| Yes (Unsafe) +| Always (Unsafe) | 1.7 | 2.16 |=== @@ -283,10 +312,12 @@ than `!equal?`. We also do not try to flag `eq` because if necessarily the same type as `b` since the `#==` operator can coerce objects for comparison. +[#safety-rspecbeeql] === Safety This cop is unsafe because it changes how values are compared. +[#examples-rspecbeeql] === Examples [source,ruby] @@ -308,18 +339,20 @@ expect(foo).to be(:bar) expect(foo).to be(nil) ---- +[#references-rspecbeeql] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeEql +[#rspecbenil] == RSpec/BeNil |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.9.0 | 2.10.0 |=== @@ -331,8 +364,10 @@ generic `be` matcher with a `nil` argument. This cop can be configured using the `EnforcedStyle` option +[#examples-rspecbenil] === Examples +[#_enforcedstyle_-be_nil_-_default_-rspecbenil] ==== `EnforcedStyle: be_nil` (default) [source,ruby] @@ -344,6 +379,7 @@ expect(foo).to be(nil) expect(foo).to be_nil ---- +[#_enforcedstyle_-be_-rspecbenil] ==== `EnforcedStyle: be` [source,ruby] @@ -355,6 +391,7 @@ expect(foo).to be_nil expect(foo).to be(nil) ---- +[#configurable-attributes-rspecbenil] === Configurable attributes |=== @@ -365,10 +402,12 @@ expect(foo).to be(nil) | `be`, `be_nil` |=== +[#references-rspecbenil] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeNil +[#rspecbeforeafterall] == RSpec/BeforeAfterAll |=== @@ -383,6 +422,7 @@ expect(foo).to be(nil) Check that before/after(:all/:context) isn't being used. +[#examples-rspecbeforeafterall] === Examples [source,ruby] @@ -400,6 +440,7 @@ describe MyClass do end ---- +[#configurable-attributes-rspecbeforeafterall] === Configurable attributes |=== @@ -410,19 +451,21 @@ end | Array |=== +[#references-rspecbeforeafterall] === References * https://rspec.rubystyle.guide/#avoid-hooks-with-context-scope * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeforeAfterAll +[#rspecchangebyzero] == RSpec/ChangeByZero |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.11 | 2.14 |=== @@ -437,8 +480,10 @@ compound expectations, but if you set the negated matcher for `change`, e.g. `not_change` with the `NegatedMatcher` option, the cop will perform the autocorrection. +[#examples-rspecchangebyzero] === Examples +[#negatedmatcher_-_-_default_-rspecchangebyzero] ==== NegatedMatcher: ~ (default) [source,ruby] @@ -469,6 +514,7 @@ expect { run } .and not_change { Foo.baz } ---- +[#negatedmatcher_-not_change-rspecchangebyzero] ==== NegatedMatcher: not_change [source,ruby] @@ -491,6 +537,7 @@ expect { run } .and not_change { Foo.baz } ---- +[#configurable-attributes-rspecchangebyzero] === Configurable attributes |=== @@ -501,26 +548,30 @@ expect { run } | |=== +[#references-rspecchangebyzero] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ChangeByZero +[#rspecclasscheck] == RSpec/ClassCheck |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.13 | - |=== Enforces consistent use of `be_a` or `be_kind_of`. +[#examples-rspecclasscheck] === Examples +[#enforcedstyle_-be_a-_default_-rspecclasscheck] ==== EnforcedStyle: be_a (default) [source,ruby] @@ -534,6 +585,7 @@ expect(object).to be_a(String) expect(object).to be_an(String) ---- +[#enforcedstyle_-be_kind_of-rspecclasscheck] ==== EnforcedStyle: be_kind_of [source,ruby] @@ -547,6 +599,7 @@ expect(object).to be_kind_of(String) expect(object).to be_a_kind_of(String) ---- +[#configurable-attributes-rspecclasscheck] === Configurable attributes |=== @@ -557,19 +610,21 @@ expect(object).to be_a_kind_of(String) | `be_a`, `be_kind_of` |=== +[#references-rspecclasscheck] === References * https://rubystyle.guide#is-a-vs-kind-of * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ClassCheck +[#rspeccontainexactly] == RSpec/ContainExactly |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.19 | - |=== @@ -580,6 +635,7 @@ This cop checks for the following: - Prefer `match_array` when matching array values. - Prefer `be_empty` when using `contain_exactly` with no arguments. +[#examples-rspeccontainexactly] === Examples [source,ruby] @@ -594,10 +650,12 @@ it { is_expected.to match_array(array1 + array2) } it { is_expected.to contain_exactly(content, *array) } ---- +[#references-rspeccontainexactly] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContainExactly +[#rspeccontextmethod] == RSpec/ContextMethod |=== @@ -605,13 +663,14 @@ it { is_expected.to contain_exactly(content, *array) } | Enabled | Yes -| Yes +| Always | 1.36 | - |=== `context` should not be used for specifying methods. +[#examples-rspeccontextmethod] === Examples [source,ruby] @@ -635,11 +694,13 @@ describe '.foo_bar' do end ---- +[#references-rspeccontextmethod] === References * https://rspec.rubystyle.guide/#example-group-naming * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextMethod +[#rspeccontextwording] == RSpec/ContextWording |=== @@ -659,11 +720,16 @@ the configuration to meet project needs. Other acceptable prefixes may include `if`, `unless`, `for`, `before`, `after`, or `during`. They may consist of multiple words if desired. +If both `Prefixes` and `AllowedPatterns` are empty, this cop will always +report an offense. So you need to set at least one of them. + This cop can be customized allowed context description pattern with `AllowedPatterns`. By default, there are no checking by pattern. +[#examples-rspeccontextwording] === Examples +[#_prefixes_-configuration-rspeccontextwording] ==== `Prefixes` configuration [source,ruby] @@ -692,6 +758,7 @@ context 'when the display name is not present' do end ---- +[#_allowedpatterns_-configuration-rspeccontextwording] ==== `AllowedPatterns` configuration [source,ruby] @@ -715,6 +782,7 @@ context '条件を満たすとき' do end ---- +[#configurable-attributes-rspeccontextwording] === Configurable attributes |=== @@ -729,12 +797,14 @@ end | Array |=== +[#references-rspeccontextwording] === References * https://rspec.rubystyle.guide/#context-descriptions * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ContextWording * http://www.betterspecs.org/#contexts +[#rspecdescribeclass] == RSpec/DescribeClass |=== @@ -753,8 +823,10 @@ It can be configured to ignore strings when certain metadata is passed. Ignores Rails and Aruba `type` metadata by default. +[#examples-rspecdescribeclass] === Examples +[#_ignoredmetadata_-configuration-rspecdescribeclass] ==== `IgnoredMetadata` configuration [source,ruby] @@ -786,6 +858,7 @@ describe "A feature example", type: :feature do end ---- +[#configurable-attributes-rspecdescribeclass] === Configurable attributes |=== @@ -800,10 +873,12 @@ end | |=== +[#references-rspecdescribeclass] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass +[#rspecdescribemethod] == RSpec/DescribeMethod |=== @@ -818,6 +893,7 @@ end Checks that the second argument to `describe` specifies a method. +[#examples-rspecdescribemethod] === Examples [source,ruby] @@ -834,10 +910,12 @@ describe MyClass, '.my_class_method' do end ---- +[#references-rspecdescribemethod] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeMethod +[#rspecdescribesymbol] == RSpec/DescribeSymbol |=== @@ -852,6 +930,7 @@ end Avoid describing symbols. +[#examples-rspecdescribesymbol] === Examples [source,ruby] @@ -867,11 +946,13 @@ describe '#my_method' do end ---- +[#references-rspecdescribesymbol] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeSymbol * https://github.com/rspec/rspec-core/issues/1610 +[#rspecdescribedclass] == RSpec/DescribedClass |=== @@ -879,9 +960,9 @@ end | Enabled | Yes -| Yes (Unsafe) +| Always (Unsafe) | 1.0 -| 1.11 +| 2.27 |=== Checks that tests use `described_class`. @@ -889,8 +970,10 @@ Checks that tests use `described_class`. If the first argument of describe is a class, the class is exposed to each example via described_class. -This cop can be configured using the `EnforcedStyle` and `SkipBlocks` -options. +This cop can be configured using the `EnforcedStyle`, `SkipBlocks` +and `OnlyStaticConstants` options. +`OnlyStaticConstants` is only relevant when `EnforcedStyle` is +`described_class`. There's a known caveat with rspec-rails's `controller` helper that runs its block in a different context, and `described_class` is not @@ -901,8 +984,10 @@ To narrow down this setting to only a specific directory, it is possible to use an overriding configuration file local to that directory. +[#examples-rspecdescribedclass] === Examples +[#_enforcedstyle_-described_class_-_default_-rspecdescribedclass] ==== `EnforcedStyle: described_class` (default) [source,ruby] @@ -918,6 +1003,29 @@ describe MyClass do end ---- +[#_onlystaticconstants_-true_-_default_-rspecdescribedclass] +==== `OnlyStaticConstants: true` (default) + +[source,ruby] +---- +# good +describe MyClass do + subject { MyClass::CONSTANT } +end +---- + +[#_onlystaticconstants_-false_-rspecdescribedclass] +==== `OnlyStaticConstants: false` + +[source,ruby] +---- +# bad +describe MyClass do + subject { MyClass::CONSTANT } +end +---- + +[#_enforcedstyle_-explicit_-rspecdescribedclass] ==== `EnforcedStyle: explicit` [source,ruby] @@ -933,6 +1041,7 @@ describe MyClass do end ---- +[#_skipblocks_-true_-rspecdescribedclass] ==== `SkipBlocks: true` [source,ruby] @@ -949,6 +1058,7 @@ describe MyConcern do end ---- +[#configurable-attributes-rspecdescribedclass] === Configurable attributes |=== @@ -961,12 +1071,18 @@ end | EnforcedStyle | `described_class` | `described_class`, `explicit` + +| OnlyStaticConstants +| `true` +| Boolean |=== +[#references-rspecdescribedclass] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribedClass +[#rspecdescribedclassmodulewrapping] == RSpec/DescribedClassModuleWrapping |=== @@ -981,6 +1097,7 @@ end Avoid opening modules and defining specs within them. +[#examples-rspecdescribedclassmodulewrapping] === Examples [source,ruby] @@ -998,11 +1115,13 @@ RSpec.describe MyModule::MyClass do end ---- +[#references-rspecdescribedclassmodulewrapping] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribedClassModuleWrapping * https://github.com/rubocop/rubocop-rspec/issues/735 +[#rspecdialect] == RSpec/Dialect |=== @@ -1010,7 +1129,7 @@ end | Disabled | Yes -| Yes +| Always | 1.33 | - |=== @@ -1041,8 +1160,22 @@ a config like: PreferredMethods: context: describe +If you were previously using the `RSpec/Capybara/FeatureMethods` cop and +want to keep disabling all Capybara-specific methods that have the same +native RSpec method (e.g. are just aliases), use the following config: + + RSpec/Dialect: + PreferredMethods: + background: :before + scenario: :it + xscenario: :xit + given: :let + given!: :let! + feature: :describe + You can expect the following behavior: +[#examples-rspecdialect] === Examples [source,ruby] @@ -1058,6 +1191,7 @@ describe 'display name presence' do end ---- +[#configurable-attributes-rspecdialect] === Configurable attributes |=== @@ -1068,24 +1202,27 @@ end | |=== +[#references-rspecdialect] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Dialect +[#rspecduplicatedmetadata] == RSpec/DuplicatedMetadata |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.16 | - |=== Avoid duplicated metadata. +[#examples-rspecduplicatedmetadata] === Examples [source,ruby] @@ -1097,10 +1234,12 @@ describe 'Something', :a, :a describe 'Something', :a ---- +[#references-rspecduplicatedmetadata] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DuplicatedMetadata +[#rspecemptyexamplegroup] == RSpec/EmptyExampleGroup |=== @@ -1108,15 +1247,17 @@ describe 'Something', :a | Enabled | Yes -| Yes (Unsafe) +| Command-line only (Unsafe) | 1.7 -| 2.13 +| 2.31 |=== Checks if an example group does not include any tests. +[#examples-rspecemptyexamplegroup] === Examples +[#usage-rspecemptyexamplegroup] ==== usage [source,ruby] @@ -1151,10 +1292,12 @@ describe Bacon do end ---- +[#references-rspecemptyexamplegroup] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup +[#rspecemptyhook] == RSpec/EmptyHook |=== @@ -1162,13 +1305,14 @@ end | Enabled | Yes -| Yes +| Command-line only | 1.39 -| - +| 2.31 |=== Checks for empty before and after hooks. +[#examples-rspecemptyhook] === Examples [source,ruby] @@ -1191,10 +1335,12 @@ end after(:all) { cleanup_feed } ---- +[#references-rspecemptyhook] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook +[#rspecemptylineafterexample] == RSpec/EmptyLineAfterExample |=== @@ -1202,13 +1348,14 @@ after(:all) { cleanup_feed } | Enabled | Yes -| Yes +| Always | 1.36 | - |=== Checks if there is an empty line after example blocks. +[#examples-rspecemptylineafterexample] === Examples [source,ruby] @@ -1237,6 +1384,7 @@ RSpec.describe Foo do end ---- +[#with-allowconsecutiveoneliners-configuration-rspecemptylineafterexample] ==== with AllowConsecutiveOneLiners configuration [source,ruby] @@ -1252,6 +1400,7 @@ RSpec.describe Foo do end ---- +[#configurable-attributes-rspecemptylineafterexample] === Configurable attributes |=== @@ -1262,11 +1411,13 @@ end | Boolean |=== +[#references-rspecemptylineafterexample] === References * https://rspec.rubystyle.guide/#empty-lines-around-examples * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterExample +[#rspecemptylineafterexamplegroup] == RSpec/EmptyLineAfterExampleGroup |=== @@ -1274,13 +1425,14 @@ end | Enabled | Yes -| Yes +| Always | 1.27 | - |=== Checks if there is an empty line after example group blocks. +[#examples-rspecemptylineafterexamplegroup] === Examples [source,ruby] @@ -1303,11 +1455,13 @@ RSpec.describe Foo do end ---- +[#references-rspecemptylineafterexamplegroup] === References * https://rspec.rubystyle.guide/#empty-lines-between-describes * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterExampleGroup +[#rspecemptylineafterfinallet] == RSpec/EmptyLineAfterFinalLet |=== @@ -1315,13 +1469,14 @@ end | Enabled | Yes -| Yes +| Always | 1.14 | - |=== Checks if there is an empty line after the last let block. +[#examples-rspecemptylineafterfinallet] === Examples [source,ruby] @@ -1338,11 +1493,13 @@ let(:something) { other } it { does_something } ---- +[#references-rspecemptylineafterfinallet] === References * https://rspec.rubystyle.guide/#empty-line-after-let * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterFinalLet +[#rspecemptylineafterhook] == RSpec/EmptyLineAfterHook |=== @@ -1350,7 +1507,7 @@ it { does_something } | Enabled | Yes -| Yes +| Always | 1.27 | 2.13 |=== @@ -1360,6 +1517,7 @@ Checks if there is an empty line after hook blocks. `AllowConsecutiveOneLiners` configures whether adjacent one-line definitions are considered an offense. +[#examples-rspecemptylineafterhook] === Examples [source,ruby] @@ -1388,6 +1546,7 @@ after { do_something } it { does_something } ---- +[#with-allowconsecutiveoneliners-configuration-rspecemptylineafterhook] ==== with AllowConsecutiveOneLiners configuration [source,ruby] @@ -1410,6 +1569,7 @@ after { do_something } it { does_something } ---- +[#configurable-attributes-rspecemptylineafterhook] === Configurable attributes |=== @@ -1420,11 +1580,13 @@ it { does_something } | Boolean |=== +[#references-rspecemptylineafterhook] === References * https://rspec.rubystyle.guide/#empty-line-after-let * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterHook +[#rspecemptylineaftersubject] == RSpec/EmptyLineAfterSubject |=== @@ -1432,13 +1594,14 @@ it { does_something } | Enabled | Yes -| Yes +| Always | 1.14 | - |=== Checks if there is an empty line after subject block. +[#examples-rspecemptylineaftersubject] === Examples [source,ruby] @@ -1453,27 +1616,31 @@ subject(:obj) { described_class } let(:foo) { bar } ---- +[#references-rspecemptylineaftersubject] === References * https://rspec.rubystyle.guide/#empty-line-after-let * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterSubject +[#rspecemptymetadata] == RSpec/EmptyMetadata |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Command-line only | 2.24 -| - +| 2.31 |=== Avoid empty metadata hash. +[#examples-rspecemptymetadata] === Examples +[#enforcedstyle_-symbol-_default_-rspecemptymetadata] ==== EnforcedStyle: symbol (default) [source,ruby] @@ -1485,24 +1652,61 @@ describe 'Something', {} describe 'Something' ---- +[#references-rspecemptymetadata] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyMetadata -== RSpec/Eq +[#rspecemptyoutput] +== RSpec/EmptyOutput |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes +| Always +| 2.29 +| - +|=== + +Check that the `output` matcher is not called with an empty string. + +[#examples-rspecemptyoutput] +=== Examples + +[source,ruby] +---- +# bad +expect { foo }.to output('').to_stdout +expect { bar }.not_to output('').to_stderr + +# good +expect { foo }.not_to output.to_stdout +expect { bar }.to output.to_stderr +---- + +[#references-rspecemptyoutput] +=== References + +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyOutput + +[#rspeceq] +== RSpec/Eq + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Enabled | Yes +| Always | 2.24 | - |=== Use `eq` instead of `be ==` to compare objects. +[#examples-rspeceq] === Examples [source,ruby] @@ -1514,10 +1718,12 @@ expect(foo).to be == 42 expect(foo).to eq 42 ---- +[#references-rspeceq] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Eq +[#rspecexamplelength] == RSpec/ExampleLength |=== @@ -1541,6 +1747,7 @@ Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct will be counted as one line regardless of its actual size. +[#examples-rspecexamplelength] === Examples [source,ruby] @@ -1562,6 +1769,7 @@ it do end ---- +[#countasone_-__array__-_heredoc__-_method_call__-rspecexamplelength] ==== CountAsOne: ['array', 'heredoc', 'method_call'] [source,ruby] @@ -1588,6 +1796,7 @@ it do end # 6 points ---- +[#configurable-attributes-rspecexamplelength] === Configurable attributes |=== @@ -1602,10 +1811,12 @@ end # 6 points | Array |=== +[#references-rspecexamplelength] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleLength +[#rspecexamplewithoutdescription] == RSpec/ExampleWithoutDescription |=== @@ -1622,6 +1833,7 @@ Checks for examples without a description. RSpec allows for auto-generated example descriptions when there is no description provided or the description is an empty one. +It is acceptable to use `specify` without a description This cop removes empty descriptions. It also defines whether auto-generated description is allowed, based @@ -1629,27 +1841,39 @@ on the configured style. This cop can be configured using the `EnforcedStyle` option +[#examples-rspecexamplewithoutdescription] === Examples +[source,ruby] +---- +# always good +specify do + result = service.call + expect(result).to be(true) +end +---- + +[#_enforcedstyle_-always_allow_-_default_-rspecexamplewithoutdescription] ==== `EnforcedStyle: always_allow` (default) [source,ruby] ---- # bad it('') { is_expected.to be_good } -it '' do +specify '' do result = service.call expect(result).to be(true) end # good it { is_expected.to be_good } -it do +specify do result = service.call expect(result).to be(true) end ---- +[#_enforcedstyle_-single_line_only_-rspecexamplewithoutdescription] ==== `EnforcedStyle: single_line_only` [source,ruby] @@ -1665,6 +1889,7 @@ end it { is_expected.to be_good } ---- +[#_enforcedstyle_-disallow_-rspecexamplewithoutdescription] ==== `EnforcedStyle: disallow` [source,ruby] @@ -1677,6 +1902,7 @@ it do end ---- +[#configurable-attributes-rspecexamplewithoutdescription] === Configurable attributes |=== @@ -1687,10 +1913,13 @@ end | `always_allow`, `single_line_only`, `disallow` |=== +[#references-rspecexamplewithoutdescription] === References +* https://rspec.rubystyle.guide/#it-and-specify * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWithoutDescription +[#rspecexamplewording] == RSpec/ExampleWording |=== @@ -1698,7 +1927,7 @@ end | Enabled | Yes -| Yes +| Always | 1.0 | 2.13 |=== @@ -1715,6 +1944,7 @@ Use the DisallowedExamples setting to prevent unclear or insufficient descriptions. Please note that this config will not be treated as case sensitive. +[#examples-rspecexamplewording] === Examples [source,ruby] @@ -1742,6 +1972,7 @@ it 'does things' do end ---- +[#_disallowedexamples_-__works___-_default_-rspecexamplewording] ==== `DisallowedExamples: ['works']` (default) [source,ruby] @@ -1755,6 +1986,7 @@ it 'marks the task as done' do end ---- +[#configurable-attributes-rspecexamplewording] === Configurable attributes |=== @@ -1773,26 +2005,29 @@ end | Array |=== +[#references-rspecexamplewording] === References * https://rspec.rubystyle.guide/#should-in-example-docstrings * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExampleWording * http://betterspecs.org/#should +[#rspecexcessivedocstringspacing] == RSpec/ExcessiveDocstringSpacing |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.5 | - |=== Checks for excessive whitespace in example descriptions. +[#examples-rspecexcessivedocstringspacing] === Examples [source,ruby] @@ -1817,10 +2052,12 @@ context 'when a condition is met' do end ---- +[#references-rspecexcessivedocstringspacing] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExcessiveDocstringSpacing +[#rspecexpectactual] == RSpec/ExpectActual |=== @@ -1828,7 +2065,7 @@ end | Enabled | Yes -| Yes +| Always | 1.7 | 2.23 |=== @@ -1837,6 +2074,7 @@ Checks for `expect(...)` calls containing literal values. Autocorrection is performed when the expected is not a literal. +[#examples-rspecexpectactual] === Examples [source,ruby] @@ -1855,6 +2093,7 @@ expect(name).to eq("John") expect(false).to eq(true) ---- +[#configurable-attributes-rspecexpectactual] === Configurable attributes |=== @@ -1865,10 +2104,12 @@ expect(false).to eq(true) | Array |=== +[#references-rspecexpectactual] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectActual +[#rspecexpectchange] == RSpec/ExpectChange |=== @@ -1876,7 +2117,7 @@ expect(false).to eq(true) | Enabled | Yes -| Yes (Unsafe) +| Always (Unsafe) | 1.22 | 2.5 |=== @@ -1888,8 +2129,10 @@ or passing a block that reads the attribute value. This cop can be configured using the `EnforcedStyle` option. +[#examples-rspecexpectchange] === Examples +[#_enforcedstyle_-method_call_-_default_-rspecexpectchange] ==== `EnforcedStyle: method_call` (default) [source,ruby] @@ -1906,6 +2149,7 @@ expect { run }.to change { Foo.bar(:count) } expect { run }.to change { user.reload.name } ---- +[#_enforcedstyle_-block_-rspecexpectchange] ==== `EnforcedStyle: block` [source,ruby] @@ -1917,6 +2161,7 @@ expect { run }.to change(Foo, :bar) expect { run }.to change { Foo.bar } ---- +[#configurable-attributes-rspecexpectchange] === Configurable attributes |=== @@ -1927,10 +2172,12 @@ expect { run }.to change { Foo.bar } | `method_call`, `block` |=== +[#references-rspecexpectchange] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectChange +[#rspecexpectinhook] == RSpec/ExpectInHook |=== @@ -1945,6 +2192,7 @@ expect { run }.to change { Foo.bar } Do not use `expect` in hooks such as `before`. +[#examples-rspecexpectinhook] === Examples [source,ruby] @@ -1965,11 +2213,13 @@ it do end ---- +[#references-rspecexpectinhook] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectInHook -== RSpec/ExpectOutput +[#rspecexpectinlet] +== RSpec/ExpectInLet |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed @@ -1977,31 +2227,35 @@ end | Enabled | Yes | No -| 1.10 +| 2.30 | - |=== -Checks for opportunities to use `expect { ... }.to output`. +Do not use `expect` in let. +[#examples-rspecexpectinlet] === Examples [source,ruby] ---- # bad -$stdout = StringIO.new -my_app.print_report -$stdout = STDOUT -expect($stdout.string).to eq('Hello World') +let(:foo) do + expect(something).to eq 'foo' +end # good -expect { my_app.print_report }.to output('Hello World').to_stdout +it do + expect(something).to eq 'foo' +end ---- +[#references-rspecexpectinlet] === References -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectOutput +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectInLet -== RSpec/FilePath +[#rspecexpectoutput] +== RSpec/ExpectOutput |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed @@ -2009,107 +2263,33 @@ expect { my_app.print_report }.to output('Hello World').to_stdout | Enabled | Yes | No -| 1.2 -| 2.24 +| 1.10 +| - |=== -Checks that spec file paths are consistent and well-formed. - -This cop is deprecated. -We plan to remove it in the next major version update to 3.0. -The migration targets are `RSpec/SpecFilePathSuffix` -and `RSpec/SpecFilePathFormat`. -If you are using this cop, please plan for migration. - -By default, this checks that spec file paths are consistent with the -test subject and enforces that it reflects the described -class/module and its optionally called out method. - -With the configuration option `IgnoreMethods` the called out method will -be ignored when determining the enforced path. - -With the configuration option `CustomTransform` modules or classes can -be specified that should not as usual be transformed from CamelCase to -snake_case (e.g. 'RuboCop' => 'rubocop' ). - -With the configuration option `SpecSuffixOnly` test files will only -be checked to ensure they end in '_spec.rb'. This option disables -checking for consistency in the test subject or test methods. +Checks for opportunities to use `expect { ... }.to output`. +[#examples-rspecexpectoutput] === Examples [source,ruby] ---- # bad -whatever_spec.rb # describe MyClass - -# bad -my_class_spec.rb # describe MyClass, '#method' - -# good -my_class_spec.rb # describe MyClass - -# good -my_class_method_spec.rb # describe MyClass, '#method' - -# good -my_class/method_spec.rb # describe MyClass, '#method' ----- - -==== when configuration is `IgnoreMethods: true` - -[source,ruby] ----- -# bad -whatever_spec.rb # describe MyClass - -# good -my_class_spec.rb # describe MyClass - -# good -my_class_spec.rb # describe MyClass, '#method' ----- - -==== when configuration is `SpecSuffixOnly: true` - -[source,ruby] ----- -# good -whatever_spec.rb # describe MyClass - -# good -my_class_spec.rb # describe MyClass +$stdout = StringIO.new +my_app.print_report +$stdout = STDOUT +expect($stdout.string).to eq('Hello World') # good -my_class_spec.rb # describe MyClass, '#method' +expect { my_app.print_report }.to output('Hello World').to_stdout ---- -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| Include -| `+**/*_spec*rb*+`, `+**/spec/**/*+` -| Array - -| CustomTransform -| `{"RuboCop"=>"rubocop", "RSpec"=>"rspec"}` -| - -| IgnoreMethods -| `false` -| Boolean - -| SpecSuffixOnly -| `false` -| Boolean -|=== - +[#references-rspecexpectoutput] === References -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FilePath +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ExpectOutput +[#rspecfocus] == RSpec/Focus |=== @@ -2117,15 +2297,16 @@ my_class_spec.rb # describe MyClass, '#method' | Enabled | Yes -| Yes +| Command-line only | 1.5 -| 2.1 +| 2.31 |=== Checks if examples are focused. This cop does not support autocorrection in some cases. +[#examples-rspecfocus] === Examples [source,ruby] @@ -2172,10 +2353,12 @@ shared_context 'test' do; end focus 'test' do; end ---- +[#references-rspecfocus] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Focus +[#rspechookargument] == RSpec/HookArgument |=== @@ -2183,7 +2366,7 @@ focus 'test' do; end | Enabled | Yes -| Yes +| Always | 1.7 | - |=== @@ -2195,8 +2378,10 @@ hooks which run for each example. There are three supported styles: "implicit", "each", and "example." All styles have the same behavior. +[#examples-rspechookargument] === Examples +[#_enforcedstyle_-implicit_-_default_-rspechookargument] ==== `EnforcedStyle: implicit` (default) [source,ruby] @@ -2217,6 +2402,7 @@ before do end ---- +[#_enforcedstyle_-each_-rspechookargument] ==== `EnforcedStyle: each` [source,ruby] @@ -2237,6 +2423,7 @@ before(:each) do end ---- +[#_enforcedstyle_-example_-rspechookargument] ==== `EnforcedStyle: example` [source,ruby] @@ -2257,6 +2444,7 @@ before(:example) do end ---- +[#configurable-attributes-rspechookargument] === Configurable attributes |=== @@ -2267,11 +2455,13 @@ end | `implicit`, `each`, `example` |=== +[#references-rspechookargument] === References * https://rspec.rubystyle.guide/#redundant-beforeeach * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/HookArgument +[#rspechooksbeforeexamples] == RSpec/HooksBeforeExamples |=== @@ -2279,13 +2469,14 @@ end | Enabled | Yes -| Yes +| Command-line only | 1.29 -| - +| 2.31 |=== Checks for before/around/after hooks that come after an example. +[#examples-rspechooksbeforeexamples] === Examples [source,ruby] @@ -2307,16 +2498,18 @@ it 'checks what foo does' do end ---- +[#references-rspechooksbeforeexamples] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/HooksBeforeExamples +[#rspecidenticalequalityassertion] == RSpec/IdenticalEqualityAssertion |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.4 @@ -2325,6 +2518,7 @@ end Checks for equality assertions with identical expressions on both sides. +[#examples-rspecidenticalequalityassertion] === Examples [source,ruby] @@ -2338,10 +2532,12 @@ expect(foo.bar).to eq(2) expect(foo.bar).to eql(2) ---- +[#references-rspecidenticalequalityassertion] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IdenticalEqualityAssertion +[#rspecimplicitblockexpectation] == RSpec/ImplicitBlockExpectation |=== @@ -2358,6 +2554,7 @@ Check that implicit block expectation syntax is not used. Prefer using explicit block expectations. +[#examples-rspecimplicitblockexpectation] === Examples [source,ruby] @@ -2372,11 +2569,13 @@ it 'changes something to a new value' do end ---- +[#references-rspecimplicitblockexpectation] === References * https://rspec.rubystyle.guide/#implicit-block-expectations * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitBlockExpectation +[#rspecimplicitexpect] == RSpec/ImplicitExpect |=== @@ -2384,7 +2583,7 @@ end | Enabled | Yes -| Yes +| Always | 1.8 | - |=== @@ -2394,8 +2593,10 @@ Check that a consistent implicit expectation style is used. This cop can be configured using the `EnforcedStyle` option and supports the `--auto-gen-config` flag. +[#examples-rspecimplicitexpect] === Examples +[#_enforcedstyle_-is_expected_-_default_-rspecimplicitexpect] ==== `EnforcedStyle: is_expected` (default) [source,ruby] @@ -2407,6 +2608,7 @@ it { should be_truthy } it { is_expected.to be_truthy } ---- +[#_enforcedstyle_-should_-rspecimplicitexpect] ==== `EnforcedStyle: should` [source,ruby] @@ -2418,6 +2620,7 @@ it { is_expected.to be_truthy } it { should be_truthy } ---- +[#configurable-attributes-rspecimplicitexpect] === Configurable attributes |=== @@ -2428,11 +2631,13 @@ it { should be_truthy } | `is_expected`, `should` |=== +[#references-rspecimplicitexpect] === References * https://rspec.rubystyle.guide/#use-expect * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitExpect +[#rspecimplicitsubject] == RSpec/ImplicitSubject |=== @@ -2440,7 +2645,7 @@ it { should be_truthy } | Enabled | Yes -| Yes +| Always | 1.29 | 2.13 |=== @@ -2449,8 +2654,10 @@ Checks for usage of implicit subject (`is_expected` / `should`). This cop can be configured using the `EnforcedStyle` option +[#examples-rspecimplicitsubject] === Examples +[#_enforcedstyle_-single_line_only_-_default_-rspecimplicitsubject] ==== `EnforcedStyle: single_line_only` (default) [source,ruby] @@ -2467,6 +2674,7 @@ it do end ---- +[#_enforcedstyle_-single_statement_only_-rspecimplicitsubject] ==== `EnforcedStyle: single_statement_only` [source,ruby] @@ -2487,6 +2695,7 @@ it do end ---- +[#_enforcedstyle_-disallow_-rspecimplicitsubject] ==== `EnforcedStyle: disallow` [source,ruby] @@ -2498,6 +2707,7 @@ it { is_expected.to be_truthy } it { expect(subject).to be_truthy } ---- +[#_enforcedstyle_-require_implicit_-rspecimplicitsubject] ==== `EnforcedStyle: require_implicit` [source,ruby] @@ -2522,6 +2732,7 @@ end it { expect(named_subject).to be_truthy } ---- +[#configurable-attributes-rspecimplicitsubject] === Configurable attributes |=== @@ -2532,16 +2743,18 @@ it { expect(named_subject).to be_truthy } | `single_line_only`, `single_statement_only`, `disallow`, `require_implicit` |=== +[#references-rspecimplicitsubject] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitSubject +[#rspecindexedlet] == RSpec/IndexedLet |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.20 @@ -2556,8 +2769,10 @@ is tested by this particular example. The configurable options `AllowedIdentifiers` and `AllowedPatterns` will also read those set in `Naming/VariableNumber`. +[#examples-rspecindexedlet] === Examples +[#_max_-1-_default__-rspecindexedlet] ==== `Max: 1 (default)` [source,ruby] @@ -2575,6 +2790,7 @@ let(:visible_item) { create(:item, visible: true) } let(:invisible_item) { create(:item, visible: false) } ---- +[#_max_-2_-rspecindexedlet] ==== `Max: 2` [source,ruby] @@ -2589,6 +2805,7 @@ let(:item_1) { create(:item) } let(:item_2) { create(:item) } ---- +[#_allowedidentifiers_-__item_1__-_item_2___-rspecindexedlet] ==== `AllowedIdentifiers: ['item_1', 'item_2']` [source,ruby] @@ -2598,6 +2815,7 @@ let(:item_1) { create(:item) } let(:item_2) { create(:item) } ---- +[#_allowedpatterns_-__item___-rspecindexedlet] ==== `AllowedPatterns: ['item']` [source,ruby] @@ -2607,6 +2825,7 @@ let(:item_1) { create(:item) } let(:item_2) { create(:item) } ---- +[#configurable-attributes-rspecindexedlet] === Configurable attributes |=== @@ -2625,10 +2844,12 @@ let(:item_2) { create(:item) } | Array |=== +[#references-rspecindexedlet] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IndexedLet +[#rspecinstancespy] == RSpec/InstanceSpy |=== @@ -2636,13 +2857,14 @@ let(:item_2) { create(:item) } | Enabled | Yes -| Yes +| Always | 1.12 | - |=== Checks for `instance_double` used with `have_received`. +[#examples-rspecinstancespy] === Examples [source,ruby] @@ -2660,10 +2882,12 @@ it do end ---- +[#references-rspecinstancespy] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceSpy +[#rspecinstancevariable] == RSpec/InstanceVariable |=== @@ -2683,6 +2907,7 @@ will configure the cop to only register offenses on instance variable usage if the instance variable is also assigned within the spec +[#examples-rspecinstancevariable] === Examples [source,ruby] @@ -2700,13 +2925,14 @@ describe MyClass do end ---- +[#with-assignmentonly-configuration-rspecinstancevariable] ==== with AssignmentOnly configuration [source,ruby] ---- # rubocop.yml # RSpec/InstanceVariable: -# AssignmentOnly: false +# AssignmentOnly: true # bad describe MyClass do @@ -2726,6 +2952,7 @@ describe MyClass do end ---- +[#configurable-attributes-rspecinstancevariable] === Configurable attributes |=== @@ -2736,27 +2963,70 @@ end | Boolean |=== +[#references-rspecinstancevariable] === References * https://rspec.rubystyle.guide/#instance-variables * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable -== RSpec/ItBehavesLike +[#rspecisexpectedspecify] +== RSpec/IsExpectedSpecify |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed | Enabled | Yes +| Always +| 2.27 +| - +|=== + +Check for `specify` with `is_expected` and one-liner expectations. + +[#examples-rspecisexpectedspecify] +=== Examples + +[source,ruby] +---- +# bad +specify { is_expected.to be_truthy } + +# good +it { is_expected.to be_truthy } + +# good +specify do + # ... +end +specify { expect(sqrt(4)).to eq(2) } +---- + +[#references-rspecisexpectedspecify] +=== References + +* https://rspec.rubystyle.guide/#it-and-specify +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IsExpectedSpecify + +[#rspecitbehaveslike] +== RSpec/ItBehavesLike + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Enabled | Yes +| Always | 1.13 | - |=== Checks that only one `it_behaves_like` style is used. +[#examples-rspecitbehaveslike] === Examples +[#_enforcedstyle_-it_behaves_like_-_default_-rspecitbehaveslike] ==== `EnforcedStyle: it_behaves_like` (default) [source,ruby] @@ -2768,6 +3038,7 @@ it_should_behave_like 'a foo' it_behaves_like 'a foo' ---- +[#_enforcedstyle_-it_should_behave_like_-rspecitbehaveslike] ==== `EnforcedStyle: it_should_behave_like` [source,ruby] @@ -2779,6 +3050,7 @@ it_behaves_like 'a foo' it_should_behave_like 'a foo' ---- +[#configurable-attributes-rspecitbehaveslike] === Configurable attributes |=== @@ -2789,10 +3061,12 @@ it_should_behave_like 'a foo' | `it_behaves_like`, `it_should_behave_like` |=== +[#references-rspecitbehaveslike] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ItBehavesLike +[#rspeciteratedexpectation] == RSpec/IteratedExpectation |=== @@ -2807,6 +3081,7 @@ it_should_behave_like 'a foo' Check that `all` matcher is used instead of iterating over an array. +[#examples-rspeciteratedexpectation] === Examples [source,ruby] @@ -2822,10 +3097,12 @@ it 'validates users' do end ---- +[#references-rspeciteratedexpectation] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IteratedExpectation +[#rspecleadingsubject] == RSpec/LeadingSubject |=== @@ -2833,13 +3110,14 @@ end | Enabled | Yes -| Yes +| Always | 1.7 | 1.14 |=== Enforce that subject is the first definition in the test. +[#examples-rspecleadingsubject] === Examples [source,ruby] @@ -2869,11 +3147,13 @@ it { expect_something } it { expect_something_else } ---- +[#references-rspecleadingsubject] === References * https://rspec.rubystyle.guide/#leading-subject * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LeadingSubject +[#rspecleakyconstantdeclaration] == RSpec/LeakyConstantDeclaration |=== @@ -2900,8 +3180,10 @@ Even worse when a class that exists in the codebase is reopened. Anonymous classes are fine, since they don't result in global namespace name clashes. +[#examples-rspecleakyconstantdeclaration] === Examples +[#constants-leak-between-examples-rspecleakyconstantdeclaration] ==== Constants leak between examples [source,ruby] @@ -2985,12 +3267,14 @@ describe SomeClass do end ---- +[#references-rspecleakyconstantdeclaration] === References * https://rspec.rubystyle.guide/#declare-constants * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LeakyConstantDeclaration * https://rspec.info/features/3-12/rspec-mocks/mutating-constants +[#rspecletbeforeexamples] == RSpec/LetBeforeExamples |=== @@ -2998,13 +3282,14 @@ end | Enabled | Yes -| Yes +| Command-line only | 1.16 -| 1.22 +| 2.31 |=== Checks for `let` definitions that come after an example. +[#examples-rspecletbeforeexamples] === Examples [source,ruby] @@ -3035,10 +3320,12 @@ it 'checks what some does' do end ---- +[#references-rspecletbeforeexamples] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetBeforeExamples +[#rspecletsetup] == RSpec/LetSetup |=== @@ -3053,6 +3340,7 @@ end Checks unreferenced `let!` calls being used for test setup. +[#examples-rspecletsetup] === Examples [source,ruby] @@ -3078,18 +3366,20 @@ it 'counts widgets' do end ---- +[#references-rspecletsetup] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup +[#rspecmatcharray] == RSpec/MatchArray |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.19 | - |=== @@ -3100,6 +3390,7 @@ This cop checks for the following: - Prefer `contain_exactly` when matching an array with values. - Prefer `eq` when using `match_array` with an empty array literal. +[#examples-rspecmatcharray] === Examples [source,ruby] @@ -3117,10 +3408,12 @@ it { is_expected.to match_array([content] + array) } it { is_expected.to match_array(%w(tremble in fear foolish mortals)) } ---- +[#references-rspecmatcharray] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MatchArray +[#rspecmessagechain] == RSpec/MessageChain |=== @@ -3135,6 +3428,7 @@ it { is_expected.to match_array(%w(tremble in fear foolish mortals)) } Check that chains of messages are not being stubbed. +[#examples-rspecmessagechain] === Examples [source,ruby] @@ -3147,10 +3441,12 @@ thing = Thing.new(baz: 42) allow(foo).to receive(:bar).and_return(thing) ---- +[#references-rspecmessagechain] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MessageChain +[#rspecmessageexpectation] == RSpec/MessageExpectation |=== @@ -3168,8 +3464,10 @@ Checks for consistent message expectation style. This cop can be configured in your configuration using the `EnforcedStyle` option and supports `--auto-gen-config`. +[#examples-rspecmessageexpectation] === Examples +[#_enforcedstyle_-allow_-_default_-rspecmessageexpectation] ==== `EnforcedStyle: allow` (default) [source,ruby] @@ -3181,6 +3479,7 @@ expect(foo).to receive(:bar) allow(foo).to receive(:bar) ---- +[#_enforcedstyle_-expect_-rspecmessageexpectation] ==== `EnforcedStyle: expect` [source,ruby] @@ -3192,6 +3491,7 @@ allow(foo).to receive(:bar) expect(foo).to receive(:bar) ---- +[#configurable-attributes-rspecmessageexpectation] === Configurable attributes |=== @@ -3202,10 +3502,12 @@ expect(foo).to receive(:bar) | `allow`, `expect` |=== +[#references-rspecmessageexpectation] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MessageExpectation +[#rspecmessagespies] == RSpec/MessageSpies |=== @@ -3223,8 +3525,10 @@ Checks that message expectations are set using spies. This cop can be configured in your configuration using the `EnforcedStyle` option and supports `--auto-gen-config`. +[#examples-rspecmessagespies] === Examples +[#_enforcedstyle_-have_received_-_default_-rspecmessagespies] ==== `EnforcedStyle: have_received` (default) [source,ruby] @@ -3239,6 +3543,7 @@ do_something expect(foo).to have_received(:bar) ---- +[#_enforcedstyle_-receive_-rspecmessagespies] ==== `EnforcedStyle: receive` [source,ruby] @@ -3253,6 +3558,7 @@ expect(foo).to receive(:bar) do_something ---- +[#configurable-attributes-rspecmessagespies] === Configurable attributes |=== @@ -3263,18 +3569,20 @@ do_something | `have_received`, `receive` |=== +[#references-rspecmessagespies] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MessageSpies +[#rspecmetadatastyle] == RSpec/MetadataStyle |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.24 | - |=== @@ -3285,8 +3593,10 @@ This cop does not support autocorrection in the case of `EnforcedStyle: hash` where the trailing metadata type is ambiguous. (e.g. `describe 'Something', :a, b`) +[#examples-rspecmetadatastyle] === Examples +[#enforcedstyle_-symbol-_default_-rspecmetadatastyle] ==== EnforcedStyle: symbol (default) [source,ruby] @@ -3298,6 +3608,7 @@ describe 'Something', a: true describe 'Something', :a ---- +[#enforcedstyle_-hash-rspecmetadatastyle] ==== EnforcedStyle: hash [source,ruby] @@ -3309,6 +3620,7 @@ describe 'Something', :a describe 'Something', a: true ---- +[#configurable-attributes-rspecmetadatastyle] === Configurable attributes |=== @@ -3319,10 +3631,12 @@ describe 'Something', a: true | `hash`, `symbol` |=== +[#references-rspecmetadatastyle] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MetadataStyle +[#rspecmissingexamplegroupargument] == RSpec/MissingExampleGroupArgument |=== @@ -3337,6 +3651,7 @@ describe 'Something', a: true Checks that the first argument to an example group is not empty. +[#examples-rspecmissingexamplegroupargument] === Examples [source,ruby] @@ -3356,10 +3671,51 @@ describe "A feature example" do end ---- +[#references-rspecmissingexamplegroupargument] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument +[#rspecmissingexpectationtargetmethod] +== RSpec/MissingExpectationTargetMethod + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Enabled +| Yes +| No +| 3.0 +| - +|=== + +Checks if `.to`, `not_to` or `to_not` are used. + +The RSpec::Expectations::ExpectationTarget must use `to`, `not_to` or +`to_not` to run. Therefore, this cop checks if other methods are used. + +[#examples-rspecmissingexpectationtargetmethod] +=== Examples + +[source,ruby] +---- +# bad +expect(something).kind_of? Foo +is_expected == 42 +expect{something}.eq? BarError + +# good +expect(something).to be_a Foo +is_expected.to eq 42 +expect{something}.to raise_error BarError +---- + +[#references-rspecmissingexpectationtargetmethod] +=== References + +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExpectationTargetMethod + +[#rspecmultipledescribes] == RSpec/MultipleDescribes |=== @@ -3377,6 +3733,7 @@ Checks for multiple top-level example groups. Multiple descriptions for the same class or module should either be nested or separated into different test files. +[#examples-rspecmultipledescribes] === Examples [source,ruby] @@ -3396,10 +3753,12 @@ describe MyClass do end ---- +[#references-rspecmultipledescribes] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleDescribes +[#rspecmultipleexpectations] == RSpec/MultipleExpectations |=== @@ -3417,6 +3776,7 @@ Checks if examples contain too many `expect` calls. This cop is configurable using the `Max` option and works with `--auto-gen-config`. +[#examples-rspecmultipleexpectations] === Examples [source,ruby] @@ -3441,41 +3801,54 @@ describe UserCreator do end ---- +[#_aggregate_failures_-true_-_default_-rspecmultipleexpectations] ==== `aggregate_failures: true` (default) [source,ruby] ---- # good - the cop ignores when RSpec aggregates failures - describe UserCreator do - it 'builds a user', :aggregate_failures do - expect(user.name).to eq("John") - expect(user.age).to eq(22) - end - end +describe UserCreator do + it 'builds a user', :aggregate_failures do + expect(user.name).to eq("John") + expect(user.age).to eq(22) + end +end ---- +[#_aggregate_failures_-false_-rspecmultipleexpectations] ==== `aggregate_failures: false` [source,ruby] ---- # Detected as an offense - describe UserCreator do - it 'builds a user', aggregate_failures: false do - expect(user.name).to eq("John") - expect(user.age).to eq(22) - end - end +describe UserCreator do + it 'builds a user', aggregate_failures: false do + expect(user.name).to eq("John") + expect(user.age).to eq(22) + end +end ---- -==== configuration +[#_max_-1_-_default_-rspecmultipleexpectations] +==== `Max: 1` (default) [source,ruby] ---- -# .rubocop.yml -# RSpec/MultipleExpectations: -# Max: 2 +# bad +describe UserCreator do + it 'builds a user' do + expect(user.name).to eq("John") + expect(user.age).to eq(22) + end +end +---- + +[#_max_-2_-rspecmultipleexpectations] +==== `Max: 2` -# not flagged by rubocop +[source,ruby] +---- +# good describe UserCreator do it 'builds a user' do expect(user.name).to eq("John") @@ -3484,6 +3857,7 @@ describe UserCreator do end ---- +[#configurable-attributes-rspecmultipleexpectations] === Configurable attributes |=== @@ -3494,12 +3868,14 @@ end | Integer |=== +[#references-rspecmultipleexpectations] === References * https://rspec.rubystyle.guide/#expectation-per-example * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleExpectations * http://betterspecs.org/#single +[#rspecmultiplememoizedhelpers] == RSpec/MultipleMemoizedHelpers |=== @@ -3518,6 +3894,7 @@ This cop is configurable using the `Max` option and the `AllowSubject` which will configure the cop to only register offenses on calls to `let` and not calls to `subject`. +[#examples-rspecmultiplememoizedhelpers] === Examples [source,ruby] @@ -3568,6 +3945,7 @@ describe MyClass do end ---- +[#when-disabling-allowsubject-configuration-rspecmultiplememoizedhelpers] ==== when disabling AllowSubject configuration [source,ruby] @@ -3587,6 +3965,7 @@ describe MyClass do end ---- +[#with-max-configuration-rspecmultiplememoizedhelpers] ==== with Max configuration [source,ruby] @@ -3602,6 +3981,7 @@ describe MyClass do end ---- +[#configurable-attributes-rspecmultiplememoizedhelpers] === Configurable attributes |=== @@ -3616,11 +3996,13 @@ end | Integer |=== +[#references-rspecmultiplememoizedhelpers] === References * https://rspec.rubystyle.guide/#let-blocks * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleMemoizedHelpers +[#rspecmultiplesubjects] == RSpec/MultipleSubjects |=== @@ -3628,7 +4010,7 @@ end | Enabled | Yes -| Yes +| Always | 1.16 | - |=== @@ -3651,6 +4033,7 @@ duplication: This is enough of an edge case that people can just move this to a `before` hook on their own +[#examples-rspecmultiplesubjects] === Examples [source,ruby] @@ -3682,10 +4065,12 @@ describe Foo do end ---- +[#references-rspecmultiplesubjects] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleSubjects +[#rspecnamedsubject] == RSpec/NamedSubject |=== @@ -3711,8 +4096,10 @@ This cop can be configured in your configuration using `EnforcedStyle`, and `IgnoreSharedExamples` which will not report offenses for implicit subjects in shared example groups. +[#examples-rspecnamedsubject] === Examples +[#_enforcedstyle_-always_-_default_-rspecnamedsubject] ==== `EnforcedStyle: always` (default) [source,ruby] @@ -3743,6 +4130,7 @@ RSpec.describe User do end ---- +[#_enforcedstyle_-named_only_-rspecnamedsubject] ==== `EnforcedStyle: named_only` [source,ruby] @@ -3782,6 +4170,7 @@ RSpec.describe User do end ---- +[#configurable-attributes-rspecnamedsubject] === Configurable attributes |=== @@ -3796,11 +4185,13 @@ end | Boolean |=== +[#references-rspecnamedsubject] === References * https://rspec.rubystyle.guide/#use-subject * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NamedSubject +[#rspecnestedgroups] == RSpec/NestedGroups |=== @@ -3818,6 +4209,7 @@ Checks for nested example groups. This cop is configurable using the `Max` option and supports `--auto-gen-config`. +[#examples-rspecnestedgroups] === Examples [source,ruby] @@ -3867,6 +4259,7 @@ context 'using some feature as an admin' do end ---- +[#_max_-3_-_default_-rspecnestedgroups] ==== `Max: 3` (default) [source,ruby] @@ -3882,6 +4275,7 @@ describe Foo do end ---- +[#_max_-2_-rspecnestedgroups] ==== `Max: 2` [source,ruby] @@ -3897,6 +4291,7 @@ describe Foo do end ---- +[#_allowedgroups_-__-_default__-rspecnestedgroups] ==== `AllowedGroups: [] (default)` [source,ruby] @@ -3909,6 +4304,7 @@ describe Foo do # <-- nested groups 1 end ---- +[#_allowedgroups_-_path__-rspecnestedgroups] ==== `AllowedGroups: [path]` [source,ruby] @@ -3921,6 +4317,7 @@ describe Foo do # <-- nested groups 1 end ---- +[#configurable-attributes-rspecnestedgroups] === Configurable attributes |=== @@ -3935,16 +4332,18 @@ end | Array |=== +[#references-rspecnestedgroups] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NestedGroups +[#rspecnoexpectationexample] == RSpec/NoExpectationExample |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | No | No | 2.13 @@ -3969,6 +4368,7 @@ This cop can be customized with an allowed expectation methods pattern with an `AllowedPatterns` option. ^expect_ and ^assert_ are allowed by default. +[#examples-rspecnoexpectationexample] === Examples [source,ruby] @@ -3984,6 +4384,7 @@ it do end ---- +[#_allowedpatterns_-configuration-rspecnoexpectationexample] ==== `AllowedPatterns` configuration [source,ruby] @@ -4012,6 +4413,7 @@ it do end ---- +[#configurable-attributes-rspecnoexpectationexample] === Configurable attributes |=== @@ -4022,10 +4424,12 @@ end | Array |=== +[#references-rspecnoexpectationexample] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NoExpectationExample +[#rspecnottonot] == RSpec/NotToNot |=== @@ -4033,15 +4437,17 @@ end | Enabled | Yes -| Yes +| Always | 1.4 | - |=== Checks for consistent method usage for negating expectations. +[#examples-rspecnottonot] === Examples +[#_enforcedstyle_-not_to_-_default_-rspecnottonot] ==== `EnforcedStyle: not_to` (default) [source,ruby] @@ -4057,6 +4463,7 @@ it '...' do end ---- +[#_enforcedstyle_-to_not_-rspecnottonot] ==== `EnforcedStyle: to_not` [source,ruby] @@ -4072,6 +4479,7 @@ it '...' do end ---- +[#configurable-attributes-rspecnottonot] === Configurable attributes |=== @@ -4082,10 +4490,12 @@ end | `not_to`, `to_not` |=== +[#references-rspecnottonot] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NotToNot +[#rspecoverwritingsetup] == RSpec/OverwritingSetup |=== @@ -4100,6 +4510,7 @@ end Checks if there is a let/subject that overwrites an existing one. +[#examples-rspecoverwritingsetup] === Examples [source,ruby] @@ -4121,10 +4532,12 @@ let(:baz) { baz } let!(:other) { other } ---- +[#references-rspecoverwritingsetup] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/OverwritingSetup +[#rspecpending] == RSpec/Pending |=== @@ -4139,6 +4552,7 @@ let!(:other) { other } Checks for any pending or skipped examples. +[#examples-rspecpending] === Examples [source,ruby] @@ -4170,16 +4584,18 @@ describe MyClass do end ---- +[#references-rspecpending] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Pending +[#rspecpendingwithoutreason] == RSpec/PendingWithoutReason |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.16 @@ -4188,6 +4604,7 @@ end Checks for pending or skipped examples without reason. +[#examples-rspecpendingwithoutreason] === Examples [source,ruby] @@ -4244,10 +4661,12 @@ it 'does something', skip: 'reason' do end ---- +[#references-rspecpendingwithoutreason] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/PendingWithoutReason +[#rspecpredicatematcher] == RSpec/PredicateMatcher |=== @@ -4255,7 +4674,7 @@ end | Enabled | Yes -| Yes (Unsafe) +| Always (Unsafe) | 1.16 | - |=== @@ -4266,8 +4685,10 @@ RSpec defines magic matchers for predicate methods. This cop recommends to use the predicate matcher instead of using predicate method directly. +[#examples-rspecpredicatematcher] === Examples +[#strict_-true_-enforcedstyle_-inflected-_default_-rspecpredicatematcher] ==== Strict: true, EnforcedStyle: inflected (default) [source,ruby] @@ -4282,6 +4703,7 @@ expect(foo).to be_something expect(foo.something?).to be(true) ---- +[#strict_-false_-enforcedstyle_-inflected-rspecpredicatematcher] ==== Strict: false, EnforcedStyle: inflected [source,ruby] @@ -4294,6 +4716,7 @@ expect(foo.something?).to be(true) expect(foo).to be_something ---- +[#strict_-true_-enforcedstyle_-explicit-rspecpredicatematcher] ==== Strict: true, EnforcedStyle: explicit [source,ruby] @@ -4316,6 +4739,7 @@ expect(foo.something?(<<~TEXT)).to be(true) TEXT ---- +[#strict_-false_-enforcedstyle_-explicit-rspecpredicatematcher] ==== Strict: false, EnforcedStyle: explicit [source,ruby] @@ -4327,6 +4751,7 @@ expect(foo).to be_something expect(foo.something?).to be_truthy ---- +[#configurable-attributes-rspecpredicatematcher] === Configurable attributes |=== @@ -4345,11 +4770,13 @@ expect(foo.something?).to be_truthy | Array |=== +[#references-rspecpredicatematcher] === References * https://rspec.rubystyle.guide/#predicate-matchers * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/PredicateMatcher +[#rspecreceivecounts] == RSpec/ReceiveCounts |=== @@ -4357,13 +4784,14 @@ expect(foo.something?).to be_truthy | Enabled | Yes -| Yes +| Always | 1.26 | - |=== Check for `once` and `twice` receive counts matchers usage. +[#examples-rspecreceivecounts] === Examples [source,ruby] @@ -4385,30 +4813,34 @@ expect(foo).to receive(:bar).at_most(:once) expect(foo).to receive(:bar).at_most(:twice).times ---- +[#references-rspecreceivecounts] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveCounts +[#rspecreceivemessages] == RSpec/ReceiveMessages |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes -| Yes (Unsafe) +| Always (Unsafe) | 2.23 | - |=== Checks for multiple messages stubbed on the same object. +[#safety-rspecreceivemessages] === Safety The autocorrection is marked as unsafe, because it may change the order of stubs. This in turn may cause e.g. variables to be called before they are defined. +[#examples-rspecreceivemessages] === Examples [source,ruby] @@ -4431,10 +4863,12 @@ before do end ---- +[#references-rspecreceivemessages] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveMessages +[#rspecreceivenever] == RSpec/ReceiveNever |=== @@ -4442,13 +4876,14 @@ end | Enabled | Yes -| Yes +| Always | 1.28 | - |=== Prefer `not_to receive(...)` over `receive(...).never`. +[#examples-rspecreceivenever] === Examples [source,ruby] @@ -4460,24 +4895,27 @@ expect(foo).to receive(:bar).never expect(foo).not_to receive(:bar) ---- +[#references-rspecreceivenever] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveNever +[#rspecredundantaround] == RSpec/RedundantAround |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.19 | - |=== Remove redundant `around` hook. +[#examples-rspecredundantaround] === Examples [source,ruby] @@ -4490,24 +4928,27 @@ end # good ---- +[#references-rspecredundantaround] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantAround +[#rspecredundantpredicatematcher] == RSpec/RedundantPredicateMatcher |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.26 | - |=== Checks for redundant predicate matcher. +[#examples-rspecredundantpredicatematcher] === Examples [source,ruby] @@ -4515,22 +4956,26 @@ Checks for redundant predicate matcher. # bad expect(foo).to be_exist(bar) expect(foo).not_to be_include(bar) +expect(foo).to be_all(bar) # good expect(foo).to exist(bar) expect(foo).not_to include(bar) +expect(foo).to all be(bar) ---- +[#references-rspecredundantpredicatematcher] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantPredicateMatcher +[#rspecremoveconst] == RSpec/RemoveConst |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.26 @@ -4539,6 +4984,7 @@ expect(foo).not_to include(bar) Checks that `remove_const` is not used in specs. +[#examples-rspecremoveconst] === Examples [source,ruby] @@ -4553,10 +4999,12 @@ before do end ---- +[#references-rspecremoveconst] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RemoveConst +[#rspecrepeateddescription] == RSpec/RepeatedDescription |=== @@ -4571,6 +5019,7 @@ end Check for repeated description strings in example groups. +[#examples-rspecrepeateddescription] === Examples [source,ruby] @@ -4609,10 +5058,12 @@ RSpec.describe User do end ---- +[#references-rspecrepeateddescription] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedDescription +[#rspecrepeatedexample] == RSpec/RepeatedExample |=== @@ -4627,6 +5078,7 @@ end Check for repeated examples within example groups. +[#examples-rspecrepeatedexample] === Examples [source,ruby] @@ -4640,10 +5092,12 @@ it 'validates the user' do end ---- +[#references-rspecrepeatedexample] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExample +[#rspecrepeatedexamplegroupbody] == RSpec/RepeatedExampleGroupBody |=== @@ -4658,6 +5112,7 @@ end Check for repeated describe and context block body. +[#examples-rspecrepeatedexamplegroupbody] === Examples [source,ruby] @@ -4699,10 +5154,12 @@ context Hash do end ---- +[#references-rspecrepeatedexamplegroupbody] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody +[#rspecrepeatedexamplegroupdescription] == RSpec/RepeatedExampleGroupDescription |=== @@ -4717,6 +5174,7 @@ end Check for repeated example group descriptions. +[#examples-rspecrepeatedexamplegroupdescription] === Examples [source,ruby] @@ -4758,10 +5216,12 @@ context 'when another case' do end ---- +[#references-rspecrepeatedexamplegroupdescription] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription +[#rspecrepeatedincludeexample] == RSpec/RepeatedIncludeExample |=== @@ -4776,6 +5236,7 @@ end Check for repeated include of shared examples. +[#examples-rspecrepeatedincludeexample] === Examples [source,ruby] @@ -4820,18 +5281,69 @@ context 'foo' do end ---- +[#references-rspecrepeatedincludeexample] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedIncludeExample -== RSpec/ReturnFromStub +[#rspecrepeatedsubjectcall] +== RSpec/RepeatedSubjectCall |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed | Enabled | Yes +| No +| 2.27 +| - +|=== + +Checks for repeated calls to subject missing that it is memoized. + +[#examples-rspecrepeatedsubjectcall] +=== Examples + +[source,ruby] +---- +# bad +it do + subject + expect { subject }.to not_change { A.count } +end + +it do + expect { subject }.to change { A.count } + expect { subject }.to not_change { A.count } +end + +# good +it do + expect { my_method }.to change { A.count } + expect { my_method }.to not_change { A.count } +end + +# also good +it do + expect { subject.a }.to change { A.count } + expect { subject.b }.to not_change { A.count } +end +---- + +[#references-rspecrepeatedsubjectcall] +=== References + +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedSubjectCall + +[#rspecreturnfromstub] +== RSpec/ReturnFromStub + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Enabled | Yes +| Always | 1.16 | 1.22 |=== @@ -4844,8 +5356,10 @@ are the result would be different This cop can be configured using the `EnforcedStyle` option +[#examples-rspecreturnfromstub] === Examples +[#_enforcedstyle_-and_return_-_default_-rspecreturnfromstub] ==== `EnforcedStyle: and_return` (default) [source,ruby] @@ -4861,6 +5375,7 @@ expect(Foo).to receive(:bar).and_return("baz") allow(Foo).to receive(:bar) { bar.baz } ---- +[#_enforcedstyle_-block_-rspecreturnfromstub] ==== `EnforcedStyle: block` [source,ruby] @@ -4876,6 +5391,7 @@ expect(Foo).to receive(:bar) { "baz" } allow(Foo).to receive(:bar).and_return(bar.baz) ---- +[#configurable-attributes-rspecreturnfromstub] === Configurable attributes |=== @@ -4886,10 +5402,12 @@ allow(Foo).to receive(:bar).and_return(bar.baz) | `and_return`, `block` |=== +[#references-rspecreturnfromstub] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReturnFromStub +[#rspecscatteredlet] == RSpec/ScatteredLet |=== @@ -4897,15 +5415,16 @@ allow(Foo).to receive(:bar).and_return(bar.baz) | Enabled | Yes -| Yes +| Command-line only | 1.14 -| 1.39 +| 2.31 |=== Checks for let scattered across the example group. Group lets together +[#examples-rspecscatteredlet] === Examples [source,ruby] @@ -4929,10 +5448,12 @@ describe Foo do end ---- +[#references-rspecscatteredlet] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredLet +[#rspecscatteredsetup] == RSpec/ScatteredSetup |=== @@ -4940,15 +5461,16 @@ end | Enabled | Yes -| Yes +| Command-line only | 1.10 -| - +| 2.31 |=== Checks for setup scattered across multiple hooks in an example group. Unify `before`, `after`, and `around` hooks when possible. +[#examples-rspecscatteredsetup] === Examples [source,ruby] @@ -4968,10 +5490,12 @@ describe Foo do end ---- +[#references-rspecscatteredsetup] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredSetup +[#rspecsharedcontext] == RSpec/SharedContext |=== @@ -4979,7 +5503,7 @@ end | Enabled | Yes -| Yes +| Always | 1.13 | - |=== @@ -4989,6 +5513,7 @@ Checks for proper shared_context and shared_examples usage. If there are no examples defined, use shared_context. If there is no setup defined, use shared_examples. +[#examples-rspecsharedcontext] === Examples [source,ruby] @@ -5037,10 +5562,12 @@ RSpec.shared_context 'only setup here' do end ---- +[#references-rspecsharedcontext] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SharedContext +[#rspecsharedexamples] == RSpec/SharedExamples |=== @@ -5048,7 +5575,7 @@ end | Enabled | Yes -| Yes +| Always | 1.25 | 2.26 |=== @@ -5059,8 +5586,10 @@ Enforces either `string` or `symbol` for shared example names. This cop can be configured using the `EnforcedStyle` option +[#examples-rspecsharedexamples] === Examples +[#_enforcedstyle_-string_-_default_-rspecsharedexamples] ==== `EnforcedStyle: string` (default) [source,ruby] @@ -5080,6 +5609,7 @@ shared_examples_for 'foo bar baz' include_examples 'foo bar baz' ---- +[#_enforcedstyle_-symbol_-rspecsharedexamples] ==== `EnforcedStyle: symbol` [source,ruby] @@ -5099,6 +5629,7 @@ shared_examples_for :foo_bar_baz include_examples :foo_bar_baz ---- +[#configurable-attributes-rspecsharedexamples] === Configurable attributes |=== @@ -5109,10 +5640,12 @@ include_examples :foo_bar_baz | `string`, `symbol` |=== +[#references-rspecsharedexamples] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SharedExamples +[#rspecsingleargumentmessagechain] == RSpec/SingleArgumentMessageChain |=== @@ -5120,13 +5653,14 @@ include_examples :foo_bar_baz | Enabled | Yes -| Yes +| Always | 1.9 | 1.10 |=== Checks that chains of messages contain more than one element. +[#examples-rspecsingleargumentmessagechain] === Examples [source,ruby] @@ -5142,16 +5676,18 @@ allow(foo).to receive(:bar, :baz) allow(foo).to receive("bar.baz") ---- +[#references-rspecsingleargumentmessagechain] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SingleArgumentMessageChain +[#rspecskipblockinsideexample] == RSpec/SkipBlockInsideExample |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.19 @@ -5160,6 +5696,7 @@ allow(foo).to receive("bar.baz") Checks for passing a block to `skip` within examples. +[#examples-rspecskipblockinsideexample] === Examples [source,ruby] @@ -5182,24 +5719,27 @@ skip 'not yet implemented' do end ---- +[#references-rspecskipblockinsideexample] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SkipBlockInsideExample +[#rspecsortmetadata] == RSpec/SortMetadata |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending -| Yes +| Enabled | Yes +| Always | 2.14 | - |=== Sort RSpec metadata alphabetically. +[#examples-rspecsortmetadata] === Examples [source,ruby] @@ -5215,16 +5755,18 @@ context 'Something', baz: true, foo: 'bar' it 'works', :a, :b, baz: true, foo: 'bar' ---- +[#references-rspecsortmetadata] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SortMetadata +[#rspecspecfilepathformat] == RSpec/SpecFilePathFormat |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.24 @@ -5233,6 +5775,7 @@ it 'works', :a, :b, baz: true, foo: 'bar' Checks that spec file paths are consistent and well-formed. +[#examples-rspecspecfilepathformat] === Examples [source,ruby] @@ -5247,6 +5790,7 @@ my_class_method_spec.rb # describe MyClass, '#method' my_class/method_spec.rb # describe MyClass, '#method' ---- +[#_customtransform_-_rubocop__rubocop_-rspec__rspec__-_default_-rspecspecfilepathformat] ==== `CustomTransform: {RuboCop=>rubocop, RSpec=>rspec}` (default) [source,ruby] @@ -5256,6 +5800,7 @@ rubocop_spec.rb # describe RuboCop rspec_spec.rb # describe RSpec ---- +[#_ignoremethods_-false_-_default_-rspecspecfilepathformat] ==== `IgnoreMethods: false` (default) [source,ruby] @@ -5264,6 +5809,7 @@ rspec_spec.rb # describe RSpec my_class_spec.rb # describe MyClass, '#method' ---- +[#_ignoremethods_-true_-rspecspecfilepathformat] ==== `IgnoreMethods: true` [source,ruby] @@ -5272,6 +5818,7 @@ my_class_spec.rb # describe MyClass, '#method' my_class_spec.rb # describe MyClass, '#method' ---- +[#_ignoremetadata_-_type__routing__-_default_-rspecspecfilepathformat] ==== `IgnoreMetadata: {type=>routing}` (default) [source,ruby] @@ -5280,6 +5827,7 @@ my_class_spec.rb # describe MyClass, '#method' whatever_spec.rb # describe MyClass, type: :routing do; end ---- +[#configurable-attributes-rspecspecfilepathformat] === Configurable attributes |=== @@ -5306,16 +5854,18 @@ whatever_spec.rb # describe MyClass, type: :routing do; end | |=== +[#references-rspecspecfilepathformat] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathFormat +[#rspecspecfilepathsuffix] == RSpec/SpecFilePathSuffix |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.24 @@ -5324,6 +5874,7 @@ whatever_spec.rb # describe MyClass, type: :routing do; end Checks that spec file paths suffix are consistent and well-formed. +[#examples-rspecspecfilepathsuffix] === Examples [source,ruby] @@ -5340,6 +5891,7 @@ my_class_spec.rb # describe MyClass spec/models/user.rb # shared_examples_for 'foo' ---- +[#configurable-attributes-rspecspecfilepathsuffix] === Configurable attributes |=== @@ -5350,10 +5902,51 @@ spec/models/user.rb # shared_examples_for 'foo' | Array |=== +[#references-rspecspecfilepathsuffix] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathSuffix +[#rspecstringasinstancedoubleconstant] +== RSpec/StringAsInstanceDoubleConstant + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| No +| Always (Unsafe) +| 3.1 +| - +|=== + +Do not use a string as `instance_double` constant. + +[#safety-rspecstringasinstancedoubleconstant] +=== Safety + +This cop is unsafe because the correction requires loading the class. +Loading before stubbing causes RSpec to only allow instance methods +to be stubbed. + +[#examples-rspecstringasinstancedoubleconstant] +=== Examples + +[source,ruby] +---- +# bad +instance_double('User', name: 'John') + +# good +instance_double(User, name: 'John') +---- + +[#references-rspecstringasinstancedoubleconstant] +=== References + +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/StringAsInstanceDoubleConstant + +[#rspecstubbedmock] == RSpec/StubbedMock |=== @@ -5368,6 +5961,7 @@ spec/models/user.rb # shared_examples_for 'foo' Checks that message expectations do not have a configured response. +[#examples-rspecstubbedmock] === Examples [source,ruby] @@ -5380,16 +5974,18 @@ allow(foo).to receive(:bar).with(42).and_return("hello world") expect(foo).to receive(:bar).with(42) ---- +[#references-rspecstubbedmock] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/StubbedMock +[#rspecsubjectdeclaration] == RSpec/SubjectDeclaration |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes | No | 2.5 @@ -5398,6 +5994,7 @@ expect(foo).to receive(:bar).with(42) Ensure that subject is defined using subject helper. +[#examples-rspecsubjectdeclaration] === Examples [source,ruby] @@ -5416,10 +6013,12 @@ let(:subject, &block) subject(:test_subject) { foo } ---- +[#references-rspecsubjectdeclaration] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SubjectDeclaration +[#rspecsubjectstub] == RSpec/SubjectStub |=== @@ -5437,6 +6036,7 @@ Checks for stubbed test subjects. Checks nested subject stubs for innermost subject definition when subject is also defined in parent example groups. +[#examples-rspecsubjectstub] === Examples [source,ruby] @@ -5475,6 +6075,7 @@ describe Article do end ---- +[#references-rspecsubjectstub] === References * https://rspec.rubystyle.guide/#dont-stub-subject @@ -5482,6 +6083,71 @@ end * https://robots.thoughtbot.com/don-t-stub-the-system-under-test * https://penelope.zone/2015/12/27/introducing-rspec-smells-and-where-to-find-them.html#smell-1-stubjec +[#rspecundescriptiveliteralsdescription] +== RSpec/UndescriptiveLiteralsDescription + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Enabled +| Yes +| No +| 2.29 +| - +|=== + +Description should be descriptive. + +If example group or example contains only `execute string`, numbers +and regular expressions, the description is not clear. + +[#examples-rspecundescriptiveliteralsdescription] +=== Examples + +[source,ruby] +---- +# bad +describe `time` do + # ... +end + +# bad +context /when foo/ do + # ... +end + +# bad +it 10000 do + # ... +end + +# good +describe Foo do + # ... +end + +# good +describe '#foo' do + # ... +end + +# good +context "when #{foo} is bar" do + # ... +end + +# good +it 'does something' do + # ... +end +---- + +[#references-rspecundescriptiveliteralsdescription] +=== References + +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UndescriptiveLiteralsDescription + +[#rspecunspecifiedexception] == RSpec/UnspecifiedException |=== @@ -5500,6 +6166,7 @@ Enforces one of an Exception type, a string, or a regular expression to match against the exception message as a parameter to `raise_error` +[#examples-rspecunspecifiedexception] === Examples [source,ruby] @@ -5525,10 +6192,12 @@ expect { expect { do_something }.not_to raise_error ---- +[#references-rspecunspecifiedexception] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UnspecifiedException +[#rspecvariabledefinition] == RSpec/VariableDefinition |=== @@ -5536,15 +6205,17 @@ expect { do_something }.not_to raise_error | Enabled | Yes -| Yes +| Always | 1.40 | - |=== Checks that memoized helpers names are symbols or strings. +[#examples-rspecvariabledefinition] === Examples +[#enforcedstyle_-symbols-_default_-rspecvariabledefinition] ==== EnforcedStyle: symbols (default) [source,ruby] @@ -5558,6 +6229,7 @@ subject(:user) { create_user } let(:user_name) { 'Adam' } ---- +[#enforcedstyle_-strings-rspecvariabledefinition] ==== EnforcedStyle: strings [source,ruby] @@ -5571,6 +6243,7 @@ subject('user') { create_user } let('user_name') { 'Adam' } ---- +[#configurable-attributes-rspecvariabledefinition] === Configurable attributes |=== @@ -5581,10 +6254,12 @@ let('user_name') { 'Adam' } | `symbols`, `strings` |=== +[#references-rspecvariabledefinition] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableDefinition +[#rspecvariablename] == RSpec/VariableName |=== @@ -5602,8 +6277,10 @@ Checks that memoized helper names use the configured style. Variables can be excluded from checking using the `AllowedPatterns` option. +[#examples-rspecvariablename] === Examples +[#enforcedstyle_-snake_case-_default_-rspecvariablename] ==== EnforcedStyle: snake_case (default) [source,ruby] @@ -5617,6 +6294,7 @@ subject(:user_name_1) { 'Adam' } let(:user_name_2) { 'Adam' } ---- +[#enforcedstyle_-camelcase-rspecvariablename] ==== EnforcedStyle: camelCase [source,ruby] @@ -5630,6 +6308,7 @@ subject(:userName1) { 'Adam' } let(:userName2) { 'Adam' } ---- +[#allowedpatterns-configuration-rspecvariablename] ==== AllowedPatterns configuration [source,ruby] @@ -5648,6 +6327,7 @@ subject(:userFood_1) { 'spaghetti' } let(:userFood_2) { 'fettuccine' } ---- +[#configurable-attributes-rspecvariablename] === Configurable attributes |=== @@ -5662,18 +6342,20 @@ let(:userFood_2) { 'fettuccine' } | Array |=== +[#references-rspecvariablename] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName +[#rspecverifieddoublereference] == RSpec/VerifiedDoubleReference |=== | Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed -| Pending +| Enabled | Yes -| Yes (Unsafe) +| Always (Unsafe) | 2.10.0 | 2.12 |=== @@ -5685,8 +6367,10 @@ Only investigates references that are one of the supported styles. This cop can be configured in your configuration using the `EnforcedStyle` option and supports `--auto-gen-config`. +[#examples-rspecverifieddoublereference] === Examples +[#_enforcedstyle_-constant_-_default_-rspecverifieddoublereference] ==== `EnforcedStyle: constant` (default) [source,ruby] @@ -5702,6 +6386,7 @@ let(:foo) do end ---- +[#_enforcedstyle_-string_-rspecverifieddoublereference] ==== `EnforcedStyle: string` [source,ruby] @@ -5717,6 +6402,7 @@ let(:foo) do end ---- +[#reference-is-not-in-the-supported-style-list_-no-enforcement-rspecverifieddoublereference] ==== Reference is not in the supported style list. No enforcement [source,ruby] @@ -5727,6 +6413,7 @@ let(:foo) do end ---- +[#configurable-attributes-rspecverifieddoublereference] === Configurable attributes |=== @@ -5737,11 +6424,13 @@ end | `constant`, `string` |=== +[#references-rspecverifieddoublereference] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VerifiedDoubleReference * https://rspec.info/features/3-12/rspec-mocks/verifying-doubles +[#rspecverifieddoubles] == RSpec/VerifiedDoubles |=== @@ -5756,6 +6445,7 @@ end Prefer using verifying doubles over normal doubles. +[#examples-rspecverifieddoubles] === Examples [source,ruby] @@ -5776,6 +6466,7 @@ let(:foo) do end ---- +[#configurable-attributes-rspecverifieddoubles] === Configurable attributes |=== @@ -5790,12 +6481,14 @@ end | Boolean |=== +[#references-rspecverifieddoubles] === References * https://rspec.rubystyle.guide/#doubles * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VerifiedDoubles * https://rspec.info/features/3-12/rspec-mocks/verifying-doubles +[#rspecvoidexpect] == RSpec/VoidExpect |=== @@ -5810,6 +6503,7 @@ end Checks void `expect()`. +[#examples-rspecvoidexpect] === Examples [source,ruby] @@ -5821,10 +6515,12 @@ expect(something) expect(something).to be(1) ---- +[#references-rspecvoidexpect] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VoidExpect +[#rspecyield] == RSpec/Yield |=== @@ -5832,13 +6528,14 @@ expect(something).to be(1) | Enabled | Yes -| Yes +| Always | 1.32 | - |=== Checks for calling a block within a stub. +[#examples-rspecyield] === Examples [source,ruby] @@ -5850,6 +6547,7 @@ allow(foo).to receive(:bar) { |&block| block.call(1) } expect(foo).to receive(:bar).and_yield(1) ---- +[#references-rspecyield] === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Yield diff --git a/docs/modules/ROOT/pages/cops_rspec_capybara.adoc b/docs/modules/ROOT/pages/cops_rspec_capybara.adoc deleted file mode 100644 index bb87b6b6e..000000000 --- a/docs/modules/ROOT/pages/cops_rspec_capybara.adoc +++ /dev/null @@ -1,374 +0,0 @@ -= RSpec/Capybara - -== RSpec/Capybara/CurrentPathExpectation - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| Yes -| 1.18 -| 2.0 -|=== - -Checks that no expectations are set on Capybara's `current_path`. - -The -https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers#have_current_path-instance_method[`have_current_path` matcher] -should be used on `page` to set expectations on Capybara's -current path, since it uses -https://github.com/teamcapybara/capybara/blob/master/README.md#asynchronous-javascript-ajax-and-friends[Capybara's waiting functionality] -which ensures that preceding actions (like `click_link`) have -completed. - -This cop does not support autocorrection in some cases. - -=== Examples - -[source,ruby] ----- -# bad -expect(current_path).to eq('/callback') - -# good -expect(page).to have_current_path('/callback') - -# bad (does not support autocorrection) -expect(page.current_path).to match(variable) - -# good -expect(page).to have_current_path('/callback') ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/CurrentPathExpectation - -== RSpec/Capybara/FeatureMethods - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| Yes -| 1.17 -| 2.0 -|=== - -Checks for consistent method usage in feature specs. - -By default, the cop disables all Capybara-specific methods that have -the same native RSpec method (e.g. are just aliases). Some teams -however may prefer using some of the Capybara methods (like `feature`) -to make it obvious that the test uses Capybara, while still disable -the rest of the methods, like `given` (alias for `let`), `background` -(alias for `before`), etc. You can configure which of the methods to -be enabled by using the EnabledMethods configuration option. - -=== Examples - -[source,ruby] ----- -# bad -feature 'User logs in' do - given(:user) { User.new } - - background do - visit new_session_path - end - - scenario 'with OAuth' do - # ... - end -end - -# good -describe 'User logs in' do - let(:user) { User.new } - - before do - visit new_session_path - end - - it 'with OAuth' do - # ... - end -end ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| EnabledMethods -| `[]` -| Array -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods - -== RSpec/Capybara/MatchStyle - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.17 -| - -|=== - -Checks for usage of deprecated style methods. - -=== Examples - -==== when using `assert_style` - -[source,ruby] ----- -# bad -page.find(:css, '#first').assert_style(display: 'block') - -# good -page.find(:css, '#first').assert_matches_style(display: 'block') ----- - -==== when using `has_style?` - -[source,ruby] ----- -# bad -expect(page.find(:css, 'first') - .has_style?(display: 'block')).to be true - -# good -expect(page.find(:css, 'first') - .matches_style?(display: 'block')).to be true ----- - -==== when using `have_style` - -[source,ruby] ----- -# bad -expect(page).to have_style(display: 'block') - -# good -expect(page).to match_style(display: 'block') ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/MatchStyle - -== RSpec/Capybara/NegationMatcher - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.14 -| - -|=== - -Enforces use of `have_no_*` or `not_to` for negated expectations. - -=== Examples - -==== EnforcedStyle: not_to (default) - -[source,ruby] ----- -# bad -expect(page).to have_no_selector -expect(page).to have_no_css('a') - -# good -expect(page).not_to have_selector -expect(page).not_to have_css('a') ----- - -==== EnforcedStyle: have_no - -[source,ruby] ----- -# bad -expect(page).not_to have_selector -expect(page).not_to have_css('a') - -# good -expect(page).to have_no_selector -expect(page).to have_no_css('a') ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| EnforcedStyle -| `not_to` -| `have_no`, `not_to` -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/NegationMatcher - -== RSpec/Capybara/SpecificActions - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| No -| 2.14 -| - -|=== - -Checks for there is a more specific actions offered by Capybara. - -=== Examples - -[source,ruby] ----- -# bad -find('a').click -find('button.cls').click -find('a', exact_text: 'foo').click -find('div button').click - -# good -click_link -click_button(class: 'cls') -click_link(exact_text: 'foo') -find('div').click_button ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificActions - -== RSpec/Capybara/SpecificFinders - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.13 -| - -|=== - -Checks if there is a more specific finder offered by Capybara. - -=== Examples - -[source,ruby] ----- -# bad -find('#some-id') -find('[visible][id=some-id]') - -# good -find_by_id('some-id') -find_by_id('some-id', visible: true) ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificFinders - -== RSpec/Capybara/SpecificMatcher - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| No -| 2.12 -| - -|=== - -Checks for there is a more specific matcher offered by Capybara. - -=== Examples - -[source,ruby] ----- -# bad -expect(page).to have_selector('button') -expect(page).to have_no_selector('button.cls') -expect(page).to have_css('button') -expect(page).to have_no_css('a.cls', href: 'http://example.com') -expect(page).to have_css('table.cls') -expect(page).to have_css('select') -expect(page).to have_css('input', exact_text: 'foo') - -# good -expect(page).to have_button -expect(page).to have_no_button(class: 'cls') -expect(page).to have_button -expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com') -expect(page).to have_table(class: 'cls') -expect(page).to have_select -expect(page).to have_field('foo') ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/SpecificMatcher - -== RSpec/Capybara/VisibilityMatcher - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| No -| 1.39 -| 2.0 -|=== - -Checks for boolean visibility in Capybara finders. - -Capybara lets you find elements that match a certain visibility -using the `:visible` option. `:visible` accepts both boolean and -symbols as values, however using booleans can have unwanted -effects. `visible: false` does not find just invisible elements, -but both visible and invisible elements. For expressiveness and -clarity, use one of the # symbol values, `:all`, `:hidden` or -`:visible`. -Read more in -https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all[the documentation]. - -=== Examples - -[source,ruby] ----- -# bad -expect(page).to have_selector('.foo', visible: false) -expect(page).to have_css('.foo', visible: true) -expect(page).to have_link('my link', visible: false) - -# good -expect(page).to have_selector('.foo', visible: :visible) -expect(page).to have_css('.foo', visible: :all) -expect(page).to have_link('my link', visible: :hidden) ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher diff --git a/docs/modules/ROOT/pages/cops_rspec_factorybot.adoc b/docs/modules/ROOT/pages/cops_rspec_factorybot.adoc deleted file mode 100644 index 979216201..000000000 --- a/docs/modules/ROOT/pages/cops_rspec_factorybot.adoc +++ /dev/null @@ -1,355 +0,0 @@ -= RSpec/FactoryBot - -== RSpec/FactoryBot/AttributeDefinedStatically - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| Yes -| 1.28 -| 2.23 -|=== - -Always declare attribute values as blocks. - -=== Examples - -[source,ruby] ----- -# bad -kind [:active, :rejected].sample - -# good -kind { [:active, :rejected].sample } - -# bad -closed_at 1.day.from_now - -# good -closed_at { 1.day.from_now } - -# bad -count 1 - -# good -count { 1 } ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| Include -| `+**/spec/factories.rb+`, `+**/spec/factories/**/*.rb+`, `+**/features/support/factories/**/*.rb+` -| Array -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically - -== RSpec/FactoryBot/ConsistentParenthesesStyle - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.14 -| - -|=== - -Use a consistent style for parentheses in factory bot calls. - -=== Examples - -[source,ruby] ----- -# bad -create :user -build(:user) -create(:login) -create :login ----- - -==== `EnforcedStyle: require_parentheses` (default) - -[source,ruby] ----- -# good -create(:user) -create(:user) -create(:login) -build(:login) ----- - -==== `EnforcedStyle: omit_parentheses` - -[source,ruby] ----- -# good -create :user -build :user -create :login -create :login - -# also good -# when method name and first argument are not on same line -create( - :user -) -build( - :user, - name: 'foo' -) ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| EnforcedStyle -| `require_parentheses` -| `require_parentheses`, `omit_parentheses` -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/ConsistentParenthesesStyle - -== RSpec/FactoryBot/CreateList - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| Yes -| 1.25 -| 2.23 -|=== - -Checks for create_list usage. - -This cop can be configured using the `EnforcedStyle` option - -=== Examples - -==== `EnforcedStyle: create_list` (default) - -[source,ruby] ----- -# bad -3.times { create :user } - -# good -create_list :user, 3 - -# bad -3.times { create :user, age: 18 } - -# good - index is used to alter the created models attributes -3.times { |n| create :user, age: n } - -# good - contains a method call, may return different values -3.times { create :user, age: rand } ----- - -==== `EnforcedStyle: n_times` - -[source,ruby] ----- -# bad -create_list :user, 3 - -# good -3.times { create :user } ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| Include -| `+**/*_spec.rb+`, `+**/spec/**/*+`, `+**/spec/factories.rb+`, `+**/spec/factories/**/*.rb+`, `+**/features/support/factories/**/*.rb+` -| Array - -| EnforcedStyle -| `create_list` -| `create_list`, `n_times` -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList - -== RSpec/FactoryBot/FactoryClassName - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| Yes -| 1.37 -| 2.23 -|=== - -Use string value when setting the class attribute explicitly. - -This cop would promote faster tests by lazy-loading of -application files. Also, this could help you suppress potential -bugs in combination with external libraries by avoiding a preload -of application files from the factory files. - -=== Examples - -[source,ruby] ----- -# bad -factory :foo, class: Foo do -end - -# good -factory :foo, class: 'Foo' do -end ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| Include -| `+**/spec/factories.rb+`, `+**/spec/factories/**/*.rb+`, `+**/features/support/factories/**/*.rb+` -| Array -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName - -== RSpec/FactoryBot/FactoryNameStyle - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.16 -| - -|=== - -Checks for name style for argument of FactoryBot::Syntax::Methods. - -=== Examples - -==== EnforcedStyle: symbol (default) - -[source,ruby] ----- -# bad -create('user') -build "user", username: "NAME" - -# good -create(:user) -build :user, username: "NAME" ----- - -==== EnforcedStyle: string - -[source,ruby] ----- -# bad -create(:user) -build :user, username: "NAME" - -# good -create('user') -build "user", username: "NAME" ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| EnforcedStyle -| `symbol` -| `symbol`, `string` -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryNameStyle - -== RSpec/FactoryBot/SyntaxMethods - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes (Unsafe) -| 2.7 -| - -|=== - -Use shorthands from `FactoryBot::Syntax::Methods` in your specs. - -=== Safety - -The autocorrection is marked as unsafe because the cop -cannot verify whether you already include -`FactoryBot::Syntax::Methods` in your test suite. - -If you're using Rails, add the following configuration to -`spec/support/factory_bot.rb` and be sure to require that file -in `rails_helper.rb`: - -[source,ruby] ----- -RSpec.configure do |config| - config.include FactoryBot::Syntax::Methods -end ----- - -If you're not using Rails: - -[source,ruby] ----- -RSpec.configure do |config| - config.include FactoryBot::Syntax::Methods - - config.before(:suite) do - FactoryBot.find_definitions - end -end ----- - -=== Examples - -[source,ruby] ----- -# bad -FactoryBot.create(:bar) -FactoryBot.build(:bar) -FactoryBot.attributes_for(:bar) - -# good -create(:bar) -build(:bar) -attributes_for(:bar) ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/SyntaxMethods diff --git a/docs/modules/ROOT/pages/cops_rspec_rails.adoc b/docs/modules/ROOT/pages/cops_rspec_rails.adoc deleted file mode 100644 index 21580e208..000000000 --- a/docs/modules/ROOT/pages/cops_rspec_rails.adoc +++ /dev/null @@ -1,373 +0,0 @@ -= RSpec/Rails - -== RSpec/Rails/AvoidSetupHook - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.4 -| - -|=== - -Checks that tests use RSpec `before` hook over Rails `setup` method. - -=== Examples - -[source,ruby] ----- -# bad -setup do - allow(foo).to receive(:bar) -end - -# good -before do - allow(foo).to receive(:bar) -end ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/AvoidSetupHook - -== RSpec/Rails/HaveHttpStatus - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes (Unsafe) -| 2.12 -| - -|=== - -Checks that tests use `have_http_status` instead of equality matchers. - -=== Examples - -[source,ruby] ----- -# bad -expect(response.status).to be(200) -expect(response.code).to eq("200") - -# good -expect(response).to have_http_status(200) ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus - -== RSpec/Rails/HttpStatus - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Enabled -| Yes -| Yes -| 1.23 -| 2.20 -|=== - -Enforces use of symbolic or numeric value to describe HTTP status. - -This cop inspects only `have_http_status` calls. -So, this cop does not check if a method starting with `be_*` is used -when setting for `EnforcedStyle: symbolic` or -`EnforcedStyle: numeric`. - -=== Examples - -==== `EnforcedStyle: symbolic` (default) - -[source,ruby] ----- -# bad -it { is_expected.to have_http_status 200 } -it { is_expected.to have_http_status 404 } -it { is_expected.to have_http_status "403" } - -# good -it { is_expected.to have_http_status :ok } -it { is_expected.to have_http_status :not_found } -it { is_expected.to have_http_status :forbidden } -it { is_expected.to have_http_status :success } -it { is_expected.to have_http_status :error } ----- - -==== `EnforcedStyle: numeric` - -[source,ruby] ----- -# bad -it { is_expected.to have_http_status :ok } -it { is_expected.to have_http_status :not_found } -it { is_expected.to have_http_status "forbidden" } - -# good -it { is_expected.to have_http_status 200 } -it { is_expected.to have_http_status 404 } -it { is_expected.to have_http_status 403 } -it { is_expected.to have_http_status :success } -it { is_expected.to have_http_status :error } ----- - -==== `EnforcedStyle: be_status` - -[source,ruby] ----- -# bad -it { is_expected.to have_http_status :ok } -it { is_expected.to have_http_status :not_found } -it { is_expected.to have_http_status "forbidden" } -it { is_expected.to have_http_status 200 } -it { is_expected.to have_http_status 404 } -it { is_expected.to have_http_status "403" } - -# good -it { is_expected.to be_ok } -it { is_expected.to be_not_found } -it { is_expected.to have_http_status :success } -it { is_expected.to have_http_status :error } ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| EnforcedStyle -| `symbolic` -| `numeric`, `symbolic`, `be_status` -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus - -== RSpec/Rails/InferredSpecType - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| No -| Yes (Unsafe) -| 2.14 -| - -|=== - -Identifies redundant spec type. - -After setting up rspec-rails, you will have enabled -`config.infer_spec_type_from_file_location!` by default in -spec/rails_helper.rb. This cop works in conjunction with this config. -If you disable this config, disable this cop as well. - -=== Safety - -This cop is marked as unsafe because -`config.infer_spec_type_from_file_location!` may not be enabled. - -=== Examples - -[source,ruby] ----- -# bad -# spec/models/user_spec.rb -RSpec.describe User, type: :model do -end - -# good -# spec/models/user_spec.rb -RSpec.describe User do -end - -# good -# spec/models/user_spec.rb -RSpec.describe User, type: :common do -end ----- - -==== `Inferences` configuration - -[source,ruby] ----- -# .rubocop.yml -# RSpec/Rails/InferredSpecType: -# Inferences: -# services: service - -# bad -# spec/services/user_spec.rb -RSpec.describe User, type: :service do -end - -# good -# spec/services/user_spec.rb -RSpec.describe User do -end - -# good -# spec/services/user_spec.rb -RSpec.describe User, type: :common do -end ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| Inferences -| `{"channels"=>"channel", "controllers"=>"controller", "features"=>"feature", "generator"=>"generator", "helpers"=>"helper", "jobs"=>"job", "mailboxes"=>"mailbox", "mailers"=>"mailer", "models"=>"model", "requests"=>"request", "integration"=>"request", "api"=>"request", "routing"=>"routing", "system"=>"system", "views"=>"view"}` -| -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/InferredSpecType - -== RSpec/Rails/MinitestAssertions - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| Yes -| Yes -| 2.17 -| - -|=== - -Check if using Minitest matchers. - -=== Examples - -[source,ruby] ----- -# bad -assert_equal(a, b) -assert_equal a, b, "must be equal" -refute_equal(a, b) - -# good -expect(b).to eq(a) -expect(b).to(eq(a), "must be equal") -expect(b).not_to eq(a) ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/MinitestAssertions - -== RSpec/Rails/NegationBeValid - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| No -| Yes (Unsafe) -| 2.23 -| - -|=== - -Enforces use of `be_invalid` or `not_to` for negated be_valid. - -=== Safety - -This cop is unsafe because it cannot guarantee that -the test target is an instance of `ActiveModel::Validations``. - -=== Examples - -==== EnforcedStyle: not_to (default) - -[source,ruby] ----- -# bad -expect(foo).to be_invalid - -# good -expect(foo).not_to be_valid - -# good (with method chain) -expect(foo).to be_invalid.and be_odd ----- - -==== EnforcedStyle: be_invalid - -[source,ruby] ----- -# bad -expect(foo).not_to be_valid - -# good -expect(foo).to be_invalid - -# good (with method chain) -expect(foo).to be_invalid.or be_even ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| EnforcedStyle -| `not_to` -| `not_to`, `be_invalid` -|=== - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/NegationBeValid - -== RSpec/Rails/TravelAround - -|=== -| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed - -| Pending -| No -| Yes (Unsafe) -| 2.19 -| - -|=== - -Prefer to travel in `before` rather than `around`. - -=== Safety - -This cop is unsafe because the automatic `travel_back` is only run -on test cases that are considered as Rails related. - -And also, this cop's autocorrection is unsafe because the order of -execution will change if other steps exist before traveling in -`around`. - -=== Examples - -[source,ruby] ----- -# bad -around do |example| - freeze_time do - example.run - end -end - -# good -before { freeze_time } ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/TravelAround diff --git a/docs/modules/ROOT/pages/development.adoc b/docs/modules/ROOT/pages/development.adoc index f10522d03..308980d8b 100644 --- a/docs/modules/ROOT/pages/development.adoc +++ b/docs/modules/ROOT/pages/development.adoc @@ -2,6 +2,47 @@ This page describes considerations when developing RSpec-specific cops. It is intended to be a complement to the general https://docs.rubocop.org/rubocop/development.html[RuboCop development documentation]. +== Create a new cop + +NOTE: Clone the repository and run `bundle install` if not done yet. +The following rake task can only be run inside the rubocop project directory itself. + +Use the bundled rake task `new_cop` to generate a cop template: + +[source,sh] +---- +$ bundle exec rake 'new_cop[RSpec/CopName]' +[create] lib/rubocop/cop/rspec/cop_name.rb +[create] spec/rubocop/cop/rspec/cop_name_spec.rb +[modify] lib/rubocop/cop/rspec_cops.rb - `require_relative 'rspec/cop_name'` was injected. +[modify] A configuration for the cop is added into config/default.yml. +Do 4 steps: + 1. Modify the description of RSpec/CopName in config/default.yml + 2. Implement your new cop in the generated file! + 3. Add an entry about new cop to CHANGELOG.md + 4. Commit your new cop with a message such as + e.g. "Add new `#{badge}` cop" +---- + +=== Choose a Name + +Use the following rules to give the new cop a name: + +* Pick a department. See the xref:cops.adoc[list of existing departments] +* The name is self-explanatory +* The name explains the offense the cop detects, e.g. `ExtraSpacing` +* The name starts with a noun instead of a verb, e.g. `ArrayAlignment` instead of `AlignArray` +* The name is easy to understand, e.g. `IndentationStyle` instead of just `Tab` +* The name is specific, e.g. `DuplicateHashKey` instead of just `DuplicateKey` +* The name is neutral when possible and accommodates multiple styles when feasible, e.g. `EmptyLineBeforeBegin`. +* The name uses commonly-used terms, e.g. `RedundantPercentI` instead of `RedundantPercentSymbolArray` +* The name uses correct terms, e.g. arguments in a method call, and parameters in a method signature +* Lines with no symbols are called "empty", not "blank", e.g. `LeadingEmptyLines` instead of `LeadingBlankLines` +* Prefer "redundant" to "unneeded", e.g. `RedundantSelf` instead of `UnneededSelf` + +See the https://github.com/rubocop/rubocop/blob/12fd014e255617a08b7b42aa5df0745e7382af88/config/obsoletion.yml#L4["renamed" section of `config/obsoletion.yml`] +for good and bad examples (old name is on the left, new name on the right). + == Base class The `RuboCop::Cop::RSpec::Base` class includes convenient https://docs.rubocop.org/rubocop-ast/node_pattern.html[node pattern DSL] matchers that will automatically account for any xref:usage.adoc#rspec-dsl-configuration[custom RSpec DSL configuration]. diff --git a/docs/modules/ROOT/pages/upgrade_to_version_3.adoc b/docs/modules/ROOT/pages/upgrade_to_version_3.adoc new file mode 100644 index 000000000..29de466d4 --- /dev/null +++ b/docs/modules/ROOT/pages/upgrade_to_version_3.adoc @@ -0,0 +1,60 @@ += Upgrade to Version 3.x +:doctype: book + +== Configuration File Update + +In version 3.x: + + - cop departments are extracted to another gem. (`Capybara`, `FactoryBot`, `Rails`) + +[discrete] +=== Extraction of cop departments. (`Capybara`, `FactoryBot`, `Rails`) + +If you are using the RSpec/Capybara, RSpec/FactoryBot, or RSpec/Rails departments -- or have one in a `require` list in your rubocop.yml -- you need to install the corresponding gem and add it to your `.rubocop.yml` file: + +* Capybara: `rubocop-capybara` +* FactoryBot: `rubocop-factory_bot` +* Rails: `rubocop-rspec_rails` + +For example, if you are using the RSpec/Capybara department, you need to install the `rubocop-capybara` gem and add it to your `.rubocop.yml` file: + +[source,ruby] +---- +# Gemfile +group :test do + gem 'rubocop-rspec' + gem 'rubocop-capybara' +end +---- + +[source,yaml] +---- +require: + - rubocop-rspec + - rubocop-capybara +---- + +And you need to remove the old department in your `.rubocop.yml` file: + +[source,yaml] +---- +RSpec/Capybara: + Enabled: false +---- + +For another example, if you are not using these departments, you don't need to do anything. +And when you update to RuboCop RSpec v3.0.0, you need to remove the old departments from your `.rubocop.yml` file, e.g.: + +[source,yaml] +---- +RSpec/Capybara: + Enabled: false +RSpec/FactoryBot: + Enabled: false +RSpec/Rails: + Enabled: false +---- + +== Troubleshooting + +If you're seeing `cannot load such file` when running rubocop, even after installing the gems, restart the server with `rubocop --restart-server`. diff --git a/lib/rubocop-rspec.rb b/lib/rubocop-rspec.rb index 21a2d1f91..ac03fa7dc 100644 --- a/lib/rubocop-rspec.rb +++ b/lib/rubocop-rspec.rb @@ -4,19 +4,14 @@ require 'yaml' require 'rubocop' -require 'rubocop-capybara' -require 'rubocop-factory_bot' require_relative 'rubocop/rspec' require_relative 'rubocop/rspec/inject' -require_relative 'rubocop/rspec/language/node_pattern' +require_relative 'rubocop/rspec/language' require_relative 'rubocop/rspec/node' require_relative 'rubocop/rspec/version' require_relative 'rubocop/rspec/wording' -# Dependent on `RuboCop::RSpec::Language::NodePattern`. -require_relative 'rubocop/rspec/language' - require_relative 'rubocop/cop/rspec/mixin/file_help' require_relative 'rubocop/cop/rspec/mixin/final_end_location' require_relative 'rubocop/cop/rspec/mixin/inside_example_group' @@ -55,12 +50,4 @@ def autocorrect_incompatible_with end ) -RuboCop::Cop::Style::TrailingCommaInArguments.singleton_class.prepend( - Module.new do - def autocorrect_incompatible_with - super.push(RuboCop::Cop::RSpec::Capybara::CurrentPathExpectation) - end - end -) - RuboCop::AST::Node.include(RuboCop::RSpec::Node) diff --git a/lib/rubocop/cop/rspec/base.rb b/lib/rubocop/cop/rspec/base.rb index 93c174a7e..0152f108f 100644 --- a/lib/rubocop/cop/rspec/base.rb +++ b/lib/rubocop/cop/rspec/base.rb @@ -6,7 +6,6 @@ module RSpec # @abstract parent class to RSpec cops class Base < ::RuboCop::Cop::Base include RuboCop::RSpec::Language - extend RuboCop::RSpec::Language::NodePattern exclude_from_registry diff --git a/lib/rubocop/cop/rspec/be_empty.rb b/lib/rubocop/cop/rspec/be_empty.rb index 68d3cd69d..fbd79fdb0 100644 --- a/lib/rubocop/cop/rspec/be_empty.rb +++ b/lib/rubocop/cop/rspec/be_empty.rb @@ -28,6 +28,7 @@ class BeEmpty < Base (send nil? :match_array (array)) (send nil? :contain_exactly) } + _? ) PATTERN diff --git a/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb b/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb deleted file mode 100644 index 41fcd2170..000000000 --- a/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Checks that no expectations are set on Capybara's `current_path`. - # # - # # The - # # https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers#have_current_path-instance_method[`have_current_path` matcher] - # # should be used on `page` to set expectations on Capybara's - # # current path, since it uses - # # https://github.com/teamcapybara/capybara/blob/master/README.md#asynchronous-javascript-ajax-and-friends[Capybara's waiting functionality] - # # which ensures that preceding actions (like `click_link`) have - # # completed. - # # - # # This cop does not support autocorrection in some cases. - # # - # # @example - # # # bad - # # expect(current_path).to eq('/callback') - # # - # # # good - # # expect(page).to have_current_path('/callback') - # # - # # # bad (does not support autocorrection) - # # expect(page.current_path).to match(variable) - # # - # # # good - # # expect(page).to have_current_path('/callback') - # # - # class CurrentPathExpectation < ::RuboCop::Cop::Base; end - CurrentPathExpectation = - ::RuboCop::Cop::Capybara::CurrentPathExpectation - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/feature_methods.rb b/lib/rubocop/cop/rspec/capybara/feature_methods.rb deleted file mode 100644 index 2b95dc70b..000000000 --- a/lib/rubocop/cop/rspec/capybara/feature_methods.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # Checks for consistent method usage in feature specs. - # - # By default, the cop disables all Capybara-specific methods that have - # the same native RSpec method (e.g. are just aliases). Some teams - # however may prefer using some of the Capybara methods (like `feature`) - # to make it obvious that the test uses Capybara, while still disable - # the rest of the methods, like `given` (alias for `let`), `background` - # (alias for `before`), etc. You can configure which of the methods to - # be enabled by using the EnabledMethods configuration option. - # - # @example - # # bad - # feature 'User logs in' do - # given(:user) { User.new } - # - # background do - # visit new_session_path - # end - # - # scenario 'with OAuth' do - # # ... - # end - # end - # - # # good - # describe 'User logs in' do - # let(:user) { User.new } - # - # before do - # visit new_session_path - # end - # - # it 'with OAuth' do - # # ... - # end - # end - # - class FeatureMethods < Base - extend AutoCorrector - include InsideExampleGroup - - MSG = 'Use `%s` instead of `%s`.' - - # https://github.com/teamcapybara/capybara/blob/e283c1aeaa72441f5403963577e16333bf111a81/lib/capybara/rspec/features.rb#L31-L36 - MAP = { - background: :before, - scenario: :it, - xscenario: :xit, - given: :let, - given!: :let!, - feature: :describe - }.freeze - - # @!method capybara_speak(node) - def_node_matcher :capybara_speak, <<~PATTERN - {#{MAP.keys.map(&:inspect).join(' ')}} - PATTERN - - # @!method feature_method(node) - def_node_matcher :feature_method, <<~PATTERN - (block - $(send #rspec? $#capybara_speak ...) - ...) - PATTERN - - def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler - return unless inside_example_group?(node) - - feature_method(node) do |send_node, match| - next if enabled?(match) - - add_offense(send_node.loc.selector) do |corrector| - corrector.replace(send_node.loc.selector, MAP[match].to_s) - end - end - end - - def message(range) - name = range.source.to_sym - format(MSG, method: name, replacement: MAP[name]) - end - - private - - def enabled?(method_name) - enabled_methods.include?(method_name) - end - - def enabled_methods - cop_config - .fetch('EnabledMethods', []) - .map(&:to_sym) - end - end - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/match_style.rb b/lib/rubocop/cop/rspec/capybara/match_style.rb deleted file mode 100644 index b18102d45..000000000 --- a/lib/rubocop/cop/rspec/capybara/match_style.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Checks for usage of deprecated style methods. - # # - # # @example when using `assert_style` - # # # bad - # # page.find(:css, '#first').assert_style(display: 'block') - # # - # # # good - # # page.find(:css, '#first').assert_matches_style(display: 'block') - # # - # # @example when using `has_style?` - # # # bad - # # expect(page.find(:css, 'first') - # # .has_style?(display: 'block')).to be true - # # - # # # good - # # expect(page.find(:css, 'first') - # # .matches_style?(display: 'block')).to be true - # # - # # @example when using `have_style` - # # # bad - # # expect(page).to have_style(display: 'block') - # # - # # # good - # # expect(page).to match_style(display: 'block') - # # - # class MatchStyle < ::RuboCop::Cop::Base; end - MatchStyle = ::RuboCop::Cop::Capybara::MatchStyle - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/negation_matcher.rb b/lib/rubocop/cop/rspec/capybara/negation_matcher.rb deleted file mode 100644 index 7c9c51702..000000000 --- a/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Enforces use of `have_no_*` or `not_to` for negated expectations. - # # - # # @example EnforcedStyle: not_to (default) - # # # bad - # # expect(page).to have_no_selector - # # expect(page).to have_no_css('a') - # # - # # # good - # # expect(page).not_to have_selector - # # expect(page).not_to have_css('a') - # # - # # @example EnforcedStyle: have_no - # # # bad - # # expect(page).not_to have_selector - # # expect(page).not_to have_css('a') - # # - # # # good - # # expect(page).to have_no_selector - # # expect(page).to have_no_css('a') - # # - # class NegationMatcher < ::RuboCop::Cop::Base; end - NegationMatcher = ::RuboCop::Cop::Capybara::NegationMatcher - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/specific_actions.rb b/lib/rubocop/cop/rspec/capybara/specific_actions.rb deleted file mode 100644 index 93cea5a7f..000000000 --- a/lib/rubocop/cop/rspec/capybara/specific_actions.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Checks for there is a more specific actions offered by Capybara. - # # - # # @example - # # - # # # bad - # # find('a').click - # # find('button.cls').click - # # find('a', exact_text: 'foo').click - # # find('div button').click - # # - # # # good - # # click_link - # # click_button(class: 'cls') - # # click_link(exact_text: 'foo') - # # find('div').click_button - # # - # class SpecificActions < ::RuboCop::Cop::Base; end - SpecificActions = ::RuboCop::Cop::Capybara::SpecificActions - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/specific_finders.rb b/lib/rubocop/cop/rspec/capybara/specific_finders.rb deleted file mode 100644 index 41c346e5a..000000000 --- a/lib/rubocop/cop/rspec/capybara/specific_finders.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Checks if there is a more specific finder offered by Capybara. - # # - # # @example - # # # bad - # # find('#some-id') - # # find('[visible][id=some-id]') - # # - # # # good - # # find_by_id('some-id') - # # find_by_id('some-id', visible: true) - # # - # class SpecificFinders < ::RuboCop::Cop::Base; end - SpecificFinders = ::RuboCop::Cop::Capybara::SpecificFinders - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/specific_matcher.rb b/lib/rubocop/cop/rspec/capybara/specific_matcher.rb deleted file mode 100644 index e6f5fa4d6..000000000 --- a/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Checks for there is a more specific matcher offered by Capybara. - # # - # # @example - # # - # # # bad - # # expect(page).to have_selector('button') - # # expect(page).to have_no_selector('button.cls') - # # expect(page).to have_css('button') - # # expect(page).to have_no_css('a.cls', href: 'http://example.com') - # # expect(page).to have_css('table.cls') - # # expect(page).to have_css('select') - # # expect(page).to have_css('input', exact_text: 'foo') - # # - # # # good - # # expect(page).to have_button - # # expect(page).to have_no_button(class: 'cls') - # # expect(page).to have_button - # # expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com') - # # expect(page).to have_table(class: 'cls') - # # expect(page).to have_select - # # expect(page).to have_field('foo') - # # - # class SpecificMatcher < ::RuboCop::Cop::Base; end - SpecificMatcher = ::RuboCop::Cop::Capybara::SpecificMatcher - end - end - end -end diff --git a/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb b/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb deleted file mode 100644 index bb63f8b73..000000000 --- a/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module Capybara - # @!parse - # # Checks for boolean visibility in Capybara finders. - # # - # # Capybara lets you find elements that match a certain visibility - # # using the `:visible` option. `:visible` accepts both boolean and - # # symbols as values, however using booleans can have unwanted - # # effects. `visible: false` does not find just invisible elements, - # # but both visible and invisible elements. For expressiveness and - # # clarity, use one of the # symbol values, `:all`, `:hidden` or - # # `:visible`. - # # Read more in - # # https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all[the documentation]. - # # - # # @example - # # # bad - # # expect(page).to have_selector('.foo', visible: false) - # # expect(page).to have_css('.foo', visible: true) - # # expect(page).to have_link('my link', visible: false) - # # - # # # good - # # expect(page).to have_selector('.foo', visible: :visible) - # # expect(page).to have_css('.foo', visible: :all) - # # expect(page).to have_link('my link', visible: :hidden) - # # - # class VisibilityMatcher < ::RuboCop::Cop::Base; end - VisibilityMatcher = ::RuboCop::Cop::Capybara::VisibilityMatcher - end - end - end -end diff --git a/lib/rubocop/cop/rspec/change_by_zero.rb b/lib/rubocop/cop/rspec/change_by_zero.rb index 457b645a4..82da74193 100644 --- a/lib/rubocop/cop/rspec/change_by_zero.rb +++ b/lib/rubocop/cop/rspec/change_by_zero.rb @@ -59,6 +59,8 @@ module RSpec # class ChangeByZero < Base extend AutoCorrector + include RangeHelp + MSG = 'Prefer `not_to change` over `to %s.by(0)`.' MSG_COMPOUND = 'Prefer %s with compound expectations ' \ 'over `%s.by(0)`.' @@ -102,12 +104,12 @@ def on_send(node) # rubocop:disable Metrics/MethodLength def register_offense(node, change_node) if compound_expectations?(node) - add_offense(node.source_range, + add_offense(node, message: message_compound(change_node)) do |corrector| autocorrect_compound(corrector, node) end else - add_offense(node.source_range, + add_offense(node, message: message(change_node)) do |corrector| autocorrect(corrector, node, change_node) end @@ -116,7 +118,8 @@ def register_offense(node, change_node) # rubocop:enable Metrics/MethodLength def compound_expectations?(node) - %i[and or & |].include?(node.parent.method_name) + node.parent.send_type? && + %i[and or & |].include?(node.parent.method_name) end def message(change_node) @@ -140,8 +143,32 @@ def autocorrect_compound(corrector, node) change_nodes(node) do |change_node| corrector.replace(change_node.loc.selector, negated_matcher) - range = node.loc.dot.with(end_pos: node.source_range.end_pos) + insert_operator(corrector, node, change_node) + remove_by_zero(corrector, node, change_node) + end + end + + def insert_operator(corrector, node, change_node) + operator = node.right_siblings.first + return unless %i[& |].include?(operator) + + corrector.insert_after( + replace_node(node, change_node), " #{operator}" + ) + end + + def replace_node(node, change_node) + expect_change_with_arguments(node) ? change_node : change_node.parent + end + + def remove_by_zero(corrector, node, change_node) + range = node.loc.dot.with(end_pos: node.source_range.end_pos) + if change_node.loc.line == range.line corrector.remove(range) + else + corrector.remove( + range_by_whole_lines(range, include_final_newline: true) + ) end end diff --git a/lib/rubocop/cop/rspec/context_wording.rb b/lib/rubocop/cop/rspec/context_wording.rb index 693c5c185..3fdba92f6 100644 --- a/lib/rubocop/cop/rspec/context_wording.rb +++ b/lib/rubocop/cop/rspec/context_wording.rb @@ -12,6 +12,9 @@ module RSpec # # @see http://www.betterspecs.org/#contexts # + # If both `Prefixes` and `AllowedPatterns` are empty, this cop will always + # report an offense. So you need to set at least one of them. + # # @example `Prefixes` configuration # # .rubocop.yml # # RSpec/ContextWording: @@ -58,7 +61,9 @@ module RSpec class ContextWording < Base include AllowedPattern - MSG = 'Context description should match %s.' + MSG_MATCH = 'Context description should match %s.' + MSG_ALWAYS = 'Current settings will always report an offense. Please ' \ + 'add allowed words to `Prefixes` or `AllowedPatterns`.' # @!method context_wording(node) def_node_matcher :context_wording, <<~PATTERN @@ -67,8 +72,7 @@ class ContextWording < Base def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler context_wording(node) do |context| - if bad_pattern?(context) - message = format(MSG, patterns: expect_patterns) + unless matches_allowed_pattern?(description(context)) add_offense(context, message: message) end end @@ -84,12 +88,6 @@ def prefix_regexes @prefix_regexes ||= prefixes.map { |pre| /^#{Regexp.escape(pre)}\b/ } end - def bad_pattern?(node) - return false if allowed_patterns.empty? - - !matches_allowed_pattern?(description(node)) - end - def description(context) if context.xstr_type? context.value.value @@ -98,6 +96,14 @@ def description(context) end end + def message + if allowed_patterns.empty? + MSG_ALWAYS + else + format(MSG_MATCH, patterns: expect_patterns) + end + end + def expect_patterns inspected = allowed_patterns.map do |pattern| pattern.inspect.gsub(/\A"|"\z/, '/') diff --git a/lib/rubocop/cop/rspec/described_class.rb b/lib/rubocop/cop/rspec/described_class.rb index 8a1713080..389e675c5 100644 --- a/lib/rubocop/cop/rspec/described_class.rb +++ b/lib/rubocop/cop/rspec/described_class.rb @@ -8,8 +8,10 @@ module RSpec # If the first argument of describe is a class, the class is exposed to # each example via described_class. # - # This cop can be configured using the `EnforcedStyle` and `SkipBlocks` - # options. + # This cop can be configured using the `EnforcedStyle`, `SkipBlocks` + # and `OnlyStaticConstants` options. + # `OnlyStaticConstants` is only relevant when `EnforcedStyle` is + # `described_class`. # # @example `EnforcedStyle: described_class` (default) # # bad @@ -22,6 +24,18 @@ module RSpec # subject { described_class.do_something } # end # + # @example `OnlyStaticConstants: true` (default) + # # good + # describe MyClass do + # subject { MyClass::CONSTANT } + # end + # + # @example `OnlyStaticConstants: false` + # # bad + # describe MyClass do + # subject { MyClass::CONSTANT } + # end + # # @example `EnforcedStyle: explicit` # # bad # describe MyClass do @@ -54,7 +68,7 @@ module RSpec # end # end # - class DescribedClass < Base + class DescribedClass < Base # rubocop:disable Metrics/ClassLength extend AutoCorrector include ConfigurableEnforcedStyle include Namespace @@ -112,14 +126,17 @@ def autocorrect(corrector, match) def find_usage(node, &block) yield(node) if offensive?(node) - - return if scope_change?(node) || node.const_type? + return if scope_change?(node) || allowed?(node) node.each_child_node do |child| find_usage(child, &block) end end + def allowed?(node) + node.const_type? && only_static_constants? + end + def message(offense) if style == :described_class format(MSG, replacement: DESCRIBED_CLASS, src: offense) @@ -139,6 +156,10 @@ def skippable_block?(node) node.block_type? && !rspec_block?(node) && cop_config['SkipBlocks'] end + def only_static_constants? + cop_config.fetch('OnlyStaticConstants', true) + end + def offensive?(node) if style == :described_class offensive_described_class?(node) @@ -194,7 +215,8 @@ def collapse_namespace(namespace, const) # const_name(s(:const, s(:const, nil, :M), :C)) # => [:M, :C] # const_name(s(:const, s(:cbase), :C)) # => [nil, :C] def const_name(node) - namespace, name = *node # rubocop:disable InternalAffairs/NodeDestructuring + namespace = node.namespace + name = node.short_name if !namespace [name] elsif namespace.const_type? diff --git a/lib/rubocop/cop/rspec/dialect.rb b/lib/rubocop/cop/rspec/dialect.rb index 7140541c4..5bc91f0cc 100644 --- a/lib/rubocop/cop/rspec/dialect.rb +++ b/lib/rubocop/cop/rspec/dialect.rb @@ -29,6 +29,19 @@ module RSpec # PreferredMethods: # context: describe # + # If you were previously using the `RSpec/Capybara/FeatureMethods` cop and + # want to keep disabling all Capybara-specific methods that have the same + # native RSpec method (e.g. are just aliases), use the following config: + # + # RSpec/Dialect: + # PreferredMethods: + # background: :before + # scenario: :it + # xscenario: :xit + # given: :let + # given!: :let! + # feature: :describe + # # You can expect the following behavior: # # @example diff --git a/lib/rubocop/cop/rspec/empty_hook.rb b/lib/rubocop/cop/rspec/empty_hook.rb index fa6c63884..6dae31aea 100644 --- a/lib/rubocop/cop/rspec/empty_hook.rb +++ b/lib/rubocop/cop/rspec/empty_hook.rb @@ -3,7 +3,7 @@ module RuboCop module Cop module RSpec - # Checks for empty before and after hooks. + # Checks for empty before and after hooks. # # @example # # bad diff --git a/lib/rubocop/cop/rspec/empty_output.rb b/lib/rubocop/cop/rspec/empty_output.rb new file mode 100644 index 000000000..f70d3de21 --- /dev/null +++ b/lib/rubocop/cop/rspec/empty_output.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Check that the `output` matcher is not called with an empty string. + # + # @example + # # bad + # expect { foo }.to output('').to_stdout + # expect { bar }.not_to output('').to_stderr + # + # # good + # expect { foo }.not_to output.to_stdout + # expect { bar }.to output.to_stderr + # + class EmptyOutput < Base + extend AutoCorrector + + MSG = 'Use `%s` instead of matching on an empty output.' + RESTRICT_ON_SEND = Runners.all + + # @!method matching_empty_output(node) + def_node_matcher :matching_empty_output, <<~PATTERN + (send + (block + (send nil? :expect) ... + ) + #Runners.all + (send $(send nil? :output (str empty?)) ...) + ) + PATTERN + + def on_send(send_node) + matching_empty_output(send_node) do |node| + runner = send_node.method?(:to) ? 'not_to' : 'to' + message = format(MSG, runner: runner) + add_offense(node, message: message) do |corrector| + corrector.replace(send_node.loc.selector, runner) + corrector.replace(node, 'output') + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/example_without_description.rb b/lib/rubocop/cop/rspec/example_without_description.rb index 8d5209849..e53d574b1 100644 --- a/lib/rubocop/cop/rspec/example_without_description.rb +++ b/lib/rubocop/cop/rspec/example_without_description.rb @@ -7,6 +7,7 @@ module RSpec # # RSpec allows for auto-generated example descriptions when there is no # description provided or the description is an empty one. + # It is acceptable to use `specify` without a description # # This cop removes empty descriptions. # It also defines whether auto-generated description is allowed, based @@ -14,17 +15,24 @@ module RSpec # # This cop can be configured using the `EnforcedStyle` option # + # @example + # # always good + # specify do + # result = service.call + # expect(result).to be(true) + # end + # # @example `EnforcedStyle: always_allow` (default) # # bad # it('') { is_expected.to be_good } - # it '' do + # specify '' do # result = service.call # expect(result).to be(true) # end # # # good # it { is_expected.to be_good } - # it do + # specify do # result = service.call # expect(result).to be(true) # end @@ -75,6 +83,7 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler def check_example_without_description(node) return if node.arguments? return unless disallow_empty_description?(node) + return if node.method?(:specify) && node.parent.multiline? add_offense(node, message: MSG_ADD_DESCRIPTION) end diff --git a/lib/rubocop/cop/rspec/example_wording.rb b/lib/rubocop/cop/rspec/example_wording.rb index a650183f2..a07e6abfa 100644 --- a/lib/rubocop/cop/rspec/example_wording.rb +++ b/lib/rubocop/cop/rspec/example_wording.rb @@ -55,8 +55,8 @@ class ExampleWording < Base MSG_INSUFFICIENT_DESCRIPTION = 'Your example description is ' \ 'insufficient.' - SHOULD_PREFIX = /\Ashould(?:n't)?\b/i.freeze - WILL_PREFIX = /\A(?:will|won't)\b/i.freeze + SHOULD_PREFIX = /\Ashould(?:n't|n’t)?\b/i.freeze + WILL_PREFIX = /\A(?:will|won't|won’t)\b/i.freeze IT_PREFIX = /\Ait /i.freeze # @!method it_description(node) diff --git a/lib/rubocop/cop/rspec/expect_actual.rb b/lib/rubocop/cop/rspec/expect_actual.rb index ea1fa0920..7aeb0fa81 100644 --- a/lib/rubocop/cop/rspec/expect_actual.rb +++ b/lib/rubocop/cop/rspec/expect_actual.rb @@ -50,27 +50,35 @@ class ExpectActual < Base regexp ].freeze - SUPPORTED_MATCHERS = %i[eq eql equal be].freeze + SKIPPED_MATCHERS = %i[route_to be_routable].freeze + CORRECTABLE_MATCHERS = %i[eq eql equal be].freeze # @!method expect_literal(node) def_node_matcher :expect_literal, <<~PATTERN (send (send nil? :expect $#literal?) #Runners.all - { + ${ (send (send nil? $:be) :== $_) (send nil? $_ $_ ...) } ) PATTERN - def on_send(node) - expect_literal(node) do |actual, matcher, expected| - add_offense(actual.source_range) do |corrector| - next unless SUPPORTED_MATCHERS.include?(matcher) + def on_send(node) # rubocop:disable Metrics/MethodLength + expect_literal(node) do |actual, send_node, matcher, expected| + next if SKIPPED_MATCHERS.include?(matcher) + + add_offense(actual) do |corrector| + next unless CORRECTABLE_MATCHERS.include?(matcher) next if literal?(expected) - swap(corrector, actual, expected) + corrector.replace(actual, expected.source) + if matcher == :be + corrector.replace(expected, actual.source) + else + corrector.replace(send_node, "#{matcher}(#{actual.source})") + end end end end @@ -89,12 +97,7 @@ def simple_literal?(node) def complex_literal?(node) COMPLEX_LITERALS.include?(node.type) && - node.each_child_node.all?(&method(:literal?)) - end - - def swap(corrector, actual, expected) - corrector.replace(actual, expected.source) - corrector.replace(expected, actual.source) + node.each_child_node.all? { |child_node| literal?(child_node) } end end end diff --git a/lib/rubocop/cop/rspec/expect_in_hook.rb b/lib/rubocop/cop/rspec/expect_in_hook.rb index adfcbeccf..4474c707b 100644 --- a/lib/rubocop/cop/rspec/expect_in_hook.rb +++ b/lib/rubocop/cop/rspec/expect_in_hook.rb @@ -27,7 +27,7 @@ class ExpectInHook < Base # @!method expectation(node) def_node_search :expectation, '(send nil? #Expectations.all ...)' - def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler + def on_block(node) return unless hook?(node) return if node.body.nil? diff --git a/lib/rubocop/cop/rspec/expect_in_let.rb b/lib/rubocop/cop/rspec/expect_in_let.rb new file mode 100644 index 000000000..d74e4a65a --- /dev/null +++ b/lib/rubocop/cop/rspec/expect_in_let.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Do not use `expect` in let. + # + # @example + # # bad + # let(:foo) do + # expect(something).to eq 'foo' + # end + # + # # good + # it do + # expect(something).to eq 'foo' + # end + # + class ExpectInLet < Base + MSG = 'Do not use `%s` in let' + + # @!method expectation(node) + def_node_search :expectation, '(send nil? #Expectations.all ...)' + + def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler + return unless let?(node) + return if node.body.nil? + + expectation(node.body) do |expect| + add_offense(expect.loc.selector, message: message(expect)) + end + end + + private + + def message(expect) + format(MSG, expect: expect.method_name) + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/expect_output.rb b/lib/rubocop/cop/rspec/expect_output.rb index bc57dcdde..7a0ff63dd 100644 --- a/lib/rubocop/cop/rspec/expect_output.rb +++ b/lib/rubocop/cop/rspec/expect_output.rb @@ -22,10 +22,7 @@ class ExpectOutput < Base def on_gvasgn(node) return unless inside_example_scope?(node) - # rubocop:disable InternalAffairs/NodeDestructuring - variable_name, _rhs = *node - # rubocop:enable InternalAffairs/NodeDestructuring - name = variable_name[1..] + name = node.name[1..] return unless name.eql?('stdout') || name.eql?('stderr') add_offense(node.loc.name, message: format(MSG, name: name)) diff --git a/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb b/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb deleted file mode 100644 index 60cbe185c..000000000 --- a/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module FactoryBot - # @!parse - # # Always declare attribute values as blocks. - # # - # # @example - # # # bad - # # kind [:active, :rejected].sample - # # - # # # good - # # kind { [:active, :rejected].sample } - # # - # # # bad - # # closed_at 1.day.from_now - # # - # # # good - # # closed_at { 1.day.from_now } - # # - # # # bad - # # count 1 - # # - # # # good - # # count { 1 } - # # - # class AttributeDefinedStatically < ::RuboCop::Cop::Base; end - AttributeDefinedStatically = - ::RuboCop::Cop::FactoryBot::AttributeDefinedStatically - end - end - end -end diff --git a/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb b/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb deleted file mode 100644 index 59089974e..000000000 --- a/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module FactoryBot - # @!parse - # # Use a consistent style for parentheses in factory bot calls. - # # - # # @example - # # - # # # bad - # # create :user - # # build(:user) - # # create(:login) - # # create :login - # # - # # @example `EnforcedStyle: require_parentheses` (default) - # # - # # # good - # # create(:user) - # # create(:user) - # # create(:login) - # # build(:login) - # # - # # @example `EnforcedStyle: omit_parentheses` - # # - # # # good - # # create :user - # # build :user - # # create :login - # # create :login - # # - # # # also good - # # # when method name and first argument are not on same line - # # create( - # # :user - # # ) - # # build( - # # :user, - # # name: 'foo' - # # ) - # # - # class ConsistentParenthesesStyle < ::RuboCop::Cop::Base; end - ConsistentParenthesesStyle = - ::RuboCop::Cop::FactoryBot::ConsistentParenthesesStyle - end - end - end -end diff --git a/lib/rubocop/cop/rspec/factory_bot/create_list.rb b/lib/rubocop/cop/rspec/factory_bot/create_list.rb deleted file mode 100644 index 58e5c577d..000000000 --- a/lib/rubocop/cop/rspec/factory_bot/create_list.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module FactoryBot - # @!parse - # # Checks for create_list usage. - # # - # # This cop can be configured using the `EnforcedStyle` option - # # - # # @example `EnforcedStyle: create_list` (default) - # # # bad - # # 3.times { create :user } - # # - # # # good - # # create_list :user, 3 - # # - # # # bad - # # 3.times { create :user, age: 18 } - # # - # # # good - index is used to alter the created models attributes - # # 3.times { |n| create :user, age: n } - # # - # # # good - contains a method call, may return different values - # # 3.times { create :user, age: rand } - # # - # # @example `EnforcedStyle: n_times` - # # # bad - # # create_list :user, 3 - # # - # # # good - # # 3.times { create :user } - # # - # class CreateList < ::RuboCop::Cop::Base; end - CreateList = ::RuboCop::Cop::FactoryBot::CreateList - end - end - end -end diff --git a/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb b/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb deleted file mode 100644 index cb0f5a970..000000000 --- a/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module FactoryBot - # @!parse - # # Use string value when setting the class attribute explicitly. - # # - # # This cop would promote faster tests by lazy-loading of - # # application files. Also, this could help you suppress potential - # # bugs in combination with external libraries by avoiding a preload - # # of application files from the factory files. - # # - # # @example - # # # bad - # # factory :foo, class: Foo do - # # end - # # - # # # good - # # factory :foo, class: 'Foo' do - # # end - # # - # class FactoryClassName < ::RuboCop::Cop::Base; end - FactoryClassName = ::RuboCop::Cop::FactoryBot::FactoryClassName - end - end - end -end diff --git a/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb b/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb deleted file mode 100644 index c04ed4702..000000000 --- a/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module FactoryBot - # @!parse - # # Checks for name style for argument of FactoryBot::Syntax::Methods. - # # - # # @example EnforcedStyle: symbol (default) - # # # bad - # # create('user') - # # build "user", username: "NAME" - # # - # # # good - # # create(:user) - # # build :user, username: "NAME" - # # - # # @example EnforcedStyle: string - # # # bad - # # create(:user) - # # build :user, username: "NAME" - # # - # # # good - # # create('user') - # # build "user", username: "NAME" - # # - # class FactoryNameStyle < ::RuboCop::Cop::Base; end - FactoryNameStyle = ::RuboCop::Cop::FactoryBot::FactoryNameStyle - end - end - end -end diff --git a/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb b/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb deleted file mode 100644 index 05492740f..000000000 --- a/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - module FactoryBot - # @!parse - # # Use shorthands from `FactoryBot::Syntax::Methods` in your specs. - # # - # # @safety - # # The autocorrection is marked as unsafe because the cop - # # cannot verify whether you already include - # # `FactoryBot::Syntax::Methods` in your test suite. - # # - # # If you're using Rails, add the following configuration to - # # `spec/support/factory_bot.rb` and be sure to require that file - # # in `rails_helper.rb`: - # # - # # [source,ruby] - # # ---- - # # RSpec.configure do |config| - # # config.include FactoryBot::Syntax::Methods - # # end - # # ---- - # # - # # If you're not using Rails: - # # - # # [source,ruby] - # # ---- - # # RSpec.configure do |config| - # # config.include FactoryBot::Syntax::Methods - # # - # # config.before(:suite) do - # # FactoryBot.find_definitions - # # end - # # end - # # ---- - # # - # # @example - # # # bad - # # FactoryBot.create(:bar) - # # FactoryBot.build(:bar) - # # FactoryBot.attributes_for(:bar) - # # - # # # good - # # create(:bar) - # # build(:bar) - # # attributes_for(:bar) - # # - # class SyntaxMethods < ::RuboCop::Cop::Base; end - SyntaxMethods = ::RuboCop::Cop::FactoryBot::SyntaxMethods - end - end - end -end diff --git a/lib/rubocop/cop/rspec/file_path.rb b/lib/rubocop/cop/rspec/file_path.rb deleted file mode 100644 index f66e22b34..000000000 --- a/lib/rubocop/cop/rspec/file_path.rb +++ /dev/null @@ -1,179 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - # Checks that spec file paths are consistent and well-formed. - # - # This cop is deprecated. - # We plan to remove it in the next major version update to 3.0. - # The migration targets are `RSpec/SpecFilePathSuffix` - # and `RSpec/SpecFilePathFormat`. - # If you are using this cop, please plan for migration. - # - # By default, this checks that spec file paths are consistent with the - # test subject and enforces that it reflects the described - # class/module and its optionally called out method. - # - # With the configuration option `IgnoreMethods` the called out method will - # be ignored when determining the enforced path. - # - # With the configuration option `CustomTransform` modules or classes can - # be specified that should not as usual be transformed from CamelCase to - # snake_case (e.g. 'RuboCop' => 'rubocop' ). - # - # With the configuration option `SpecSuffixOnly` test files will only - # be checked to ensure they end in '_spec.rb'. This option disables - # checking for consistency in the test subject or test methods. - # - # @example - # # bad - # whatever_spec.rb # describe MyClass - # - # # bad - # my_class_spec.rb # describe MyClass, '#method' - # - # # good - # my_class_spec.rb # describe MyClass - # - # # good - # my_class_method_spec.rb # describe MyClass, '#method' - # - # # good - # my_class/method_spec.rb # describe MyClass, '#method' - # - # @example when configuration is `IgnoreMethods: true` - # # bad - # whatever_spec.rb # describe MyClass - # - # # good - # my_class_spec.rb # describe MyClass - # - # # good - # my_class_spec.rb # describe MyClass, '#method' - # - # @example when configuration is `SpecSuffixOnly: true` - # # good - # whatever_spec.rb # describe MyClass - # - # # good - # my_class_spec.rb # describe MyClass - # - # # good - # my_class_spec.rb # describe MyClass, '#method' - # - class FilePath < Base - include TopLevelGroup - include Namespace - - MSG = 'Spec path should end with `%s`.' - - # @!method example_group(node) - def_node_matcher :example_group, <<~PATTERN - (block - $(send #rspec? _example_group $_ $...) ... - ) - PATTERN - - # @!method routing_metadata?(node) - def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))' - - def on_top_level_example_group(node) - return unless top_level_groups.one? - - example_group(node) do |send_node, example_group, arguments| - ensure_correct_file_path(send_node, example_group, arguments) - end - end - - private - - def ensure_correct_file_path(send_node, example_group, arguments) - pattern = pattern_for(example_group, arguments) - return if filename_ends_with?(pattern) - - # For the suffix shown in the offense message, modify the regular - # expression pattern to resemble a glob pattern for clearer error - # messages. - offense_suffix = pattern.gsub('.*', '*').sub('[^/]', '') - .sub('\.', '.') - add_offense(send_node, message: format(MSG, suffix: offense_suffix)) - end - - def routing_spec?(args) - args.any?(&method(:routing_metadata?)) || routing_spec_path? - end - - def pattern_for(example_group, arguments) - method_name = arguments.first - if spec_suffix_only? || !example_group.const_type? || - routing_spec?(arguments) - return pattern_for_spec_suffix_only - end - - [ - expected_path(example_group), - name_pattern(method_name), - '[^/]*_spec\.rb' - ].join - end - - def pattern_for_spec_suffix_only - '.*_spec\.rb' - end - - def name_pattern(method_name) - return unless method_name&.str_type? - return if ignore_methods? - - ".*#{method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')}" - end - - def expected_path(constant) - constants = namespace(constant) + constant.const_name.split('::') - - File.join( - constants.map do |name| - custom_transform.fetch(name) { camel_to_snake_case(name) } - end - ) - end - - def camel_to_snake_case(string) - string - .gsub(/([^A-Z])([A-Z]+)/, '\1_\2') - .gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2') - .downcase - end - - def custom_transform - cop_config.fetch('CustomTransform', {}) - end - - def ignore_methods? - cop_config['IgnoreMethods'] - end - - def filename_ends_with?(pattern) - expanded_file_path.match?("#{pattern}$") - end - - def relevant_rubocop_rspec_file?(_file) - true - end - - def spec_suffix_only? - cop_config['SpecSuffixOnly'] - end - - def routing_spec_path? - expanded_file_path.include?('spec/routing/') - end - - def expanded_file_path - File.expand_path(processed_source.file_path) - end - end - end - end -end diff --git a/lib/rubocop/cop/rspec/indexed_let.rb b/lib/rubocop/cop/rspec/indexed_let.rb index 546d56d3e..dab5d0e97 100644 --- a/lib/rubocop/cop/rspec/indexed_let.rb +++ b/lib/rubocop/cop/rspec/indexed_let.rb @@ -48,8 +48,8 @@ class IndexedLet < Base include AllowedIdentifiers include AllowedPattern - MSG = 'This `let` statement uses index in its name. Please give it ' \ - 'a meaningful name.' + MSG = 'This `let` statement uses `%s` in its name. ' \ + 'Please give it a meaningful name.' # @!method let_name(node) def_node_matcher :let_name, <<~PATTERN @@ -66,7 +66,8 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler return unless children filter_indexed_lets(children).each do |let_node| - add_offense(let_node) + index = let_name(let_node)[INDEX_REGEX] + add_offense(let_node, message: format(MSG, index: index)) end end diff --git a/lib/rubocop/cop/rspec/instance_variable.rb b/lib/rubocop/cop/rspec/instance_variable.rb index 0af7ed6f7..181619b83 100644 --- a/lib/rubocop/cop/rspec/instance_variable.rb +++ b/lib/rubocop/cop/rspec/instance_variable.rb @@ -26,7 +26,7 @@ module RSpec # @example with AssignmentOnly configuration # # rubocop.yml # # RSpec/InstanceVariable: - # # AssignmentOnly: false + # # AssignmentOnly: true # # # bad # describe MyClass do diff --git a/lib/rubocop/cop/rspec/is_expected_specify.rb b/lib/rubocop/cop/rspec/is_expected_specify.rb new file mode 100644 index 000000000..dd03383f9 --- /dev/null +++ b/lib/rubocop/cop/rspec/is_expected_specify.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Check for `specify` with `is_expected` and one-liner expectations. + # + # @example + # # bad + # specify { is_expected.to be_truthy } + # + # # good + # it { is_expected.to be_truthy } + # + # # good + # specify do + # # ... + # end + # specify { expect(sqrt(4)).to eq(2) } + # + class IsExpectedSpecify < Base + extend AutoCorrector + + RESTRICT_ON_SEND = %i[specify].freeze + IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze + MSG = 'Use `it` instead of `specify`.' + + # @!method offense?(node) + def_node_matcher :offense?, <<~PATTERN + (block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...)) + PATTERN + + def on_send(node) + block_node = node.parent + return unless block_node&.single_line? && offense?(block_node) + + selector = node.loc.selector + add_offense(selector) do |corrector| + corrector.replace(selector, 'it') + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/leaky_constant_declaration.rb b/lib/rubocop/cop/rspec/leaky_constant_declaration.rb index dbab968fa..907b97dea 100644 --- a/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +++ b/lib/rubocop/cop/rspec/leaky_constant_declaration.rb @@ -119,7 +119,7 @@ def on_module(node) private def inside_describe_block?(node) - node.each_ancestor(:block).any?(&method(:spec_group?)) + node.each_ancestor(:block).any? { |ancestor| spec_group?(ancestor) } end end end diff --git a/lib/rubocop/cop/rspec/message_expectation.rb b/lib/rubocop/cop/rspec/message_expectation.rb index 95c4a2373..d3cb45ce1 100644 --- a/lib/rubocop/cop/rspec/message_expectation.rb +++ b/lib/rubocop/cop/rspec/message_expectation.rb @@ -29,7 +29,6 @@ class MessageExpectation < Base MSG = 'Prefer `%