Comment puis-je écrire un test de flux e2e qui nécessite une interaction avec l'élément DOM d'entrée de fichier?
Si c'est une entrée de texte, je peux interagir avec elle (vérifier la valeur, définir la valeur), etc. en tant que composant DOM. Mais si j'ai un élément d'entrée de fichier, je suppose que l'interaction est limitée jusqu'à ce que je puisse ouvrir la boîte de dialogue pour sélectionner un fichier. Je ne peux pas avancer et sélectionner le fichier que je veux télécharger car la boîte de dialogue serait native et non un élément du navigateur.
Alors, comment puis-je tester qu'un utilisateur peut correctement télécharger un fichier depuis mon site? J'utilise Cypress pour écrire mes tests e2e.
Le test des éléments d'entrée de fichier n'est pas encore pris en charge dans Cypress. La seule façon de tester les entrées de fichier est de:
Avec cette approche/hack, vous pouvez réellement le faire: https://github.com/javieraviles/cypress-upload-file-post-form
Il est basé sur différentes réponses du fil précité https://github.com/cypress-io/cypress/issues/17
Premier scénario (upload_file_to_form_spec.js):
Je veux tester une interface utilisateur dans laquelle un fichier doit être sélectionné/téléchargé avant de soumettre le formulaire. Incluez le code suivant dans votre fichier "commands.js" dans le dossier de support de Cypress, afin que la commande cy.upload_file () puisse être utilisée à partir de n'importe quel test:
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);
}
Ensuite, au cas où vous souhaiteriez télécharger un fichier Excel, remplir d'autres entrées et soumettre le formulaire, le test serait quelque chose comme ceci:
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');
})
})
Également basé sur précédemment mentionné problème github , donc merci beaucoup aux gens là-bas.
La réponse votée a fonctionné initialement pour moi, mais j'ai rencontré des problèmes de décodage de chaînes en essayant de gérer les fichiers JSON. Cela ressemblait également à du travail supplémentaire pour faire face à l'hex.
Le code ci-dessous gère les fichiers JSON légèrement différemment pour éviter les problèmes d'encodage/décodage et utilise le Cypress.Blob.base64StringToBlob
Intégré à Cypress:
/**
* 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
})
})
})
})
La fonction suivante fonctionne pour moi,
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}"]`)
// );