Skip to content

feat: upgrade chai to 4.5.0 and expose new assertion aliases#34178

Open
jennifer-shehane wants to merge 13 commits into
developfrom
claude/driver-chai-v4-upgrade-o4jd2t
Open

feat: upgrade chai to 4.5.0 and expose new assertion aliases#34178
jennifer-shehane wants to merge 13 commits into
developfrom
claude/driver-chai-v4-upgrade-o4jd2t

Conversation

@jennifer-shehane

@jennifer-shehane jennifer-shehane commented Jun 28, 2026

Copy link
Copy Markdown
Member
  • Closes N/A — chai dependency-maintenance upgrade for the driver. There is no dedicated open issue; the prior chai-upgrade issues (#2529, #2863) are closed.

Additional details

Upgrades the driver's bundled chai from 4.2.0 to 4.5.0 (the latest 4.x release). chai is the assertion library Cypress exposes to users via expect, assert, should, and cy.…should(...), so this is treated as a user-facing change.

This is intended to be non-breaking. The driver overrides chai's value inspection (chai.util.inspect / objDisplay / getMessage) with its own implementation, and chai's internals call those dynamically (util.getMessage(...)). As a result, chai 4.3+'s switch to the loupe inspector is bypassed — both the command-log message and the thrown error are formatted by Cypress's own inspector, so assertion failure messages and truncation are unchanged. See "Why this is safe" below.

Internal: the chai.ts TypeScript suppressions were converted from @ts-ignore to @ts-expect-error (with inline reasons), and one suppression that @types/chai 5 made unnecessary was dropped.

Dependency diff (chai 4.2.0 → 4.5.0)

dependency 4.2.0 4.5.0 note
assertion-error ^1.1.0 ^1.1.0 unchanged
check-error ^1.0.2 ^1.0.3 patch
deep-eql ^3.0.1 ^4.1.3 major bump (deep-equality engine)
get-func-name ^2.0.0 ^2.0.2 patch
loupe ^2.3.6 new (value inspector)
pathval ^1.1.0 ^1.1.1 patch
type-detect ^4.0.5 ^4.1.0 minor

chai changelog between the versions (release-by-release)

Source: chai GitHub releases. Versions between 4.2.0 (exclusive) and 4.5.0 (inclusive). Items verified against the lib/chai/core/assertions.js source diff are marked ✅.

  • 4.3.0 (2021-01) — the largest release in this range:
    • Value inspection rewritten to use the loupe library (this is the change Cypress neutralizes). ✅ (objDisplay/inspect now delegate to loupe)
    • deep-eql bumped from v3 to v4.
    • New: .oneOf can be chained with .contain/.include (e.g. expect([1,2,3]).to.contain.oneOf([3,4,5])) and supports .deep. ✅
    • New: also language chain. ✅
    • New: .exists alias of .exist. ✅
    • New: .greaterThanOrEqual (alias of .least/.gte) and .lessThanOrEqual (alias of .most/.lte). ✅
    • AssertionError now carries an operator attribute (improves diffs in Jest, etc.).
    • .closeTo/.approximately error message now states , and a delta is required when the delta is omitted. ✅
    • .include invalid-target message reworded to "the given combination of arguments (… and …) is invalid for this assertion. …". ✅
    • .within with Date bounds now renders the range using toISOString() instead of toUTCString(). ✅
    • Maintenance: dropped Node 4/6/9, added Node 10/12, removed PhantomJS.
  • 4.3.1 – 4.3.10 (2021–2023) — patch releases: dependency bumps (deep-eql, loupe, get-func-name, check-error, pathval), security/maintenance updates, and documentation fixes. Notably 4.3.8 included a version export correction and dev-dependency bumps. No assertion API changes.
  • 4.4.0 (2024-01-05) — deep-equality made configurable: an eql comparator can be supplied so deep assertions use a custom equality function instead of the built-in. ✅ (deep assertions now read flag(this, 'eql'), defaulting to deep-eql)
  • 4.4.1 (2024-01-12) — Node compatibility fix: removed use of the nullish-coalescing operator (??) for older runtimes.
  • 4.5.0 (2024-07-25) — type-detect bumped to 4.1.0 and deep-eql bumped (adds correct comparison of Symbol-keyed properties). No assertion API changes.

deep-eql v3 → v4 behavioral diff (verified empirically)

Across 18 representative comparisons, the only behavior that changes for common values:

  • Two distinct Error objects with the same name/message are now deeply equal (they were not under v3). This is an edge-case bug fix — it turns a previously-failing deep.equal into a passing one, never the reverse.
  • v4 also adds correct comparison of Symbol-keyed properties.

Everything else tested (plain/nested objects, arrays, NaN, +0/-0, dates, regexes, Map/Set, typed arrays, arguments, promises, functions) is identical between v3 and v4.

Net user-facing changes in Cypress

  • New assertion aliases (additive, the "Feature" in the changelog): .exists, .greaterThanOrEqual, .lessThanOrEqual, and .contain.oneOf. TypeScript definitions were added for all of them (BDD form in cli/types/cy-chai.d.ts, should('…') string-chainer form in cli/types/cypress.d.ts), with matching dtslint examples.
  • .exists gets Cypress's DOM-aware existence behavior: chai 4.3 registers .exists as a separate property from .exist, so the driver now applies its existence override (attachment check + retry + "to exist in the DOM" messaging) to both. Without this, expect($el).to.exists / should('not.exists') on a jQuery/DOM subject would fall through to chai's vanilla nullish check (a jQuery wrapper is never null). Raised by Cursor Bugbot.
  • Deep-equality: the Error-comparison edge case above.
  • Reporter error-UI expectation updated: chai 4.3 reworded the invalid-.include-target error, which is asserted by the runner error-UI tests (a fixture that calls assert.deepInclude()), so those expectations were updated to the new wording (is invalid for this assertion). The other reworded messages (.closeTo missing-delta, .within with Date bounds) are not asserted anywhere.
  • Assertion message formatting/truncation: unchanged (loupe is bypassed — see below).

Why this is safe (loupe is a no-op in Cypress)

The driver replaces chai's inspect/objDisplay/getMessage, and chai's assertion.js calls util.getMessage(...) dynamically, so every failure message — command-log and thrown error alike — is rendered by Cypress's own inspector. loupe is effectively dead code after the overrides. For reference, if loupe ever leaked, messages would differ like this (driver = what we keep, loupe = raw chai 4.5):

value driver (kept) loupe (raw)
function [Function: foo] [Function foo]
date Thu, 01 Jan 1970 00:00:00 GMT 1970-01-01T00:00:00.000Z
long object { Object (name, age, ...) } { name: 'Joe', age: 20, …(1) }
long array [ Array(15) ] [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, …(5) ]

New regression tests in assertions.cy.js (uses Cypress value inspection) pin the "driver" column for functions, dates, and truncated objects/arrays, so a loupe leak would fail CI.

Steps to test

  1. expect(2).to.be.greaterThanOrEqual(1) / cy.wrap(2).should('be.greaterThanOrEqual', 2) pass.
  2. expect(1).to.be.lessThanOrEqual(2) / cy.wrap(1).should('be.lessThanOrEqual', 2) pass.
  3. expect(0).to.exists passes; expect(null).to.not.exists passes; cy.get('#missing').should('not.exists') retries and reports Cypress's "to exist in the DOM" message (not chai's nullish check).
  4. expect('Today is sunny').to.contain.oneOf(['sunny', 'cloudy']) passes.
  5. A failing assertion on a function/date/large object still renders the same message as before the upgrade (e.g. expected [Function: foo] to equal [Function: bar], expected [ Array(15) ] to equal null).
  6. The above also type-check in TypeScript (cli/types/tests/chainer-examples.ts).

