Cypress:Is there any way to check invisibility of an element - cypress

In our application when user do some actions that require communicatiion with server -a loading bar will be displayed on top panel.Once the action completed-Loading bar will be disappeared.In tests we are using this as a check before we move to next step.
In selenium i am checking the disappearance of loading bar as shown below
WebDriverLongWait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.Id("loading-bar")));
Is there a similar way to check invisibility of an element in Cypress
instead of waiting for loading bar i am waiting for the request to finish
as shown below cy.server()
cy.route('POST','**/saveExpression').as('saveExpression')
cy.get('.IEE-save-button',{ timeout: 100000 }).contains('Apply Expression').click();
cy.wait('#saveExpression').then((xhr)=>
{
cy.contains('browse',{timeout: 60000}).click()
})

In this scenario, it's ideal to use .should('not.be.visible'). To place in your example as below,
cy.get('#loading-bar').should('not.be.visible')
Since loading bar indicator is dependent on some network request, you can wait for the XHR request to finish before making an assertion. You could use the wait() function of cypress. For instance:
// Wait for the route aliased as 'getAccount' to respond
cy.server()
cy.route('/accounts/*').as('getAccount')
cy.visit('/accounts/123')
cy.wait('#getAccount').then((xhr) => {
cy.get('#loading-bar').should('not.be.visible')
})

Related

Cypress cy.wait() only waits for the first networkcall, need to wait for all calls

I would like to wait until the webpage is loaded with items. Each is getting retreived with a GET.
And I would like to wait on all these items until the page is loaded fully. I already made a interceptions for these. Named: 4ItemsInEditorStub
I have tried cy.wait('#4ItemsInEditorStub.all')
But this gives an timeout error at the end.
How can I let Cypress wait untill all "4ItemsInEditorStub" interceptions are completed?
Trying to wait on alias.all won't work -- Cypress has no idea what .all means in this context, or what value it should have. Even after your 4 expected calls are completed, there could be a fifth call after that (Cypress doesn't know). alias.all should only be used with cy.get(), to retrieve all yielded calls by that alias.
Instead, if you know that it will always be four calls, you can just wait four times.
cy.wait('4ItemsInEditorStub')
.wait('4ItemsInEditorStub')
.wait('4ItemsInEditorStub')
.wait('4ItemsInEditorStub');
You can either hard code a long enough wait (ie. cy.wait(3_000)) to cover the triggered request time and then use cy.get('#4ItemsInEditorStub.all')
cy.wait(10_000)
cy.get('#4ItemsInEditorStub.all')
// do some checks with the calls
or you can use unique intercepts and aliases to wait on all 4
cy.intercept('/your-call').as('4ItemsInEditorStub1')
cy.intercept('/your-call').as('4ItemsInEditorStub2')
cy.intercept('/your-call').as('4ItemsInEditorStub3')
cy.intercept('/your-call').as('4ItemsInEditorStub4')
cy.visit('')
cy.wait([
'#4ItemsInEditorStub1',
'#4ItemsInEditorStub2',
'#4ItemsInEditorStub3',
'#4ItemsInEditorStub4',
])
There is a package cypress-network-idle that makes the job simple
cy.waitForNetworkIdlePrepare({
method: 'GET',
pattern: '**/api/item/*',
alias: 'calls',
})
cy.visit('/')
// now wait for the "#calls" to finish
cy.waitForNetworkIdle('#calls', 2000) // no further requests after 2 seconds
Installation
# install using NPM
npm i -D cypress-network-idle
# install using Yarn
yarn add -D cypress-network-idle
In cypress/support/e2e.js
import 'cypress-network-idle'
Network idle testing looks good, but you might find it difficult to set the right time period, which may change each time you run (depending on network speed).
Take a look at my answer here Test that an API call does NOT happen in Cypress.
Using a custom command you can wait for a maximum number of calls without failing if there are actually less calls.
For example, if you have 7 or 8 calls, setting the maximum to 10 ensures you wait for all of them
Cypress.Commands.add('maybeWaitAlias', (selector, options) => {
const waitFn = Cypress.Commands._commands.wait.fn
return waitFn(cy.currentSubject(), selector, options)
.then((pass) => pass, (fail) => fail)
})
cy.intercept(...).as('allNetworkCalls')
cy.visit('/');
// up to 10 calls
Cypress._.times(10, () => {
cy.maybeWaitAlias('#allNetworkCalls', {timeout:1000}) // only need short timeout
})
// get array of all the calls
cy.get('#allNetworkCalls.all')
.then(calls => {
console.log(calls)
})

