cypress use same endpoint with different response (testing http race condition) - cypress

I try to intercept two similar requests but two different responses in delay between each responses.
send a first request to the /endpoint with delay in response like 3000ms
second request to the same /endpoint with delay in response 1000ms
both requests have different response, something difficult to overwrite interceptor here
cy.intercept('POST', '/validate', (req) => {
req.reply({
delay: 3000,
fixture: 'invalidData.json'
});
})
cy.intercept('POST', '/validate', (req) => {
req.reply({
delay: 1000,
fixture: 'validData.json'
});
})

You can supply an array of routeHandlers in the order you want them applied
it('test the same route multiple times', () => {
let request = 0;
const replies = [
{delay: 3000, fixture: 'invalidData.json'},
{delay: 1000, fixture: 'validData.json'},
]
cy.intercept('POST', '/validate', (req) => {
req.reply(replies[request++])
});
cy.visit(...);
})

See Gleb Bahmutov's answer here
Change fixture response in cypress for the same url with intercept
Use the times option to restrict how many calls the intercept will catch.
But note, the last added intercept is checked first so you probably need to reverse the order.
cy.intercept({method: 'POST', url: '/validate', times: 1}, (req) => {
req.reply({
delay: 1000,
fixture: 'validData.json'
});
})
cy.intercept({method: 'POST', url: '/validate', times: 1}, (req) => {
req.reply({
delay: 3000,
fixture: 'invalidData.json'
});
})
Example app
<script>
setTimeout(() => {
fetch('/validate', { method: 'POST'})
.then(res => res.json())
.then(res => console.log('1st', res))
}, 100)
setTimeout(() => {
fetch('/validate', { method: 'POST'})
.then(res => res.json())
.then(res => console.log('2nd', res))
}, 200)
</script>
Console outputs in expected order, with different fixture data
2nd {data: 'valid'}
1st {data: 'invalid'}

You can use Gleb's example here as a starting point. We'll be using a variable out of scope of the intercept to keep track of requests.
it('some test', () => {
let requestCount = 0;
cy.intercept('POST', '/validate', (req) => {
requestCount++;
req.reply({
delay: requestCount === 1 ? 1000 : 3000,
fixture: requestCount === 1 ? 'validData.json' : 'invalidData.json'
});
});
// rest of test
})
You can modify your logic to return different delays / fixtures if the ternary provided does not accomplish your goal.

Similar requests means you can distinguish them, so do that by any property within the request.
cy.intercept('POST', '/validate', (req) => {
if (req.body.any.property.only.of.my.first.request === true) {
req.reply({
delay: 1000,
fixture: 'validData.json'
});
} else {
req.reply({
delay: 3000,
fixture: 'invalidData.json'
});
}
})

Related

Cypress resets to cy.visit() after running each describe section

