Cypress - log response data from an request after a click() - testing

Although I know this may not be considered as a best practice, but what I want to achieve is to silently delete a record from a database after the same was created throughout UI. In htat way I want to keep our test environment clear as much as possible and reduce the noise of test data.
After my tests create a new record by clicking over the UI, I wait for POST request to finish and then I would like to extract the id from the response (so I can reuse it to silently delete that record by calling the cy.request('DELETE', '/id')).
Here's a sample test I have put on as a showcase. I'm wondering why nothing is logged in this example.
it('GET cypress and log', () => {
cy.server()
.route('**/repos/cypress-io/cypress*')
.as('getSiteInfo');
cy.visit('https://www.cypress.io/dashboard');
cy.get('img[alt="Cypress.io"]')
.click()
.wait('#getSiteInfo')
.then((response) => {
cy.log(response.body)
})
})
As far as I can see from here https://docs.cypress.io/api/commands/wait.html#Alias this should be fine.

your code contains two problems.
First:
The click triggers a new page to be loaded but cypress does not wait until the PageLoad event is raised (because you do not use visit). On my PC the Request takes about 5 seconds until it is triggered after the click. So you should use wait(..., { timeout: 10000 }).
Second:
wait() yields the XHR object, not the response. So your code within then is not correct. Also the body is passed as object. So you should use JSON.stringify() to see the result in the command log.
This code works:
describe("asda", () => {
it('GET cypress and log', () => {
cy.server()
.route('**/repos/cypress-io/cypress*')
.as('getSiteInfo');
cy.visit('https://www.cypress.io/dashboard');
cy
.get('img[alt="Cypress.io"]')
.click()
.wait('#getSiteInfo', { timeout: 20000 })
.then((xhr) => {
cy.log(JSON.stringify(xhr.response.body))
})
})
})

Instead of route and server method, try intercept directly

Related

(uncaught exception)TypeError: $(...).datepicker is not a function

it('Access URL', () => {
cy.visit('URL')
cy.wait(5000)
cy.get('.login-btn').click()
})
URL is working but login click is not working.
(uncaught exception)TypeError: $(...).datepicker is not a function --- is displayed
Trying to access url and trying to click the Login link in the website.
If it takes the page more then 5 seconds to load cypress would never find the button in your code. If you can give the button an id it would be able to find it, but if you can't you can try to add a long timeout to the button and when the button appear on the page it will click it, for example.
`cy.get('.login-btn', { timeout: 30000 }).click()`
By the looks of your comment, you have an uncaught exception in your application. That’s an indication you or a dev needs to fix something in the app. Cypress is just trying to tell you.
If you want to ignore the error, you can add this to your test
it('Access URL', () => {
cy.once('uncaught:exception', () => false);
cy.visit('URL')
cy.wait(5000)
cy.get('.login-btn').click()
})
If you want to ignore uncaught exceptions in ALL tests you can add this to your support file. (I do NOT recommend this, I recommend fixing your application to find out why it’s throwing an exception in the first place)
// support/index.js
Cypress.on('uncaught:exception', () => false);

Cypress - Unable to get the Response Headers - API Automation

I have an API automation test suite using Cypress and one of the issue I am facing in one of the test is to validate the response headers.
For some reason, I am not able to read the response headers using Cypress.
The code is below
cy.request({
method:'GET',
url:Cypress.env("Authorisationurl")+tokenId+'&decision=allow&acr_values=1',
followRedirect: false,
headers:{
'Accept': "/*"
}
}).then((response) => {
const rbody = (response.body);
cy.log(response.status)
//THIS GOT ASSERTED TO TRUE
expect(response.status).to.equal(302)
//OPTION1
cy.wrap(response.headers['X-Frame-Options']).then(() => {
return response.headers['X-Frame-Options'];
});
//OPTION2
return response.headers['X-Frame-Options']
//OPTION3
return response.headers
})
None of the above options gives me the header information. Infact I am confused with the order of execution too.
This is my output.
for the following code.
const rbody = (response.body);
cy.log(response.status)
cy.log(response)
expect(response.status).to.equal(302)
cy.log(response.headers)
cy.log(response.headers['X-Frame-Options'])
return response.headers['X-Frame-Options']
Also, not very sure what Object{9} indicates. Can anyone please explain what is happening here.
I am aware of Cypress flow of execution and the code is written in then block as a call back function.
Option 3 is very scary as it gives an error
cy.then() failed because you are mixing up async and sync code.
In your callback function you invoked 1 or more cy commands but then returned a synchronous value.
Cypress commands are asynchronous and it doesn't make sense to queue cy commands and yet return a synchronous value.
You likely forgot to properly chain the cy commands using another cy.then().
The value you synchronously returned was: Object{9}
Can anyone please help me here as in what is the correct way of doing it. I know Cypress is very quick and easy to use but to move away from Selenium, we need to make coding easier for developers with meaningful error message. Object{9} is not very helpful.
Also, Do I need to use Cy.log ? As the sequence of prints is not what I have written in the code. Thanks very much for your time on this.
Please use like this:
JSON.parse(JSON.stringify(response.headers))["X-Frame-Options"];
The "mixing async and sync code" message is basically saying you should keep the .then() callback simple.
But you can chain more than one .then() to run the async and sync code separately.
Use an alias to "return" the value. Since cy.request() is asynchronous, you will need to wait for the value and the alias pattern is the most straight-forward way to do this reliably.
WRT Object{9}, it's the result of the way Cypress logs complex objects.
Don't use cy.log() to debug things, use console.log().
cy.request( ... )
.then(response => {
expect(response.status).to.equal(200) // assert some properties
})
.then(response => response.headers) // convert response to response.headers
.as('headers')
cy.get('#headers')
.then(headers => {
console.log(headers)
})
Since this was originally posted a year ago and Cypress has had many versions since then, the behavior may have changed, however this works in Cypress 11.
You can access the response.headers array as you would normally expect, however the casing of the header name was not as expected, Postman reported the header as X-Frame-Options, but Cypress would only allow me to access it if I used a lower-cased version of the header name (x-frame-options).
cy.request({
url: '<Url>',
method: 'GET',
failOnStatusCode: false
})
.then((response) => {
expect(response.status).to.eq(200);
expect(response.headers["x-frame-options"]).to.equal("SameOrigin");
})
.its('body')
.then((response) => {
// Add your response testing here
});
});

