I got form and when I trigger click on submit button this cause to refresh current page. After that I would like to do some assertions. How I know that page finished refreshing and I can start to searching an element to do assertions?
Looking for something like page.waitForNavigation
in Puppeteer.
cy.get('#formButton').click() // adds new item and refresh page
// do assertions on page
Hey @kkorus I think this FAQ should help answer your question, but basically cy.visit()
automatically does was Puppeteer's page.page.waitForNavigation()
does - waits until the load
event fires.
https://on.cypress.io/using-cypress-faq#How-do-I-wait-for-my-application-to-load
Edit by @jennifer-shehane - Added link to FAQ. Sorry y'all 馃槵
@jennifer-shehane
cy.visit()
- without a URL?A workaround I found was
// click (page starts reloading)
cy.wait(3000);
// check the changed content
@tivnet yeah, I saw that, but it looks like a hack for me rather then solution.
Cypress automatically detects and waits for the page to finish loading. You don't need to be concerned with it. It will pause command execution, prevent them from timing out, and then begin re-running them once the page transition is complete.
@brian-mann
The script below:
Cypress.config(
{
"baseUrl": "https://www.artbylena.com"
}
);
describe("Test", () => {
const DOM_PRICE_CURRENCY_SYMBOL = ".woocommerce-Price-amount > .woocommerce-Price-currencySymbol";
it('Switches currencies', () => {
cy.visit("/paintings/");
cy.get(DOM_PRICE_CURRENCY_SYMBOL).contains("US$");
cy.get('.wpglobus-currency-selector').select("CAD");
// cy.wait(2000);
cy.get(DOM_PRICE_CURRENCY_SYMBOL).contains("C$");
});
});
fails without cy.wait
:
and works with it:
@tivnet I faced this sort of thing already, and even though it works setting a cy.wai()
in between, you don't want to go that way as setting an implicit wait might delay your tests, you can read more here
Have also in mind that .contains('C$')
is not an assertion, and .should('contain', 'C$')
is.
Hope this is useful info
@davidzambrana
That's a general "anti-pattern" notice. However, I am not sure how to avoid it in my specific case. When a click results in a different page, I do not need to wait. Here, it's the same page, reloaded by a JS call. As you see, without the wait, it results in a timeout.
P.S.
Regarding the .contains
- yes, that's not an assertion, but it works perfectly. I use .should
when I need more than one assertion within the same DOM node.
In my case for example when I had to check a field that changed its value after some action, I also had to check that a toast showed up, so when I triggered the click, I included in a .then
clause that the toast showed up and afterwards that the new value was loaded in the field. But this was a workaround that worked in my case.
@tivnet excellent example - in your test, you need _something_ observable that shows how the new page is different after reload. Since the url stays the same, the only difference is that the new page sets the cookie wpglobus-forced_currency
to a new value. So here is the test - just use getCookie
https://docs.cypress.io/api/commands/getcookie.html and make an assertion.
/// <reference types="cypress" />
describe("Test", () => {
const DOM_PRICE_CURRENCY_SYMBOL = ".woocommerce-Price-amount > .woocommerce-Price-currencySymbol";
it('Switches currencies', () => {
cy.visit("/paintings/");
cy.contains(DOM_PRICE_CURRENCY_SYMBOL, "US$");
cy.get('.wpglobus-currency-selector').select("CAD");
// cy.wait(2000);
cy.getCookie('wpglobus-forced_currency').should('have.property', 'value', 'CAD')
cy.contains(DOM_PRICE_CURRENCY_SYMBOL, "C$");
});
});
The GUI shows how this assertion only passes after the page reloads
@davidzambrana contains
is a valid command that finds element that contains given text. But you can collapse get(selector).contains(text)
to just cy.contains(selector, text)
https://docs.cypress.io/api/commands/contains.html#Syntax
Bonus: notice /// <reference types="cypress" />
at the top of my JS spec file. In VSCode this will turn on correct IntelliSense which we highly recommend https://docs.cypress.io/guides/tooling/intelligent-code-completion.html#Triple-slash-directives
@bahmutov Thanks, Gleb, it works perfectly and even adds an additional assertion for the cookie value.
Don't you think that Cypress should wait for the page reload anyway? It knows that the page is being reloaded, but tries to search the old DOM.
Intellisense works in *Storm IDE without additional setup.
Do you know if there an easy way to have it for the custom commands, too?
Thanks for the GREAT TESTING SUITE 馃
@tivnet so the problem is that Cypress cannot know how long to wait for potential page reload. Maybe the page will reload in 50ms or 500ms or 5 seconds - there is no way to predict this. So when it can grab the element right away (and it is found!) then it just goes with that.
Glad to hear about WebStorm IDE doing good job understanding types, nice. For custom commands you need to describe them, which is extra work (I prefer just using functions instead of adding custom commands). See https://github.com/cypress-io/add-cypress-custom-command-in-typescript
Thanks a lot for positive feedback, appreciate this
Sorry, I failed to paste the FAQ link on loading 馃槵
https://on.cypress.io/using-cypress-faq#How-do-I-wait-for-my-application-to-load
I have this page where a user can register and reloads into the same url with the user logged in. I'm doing an assertion with cy.getCookie('uid')
after triggering signup requests and a page reload but the assertion triggers before it:
@jindrake Providing the test code run would be helpful.
Also try asking in our community chat, searching our existing GitHub issues, reading through our documentation, or searching Stack Overflow for relevant answers.
Hi @jennifer-shehane
I am also seeing the same issue as @jindrake mentioned
In my application SessionID is set in local storage after user clicks on login
I have Code Like This:
Given I login in to App
And I navigate to addresses
in the step definition of " I navigate to addresses" i am checking if session is not null or not using this code
expect(localStorage.read('CD-SessionID')).not.to.be.null;
The Assertion is being executed right away with out waiting for first step
Hey @darasandeep91, could you provide the full Cypress test code that is being run? The first and second step?
HI @jennifer-shehane
Here are the steps i am trying to perform:
when i run the test even before the login step i am getting the error stating expected null not to be null
Given(/^I have logged in to app as "([^"]*)"$/, (userType) => {
cy.fixture('selfcareUsers').then((user) => {
const { [userType]: { username } } = user;
const { [userType]: { password } } = user;
cy.Login(username, password);
});
});
Given(/^I navigate to "([^"]*)" page$/, (pageName) => {
expect(localstorage.read('CD-SessionID')).not.to.be.null;
cy.fixture('pages').then((pages) => {
cy.visit((pages[pageName]));
});
});
Here is code for Cy.login()
Cypress.Commands.add('Login', (userName, password, environment, businessUnit) => {
cy.visit('/login');
setEnvironment(environment, businessUnit);
cy.get('#login').type(userName);
cy.get('#password').type(password);
cy.get('button:contains("Sign In")').click();
});
I believe the localstorage.read('CD-SessionID')
is evaluating to null
, so your assertion is accurately failing. Can you verify that?
The real solution to the problem in the original post is this:
cy.click('#someButtonToNavigateOnNewPage');
cy.location('pathname', {timeout: 10000})
.should('include', '/newPage');
cy.click('#YouAreOnNewPage');
@jennifer-shehane Not sure why you link to docs about how cy.visit()
works when the issue is clearly about waiting for a page to load after the cy.click()
I have the same problem except that I don't know the path ahead of time. I'm submitting a form that creates a new object whose id is in the location url, so I can't even use @msty's solution. The only option I have is to slap an arbitrary cy.wait() in there. Why isn't there a generic, 'wait until page loads' function? Or am I missing something?
I'm new-ish to cypress but have been using it for a few months and this is still the thing I struggle with the most. I think testing that a page loads (refresh, nagivation, whatever) is something that should be easily testable. For example, if the application triggers a refresh upon user selection of a select menu, you should be able to easily test that it occurs.
So like:
cy.get('[data-test="shipping-select"]').select("UPS");
cy.pageRefreshed(); // <-- why not?
As mentioned before, I'm supposed to be looking for something that changed in the DOM rather than looking for the page refresh itself. But in this case, there is nothing different. If you try to assert that "UPS" is selected it will be true both before and after the page is refreshed.
Perhaps there is a way to do this or perhaps I am indulging in an anti-pattern.
Here is a solution for anyone wondering what to do if the URL stays the same, and the page itself stays the same: add a property to the window
object. That property will be gone after reload.
Example: here is the page markup
<body>
<button id="button">Click to reload</button>
<script>
document.getElementById('button').addEventListener('click', () => {
console.log('clicked')
setTimeout(() => {
console.log('reloading page after delay')
location.reload()
}, 2000)
})
</script>
</body>
and here is the test
it('reloads', () => {
cy.visit('index.html')
// mark our window object to "know" when it gets reloaded
cy.window().then(w => w.beforeReload = true)
// initially the new property is there
cy.window().should('have.prop', 'beforeReload', true)
cy.get('#button').click()
// after reload the property should be gone
cy.window().should('not.have.prop', 'beforeReload')
})
Notice how the test waits as long as necessary using .should
and built-in https://on.cypress.io/retry-ability
Nice - that's a great idea. Thanks for cypress, it's really alot of fun to work with!
I've used the @msty solution with one modification, I've to check that the body is there before to access anything cy.get('body');
, I'm waiting for an API call after the change the location with cy.wait('@api')
and this wait only works if I get the body first.
cy.click('#someButtonToNavigateOnNewPage');
cy.location('pathname', {timeout: 10000})
.should('include', '/newPage');
cy.get('body');
I also used the @msty solution but with a little bit of modification, which I think it a bit more specific and nice since you can do whatever logic you want with the function.
cy.location({ timeout: 15000 }).should(($url) => {
expect($url).to.match(/http:\/\/localhost:3000\/event\/1dc49a776b63be235aecccfd\/1573165592600\/payment\/v2\/.{0,}\/confirmation/)
});
And I use the regex .{0,}
for any object id, since the payment object is different for each payment.
Could be a feature request a method like page.waitForNavigation({ waitUntil: 'networkidle0' }),
from puppetter ? Any other 'solution' seems not correct for me.
I met the same problem, and I use cy.wait() https://docs.cypress.io/api/commands/wait.html#Syntax solved it.
Here's another solution from the Cypress docs:
// Wait for the route aliased as 'getAccount' to respond
// without changing or stubbing its response
cy.server()
cy.route('/accounts/*').as('getAccount')
cy.visit('/accounts/123') // or, in our case: cy.get('.submit-button').click()
cy.wait('@getAccount').then((xhr) => {
// Make assertions here
})
My solution to check for a reload ist:
cy.window().its('performance.navigation.type').should('eq', 0); // checks for 'TYPE_NAVIGATE'
doReloadStuff();
cy.window().its('performance.navigation.type').should('eq', 1); // checks for 'TYPE_RELOAD'
Look here for further details: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation/type., it's deprecated but still works. The newer option performance.getEntriesByType('navigation')[0].type
will need more code.
@vitorpiovezam There is an open issue proposing a 'waitForNetworkIdle' feature here: https://github.com/cypress-io/cypress/issues/1773
Cypress automatically detects and waits for the page to finish loading. You don't need to be concerned with it. It will pause command execution, prevent them from timing out, and then begin re-running them once the page transition is complete.
Its is not happening in my case I have to give wait cy.wait() to avoid timeout issue to navigate to another page after a click button .
My use case:
what is my best option?
Im using svelte btw
If it's helpful to anyone else, I adapted https://github.com/cypress-io/cypress/issues/1805#issuecomment-525482440 into this custom command:
/**
* Given a function with some commands that cause the page to change or even
* just reload, this command runs the command then waits for that page load.
*
* Ideally this command should be used sparingly, instead preferring to use
* matching functionality to wait for reload.
*
* Adapted from:
* https://github.com/cypress-io/cypress/issues/1805#issuecomment-525482440
*/
Cypress.Commands.add("waitForPageLoadAfter", block => {
// mark our window object to "know" when it gets reloaded
cy.window().then(win => {
// eslint-disable-next-line no-param-reassign
win.beforeReload = true;
});
// initially the new property is there
cy.window().should("have.prop", "beforeReload", true);
// Run the code that triggers the page reload/change
block();
// after reload the property should be gone
cy.window().should("not.have.prop", "beforeReload");
});
Then use it like:
cy.waitForPageLoadAfter(() => { cy.contains("button", "Click here to reload"); });
cy.contains("page reloaded!").should("exist");
thanks, @bahmutov !
How about:
npm i -D cypress-wait-until
In cypress/support/commands.js
:
import 'cypress-wait-until';
And:
cy.click('#someButtonToNavigateOnNewPage');
cy.waitUntil(() => cy.url().should('contain', '/newPage'));
cy.click('#YouAreOnNewPage');
@emilong, by adding a timeout, it's possible to have a longer running request:
cy.window({timeout: 15000}).should("not.have.prop", "beforeReload");
Most helpful comment
The real solution to the problem in the original post is this:
@jennifer-shehane Not sure why you link to docs about how
cy.visit()
works when the issue is clearly about waiting for a page to load after thecy.click()