最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Upload File with Cypress.io via Request - Stack Overflow

programmeradmin5浏览0评论

Given this simple form

<form action="/upload_file" method="POST" enctype="multipart/form-data" id="upload_file_form">
    <input type="file" name="file" required>
    <input type="text" placeholder="Select file" disabled>
    <input type="submit" value="upload">
</form>

Since native events are not supported by cypress.io (can't select a file), I have to use a post-request.

The request structure in cypress.io looks like

cy.request({
    method: 'POST',
    url: '/some/url',
    form: true,
    body: { ... },
    headers:{ ... }
})

I would like to know how I could send a simple *.txt

Any suggestions appreciated!

Given this simple form

<form action="/upload_file" method="POST" enctype="multipart/form-data" id="upload_file_form">
    <input type="file" name="file" required>
    <input type="text" placeholder="Select file" disabled>
    <input type="submit" value="upload">
</form>

Since native events are not supported by cypress.io (can't select a file), I have to use a post-request.

The request structure in cypress.io looks like

cy.request({
    method: 'POST',
    url: '/some/url',
    form: true,
    body: { ... },
    headers:{ ... }
})

I would like to know how I could send a simple *.txt

Any suggestions appreciated!

Share Improve this question edited Nov 28, 2017 at 14:46 Felix PK asked Nov 28, 2017 at 14:28 Felix PKFelix PK 1541 gold badge3 silver badges11 bronze badges
Add a ment  | 

6 Answers 6

Reset to default 8

Will leave this here in case it helps somebody https://github./javieraviles/cypress-upload-file-post-form

Second scenario (send_form_data_with_file_in_post_request_spec.js):

I want to build up the FormData myself( new FormData(), formData.append/formData.set ) and send it directly with a POST request to the backend or submit the form with the FormData I have created. For this case, the transmitted data must be in the same format as the form's submit(), which type is set to "multipart/form-data". Having a look at the MDN web docs to see how you can build a FormData: Using FormData Objects, and knowing that at this very moment (Cypress 2.1.0) cy.request doesn't support FormData (multipart/form-data) so we will need a XMLHttpRequest, the test can be performed as follows. Include the following code in your "mands.js" file within the cypress support folder, so the mand cy.form_request() can be used from any test:
// Performs an XMLHttpRequest instead of a cy.request (able to send data as 
// FormData - multipart/form-data)
Cypress.Commands.add('form_request', (method, url, formData, done) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
        done(xhr);
    };
    xhr.onerror = function () {
        done(xhr);
    };
    xhr.send(formData);
})

Then, in case you want to send the same form as before (form containing an excel file and other plain inputs) but build it by yourself and send it directly to the server, the test would be something like this:

describe('Testing the API', function () {

    it('Receives valid FormData and proccesses the information correctly', 
        function () {

    /*
    The reason why this test may look a bit tricky is because the backend endpoint is expecting the 
    submission of a web Form (multipart/form-data), not just data within a POST. The "cy.request()" 
    mand doesn't support sending a web Form as a body in a POST request, so the test uses a support 
    mand that has been created to perform a genuine XMLHttpRequest where a web Form can be placed.
    */

    //Declarations
    const fileName = 'your_excel_file.xlsx';
    const method = 'POST';
    const url = 'http://localhost:3000/api/excel_form';
    const fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
    const inputContent2 = 'input_content2';
    const expectedAnswer = '{"msg":"X elements from the excel where successfully imported"}';

    // Get file from fixtures as binary
    cy.fixture(fileName, 'binary').then( (excelBin) => {

        // File in binary format gets converted to blob so it can be sent as Form data
        Cypress.Blob.binaryStringToBlob(excelBin, fileType).then((blob) => {

            // Build up the form
            const formData = new FormData();
            formData.set('file', blob, fileName); //adding a file to the form
            formData.set('input2', inputContent2); //adding a plain input to the form
            .
            .
            .
            // Perform the request
            cy.form_request(method, url, formData, function (response) {
                expect(response.status).to.eq(200);
                expect(expectedAnswer).to.eq(response.response);
            });

        })

    })

})

})

I just needed from Cypress context to upload a file somewhere (to mock something out before the browser does request it).

The following is working just fine in my case:

cy.fixture('something.svg').then((file) => {
  cy.request({
    url: 'http://mock/uploads/some-id',
    method: 'put',
    body: file,
  });
});

