Cypress: Test if element does not exist - javascript

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 )

Related

Cypress getByTestId, queryByTestId, findByTestId to check if element doesn't exist

I am trying to check if element doesn't exist in a DOM Tree with Cypress and testing-library/cypress.
If I try to do cy.getByTestId("my-button").should("not.exist") test fails because it couldn't find element.
If I do cy.findByTestId("my-button").should("not.exist") it also fails because of time out.
The test does work if I do either cy.queryByTestId("my-button").should("not.exist") or
cy.get('[data-testid="my-button"]').should("not.exist").
Can someone please explain what's the difference between all 4.
Thanks
https://testing-library.com/docs/dom-testing-library/api-queries
getBy will throw errors if it can't find the element
findBy will return and reject a Promise if it doesn't find an element
queryBy will return null if no element is found:
This is useful for asserting an element that is not present.
looks like queryBy is your best choice for this problem
In the latest version of Cypress Testing Library they have removed queryBy.
Cypress Testing Library | Intro
If you want to check if something doesn't exist just use findBy, but put a should() straight afterwards. It won't time out in that case.
cy.findByText('My error message').should('not.exist')
Discussion on GitHub

How do I use Protractor to loop through a set of elements and test if they are clickable

I need to test if a list of buttons on the page are displayed and enabled First I gather all of the elements with elements.all
allEmployeeOptions = element.all(by.css('[role=option]'));
Then I attempted to use the .each function to loop through them and test if they are displayed (I will add isEnabled too).
testAllOptionsClickable(){
//this.actions.click();
browser.sleep(3000);
this.allEmployeeOptions.each(function(elm){
expect(elm.isDisplayed).toBe(true);
});
This doesn't seem to be working, I get "Expected function to be true" repeated 10 times for each element.
My best guess is that it is having trouble resolving the looping promise, but this is my first time writing a loop like this in Protractor. It's also possible .each is not the right approach and a for loop would be better.
Any help is appreciated.
.each is the right approach, I would not use a for loop to iterate over elements. Your problem is just missing parenthesis, isDisplayed() is a function so you simply need to change your assertion to expect(elm.isDisplayed()).toBe(true);
Also, for what it's worth I would add another assertion. Being displayed does not mean an element is necessarily clickable, you should consider adding a check for isEnabled() as well.
You are doing it right. But protractor has it's method to ckeck if an element is clickable.
example:
it('....xxxx.', function() {
elems=$$('.items.item li'); // use your css locator
var EC = protractor.ExpectedConditions;
elems.each(async function(elem){
browser.wait(EC.elementToBeClickable(elem), 5000);
// perform other action
let elmtxt= await elem.getText();
console.log("text: "+elmtxt);
//expect(elem.isDisplayed()).toBe(true); /toBeTruthy();/
});

Selenium with Mocha/Chai - How to return false is item does not exist in DOM

I'm currently using selenium-webdriver with Mocha & Chai in a JavaScript environment & I'm looking for a way to return a false value if an item does not exist in the DOM; basically to see if a user is logged in or not.
If they are logged in then the following exists in the DOM (and doesn't exist at all if your not signed in).
<a class="account-panel-controls__link" role="link" id="sign-out-nav" data-log-out="">Sign out</a>
I'm looking for a way to find out if it exists & click it if present, otherwise I want to click something else (which only exists when the other snippet above does not).
<a class="account-panel-controls__link" role="link" id="registration-sign-in-nav" data-log-out="">Sign in</a>
I've tried a few variations of the follow but it's gotten me nowhere. It works fine if the element with Id 'sign-out-nav' is present initially but if it's not then the test fails, saying that it was unable to find that element (I'm guess because it's not in the DOM at all?).
driver.findElement(By.id('sign-out-nav')).isDisplayed().then(function (displayed) {
if (displayed) {
driver.findElement(By.id('sign-out-nav')).click();
}
else{
driver.findElement(By.id('registration-sign-in-nav')).click();
}
});
I've search a number of posts & tried stuff like .getSize() & seeing if it's over 0 but can't get anything to run that way. I've also tried using 'findElements' together with .length but it also returns a value of [Object object] and a length of 1 so I can't seem to tell the values apart.
Is there something I'm missing?
Thanks
Not sure if this helps in your instance, but using a webdriverio + mocha + assertjs + selenium, I used the following:
assert.doesNotThrow(() => { browser.getText('#registration-sign-in-nav') });
This works for the stack I work with, because when the element doesn't exist, it throws an error.
To find elements on a page it is recommended to use .findElements():
// find an element on the page sample:
driver.findElements(By.id('sign-out-nav')) .then(found => console.log('Element found? %s', !!found.length));
Now if the .length > 0 the element is found, you can continue with .elementIsVisible() & .elementIsEnabled() for additional validation and then do the clicks (as per your example) or anything you need.
Oh, not to forget, depending of what you want to do you might need to use indexes if there are multiple elements found either to the .findElement() in case you have only one.
Selenium is really, really fickle when it is trying to find page elements. Surround your driver.findElement with a driver.wait() and see if that allows it to be found.

Javascript test results. can anyone explain them?

I was recently writing a blog post about checking if jquery elements exist before binding event handlers, I did a quick jsfiddle which you can see here
The thing I dont understand, is that the results show (using chrome to measure in microseconds) that test 2 is a lot faster then test 1.
You'll see from the jsfiddle that test 2 checks the existent of the matching before binding a click event
TEST 1 is:
console.time('time_1');
$('.yep').click(function() {
alert('clicked');
});
console.timeEnd('time_1');
test 1 just tried to bind the event
TEST 2:
console.time('time_2');
if ($('.yep').length) {
$('.yep').click(function() {
alert('clicked');
});
}
console.timeEnd('time_2');
test 2 check the element exists before binding.
I am running the two bits of code on some, 87 I think 'section' elemenets, one of which has a class of 'yep'
I cant really see why the second test is faster, as its doing more work.
results:
time_1: 0.856ms
time_2: 0.146ms
Can anyone shed some light and help out a confused developer.
thanks
n.b please dont reply with alternative ways to bind click events in jquery, the .click is just used as a simple test
The primary thing going on here is that the first time you query a selector, the engine has to do more work than subsequent queries, which can sometimes be cached. If you reverse the first two tests, you'll find that whichever one runs first tends to be the slower one.
Despite that, and mostly as a side note, in test 2 you're querying the DOM twice, first to check the length, and then to hook up the handler. If the query is cached it doesn't matter much, but still, just do it once:
console.time('time_x');
var yep = $('.yep');
if (yep.length) {
yep.click(function() {
alert('clicked');
});
}
console.timeEnd('time_x');
Note, though, that calling click on a jQuery set with no elements in it is a harmless no-op (not an error or anything), so there's no need for the length check unless you're also doing something else you haven't shown.

conditionals in javascript based on page currently showing

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).

Categories