Cypress: how to wait for all requests to finish

I am using cypress to test our web application.
In certain pages there are different endpoint requests that are executed multiple times. [ e.g. GET /A GET /B GET /A].
What would be the best practise in cypress in order to wait for all requests to finish and guarantee that page has been fully loaded.
I don't want to use a ton cy.wait() commands to wait for all request to be processed. (there are a lot of different sets of requests in each page)
You can use the cy.route() feature from cypress. Using this you can intercept all your Get requests and wait till all of them are executed:
cy.server()
cy.route('GET', '**/users').as('getusers')
cy.visit('/')
cy.wait('#getusers')
I'm sure this is not recommended practice but here's what I came up with. It effectively waits until there's no response for a certain amount of time:
function debouncedWait({ debounceTimeout = 3000, waitTimeout = 4000 } = {}) {
cy.intercept('/api/*').as('ignoreMe');
let done = false;
const recursiveWait = () => {
if (!done) {
// set a timeout so if no response within debounceTimeout
// send a dummy request to satisfy the current wait
const x = setTimeout(() => {
done = true; // end recursion
fetch('/api/blah');
}, debounceTimeout);
// wait for a response
cy.wait('#ignoreMe', { timeout: waitTimeout }).then(() => {
clearTimeout(x); // cancel this wait's timeout
recursiveWait(); // wait for the next response
});
}
};
recursiveWait();
}
According to Cypress FAQ there is no definite way. But I will share some solutions I use:
Use the JQuery sintax supported by cypress
$('document').ready(function() {
//Code to run after it is ready
});
The problem is that after the initial load - some action on the page can initiate a second load.
Select an element like an image or select and wait for it to load. The problem with this method is that some other element might need more time.
Decide on a maindatory time you will wait for the api requests (I personaly use 4000 for my app) and place a cy.wait(mandatoryWaitTime) where you need your page to be loaded.
I faced the same issue with our large Angular application doing tens of requests as you navigate through it.
At first I tried what you are asking: to automatically wait for all requests to complete. I used https://github.com/bahmutov/cypress-network-idle as suggested by #Xiao Wang in this post. This worked and did the job, but I eventually realized I was over-optimizing my tests. Tests became slow. Test was waiting for all kinds of calls to finish, even those that weren't needed at that point in time to finish (like 3rd party analytics etc).
So I'd suggest not trying to wait for everything at a step, but instead finding the key API calls (you don't need to know the full path, even api/customers is enough) in your test step, use cy.intercept() and create an alias for it. Then use cy.wait() with your alias. The result is that you are waiting only when needed and only for the calls that really matter.
// At this point, there are lots of GET requests that need to finish in order to continue the test
// Intercept calls that contain a GET request with a request path containing /api/customer/
cy.intercept({ method: 'GET', url: '**/api/customer/**' }).as("customerData");
// Wait for all the GET requests with path containing /api/customer/ to complete
cy.wait("#customerData");
// Continue my test knowing all requested data is available..
cy.get(".continueMyTest").click()

Cypress async form validation - how to capture (possibly) quick state changes

