Skip to content

Commit 5236d97

Browse files
authored
split long file download spec into separate specs (#656)
1 parent cf5183d commit 5236d97

File tree

9 files changed

+587
-377
lines changed

9 files changed

+587
-377
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// <reference types="cypress" />
22
describe('XHR', () => {
3-
it('is faster than 300ms', { retries: 3 }, () => {
3+
it('is faster than 600ms', { retries: 3 }, () => {
44
cy.visit('index.html')
55

66
// before the request goes out we need to set up spying
@@ -9,6 +9,6 @@ describe('XHR', () => {
99
cy.route('POST', '/posts').as('post')
1010

1111
cy.get('#load').click()
12-
cy.wait('@post').its('duration').should('be.lessThan', 300)
12+
cy.wait('@post').its('duration').should('be.lessThan', 600)
1313
})
1414
})

examples/testing-dom__download/README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22

33
![File download in Chrome](images/chrome.png)
44

5-
See [cypress/plugins/index.js](cypress/plugins/index.js) to see how we validate the files after download. See the [cypress/integration/spec.js](cypress/integration/spec.js) spec file that downloads and verifies:
6-
75
- a CSV file
86
- an Excel file
97
- a PNG image
108
- a TXT file
119
- a JS file
1210
- a Zip file
1311

14-
Text files are validated right from the browser spec, but the binary files like the downloaded Zip archives need to be validated from Node code using the plugins file.
12+
Spec file | Description
13+
---|---
14+
[form-submission-spec.js](./cypress/integration/form-submission-spec.js) | Intercepts and verifies a file downloaded after a form submission
15+
[local-download-spec.js](./cypress/integration/local-download-spec.js) | Downloads files from local domain by using `<a href=... download>` anchor links
16+
[location-href-spec.js](./cypress/integration/location-href-spec.js) | Intercepts and verifies a file downloaded via setting `document.location.href=...` URL
17+
[remote-download-spec.js](./cypress/integration/remote-download-spec.js) | Downloads files from another domain by using `<a href=... download>` anchor links
18+
19+
## Notes
20+
21+
Text files are validated right from the browser spec, but the binary files like the downloaded Zip archives need to be validated from Node code using the plugins file, see [cypress/plugins/index.js](cypress/plugins/index.js)
1522

1623
The spec also shows how to "catch" form submission that downloads a file using [cy.intercept()](https://on.cypress.io/intercept). After intercepting the request and redirecting back at the test, the test shows how to request the file and validate it.
1724

examples/testing-dom__download/cypress.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"viewportHeight": 900,
66
"baseUrl": "http://localhost:8070",
77
"chromeWebSecurity": false,
8-
"pageLoadTimeout": 5000
8+
"pageLoadTimeout": 5000,
9+
"testFiles": "**/*-spec.js"
910
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// @ts-check
2+
/// <reference types="cypress" />
3+
import { validateCsvList } from './utils'
4+
const neatCSV = require('neat-csv')
5+
6+
describe('file download', () => {
7+
context('form submission', () => {
8+
it('sends csv', () => {
9+
cy.visit('/')
10+
cy.contains('h3', 'Download from a form')
11+
12+
// prepare for form submission that returns back a file
13+
// https://on.cypress.io/intercept
14+
cy.intercept({
15+
pathname: '/records.csv',
16+
}, (req) => {
17+
// instead of redirecting to the CSV file
18+
// and having the browser deal with it
19+
// download the file ourselves
20+
// but we cannot use Cypress commands inside the callback
21+
// thus we will download it later using the captured URL
22+
req.redirect('/')
23+
}).as('records')
24+
25+
cy.get('button[data-cy=download-form-csv]').click()
26+
cy.wait('@records').its('request').then((req) => {
27+
cy.request(req)
28+
.then(({ body, headers }) => {
29+
expect(headers).to.have.property('content-type', 'text/csv; charset=utf-8')
30+
31+
return neatCSV(body)
32+
}).then(validateCsvList)
33+
})
34+
})
35+
})
36+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// @ts-check
2+
/// <reference types="cypress" />
3+
import { validateCsvList, validateCsvFile, validateExcelFile, validateTextFile, validateImage, validateZip } from './utils'
4+
const neatCSV = require('neat-csv')
5+
const path = require('path')
6+
7+
describe('file download', () => {
8+
const downloadsFolder = Cypress.config('downloadsFolder')
9+
10+
context('from local domain localhost:8070', () => {
11+
it('CSV file', () => {
12+
cy.visit('/')
13+
cy.contains('h3', 'Download CSV')
14+
cy.get('[data-cy=download-csv]').click()
15+
16+
cy.log('**read downloaded file**')
17+
18+
// file path is relative to the working folder
19+
const filename = path.join(downloadsFolder, 'records.csv')
20+
21+
// browser might take a while to download the file,
22+
// so use "cy.readFile" to retry until the file exists
23+
// and has length - and we assume that it has finished downloading then
24+
cy.readFile(filename, { timeout: 15000 })
25+
.should('have.length.gt', 50)
26+
// parse CSV text into objects
27+
.then(neatCSV)
28+
.then(validateCsvList)
29+
})
30+
31+
it('CSV file using anchor href name', () => {
32+
cy.visit('/')
33+
cy.contains('h3', 'Download CSV')
34+
cy.get('[data-cy=download-csv]').click()
35+
36+
// let's find out the download name
37+
cy.get('[data-cy=download-csv]').should('have.attr', 'download')
38+
cy.get('[data-cy=download-csv]').should('have.attr', 'href')
39+
.then((filename) => {
40+
expect(filename).to.match(/\.csv$/)
41+
cy.log(`CSV name **${filename}**`)
42+
//@ts-ignore
43+
validateCsvFile(filename)
44+
})
45+
})
46+
47+
it('Excel file', () => {
48+
// let's download a binary file
49+
50+
cy.visit('/')
51+
cy.contains('h3', 'Download XLSX')
52+
cy.get('[data-cy=download-xlsx]').click()
53+
54+
cy.log('**confirm downloaded file**')
55+
56+
validateExcelFile()
57+
})
58+
59+
it('TXT file', { browser: '!firefox' }, () => {
60+
cy.visit('/')
61+
cy.get('[data-cy=download-txt]').click()
62+
63+
cy.log('**confirm downloaded text file**')
64+
validateTextFile()
65+
})
66+
67+
// limiting this test to Chrome browsers
68+
// since in FF we get a cross-origin request error
69+
it('PNG image', { browser: '!firefox' }, () => {
70+
// image comes from the same domain as the page
71+
cy.visit('/')
72+
cy.get('[data-cy=download-png]').click()
73+
74+
cy.log('**confirm downloaded image**')
75+
validateImage()
76+
})
77+
78+
it('ZIP archive', () => {
79+
cy.visit('/')
80+
cy.get('[data-cy=download-zip]').click()
81+
82+
cy.log('**confirm downloaded ZIP**')
83+
validateZip()
84+
})
85+
})
86+
87+
it('finds file', { browser: '!firefox', retries: 1 }, () => {
88+
// imagine we do not know the exact filename after download
89+
// so let's call a task to find the file on disk before verifying it
90+
// image comes from the same domain as the page
91+
cy.visit('/')
92+
cy.get('[data-cy=download-png]').click()
93+
94+
// give the file time to download
95+
cy.wait(3000)
96+
97+
cy.log('**find the image**')
98+
const mask = `${downloadsFolder}/*.png`
99+
100+
cy.task('findFile', mask).then((foundImage) => {
101+
cy.log(`found image ${foundImage}`)
102+
cy.log('**confirm downloaded image**')
103+
validateImage()
104+
})
105+
})
106+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// @ts-check
2+
/// <reference types="cypress" />
3+
import { validateCsv, validateCsvFile, downloadByClicking } from './utils'
4+
const path = require('path')
5+
6+
describe('file download', () => {
7+
const downloadsFolder = Cypress.config('downloadsFolder')
8+
9+
context('using location.href', () => {
10+
describe('CSV file', () => {
11+
// NOTE: test times out because the browser stays on the CSV file URL
12+
it.skip('CSV file', () => {
13+
cy.visit('/')
14+
cy.get('[data-cy=download-csv-href]').click()
15+
})
16+
17+
it('becomes the document after download', () => {
18+
cy.visit('/')
19+
cy.intercept('GET', '*.csv', (req) => {
20+
req.reply((res) => {
21+
// show the CSV as plain text in the browser
22+
res.headers['content-type'] = 'text/html; charset=utf-8'
23+
res.send(res.body)
24+
})
25+
})
26+
27+
cy.get('[data-cy=download-csv-href]').click()
28+
// the actual CSV file is NOT downloaded
29+
// the browser shows the CSV file url
30+
cy.location('pathname').should('be.equal', '/records.csv')
31+
// the contents of the CSV file is shown
32+
cy.contains('Adam').should('be.visible')
33+
// now that we have seen the CSV contents,
34+
// we can get back to the original HTML page
35+
cy.go('back')
36+
})
37+
38+
it('grabbed via intercept', () => {
39+
cy.visit('/')
40+
// we will set the CSV file contents in this variable
41+
let csv
42+
43+
cy.intercept('GET', '*.csv', (req) => {
44+
req.reply((res) => {
45+
csv = res.body
46+
// redirect the browser back to the original page
47+
res.headers.location = '/'
48+
res.send(302)
49+
})
50+
})
51+
.as('csvDownload')
52+
53+
cy.get('[data-cy=download-csv-href]').click()
54+
cy.wait('@csvDownload')
55+
// we should stay on the original URL
56+
cy.location('pathname').should('be.equal', '/')
57+
.then(() => {
58+
// by now the CSV variable should have CSV text
59+
validateCsv(csv)
60+
})
61+
})
62+
63+
it('downloaded via cy.request', () => {
64+
cy.visit('/')
65+
let downloadUrl
66+
67+
cy.intercept('GET', '*.csv', (req) => {
68+
downloadUrl = req.url
69+
req.reply({
70+
statusCode: 302,
71+
location: '/',
72+
})
73+
})
74+
75+
cy.get('[data-cy=download-csv-href]').click()
76+
.should(() => {
77+
// retries until the intercept sets the download URL
78+
expect(downloadUrl).to.be.a('string')
79+
})
80+
.then(() => {
81+
// download URL ourselves and save as a file
82+
cy.request(downloadUrl).its('body').then((csv) => {
83+
// save so we have it as an artifact
84+
const filename = path.join(downloadsFolder, 'records.csv')
85+
86+
cy.writeFile(filename, csv, 'utf8')
87+
validateCsv(csv)
88+
})
89+
})
90+
})
91+
92+
// NOTE: the Command Log says the file has been downloaded, but it's not there
93+
it.skip('downloaded via out own anchor click', () => {
94+
cy.visit('/')
95+
let downloadUrl
96+
97+
cy.intercept('GET', '*.csv', (req) => {
98+
downloadUrl = req.url
99+
req.reply({
100+
statusCode: 302,
101+
location: '/',
102+
})
103+
})
104+
105+
cy.get('[data-cy=download-csv-href]').click()
106+
.should(() => {
107+
// retries until the intercept sets the download URL
108+
expect(downloadUrl).to.be.a('string')
109+
})
110+
.then(() => {
111+
// we can go back to the original page
112+
cy.visit('/')
113+
downloadByClicking(downloadUrl, 'records.csv')
114+
// hmm, the file does not seem to be downloaded
115+
// despite the command log's message
116+
validateCsvFile('records.csv')
117+
})
118+
})
119+
})
120+
})
121+
})

0 commit comments

Comments
 (0)