Assertions "is.visible" pass on elements which are not visible in cypress - cypress

Is there a workaround for this? I want to force Cypress to wait until the loading screen ends but it's asserting a bunch of elements that are not yet visible on screen.
The assertion is:
cy.get('[data-cy="cam-button"]').should('be.visible');

the most important thing is to add to cypress.json flexible/Explicit wait
"defaultCommandTimeout": 25000,
it will wait until your element will be visible and also will apply globally for the whole framework
cypress docs https://docs.cypress.io/guides/references/configuration#Timeouts

You should assert the button is not visible first, then assert the loader is not visible & then assert the button is visible like so:
cy.get('[data-cy="cam-button"]').should('not.be.visible');
cy.get('<loader-selector-here>').should('not.be.visible');
cy.get('[data-cy="cam-button"]').should('be.visible');
You can even use an alias if you use the same selector through the code like so:
cy.get('[data-cy="cam-button"]').as('camButton');
cy.get('#camButton').should('not.be.visible');
cy.get('<loader-selector-here>').should('not.be.visible');
cy.get('#camButton').should('be.visible');

You can add a timeout if you how long it takes
cy.get('[data-cy="cam-button"]', { timeout: xxx }).should('be.visible');

Related

failed because this element is disabled

I have an error in cypress that I get sometimes but other times the tests pass.
I have tried everything but I need it to always work
You should dig deeper to understand why your test is flaky. You haven't specified where you are running this test so it could be your app issue or your resources being tied if you are running in CI.
Either way, it is always good to add assertions before invoking an action on the HTML element. This will ease out the debugging any failures/flakiness. Plus, these assertions will be relatively quick if your app is ready.
// sometimes it is good to check a checkbox is disabled before you enable it
cy.get(x.foward_button1)
.should('be.visible')
.and('be.disabled')
cy.get(x.checkboxes)
// good assertion for any element
.should('be.visible')
// good for any button, checkbox, radio, etc
.and('be.enabled')
.click({multiple:true})
cy.get(x.foward_button1)
.should('be.visible')
.and('be.enabled')
Is button changing state from disabled to enabled by some conditions for exmaple validation or data request?
if yes before clicking button tell cypress to wait for these conditions to end. You can do it ugly way by telling cypress to wait for example 5secounds.
https://docs.cypress.io/api/commands/wait
cy.intercept('GET', '/exampleGet').as('someFetch')
cy.wait(5000) or cy.wait('#someFetch')
You have to add force: true to click the disabled button:
cy.get('x.forward_button1').click({force: true})

If element exists wait for it to disappear

So I'm trying to write some cypress code and the documentation imo isn't really clear.
I have two scenarios.
A page is loaded with no loading spinner.
A page is loaded with a loading spinner.
I would like to write code that would satisfy both scenarios and let the test just continue.
If the page does not have a loading spinner element: Continue with the tests as usual.
If the page does have a loading spinner element: Wait until the element disappears and then continue
Cypress has a lot of fancy functions but the documentation on how to use them just isn't clear enough.
I tried with the following piece of code:
try {
cy.getByTestId('loader-spinner')
.should('exist')
.then(el => {
el.should('not.exist');
});
} catch (error) {
cy.getByTestId('loader-spinner').should('not.exist');
}
Because of the timing aspect it can be tricky to get this test right.
Controlling Triggers
You really need to know what controls the spinner - usually it's a call to API. You can then delay that call (or rather it's response) to "force" the spinner to appear.
To do that, use an intercept
cy.intercept(url-for-api-call,
(req) => {
req.on('response', (res) => res.delay(100)) // enough delay so that spinner appears
}
)
// whatever action triggers the spinner, e.g click a button
cy.getByTestId('loader-spinner') // existence is implied in this command
// if the spinner does not appear
// the test will fail here
cy.getByTestId('loader-spinner').should('not.exist') // gone after delay finishes
Two scenarios
First off, I don't think your two scenario idea is going to help you write the test correctly.
You are trying to conditionally test using try..catch (nice idea, but does not work). The trouble is conditional testing is flaky because of the timing aspect, you get the test working in a fast environment then it starts to break in a slower one (e.g CI).
Better to control the conditions (like delay above) then test page behaviour under that condition.
To test that the spinner isn't appearing, return a stub in the intercept It should be fast enough to prevent the spinner showing.
cy.intercept(url-for-api-call, {stubbed-response-object})
// whatever action triggers the spinner, e.g click a button
cy.getByTestId('loader-spinner').should('not.exist') // never appears
Take a look at When Can The Test Blink?
You should be able to just use a should('not.exist') assertion, causing Cypress to wait for the element to not exist. Remember, Cypress automatically retries up until the timeout, so if you haven't changed the global timeout, then the following will try for up to 4 seconds.
cy.getByTestId('loader-spinner')
.should('not.exist');
If you find the test failing because the element still exists, you can bump the timeout. Below, I've defined a 10s (10000ms) timeout for the should() command.
cy.getByTestId('loader-spinner')
.should('not.exist', { timeout: 10000 });
Additionally, you may find that the element does still exist, but is not visible. In that case, change not.exist to not.be.visible

