如何使用赛普拉斯测试文件输入?
Posted
技术标签:
【中文标题】如何使用赛普拉斯测试文件输入?【英文标题】:How to test file inputs with Cypress? 【发布时间】:2018-04-14 21:55:55 【问题描述】:如何编写需要与文件输入 DOM 元素交互的 e2e 流测试?
如果它是文本输入,我可以将其作为 DOM 组件与之交互(检查值、设置值)等。但是如果我有一个文件输入元素,我猜测交互是有限的,直到我可以打开对话框来选择一个文件。我无法前进并选择我要上传的文件,因为对话框将是本机的,而不是某些浏览器元素。
那么我将如何测试用户是否可以从我的站点正确上传文件?我正在使用 Cypress 编写我的 e2e 测试。
【问题讨论】:
请注意,从 9.3.0 开始,此功能通过 cy.selectFile 原生支持 【参考方案1】: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'
);
);
);
使用cypress文件上传包:https://www.npmjs.com/package/cypress-file-upload
注意:testPicture.png 必须在 cypress 的 fixture 文件夹中
【讨论】:
无法正常工作。好像代码变了。该函数现在称为cy.get( ... ).attachFile()
,而不是 cy.get( ... ).upload()
。我已经编辑了原始答案。
是的 Jules Colle,我刚刚查看了官方文档,是的,您是对的,.upload 已更改为 .attachFile。谢谢
我也遇到了麻烦。为了修复它,我必须将 then 语句链接在一起,例如 cy.fixture(...).then( fc => return Cypress.Blob.base64StringToBlob( fc ) ).then(fileContentasBlob => cy.get('input[type="file"]').attachFile( ......
我在使用这种方法时遇到了麻烦。上传成功,但服务器无法处理上传的图片:(PIL.UnidentifiedImageError: cannot identify image file
)。我能够使用Lucas Andrade's approach 和cy.get('['input[type="file"]']').attachFile(fixtureFile)
避免这个错误(没有关于mimeType 等的信息)。
你不需要fixture,但你必须提供有效的fileContent,当你知道时更好,例如csv内容 - 甚至不需要创建源文件,并且文件路径可能是伪造的:`cy.get(input_name).attachFile( fileContent: csv_content, fileName: csv_path, mimeType: 'text/csv' );`【参考方案2】:
对我来说,更简单的方法是使用cypress file upload package
安装它:
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/
中
如需更多示例,请查看Usage section on README of the package.
【讨论】:
直解! ? 哇!我尝试了软件包自述文件中的所有(更复杂的)示例,但是这个——最简单的——是唯一一个有效的!谢谢! 这对我很有用!但是,对于所有场景,当我尝试在 files[] 数组上使用客户端 jQuery 验证时。它无法验证文件类型。我有验证检查您不能上传除图像文件以外的任何内容。但是当我在“attachfile”中指定一个 mimetupe 时,发送到服务器的文件上传文件为空? 此解决方案有效(必须从答案中链接的 npm 包作者获取更多详细信息),但为了让我的智能感知识别“attachFile”,我必须添加` ///通过这种方法/hack,您实际上可以做到: https://github.com/javieraviles/cypress-upload-file-post-form
它基于上述线程https://github.com/cypress-io/cypress/issues/170的不同答案
第一个场景(upload_file_to_form_spec.js):
我想测试一个必须先选择/上传文件的 UI 提交表格。 在柏树内的“commands.js”文件中包含以下代码 support 文件夹,因此可以在任何测试中使用命令 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');
)
)
【讨论】:
我已经尝试了几十种方法,这是第一个有效的方法。干得好! 很高兴它对你有用 :) 我爱这个社区 自定义的 hexStringToByte 可以替换为原生方法 Blob...cy.readFile(filePath) .then(Cypress.Blob.binaryStringToBlob) .then(blob => const file = new File([blob], fileName);...
,这只是一种更简单的方式
没试过,但听起来很合理【参考方案4】:
赛普拉斯尚不支持测试文件输入元素。测试文件输入的唯一方法是:
-
发布原生事件(赛普拉斯在其Roadmap 上提供)。
了解您的应用程序如何使用 File API 处理文件上传,然后将其存根。可能但不够笼统,无法提供任何具体建议。
See this open issue for more detail.
【讨论】:
我将暂时存根文件上传,并将跟踪该问题。谢谢。 如果有帮助,问题中描述的解决方法已浓缩在github.com/abramenal/cypress-file-upload 中 这已部署在新版 Cypress 中:***.com/a/70771129/6774916【参考方案5】:在我的情况下,我进行了客户端和服务器端文件验证,以检查文件是 JPEG 还是 PDF。所以我必须创建一个上传命令,它会从 Fixtures 中读取二进制文件,并准备一个带有文件扩展名的 blob。
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 文件夹中
【讨论】:
这是我能找到的唯一一个适用于我的特定 XML 文件格式的示例(否则编码很奇怪)。唯一的变化是Cypress.Blob.binaryStringToBlob(...)
不再返回 Promiseconst blob = Cypress.Blob.binaryStringToBlob(content, fileType);
更多 https://docs.cypress.io/api/utilities/blob以下功能对我有用,
cy.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"]`)
// );
【讨论】:
我正在寻找这个通过 change 事件“刷新”的功能,非常感谢!【参考方案7】:由于9.3.0,您可以使用selectFile
。
cy.get('input[type=file]').selectFile('cypress/fixtures/file.json')
见:
https://github.com/cypress-io/cypress/issues/170 https://cypress.io/blog/2022/01/18/uploading-files-with-selectfile/ https://docs.cypress.io/guides/references/migration-guide#Migrating-from-cypress-file-upload-to-selectFile【讨论】:
【参考方案8】:同样基于前面提到的github issue,非常感谢那里的人们。
赞成的答案最初对我有用,但我在尝试处理 JSON 文件时遇到了字符串解码问题。处理十六进制也感觉像是额外的工作。
以下代码处理 JSON 文件的方式略有不同,以防止编码/解码问题,并使用赛普拉斯内置的 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
)
)
)
)
【讨论】:
【参考方案9】:在测试文件夹中的 commands.ts 文件中添加:
//this is for typescript intellisense to recognize new command
declare namespace Cypress
interface Chainable<Subject>
attach_file(value: string, fileType: string): Chainable<Subject>;
//new command
Cypress.Commands.add(
'attach_file',
prevSubject: 'element',
,
(input, fileName, fileType) =>
cy.fixture(fileName)
.then((content) => Cypress.Blob.base64StringToBlob(content, fileType))
.then((blob) =>
const testFile = new File([blob], fileName);
const dataTransfer = new DataTransfer();
dataTransfer.items.add(testFile);
input[0].files = dataTransfer.files;
return input;
);
,
);
用法:
cy.get('[data-cy=upload_button_input]')
.attach_file('./food.jpg', 'image/jpg')
.trigger('change', force: true );
另一种选择是使用cypress-file-upload,它在 4.0.7 版本中存在错误(上传文件两次)
【讨论】:
【参考方案10】:cy.fixture("image.jpg").then((fileContent) =>
cy.get("#fsp-fileUpload").attachFile(
fileContent,
fileName: "image",
encoding: "base64",
mimeType: "image/jpg",
);
);
【讨论】:
【参考方案11】:这里是多文件上传版本:
Cypress.Commands.add('uploadMultiFiles',(args) =>
const dataJson, dirName, inputTag, mineType = args
const arr = []
dataJson.files.forEach((file, i) =>
cy.fixture(`$ dirName + file `).as(`file$i`)
)
cy.get(`$inputTag`).then(function (el)
for(const prop in this)
if (prop.includes("file"))
arr.push(this[prop])
const list = new DataTransfer()
dataJson.files.forEach((item, i) =>
// convert the logo base64 string to a blob
const blob = Cypress.Blob.base64StringToBlob(arr[i], mineType)
const file = new FileCopy([blob], `$item`, type: mineType , `$ dirName + item `)
const pathName = dirName.slice(1)
file.webkitRelativePath = `$ pathName + item`
console.log(file)
list.items.add(file)
)
const myFileList = list.files
el[0].files = myFileList
el[0].dispatchEvent(new Event('change', bubbles: true ))
)
)
用法:
首先,在fixtures文件夹中准备一个data.json文件,例如:
data.json
"files":[
"1_TEST-JOHN-01.jpeg",
"2_TEST-JOHN-01.jpeg",
"3_TEST-JOHN-01.jpeg",
"4_TEST-JOHN-01.jpeg",
"5_TEST-JOHN-01.jpeg",
"6_TEST-JOHN-01.jpeg",
"7_TEST-JOHN-01.jpeg",
"8_TEST-JOHN-01.jpeg",
"9_TEST-JOHN-01.jpeg",
"10_TEST-JOHN-01.jpeg"
]
其次,将 json 数据导入你的 spec.js
import data from '../fixtures/data.json'
第三,编写一个类来扩展 File web API 对象,其中包含设置和获取 webkitRelativePath 值的功能
class FileCopy extends File
constructor(bits, filename, options)
super(bits, filename, options)
let webkitRelativePath
Object.defineProperties(this,
webkitRelativePath :
enumerable : true,
set : function(value)
webkitRelativePath = value;
,
get : function()
return webkitRelativePath;
,
);
最后调用spec.js中的cmd
cy.uploadMultiFiles(
dataJson:data, // the data.json you imported.
dirName:"/your/dirname/",
inputTag:"input#upload",
mineType:"image/jpeg"
)
【讨论】:
【参考方案12】:您可以使用新的 Cypress 命令来做到这一点:
cy.get('input[type=file]').selectFile('file.json')
这现在可以在赛普拉斯库本身中使用,版本为 9.3
及更高版本。按照迁移指南了解如何从 cypress-file-upload
插件迁移到 Cypress .selectFile()
命令:
Migrating-from-cypress-file-upload-to-selectFile
【讨论】:
以上是关于如何使用赛普拉斯测试文件输入?的主要内容,如果未能解决你的问题,请参考以下文章