【问题标题】:Upload File with Cypress.io via Request通过请求使用 Cypress.io 上传文件
【发布时间】:2017-11-28 14:28:30
【问题描述】:

鉴于这种简单的形式

<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>

由于 cypress.io 不支持原生事件(无法选择文件),我必须使用后请求。

cypress.io 中的请求结构是这样的

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

我想知道如何发送一个简单的 *.txt

任何建议表示赞赏!

【问题讨论】:

    标签: javascript post cypress


    【解决方案1】:

    将其留在这里以防它对某人有所帮助 https://github.com/javieraviles/cypress-upload-file-post-form

    第二种情况(send_form_data_with_file_in_post_request_spec.js):

    我想自己构建 FormData( new FormData(), formData.append/formData.set )并通过 POST 请求直接将其发送到后端,或者使用我创建的 FormData 提交表单。 对于这种情况,传输的数据必须与表单的 submit() 格式相同,类型设置为“multipart/form-data”。查看 MDN Web 文档以了解如何构建 FormData:使用 FormData 对象,并知道此时(赛普拉斯 2.1.0)cy.request 不支持 FormData(multipart/form-data)所以我们需要一个 XMLHttpRequest,测试可以如下进行。 在 cypress 支持文件夹中的“commands.js”文件中包含以下代码,因此可以在任何测试中使用命令 cy.form_request():
    // 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);
    })
    

    然后,如果您想发送与以前相同的表单(包含 excel 文件和其他普通输入的表单)但自己构建并直接发送到服务器,测试将是这样的:

    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()" 
        command doesn't support sending a web Form as a body in a POST request, so the test uses a support 
        command 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);
                });
    
            })
    
        })
    
    })
    

    })

    【讨论】:

    • 它似乎确实创建了一个正确的请求,但它被赛普拉斯拦截了:(
    • 呃……“被赛普拉斯拦截”是什么意思?
    • 嗨,你能建议我如何使用 then() 来 form_request 吗?我的意思是我该怎么做 cy.form_request(param1, param2).then(response => { ... }) ?因为否则我无法为 form_request 创建子命令。
    • 看看 GitHub 存储库,在我创建函数 form_request 的 commands.js 中,有一个名为 done(method, url, formData, done) 的参数,我认为这就是你要找的 :)
    • 在“完成”函数中,我得到响应 i 并发出另一个 cy.request 它只是没有执行。也许问题出在其他地方...cy.formRequest('POST', url, input, function (uploadResponse) {cy.request({method: 'GET',url: _URL,headers: { ...HTTP_GET_HEADERS }}).then(resp =&gt; {})})
    【解决方案2】:

    我只需要从 Cypress 上下文中将文件上传到某处(在浏览器请求之前模拟某些内容)。

    在我的情况下,以下工作正常:

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

    【讨论】:

      【解决方案3】:

      bahmutov's cypress-form-data-with-file-upload 提到了 github 问题(311170),其中正在考虑这个问题,并为那些无法适应赛普拉斯在这方面的限制的人提供了一个相当肮脏的解决方法。

      解决方法是创建一个自定义XHR 对象并使用它来提交带有附加文件的请求,而不是赛普拉斯的cy.request。提供的解决方法仅限于使用 &lt;input type="file" /&gt; 元素的值。在我的情况下,我需要在我的请求中使用一个夹具,所以我的解决方案是创建一个custom cypress command

      Cypress.Commands.add("xhr", (method, url, formdata) => { // 灵感来自 https://github.com/bahmutov/cypress-form-data-with-file-upload/blob/8fe6106d28eef0634c78564c746746d1cc354e99/index.js // 修复 cy.request 中缺少 FormData (multipart/form-data) 支持 cy.window() .then(赢=> { 返回新的承诺((解决,拒绝)=> { const XHR = new win.XMLHttpRequest() XHR.onload = 解决 XHR.open(方法,网址) XHR.send(formdata) }) }) })

      并根据需要将BlobsFiles 附加到FormData 的实例。

      由于与此解决方法相关的复杂性,我强烈建议使用 Cypress,例如,如果可以的话,将 base64 编码的文件数据作为纯文本接受。

      【讨论】:

        【解决方案4】:

        我今天遇到了这个问题并开发了与其他答案类似的解决方案,但它允许您使用 .then() Cypress 语法。

        将此添加到 support/commands.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");
          });
        

        那么您就可以在测试中执行以下操作:

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

        【讨论】:

          【解决方案5】:

          我周末大部分时间都花在这上面,所以我会发布适合我的解决方案。

          我无法让 NPM 包 cypress-upload-file-post-form 中的示例按其编写方式工作。具体来说,我在Cypress.Blob.binaryStringToBlob 上遇到了一个未找到函数的错误。

          步骤 1

          support/commands.js添加这个函数

          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);
          });
          

          第二步

          将名为Base64TestCV.rtf 的文件添加到fixtures 文件夹。

          我想要一个.rtf 文件,但显然你可以创建一个.txt 文件——无论你做什么,都必须使用在线转换器或Linux 中的base64 命令以base64 编码。

          出于测试目的,请确保它包含文本 MyCypressTestCV(显然是用 base64 编码的)。

          第三步

          创建并运行您的测试。在这个例子中,我的 .rtf 文件包含文本 MyCypressTestCV,它被 httpbin.org 反射回来,因此证明它是从 base64 正确解码并上传的。

          describe('Upload a file to a multipart form using cy.request', function () {
              it('Performs a multipart post to httpbin.org', function () {
                  const baseUrl = 'http://httpbin.org';
                  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.org/post reflects what you post
                      });
                  });
              });
          });
          

          【讨论】:

            【解决方案6】:

            在等待内置支持的同时,我们可以停止复制粘贴 hack 并从中创建一个可重复使用的包。 随意打开问题,以便我们使其适用于最流行的用例。

            请注意,这更多的是上传文件,而不是专门关注表单。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2022-08-09
              • 2018-12-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-12-10
              • 1970-01-01
              • 2022-08-06
              相关资源
              最近更新 更多