Here is a snip of my test code:
cy.get('div[data-component-data-id=301602] h2:first')
.should(($el) => {
expect($el).to.have.text('dress')
})
and cypress complains about the assertion:
CypressError: Timed out retrying: You attempted to make a chai-jQuery assertion on an object that is neither a DOM object or a jQuery object.
The chai-jQuery assertion you used was:
> text
The invalid subject you asserted on was:
> Object{3}
To use chai-jQuery assertions your subject must be valid.
This can sometimes happen if a previous assertion changed the subject.
So I have to change expect($el).to.have.text('dress')
to expect($el[0]).to.have.text('dress')
, then the complain dismissed and test passed.
I debug the assertion a bit, turns out $el[0]
is also a jquery element.
Here is a snapshot about $el
and $el[0]
:
So my question is: Isn't cy.get
equivalent to jquery $
? Why should I have to get the first element from $el
? Why $el[0] is also a jquery element?
Thanks in advance.
EDIT:
It turns out that this error related to the application code when it includes zepto library. I have raised an issue in the Cypress Github repository which you can track the progress.
I'm not quite sure what you are looking for at this stage, but have found a way to reset the effect of the zepto library.
To summarize,
zepto is a jquery-compatible library that takes over the $
global on your app window ref
$ = function(selector, context) {
return zepto.init(selector, context)
}
the result is that cy commands like .get()
and .wrap()
yield a zepto-wrapped form of the jquery result that's not compatible with chai expect()
without some destructuring.
Experimenting with Cypress.$
I found this is still referencing jquery propper, for example
const h2 = Cypress.$('h2')
returns a jquery object not a zepto object, so we can reset the app global $
from Cypress.$.
it('title display', () => {
cy.visit('index.html') // zepto takes effect here
// Reset $
const win = cy.state('window');
win.$ = Cypress.$;
cy.get('h2')
.should($el => {
expect($el).to.have.text('dress') // passes
})
})
The reset code could be incorporated into an overwrite of the cy.visit()
command to make it less pervasive.
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
return originalFn(url, options).then(_ => {
const win = cy.state('window')
win.$ = Cypress.$
})
})
...
it('title display', () => {
cy.visit('index.html')
cy.get('h2')
.should($el => {
expect($el).to.have.text('dress') // passes
})
})
NOTE This affects the way zepto works in the app.
Work around that leaves Zepto functional
This version of the cy.visit()
overwrite will leave the Zepto library functional, but allow Cypress to .should()
to receive the correct jQuery object.
Essentially we add our own proxy (on top of zepto's proxy) and examine the type of the selector on each call.
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
return originalFn(url, options).then(_ => {
const win = cy.state('window')
const zepto_$ = win.$;
win.$ = function(selector, context) {
return typeof selector === 'string'
? zepto_$(selector, context)
: Cypress.$(selector, context);
}
})
})
Use this html fragment to test it. If the two logs are identical, zepto is not affected.
<body>
<h2>dress</h2>
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script>
console.log('from app, call #1', $('h2'))
setTimeout(() => {
console.log('from app, call #2', $('h2'))
}, 1000)
</script>
</body>
I feel like using .and("contain", "dress")
would solve your issue.
I've tried running a snippet similar to yours on my machine. Using should
didn't seem to have the expected results and I encountered the same jquery weird behavior. However, when using then
, it works like a charm. $el and $el[0] both return the jquery element normally
cy.get("h1.h2:first").then(($el) => {
cy.log($el)
cy.log($el[0])
expect($el).to.have.text('measure')
expect($el[0]).to.have.text('measure')
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With