I upgraded from Cypress 6.8.0 to 7.0.1. After the upgrade, when this function is called by one of the Cypress tests
async saveTask (task, file) {
const requestBody = new FormData()
requestBody.append('file', file)
return await http.post('/api/endpoint', requestBody, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
},
I get the following error
TypeError [ERR_INVALID_ARG_TYPE] [ERR_INVALID_ARG_TYPE]:
The first argument must be of type string or an instance of Buffer or Uint8Array. Received type number (45)
at write_ (_http_outgoing.js:696:11)
at ClientRequest.write (_http_outgoing.js:661:15)
at Request.write (/Users/donal/Library/Caches/Cypress/7.0.1/Cypress.app/Contents/Resources/app/packages/server/node_modules/@cypress/request/request.js:1496:27)
at /Users/donal/Library/Caches/Cypress/7.0.1/Cypress.app/Contents/Resources/app/packages/server/node_modules/@cypress/request/request.js:546:20
at Array.forEach (<anonymous>:null:null)
at end (/Users/donal/Library/Caches/Cypress/7.0.1/Cypress.app/Contents/Resources/app/packages/server/node_modules/@cypress/request/request.js:545:23)
at Immediate._onImmediate (/Users/donal/Library/Caches/Cypress/7.0.1/Cypress.app/Contents/Resources/app/packages/server/node_modules/@cypress/request/request.js:578:7)
at processImmediate (internal/timers.js:461:21)
{
code: 'ERR_INVALID_ARG_TYPE'
}
The http
object that I use to make the POST request is an Axios instance and the file
object that I append to the request body is a File. The file object is the cause of the problem, because if I don't append it to the request body, the error doesn't occur.
The error only occurs when the function is run by a Cypress test. Cypress uses Node.js, and judging by the error message above it seems the File
type is not allowed. Furthermore, the Axios request config docs indicate that when Axios runs under Node, a File
is not allowed.
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},
So I guess I need to convert the File
objects to something else, so that this function will work both within the app itself, and when run by Cypress.
On the server-side (a Spring Boot app), this file is bound to a MultipartFile
public void handlePost(
RequestPart(value = "file") MultipartFile file) {
// controller action body
}
If my theory that the File
type is the problem, what should I use instead and how should I do the conversion?
Handling Error in Cypress Unlike other Javascript-Based Frameworks, Cypress doesn't allow you to use the try and catch block to handle the exception. Cypress provides a special mechanism for handling exceptions in your code. Handling different types of Exceptions such as: Exception from Webpage Under Test.
The error only occurs when the function is run by a Cypress test. Cypress uses Node.js, and judging by the error message above it seems the File type is not allowed. Furthermore, the Axios request config docs indicate that when Axios runs under Node, a File is not allowed.
By default Cypress
starts Electron
in headless mode, i.e. the tests would always have access to browser APIs. You can control on which browser you want to run the tests. More details here.
Now let's look at the solution for file upload
Assuming the file
you are referring in the function saveTask
is an input
field of type=file
and name=file
.
And let's say, you need to upload a png image named email.png
which is at cypress/fixtures/images/email.png
.
Use the following block of code for uploading the file.
cy.fixture('images/email.png').as('emailImage')
cy.get('input[name=file]').then(function (el) {
const blob = Cypress.Blob.base64StringToBlob(this.emailImage, 'image/png')
const file = new File([blob], 'email.png', { type: 'image/png' })
const list = new DataTransfer()
list.items.add(file)
el[0].files = list.files
el[0].dispatchEvent(new Event('change', { bubbles: true }))
})
Now you can trigger the action based on which your upload api (/api/endpoint) is invoked and finally verify the upload is successful.
// Trigger the action which causes the upload api to be invoked e.g. clicking on a button
cy.get('input[type=button]').click();
// Verify the api invocation is successful (The below code checks a div with id result to have the String 'Success')
cy.get('#result').should('contain', 'Success');
You can upload any type of file and it doesn't have to be an image. For more information look at fixture and Blob. This answer is based on this example on Blob documentation.
This repo runs a test on CI using GitHub Actions https://github.com/GSSwain/file-upload-cypress-test and the server is a Spring boot app (Run as a Docker container on CI server) code is at https://github.com/GSSwain/file-upload-ajax-sample
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With