Skip to content

Commit bad0cb6

Browse files
fix: remote jQuery is not function thrown when jQuery overwritten in app as non-function (#32682)
* fix: address jquery remote is not a function error * remove some comments * add link to issue * add link to issue in code * add comments to ignore ts checks * add changelog entry * update to ts-expect-error
1 parent 6e7a742 commit bad0cb6

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

cli/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
_Released 10/21/2025 (PENDING)_
55

6+
**Bugfixes:**
7+
8+
- An error is no longer thrown during command execution when the application under test overwrites the `window.$` property with a non-function. Fixes [#1502](https://github.com/cypress-io/cypress/issues/1502). Fixed in [#32682](https://github.com/cypress-io/cypress/pull/32682).
9+
610
**Misc:**
711

812
Browser detection in Cypress now always prefers 64-bit browser installs to 32-bit browser installs. Addressed in [#32656](https://github.com/cypress-io/cypress/pull/32656).

packages/driver/cypress/e2e/dom/jquery.cy.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
describe('src/dom/jquery', () => {
22
context('.isJquery', () => {
33
it('does not get confused when window contains jquery function', () => {
4+
// @ts-expect-error - Intentionally adding jquery property to test conflict detection
45
window.jquery = () => {}
56

67
expect(Cypress.dom.isJquery(window)).to.be.false
@@ -16,7 +17,7 @@ describe('src/dom/jquery', () => {
1617

1718
cy.get('#dom').then(($el) => {
1819
expect(Cypress.dom.isJquery($el[0])).to.eql(false)
19-
// @ts-ignore
20+
// @ts-expect-error
2021
expect(Cypress.dom.isJquery()).to.eql(false)
2122
})
2223
})
@@ -33,4 +34,81 @@ describe('src/dom/jquery', () => {
3334
cy.visit('fixtures/dom.html')
3435
cy.noop(cy.$$('#should-not-exist')).scrollTo('250px', '250px')
3536
})
37+
38+
// https://github.com/cypress-io/cypress/issues/1502
39+
context('jQuery conflicts', () => {
40+
it('handles window.$ overridden with non-function value (dynamic)', () => {
41+
cy.visit('fixtures/dom.html')
42+
43+
// Override window.$ with a string value after page load
44+
cy.window().then((win) => {
45+
// @ts-expect-error - Intentionally overriding jQuery with non-function to test conflict handling
46+
win.$ = 'foo'
47+
})
48+
49+
// This should not throw "remoteJQuery is not a function" error
50+
cy.get('#dom').then(() => {
51+
// Test should pass without errors
52+
})
53+
})
54+
55+
it('handles window.$ overridden with object value (dynamic)', () => {
56+
cy.visit('fixtures/dom.html')
57+
58+
// Override window.$ with an object value after page load
59+
cy.window().then((win) => {
60+
// @ts-expect-error - Intentionally overriding jQuery with non-function to test conflict handling
61+
win.$ = { notAFunction: true }
62+
})
63+
64+
// This should not throw "remoteJQuery is not a function" error
65+
cy.get('#dom').then(() => {
66+
// Test should pass without errors
67+
})
68+
})
69+
70+
it('handles window.$ overridden with non-function value (static)', () => {
71+
// Test with window.$ pre-set in HTML
72+
cy.visit('fixtures/jquery-conflict-test.html')
73+
74+
// This should not throw "remoteJQuery is not a function" error
75+
cy.get('h1').then(() => {
76+
// Test should pass without errors
77+
})
78+
})
79+
80+
it('reproduces the exact user issue: window.$ = "foo" with h1 element', () => {
81+
cy.visit('fixtures/jquery-conflict-test.html')
82+
83+
// The HTML already has window.$ = 'foo' set
84+
// This should not throw "remoteJQuery is not a function" error
85+
cy.get('h1').then(() => {
86+
// Test should pass without errors - this was failing before the fix
87+
})
88+
})
89+
90+
it('assertions work correctly when window.$ is overridden', () => {
91+
cy.visit('fixtures/jquery-conflict-test.html')
92+
93+
// Test that assertions work properly
94+
cy.get('h1')
95+
.should('contain', 'Hello world')
96+
.should('be.visible')
97+
.should('have.text', 'Hello world')
98+
.then(($el) => {
99+
expect($el).to.exist
100+
expect($el.text()).to.equal('Hello world')
101+
})
102+
})
103+
104+
it('should commands work with jQuery conflicts', () => {
105+
cy.visit('fixtures/jquery-conflict-test.html')
106+
107+
// Test should() with function callback
108+
cy.get('h1').should(($el) => {
109+
expect($el).to.exist
110+
expect($el.text()).to.equal('Hello world')
111+
})
112+
})
113+
})
36114
})
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>jQuery Conflict Test</title>
5+
</head>
6+
<body>
7+
<h1>Hello world</h1>
8+
<script>
9+
window.$ = 'foo'
10+
</script>
11+
</body>
12+
</html>

packages/driver/src/cy/jquery.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ export const create = (state: StateFunc) => ({
2929
const remoteJQuery = state('jQuery') || state('window').$
3030

3131
if (remoteJQueryisNotSameAsGlobal(remoteJQuery)) {
32+
// Check if remoteJQuery is actually a function before calling it
33+
// https://github.com/cypress-io/cypress/issues/1502
34+
if (typeof remoteJQuery !== 'function') {
35+
return
36+
}
37+
3238
const remoteSubject = remoteJQuery(subject)
3339

3440
return remoteSubject

0 commit comments

Comments
 (0)