如何编写与文件输入DOM元素交互的e2e测试流程?
如果是文本输入,我可以与之交互(检查值、设置值)等,因为它是一个DOM组件。但是,如果我有一个文件输入元素,我猜想交互仅限于打开对话框以选择文件。我无法继续并选择要上传的文件,因为对话框是本地的而不是浏览器元素。
那么,如何测试用户能否正确地从我的网站上上传文件? 我正在使用Cypress编写我的e2e测试。
如何编写与文件输入DOM元素交互的e2e测试流程?
如果是文本输入,我可以与之交互(检查值、设置值)等,因为它是一个DOM组件。但是,如果我有一个文件输入元素,我猜想交互仅限于打开对话框以选择文件。我无法继续并选择要上传的文件,因为对话框是本地的而不是浏览器元素。
那么,如何测试用户能否正确地从我的网站上上传文件? 我正在使用Cypress编写我的e2e测试。
注意:Cypress 的最新版本支持 selectFile - 请参见其他答案
it('Testing picture uploading', () => {
cy.fixture('testPicture.png').then(fileContent => {
cy.get('input[type="file"]').attachFile({
fileContent: fileContent.toString(),
fileName: 'testPicture.png',
mimeType: 'image/png'
});
});
});
cy.get( ... ).attachFile()
,而不是 cy.get( ... ).upload()
。我已编辑原始答案。 - Jules Collecy.fixture(...).then( fc => {return Cypress.Blob.base64StringToBlob( fc ) }).then(fileContentasBlob => { cy.get('input[type="file"]').attachFile({ ......
- Chadd FrasierPIL.UnidentifiedImageError: cannot identify image file
)。我能够通过Lucas Andrade的方法以仅使用cy.get('['input[type="file"]']').attachFile(fixtureFile)
(没有有关mimeType等信息)来避免此错误。 - Jasmin对我来说,更简单的方法是使用这个cypress文件上传包
安装它:
npm install --save-dev cypress-file-upload
然后将以下行添加到您项目的cypress/support/commands.js
文件中:
import 'cypress-file-upload';
现在你可以做:
const fixtureFile = 'photo.png';
cy.get('[data-cy="file-input"]').attachFile(fixtureFile);
photo.png
必须在 cypress/fixtures/
目录中。
有关更多示例,请查看包的README中的使用部分。
/// <reference types="cypress-file-upload" />
。 - tengen使用这种方法/技巧,您实际上可以做到:
https://github.com/javieraviles/cypress-upload-file-post-form它基于来自上述线程https://github.com/cypress-io/cypress/issues/170的不同答案。
第一种情况(upload_file_to_form_spec.js):
我想测试一个UI,在提交表单之前必须选择/上传文件。在Cypress支持文件夹中的“commands.js”文件中包含以下代码,以便可以从任何测试中使用cy.upload_file()命令:
Cypress.Commands.add('upload_file', (fileName, fileType, selector) => {
cy.get(selector).then(subject => {
cy.fixture(fileName, 'hex').then((fileHex) => {
const fileBytes = hexStringToByte(fileHex);
const testFile = new File([fileBytes], fileName, {
type: fileType
});
const dataTransfer = new DataTransfer()
const el = subject[0]
dataTransfer.items.add(testFile)
el.files = dataTransfer.files
})
})
})
// UTILS
function hexStringToByte(str) {
if (!str) {
return new Uint8Array();
}
var a = [];
for (var i = 0, len = str.length; i < len; i += 2) {
a.push(parseInt(str.substr(i, 2), 16));
}
return new Uint8Array(a);
}
如果您想上传Excel文件并填写其他输入项并提交表单,则测试内容如下:
describe('Testing the excel form', function () {
it ('Uploading the right file imports data from the excel successfully', function() {
const testUrl = 'http://localhost:3000/excel_form';
const fileName = 'your_file_name.xlsx';
const fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
const fileInput = 'input[type=file]';
cy.visit(testUrl);
cy.upload_file(fileName, fileType, fileInput);
cy.get('#other_form_input2').type('input_content2');
.
.
.
cy.get('button').contains('Submit').click();
cy.get('.result-dialog').should('contain', 'X elements from the excel where successfully imported');
})
})
.then(Cypress.Blob.binaryStringToBlob)
.then(blob => {
const file = new File([blob], fileName);...
这只是一种更简单的方式。 - Alexey Nikonov目前,Cypress尚不支持测试文件上传元素。测试文件上传的唯一方法是:
Cypress.Commands.add('uploadFile', { prevSubject: true }, (subject, fileName, fileType = '') => {
cy.fixture(fileName,'binary').then(content => {
return Cypress.Blob.binaryStringToBlob(content, fileType).then(blob => {
const el = subject[0];
const testFile = new File([blob], fileName, {type: fileType});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(testFile);
el.files = dataTransfer.files;
cy.wrap(subject).trigger('change', { force: true });
});
});
});
cy.get('input[type=file]').uploadFile('smiling_pic.jpg', 'image/jpeg');
smiling_pic.jpg将位于fixtures文件夹中。
Cypress.Blob.binaryStringToBlob(...)
不再返回 Promise<Blob>,而是直接返回 Blob,这意味着现在的用法是 const blob = Cypress.Blob.binaryStringToBlob(content, fileType);
更多内容请参见https://docs.cypress.io/api/utilities/blob。 - Lesley.Oakeycy.getTestElement('testUploadFront').should('exist');
const fixturePath = 'test.png';
const mimeType = 'application/png';
const filename = 'test.png';
cy.getTestElement('testUploadFrontID')
.get('input[type=file')
.eq(0)
.then(subject => {
cy.fixture(fixturePath, 'base64').then(front => {
Cypress.Blob.base64StringToBlob(front, mimeType).then(function(blob) {
var testfile = new File([blob], filename, { type: mimeType });
var dataTransfer = new DataTransfer();
var fileInput = subject[0];
dataTransfer.items.add(testfile);
fileInput.files = dataTransfer.files;
cy.wrap(subject).trigger('change', { force: true });
});
});
});
// Cypress.Commands.add(`getTestElement`, selector =>
// cy.get(`[data-testid="${selector}"]`)
// );
根据之前提到的Github问题,在此向那里的人们表示感谢。
对我来说,得票最高的答案一开始起作用了,但是当我尝试处理JSON文件时遇到了字符串解码问题。同时,使用十六进制也感觉像是额外的工作。
下面的代码稍微不同地处理JSON文件以防止编码/解码问题,并使用Cypress内置的Cypress.Blob.base64StringToBlob
:
/**
* Converts Cypress fixtures, including JSON, to a Blob. All file types are
* converted to base64 then converted to a Blob using Cypress
* expect application/json. Json files are just stringified then converted to
* a blob (prevents issues with invalid string decoding).
* @param {String} fileUrl - The file url to upload
* @param {String} type - content type of the uploaded file
* @return {Promise} Resolves with blob containing fixture contents
*/
function getFixtureBlob(fileUrl, type) {
return type === 'application/json'
? cy
.fixture(fileUrl)
.then(JSON.stringify)
.then(jsonStr => new Blob([jsonStr], { type: 'application/json' }))
: cy.fixture(fileUrl, 'base64').then(Cypress.Blob.base64StringToBlob)
}
/**
* Uploads a file to an input
* @memberOf Cypress.Chainable#
* @name uploadFile
* @function
* @param {String} selector - element to target
* @param {String} fileUrl - The file url to upload
* @param {String} type - content type of the uploaded file
*/
Cypress.Commands.add('uploadFile', (selector, fileUrl, type = '') => {
return cy.get(selector).then(subject => {
return getFixtureBlob(fileUrl, type).then(blob => {
return cy.window().then(win => {
const el = subject[0]
const nameSegments = fileUrl.split('/')
const name = nameSegments[nameSegments.length - 1]
const testFile = new win.File([blob], name, { type })
const dataTransfer = new win.DataTransfer()
dataTransfer.items.add(testFile)
el.files = dataTransfer.files
return subject
})
})
})
})
您可以使用新的Cypress命令来完成此操作:
cy.get('input[type=file]').selectFile('file.json')
9.3
及以上中。请按照迁移指南,从cypress-file-upload
插件迁移到Cypress的.selectFile()
命令:
从cypress-file-upload迁移到selectFile
如果您的文件输入 display: none;
,请使用以下代码:
cy.get('[data-type=inputFile]').selectFile('cypress/fixtures/avatar.jpg', { force: true })
否则
cy.get('[data-type=inputFile]').selectFile('cypress/fixtures/avatar.jpg')