fix: can send FormData with File. (#16576)

This commit is contained in:
Kukhyeon Heo
2021-05-24 23:07:44 +09:00
committed by GitHub
parent c4aaa58744
commit c2588798a0
4 changed files with 82 additions and 1 deletions
@@ -499,6 +499,32 @@ describe('src/cy/commands/request', () => {
expect(dec.decode(response.body)).to.contain('1,2,3,4')
})
})
it('can send FormData with File', () => {
const formData = new FormData()
formData.set('file', new File(['1,2,3,4'], 'upload.txt'), 'upload.txt')
formData.set('name', 'Tony Stark')
cy.request({
method: 'POST',
url: 'http://localhost:3500/dump-form-data',
body: formData,
headers: {
'content-type': 'multipart/form-data',
},
})
.then((response) => {
expect(response.status).to.equal(200)
// When user-passed body to the Nodejs server is a Buffer,
// Nodejs doesn't provide any decoder in the response.
// So, we need to decode it ourselves.
const dec = new TextDecoder()
const result = dec.decode(response.body)
expect(result).to.contain('Tony Stark')
expect(result).to.contain('upload.txt')
})
})
})
describe('subjects', () => {
@@ -6,6 +6,8 @@ const http = require('http')
const httpsProxy = require('@packages/https-proxy')
const path = require('path')
const Promise = require('bluebird')
const multer = require('multer')
const upload = multer({ dest: 'cypress/_test-output/' })
const PATH_TO_SERVER_PKG = path.dirname(require.resolve('@packages/server'))
const httpPorts = [3500, 3501]
@@ -144,6 +146,10 @@ const createApp = (port) => {
return res.send(`<html><body>it worked!<br>request body:<br>${req.body.toString()}</body></html>`)
})
app.all('/dump-form-data', upload.single('file'), (req, res) => {
return res.send(`<html><body>it worked!<br>request body:<br>${JSON.stringify(req.body)}<br>original name:<br>${req.file.originalname}</body></html>`)
})
app.get('/status-404', (req, res) => {
return res
.status(404)
+1
View File
@@ -59,6 +59,7 @@
"minimist": "1.2.5",
"mocha": "7.0.1",
"morgan": "1.9.1",
"multer": "1.4.2",
"ordinal": "1.0.3",
"react-15.6.1": "npm:react@15.6.1",
"react-16.0.0": "npm:react@16.0.0",
+49 -1
View File
@@ -282,13 +282,61 @@ module.exports = (Commands, Cypress, cy, state, config) => {
// Check if body is Blob.
// construct.name is added because the parent of the Blob is not the same Blob
// if it's generated from the test spec code.
if (requestOpts.body instanceof Blob || requestOpts?.body?.constructor.name === 'Blob') {
if (requestOpts.body instanceof Blob || requestOpts.body?.constructor.name === 'Blob') {
requestOpts.bodyIsBase64Encoded = true
return Cypress.Blob.blobToBase64String(requestOpts.body).then((str) => {
requestOpts.body = str
})
}
// https://github.com/cypress-io/cypress/issues/1647
// Handle if body is FormData
if (requestOpts.body instanceof FormData || requestOpts.body?.constructor.name === 'FormData') {
const boundary = '----CypressFormDataBoundary'
// reset content-type
if (requestOpts.headers) {
delete requestOpts.headers[Object.keys(requestOpts).find((key) => key.toLowerCase() === 'content-type')]
} else {
requestOpts.headers = {}
}
// boundary is required for form data
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
requestOpts.headers['content-type'] = `multipart/form-data; boundary=${boundary}`
// socket.io ignores FormData.
// So, we need to encode the data into base64 string format.
const formBody = []
requestOpts.body.forEach((value, key) => {
// HTTP line break style is \r\n.
// @see https://stackoverflow.com/questions/5757290/http-header-line-break-style
if (value instanceof File || value?.constructor.name === 'File') {
formBody.push(`--${boundary}\r\n`)
formBody.push(`Content-Disposition: form-data; name="${key}"; filename="${value.name}"\r\n`)
formBody.push(`Content-Type: ${value.type || 'application/octet-stream'}\r\n`)
formBody.push('\r\n')
formBody.push(value)
formBody.push('\r\n')
} else {
formBody.push(`--${boundary}\r\n`)
formBody.push(`Content-Disposition: form-data; name="${key}"\r\n`)
formBody.push('\r\n')
formBody.push(value)
formBody.push('\r\n')
}
})
formBody.push(`--${boundary}--\r\n`)
requestOpts.bodyIsBase64Encoded = true
return Cypress.Blob.blobToBase64String(new Blob(formBody)).then((str) => {
requestOpts.body = str
})
}
})
.then(() => {
return Cypress.backend('http:request', requestOpts)