bahmutov's cypress-form-data-with-file-upload makes reference to the github issues (311 and 170) where this problem is being considered and provides a rather dirty workaround for those that cannot acmodate Cypress' limitation on this front.

The workaround is to create a custom XHR object and use that to submit the request with the file attached, instead of Cypress' cy.request. The provided workaround is limited to functioning with an <input type="file" /> element's value. In my case, I needed to use a fixture in my request, so my solution was to create a custom cypress mand

Cypress.Commands.add("xhr", (method, url, formdata) => {
    // inspired by https://github./bahmutov/cypress-form-data-with-file-upload/blob/8fe6106d28eef0634c78564c746746d1cc354e99/index.js
    // fixes lack of FormData (multipart/form-data) support in cy.request
    cy.window()
    .then(win => {
        return new Promise((resolve, reject) => {
            const XHR = new win.XMLHttpRequest()
            XHR.onload = resolve
            XHR.open(method, url)
            XHR.send(formdata)
        })
    })
})

And attach Blobs or Files as needed to an instance of FormData.

I strongly remend acmodating Cypress, for example, by accepting base64-encoded file data as plain text, if you can, due to the plexity associated with this workaround.

I ran into this issue today and developed a similar solution to the other answers, but it allows you to use the .then() Cypress syntax.

Add this to support/mands.js:

Cypress.Commands.add("form_request", (url, formData) => {
  return cy
    .server()
    .route("POST", url)
    .as("formRequest")
    .window()
    .then(win => {
      var xhr = new win.XMLHttpRequest();
      xhr.open(method, url);
      xhr.send(formData);
    })
    .wait("@formRequest");
  });

Then you'd be able to do the following in your tests:

cy
  .form_request(url, formData)
  .then(response => {
    // do stuff with your response
  });

I spent most of the weekend on this so will post the solution that works for me.

I couldn't get the example in the NPM package cypress-upload-file-post-form to work as it is written. Specifically I got a function not found error on Cypress.Blob.binaryStringToBlob.

Step 1

In support/mands.js add this function

Cypress.Commands.add('multipartFormRequest', (method, url, formData, done) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onload = function () {
        done(xhr);
    };
    xhr.onerror = function () {
        done(xhr);
    };
    xhr.send(formData);
});

Step 2

Add a file called Base64TestCV.rtf to the fixtures folder.

I wanted a .rtf file but obviously you can make a .txt file - whatever you do it must be encoded in base64 by using an online converter or the base64 mand in Linux.

For testing purposes, make sure it contains the text MyCypressTestCV (encoded in base64 obviously).

Step 3

Create and run your test. In this example my .rtf file contains the text MyCypressTestCV which is reflected back by httpbin so it proves it was decoded from base64 correctly and uploaded.

describe('Upload a file to a multipart form using cy.request', function () {
    it('Performs a multipart post to httpbin', function () {
        const baseUrl = 'http://httpbin';
        const postUrl = `${baseUrl}/post`;

        cy.visit(baseUrl); // Cypress will be very buggy if you don't do at least one cy.visit

        cy.request(baseUrl).as('multipartForm'); // pretend we are doing the GET request for the multipart form

        const base64FileName = 'Base64TestCV.rtf';
        const postedFileName = 'TestCV.rtf';
        const method = 'POST';
        const mimeType = 'application/rtf';

        cy.fixture(base64FileName).as('base64File'); // file content in base64

        cy.get('@multipartForm').then((response) => {
            const formData = new FormData();
            formData.append('firstFormField', 'Hello');
            formData.append('secondFormField', '25');
            const blob = Cypress.Blob.base64StringToBlob(this.base64File, mimeType);
            formData.append('uploadFile', blob, postedFileName);

            cy.multipartFormRequest(method, postUrl, formData, function (response) {
                expect(response.status).to.eq(200);
                expect(response.response).to.match(/MyCypressTestCV/); // http://httpbin/post reflects what you post
            });
        });
    });
});

While waiting for the built-in support, we can stop copy-pasting the hack and create a reusable package out of it. Feel free to open issues so we can make it suitable for the most popular use cases.

Note that's more of just uploading a file and not focused on a form specifically.

https://www.npmjs./package/cypress-file-upload

发布评论

评论列表(0)

  1. 暂无评论