I'm new to Cypress. My app as a "routing system" manually changes window.location.hash
.
At some point, I click on a button that changes the hash and consequently should change the page during the test. I can see a "new url" entry appearing during the execution, but how can I make cypress visit that url?
In few words, what the problem is: you can see I type the password and then {enter}
. Running the test I can see the hash change in the address bar, but the page doesn't change in accord to the hash change.
This is the testing code
context("Workflow", () => {
it("login", () => {
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demo").should("have.value", "demouser")
cy.get("#password").type("demo{enter}").should("have.value", "demo") // this should redirect to "/#home"
//cy.wait(10000)
cy.get(".subtitle").should("have.value", "Welcome") //this line fails as ".subtitle" is an element of "/#home"
})
})
EDIT: Following tons of failed attempts, I came up with a partially working, clunky and hacky solution. I think I shouldn't need to use reload()
to solve that (there must be a better solution..), but to make it works I have to wait for all the remote requests to be done (otherwise reload()
cancels them). I say partially working because you can see from the comments in the code if I try to visit #login
first, then follow the redirect in #home
and then change the page to #browser
, the last one doesn't work (I can see the hash changing to #browser
, but the page is still #home
).
import 'cypress-wait-until';
let i = 0;
context("Workflow", () => {
it("login", () => {
cy.server( {
onRequest: () => {
i++;
},
onResponse: () => {
i--;
}
});
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demouser").should("have.value", "demouser")
cy.get("#password").type("demouser").should("have.value", "demouser")
cy.get("form#formLogin").submit()
cy.waitUntil(() => i > 0)
cy.waitUntil(() => i === 0)
cy.reload(); // it correctly change the hash AND the page to #home!
cy.url().should("include", "#home")
cy.get(".version").contains( "v2.0.0-beta") // it works!!
cy.get("a[data-id=browser]").click({force: true}) // it correctly changes the hash to #browser
cy.waitUntil(() => i > 0)
cy.waitUntil(() => i === 0)
cy.reload();
// the hash in the address bar is #browser, but the web page is #home
})
})
I'm new to Cypress. My app as a "routing system" manually changes window.location.hash
.
At some point, I click on a button that changes the hash and consequently should change the page during the test. I can see a "new url" entry appearing during the execution, but how can I make cypress visit that url?
In few words, what the problem is: you can see I type the password and then {enter}
. Running the test I can see the hash change in the address bar, but the page doesn't change in accord to the hash change.
This is the testing code
context("Workflow", () => {
it("login", () => {
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demo").should("have.value", "demouser")
cy.get("#password").type("demo{enter}").should("have.value", "demo") // this should redirect to "/#home"
//cy.wait(10000)
cy.get(".subtitle").should("have.value", "Welcome") //this line fails as ".subtitle" is an element of "/#home"
})
})
EDIT: Following tons of failed attempts, I came up with a partially working, clunky and hacky solution. I think I shouldn't need to use reload()
to solve that (there must be a better solution..), but to make it works I have to wait for all the remote requests to be done (otherwise reload()
cancels them). I say partially working because you can see from the comments in the code if I try to visit #login
first, then follow the redirect in #home
and then change the page to #browser
, the last one doesn't work (I can see the hash changing to #browser
, but the page is still #home
).
import 'cypress-wait-until';
let i = 0;
context("Workflow", () => {
it("login", () => {
cy.server( {
onRequest: () => {
i++;
},
onResponse: () => {
i--;
}
});
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demouser").should("have.value", "demouser")
cy.get("#password").type("demouser").should("have.value", "demouser")
cy.get("form#formLogin").submit()
cy.waitUntil(() => i > 0)
cy.waitUntil(() => i === 0)
cy.reload(); // it correctly change the hash AND the page to #home!
cy.url().should("include", "#home")
cy.get(".version").contains( "v2.0.0-beta") // it works!!
cy.get("a[data-id=browser]").click({force: true}) // it correctly changes the hash to #browser
cy.waitUntil(() => i > 0)
cy.waitUntil(() => i === 0)
cy.reload();
// the hash in the address bar is #browser, but the web page is #home
})
})
Share
Improve this question
edited May 9, 2020 at 21:26
alfredopacino
asked Apr 30, 2020 at 20:05
alfredopacinoalfredopacino
3,2419 gold badges47 silver badges79 bronze badges
12
|
Show 7 more comments
3 Answers
Reset to default 8 +25Cypress has an event called url:changed
. See doc on Events here
Using this, something like this may work:
context("Workflow", () => {
it("login", () => {
cy.on('url:changed', url => {
cy.location('hash').then(hash => {
if (!url.endsWith(hash)) {
cy.visit(url);
}
});
});
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demo").should("have.value", "demouser")
cy.get("#password").type("demo{enter}").should("have.value", "demo") // this should redirect to "/#home"
cy.get(".subtitle").should("have.value", "Welcome") //this line fails as ".subtitle" is an element of "/#home"
})
})
Edit: just for troubleshooting purposes and to focus on your issue, can you try it like this without cy.location()
:
context("Workflow", () => {
it("login", () => {
cy.on('url:changed', url => {
cy.visit(url);
});
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demo").should("have.value", "demouser")
cy.get("#password").type("demo{enter}").should("have.value", "demo") // this should redirect to "/#home"
cy.get(".subtitle").should("have.value", "Welcome") //this line fails as ".subtitle" is an element of "/#home"
})
})
Edit: Have you tried cy.reload()
context("Workflow", () => {
it("login", () => {
cy.visit("http://localhost:3000/src/#login")
cy.get("#username").type("demo").should("have.value", "demouser")
cy.get("#password").type("demo{enter}").should("have.value", "demo") // this should redirect to "/#home"
cy.reload();
cy.get(".subtitle").should("have.value", "Welcome");
})
})
Thanks for all the attempts to solve, but they were all tricky workarounds to something as simple as "trigger the related listener when window.location.hash
changes".
The root of the problem was a bug in Cypress. The bug on window.location.hash
is present in 4.5.0
, but it has been solved somewhere between 4.5.0
and 4.12.1
.
In 4.12.1
the problem is solved.
There are some ways to do that. One of the basic approach is like this:
cy.visit(`your_login_page`)
cy.get('[data-testid="input-username"]').type(`demo`, { force: true })
cy.get('[data-testid="input-password"]')
.type(`demo`, {
force: true,
})
.wait(1000)
.then(() => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq(your_new_url)
})
})
You should add data-testid
or findByTestId
to your elements with a unique id.
To visit the new URL, you can use cy.visit(loc.pathname)
cy.visit()
? – jmargolisvt Commented Apr 30, 2020 at 20:12cy.visit()
takes the url as argument. I don't need that. I simply need to visit the page the menu button is pointing to. – alfredopacino Commented Apr 30, 2020 at 21:03window.location.hash
. When you manually click on the button in the webpage, are you redirected to the anchor? – natn2323 Commented May 2, 2020 at 6:17cy.find("#mybutton").click()
and cypress simply doesn't change the page. – alfredopacino Commented May 2, 2020 at 13:19cy.find("#mybutton")
is invalid syntax, see here. It would be much more helpful to show the test code. – Richard Matsen Commented May 5, 2020 at 12:22