npm.io
0.2.1 • Published yesterday

eslint-plugin-cypress-silent-pass

Licence
Apache-2.0
Version
0.2.1
Deps
1
Size
31 kB
Vulns
0
Weekly
0

ESLint Plugin: Cypress Silent Pass

npm Cypress ESLint flat config Auto-fixable Part of e2e-skills License: Apache-2.0

One ESLint rule for a blind spot that eslint-plugin-cypress does not cover: chai assertions on a Cypress query that can never fail.

// ❌ always passes — cy.get() yields a chainable object that always exists
expect(cy.get('.badge')).to.exist;
expect(cy.get('.badge')).to.be.ok;
expect(cy.find('.row')).to.not.be.null;
expect(cy.contains('Save')).to.be.an('object');

// ✅ retrying assertion that actually checks the element
cy.get('.badge').should('be.visible');

cy.get() / cy.find() / cy.contains() don't return the element — they return a Cypress chainable object, which always exists, is always truthy, and is never null. So a chai expect(cy.get(...)).to.exist asserts nothing about the DOM; the test stays green whether the element is there or not.

Why a new rule?

eslint-plugin-cypress ships no rule for this. eslint-plugin-ui-testing has missing-assertion-in-test (catches tests with no assertion) but not the always-true assertion above. This rule fills that gap and is designed to avoid false positives: it only fires when the expect subject is an inline cy.* query chain, so plain values and non-Cypress code are never touched.

The always-pass class isn't hypothetical — fixes for silent-pass E2E assertions have been reviewed and merged into real projects (see e2e-skills · Proven in OSS, 8 merged PRs). This rule catches the inline cy-query slice of that class automatically.

Install

# npm
npm i -D eslint-plugin-cypress-silent-pass

# yarn
yarn add -D eslint-plugin-cypress-silent-pass

# pnpm
pnpm add -D eslint-plugin-cypress-silent-pass

# bun
bun add -d eslint-plugin-cypress-silent-pass

Usage (flat config, ESLint 9+)

// eslint.config.js
import silentPass from "eslint-plugin-cypress-silent-pass";

export default [
  silentPass.configs["flat/recommended"],
];

Or wire it yourself:

import silentPass from "eslint-plugin-cypress-silent-pass";

export default [
  {
    plugins: { "cypress-silent-pass": silentPass },
    rules: { "cypress-silent-pass/no-silent-pass": "error" },
  },
];
Legacy .eslintrc
{
  "plugins": ["cypress-silent-pass"],
  "rules": { "cypress-silent-pass/no-silent-pass": "error" }
}
Run
npx eslint .          # or: npx eslint --fix .
bunx eslint .         # or: bunx eslint --fix .

Rule: no-silent-pass

Flags expect(<inline cy query>) followed by an always-true chai assertion:

Assertion Why it always passes on a chainable
.to.exist the chainable object always exists
.to.be.ok the chainable object is always truthy
.to.not.be.null the chainable object is never null
.to.not.be.undefined the chainable object is never undefined
.to.be.an('object') the chainable is always an object

A query is recognized as a cy.* chain containing one of: get, find, contains, eq, first, last, filter, children, parent, parents, siblings, closest, next, prev, within, focused, root.

Auto-fixable. eslint --fix rewrites inline cy-query violations to cy.get(...).should('be.visible'). The opt-in identifier heuristic is reported only, never auto-fixed.

Options
"cypress-silent-pass/no-silent-pass": ["error", { "checkIdentifiers": true }]
  • checkIdentifiers (default false) — also flag expect(<identifier>) when the identifier looks like a yielded jQuery element ($el, $row, userBadge…), e.g. inside a .then(($el) => { expect($el).to.exist }) callback. A heuristic; no autofix is offered for this case because the correct rewrite depends on context.

Scope

Catches the mechanical, inline always-true case. Semantic silent-pass smells — a test that clicks Delete and never checks the row is gone, asserting the pre-state instead of the post-state — are not decidable by AST and are out of scope for any linter.

Part of a small family for catching tests that pass but prove nothing:

  • eslint-plugin-playwright-silent-pass — the same always-pass check for Playwright.
  • e2e-skills — the full agent-skill catalog: 24 Playwright/Cypress anti-patterns, including the semantic silent-pass smells a linter can't decide (nameassertion mismatch, missing post-state checks, missing auth setup, …).

This plugin is the mechanical, AST-decidable slice; e2e-skills covers the rest.

License

Apache-2.0 voidmatcha. See LICENSE.

Keywords