cypress-io/cypress

File upload on input, stop working in Chrome 73

Closed this issue Β· 19 comments

Code:

// fixture test_file.png exist
// test.spec.js
cy.uploadFile('input[type="file"]', "test_file.png", 'image/png');

// command.js
Cypress.Commands.add("uploadFile", (selector, fileUrl, type = "") => {
  return cy.get(selector).then(subject => {
    return cy
      .fixture(fileUrl, "base64")
      .then(Cypress.Blob.base64StringToBlob)
      .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 DataTransfer();
          dataTransfer.items.add(testFile);
          el.files = dataTransfer.files;
          return subject;
        });
      });
  });
});

in 3.1.5, this code work, after update to 3.2.0 stop working.

          dataTransfer.items.add(testFile);
          el.files = dataTransfer.files;
// this code not trigger change
// but set files

also cypress-file-upload not working in 3.2.0 (working only drag mode)

solution from #170 (comment) not working

If add cy.wrap(subject).trigger('change'); after el.files = dataTransfer.files;, event change fire on input

Cypress.Commands.add("uploadFile", (selector, fileUrl, type = "") => {
  return cy.get(selector).then(subject => {
    return cy
      .fixture(fileUrl, "base64")
      .then(Cypress.Blob.base64StringToBlob)
      .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 DataTransfer();
          dataTransfer.items.add(testFile);
          el.files = dataTransfer.files;
          return cy.wrap(subject).trigger('change');
        });
      });
  });
});

Gosh, yeah, I can't think of what could have affected this from the 3.2.0 update. But, also, I'm not able to see any difference in how this test runs in 3.2.0 versus 3.1.5. Can you write an assertion that fails in 3.2.0 that passes in 3.1.5 so that I can see the exact change?

Gosh, yeah, I can't think of what could have affected this from the 3.2.0 update. But, also, I'm not able to see any difference in how this test runs in 3.2.0 versus 3.1.5. Can you write an assertion that fails in 3.2.0 that passes in 3.1.5 so that I can see the exact change?

Thanks for the quick reaction, today I will prepare examples for reproduction

@jennifer-shehane Start explore more in detail of the problem. Π‘ypress update is not related to the problem.

The problem was caused by different versions of chromium.

Electron 59: el.files = dataTransfer.files; trigger change on input
Chrome 69 : el.files = dataTransfer.files; trigger change on input
Chrome 73: el.files = dataTransfer.files; not trigger change on input

I apologize for not fully understanding it before creating issue.

Yes, I can confirm that the problem does not exist with Chrome 72, but came up after the update to Chrome 73.

Your workaround works for me, as well.

I could not find anything on google, regarding a bug in Chrome.

@DragorWW I think you can close the issue

@giovannyquin why? This workaround leads to the event being fired twice for chrome < 73 and Electron.

@giovannyquin @pszalanski Yes, there is no problem in cypress, but the problem with this behavior did not come from this, perhaps you should find the reason for its appearance and add a link to the documentation

You are right. This is probably some Chrome 73 bug or change. I'll post here if I find anything useful.

It'll be beneficial if we can find any info about this changes. It looks quite weird since there is nothing related to this change in Chrome 73 changelog.

Hi, this workaround does not solve the issue in 73 for me.... same code is actually working without any issue with 72 (just tested now)

test file :

When('I import the product file', () => {
cy.uploadFile('input[type=file]', 'import_product_test.json', 'application/json')

command.js

function getFixtureBlob(fileName, type) {
return type === 'application/json'
? cy
.fixture(fileName)
.then(JSON.stringify)
.then(jsonStr => new Blob([jsonStr], { type: 'application/json' }))
: cy.fixture(fileName)
}

/**

Uploads a file to an input
@memberof Cypress.Chainable#
@name uploadFile
@function
@param {String} selector - element to target
@param {String} fileName - The file url to upload
@param {String} type - content type of the uploaded file
*/
Cypress.Commands.add('uploadFile', (selector, fileName, type = '') => {
return cy.get(selector).then(subject => {
return getFixtureBlob(fileName, type).then(blob => {
return cy.window().then(win => {
const el = subject[0]
const nameSegments = fileName.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 cy.wrap(subject).trigger('change', {force: true});
//return subject
})
})
})
})

Best regards

I confirm that using these line
return cy.wrap(subject).trigger('change', {force: true});
is working well on Chrome 73. Thx

@cydashboardthqa I can confirm this too.
Just recently released an update to fix this in cypress-file-upload@3.0.4.

In case anyone was wondering why it happened, it was an intentional change: https://bugs.chromium.org/p/chromium/issues/detail?id=792336

In our specific case
cy.wrap(subject).trigger('change', {force: true});
didn't work out in electron with error:

cy.trigger() failed because this element is detached from the DOM.

this one works though:

cy.get('[data-qa=file_upload_hidden_input]').trigger('change', {force:true});

i believe this is somehow connected with jquery.fileupload we use

is there any version of Chrome/cypress that I can download to workaround this issue?

is there any version of Chrome/cypress that I can download to workaround this issue?

The best option is probably to use cypress-file-upload with any version Chrome/cypress.

Or use Chrome <= 72.

@GammaGames Thank you for finding the change in Chromium. These are notoriously difficult to track down. πŸ’―

Chrome 73 Behavior from https://bugs.chromium.org/p/chromium/issues/detail?id=792336:

File Inputs: Don't generate change events when 'files' is set

The <input type=file> element generates "change" events when the user
interacts with the control to select files, exposed as the 'files'
property (a FileList). This property can be assigned to by script, and
should not generate a "change" event in such a case. It was
erroneously doing so, so fix it.

In Chrome 73 no change event will be fired on el.files = dataTransfer.files;, this has nothing to do with Cypress, so this issue will be closed.

If your application code is relying on the change event firing when the user selects files, you should change your implementation and also remove the manual firing of the change event within Cypress as this does not reflect what a real user will experience in Chrome 73.

I was having the same issue. I changed from onInput to onChange and it works fine. Btw I am using cypress-file-upload.