How has the user experience changed?

No change to existing assertion behavior or failure messages. The only additions are the new optional assertion aliases listed above (with .exists matching .exist's DOM-aware behavior). No visual change.

PR Tasks

  • [na] Is there an associated issue with maintainer approval for PR submission?
  • Have tests been added/updated?
  • [na] Has a PR for user-facing changes been opened in cypress-documentation?
  • Have API changes been updated in the type definitions?

🤖 Generated with Claude Code


Generated by Claude Code


Note

Medium Risk
Changes the shared assertion stack (including deep-eql v4) used in every spec; risk is mitigated by preserved Cypress chai overrides and new regression tests, but edge-case deep equality or rare misuse messages could still differ.

Overview
Upgrades the driver’s bundled chai from 4.2.0 to 4.5.0, exposing new optional assertion aliases (.exists, .greaterThanOrEqual, .lessThanOrEqual, and .contain.oneOf) through expect, assert, and cy.…should(...).

Because chai 4.3+ registers exists separately from exist, the driver now applies the same DOM-aware existence override to both properties so should('not.exists') on missing elements still uses Cypress’s “in the DOM” messaging instead of chai’s nullish check.

TypeScript is updated in cy-chai.d.ts and cypress.d.ts (including should('be.greaterThanOrEqual', …) / negated variants). New driver and type tests cover the aliases, DOM behavior for exists, and regression guards that assertion failure text still uses Cypress’s inspector (functions, dates, truncated objects/arrays) rather than chai’s loupe formatting.

Reporter e2e expectations change for one chai assert misuse error string (is invalid for this assertion). The changelog documents the feature and the upgrade.

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

claude added 8 commits June 28, 2026 04:02
Upgrade the driver's chai dependency from 4.2.0 to 4.5.0, the latest v4
release. The 4.5.0 lockfile entry already existed (shared with other
packages), so this consolidates the driver onto it and drops the orphaned
4.2.0 entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
Document the driver's chai 4.2.0 -> 4.5.0 dependency update under
Dependency Updates. The upgrade is backward compatible for users: the
driver's existing chai patch layer (custom inspect/objDisplay/getMessage)
keeps assertion behavior and error-message formatting unchanged, and the
bump only adds new assertion aliases.

PR link is a placeholder to be filled in when the PR is opened.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
The chai upgrade adds new assertion aliases (.exists, .greaterThanOrEqual,
.lessThanOrEqual, and .contain.oneOf), so frame it as a user-facing
feature rather than a plain dependency update.

PR link is a placeholder to be filled in when the PR is opened.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
Add TypeScript definitions for the assertion aliases introduced by the
chai 4.5 upgrade so they are available to users:
- expect(...).to.exists / should('exist')
- expect(...).to.be.greaterThanOrEqual(n) / should('be.greaterThanOrEqual', n)
- expect(...).to.be.lessThanOrEqual(n) / should('be.lessThanOrEqual', n)
- expect(...).to.contain.oneOf([...])

The BDD aliases are added to the bundled chai augmentation (cy-chai.d.ts)
and the should() string-chainer forms to the Chainer overloads
(cypress.d.ts), with matching dtslint examples in chainer-examples.ts.

Also add runtime smoke tests verifying the aliases are reachable through
Cypress's expect/assert/should surfaces. These are vanilla chai aliases,
so the tests only assert reachability, not chai's behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
Keep comments describing present state rather than the upgrade that
introduced the aliases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
Add regression tests asserting the rendered values in assertion failure
messages for functions and dates. These render differently under chai's
built-in inspector ([Function foo], ISO-8601 dates) than under Cypress's
inspector ([Function: foo], toUTCString), so the tests catch any change
that would alter user-facing failure messages.

Existing message-formatting tests only cover simple objects/strings,
where both inspectors produce identical output, so they did not guard
this.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
The function/date guards only exercise short values that never reach the
truncateThreshold, so they would not catch a change to how long values
are trimmed. Add cases for a long object and a long array, which chai's
inspector summarizes very differently ({ name: 'Joe', ...(1) } and
[ 1, 2, ...(5) ]) than Cypress's ({ Object (name, ...) } and
[ Array(15) ]).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
@jennifer-shehane jennifer-shehane marked this pull request as ready for review June 28, 2026 13:41
cypress-bot[bot]
cypress-bot Bot previously approved these changes Jun 28, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
@cypress-bot cypress-bot Bot dismissed their stale review June 28, 2026 14:32