I have a spec file in Cypress below and every time it runs the spec, the "Analyze Section" succeeds but the "Other Section" fails due to it returning to the login page even though the before() hook at the root should just run once based on the level of nesting. I'm trying to make it so the login happens one time whenever any tests in this suite are run. Likewise, when any test in the "Analyze Section" are run we click the #HyperLinkAnalyze link one time to ensure we are on the proper page for any test. I was trying to make them cascade down but the beforeEach() call in each section ends up popping the page back out to the login page that happened in before().
context('Admin - Analyze Tab', { tags: ['#admin'] }, () => {
let user;
before(() => {
cy.visit(Cypress.env('admin_url'));
user = Cypress.env('admin_user');
cy.login(user.email, user.password);
});
describe('Analyze Section', ()=>{
beforeEach(() => {
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkCampaignStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
describe('Other Section', ()=>{
beforeEach(() => {
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkXSellStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
});
```js
You can try Cypress.session .
The new cy.session() command solves problem by caching and
restoring cookies, localStorage and sessionStorage after a successful
login. The steps that your login code takes to create the session will
only be performed once when it's called the first time in any given
spec file. Subsequent calls will restore the session from cache.
Set experimentalSessionSupport flag to true in the Cypress config or by using Cypress.config() at the top of a spec file.
Check below example -
const loginWithSession = () => {
cy.session(() => {
cy.visit(Cypress.env('admin_url'));
let user = Cypress.env('admin_user');
cy.login(user.email, user.password);// Can be parameterize
});
cy.visit(Cypress.env('admin_url'));//Or home page url
})
}
context('Admin - Analyze Tab', { tags: ['#admin'] }, () => {
before(() => {
loginWithSession();
});
describe('Analyze Section', ()=>{
beforeEach(() => {
loginWithSession();
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkCampaignStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
describe('Other Section', ()=>{
beforeEach(() => {
loginWithSession();
cy.get('#HyperLinkAnalyze').click();
cy.get('#HyperLinkXSellStats').click();
});
it('TEST 1', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
it('TEST 2', {}, () => {
cy.contains('#analytics-row1', 'Response Rate').should('be.visible');
});
});
});
In older version of Cypress you can use https://docs.cypress.io/api/cypress-api/cookies#Preserve-Once to preserve the cookies and Cypress will not clear it .
beforeEach(() => {
Cypress.Cookies.preserveOnce('session_id', 'remember_token')
})

How to search through JSON response with Cypress assertions

Considering the below API response I would like to assert the exact location of a certain value in a JSON structure. In my case the name of pikachu within forms:
"abilities": [
{
"ability": {
"name": "lightning-rod",
"url": "https://pokeapi.co/api/v2/ability/31/"
},
"is_hidden": true,
"slot": 3
},
{
"ability": {
"name": "static",
"url": "https://pokeapi.co/api/v2/ability/9/"
},
"is_hidden": false,
"slot": 1
}
],
"base_experience": 112,
"forms": [
{
"name": "pikachu",
"url": "https://pokeapi.co/api/v2/pokemon-form/25/"
}]
I would like to extend below code snippet to not scan the entire body as a whole as there are a lot of name's in the response, but rather go via forms to exactly pinpoint it:
describe('API Testing with Cypress', () => {
var baseURL = "https://pokeapi.co/api/v2/pokemon"
beforeEach(() => {
cy.request(baseURL+"/25").as('pikachu');
});
it('Validate the pokemon\'s name', () => {
cy.get('#pikachu')
.its('body')
.should('include', { name: 'pikachu' })
.should('not.include', { name: 'johndoe' });
});
Many thanks in advance!
Getting to 'forms' is just a matter of chaining another its(), but the 'include' selector seems to require an exact match on the object in the array.
So this works
it("Validate the pokemon's name", () => {
cy.get("#pikachu")
.its("body")
.its('forms')
.should('include', {
name: 'pikachu',
url: 'https://pokeapi.co/api/v2/pokemon-form/25/'
})
})
or if you just have the name,
it("Validate the pokemon's name", () => {
cy.get("#pikachu")
.its("body")
.its('forms')
.should(items => {
expect(items.map(i => i.name)).to.include('pikachu')
})
})
and you can assert the negative,
.should(items => {
expect(items.map(i => i.name)).to.not.include('johndoe')
})
Can you try the below code and see if it helps with your expectation. From the response you could get the name as below;
describe('API Testing with Cypress', () => {
var baseURL = "https://pokeapi.co/api/v2/pokemon"
beforeEach(() => {
cy.request(baseURL+"/25").as('pikachu');
});
it('Validate the pokemon\'s name', () => {
cy.get('#pikachu').then((response)=>{
const ability_name = response.body.name;
expect(ability_name).to.eq("pikachu");
})
});
})

Promise keeps pending

I am trying to combine fetch API and promises
When I do this, everything works
queryAPI(currency, cryptocurrency){
const url = fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => response.json())
.then(data => console.log(data));
}
However, when I try to store it in a variable, the promise keeps pending
queryAPI(currency, cryptocurrency){
const url = fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => {
const user = response.json()
console.log(user);
});
}
1) What am I doing wrong?
2) Is there any way I can get the value of the "user" outside of the function?
Thanks
The .json method also returns a promise. You have to call .then once again to get the final result:
queryAPI(currency, cryptocurrency){
const url = fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => response.json())
.then(user => console.log(user))
}
so you could return the chained .thens from the queryAPI method and use it in the rest of your code:
const queryAPI = (currency, cryptocurrency) => {
return new Promise((resolve, rej) => {
return fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => response.json())
.then(data => resolve({user: data, currency: 'EUR'}))
.catch(e => rej(e))
});
};
queryAPI('EU', 'bitcoin').then(data => {
console.log(data.user);
console.log(data.currency)
});

How to execute dependent, sequential calls and return an array with all responses?

I have a redux-observable epic which polls an API and I'm trying to execute three dependent, sequential http requests and gather all responses in an array.
toArray() is never executed in this case because concatMap() is not finished. I tried to move the calls inside of a mergeMap() and gather the array there, but only the last call was in the array.
timer(0, POLLING_INTERVAL).pipe(
concatMap(() => from(fetchApi(url1))),
concatMap(response => {
const url2 = 'URL based on first response';
return from(fetchApi(url2));
}),
concatMap(response => {
const url3 = 'URL based on second response';
return from(fetchApi(url3));
}),
toArray(), // expected [{response1}, {response2}, {response3}]
map(data => ({
type: ActionTypes.FETCH_SUCCESS,
payload: { data },
})),
catchError(error =>
of({
type: ActionTypes.FETCH_FAILED,
payload: { error },
}),
),
takeUntil(
action$.pipe(
ofType(ActionTypes.CANCEL_POLLING),
),
),
);
This depends on what you want to do. toArray() won't help you because timer never completes and toArray() emits only when its source completes.
Maybe you're looking for something like this:
timer(0, POLLING_INTERVAL).pipe(
concatMap(() => from(fetchApi(url1)).pipe(
concatMap(response1 => {
const url2 = 'URL based on first response';
return forkJoin([of(response1), fetchApi(url2)]);
}),
concatMap(([response1, response2]) => {
const url3 = 'URL based on second response';
return forkJoin([of(response1), of(response2), fetchApi(url3)]);
}),
)),
map(data => ({
type: ActionTypes.FETCH_SUCCESS,
payload: { data },
})),
...

Vue request fails but does not log errors

I have this add method in my vue script
if (this.edit === false) {
this.link.link = submitEvent.target.elements.link.value;
fetch('products', {
method: 'POST',
body: JSON.stringify(this.link),
headers: {
'content-type': 'application/json'
}
})
.then(res => res.json())
.then(res => { // this does not get executed
this.qrcode.redirect_url = "";
alert('Added');
this.fetchAll()
})
.catch(err => console.log(err.res));
}
}
When I fill the form the request is send and entry is made to the database but I do not get response.
I am using laravel as backend and Add method in Controller returns 200 response after creation.
What could cause it and why console.log(err) does not not display anything?

Resources