web-dev-qa-db-ja.com

サイプレスでファイル入力をテストする方法は?

ファイルInput DOM要素との相互作用を必要とするe2eフローのテストを作成するにはどうすればよいですか?

テキスト入力の場合は、DOMコンポーネントとして対話することができます(値のチェック、値の設定)。しかし、ファイル入力要素がある場合、ダイアログを開いてファイルを選択できるようになるまで、対話は制限されていると推測します。ダイアログはネイティブであり、ブラウザ要素ではないため、先に進んでアップロードするファイルを選択することはできません。

それでは、ユーザーが自分のサイトからファイルを正しくアップロードできることをどのようにテストしますか?私は Cypress を使用してe2eテストを記述しています。

9
sidoshi

サイプレスでは、ファイル入力要素のテストはまだサポートされていません。ファイル入力をテストする唯一の方法は次のとおりです。

  1. ネイティブイベントを発行します(サイプレスの ロードマップ にあります)。
  2. アプリケーションがFile APIを使用してファイルのアップロードを処理する方法を理解し、それをスタブ化します。それは可能ですが、具体的なアドバイスを与えるほど一般的ではありません。

詳細については、この未解決の問題を参照

8

このアプローチ/ハックを使用すると、実際に作成できます: 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');
})

})

13
Javier Aviles

また、前述の 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
      })
    })
  })
})
3
Scott

次の機能は私のために働く、

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}"]`)
// );
1
Vicky