How to check headers of most recent response in Cypress? - javascript

Our application has a feature where you can press a button, wait for a while, and then get some information after it's been delivered. It'll send about 10 responses with identical url's and only the last one should have the information we care about in it. My script right now is:
beforeEach(function() {
cy.server()
cy.route('get', /waiting_for_response/).as('response')
})
it('checks for good data', function() {
cy.get('.get-data').click()
cy.wait('#response', {timeout:10000}).then((xhr) => {
expect(xhr.responseHeaders.data).to.not.be.a('null')
})
})
This always fails since cy.wait is resolved on the first matching url and only the last response's headers have a data value of not null. Is there a way to wait for all of the responses to have come back before checking if any of them were not null? Or perhaps specify the route has to have a certain response header in order to match? Any help or ideas would be super appreciated, thanks in advance!

You can break the problem into smaller conditions
cy.wait(10000)
check length of cy.get('<>').should('have.length', 10)
now you can check condition expect(xhr.responseHeaders.data).to.not.be.a('null')
You can refer to documentation
https://docs.cypress.io/api/commands/route.html#With-Stubbing
Section: Making multiple requests to the same route

Related

Cypress - wait for the API response and verify UI changes

I have a component that I want to cover with some e2e tests. This component takes the URL provided by the user in the input, calls the API after the button click and then returns the shortened version of that URL. After that, shortened url is added to the list below the input on the UI and makes some localStorage assertion. I want Cypress to wait for the API response and only then check the UI if the list item was added. I made this working but I hardcoded the wait time in the wait() method. How Can I achieve that programatically ?
describe("Shortener component", () => {
it("Should add the list item and data to localStorage", () => {
cy.visit("http://127.0.0.1:5500"); //Live server extension address
cy.get("#url-input").type("https://facebook.com");
cy.get("#form-submit-button").click();
// wait for the api response and make sure that the value has been added to the localStorage
cy.wait(40000); //todo - wait for the api response instead of hardcoding the wait time
const localStorageData = localStorage.getItem("linksData");
if (JSON.parse(localStorageData)) {
expect(JSON.parse(localStorageData)[0].inputValue).to.eq(
"https://facebook.com"
);
}
// check if the new list item with the corrct value has been addded
cy.get(".shortener-component__list-item")
.contains("https://facebook.com")
.should("be.visible");
//validation mesasge should not be visible
cy.get("#validationMesage")
.contains("Please add a valid link")
.should("not.be.visible");
});
});
I tried with intercept() however I failed. Not sure how to make it working. I also saw some similar SE topics on that but it did not help me.
Any ideas / examples apreciated :)
Thx !
From the order of events you've given
short URL returned
added to localStorage
added to list
just change the order of feature testing
test list - it is last event, but has retriable commands (you can increase the timeout)
now test localStorage, if UI has the short URL so will localStorage
cy.contains('.shortener-component__list-item', 'https://facebook.com', { timeout: 40000 })
.then(() => {
// nested inside .then() so that the above passes first
const localStorageData = localStorage.getItem("linksData");
const linksData = JSON.parse(localStorageData);
expect(linksData).not.to.eq(undefined);
expect(linksData[0].inputValue).to.eq("https://facebook.com");
})
Alternatively, to make use of retry and timeout on the localStorage check,
cy.wrap(localStorage.getItem("linksData"))
.should('not.eq', undefined, { timeout: 40000 }) // will retry the above until true
.then(data => JSON.parse(data))
.should(parsedData => {
expect(parsedData.inputValue).to.eq("https://facebook.com");
});
I guess you should also start the test with
cy.clearLocalStorage("linksData")
There're examples in the documentation, it only takes some reading and experimentation.
In general, you need three commands: cy.intercept(), .as(), and cy.wait():
cy.intercept(your_url).as('getShortenedUrl');
cy.wait('#getShortenedUrl');
you can also use .then() to access the interception object, e.g. a response:
cy.wait('#getShortenedUrl').then(interception => { });
or you can check something in the response using .its():
cy.wait('#getShortenedUrl').its('response.statusCode').should('eq', 200);
The point is that after cy.wait('#getShortenedUrl'), the response has been received.