I have some async form validation code that I'd like to put under test using Cypress. The code is pretty simple -
on user input, enter async validation UI state (or stay in that state if there are previous validation requests that haven't been responded to)
send a request to the server
receive a response
if there are no pending requests, leave async validation UI state
Step 1 is the part I want to test. Right now, this means checking if some element has been assigned some class -- but the state changes can happen very fast, and most of the time (not always!) Cypress times out waiting for something that has ALREADY happened (in other words, step 4 has already occurred by the time we get around to seeing if step 1 happened).
So the failing test looks like:
cy.get("#some-input").type("...");
cy.get("#some-target-element").should("have.class", "class-to-check-for");
Usually, by the time Cypress gets to the second line, step 4 has already ran and the test fails. Is there a common pattern I should know about to solve this? I would naturally prefer not to have change the code under test.
Edit 1:
I'm not certain that I've 100% solved the "race" condition here, but if I use the underlying native elements (discarding the jQuery abstraction), I haven't had a failure yet.
So, changing:
cy.get("#some-input").type("...")
to:
cy.get("#some-input").then(jQueryObj => {
let nativeElement = jQueryObj[0];
nativeElement.value = "...";
nativeElement.dispatchEvent(new Event("input")); // make sure the app knows this element changed
});
And then running Cypress' checks for what classes have / haven't been added has been effective.
You can stub the server request that happens during form validation - and slow it down, see delay parameter https://docs.cypress.io/api/commands/route.html#Use-delays-for-responses
While the request is delayed, your app's validation UI is showing, you can validate it and then once the request finishes, check if the UI goes away.

Protractor - changing pages/views

I have a test case in Protractor which loads the home page and then clicks a button that redirects to another page. In that other page I want to grab the value of an element.
describe('todo list', function() {
it('should find the contact phone number from the home page', function() {
browser.get('http://homepage...');
element(by.id('re_direct_to_contact_page')).click();
var number = element(by.id('phonenumber')).getText();
expect(number).toEqual('412-....-...');
});
});
However although it loads the page it does not check the element value and the test case fails.
How can Protractor load another page to check a value?
NOTE: - I made up this example but in my real test case I am sending data from one page to another so I cannot load the page I want directly.
EDIT: STACK TRACE
protractor conf.js Using the selenium server
at http://localhost:4444/wd/hub [launcher] Running 1 instances of
WebDriver
Error: Error while waiting for Protractor to sync with the page:
"[ng:test] http://errors.angularjs.or/1.4.0/ng/test"
StackTrace: undefined
1 test, 1 assertion, 1 failure
It's possible that you might be facing a timing issue. Try re-structuring your test from this:
var number = element(by.id('phonenumber')).getText();
expect(number).toEqual('412-....-...');
To this:
expect(element(by.id('phonenumber')).getText()).toEqual('412-....-...');
While Jasmine and Protractor try to resolve every promise before moving on, sometimes it doesn't happen as quickly as you would like. Putting the entire promise chain into the expect helps to enforce the resolution of all of them before going through the matcher.

How can I implement wait_for_page_to_load in Selenium 2?

I am new to automated web testing and I am currently migrating from an old Selenium RC implementation to Selenium 2 in Ruby. Is there a way to halt the execution of commands until the page gets loaded, similar to "wait_for_page_to_load" in Selenium RC?
I fixed a lot of issues I was having in that department adding this line after starting my driver
driver.manage.timeouts.implicit_wait = 20
This basically makes every failed driver call you make retry for maximum 20 seconds before throwing an exception, which is usually enough time for your AJAX to finish.
Try using Javascript to inform you!
I created a couple methods that checks via our javascript libraries and waits to see if the page has finished loading the DOM and that all ajax requests are complete. Here's a sample snippet. The javascript you will need to use is just going to depend on your library.
Selenium::WebDriver::Wait.new(:timeout => 30).until { #driver.execute_script("[use javascript to return true once loaded, false if not]"}
I then wrapped these methods in a clickAndWait method that clicks the element and calls the waitForDomLoad and waitForAjaxComplete. Just for good measure, the very next command after a clickAndWait is usually a waitForVisble element command to ensure that we are on the right page.
# Click element and wait for page elements, ajax to complete, and then run whatever else
def clickElementAndWait(type, selector)
#url = #driver.current_url
clickElement(type, selector)
# If the page changed to a different URL, wait for DOM to complete loading
if #driver.current_url != #url
waitForDomLoad
end
waitForAjaxComplete
if block_given?
yield
end
end
If you are using capybara, whenever you are testing for page.should have_content("foo"), capybara will not fail instantly if the page doesn't have the content (yet) but will wait for a while to see if an ajax call will change that.
So basically: after you click, you want to check right away for have_content("some content that is a consequence of that click").

Resources