ファイルInput DOM要素との相互作用を必要とするe2eフローのテストを作成するにはどうすればよいですか?
テキスト入力の場合は、DOMコンポーネントとして対話することができます(値のチェック、値の設定)。しかし、ファイル入力要素がある場合、ダイアログを開いてファイルを選択できるようになるまで、対話は制限されていると推測します。ダイアログはネイティブであり、ブラウザ要素ではないため、先に進んでアップロードするファイルを選択することはできません。
それでは、ユーザーが自分のサイトからファイルを正しくアップロードできることをどのようにテストしますか?私は Cypress を使用してe2eテストを記述しています。
サイプレスでは、ファイル入力要素のテストはまだサポートされていません。ファイル入力をテストする唯一の方法は次のとおりです。
このアプローチ/ハックを使用すると、実際に作成できます: https://github.com/javieraviles/cypress-upload-file-post-form
上記のスレッドからのさまざまな回答に基づいています https://github.com/cypress-io/cypress/issues/17
最初のシナリオ(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');
})
})
また、前述の 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
})
})
})
})
次の機能は私のために働く、
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}"]`)
// );