Node.js API requests randomly stopping

This is my first project, yes the code is bad, please be nice!
I'm working on a Discord Bot, which requests Game APIs for a whole Guild. First I get all the Ids for a guild into an array and then I make a get request for every Id.
My problem is, that the request just randomly stops after some time (always different), and I don't know why. It's also not giving an error.
I've already tried to run it from another program, change the package I'm using, look that the key can be used, but in the end, everything is the same.
while (guildmembers[i2] != undefined) {
console.log(guildmembers[i2])
let url = 'https://api.hypixel.net/player?key=' + APIkey
let Guildmembersuuid = guildmembers[i2]
let profilelurl = url + '&uuid=' + Guildmembersuuid
const TESTguildPlayerinfos = await axios.get(profilelurl)
console.log(TESTguildPlayerinfos)
i2++
}
If I did anything wrong with the post or didn't give enough information please tell me!
Thanks a lot for the help!
try using Promise.all(),which takes an array of promises and then put a catch block afterwards to see if there is any problem.
It looks like you are hitting the Hypixel throttling limits - you need to be more careful with your requests. As it stands, they will stop you at 120 requests/minute. You either need to manually throttle yourself like one member in the thread mentioned (waiting .5 seconds between any call to the API to ensure you don't overflow the limit) or insert a cache in between so that calls don't actually always land on the Hypixel backend.
https://hypixel.net/threads/api-request-limit.815723/

In Cypress, how to check the headers of the most recent response?

In Cypress, I'm setting up my login loop, then want to extract certain headers for use in subsequent requests. Here's how I log in:
function login() {
return cy.visit('/login')
.get("#username").type(Cypress.env("USERNAME"))
.get("#password").type(Cypress.env("PASSWORD"))
.get("form").submit()
.then((result) => {
// TODO - extract the last response's headers and save for later
});
}
But in that context, result is a DOM selection of the form, and I'm not sure how to get the most recent result.
Are there some interceptors or other way to submit a form that would give me more access to the request or response?
I can't just replace this with a cy.request() because it follows a few redirects to take me to a foreign SSO login page, hence why I need to only inspect the most recent result.
Thanks in advance.
You need to wait for the response from the url that contains your desired info. Look at this example.
cy.server().route('http://' + host + '/api/managerecord/userdetails/*').as('getUserDetails')
cy.get(submitButton).click()
cy.wait('#getUserDetails').then((response) => {
expect(response.response.body.waistSize).to.eq(100)
})
In your case, put a route on that last respone and wait for that one. If you don't know that url have a look in the Chrome inspector.

JavaScript - Promise fulfilled too early?

I created a small sample application using VueJs and created a C# REST API to store and retrieve data in a SQL Server back end.
For testing, I created a simple web page with a form to create a "note". The note is stored by the following function, 'saveData()':
saveData()
{
let promiseStack = [];
var jsondata = JSON.stringify(this.note);
promiseStack.push(this.$http.post('REST_API/note', jsondata));
Promise.all(promiseStack).then(data =>
{
this.$http.get('REST_API/note');
this.$router.push({ name: 'viewnotes', params: { id: data[0].body.id }})
}, error =>
{
console.log(error);
});
}
I tried to use a promise to wait until the 'store' operation in the backend is complete, and issue a GET request to retrieve all notes once the promise is fulfilled.
However, the get request inside the promise doesn't return any data. If I issue the get request manually later one, I retrieve the data that was stored previously.
So I had look into the C# REST API. There are currently two functions: createNote(...), getAllNotes(...). I used a StreamWriter to log to the filesystem when these functions are called, using milisecond precision. What I see is that 'createNote' is called after 'getAllNotes'. So I suspect that the API is working correctly, but something with the way I'm using promises seems to be awfully wrong.
Maybe somebody has a hint?
UPDATE
I know that the GET request doesn't return any data by using the developer toolbar in Chromium. The response is empty
The developer toolbar in the network tab shows that the requests are submitted in the correct order, so the "POST" request is issued first
It seems I found the problem. I had a 'href' tag in my 'Save' link, which triggered an early routing. The intended 'POST' and 'GET' were fired correctly, but there was another 'GET' inbetween somewhere because of the 'href' tag in the link, even though it was empty.
I removed the tag, now it works as intended.

Second test doesnt change URL

I have two test. The first test passes successfully. Then there is an url method call in the second test, but it doesn't change the url in the browser.
The baseUrl in wdio.conf.js is set to http://localhost/web/es/index.html#
Tests:
var assert = require('assert');
describe('user login ', function(){
it('user login', function(){
browser
.url('/system/login')
.setValue('[name="username"]','test')
.setValue('[name="password"]','test')
.click('=Potvrď');
assert(browser.waitUntil('=test test'));
});
it('user form', function(){
browser
.url('/user/form');
});
});
In the first test /system/login is opened correctly. But in the second test the url never changes to /user/form
I'm just starting with webdriverio so am i missing something ?
A lot of time passed since #user49126 asked the question, but since no one actually saw that this is in fact a routing issue, here is my answer:
It looks like you are using a bad baseUrl value. You have to go back and re-read it's description in the wdio.config.js file. Any value you pass to the browser.url() function will be postpended to your baseURL.
In your case, firstly you are going to http://localhost/web/es/index.html# + /system/login, which due to the # and probably your routing logic behind, evaluates correctly to a valid route. The second test most surely takes you to an invalid route.
In order to completely isolate your issue we will need the console output.
Here is the setup I used to debug this:
wdio.config.js:
// Set a base URL in order to shorten url command calls. If your url parameter starts
// with "/", then the base url gets PREPENDED.
baseUrl: ' http://127.0.0.1:8303',
Code:
describe("\nDebug routing issue", function() {
it("\nShould verify the URL, click something, move on\n", function() {
return browser
.url('/product-page.html')
.getUrl()
.then( function(retURL) {
assert.include(retURL, "product-page", "Yeap! You're good!");
})
.waitUntil(function() {
return browser.isVisible("div.top-bar-right a");
}, 3000, "\nLogin failed...")
.click('div.top-bar-right a')
// Casual 'pause' to witness the click
.pause('1000');
});
it("\nShould reload different URL and validate site routing\n", function() {
return browser
.url('/index.html')
.getUrl()
.then( function(retURL) {
assert.include(retURL, "index", "Yeap! You're good!");
})
// Casual 'pause' to witness the new route
.pause('1000')
});
TL;DR: Just make sure you are have the correct value in the baseUrl variable. In your case, change the baseUrl value to http://localhost/.
My suggestion is to also use the port, e.g. http://localhost:8303/, or the IP of your localhost server altogether, as I also experienced some issues using it as simply localhost.
PS: None of the other answers touch upon the real issue the user is experiencing. Unfortunately I have no way to comment/signal this. ('Not enough minera... errrg, rep points,' #feelsbadman)
Check the condition on browser.waitUntil(...). The first parameter is a function according to http://webdriver.io/api/utility/waitUntil.html.
Also, adding a timeout (3seconds) will help to see if the wait has been successful.
browser.waitUntil(function() {
return browser.getText('#some_id') === 'test test';
}, 3000, "Timeout!!");
That is because it's not really a test. What are you testing, where is your acceptance criteria? You told the browser to do something and didn't wait to check up on it. It's basically like you said: Selenium, do this and I don't care I am done, bye.
What you can use?
Try giving it a pause:
.pause(ms);
give it 10000 (10s) and visually see if it changes.
you can also add an additional:
.getUrl() - to get the URL after the time stated under ms.
Alternatively (instead of the things stated above):
On your new URL, is there an element in DOM that is not present in the DOM on old URL? If so, add to the end:
.waitForExist(selector, ms);
give it 1000 under ms, and make sure you got the right selector for that element
This way browser will wait for that element to appear.

Categories

Resources