Cypress: Making HTTP request working with Promise - javascript

I'm trying to make an HTTP request on my server and check the result after.
Currently, I well have a result in the response object of the postCar method. But the result is null on the test on the then() method.
Where is the problem ?
postCar($access_token, $body) {
return new Cypress.Promise((resolve) => {
let bodyString = '';
cy.request({
method: 'POST',
url: '/applications',
form: true,
failOnStatusCode: false,
body: $body,
headers: {
Content: 'application/json',
Authorization: 'Bearer ' + $access_token
}
})
.then((response) => {
cy.log('Response = ' + JSON.stringify(response));
resolve(response);
});
});
}
My test:
it('Car Spec : create a car', function () {
let accessToken = Cypress.env('ACCESS_TOKEN')
var correct_body = getValidBody();
cy.postCar(accessToken, correct_body).then(function(res) {
cy.log('Application Spec : postCar response :' + JSON.stringify(res));
});
});

You shouldn't wrap the command in a Promise. Cypress Custom commands are not meant to return promises. Try just this:
function postCar ($access_token, $body) {
let bodyString = ''
cy.request({
method: 'POST',
url: '/applications',
form: true,
failOnStatusCode: false,
body: $body,
headers: {
Content: 'application/json',
Authorization: `Bearer ${$access_token}`,
},
})
.then((response) => {
cy.log(`Response = ${JSON.stringify(response)}`)
return response
})
}

Related

Passing a value from one request to another request in ReactJs

I need help because I couldn't use a separate function to generate the token - it gives out a promise, not a value. I was told that a value can only be used inside a function.
For each request, I generate a new token in the first request and then pass that token into the second request.
I tried making a separate function to generate the token, but fetch returns a promise.
As a result, I made such a big function and it works.
Is there a way to make a separate function for the first request and pass the result to the second request?
The first token generation function is required frequently, while the second request is always different.
fetch('/api/token', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ 'id': '5' }),
})
.then(response => response.json())
.then(result => {
fetch('/api/reviews', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + result.token,
},
body: JSON.stringify({ 'limit': 10 }),
})
.then(response => response.json())
.then(result => {
this.setState({ data: result.data });
})
})
create a function that return promise
async function getToken() {
return await fetch('/api/token', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ 'id': '5' }),
})
.then(response => response.json())
.then(result => {
return Promise.resolve(result.token);
}).catch(error => {
return Promise.reject(error);
})
}
async function getReview() {
const token = await getToken().then(token => {
return token
}).catch(error => {
//handle error
});
fetch('/api/reviews', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify({ 'limit': 10 }),
})
.then(response => response.json())
.then(result => {
this.setState({ data: result.data });
})
}
i did not test this code but you get the idea
i will test and update my answer asap
Yes you can with async / await. It will allow you to lift the lexical scope of the API response from inside the .then "callback hell" and into the parent function scope.
Your separate function which fetches the token will return a promise, but then the requesting function will wait for the promise to execute and resolve before continuing.
async function fetchToken() {
const response = await fetch('/api/token', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ 'id': '5' }),
})
return await response.json();
}
async function getReviews() {
const response = await fetch('/api/reviews', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + result.token,
},
body: JSON.stringify({ 'limit': 10 }),
})
const result = await response.json();
this.setState({ data: result.data });
}
Additionally, if the token call does not need to be made every time the reviews call is made, then you can memoize the value, and use that memoized value.
const tokenMemo = useMemo(async () => await getToken(), []);
async function getReviews() {
const response = await fetch('/api/reviews', {
// ...
'Authorization': 'Bearer ' + tokenMemo,
// ...
}

Javascript: Post data to PHP with fetch

I'm using Ajax to submit data to PHP via POST method.
I have two questions:
Is there a better way serialize data instead of doing this
const reqData = 'id= + ' myobj.id + '&name=' + myobj.name'
Please note this ^ data is an object not form data.
How can I convert the ajax call to fetch
I have tried doing it like so. It returns 200 response code. but php didn't saved the data.
async function updateCountry(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: data,
});
return response;
}
updateCountry('some-url', data).then(resp => console.log(resp)).catch(err => console.log(err))
here's my Ajax code:
const reqData = `id=${myobj.id}&name=${myobj.name}`;
$.ajax({
type: 'POST',
url: 'my-php-script-url',
data: reqData,
success: function (response) {
console.log(typeof response);
$('#saving-btn').hide();
if (response.includes('success')) {
$('#saved-btn').show();
setTimeout(() => {
$('#saved-btn').hide();
}, 3000);
} else {
$('#error-btn').show();
setTimeout(() => {
$('#error-btn').hide();
}, 3000);
}
},
error: function (err) {
console.log(err);
},
});
PHP doesn't support JSON parameters automatically, it requires the parameters to be either URL-encoded or FormData.
If the object keys are the same as the post parameters, you can write a loop to create the URL-encoded string:
async function updateCountry(url, data) {
const reqData = Object.entries(myobj).map(([key, value]) => key + '=' + encodeURIComponent(value)).join('&');
const response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: reqData,
});
return response;
}

How to get the response JSON from API call

I want to retrieve the JSON response from the api call I am doing. Example, I want to retrieve something like this:
{"error":{},"success":true,"data":{"user":"tom","password":"123","skill":"beginner","year":2019,"month":"Mar","day":31,"playmorning":0,"playafternoon":1,"playevening":1}}
This is my API call using fetch in react. (yes I know sending password in URL is bad, it's for a school project)
fetch('/api/user/'+ user + '?password=' + password, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}}).then((res) => {
console.log(res); //I want to get the JSON stuff here
})
This is the API call I am calling.
app.get('/api/user/:user', function (req, res) {
// console.log(JSON.stringify(req));
// var user = req.body.user;
// var password = req.body.password;
var user = req.params.user;
var password = req.query.password;
console.log(user, password);
var result = { error: {} , success:false};
if(user==""){
result["error"]["user"]="user not supplied";
}
if(password==""){
result["error"]["password"]="password not supplied";
}
if(isEmptyObject(result["error"])){
let sql = 'SELECT * FROM user WHERE user=? and password=?;';
db.get(sql, [user, password], function (err, row){
if (err) {
res.status(500);
result["error"]["db"] = err.message;
} else if (row) {
res.status(200);
result.data = row;
result.success = true;
} else {
res.status(401);
result.success = false;
result["error"]["login"] = "login failed";
}
res.json(result);
});
} else {
res.status(400);
res.json(result);
}
});
When I do console.log(res) in the fetch call, this is what is printed:
Response {type: "basic", url: "http://localhost:3000/api/user/tim?password=123", redirected: false, status: 200, ok: true, …}body: (...)bodyUsed: falseheaders: Headers {}ok: trueredirected: falsestatus: 200statusText: "OK"type: "basic"url: "http://localhost:3000/api/user/tim?password=123"proto: Response
When I visit the website, the output is:
{"error":{},"success":true,"data":{"user":"tom","password":"123","skill":"beginner","year":2019,"month":"Mar","day":31,"playmorning":0,"playafternoon":1,"playevening":1}}
This is what I want.
In general, this is how you return the response body from the Promise.
fetch(`${baseUrl}/api/user/${user}?password=${password}`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}})
.then(response => response.json())
.then‌​(data=> {
console.log(data);
})
Try this way to parse the response:
fetch('/api/user/'+ user + '?password=' + password, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}}).then(async (res) => {
const raw = await res.text();
const parsed = raw ? JSON.parse(raw) : { success: res.ok };
console.log(parsed);
})
In this case you can also add some checks for response statuses (if you want, of course) along with parsing the result JSON.
for you to get the JSON body content from the response, you need to use json()
fetch('/api/user/'+ user + '?password=' + password, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}}).then((res) => {
const jsonData = res.json();
console.log(jsonData);
})
try this
fetch(${baseUrl}/api/user/${user}?password=${password},{
method:'GET',
headers: {
'Accept': 'application/json',
'Content-Type':
'application/json',
}}) .then(async(response ) => {
await response.json()
})

Cypress - How to use response body in another request header

I have a function that will return a token
var accessToken;
function TestToken(){
return cy.request({
method: 'POST',
url: "https://site/token",
headers: {
'Content-Type': 'application/json',
},
body: {
client_id: '1234',
client_secret: '1234',
audience: 'https://site/api/v1/',
grant_type: 'credentials'
}
}).then(response => {
return new Cypress.Promise((resolve, reject) => {
const rbody = (response.body);
var tokenPattern = "(?i)\"access_token\":\\s*\"([^\"]*)\"";
const authToken = rbody.access_token;
accessToken = authToken
cy.log(accessToken);
resolve(accessToken);
return accessToken;
})
});
I call that function in another cypress command, as I need to use the token in the authorization header
Cypress.Commands.add("ResetPwd", () => {
const authToken = TestToken();
cy.log(authToken);
cy.request({
method: 'POST',
url: "https://site/api/v2/users/1234",
headers: {
'Content-Type': 'application/json',
'authorization': 'Bearer '+authToken,
},
body: {
password: 'Test4321',
connection: 'DB',
}
});
});
When I execute the command, I don't see the token being used. Instead I see "authorization": "Bearer [object Object]" in the request.
In the TestToken funtion, I added cy.log(authToken);, to verify it was correctly grabbing the token. The cypress test failed saying
CypressError: cy.then() failed because you are mixing up async and
sync code.
but I could still see the correct token in the console.
Here is the cypress test I am trying to run
describe('Token Test', function() {
before(() => {
cy.ResetPwd();
})
it('Go to website', function() {
cy.visit('https://site/login')
});
});
functions is synchronous so in moment of call it the result will be equal Promise, promise is object and when you try to stringify object (by + operator) you get [object Object].
After some time promise will be resolved and value in authToken variable will be correct.
To fix it is suggest use Cypress.Promise like in example id cypress docs: https://docs.cypress.io/api/utilities/promise.html#Basic-Promise
I am assuming the TestToken() function is a promise and resolves asynchronously causing the authToken to be [ object Object ]
You could use async/await and the code should work fine.
Add async before the arrow function and use await const authToken = TestToken()
I solved this, after lots of work using cy.task:
cy.request({
method: 'POST',
url: '/where/you/retrieve/your/token',
body: {username: user, password: password},
failOnStatusCode: true
}).then((response) => {
cy.task('setToken', response.body);
});
then:
cy.task('getToken').then((token) => {
cy.request({
method: 'GET',
url: '/your/protected/endpoint/here',
headers: {
authorization: `Bearer ${token}`
},
failOnStatusCode: true
}).then((response) => {
expect(response.something).to.eq(something.else);
});
});

CYPRESS store cy.request response in a variable

I have this one file that calls a login function
testing.js
var res = accounts.createSession(config.email_prod,config.password_prod,user_id)
on another file, I have this:
accounts.js
export function createSession(email,password,user_id){
cy.request({
method:'POST',
url:config.accounts_prod + '/token',
headers:{
'authorization': 'Basic testestestestest'
},
qs:{
'grant_type':'password',
'username':email,
'password':password
}
}).then((response)=>{
var X = response.body.access_token
cy.log("create session " + x)
login(response.body.access_token, user_id)
})
}
export function login(token,user_id){
var result = cy.request({
method:'POST',
url:config.ws_prod + '/login.pl',
headers:{
'authorization': token,
'Content-Type' : 'application/x-www-form-urlencoded'
},
body:'user_id='+user_id+'&device_id='+device_id+'&os_type='+os_type
})
return token
}
so I want to store token value and return it to res variable on testing.js file
but everytime I store token (in this example I store it inside X) and I try to print it, it always says undefined
but I can do cy.log(token) and it was fine on login() function, but that's all it can do, it cannot be store into a variable
is there another way for me to store token ?
Maybe if i use a callback like parameter, then the second function will wait for the asynchronous task is over
export function createSession(email,password,user_id,callback){
cy.request({
method:'POST',
url:config.accounts_prod + '/token',
headers:{
'authorization': 'Basic testestestestest'
},
qs:{
'grant_type':'password',
'username':email,
'password':password
}
}).then((response)=>{
var X = response.body.access_token
cy.log("create session " + x)
callback(response.body.access_token, user_id);
})
}
var login = function (token,user_id) {
var result = cy.request({
method:'POST',
url:config.ws_prod + '/login.pl',
headers:{
'authorization': token,
'Content-Type' : 'application/x-www-form-urlencoded'
},
body:'user_id='+user_id+'&device_id='+device_id+'&os_type='+os_type
})
return token
}
// then call first fn
createSession(email,password,user_id,login);
I had almost the same issue. Comment on this answer helped me
// testing.js
let res = null;
before(() => {
accounts.createSession(config.email_prod, config.password_prod, user_id).then((responseToken) => {
res = responseToken;
console.log('response token', responseToken);
});
});
it('My tests ...', () => {
console.log('...where I can use my session token', res);
});
// accounts.js
export function createSession(email, password, user_id) {
cy.request({
method: 'POST',
url: config.accounts_prod + '/token',
headers: {
'authorization': 'Basic testestestestest'
},
qs: {
'grant_type': 'password',
'username': email,
'password': password
}
}).then((response) => {
var x = response.body.access_token
cy.log("create session " + x)
login(response.body.access_token, user_id)
return response.body.access_token; // needs return statement
})
}

Categories