Cypress Upload File

Cypress

Cypress 没有提供原生的上传文件支持,如果需要在 E2E 测试中进行文件上传的测试工作,最简单的方式就是自己写一个自定义的 Command。参考代码如下:

Cypress.Commands.add(
  'uploadFile',
  { prevSubject: true },
  (subject, fixtureFileName, mimeType = '') => {
    return cy.fixture(fixtureFileName, 'base64')
      .then(Cypress.Blob.base64StringToBlob)
      .then(blob => {
          const el = subject[0];
          const nameSegments = fixtureFileName.split('/');
          const name = nameSegments[nameSegments.length - 1];
          const testFile = new File([blob], name, { type: mimeType });
          const dataTransfer = new DataTransfer();
          dataTransfer.items.add(testFile);

          const setter =
            Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'files')
              .set;
          setter.call(el, dataTransfer.files);
        
          const event = new Event('change', { bubbles: true });
          el.dispatchEvent(event);
          return subject;
      });
  }
);

代码的解释如下:

cy.fixture(fixtureFileName, 'base64')

fixture 是 Cypress 提供的原生 API,可以读取 cypress/fixture 目录下的指定文件(文件名为 fixtureFileName)。上面这个 API 指定了文件需要以 Base64 的方式读取出来。

Cypress.Blob.base64StringToBlob 这一步顾名思义,就是将 Base64 字符串转化成对应的 Blob 类型。

剩下的代码,就是用 JavaScript 的方式模拟一个文件上传事件。其中,需要先将文件从 Blob 转换成 File(这里涉及到可能的 mime type 检查);然后,创建一个 DataTransfer 对象,把文件放进去,再赋值给 input(这里需要说明的是,React 组件会对 input 的属性做一层 proxy,因此直接使用 input.files = dataTransfer.files 这样的写法,调用的是 React 的方法而不是真正 DOM 的方法。按上面代码中的方法获取到真正的 setter,然后调用可以绕过去)。最后,在创建一个 Change 事件,传递给 input 组件,触发即可。

当然,简单起见,可以直接使用现成的库:cypress-file-upload。GitHub 地址见这里