Nuxt JS - reuse async call (axios get) within mounted?

If I am rendering a page with Nuxt, Vue, and Axios - is there a way to reuse the asyncData request (or data)?. For example, if I render a response, and the user takes an action on the page to filter, sort, etc. the data, can I reuse the same data to render again - or do I need to make a new call in mounted?
export default {
asyncData ({ env, params, error }) {
return axios.get(`${env.cockpit.apiUrl}/collections/get/cat_ruleset?token=${env.cockpit.apiToken}&simple=1&sort[ruleid]=1`)
.then((res) => {
return { catrules: res.data }
})
.catch((e) => {
error({ statusCode: 404, message: 'Post not found' })
})
},
mounted() {
},
methods: {
}
}
Of course you can reuse it. The simplest way would be to store the result somewhere (anywhere, really, but your store would be a good storage candidate) and change your method to:
asyncData ({ env, params, error }) {
return X ? Promise.resolve(X) : axios.get(...)
}
... where X is the stored result of your previous call.
But you don't have to.
Because, by default, the browser will do it for you. Unless you specifically disable the caching for your call, the browser will assume making the same call to the server will yield the same result if you do it within the number of seconds set in max-age of Cache-control.
Basically, the browser returns the previous result from cache without making a call to the server, so the optimization you're after is already performed by the browser itself unless you specifically disable it.
You can easily spot which calls were served from cache and which from server by looking in the Network tab of DevTools in Chrome. The ones from cache will have (memory cache) in the Size column:
... and will have a value of 0ms in Time column.
If you want control over when to call the server and when to serve a cached result — most browsers have a limit on max-age (see link above) — you could (and should) store the result of your previous call and not rely at all on the browser cache (basically the internal check inside the method, which I suggested at the top).
This would enable you to avoid making a call long time after the cache max-age has passed, because you already have the data, should you choose to do so.

Assert element exists after all XHR requests finished

I'm visiting a page which is fetching data Asynchronously (multiple XHR requests), and then asserting if a certain DOM element is visible/exists in the page.
So far I was only able to get the page and the data fetched with using cy.wait() either with an arbitrary time, or by aliasing the actual request, and using the promise-like syntax to make sure my cy.get() is done after the XHR response has completed.
Here is what doesn't work:
before(() => {
cy.login();
cy.server();
cy.route('/v0/real-properties/*').as('getRealPropertyDetails');
cy.visit('/real-properties/1/real-property-units-table');
});
beforeEach(() => {
Cypress.Cookies.preserveOnce('platform_session');
});
after(() => {
cy.clearCookies();
});
context('when viewport is below 1367', () => {
it('should be closed by default', () => {
cy.wait('#getRealPropertyDetails'); // the documentation says this is the way to go
sSizes.forEach((size) => {
cy.viewport(size[0], size[1]);
cy.get('.v-navigation-drawer--open.real-property-details-sidebar').should('not.exist');
});
});
Adding cy.wait(1000); in the before() or beforeEach() hooks also works, but this is not really an acceptable solution.
What works, but not sure if this is the way to do this (I would have to add this for every page, would be quite annoying) :
it('should be closed by default', () => {
cy.wait('#getRealPropertyDetails').then(() => {
sSizes.forEach((size) => {
cy.viewport(size[0], size[1]);
cy.get('.real-property-details-sidebar').should('not.be.visible');
});
});
});
I see that you have browser reloads there (beforeEach), which could potentially wipe out the route spy, but not sure why cy.wait().then would work. I would try switching from before to beforeEach though, creating things once is always trickier than letting them be created before each test

What is the best way to wait for 'WebComponentsReady' event in TestCafe?

I want to wait for web components in the page to upgrade before running any TestCafe tests (In other words, wait for WebComponentsReady event before running tests). What is the best way to do this?
TestCafe starts to execute any action on the page after the DOMContentReady event is raised. As I see, the WebComponentsReady event can be raised before DOMContentReady.
TestCafe allows you to wait for some events in the browser by using ClientFunction:
const waitForWebComponentsReady = ClientFunction(() => {
return new Promise(resolve => {
window.addEventListener('WebComponentsReady', resolve);
});
});
await waitForWebComponentsReady();
However, note that TestCafe can't guarantee that this code will be executed before the WebComponentReady event is raised. As a result, this Promise will not be resolved.
As a solution, you can find another way to identify if the required Web Component is loaded. For example, you can check that some element is visible on the page:
await t.expect(Selector('some-element').visible).ok();
Meanwhile, TestCafe has a feature suggestion to add the capability to execute a custom script before page initialization scripts. You will be able to use code like this when the feature is implemented:
import { ClientFunction } from 'testcafe';
const initScript = `window.addEventListener('WebComponentsReady', () => window.WebComponentsLoaded = true);`;
fixture `My Fixture`
.page `http://example.com`
.addScriptToEachPage(initScript)
.beforeEach(async t => {
await t.expect(ClientFunction(() => window.WebComponentsLoaded)()).ok();
});