I want to be able to click on a check box and test that an element is no longer in the DOM in Cypress. Can someone suggest how you do it?
// This is the Test when the checkbox is clicked and the element is there
cy.get('[type="checkbox"]').click();
cy.get('.check-box-sub-text').contains('Some text in this div.')
I want to do the opposite of the test above.
So when I click it again the div with the class check-box-sub-text should not be in the DOM.
Well this seems to work, so it tells me I have some more to learn about .should()
cy.get('.check-box-sub-text').should('not.exist');
You can also search for a text which is not supposed to exist:
cy.contains('test_invite_member#gmail.com').should('not.exist')
Here you have the result in Cypress: 0 matched elements
Reference: Docs - Assertions, Existence
Use .should('not.exist') to assert that an element does not exist in the DOM.
Do not use not.visible assertion. It would falsely pass in < 6.0, but properly fail now:
// for element that was removed from the DOM
// assertions below pass in < 6.0, but properly fail in 6.0+
.should('not.be.visible')
.should('not.contain', 'Text')
Migration Docs here: Migrating-to-Cypress-6-0
Cypress 6.x+ Migration
According to cypress docs on Existence
The very popular attempt which is a bit naive will work until it doesn't and then you'll have to rewrite it again... and again...
// retry until loading spinner no longer exists
cy.get('#loading').should('not.exist')
This doesn't really work for the title problem which is what most people will be looking for.
This works for the case that it is being removed. but in the case that you want it to never exist... It will retry until it goes away.
However, if you want to test that the element never exists in our case.
Yes lol. This is what you really want unless you want to just have your headache again another day.
// Goes through all the like elements, and says this object doesn't exist ever
cy.get(`img[src]`)
.then(($imageSection) => {
$imageSection.map((x, i) => { expect($imageSection[x].getAttribute('src')).to.not.equal(`${Cypress.config().baseUrl}/assets/images/imageName.jpg`) });
})
cy.get('[data-e2e="create-entity-field-relation-contact-name"]').should('not.exist');
might lead to some false results, as some error messages get hidden. It might be better to use
.should('not.visible');
in that case.
Here's what worked for me:
cy.get('[data-cy=parent]').should('not.have.descendants', 'img')
I check that some <div data-cy="parent"> has no images inside.
Regarding original question, you can set data-cy="something, i.e. child" attribute on inner nodes and use this assertion:
cy.get('[data-cy=parent]').should('not.have.descendants', '[data-cy=child]')
You can use get and contains together to differentiate HTML elements as well.
<button type='button'>Text 1</button>
<button type='button'>Text 2</button>
Let's say you have 2 buttons with different texts and you want to check if the first button doesn't exist then you can use;
cy.get('button').contains('Text 1').should('not.exist')
Could be done also using jQuery mode in cypress:
assert(Cypress.$('.check-box-sub-text').length==0)
I closed an element and checked should('not.exist') but the assertion failed as it existed in the DOM. It just that it is not visible anymore.
In such cases, should('not.visible') worked for me. I have just started using cypress. A lot to learn.
No try-catch flow in cypress
In java-selenium, we usually add the NoSuchElementException and do our cases. if UI is not displaying element for some Role based access cases.
You can also query for the matched elements inside the body or inside the element's parent container, and then do some assertions on its length:
cy.get("body").find(".check-box-sub-text").should("have.length", 0);
In case anyone comes across this, I was having the issue that neither .should('not.exist') nor .should('have.length', 0) worked - even worse: If the element I was querying was actually there right from the get-go, both asserts still returned true.
In my case this lead to the very strange situation that these three assertions, executed right after each other, were true, even though asserts 1+2 and 3 contradict each other:
cy.get('[data-cy="foobar"]').should('not.exist')
cy.get('[data-cy="foobar"]').should('have.length', 0)
cy.get('[data-cy="foobar"]').should('have.text', 'Foobar')
After extensive testing, I found out that this was simply a race condition problem. I was waiting on a backend call to finish before running the above 3 lines. Like so:
cy.wait('#someBackendCall')
cy.get('[data-cy="foobar"]').should('not.exist')
However once the backend called finished Cypress immediately ran the first two assertions and both were still true, because the DOM hadn't yet caught up rerendering based on the backend-data.
I added an explicit wait on an element that I knew was gonna be there in any case, so my code now looks something like this:
cy.wait('#someBackendCall')
cy.get('[data-cy="some-element"]').should('contain', 'I am always here after loading')
cy.get('[data-cy="foobar"]').should('not.exist')
You can also use below code
expect(opportunitynametext.include("Addon")).to.be.false
or
should('be.not.be.visible')
or
should('have.attr','minlength','2')
Voted element is correct but I highly recommend not to using anti-pattern saving you from a lot of headaches. Why? Yes, because;
Your application may use dynamic classes or ID's that change
Your selectors break from development changes to CSS styles or JS behavior
Luckily, it is possible to avoid both of these problems.
Don't target elements based on CSS attributes such as: id, class, tag
Don't target elements that may change their textContent
Add data-* attributes to make it easier to target elements
Example:
<button id="main" name="submission" role="button" data-cy="submit">Submit</button>
And if you want to be more specific and want to indentify more than one selector, it is always good to use .shouldchainer.
Example:
cy.get("ul").should(($li) => {
expect($li).to.be.visible
expect($li).to.contain("[data-cy=attribute-name]")
expect($li).to.not.contain("text or another selector")
})
If there is no element, we can use simple line like:
cy.get('[type="checkbox"]').should('not.exist')
In my case, Cypress was so fast, that simple .should('not.be.visible') was passing the test and after that, loader appears and test failed.
I've manage to success with this:
cy.get('.loader__wrapper')
.should('be.visible')
cy.get('.loader__wrapper', { timeout: 10000 })
.should('not.be.visible')
Also nice to set the timeout on 10 seconds when your application loads more than 4s.
I would use :
cy.get('.check-box-sub-text').should('not.be.visible');
This is safer than
cy.get('.check-box-sub-text').should('not.exist');
( The element can be present in the DOM but not visible with display: none or opacity: 0 )
I've noticed that you when writing jasmine unit tests usually the format is:
expect($('#foo')).toHaveValue('#bar');
But recently I've discovered by accident that the following also works:
expect('#foo').toHaveValue('#bar');
Is this expected behaviour? This seems like a better way to write my expects but I have never seen this notation before and I want to be sure I am not abusing something.
Could anyone confirm this is the way to go or direct me to any documentation of this?
(I am using the jasmine jquery library)
I've played around a bit with that. Looks like it really does work, having some peculiarities, though.
I've tried things like:
expect('.search-form').toBeInDOM();
expect('.search-form').toEqual('div');
expect('.search-form').toContainElement('.search-form__footer');
the first one passes and truely fails when changing to
.not.toBeInDOM();
the third one looks same -- it truely fails is changing to some
bad selector for toContainElement
the second one is a problem because of ambiguity: '.search-form' can be treated both as string and a selector.
Had a very brief look into source code, it looks likes matchers really do resolve expectation actual as a selector (example from):
toBeInDOM: function () {
return {
compare: function (actual) {
return { pass: $.contains(document.documentElement, $(actual)[0]) }
}
}
},
Although I could not find any sign of such abilities in their docs, too. Still, source code is source code ))) and it says what it says. And now it says it will treat the actual for expect as a selector.
I was quite sure that I already did this in some earlier version of jQuery, but http://api.jquery.com/category/traversing/ seems to suggest otherwise.
What I'm looking for is similar kind of the opposite of .addBack() - a traversing function that uses "all other" elements (not .not()!)
Preusdo Example:
$('.some-class li').slice(33,55).hide().allOthers().show()
Edit: This is not actually a hide() / show() based problem, this is just a simple example to clarify what I meant.
First, I'ld like to manipulate a set of elements selected with .slice(), and then manipulate all elements that were not selected by .slice().
Is there a handy traversing function I've missed that does just that? I know how to solve it in general, but a ".allOthers()" method that I might have missed would certainly be more handy and clearer.
In your case you can just call show before calling slice
$('.some-class li').show().slice(33,55).hide();
It's true that there is no method to get all others, the closest is to get back they previous collection as you mentioned, http://api.jquery.com/addback/
You could implement a plugin, since I'm on my mobile, I'll just write some straight code
// o(n*m), could be improved
function allOthers(jqObj) {
var current = [].concat(jqObj);
var prev = jqObj.addBack();
return prev.filter(function(obj){
return !current.includes(obj);
});
}
First show all of them and then hide from 33 to 55, here is the demo
$('.some-class li').show().slice(33,55).hide();
After testing #JuanMendes suggestion, I played around with it a bit and found quite a compact way to implement this kind of functionality, due to jQuery's prevObject:
$.fn.others = function() {
return this.prevObject.not( this );
}
I didn't test it too much with other methods, so it might needs some further changes - but it seems to work fine with .slice() at least.
https://jsfiddle.net/1L3db7k4/
because of some problems with joomla "in-content javascript" I have to give all my js logic to one file, but there are problems with inconsistence of dom elements across my site (it is ajax driven, so there is only one script and various DOMs).
What is the best solution to make some conditionals solving this problem..
Is it checking $(selector).length, or is there any better solution..
And in case of the $(selector).length , is there a way to save this selector to variable (performance issues)
for example some kind of
var selector = ($(selector).length !== 0) ? this : false ;
if(selector) { makeSomething; }
The this is actually pointing to Window object..So is there any way to make it like this without need of reselection?
Thanks
var $obj = $('selector');
if ($obj.length) { makeSomething(); }
Actually, this is only meaningful if you are searching for the existence of a certain element (that might identify a whole page) and running several operations based on that.
If you just want to do something on the elements like
$('selector').append('x');
the condition might be useless, because if the jQuery collection is empty, the methods won't run anyways (as pointed out by #Gary Green).
I know getElementsByName('something') that returns the elements with name="something", but I want to return a list of elements where custom="something", how would I do that?
There are no standard API in the DOM to do this.
If you do not mind adding jQuery to your project, you could query your elements using the jQuery attribute selector:
$("[custom='something']")
To answer my own question, it seems it was easier than I thought.
elements = document.getElementsByTagName('pre');
for (elem = 0;elem < elements.length;elem++)
{
element = elements[elem];
if (element.lang != 'php')
break;
...
}
The above happened to work in my situation. :)
This page lists all the functions of the Document object in the JavaScript available in browsers. Thus it provides getElementById(), getElementByName() and getElementByTagName().
I guess need to use something like JQuery to gain more freedom as it allows you to express more complex "queries". I'm not sure, but that might also be slower, depending on how often you have to look up things.