Cursor Bugbot risk assessment is no longer Low Risk. Auto-approval dismissed; manual review required.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

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 7a4b7b8. Configure here.

Comment thread cli/types/cy-chai.d.ts
@cypress

cypress Bot commented Jun 28, 2026

Copy link
Copy Markdown

cypress    Run #71965

Run Properties:  status check passed Passed #71965  •  git commit 694ce83793: refactor(driver): use @ts-expect-error for chai type suppressions
Project cypress
Branch Review claude/driver-chai-v4-upgrade-o4jd2t
Run status status check passed Passed #71965
Run duration 20m 12s
Commit git commit 694ce83793: refactor(driver): use @ts-expect-error for chai type suppressions
Committer Claude
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 10
Tests that did not run due to a developer annotating a test with .skip  Pending 1133
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 27530
View all changes introduced in this branch ↗︎
UI Coverage  64%
  Untested elements 27  
  Tested elements 48  
Accessibility  99%
  Failed rules  0 critical   3 serious   1 moderate   0 minor
  Failed elements 19  

chai 4.3 reworded the error thrown when an .include/.deepInclude target
is not a supported type, from "object tested must be an array, ..." to
"the given combination of arguments (...) is invalid for this
assertion. ...". Update the runner error-UI verifications to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
@jennifer-shehane jennifer-shehane self-assigned this Jun 29, 2026
claude added 3 commits June 29, 2026 12:25
chai registers `.exists` as a separate property from `.exist`, but the
driver only overwrote `.exist` with its DOM-aware existence assertion
(attachment check + retry + custom messages). As a result
`expect($el).to.exists` / `should('not.exists')` on a jQuery/DOM subject
fell through to chai's vanilla nullish check, which always passes for a
jQuery wrapper. Overwrite `.exists` with the same assertion (with
matching save/restore) so both behave identically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
@types/chai 5 documents the `_super` getter parameter, so the
overwriteProperty calls type-check cleanly. The "_super is not
documented" @ts-ignore is a no-op (and @ts-expect-error would fail as an
unused directive), so remove the suppression entirely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
Switch the remaining @ts-ignore comments in chai.ts to @ts-expect-error
with the reason inline. Unlike @ts-ignore, @ts-expect-error fails if the
suppressed error ever disappears, so these won't silently rot. Verified
each still suppresses a real error under @types/chai 5 (getMessage
overload assignment; makeMethodChainable not matching chainingBehavior).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019BxGV9ZVJz7iJejFk7powF
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.

2 participants