Cypress interactions commands race condition

When I try to, for example, click on a button using Cypress, the get command will get the button before it is actionable (still invisible for example). The click command later will fail because the subject passed to it is not actionable. How to avoid this behavior?
Try adding a visibility assertion
cy.get('#button')
.should('be.visible')
.click();
You can add visibility check as well as make sure the button is enabled and then perform a click().
cy.get('selector').should('be.visible').and('be.enabled').click()
I won't suggest you to overwrite an existing cypress command, instead create a custom command under cypress/support/commands.js like this:
Cypress.Commands.add('waitAndClick', (selector) => {
cy.get(selector).should('be.visible').and('be.enabled').click()
})
And in your test you can add:
cy.waitAndClick('button')

Custom child command in cypress doesn't perform click

Background:
I'm writing test automation for a React web application, using Cypress. In the application I have a dialog box in which there are elements I need to click. When I try to click any of these elements normally, Cypress gives me an error that the element in not visible because its content is being clipped by one of its parent elements, which has a CSS property of overflow: 'hidden', 'scroll' or 'auto'. Because these DOM elements are generated by some 3rd party React components, I cannot change this, and the only way I can work-around it is to use {force:true} in the click command.
The problem:
Because I have few of these elements and in order to keep the DRY principle, I wanted to create a custom child command named forceClick that simply wraps subject.click({force:true}). However, for some reason, when I do that, Cypress does not perform the click command at all!
Note: For debugging purposes I added a cy.log command to the custom command as well, and strangely enough, I see that this log command is executed and only the click command doesn't.
Here's the code:
Cypress.Commands.add('forceClick', {prevSubject:'element'}, subject => {
cy.log('forceClick was called!');
subject.click({force:true})});
And inside my test I have the following line:
cy.get("[data-test='panel-VALUES']").forceClick();
Note that if I change it to the following line, it works as expected:
cy.get("[data-test='panel-VALUES']").click({force:true});
Any idea why the click command isn't executed by the forceClick custom command?
You are almost there, you just missed that you have to wrap the subject if you want to work with it.
Cypress.Commands.add('forceClick', {prevSubject: 'element'}, (subject, options) => {
// wrap the existing subject and do something with it
cy.wrap(subject).click({force:true})
})
I never saw a solution with subject.click({force:true}), I'm not saying it won't work, but I just never saw it before. What works anyway is this:
Custom command:
Cypress.Commands.add('forceClick', {prevSubject:'element'}, subject => {
cy.log('forceClick was called!');
cy.get(subject)
.click({force:true})});
}
Test step:
cy.forceClick('[data-test="panel-VALUES"]');
If you only use the forceClick you could even shorten it further to this:
Custom command:
Cypress.Commands.add('forceClick', {prevSubject:'element'}, subject => {
cy.log('forceClick was called!');
cy.get(`[data-test=${subject}]`)
.click({force:true})});
}
Test step:
cy.forceClick('panel-VALUES');

Capybara wait until button is enabled?

Surprised I actually haven't come across this, but I have a simple button that is disabled until a dropdown is selected. Sometimes the page isn't fast enough to "enable" the button to be clicked on after the previous dropdown is selected causing it to fail.
I could throw in a "sleep" of a second or two and fix this, but that seems like a lazy/poor way to do this.
Is there a way in capybara (or purely selenium) that I can make it wait until the button is actually enabled? I'd like to throw this is the page model method for this button (as im trying to avoid API specific methods/selenium/etc... in the actual test specs (Although I can if I need to).
FWIW this is specifically for Ruby's capybara framework but pure selenium is fine as well.
Assuming the button you're referring to is actually a button (<button> element, or <input> element with type submit, reset, image, or button) then Capybaras :button selector will (by default) wait for it to be non-disabled.
click_button('Something')
or
find_button('button_id').click
or
find(:button, 'button_value').click
If any of the finder or action methods aren't waiting long enough for a specific element you can always increase the maximum wait time for a specific finder/action by passing a :wait option
find(:button, 'Something', wait: 10).click
If you're not using selector types (if not, why not) and instead are just using raw CSS to locate the element then you can use the :enabled pseudo class along with your existing CSS and something like
find('#my_button:enabled', wait: 10).click
If the element you're calling a button isn't actually a button but some other type of element (<a> etc) styled to look like a button, then you'll need to explain exactly how you're disabling the "button".
In Python you can do something like this:
def wait_until_clickable(driver, xpath, timeout = 1):
while timeout > 0:
try:
element = driver.find_element_by_xpath(xpath)
element.click()
return element
except:
time.sleep(0.1)
timeout = timeout - 0.1
return False

Resources