Wait until element has disappeared in Cypress - cypress

please, tell me what methods to use to wait for a loading screen element to disappear? PS. I'm not using an API request.
I tried to use two methods, but it doesn't work properly:
1. cy.get('#loading', { timeout: 30000 }).should('not.be.visible');
I get the error: -- Timed out retrying after 30000ms: Expected to find element: #loading, but never found it.
2. Used plugin (cypress-wait-until) like so cy.waitUntil(() => {document.querySelector('#loading') === null};
This approach doesn't find the element at all.

If you are trying to retrieve an element that is not in the DOM, use not.exist instead:
cy.get('#loading').should('not.exist');
In cases where you do need to wait, you can try using cy.wait:
An example use case for this might be if Cypress has to route to your page first and you want to ensure the page loads before you start testing:
cy.wait(200);

Related

Cypress: Switching from cy.route() to cy.intercept()

It seems that most people I read about experence zero trouble with this. I, on the other hand, have a test suite which someone else wrote, in which I'm trying to replace route() with intercept(). The API intercepts are done to handle button clicks etc., and about 99.9% percent of them fails if I just replace it. So, there's obviously some syntax in/use of intercept() I've not found a description for.
Example:
This works:
cy.route('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', result);
This does not work. The button click is not executed:
cy.intercept('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', result);
I've tried adding '**' in front of "/prosjekt...", and I've tried removing 'POST', with no luck.
Any ideas? I'll gladly post more info if necessary.
UPDATE:
Futher attempts:
Getting some hints here and there, it seems that this is a more correct way of using intercept():
return cy.intercept('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', {
body: result
});
This doesn't work, either.
The variables result in these examples is an object describing what is sent back to the frontend of the POST-request in the route matches the api path.
For troubleshooting, I can see that when using intercept(), there is ONE route that is not working when using intercept (the bottom one in the picture). However, I cannot for the life of me see why, and how the route match can be written differently?
Most likely, you're mixing the old use of cy.route() and cy.server(). In my experience, those two won't work well together. It's easier when you're starting fresh with just cy.intercept().
Your update is correct too; You have to encapsulate the return value you want mocked in {body: value}.
from what I am seeing in your circled screenshot, the API is not called after you try to intercept it. (the count under # column is -)
You need to track when the API is to be called and ensure you intercept before the call is made. Cypres can help you with this. You can go through the run steps in the cypress window.
You could also share this if you don't mind.
If you are 100% certain the button makes the call. Steps should be:
cy.intercept()
cy.get('button').click()
In the cypress window, right after the click, you should see the API being called.

How to check each element in Cypress so that it does not fail when none are found

In my case I have a varying amount of elements with someclass class name on the pages. I need to run some validations on them for which I'm using the following command:
cy.get(".someclass").each(someValidation)
This works fine on other cases, but there might also be cases when the page we're checking contains none of .someclass elements. This is fine, and the tests should not fail. However, using the above command fails (after waiting for the timeout to end):
Timed out retrying after 20000ms: Expected to find element: .someclass, but never found it.
Solved with some jQuery hacks:
const $els = Cypress.$('.someclass')
if ($els.length > 0)
cy.wrap($els).each(someValidation)

Cypress: How to capture text from a selector on one page to use as text on another page

New cypress user here, I am aware that cypress does not handle variables like how testcafe and others do due to the asyn nature of it. Using the example given and what I could find I have this as an example:
cy.get('selector').invoke('text').as('text_needed')
cy.get('#text_needed')
const txtneeded = this.text_needed
cy.log(txtneeded)
This looks at a given selector, takes what it finds and uses it as text and set it as a variable usable at any time in the test and outputs it to the log. The plan is to use that text in a search filter in another page to find the item it references.
The problem is that it fails with Cannot read properties of undefined (reading 'text_needed')
Is this because the content of the selector is not assigned to text properly, the outer html is <a data-v-78d50a00="" data-v-3d3629a7="" href="#">PO90944</a> The PO90944 is what I want to capture.
Your help would be appreciated!
You cannot save an alias and access it via this.* in the same execution context (callback) because it's a synchronous operation and your alias is not yet resolved at this time.
This is a correct way to go:
cy.get('selector').invoke('text').as('text_needed')
cy.get('#text_needed').then(txtneeded => {
cy.log(txtneeded)
})
First, make sure to define it as traditional function, not as an arrow function as this context doesn't work as you'd expect there, more info here.
Next, typically in a single test you should use .then() callback to perform additional actions on elements, and use aliases when you need to share context between hooks or different tests, so please consider the following:
// using aliases together with this within the single test won't work
cy.get(<selector>).invoke('text').as('text_needed')
cy.get('#text_needed').should('contain', 'PO90944') // works fine
cy.log(this.text_needed) // undefined
// this will work as expected
cy.get(<selector>).invoke('text').then(($el) => {
cy.wrap($el).should('contain', 'PO90944'); // works fine
cy.log($el) // works fine
});
Setting alias in beforeEach hook for example, would let you access this.text_needed in your tests without problems.
Everything nicely explained here.
Edit based on comments:
it('Some test', function() {
cy.visit('www.example.com');
cy.get('h1').invoke('text').as('someVar');
});
it('Some other test', function() {
cy.visit('www.example.com');
cy.log('I expect "Example Domain" here: ' + this.someVar);
});
And here's the output from cypress runner:

Cypress - How to Wait for an attribute value to be false in Cypress

I have a website which has everything in an iFrame. My scripts are failing because cypress is not able to wait for loading bar as cypress does not support iframes as of now. I want to write a custom command which wait until attributes 'ng-reflect-loading' value is changed to false.
I have tried with below code but its not working and halts cypress runner. Reference Thread 136
cy.get('iframe').iframeLoaded().its('document').getInDocument('.main >ng-component > :nth-child(1)').then(function($loading) {
while( $loading.attr('ng-reflect-loading')!='false')
{
cy.log('waiting')
}
})
Can anyone please help on this.
I don't have any insight of how iFrames work. But is 'ng-reflect-loading' eventually hidden?
In that case this check should work:
cy.get('.ng-reflect-loading')
.should('not.exist')
If it remains existing but the value should change you could either check on the first value not existing:
cy.get('.ng-reflect-loading')
.should('not.contain.attr', 'attr_name', 'first value')
Or if the value changes, you could check for the second value to be available:
cy.get('.ng-reflect-loading')
.should('contain.attr', 'attr_name', 'second value')

How do i clear a multi-select input using Cypress?

How do i clear (deselect) all options within a multi-select input using Cypress?
The documentation here does not seem to cover this scenario: https://docs.cypress.io/api/commands/select.html#Syntax
The clear() command apparently cannot be applied to selects, as you might expect
https://docs.cypress.io/api/commands/clear.html#Syntax
I have tried passing an empty string / array / null to select() but i get the following error:
CypressError: Timed out retrying: cy.select() failed because it could not find a single <option> with value or text matching: ''
Since Cypress 8.6.0, this is supported in Cypress: https://stackoverflow.com/a/69537449/3474615
Before 8.6.0:
You can use cy.invoke() to call the .val('') method on the underlying JQuery element, which will cause it to be cleared.
Like this:
cy.get('select')
.invoke('val', '')
Note that this will not emit the change event. If you want to test that your code reacts to the change event, you can cause it to be emitted with cy.trigger():
cy.get('select')
.invoke('val', '')
.trigger('change')
I've also filed a feature request for this functionality, as it does seem like something that should be included with Cypress by default.
cy.select([])
cy.select([]) called with an empty array, can now be used to clear selections on all options.
Note: update cypress to v8.6.0 or higher to work.

Resources