Cypress Timeout after even the page is present - cypress

To avoid adding cy.wait, am trying to user the explicit wait, but seems it is broken. What I missed here
const APIAccountPage= {
APIAccount(name) {
//cy.wait(14000);
cy.server();
cy.route('GET', '/api/accounts').as('Accounts');
cy.wait('#Accounts', { timeout: 10000 })
.its('status').should('eq', 200);
cy.get('.vbutton__baseline___3XNit.fonts__letterSpacing___3l5GB.styles__searchIconWrapper___19oVL.styles__iconButton___LLzft')
.should('be.visible')
.should('not.be.disabled');
cy.get('.vbutton__baseline___3XNit.fonts__letterSpacing___3l5GB.styles__searchIconWrapper___19oVL.styles__iconButton___LLzft').click();
cy.wait(4000);
cy.get('.styles__patientSearchClass___2iGOV').type(name);
cy.wait(4000);
cy.get('.styles__patientSearchClass___2iGOV').contains(name).click();
}
}
module.exports = APIAccountPage;
Error Details
Cypress Window

Try this following, if the /api/account is not working, there may be multiple reason, one of which I am thinking is a backend api (which happened in my case) for which you need to handle the authentication.
If it is same then try to provide the complete URL like,
cy.server()
cy.route(
'GET',
'https://<URL>/api/accounts',
'{"errors":[]}',
).as('account');
and call cy.wait('#account') whichever place is appropriate.

Just a sidenote cy.server() and cy.route has been deprecated since 6.0.0, you can read about it from here. Instead, a new method has been introduced called cy.intercept().
cy.intercept('GET', 'https://some-url/api/accounts').as('Accounts')
cy.wait('#Account') //Wait

Related

How to get cypress to block datadog requests

We recently installed datadogRUM in our application and now so many DD events kick off in my cypress test that they cause a timeout and failure
I have tried cy.intercept in multiple ways:
cy.intercept('POST', 'https://rum.browser-intake-datadoghq.com/*', {
statusCode: 202,
body: {
},
}).as('datadogRUM');
cy.intercept('POST', 'https://rum-http-intake.logs.datadoghq.com/*', {});
cy.intercept(/\.*datadog.*$/, (req) => {
console.log('DATADOG INTERCEPTED');
req.reply("console.log('datadog intercept');");
});
cy.intercept({
method: 'POST',
url: '/\.*datadog.*$/'
}, req => {
req.destroy();
});
cy.intercept('POST', 'https://rum-http-intake.logs.datadoghq.com/*', { forceNetworkError: true });
just to start. I feel like I've tried every possible variation. I also created a cypress.json file in my /cypress folder
{
"blockHosts": "*datadoghq.com/*"
}
I get hundreds of calls back in my network tab to https://rum.browser-intake-datadoghq.com/api/v2/rum with the preview of console.log('datadog intercept') as I've intercepted them. They all display the solid blue line as if they are being intercepted and blocked. When I set the intercept to an alias I see the alias in my cypress runner window. But there are no 503s or 404s anywhere. The page still fills up with events, cypress gets overloaded, and my test times out.
I even tried copying the data-dog-rum.ts from the src/utils folder to cypress/utils and either commenting out everything or setting the sampleRate to 0, no dice.
EDIT: I am able to get the test passing by adding
Cypress.on('uncaught:exception', () => {
// returning false here prevents Cypress from
// failing the test
return false;
});
to my support/index.js but now whether I add a cy.intercept in my test makes absolutely no difference. The page still fills up with datadog requests regardless, and whether they come back as 200/pending/cancelled, they still delay a single it block in a spec to where it takes 60 seconds to run instead of approx 10 seconds
You can use javascript to perform the stub inside the routeHandler
cy.intercept('*', (req) => { // look at everything
if (req.url.includes('datadoghq')) { // add more conditions if needed
req.reply({}) // prevent request reaching the server
}
})
blockhosts should work with
Pass only the host
{
"blockHosts": "*datadoghq.com"
}

Cypress interception is not waiting

I'm using Cypress 6.0.0 new way of interception.
Waiting on a request
I need to wait for the "templatecontract" response in order to click the #template-button-next because otherwise is disabled. But is trying to click it before getting the response from the API. The documentation seems pretty straight forward.
Am I wrong here?
I have also tried just like:
cy.wait('#templatecontract')
cy.get('#template-button-next').click()
it("Test", function() {
cy.intercept(Cypress.env("baseUrl")+`/api/v1/contract-type/templatecontract`).as('templatecontract')
cy.login(Cypress.env('testUserInviteEmail'), Cypress.env('testUserInvitePassword')).then((token) => {
cy.visit(Cypress.env('baseUrl')+"/templates", {headers: {
Authorization: token,
},
});
cy.get('a[href="/create-template"]').click();
cy.get('.template-usecasetitle').contains('UBO-Formular')
cy.get('button[cy-data="Formular"]').click();
cy.get('#title').type("Title for testing");
cy.get('#usecasetitle').type("Usecasetitle for testing")
cy.get('#description').type("Description just for testing")
cy.wait('#templatecontract').then(interceptions => {
cy.get('#template-button-next').click()
});
});
});
I'm not sure why but just setting the method type (POST in this case) have solved the problem.
cy.intercept('POST', Cypress.env("baseUrl")+`/api/v1/contract-type/templatecontract`).as('templatecontract')
I had a similar problem, and the issue was that the first request my application sent was an OPTIONS request.
If you do not include the method as the first argument, all methods (including OPTIONS) are now matched. This can be puzzling as your .wait will be satisfied by the OPTIONS request, not by your second POST request.
Reference: https://docs.cypress.io/api/commands/intercept.html#Comparison-to-cy-route
Reference: https://docs.cypress.io/api/commands/intercept.html#Matching-URL
Interestingly I am getting different results for
cy.intercept("POST", "https://backend.rocketgraph.app/api/signup").as("doSignup")
and
cy.intercept("POST", `${BACKEND_URL}/signup`).as("doSignup")
Not sure what is the issue. Also have to set POST as one of the users have mentioned
The first one is actually intercepted. Weird but happened.
If you spy a route at the top of your test, as you do here, cy.wait() will return immediately if there have already been responses to that route by the time it's called.
As an example, say you notice this in your network tab:
GET some-route: 200
GET some-route: 200
GET some-route: 200
GET some-route: 200
POST something-unique: 200
GET some-route: 500
^ some-route is 500ing at some clearly-identifiable point. Should be easy to catch in a test, right? Well:
it('Should fail on this 500, but doesn't???', () => {
// start spying our indicator; seems good:
cy.intercept('GET', 'something-unique').as('indicator')
// but if we start spying the route here:
cy.intercept('POST', 'some-route').as('route')
// then hit some-route a bunch of times & return:
foo()
// then expect to catch the failure after something-unique fires:
cy.wait('#indicator').its('response.statusCode').should('eq', 200).then( () => {
// then expect the test to fail on the 500:
cy.wait('#route').its('response.statusCode).should('not.eq', 500)
// ...this won't work! this test will pass because cy.wait() will
// succeed and return the *first* GET some-route: 200 !
})
I personally find this to be pretty counter-intuitive - it feels reasonable that a wait() on a spied route would always actually wait for the next request! - but that's apparently not how it works.
One way around this is to not start spying until you're actually ready:
it('Fails on a 500', () => {
cy.intercept('GET', 'something-unique').as('indicator')
foo()
cy.wait('#indicator').its('response.statusCode').should('eq', 200).then( () => {
// don't start spying until ready:
cy.intercept('POST', 'some-route').as('route')
cy.wait('#route').its('response.statusCode).should('not.eq', 500)
// and the test fails correctly; response.statusCode 500 should not equal 500
})

cy.wait(#someXhr) taimeouts

I'm having a problem with stubbing a simple request to an API using the cypress' cy.server() and cy.route().
Here's the failing test:
it.only("should show an error message for server errors", () => {
const name = "It doesnt matter";
const email = "takenemail#yopmail.com";
const pass = "123123";
// run the server and define the stubbed route
cy.server();
cy.route(
"POST",
`${serverBaseUrl}/auth/register`,
"fixture:register-fail.json"
).as("postRegister");
// fill in the registration form and hit submit
cy.visit("/auth/register");
cy.get(selectors.registerForm.name).type(name);
cy.get(selectors.registerForm.email).type(email);
cy.get(selectors.registerForm.password).type(pass);
cy.get(selectors.registerForm.registerButton).click();
// intercept the request and mock it
cy.wait("#postRegister"); // this fails.
cy.get(selectors.registerForm.genericErrors).contains(
"This email has already been taken"
);
});
and the error:
cy.wait() timed out waiting 5000ms for the 1st request to the route: postRegister. No request ever occurred.
Note: even though it says that No request ever occurred. I can still see the request being send and a response received in the console's Network tab (which means the stub has been bypassed and a regular request's been made).
Any ideas what's happening?
Thanks in advance.
Ok, seems like i have found the problem.
It turns out that using the fetch API is not supported by cypress.
The workaround - using whatwg-fetch, which is basically a polyfill for the fetch api, working with XHR behind the scenes.
Install the whatwg-fetch package: npm install whatwg-fetch --save
Import it in your project: import "whatwg-fetch";
Last, but very important - remove the fetch object from the window before every page load in the cypress environment like this:
// you can define this in the commands file for example...
Cypress.on("window:before:load", (win) => delete win.fetch);
or an alternative, per-visit approach:
it("some test", () => {
cy.visit("/url", {
onBeforeLoad: win => delete win.fetch // <---
});
// ...the rest of the test
});
Doing this will kick-in the polyfill and the stubbing should be working properly after this intervention.

Can't intercept Cypress API call

I have stuck with Cypress fixtures. Can't intercept an XHR request with SSR and navigation routing.
cypress/integration/page.js:
const fetch = require("unfetch")
describe("/about", () => {
beforeEach(() => {
cy.visit("/", { // Visit home page to trigger SSR
onBeforeLoad (win) {
win.fetch = fetch // replace fetch with xhr implementation
},
})
})
it("Has a correct title", () => {
cy.server()
cy.fixture("about").then(about => {
// about object is correct here, like {title: "About+"}
cy.route("GET", "http://localhost:8080/api/documents/url", about) // Not sure where .route should be
cy.get(".main > :nth-child(1) > a").click() // Navigate to the /about page
cy.route("GET", "http://localhost:8080/api/documents/url", about) // Tried both ways
// This hits my server API without stubbing, getting {title: "About"}
cy.title().should("eq", "About+") // About != About+
})
})
})
cypress/fixtures/about.json:
{"title": "About+"}
I see an XHR request (type=xhr) in Dev Tools and it doesn't use the above about stub object but hits real API instead. Why? Double checked URL and method – 100% the same. Can it be that route is coupled to visit and ignores click-based routing?!
Rechecking this once again, I've found a solution. Let me share the details for everyone interested:
1) I use Next.js which is an excellent tool for SSR but it doesn't allow you to disable server-side rendering (yet) according to this and this issues.
2) You can use Cypress with SSR pages but, in this way, you're limited to testing real HTML. Which means you have to either couple tests to real data (not good in most cases) or stub the database itself (slow). In general, you want to stub HTTP requests.
3) Cypress can't stub fetch requests and mocking fetch with XHR-based implementation was trickier than I thought.
First you need to:
// cypress/integration/your-test.js
Cypress.on('window:before:load', (win) => {
delete win.fetch
})
Then:
// pages/your-page.js
Entry.getInitialProps = async function() {
window.fetch = require("unfetch").default
...
}
Other combinations of delete & update code lines I tried didn't yield positive results. For example, when I had window.fetch = line in the test file it didn't work and fetch.toString() gave "native code". Not sure why, no time to explore further.
Axios solves the above but I don't like to bloat my bundle with extra stuff. You can inject XHR-based fetch for tests only.
4) The most important missing piece. You need to wait for route.
it("Has a correct title", () => {
cy.visit("/")
cy.server()
cy.route("GET", "http://localhost:8080/api/documents/url/about", {title: "About+"}).as("about")
cy.get("[href='/about']").click()
cy.wait("#about") // !!!
cy.get("h1").contains("About+")
})

Context cleanup with apollo graphql server

I can create a context function in ApolloServer that will be executed before each request.
How can I have a cleanup function (after request execution)?
not sure if this is still relevant but I found this medium article addressing this exact question.
You can use this snippet for when you want code to run after all the resolvers have run:
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
...
},
plugins: [
{
requestDidStart: () => ({
willSendResponse: response => {
// this will run after every request
if (response.context.db) {
response.context.db.close();
}
}
})
}
]
});
I looked in the docs and this plugin system is nowhere to be found. I haven't found anything to discourage use like this but it does not seem to be an official solution.
I tried it in my project and it does seem to work.
Not sure why you would like to have context cleanup function? The context is set before as the middleware for each request therefore if you would like to have context set differently for another request you just inject the logic to the middleware. Maybe i just do not understand your use case for this. It would be helpful if you can clarify why you would like to apply it? The context is set per request, therefore you can inject enmpty object for some requests based on the req. If you need to clear up information from req object after graphql middleware you can do for example another middleware where you will set req.user (if you have authenticated user there) to null.

Resources