Skip to content

Add cop rule for bad usage of have_admin_callout#16011

Merged
alecslupu merged 18 commits intodevelopfrom
test/have_admin_callout_cop
Feb 10, 2026
Merged

Add cop rule for bad usage of have_admin_callout#16011
alecslupu merged 18 commits intodevelopfrom
test/have_admin_callout_cop

Conversation

@andreslucena
Copy link
Copy Markdown
Member

@andreslucena andreslucena commented Feb 6, 2026

🎩 What? Why?

While giving an eye to #15994, I found the usage of this antipattern:

  expect(page).to have_admin_callout("successfully")

This is something that is well extended in the codebase, and it actually can also ignore a minor bug in the process: as the spec was copy and pasted, the flash can also be copy pasted and we can be showing a wrong message in the flash (something like "Meeting successfully updated" when it should really be a Proposal).

As this is really extended through the codebase, I think we should introduce a rubocop or a failure in the CI for this. I think just adding the failure in the matcher is the cleanest way of doing it.

On this case I'm trying to rubocop approach.

📌 Related Issues

Testing

  1. See the failure of the cop: https://github.com/decidim/decidim/actions/runs/21746390098/job/62733178538?pr=16011
  2. (Before) Do a search for this string in the codebase
grep -r 'have_admin_callout."successfully' --include="*.rb" decidim-*/ | wc -l
206
  1. Apply the patch
  2. (After) Do the search and see it on 0

♥️ Thank you!

Summary by CodeRabbit

  • Chores

    • Enforced more descriptive admin notification texts across the admin UI and enabled a lint rule to check them.
    • Expanded the spelling allowlist to accept additional development terminology.
  • Tests

    • Added and updated tests to validate detection and acceptance of descriptive admin notification texts.

Copilot AI review requested due to automatic review settings February 6, 2026 08:35
@andreslucena andreslucena added the type: internal PRs that aren't necessary to add to the CHANGELOG for implementers label Feb 6, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new RuboCop cop that flags short or single-word admin callout messages, updates RuboCop configuration to load and enable it, adds "antipattern" to the spelling allowlist, and updates many test expectations to use explicit, descriptive admin callout strings.

Changes

