Day one with Cypress.
Trying to make a JSON call and examine result but all attempts giving me undefined.
describe('Test Number 000001 !!!', function() {
it('starts off OK', function() {
expect(true).to.equal(true)
})
it('can call the example api', function() {
cy.request('https://jsonplaceholder.typicode.com/todos/1')
})
it('can call the example api and store the result', function() {
result = cy.request('http://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json()) // I tried with and without then
expect(result).to.equal(1) // will fail but once i get real response I will adjust
})
})
Update: the below passes... but doesn't actually check anything (any values used will pass)
cy.request('http://jsonplaceholder.typicode.com/todos/1').
then((response) => {
response = JSON.stringify(response)
var jsonData = JSON.parse(response)
for (var i = 0; i < jsonData.length; i++) {
expect(jsonData[i]['xuserId']).to.equal(1)
}
})
I have used the same test API earlier and have got the results like below; Please have a look. I have used response.body. to get json values
it('Sample Api testing', function () {
cy.request({
method: 'get',
url: 'https://jsonplaceholder.typicode.com/todos/1',
headers: {
'accept': 'application/json'
}
}).then((response) => {
expect(response.status).to.eq(200);
expect(response.body.userId).to.eq(1);
expect(response.body.id).to.eq(1);
expect(response.body.title).to.eq('delectus aut autem');
})
})
You need to convert response object to a json string using stringify function inside the promise.
.then((response) => {
response = JSON.stringify(response)
var jsonData = JSON.parse(response)
for (var i = 0; i < jsonData.length; i++) {
expect(jsonData[i]['userId']).eq(1)
}
edit
expect(jsonData[i]['userId']).to.equal(1)
Related
I have migrated a piece of code to be able to export data as excel file in angular.
I assume the fact that the json is well formed and send from the server to the angular side. I can see it in the network frame in th browser.
For small json, it's ok but when the size of the json starts to be large, the answer still failed.
This following code corresponding to the service call
exportSynthesis(recordId: number, moduleId: number) {
const body = null;
return this.http.post(this.apiUrl + `/data`
+ `${recordId}/module/${moduleId}`, body,
{
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
observe: 'response', responseType: 'json' }).pipe(
map((resp: any) => {
return resp.body;
}));
}
and here, its the method which manages the return.
exportSynthesis() {
this.service.exportSynthesis(this.recordId, this.moduleId)
.subscribe(
(exportResult) => { this.exportResult = exportResult; },
err => {
console.log('err:', err);
this.errorHandlerService.handleError('failed', err);
},
() => {
console.log('json:', this.exportResult);
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(this.exportResult);
const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] };
const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = '(GEO) ' + this.record.label + ' - name.xlsx';
a.click();
window.URL.revokeObjectURL(url);
a.remove();
});
}
Currently, i do not manage to understand why it still finish in error and I get only "ok" in the console log.
Any idea?
regards
Angular's HttpClientModule default response is a json.
Your problem is that you try to access the body property of the HTTP response, but Angular interprets that as you trying to access the body property in the body of the response.
Remove observe and responseType from your post request and treat the response as a json. It should work.
find:
just need to use text as json
return this.http.post(this.apiUrl + `/geo/v1/synthesis/xls/record/`
+ `${recordId}/module/${moduleId}`, body,
{
headers: headers,
observe: 'response',
responseType: 'text' as 'json'}).
map((resp: any) => {
return resp.body;
});
}
This question already has answers here:
How can I get the status code from an HTTP error in Axios?
(15 answers)
Closed 7 days ago.
I'm using blob responseType with Axios in my VueJS app for downloading a document from the server. When the response code is 200 it works fine and download the file but when there is any http error, I'm not able to read the status code when I catch the error because the error is a JSON response.
Has anyone had a similar issue and worked out a way to convert the blob response type to json and thrown an error based on the status code?
I have tried sending the response as a plain text from Laravel backend and tried converting the response to JSON or text in the front-end but no luck.
I have tried reading error response headers but no luck.
Axios({
url: 'xxxx',
method: 'GET',
responseType: 'blob',
})
.then((response) => {
//code to read the response and create object url with the blob and download the document
})
.catch((error) => {
console.log('Error', error.message); //nothing
console.log('Error', error.error); //undefined
console.log('Error', error.data); //undefined
const blb = new Blob([error], {type: "text/plain"});
const reader = new FileReader();
// This fires after the blob has been read/loaded.
reader.addEventListener('loadend', (e) => {
const text = e.srcElement.result;
console.log(text);
});
// Start reading the blob as text.
reader.readAsText(blb);
});
I just want to throw the error message based on the status code. If it's 401 just want it to be unauthorized and anything else throw it on to the component.
The reason is that the response type is blob.
In case of error, the status code is available directly in your exception object. However, the response is a promise.
What you need to do is:
.catch((error) => {
let statusCode = error.response.status
let responseObj = await error.response.data.text();
:
:
For more details you can read documentation.
You can do this, it cast the error in JSON if it's a blob or let the response data as it received and you can do work with it.
let errorString = error.response.data;
if (
error.request.responseType === 'blob' &&
error.response.data instanceof Blob &&
error.response.data.type &&
error.response.data.type.toLowerCase().indexOf('json') != -1
) {
errorString = JSON.parse(await error.response.data.text());
}
alert(errorString);
You need to convert the response blob to json:
Axios({
url: 'xxxx',
method: 'GET',
responseType: 'blob',
})
.then((response) => {
//code to read the response and create object url with the blob and download the document
})
.catch((error) => {
if (
error.request.responseType === 'blob' &&
error.response.data instanceof Blob &&
error.response.data.type &&
error.response.data.type.toLowerCase().indexOf('json') != -1
) {
new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = () => {
error.response.data = JSON.parse(reader.result);
resolve(Promise.reject(error));
};
reader.onerror = () => {
reject(error);
};
reader.readAsText(error.response.data);
})
.then(err => {
// here your response comes
console.log(err.response.data)
})
};
});
You can use this inside interceptor.
For more info
I believe you might be using the error variable in your catch() incorrectly.
Axios passes an error object that has a response property that is where you will want to look for your error or message.
https://github.com/axios/axios#handling-errors
On a side note if you can catch the error server side you could try setting the Content-type header to text/plain. Using either header('Content-Type: plain/text') or Laravel's Response Methods
you can do the following way
axios.post("URL", data, {
validateStatus: (s) => s <= 500,
responseType: 'blob',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/pdf'
}
})
.then(async (response) => {
if (response.status !== 200) {
// error handling
const error = JSON.parse(await response.data.text());
console.log('error: ', error);
alert(error.message);
} else {
console.log('res', response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf'); //or any other extension
document.body.appendChild(link);
link.click();
}
})
You can convert blob response globaly:
$axios.onError(async ({request, response}) => {
const status = response.status
let data = response.data
if (request.responseType === 'blob' && data instanceof Blob && data.type) {
const text = await data.text()
data = data.type.toLowerCase().includes('json') ? JSON.parse(text) : text
}
throw {status, data} // error model
})
catch(async(error) => {
const errorJson = JSON.parse(await error.response.data.text());
console.log('error: ', errorJson.error_message);
})
I use a function for Fetch with below code :
var URL='...'
export function PostData(method,data){
fetch(URL+method,{
method:'POST',
body:JSON.stringify(data),
headers:{'Content-Type':'application/json'},
}).then(res => res.json())
.then(response => {
var ret=JSON.stringify(response)
return ret
})
.catch((error) => {
console.error(error)
})
}
and use it like below :
var retData=PostData('login/Authenticate',data)
retData is empty but in function ret has data
You PostData function does currently not return anything, so it is empty.
First step would be to add a return statement:
export function PostData(method,data){
return fetch(URL+method,{
method:'POST',
...
This will make your function return a value, but not just a simple value, but a promise! Promises are not the easiest to understand, but there is also a of people who tried to explain them
- https://developers.google.com/web/fundamentals/primers/promises
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Now how can you use the value anyway?
PostData('login/Authenticate',data)
.then(retData => {
// ... use retData here
});
Now, you used the react-native tag, so I am assuming you want to use this value in your render function. You can't do this simply by putting the PostData call in your render function. You'll have to put it in state, and then use that value in render:
state = { retData: null }
componentDidMount() {
PostData('login/Authenticate',data)
.then(retData => {
// This puts the data in the state after the request is done
this.setState({ retData: retData });
});
}
render() {
let retData = this.state.retData;
// ... use retData in your render here, will be `null` by default
There are a lot more different or cleaner ways to do this, but I tried to keep this answer as simple and bare as possible :)
It is empty at this point because the call to fetch is asynchronous and the literal is set to undefined as it moves to the next statement because it has not been resolved yet. One way around it is to return the promise object itself and then use .then to get the response once it is resolved.
var URL = '...'
export function PostData(method, data) {
// return the promise object
return fetch(URL + method, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
},
}).then(res => res.json())
.then(response => {
var ret = JSON.stringify(response)
return ret
})
.catch((error) => {
console.error(error)
})
}
PostData('login/Authenticate',data).then(response => {
// do something with the response
});
A cleaner approach would be is to use the async/await ES7 feature which makes it more readable.
var URL = '...'
export function PostData(method, data) {
// return the promise object
return fetch(URL + method, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
},
}).then(res => res.json())
.then(response => {
var ret = JSON.stringify(response)
return ret
})
.catch((error) => {
console.error(error)
})
}
async function getData() {
let retData = await PostData('login/Authenticate',data);
}
I'm working in a code with Promise to get JSON values, the code is working, but the output aways return between the words Promise { }.
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'https://api.info.com/',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
console.log(info); //Output: Promise { [ { name: 'Gary'}, { name: 'John'} ] }
What I'm doing wrong?
I'd like my output was only as in the website:
[ { name: 'Gary'}, { name: 'John'} ]
==> UPDATE
It's works partially:
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'https://api.info.com/',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
info.then(function(value) {console.log(value)});//output: [ { name: 'Gary'}, { name: 'John'} ]
but, I need to use the result outside of then to perform others queries operations using this Json result, I tried something like that:
info.then(function(value) { jsonfile = value });
console.log(jsonfile);//Output: jsonfile is not defined
You're console.logging the promise object. If you want the data from the resolve action, you can chain a function that uses that data.
ex:
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'https://api.info.com/',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
//On resolve, perform some function to the returned value (This can be replaced with a function call)
info.then(function(value) {console.log(value)});
More examples and details can be found at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
Edit in response to comment (i don't have the rep to post comments): Promises are synchronous, while code not inside the promise or then() is not treated synchronously. This is worth mentioning because while you could a sign your value to a global variable or define your var 'value' before the promise, 'value' may not have been resolved by the promise yet, meaning you have introduced a race condition. Instead, if you need the data from your promise in another function (they need to run sequentially), that function should be invoked in your then().
Ex:
info.then(function(value){
otherfunction(value);
});
function otherfunction(val) {
//does whatever you need to value and will occur after the promise above resolves
}
info is a pending promise. You need to await of use .then on it
Sample code
const info = new Promise((resolve, reject) => {
var req = require('request');
var options = {
url: 'http://httpbin.org/get',
headers: {
'User-Agent': 'request'
}
};
req(options, function (err, response, body) {
if (!err && response.statusCode === 200) {
resolve(JSON.parse(body));
}
});
});
console.log(info); // Promise { <pending> }
info.then((result) => console.log(result)); // api result
I am using http module to send a request to api. So my response body is very large, and I am getting incomplete and when trying to parse to javascript object I am getting an error, that the json is not valid.
Here is my code.
function sendPostRequest(method, url, data, callback) {
if (typeof data === 'undefined') {
data = {};
}
var data = querystring.stringify(data);
var post_options = {
host: API.Host,
port: API.Port,
path: API.Prefix + url,
method: method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + API_USER.token
}
};
var post_req = http.request(post_options, function (res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
callback(chunk);
});
});
// post the data
post_req.write(data);
post_req.end();
}
sendPostRequest('GET', 'user/get_accounts', data, function (res) {
res = JSON.parse(res);
mainWindow.webContents.send('user:account', res);
return;
}, true);
Please help to solve this problem! Thanks!
If the data is large and it's provided in chunks(incomplete json) you might have better luck with:
var post_req = http.request(post_options, function (res) {
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
callback(rawData);
});
});