Skip to content

feat: add no-add rule to disallow .and() in favor of .should()#310

Merged
mschile merged 9 commits intocypress-io:masterfrom
todd-m-kemp:tk/new-rule-no-and
Apr 9, 2026
Merged

feat: add no-add rule to disallow .and() in favor of .should()#310
mschile merged 9 commits intocypress-io:masterfrom
todd-m-kemp:tk/new-rule-no-and

Conversation

@todd-m-kemp
Copy link
Copy Markdown
Contributor

@todd-m-kemp todd-m-kemp commented Mar 27, 2026

Description

Add no-add rule to disallow .and() in favour of .should().

Cypress's .and() is an alias for .should(). This rule enforces consistent use of .should() across Cypress test chains, and includes an auto-fixer that replaces .and() with .should() automatically.

Changes:

  • lib/rules/no-add.js — new rule implementation with rootIsCy traversal to correctly identify Cypress chains
  • tests/lib/rules/no-add.js — 20 tests covering valid and invalid cases including nested .within(), .then(), long chains, multiple .and() calls, and non-cy roots
  • docs/rules/no-add.md — rule documentation
  • lib/index.js — rule registered in plugin index

Motivation

We often see folks write code that looks something like this:

cy.get('elem')
  .should('be.visible')
  .and('have.text', 'blah')
  .click();

and after a PR review tells them that they don't need to explicitly check for visibility since they are clicking the element, they delete the visibility assertion and we end up with code that looks like this:

cy.get('elem')
  .and('have.text', 'blah')
  .click();

which is functional, but doesn't read well. To avoid code that looks like this, we would just like to disallow the use of .and() suite-wide so that the use of .should() is required.


Note

Medium Risk
Adds a new auto-fixable lint rule and refactors multiple existing rules to share a new isRootCypress helper, which could subtly change what gets flagged in Cypress call chains.

Overview
Adds a new ESLint rule cypress/no-and that disallows starting assertion chains with .and() (unless it follows .should(), .and(), or .contains()), and auto-fixes offending cases by rewriting .and to .should.

Introduces a shared lib/utils/is-root-cypress.js helper and updates several existing rules (assertion-before-screenshot, no-chained-get, no-debug, no-pause, unsafe-to-chain-command) to use it instead of local chain-walking implementations. Includes new documentation and a dedicated test suite for no-and, and registers the rule in lib/index.js.

Reviewed by Cursor Bugbot for commit ee566f7. Bugbot is set up for automated code reviews on this repo. Configure here.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 27, 2026

CLA assistant check
All committers have signed the CLA.

@cypress-app-bot
Copy link
Copy Markdown

Comment thread lib/rules/no-and.js
Comment thread lib/rules/no-and.js Outdated
@mschile
Copy link
Copy Markdown
Contributor

mschile commented Apr 6, 2026

Hi @todd-m-kemp 👋🏼, thanks for opening this PR! I'll discuss with the team if we want a rule to completely disallow .and(). My initial thoughts are we would want to allow .and() for cases where it's chained off of .should() and .contains().

As a side note, even if we don't proceed with this rule, you can always create a custom rule for your needs.

@mschile mschile self-assigned this Apr 6, 2026
@mschile mschile self-requested a review April 6, 2026 23:32
@todd-m-kemp
Copy link
Copy Markdown
Contributor Author

Hi @mschile! 👋

I don't think that this rule needs to be enabled by default. I did originally create a custom rule for my organization's use but felt that it could be a good rule for others who might want to use it as well which is what led me to create this PR.

My initial thoughts are we would want to allow .and() for cases where it's chained off of .should() and .contains().

Can you tell me more about this? Is this a special case somehow? I am not sure I understand why this scenario is of particular interest.

@mschile
Copy link
Copy Markdown
Contributor

mschile commented Apr 7, 2026

@todd-m-kemp, I definitely see value in this rule as I agree that the following code doesn't read well:

cy.get('elem')
  .and('have.text', 'blah')

However, I think this rule should not flag .and() where it does make sense:

cy.get('.err')
  .should('be.empty')
  .and('be.hidden')
cy.contains('Login')
  .and('be.visible')

Both of these examples are what's provided in the .and() documentation. Would you be willing/have the time to make these updates?

@todd-m-kemp
Copy link
Copy Markdown
Contributor Author

Would you be willing/have the time to make these updates?

@mschile Yes! I just pushed some changes up now. 👍

Comment thread lib/index.js Outdated
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit eca7c7e. Configure here.

Comment thread lib/rules/no-and.js Outdated
@mschile
Copy link
Copy Markdown
Contributor

mschile commented Apr 9, 2026

Thanks for the contribution @todd-m-kemp. I decided to rename the rule back to no-and so it could evolve in the future if need be.

@mschile mschile merged commit c0b7e3b into cypress-io:master Apr 9, 2026
14 checks passed
@cypress-app-bot
Copy link
Copy Markdown

🎉 This PR is included in version 6.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants