Skip to content

feat: support chains started of helper functions in unsafe-to-chain-command#307

Open
MgmClientGuy0 wants to merge 7 commits intocypress-io:masterfrom
MgmClientGuy0:unsafe-to-chain-cy-root
Open

feat: support chains started of helper functions in unsafe-to-chain-command#307
MgmClientGuy0 wants to merge 7 commits intocypress-io:masterfrom
MgmClientGuy0:unsafe-to-chain-cy-root

Conversation

@MgmClientGuy0
Copy link
Contributor

Hi, this is a bit bigger PR, but I tried to keep it as minimal as possible without sacrificing documentation or tests.

Problem

Most rules (including unsafe-to-chain-command) check whether or not the expression they want to lint was started from a identifier called 'cy'. This fails if the chain was started somewhere else, e.g. from a helper function. The following code is invalid since two actions are chained, but it is not flagged.

function getTodo() {
  return cy.get('.todo')
}

getTodo().type('todo A{enter}').type('todo B{enter}')

typescript-eslint

The only way of reliably detecting whether or not it is a Cypress chain is to check the return type of the helper function. With JS alone, this is not possible, especially if the function was imported from another file. ESLint only lints one file at a time and we need the whole project as context. The "typescript-eslint' parser changes that.

Since this is the first time support for 'typescript-eslint' is introduced, maybe some pros and cons:

Pros

  • There is no drawback for users that don't want to use 'typescript-eslint'. The core rule works as before.
  • Users with a JS only codebase can still use this feature. Typescript can infer a lot of things by usage alone (especially return types) and the example above works without explicit type annotation.
  • The Cypress codebase itself uses it.

Cons

  • Not every developer of this plugin might be familiar with Typescript, so development gets harder.
  • There is a performance drawback for using typed linting, since the entire project has to be parsed. Users not familiar with this might activate it without considering it.
  • Documentation effort increases, since users should know what is possible with typed linting and what is not.

Implementation

  • Extracts the check for 'isRootCypress" from the rule to a utility module.
  • If a TypeScript program is available, uses the type checker to lookup the return type of the function and checks whether or not it matches 'Cypress.Chainable'. If the program is not available, returns false as before.
  • Tests with a typescript parser are added. They include all existing JS tests, as no rule should behave differently if typed linting is enabled. As the tests needs the Cypress types, I have added it as a devDependency. If you think this is too much, I can copy the .d.ts files manually.
  • The test-project was extended with a config that uses typed linting. It is run on the existing JS codebase with a minimal tsconfig.
  • The general README was extended to mention how to setup typed linting. I hopefully made it clear, that is purely optional and that the core functionality is unaffected. The documentation of the rule mentions that helper functions are only supported with typed linting.

Let me know if you have questions.

@cypress-app-bot
Copy link

@MgmClientGuy0 MgmClientGuy0 marked this pull request as draft March 16, 2026 08:16
@MgmClientGuy0 MgmClientGuy0 force-pushed the unsafe-to-chain-cy-root branch from b72e502 to a49261b Compare March 17, 2026 08:15
@MgmClientGuy0 MgmClientGuy0 marked this pull request as ready for review March 17, 2026 12:40
@cacieprins cacieprins self-requested a review March 17, 2026 14:16
@cacieprins cacieprins self-assigned this Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants