diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a41da1..4dadf980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Edge (Unreleased) - Bump RuboCop requirement to +1.81. ([@ydah]) +- Add new `Capybara/RSpec/HaveContent` cop. ([@nzlaura]) - Fix a false positive for `Capybara/FindAllFirst` when using logical operators with `all('...')[0]`. ([@ydah]) ## 2.22.1 (2025-03-12) @@ -92,6 +93,7 @@ [@darhazer]: https://github.com/Darhazer [@earlopain]: https://github.com/earlopain [@koic]: https://github.com/koic +[@nzlaura]: https://github.com/nzlaura [@onumis]: https://github.com/onumis [@oskarsezerins]: https://github.com/OskarsEzerins [@pirj]: https://github.com/pirj diff --git a/config/default.yml b/config/default.yml index 61953da4..ec1a1ec4 100644 --- a/config/default.yml +++ b/config/default.yml @@ -97,6 +97,12 @@ Capybara/RSpec: Enabled: true Include: *1 +Capybara/RSpec/HaveContent: + Description: Checks for usage of `have_content` and `have_no_content`. + Enabled: pending + VersionAdded: "<>" + Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/HaveContent + Capybara/RSpec/HaveSelector: Description: Use `have_css` or `have_xpath` instead of `have_selector`. Enabled: pending diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index c6b8d081..c64f33f9 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -17,6 +17,7 @@ === Department xref:cops_capybara_rspec.adoc[Capybara/RSpec] +* xref:cops_capybara_rspec.adoc#capybararspechavecontent[Capybara/RSpec/HaveContent] * xref:cops_capybara_rspec.adoc#capybararspechaveselector[Capybara/RSpec/HaveSelector] * xref:cops_capybara_rspec.adoc#capybararspecpredicatematcher[Capybara/RSpec/PredicateMatcher] diff --git a/docs/modules/ROOT/pages/cops_capybara_rspec.adoc b/docs/modules/ROOT/pages/cops_capybara_rspec.adoc index bf7916b2..2cef389a 100644 --- a/docs/modules/ROOT/pages/cops_capybara_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_capybara_rspec.adoc @@ -6,6 +6,44 @@ = Capybara/RSpec +[#capybararspechavecontent] +== Capybara/RSpec/HaveContent + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Always +| <> +| - +|=== + +Checks for usage of `have_content` and `have_no_content`. + +Capybara provides `have_text` and `have_no_text` matchers that are +more concise and preferred over their aliases `have_content` and +`have_no_content`. + +[#examples-capybararspechavecontent] +=== Examples + +[source,ruby] +---- +# bad +expect(page).to have_content('capy') +expect(page).to have_no_content('bara') + +# good +expect(page).to have_text('capy') +expect(page).to have_no_text('bara') +---- + +[#references-capybararspechavecontent] +=== References + +* https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/HaveContent + [#capybararspechaveselector] == Capybara/RSpec/HaveSelector diff --git a/lib/rubocop/cop/capybara/rspec/have_content.rb b/lib/rubocop/cop/capybara/rspec/have_content.rb new file mode 100644 index 00000000..289b276b --- /dev/null +++ b/lib/rubocop/cop/capybara/rspec/have_content.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Capybara + module RSpec + # Checks for usage of `have_content` and `have_no_content`. + # + # Capybara provides `have_text` and `have_no_text` matchers that are + # more concise and preferred over their aliases `have_content` and + # `have_no_content`. + # + # @example + # # bad + # expect(page).to have_content('capy') + # expect(page).to have_no_content('bara') + # + # # good + # expect(page).to have_text('capy') + # expect(page).to have_no_text('bara') + # + class HaveContent < ::RuboCop::Cop::Base + extend AutoCorrector + + MSG = 'Prefer `%s` over `%s`.' + RESTRICT_ON_SEND = %i[have_content have_no_content].freeze + PREFERRED_METHOD = { + 'have_content' => 'have_text', + 'have_no_content' => 'have_no_text' + }.freeze + + def on_send(node) + method_node = node.loc.selector + add_offense(method_node, + message: message(method_node)) do |corrector| + corrector.replace(method_node, + PREFERRED_METHOD[method_node.source]) + end + end + alias on_csend on_send + + private + + def message(node) + format(MSG, good: PREFERRED_METHOD[node.source], bad: node.source) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/capybara_cops.rb b/lib/rubocop/cop/capybara_cops.rb index c8c35312..5ceeb88c 100644 --- a/lib/rubocop/cop/capybara_cops.rb +++ b/lib/rubocop/cop/capybara_cops.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require_relative 'capybara/rspec/have_content' require_relative 'capybara/rspec/have_selector' require_relative 'capybara/rspec/predicate_matcher' diff --git a/spec/rubocop/cop/capybara/rspec/have_content_spec.rb b/spec/rubocop/cop/capybara/rspec/have_content_spec.rb new file mode 100644 index 00000000..b6b9f086 --- /dev/null +++ b/spec/rubocop/cop/capybara/rspec/have_content_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Capybara::RSpec::HaveContent do + it 'registers an offense when using `have_content`' do + expect_offense(<<~RUBY) + expect(page).to have_content('text') + ^^^^^^^^^^^^ Prefer `have_text` over `have_content`. + RUBY + + expect_correction(<<~RUBY) + expect(page).to have_text('text') + RUBY + end + + it 'registers an offense when using `have_no_content`' do + expect_offense(<<~RUBY) + expect(page).to have_no_content('capybara') + ^^^^^^^^^^^^^^^ Prefer `have_no_text` over `have_no_content`. + RUBY + + expect_correction(<<~RUBY) + expect(page).to have_no_text('capybara') + RUBY + end + + it 'does not register an offense when using `have_text`' do + expect_no_offenses(<<~RUBY) + expect(page).to have_text('ruby') + RUBY + end + + it 'does not register an offense when using `have_no_text`' do + expect_no_offenses(<<~RUBY) + expect(page).to have_no_text('capybara') + RUBY + end + + it 'preserves arguments during autocorrection' do + expect_offense(<<~RUBY) + expect(page).to have_content('text', exact: true) + ^^^^^^^^^^^^ Prefer `have_text` over `have_content`. + RUBY + + expect_correction(<<~RUBY) + expect(page).to have_text('text', exact: true) + RUBY + end +end