Open xtingray opened 6 years ago
You can only navigate to pages within Cypress that are content-type='text/html'
. I imagine that your application is navigating to a page that is not of that type (where the file is located), so Cypress is expecting a load
event to happen under the assumption of having been navigated to a new text/html
page.
As a workaround, you could test that upon clicking the button that this downloadFile
method is called using cy.spy()
or change the implementation of the button to an <a>
with href
attribute.
A a more permanent fix, Cypress could potentially be updated to handle some other content-types.
I'm with same problem, when I click the download link, the Cypress still in the same step (expecting a load
event happen).
But, how can I spy
the event downloadFile
, if I need fire it by clicking the link? The problem is the click()
need to have a loaded page response.
I am having this exact same issue. I am asserting on the status code of the call, but even after the assert passes it just keeps waitifing for the page to load.
Is there a way I could force cypress to stop the test execution after the "its" statement?
Same story here. Tests have a big lag and fail after in CI when file was downloaded.
I have the same issue. Is there a way to stop the page load from triggering?
Did any one find the workaround for this issue. I am having the Same issue. Cypress getting stuck at the step of clicking anchor element whose HREF has the full URL to download the file. Though the file gets successfully downloaded but the response never comes back to the cypress steps and eventually it gets timeout.
I am having same issue; Is there any workaround anyone found for this issue?
Same issue. I can't hack around it. This is a complete blocker.
Same for me - when running via cypress binary in Chrome, everything works fine- however when running cypress CLI the test just keeps on dangling forever w/o a fail or error message.
Would be a shame if you cannot test this crucial functionality - in my case it is downloading a dynamically generated export file from the backend.
+1 I'm clicking button that executes JS function creating element with URL and click it. Cypress test fails due timeout but file is downloaded correctly.
+1 Now we're just waiting for the 60s "Page Load" timeout each time a file download is triggered.
At the very least we need a way to prevent Cypress from triggering a Page Load event for non-html types.
+1
+1
+1. Considered to switch from our current framework + cloud service for cross-browser tests to Cypress, but for now, this issue looks like a blocker for us.
Any news on this?
I was looking for ways to work around this, so have created a reproducible example of the issue in the code below:
This requires a sheet.csv
within the root of the project, I just used a blank csv.
index.html
<!doctype html>
<html lang="en">
<body>
<script>
function startDownload(url) {
window.location.href = url;
}
</script>
<button onclick="startDownload('/assets/sheet.csv')">
</body>
</html>
spec.js
it('download csv', () => {
cy.visit('index.html')
cy.get('button').click()
})
For the above example, we can stub the call to the function. This way the method is not called directly, but we can still test that the correct argument (the path to the file) is being passed to our download method.
it('download csv', () => {
cy.visit('index.html')
cy.window().then((win) => {
cy.stub(win, 'startDownload').as('downloadMethod')
})
cy.get('button').click()
cy.get('@downloadMethod').should('be.calledWith', 'sheet.csv')
})
You could also just not call the code within the download method when running within Cypress. This is less optimal because you do lose testing the file argument, but I thought I would mention it just in case it is helpful to someone.
From within your application code, in the example above you would do:
function startDownload(url) {
if (!window.Cypress) {
window.location.href = url;
}
}
How can I call this workaround method if I have "Download all" button, where all files are selected?
it('download csv', () => { cy.visit('index.html') cy.window().then((win) => { cy.stub(win, 'startDownload').as('downloadMethod') }) cy.get('button').click() cy.get('@downloadMethod').should('be.calledWith', 'sheet.csv') })
where the startDownload function is implemented?
This is resolved when you disabling chrome web security. This can be done by adding following line into cypress.json
"chromeWebSecurity": false
One more possible solution:
cy.intercept(downloadUrl, cy.spy().as("fileDownload"));
...
cy.get("@fileDownload").should("have.been.calledOnce");
NOTE: downloadUrl probably should be relative, as absolute will hangs, if you are using faker and it doesn't exist. Relative will end with 404 which is ok.
have any update ?
I've found a workaround/solution for a similar problem as described in the original issue. The difference is that the original issue describes running a callback function after clicking a button which triggers a download. Mine works on a form's "Submit" button that triggers a POST request with a response for downloading a CSV file. However, I think this solution would work for both cases.
Here's my form:
As you can see, the user clicks the "Go" button which is type='submit'
, which submits the form data and gets a response with these headers:
The response will time out because 1) it's not content-type: text/html
, and 2) the content-disposition
header says that this file should be downloaded and saved on the computer. It can't load the page with these headers. The solution is to modify the headers of the response:
cy.intercept(
'POST', // Method
'/myapp/signups/', // URL
(req) => {
req.continue((res) => {
// Change the response headers that would prevent the
// request from being loaded in Cypress.
res.headers['content-disposition'] = undefined
res.headers['content-type'] = 'text/html'
// I know that the CSV file should contain `fname`
expect(res.body).to.include('fname')
})
}
).as('download')
I had the same issue while I was trying to download a csv file. I have a button and when I click on it I get its respective response with a content-type different of text/html based on csv format (in my case text/comma-separated-values).
My first approach was basically to retrieve the href from the button and visit it. However, as we know, the visit command only allow us to visit content-type of text/html docs.cypress.io/api/commands/visit#Requirements-Icon-namequestion-circle
So, there are the 2 workarounds I created in here:
1 - intercept the request that is triggered when I click in the download button and replace content-disposition and content-type:
/**
* Show the CSV in the screen by changing the headers to not fail by reading responses with content-type other than text/html
*/
openCSVWithInterceptionWorkAround() {
cy.get('#downloadBtn')
.invoke('attr', 'href')
.then(($href) => {
cy.intercept(
'GET',
$href, // URL
(req) => {
req.continue((res) => {
// Change the response headers that would prevent the request from being loaded in Cypress.
// @ts-ignore
res.headers['content-disposition'] = undefined
res.headers['content-type'] = 'text/html'
})
}
).as('exportCsv')
cy.forcedWait(2000) // Give some time before clicking
cy.get(selectors.exportIcon).click()
cy.wait('@exportCsv')
})
}
2 - Extract the button url, make a request on it and write a csv file with the response:
/**
* This method exports the csv file by sending a request rather than clicking in the export csv button.
* This approach avoids the page load issue to get stuck. Also, it allow us to control the file name output from the csv that is being downloaded.
*
*
* @param {String} filePathToSaveTheCsv The file path name you can choose to save the csv file. E.g "cypress/downloads/Expense Detail Report.csv"
*/
exportCsvWithRequest(filePathToSaveTheCsv) {
cy.get('#downloadBtn')
.invoke('attr', 'href')
.then(($href) => {
cy.request($href) => {
cy.writeFile(filePathToSaveTheCsv, response.body)
})
})
}
While with the first approach you can basically open a csv in the browser, the second one allows you to properly save a csv file as if you were downloading it.
I hope it helps someone, the second approach was the best shot for my case.
Alright guys! I figured this out after a day of trial and error!
I needed to be able to download a PDF. The problem is the workflow involved me clicking on a link that would direct me to a page that would render a PDF inline using the browser's in-browser PDF render.
This only works when using Firefox (and maybe Chrome, haven't tested). Electron will not work.
Replace relevant parts in all caps marked REPLACE
with your URLs, selectors, etc.:
session = saveSession()
const downloadReq = {}
cy.intercept('GET', '/REPLACE/WITH/YOUR/DOWNLOAD/URL/PREFIX/*', (req) => {
downloadReq.url = req.url
downloadReq.headers = req.headers
req.redirect(session.url) // redirect to the current page we're on
}).as('download')
cy.get('#REPLACEWITHSELECTOR')
.contains('=== REPLACE THIS WITH THE LINK TEXT TO CLICK ON ===')
.click()
cy.log('waiting for download..')
cy.wait('@download').then(() => {
cy.log(`downloading ${downloadReq.url} with headers:`, downloadReq.headers)
cy.request({
url: downloadReq.url,
method: 'GET',
headers: downloadReq.headers,
encoding: 'binary',
}).then((res) => {
if (res.status === 200) {
const filename = res.headers['content-disposition'].split('filename=')[1].replaceAll('"', '').trim()
cy.log('got response with file:', filename)
cy.writeFile(filename, res.body, 'binary')
} else {
cy.log(`failed response: ${res.status}: ${res.statusText}`)
}
})
})
What we do is we:
cy.request
with cy.writeFile
to save the PDF to where we want
Current behavior:
I am trying to test a download action from a
.ts
file using the attributewindow.location.href
. It doesn't matter what value I assign for the URL string, the test instruction fails claiming a timeout waiting for the request when the file actually has been downloaded in few seconds. For some unknown reason Cypress never can reach the URL.Note: I am using a Genesis UI theme as my development code base.
These are the paths that I have tried aiming to get an Ok result from the Cypress test:
window.location.href = '/api/download/file_id';
window.location.href = '/#/api/download/file_id';
window.location.href = 'http://localhost:5000/api/download/file_id';
window.location.href = 'http://localhost:5000/#/api/download/file_id';
All of them fail. But if I try the first option as a normal user from the browser directly, the download works perfectly.
Note: During the Cypress test, I can see how the file is actually downloaded. The download action occurs.
Desired behavior:
All that I am expecting is to be able to test my download action from the Cypress test.
If I trigger the download button using something like:
I expect to get a positive response from the test if the file was actually downloaded.
How to reproduce:
Note: It would be great if you can try this steps using a Genesis UI theme as the base application.
.ts
method on the (click) parameter:(click)="downloadFile()"
Note: Include adata-cy
parameter as part of the button in the form:data-cy="download-file"
.ts
method use the instruction, ensuring to use a valid path:window.location.href = '/path/to/file/for/download';
cy.get('[data-cy=download-file]').click();
Test code:
Additional Info (images, stack traces, etc)