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 地址见这里。