After a download attempt an excel file with cy.request() command, response.body can be saved by using cy.writeFile command with any filename and extension (like 'text.xlsx') but file cannot be open.
Any type of file should be saved with any extension and can be opened properly.
Cypress: 3.0.1
Operating system: Mac
Browser : Electron 59
To better understand this use case, why do you need to cy.writeFile a response you got from your server? Couldn't you do assertions directly on the response.body?
For instance, if you could write a giant .csv or .xslx or .pdf what more would you do inside of Cypress past that point?
I'm just trying to understand what you actually gain by writing the file after you receive the response.
What is the content type of the server's response? My guess is that you need to set the encoding correctly in fs.writeFile and then it will be opened correctly.
After download this file i need to insert some data and upload it back to server.
Here is the response:

I tried all of supported encoding types of cy.writefile but none of them worked for me.
Do you need it in .xlsx format in order to upload it to the server? Or did you want it in .xlsx format in order to update the data from that filetype?
@kutlaykural can you provide your code? The screenshot you posted is just headers, not the body.
@jennifer-shehane
Server provide me data only in excel format. And server accept data to upload in this format too.
@brian-mann
(to gather a download link i also do some other request)
here is test file code:
var downloadUrl='/v1/enterprise/import/template/'+jobId;
cy.request({
method: 'GET',
url: downloadUrl
}).then((response) => {
cy.writeFile('/cypress/fixtures/excel.xlsx',response.body)
})
I am having a similar issue here, except that I am dealing with zip files.
cy.log('Package URL is ' + downloadURL)
cy.request(downloadURL).then((response) => {
expect(response.status).to.be.equal(200)
cy.log(response.body.length)
cy.writeFile('cypress/work/downloadTest.zip', response.body, 'binary')
})
The logged response.body.length (50505) is of different value from the original file size on the server and the content-length header (53398) in the response. If I manually download the zip file using another web browser, the saved file can be opened correctly.
I suspect cy.request is performing some post-processing on the response body, even though the response's content-type is set to application/zip. I have repeated the same test a number of times; the response.body.length is always the same length, so it is clearly not a network issue.

Same issue here, the downloaded zip is corrupt.
Yes I have come across this issue too. I am thinking of maybe using cypress to get the URL, but maybe using something else to hit that URL to download the file as a workaround for now.
@nootn : Did you find any workaround for this?
Me also come across similar issue.
I want to assert the download file data. I got the way to parse the pdf but the pdf should be in the fixture or plugin folder. For that I need to write the repoinse that I got from the server.
Got the same issue on downloading/saving PDF files. Can anyone help us with this?
As a workaround, I found that I could use a custom plugin to perform the download. In my case, I was trying to download an xlsx file, but I think a similar approach could be used to test PDF files or just save the files after downloading them.
Added via npm:
plugins/index.js:
on('task', {
// args must be {url: "<url>, cookies: [{name: "cookie1", value: "value1"}, {name: "cookie2", value: "value2"}, ...]"}
parseXlsx(args) {
const cookieheader = args.cookies.map(e => e.name + "=" + e.value).join(";");
return new Promise((resolve, reject) => {
const r = request({url: args.url, encoding:null, headers: {Cookie: cookieheader}}, function(err, res, body) {
if (!res) {
return reject(new Error("No response"));
}
if (res.statusCode !== 200) {
return reject(new Error("Bad status code: " + res.statusCode));
}
const sheet = xlsx.parse(body);
console.log(JSON.stringify(sheet));
resolve(sheet);
});
});
}
});
support/commands.js:
Cypress.Commands.add("parseXlsx", (url) => {
return cy.getCookies().then(cookies => {
return cy.task('parseXlsx', {url: url, cookies: cookies });
});
});
In my test file:
// Call parseXlsx and verify that the export is as expected
cy.parseXlsx(exportUrl).should(data => {
expect(data).to.have.xlsSheetName("Payments");
expect(data).to.have.xlsSheetRowCell(0,0,0,'Reference #');
});
@WesleySSmith thank you very much, this idea helped me to create a workaround. Maybe I put it here, just in a case someone needs it, too:
plugins/index.js:
const request = require('request');
const fs = require('fs');
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task', {
downloadPdf(args) {
const directory = args.directory;
const cookieHeader = args.cookies.map(e => e.name + '=' + e.value).join(';');
return new Promise((resolve, reject) => {
request({url: args.url, encoding: null, headers: {Cookie: cookieHeader}}, function(err, res, body) {
if (!res) {
return reject(new Error('No response'));
}
if (res.statusCode !== 200) {
return reject(new Error('Bad status code: ' + res.statusCode));
}
const contentDisposition = res.headers['content-disposition'];
if (!contentDisposition || contentDisposition.indexOf('inline') === -1) {
return reject(
new Error('Broken response: does not contain content-disposition of inline file type')
);
}
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(contentDisposition);
if (matches == null || !matches[1]) {
return reject(new Error('Broken response: does not contain filename'));
}
const fileName = matches[1].replace(/['"]/g, '') + '.pdf';
fs.writeFileSync(directory + fileName, body);
resolve(body);
});
});
}
});
};
support/commands.js:
Cypress.Commands.add('downloadPdf', (url, directory) => {
return cy.getCookies().then(cookies => {
return cy.task('downloadPdf', {url: url, directory: directory, cookies: cookies });
});
});
In my test file:
cy.downloadPdf(pdfUrl, 'temp/');
My plugin/index.js contains a bit more code, because I need to parse response headers in order to get correct PDF filename.
Also, I reported this issue in a separated task: https://github.com/cypress-io/cypress/issues/3576
So it's already approved and marked as a bug and hopefully we won't need to use such workarounds soon 馃檶
Thanks roma-glushko! Saved my day.
The code for this is done in cypress-io/cypress#7382, but has yet to be released.
We'll update this issue and reference the changelog when it's released.
Released in 4.7.0.
This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v4.7.0, please open a new issue.
Most helpful comment
@WesleySSmith thank you very much, this idea helped me to create a workaround. Maybe I put it here, just in a case someone needs it, too:
plugins/index.js:
support/commands.js:
In my test file:
My
plugin/index.jscontains a bit more code, because I need to parse response headers in order to get correct PDF filename.Also, I reported this issue in a separated task: https://github.com/cypress-io/cypress/issues/3576
So it's already approved and marked as a bug and hopefully we won't need to use such workarounds soon 馃檶