Cohort / File(s) Summary
RuboCop Cop Implementation
decidim-dev/lib/decidim/dev/rubocop/cop/decidim/admin_callout_antipattern.rb
New cop RuboCop::Cop::Decidim::AdminCalloutAntipattern with thresholds, anti-pattern list, on_send handling and antipattern_text? detection logic.
RuboCop Tests
decidim-dev/spec/rubocop/cop/decidim/admin_callout_antipattern_spec.rb
New RSpec coverage for offenses and non-offenses (single-word, empty, nil, punctuation-stripped, length boundary and descriptive messages).
RuboCop Configuration
decidim-dev/config/rubocop/ruby/configuration.yml, decidim-dev/rubocop-decidim.yml
Load path added for the new cop and Decidim/AdminCalloutAntipattern enabled (included for spec/test paths).
Spelling allowlist
.github/actions/spelling/allow.txt
Added the token antipattern to the repository spelling allowlist.
Test expectation updates (many specs & shared examples)
decidim-*/spec/**/*, decidim-*/lib/*/test/*, decidim-*/spec/shared/* (many files)
Replaced numerous generic admin callout assertions ("successfully", "problem", etc.) with explicit, descriptive success/failure messages across many component/system/shared tests and examples. Changes are string updates in test assertions only.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

module: core

Suggested reviewers

  • alecslupu
  • github-actions

Poem

🐇 I hopped through specs both near and far,
sniffed tiny callouts — "too short!" — by star.
I nudged a cop to catch the terse and bland,
now messages bloom across the land. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding a RuboCop cop rule to detect bad usage of have_admin_callout.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test/have_admin_callout_cop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

github-actions[bot]
github-actions bot previously approved these changes Feb 6, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a custom RuboCop cop to detect and prevent the anti-pattern of using generic or very short text in have_admin_callout test assertions. The motivation is to catch copy-paste errors where flash messages might be incorrect (e.g., "Meeting successfully updated" appearing for a Proposal update). The cop flags single-word or short text like "successfully" and requires developers to use the full descriptive flash message.

Changes:

  • Added a new RuboCop cop Decidim::AdminCalloutAntipattern that detects generic/short text in have_admin_callout calls
  • Added RSpec tests for the cop covering single-word callouts, very short callouts, and valid descriptive callouts

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
decidim-dev/lib/decidim/dev/rubocop/cop/decidim/admin_callout_antipattern.rb Implements the custom cop with detection logic for anti-pattern text (single words, short strings, common generic terms)
decidim-dev/spec/rubocop/decidim/admin_callout_antipattern_spec.rb Provides test coverage for the cop with basic test cases for offensive and acceptable usage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@decidim-dev/lib/decidim/dev/rubocop/cop/decidim/admin_callout_antipattern.rb`:
- Line 1: Add the term "antipattern" to the spelling allow list so CI won't fail
due to the filename and identifiers using it: update the file
.github/actions/spelling/allow.txt to include the word antipattern (lowercase),
since it is used in the RuboCop cop filename
decidim-dev/lib/decidim/dev/rubocop/cop/decidim/admin_callout_antipattern.rb and
in the class/method names like AdminCalloutAntipattern; commit that change so
the spell-check workflow with check_file_names enabled accepts the word.
🧹 Nitpick comments (3)
decidim-dev/lib/decidim/dev/rubocop/cop/decidim/admin_callout_antipattern.rb (2)

8-9: extend AutoCorrector declared but no autocorrector provided.

add_offense on line 43 is called without a corrector block, so the cop advertises autocorrect support but doesn't actually correct anything. Either remove extend AutoCorrector or provide a corrector (e.g., a no-op or a TODO).

Proposed fix
-      class AdminCalloutAntipattern < RuboCop::Cop::Base
-        extend AutoCorrector
+      class AdminCalloutAntipattern < RuboCop::Cop::Base

48-58: Redundant guard: line 53 is subsumed by line 55.

return true if is_single_word && is_too_short (line 53) can never be reached without line 55 (return true if is_too_short) also being true. The is_single_word conjunct adds nothing—every too-short text is already caught unconditionally two lines later.

If the intent is that short multi-word strings should not be flagged (only short single-word strings), then move line 55 above line 54 and add the is_single_word guard. Otherwise, remove line 53 as dead code.

Option A: Remove redundant line (current behavior preserved)
       def antipattern_text?(text)
         stripped_text = text.gsub(/[[:punct:]]/, "")
         is_single_word = text.strip !~ /\s/
         is_too_short = stripped_text.length < MIN_LENGTH_THRESHOLD

-        return true if is_single_word && is_too_short
         return true if is_single_word && SINGLE_WORD_ANTI_PATTERNS.include?(stripped_text.downcase)
         return true if is_too_short

         false
       end
Option B: If multi-word short strings should be allowed
       def antipattern_text?(text)
         stripped_text = text.gsub(/[[:punct:]]/, "")
         is_single_word = text.strip !~ /\s/
-        is_too_short = stripped_text.length < MIN_LENGTH_THRESHOLD
 
-        return true if is_single_word && is_too_short
+        return true if is_single_word && stripped_text.length < MIN_LENGTH_THRESHOLD
         return true if is_single_word && SINGLE_WORD_ANTI_PATTERNS.include?(stripped_text.downcase)
-        return true if is_too_short
 
         false
       end
decidim-dev/spec/rubocop/decidim/admin_callout_antipattern_spec.rb (1)

10-28: Add a few more edge-case tests to nail down intended behavior.

The happy paths are covered, but some important boundary cases are missing:

  1. Short multi-word string (e.g., "all done") — 8 chars stripped, currently flagged by the is_too_short check. Is that intentional? A test would document this decision.
  2. Long single word not in the list (e.g., "congratulations") — 15 chars, single word, not in SINGLE_WORD_ANTI_PATTERNS. Should pass; a test would confirm.
  3. Single word in the list that is exactly at the threshold (e.g., "successfully" is 12 chars, so < 12 is false — it's caught only by the list match). Worth an explicit boundary test.
  4. Non-string argument (e.g., a variable have_admin_callout(msg)) — should be silently ignored.
Suggested additional tests
it "registers an offense for short multi-word callouts" do
  expect_offense(<<~RUBY)
    expect(page).to have_admin_callout("all done")
                                       ^^^^^^^^^^ Anti-pattern detected: avoid generic single-word or very short text in have_admin_callout. Use the full admin flash message, e.g. 'Meeting successfully published'.
  RUBY
end

it "accepts long single-word callouts not in the anti-pattern list" do
  expect_no_offenses(<<~RUBY)
    expect(page).to have_admin_callout("congratulations")
  RUBY
end

it "does not flag non-string arguments" do
  expect_no_offenses(<<~RUBY)
    expect(page).to have_admin_callout(flash_message)
  RUBY
end

github-actions[bot]
github-actions bot previously approved these changes Feb 6, 2026
github-actions[bot]
github-actions bot previously approved these changes Feb 6, 2026
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
github-actions[bot]
github-actions bot previously approved these changes Feb 6, 2026
Copy link
Copy Markdown
Contributor

@alecslupu alecslupu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are some improvements to be done. Check my suggestions on rubocop-decidim.

Also, can you check the following commands and see if there is a chance we may need to implement the callout some other places (we can tackle in a new issue / PR) ?

grep -RiHn 'expect' decidim-* | grep have_content | grep successfully
grep -RiHn 'expect' decidim-* | grep have_content | grep error

Co-authored-by: Alexandru Emil Lupu <contact@alecslupu.ro>
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 10, 2026
github-actions[bot]
github-actions bot previously approved these changes Feb 10, 2026
@andreslucena
Copy link
Copy Markdown
Member Author

Also, can you check the following commands and see if there is a chance we may need to implement the callout some other places (we can tackle in a new issue / PR) ?

Yes, lets do that in a new PR.

I'd do it like this:

  1. search and replace have_content("successfully") and have_content("error") to use have_admin_callout
  2. run the full CI to check that it's passing + rubocop is failing
  3. fix these cases

@andreslucena andreslucena dismissed alecslupu’s stale review February 10, 2026 17:11

Ready for the final round

Copy link
Copy Markdown
Contributor

@alecslupu alecslupu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants