How to make Cypress wait for .intercept requests to finish? - async-await

Code:
describe("Cypress", () => {
it("Example Test", () => {
cy.intercept("GET", "/api/**").as("getApi");
cy.visit("/home/");
cy.wait("#getApi");
console.log("here");
});
});
Output:
here
Request Finished: /api/1/
Request Finished: /api/2/
Is there a way to make Cypress wait for the requests it intercepts to finish before continuing?
Desired Output:
Request Finished: /api/1/
Request Finished: /api/2/
here
Note: Page Code
$.get(url).done(function (response) {
console.log("Request Finished", this.url);
});

Related

Is there a way in Cypress to check for open requests?

Cypress Code:
describe("/gradebook/", () => {
before(() => {
cy.login("username", "password");
cy.intercept("GET", "/api/**").as("getApi");
cy.visit("/home/");
cy.wait("#getApi");
cy.log("111");
});
after(() => {
cy.intercept("GET", "/api/**").as("getApi2");
cy.wait("#getApi2");
cy.log("333");
cy.logout();
});
it("Loads Page Title: Gradebook", () => {
cy.log("222");
});
});
The Problem:
Sometimes it throws an error undefined: undefined but it seems random.
Output Image (when it fails):
Only thing I can think of is that it's somehow logging out before the /api/gradebook/scores/ request finishes, which is why it 400s, but don't know how to fix that. It is intercepting the request, so it shouldn't be continuing to the logout until that's finished.
*Note: I found this Is there a way to check if there are any pending fetch requests? but it doesn't have any answers.

Cypress MOCK api response for different status

I am testing my login component with Cypress (just started with it) and I want to handle three different cases where the API returns status 200, 400 or 500. I want to mock these responses to see how the frontend responds to that.
I want to mock the response for three different cases (200, 400 and 500) when sending a request to my API endpoint http://localhost:9999/api/login
I have written some code based on the docs but I still am not where I want to be.
describe('Login Approach', () => {
it('login', () => {
cy.visit('/login')
// these values email and pw shouldn't matter if mocking is done right
cy.get('#email')
.type('test')
.should('have.value', 'test')
cy.get('#password')
.type('123456')
.should('have.value', '123456')
cy.server()
cy.route({
method: 'POST',
url: 'http://localhost:9999/api/login', // this is the api that I send the request to
})
cy.location('pathname', { timeout: 10000 }).should('eq', '/login');
cy.title().should('include', 'Condeo')
cy.get('#notification').should('exist')
})
})
I am not getting status in the details of the test:
Method Url Stubbed Alias #
POST http://localhost:9999/api/login Yes -
You should use the wait method of cypress.
You can find the cypress documentation here.
For your use case, make sure you start the server and define the route before you visit the link. Just after visiting the link, use the cy.wait() method which will wait for that API call to finish.
Eg.
describe('Login Approach', () => {
it('login', () => {
cy.visit('/login')
// these values email and pw shouldn't matter if mocking is done right
cy.get('#email')
.type('test')
.should('have.value', 'test')
cy.get('#password')
.type('123456')
.should('have.value', '123456')
cy.server()
cy.route({
method: 'POST',
url: 'http://localhost:9999/api/login', // this is the api that I send the request to
}).as('login')
cy.location('pathname', { timeout: 10000 }).should('eq', '/login');
cy.title().should('include', 'Condeo')
cy.get('#notification').should('exist')
// Code which will try to visit the login API.
cy.wait('#login').then((xhr)=> {
if(xhr.status === 200) {
// Code to test when status is 200
} else if(xhr.status === 400) {
// Code to test when status is 400
} else {
// Code to test when status is none of the above.
}
})
})
})

How to debug "Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL"?

I have a test that passes locally but it fail during Gitlab CI pipeline due to timeout error.
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Which way can I go through to debug this? I tried to increase defaultTimeoutInterval to 240000 in protractor configuratoin file, but nothing has changed.
Test
describe('Test', () => {
beforeAll(async () => {
console.log('1) start beforeAll');
await api_wrapper.generateAllLatestMeasureToPatient(patient); // it breaks here
console.log('2) API calls completed'); // it never gets here
await page.navigateTo();
console.log('3) end beforeAll');
});
it('should display map, edit fence button and toggle fence button', async () => {
console.log('4) start test');
// ...
});
});
In generateAllLatestMeasureToPatient() I do ten HTTP POST requests to API endpoint. In CI it stops at fourth, locally works fine.
Console output
1) start beforeAll
4) start test
I use 2 types of timeouts :
defaultTimeoutInterval: 120000,
also in
exports.config = {
allScriptsTimeout: 90000,
}
my test also used to timeout more in CI environment I started running them in headless mode with Browser window-size set and it really helped.
capabilities: {
'browserName': 'chrome'
},
'chromeOptions': {
'args': ["--headless", "--disable-gpu", "--window-size=1920,1080"]
},
}

Cypress: Test fails if request from "cy.route()" is not found

I have a question about cypress testing.
I'm doing the following:
cy.route() to an url with alias
then cy.wait(#alias)
I know that the default action that cypress does it to fail the test if the there wasn't any request made to that url.
My problem is that I have multiple requests and one of them may not reach the request url. But I don't want that to fail my test, just to skip over it. How can I do this?
Basically, I'm asking how do you make your tests NOT to fail when you get this:
CypressError: Timed out retrying: cy.wait() timed out waiting 30000ms for the 221st response to the route: 'productRequest'. No response ever occurred.
If your usecase is to wait for requests and then continue with more commands, this solution might help you:
describe("route", () => {
it("hiting route", () => {
let req1 = false;
let req2 = false;
cy.server()
cy.route({
methdod: "GET",
onRequest: () => {
req1 = true;
},
url: "/will/eventually/called"
});
cy.route({
methdod: "GET",
onRequest: () => {
req2 = true;
},
url: "/will/eventually/called2"
});
setTimeout(() => {
req2 = true
}, 2000)
cy.visit("https://biehler-josef.de")
cy.get("body").should(() => {
if (req1) {
expect(req1).to.eq(true);
}
if (req2) {
expect(req2).to.eq(true);
}
if (!req1 && !req2) {
expect(false).to.eq(true)
}
});
cy.get("body").should("exist");
});
})
You define the routes and pass a onRequest function that sets a variable. This can be done with multiple routes. Then you use should with callback function. Within that you can check both variables and force to fail only if no request occurred. The setTimeoutin this example demonstrates a request that takes 2 seconds to finish.
If you want to check if a request is not hit, it is much easier. But this solution is not usable if you want to execute additional commands after the cy.wait(#alias):
describe("route", () => {
it("hiting route", (done) => {
cy.server()
cy.route("GET", "will/never/be/hit").as("requestalias");
cy.visit("https://biehler-josef.de")
cy.on("fail", (error) => {
if (error.name === "CypressError"
&& error.message.match(/.*Timed out retrying: cy.wait().*requestalias.*/)) {
// calling done forces cypress to turn test to green
done()
}
});
cy.wait("#requestalias")
});
})
With cy.on("fail") you can listen to the event that is thrown when a test fails. Caling done() within this will force the test to be green. But you can not continue with subsequent commands in your test. So the wait() must be the last command in your test

Set cookie in after() hook nightwatch

I tried to set cookie in nightwatch after() hook function but apparently it didn't work. The idea is I want to set the cookie's value as "failed" if the test failed and "success" if the test passed.
export = {
'#tags': [ 'heboh' ],
after(browser) {
browser
.setCookie({ name: 'mycookie', value: 'success' })
.getCookie('mycookie', function callback(result) {
console.log(result); // print null
})
.end();
},
'create heboh'(browser) {
browser
.url('http://www.google.com')
.waitForElementVisible('body', 1000)
.assert.title('Facebook'); // intended to make it failed
}
}
I specified --verbose and this is what I got
FAILED: 1 assertions failed and 1 passed (5.446s)
INFO Request: POST /wd/hub/session/null/cookie
- data: {"cookie":{"name":"mycookie","value":"true"}}
- headers: {"Content-Type":"application/json; charset=utf-8","Content-Length":45}
INFO Response 404 POST /wd/hub/session/null/cookie (19ms) { sessionId: 'null',
value:
{ error: 'invalid session id',
message: 'No active session with ID null',
stacktrace: '' },
status: 6 }
LOG → Completed command cookie (22 ms)
INFO Request: GET /wd/hub/session/null/cookie
- data:
- headers: {"Accept":"application/json"}
INFO Response 404 GET /wd/hub/session/null/cookie (15ms) { sessionId: 'null',
value:
{ error: 'invalid session id',
message: 'No active session with ID null',
stacktrace: '' },
status: 6 }
null
LOG → Completed command cookie (16 ms)
LOG → Completed command end (0 ms)
Looks like there is no session in after() function.
Try this, I believe you need to call done on your after hook.
after: function (browser, done) {
browser
.setCookie({name: 'mycookie', value: 'success'})
.getCookie('mycookie', function callback(result) {
console.log(result);
})
.end()
.perform(function () {
done();
});
}

Resources