New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Ability to Submit File to Input Element From Local Filesystem #170

Open
pihish opened this Issue Jun 15, 2016 · 79 comments

Comments

@pihish

pihish commented Jun 15, 2016

Description
Our app takes a file from an user, does some work with it, and produces outputs based on the input the user provides. A bunch of things happen in the system during the processing and we expect project pages to be generated in a certain fashion after a project successfully completes. We need to be able to submit files through our webpage to test the affects of this process.

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Jun 16, 2016

Member

This requires sending native events.

We've been holding off on adding native event support because of the way the debugger protocol works in Chrome. It can only accept one connection which means dev tools cannot be open. Forcing users to close their dev tools is a big reason why the testing ecosystem is so awful.

We've been investigating the user experience around this and we have some pretty good ideas about how to improve this. Once we add native events you'll be able to not just upload files but send native keyboard events, mobile events, and a slew of other things.

Member

brian-mann commented Jun 16, 2016

This requires sending native events.

We've been holding off on adding native event support because of the way the debugger protocol works in Chrome. It can only accept one connection which means dev tools cannot be open. Forcing users to close their dev tools is a big reason why the testing ecosystem is so awful.

We've been investigating the user experience around this and we have some pretty good ideas about how to improve this. Once we add native events you'll be able to not just upload files but send native keyboard events, mobile events, and a slew of other things.

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Jan 18, 2017

Member

Should also note that it is possible to upload files in your application but its different based on how you've written your own upload code.

For instance if you use the new fileReader API's you could use cy.readFile or cy.fixture to read in data, and then convert it to a Blob and then trigger the specific event your application is looking for and send in the Blob as a property.

Because the implementation is different per application there is no way for us to suggest how to do it. Once we implement native API's we will be able to show a recipe handling this.

Member

brian-mann commented Jan 18, 2017

Should also note that it is possible to upload files in your application but its different based on how you've written your own upload code.

For instance if you use the new fileReader API's you could use cy.readFile or cy.fixture to read in data, and then convert it to a Blob and then trigger the specific event your application is looking for and send in the Blob as a property.

Because the implementation is different per application there is no way for us to suggest how to do it. Once we implement native API's we will be able to show a recipe handling this.

@wescarr

This comment has been minimized.

Show comment
Hide comment
@wescarr

wescarr Jun 26, 2017

Wanted to add that I got this working by doing the following:

cy.fixture('path/to/image.png').as('logo')
  .get('input[type=file]').then(function(el) {
    return Cypress.Blob.base64StringToBlob(this.logo, 'image/png')
      .then(blob => {
        el[0].files[0] = blob
        el[0].dispatchEvent(new Event('change', {bubbles: true}))
      })
  })

wescarr commented Jun 26, 2017

Wanted to add that I got this working by doing the following:

cy.fixture('path/to/image.png').as('logo')
  .get('input[type=file]').then(function(el) {
    return Cypress.Blob.base64StringToBlob(this.logo, 'image/png')
      .then(blob => {
        el[0].files[0] = blob
        el[0].dispatchEvent(new Event('change', {bubbles: true}))
      })
  })
@GuilhermeMedeiros

This comment has been minimized.

Show comment
Hide comment
@GuilhermeMedeiros

GuilhermeMedeiros Jul 13, 2017

I've tried everything and I can't get dropzone on my react app to recognise the file.

cy.get('input[type=file]', { force: true }).then(function(el) {
    const xml = new Blob([this.xbrl], { type: 'text/xml' } )

    el[0].files[0] = xml
    el[0].dispatchEvent(new Event('change', {
        bubbles: true,
        target: {
            files: [xml],
        }
    }))
})

GuilhermeMedeiros commented Jul 13, 2017

I've tried everything and I can't get dropzone on my react app to recognise the file.

cy.get('input[type=file]', { force: true }).then(function(el) {
    const xml = new Blob([this.xbrl], { type: 'text/xml' } )

    el[0].files[0] = xml
    el[0].dispatchEvent(new Event('change', {
        bubbles: true,
        target: {
            files: [xml],
        }
    }))
})
@dziamid

This comment has been minimized.

Show comment
Hide comment
@dziamid

dziamid Aug 17, 2017

@brian-mann

Once we add native events you'll be able to not just upload files but send native keyboard events, mobile events, and a slew of other things.

Is there any progress on the road to native events? Having to hack around for file uploading, hovering and tabbing is a big deal for us.

dziamid commented Aug 17, 2017

@brian-mann

Once we add native events you'll be able to not just upload files but send native keyboard events, mobile events, and a slew of other things.

Is there any progress on the road to native events? Having to hack around for file uploading, hovering and tabbing is a big deal for us.

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Aug 17, 2017

Member

Chromium team is still working on multiplex'd debugger support. Implementing native events without this is significantly difficult and there are many edge cases and UI changes we'd have to account for, and then once they do land this, we'd have to fork the code and handle it in a separate way.

I'm still on the side of waiting until this is implemented properly by them. You can essentially achieve most things without native event by firing events directly. You can't do things like tab, but you could always use a different tool just for those tests, while letting Cypress handle all the rest.

You can test file uploads using events directly if your app is using HTML5 API's and its not like an oldschool traditional form.

Member

brian-mann commented Aug 17, 2017

Chromium team is still working on multiplex'd debugger support. Implementing native events without this is significantly difficult and there are many edge cases and UI changes we'd have to account for, and then once they do land this, we'd have to fork the code and handle it in a separate way.

I'm still on the side of waiting until this is implemented properly by them. You can essentially achieve most things without native event by firing events directly. You can't do things like tab, but you could always use a different tool just for those tests, while letting Cypress handle all the rest.

You can test file uploads using events directly if your app is using HTML5 API's and its not like an oldschool traditional form.

@dziamid

This comment has been minimized.

Show comment
Hide comment
@dziamid

dziamid Aug 17, 2017

Thanks for the detailed response. While I could get away without tabbing and work around file upload, this involved a lot of boilerplate, complexity and changes in the application code. Nevertheless, great product anyway and good to know that you are looking forward to switching to native events when things get stable on chrome side!

dziamid commented Aug 17, 2017

Thanks for the detailed response. While I could get away without tabbing and work around file upload, this involved a lot of boilerplate, complexity and changes in the application code. Nevertheless, great product anyway and good to know that you are looking forward to switching to native events when things get stable on chrome side!

@rdamborsky

This comment has been minimized.

Show comment
Hide comment
@rdamborsky

rdamborsky Oct 19, 2017

Using el[0].files[0] = blob as @wescarr recommends worked until few weeks back (I don't recall what Chrome version it was).

But now, at least in v.61+ this no longer works and errors out with "TypeError: Failed to set an indexed property on 'FileList': Index property setter is not supported."

This is not an issue on side of Cypress. I just felt mentioning it might save time to someone running into this thread. Unfortunately, I didn't find a way around this yet.

rdamborsky commented Oct 19, 2017

Using el[0].files[0] = blob as @wescarr recommends worked until few weeks back (I don't recall what Chrome version it was).

But now, at least in v.61+ this no longer works and errors out with "TypeError: Failed to set an indexed property on 'FileList': Index property setter is not supported."

This is not an issue on side of Cypress. I just felt mentioning it might save time to someone running into this thread. Unfortunately, I didn't find a way around this yet.

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Oct 19, 2017

Member

Haven't looked into this, but oftentimes you can overwrite things manually using Object.defineProperty which can forcibly change the descriptor for values.

That is of course if it's configurable: true

Member

brian-mann commented Oct 19, 2017

Haven't looked into this, but oftentimes you can overwrite things manually using Object.defineProperty which can forcibly change the descriptor for values.

That is of course if it's configurable: true

@robrkerr

This comment has been minimized.

Show comment
Hide comment
@robrkerr

robrkerr Oct 23, 2017

I'm having the same issue @rdamborsky mentioned and can't find a way around it.

robrkerr commented Oct 23, 2017

I'm having the same issue @rdamborsky mentioned and can't find a way around it.

@aboutlo

This comment has been minimized.

Show comment
Hide comment
@aboutlo

aboutlo Oct 27, 2017

@brian-mann I tried to overwrite but it seems not possible:
https://stackoverflow.com/questions/1711357/how-would-you-overload-the-operator-in-javascript

The FileList descriptor is empty
Object.getOwnPropertyDescriptors($input[0].files) // => {}

Any suggestion to get a file upload tested? Perhaps we could pass an option to the cypress run command to turn off chrome security sandbox?

aboutlo commented Oct 27, 2017

@brian-mann I tried to overwrite but it seems not possible:
https://stackoverflow.com/questions/1711357/how-would-you-overload-the-operator-in-javascript

The FileList descriptor is empty
Object.getOwnPropertyDescriptors($input[0].files) // => {}

Any suggestion to get a file upload tested? Perhaps we could pass an option to the cypress run command to turn off chrome security sandbox?

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Oct 27, 2017

Member

As mentioned above - there is no way to set values in file upload inputs, and there are no flags that chrome exposes to force this. You have to use native events. I commented on this recently yesterday.

#311 (comment)

The only other way to get file uploads working today is to understand how your application handles them with File API and then stub it out. It's possible but not generic enough to give any advice on it.

Member

brian-mann commented Oct 27, 2017

As mentioned above - there is no way to set values in file upload inputs, and there are no flags that chrome exposes to force this. You have to use native events. I commented on this recently yesterday.

#311 (comment)

The only other way to get file uploads working today is to understand how your application handles them with File API and then stub it out. It's possible but not generic enough to give any advice on it.

@aboutlo

This comment has been minimized.

Show comment
Hide comment
@aboutlo

aboutlo commented Oct 30, 2017

tks @brian-mann 👍

@rafiek

This comment has been minimized.

Show comment
Hide comment
@rafiek

rafiek Nov 16, 2017

I think Cypress is really awesome and am already using it within my company. We previously used Protractor and were able to add a file to an input[type="file"] element. Would really love to be able to this as well with Cypress, because it is sometimes an essential step when doing full e2e tests for our applications.

rafiek commented Nov 16, 2017

I think Cypress is really awesome and am already using it within my company. We previously used Protractor and were able to add a file to an input[type="file"] element. Would really love to be able to this as well with Cypress, because it is sometimes an essential step when doing full e2e tests for our applications.

@anned20

This comment has been minimized.

Show comment
Hide comment
@anned20

anned20 Nov 16, 2017

Hey everyone!

I fixed this for DropZone using this piece of code:

const dropEvent = {
    dataTransfer: {
        files: [
        ],
    },
};

cy.fixture('Path to picture fixture').then((picture) => {
    return Cypress.Blob.base64StringToBlob(picture, 'image/jpeg').then((blob) => {
        dropEvent.dataTransfer.files.push(blob);
    });
});

cy.get('Your dropzone element').trigger('drop', dropEvent);

I hope this will work for you too!

anned20 commented Nov 16, 2017

Hey everyone!

I fixed this for DropZone using this piece of code:

const dropEvent = {
    dataTransfer: {
        files: [
        ],
    },
};

cy.fixture('Path to picture fixture').then((picture) => {
    return Cypress.Blob.base64StringToBlob(picture, 'image/jpeg').then((blob) => {
        dropEvent.dataTransfer.files.push(blob);
    });
});

cy.get('Your dropzone element').trigger('drop', dropEvent);

I hope this will work for you too!

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Nov 16, 2017

Member

@anned20 this is really good - thank you for this.

One tidbit - you're missing a return statement on Cypress.blob.base64StringToBlob because that returns a promise and the cypress cy.then needs to know about it else it will not properly await it. This is working in your example by chance because the blob promise is resolving faster than the later cy.get and cy.trigger resolve.

Return the promise will always finish the promise chain first before moving on.

Member

brian-mann commented Nov 16, 2017

@anned20 this is really good - thank you for this.

One tidbit - you're missing a return statement on Cypress.blob.base64StringToBlob because that returns a promise and the cypress cy.then needs to know about it else it will not properly await it. This is working in your example by chance because the blob promise is resolving faster than the later cy.get and cy.trigger resolve.

Return the promise will always finish the promise chain first before moving on.

@anned20

This comment has been minimized.

Show comment
Hide comment
@anned20

anned20 Nov 16, 2017

@brian-mann I updated the example, is this what you meant?

anned20 commented Nov 16, 2017

@brian-mann I updated the example, is this what you meant?

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Nov 16, 2017

Member

Yup 👍

Member

brian-mann commented Nov 16, 2017

Yup 👍

@vicusbass

This comment has been minimized.

Show comment
Hide comment
@vicusbass

vicusbass Nov 23, 2017

Is there any workaround (other than using Electron) for file inputs (the dropzone example is not working for me) or we have to wait for the native events implementation?

vicusbass commented Nov 23, 2017

Is there any workaround (other than using Electron) for file inputs (the dropzone example is not working for me) or we have to wait for the native events implementation?

@brian-mann

This comment has been minimized.

Show comment
Hide comment
@brian-mann

brian-mann Nov 23, 2017

Member

The workaround is however your application is built. You fire the events and provide the object value properties and/or methods that you application uses to respond to the upload events.

There is no generic solution - you have to understand how your application works. Then however it works you fire what it needs to respond.

Member

brian-mann commented Nov 23, 2017

The workaround is however your application is built. You fire the events and provide the object value properties and/or methods that you application uses to respond to the upload events.

There is no generic solution - you have to understand how your application works. Then however it works you fire what it needs to respond.

@danceric0919

This comment has been minimized.

Show comment
Hide comment
@danceric0919

danceric0919 Nov 24, 2017

I'm new to Cypress.io and encounter issue when testing upload file implemented by Vue.js
We have a component like following:

<b-form-file id="upload-file" v-model="files[0]" @change="upload(payload)" >
</b-form-file>

I tried
cy.get('#upload-file').trigger('change', somepayload)
but nothing happened

is there any way to trigger change or proper event for application implemented by Vue?

Thanks for any respond.
Cheers.

danceric0919 commented Nov 24, 2017

I'm new to Cypress.io and encounter issue when testing upload file implemented by Vue.js
We have a component like following:

<b-form-file id="upload-file" v-model="files[0]" @change="upload(payload)" >
</b-form-file>

I tried
cy.get('#upload-file').trigger('change', somepayload)
but nothing happened

is there any way to trigger change or proper event for application implemented by Vue?

Thanks for any respond.
Cheers.

@bahmutov

This comment has been minimized.

Show comment
Hide comment
@bahmutov

bahmutov Nov 27, 2017

Collaborator

@danceric0919 can you just call upload() method on the Vue component around this element? Like load the context from Cypress fixture then call the method? I should maybe make a recipe for this that uses Vue.js - where is b-form-file coming from? Is it a public component?

Ok found it - https://bootstrap-vue.js.org/docs/components/form-file/

Collaborator

bahmutov commented Nov 27, 2017

@danceric0919 can you just call upload() method on the Vue component around this element? Like load the context from Cypress fixture then call the method? I should maybe make a recipe for this that uses Vue.js - where is b-form-file coming from? Is it a public component?

Ok found it - https://bootstrap-vue.js.org/docs/components/form-file/

@bahmutov

This comment has been minimized.

Show comment
Hide comment
@bahmutov

bahmutov Nov 27, 2017

Collaborator

@danceric0919 can you take a look how I test file upload in bahmutov/vue-vuex-todomvc@deff47a ? Just create a File in your test, set it in the component and trigger change event.

Collaborator

bahmutov commented Nov 27, 2017

@danceric0919 can you take a look how I test file upload in bahmutov/vue-vuex-todomvc@deff47a ? Just create a File in your test, set it in the component and trigger change event.

@danceric0919

This comment has been minimized.

Show comment
Hide comment
@danceric0919

danceric0919 Nov 28, 2017

@bahmutov thanks for the reply.
I'll try it later today or tomorrow.
Appreciate 👍

2017/11/29 update
After updating application slightly, your example works in my site.
Really appreciate for the example, it saved my days.

danceric0919 commented Nov 28, 2017

@bahmutov thanks for the reply.
I'll try it later today or tomorrow.
Appreciate 👍

2017/11/29 update
After updating application slightly, your example works in my site.
Really appreciate for the example, it saved my days.

@MysteriousNothing

This comment has been minimized.

Show comment
Hide comment
@MysteriousNothing

MysteriousNothing Apr 13, 2018

Here is my working example, what I use.

Enter code bellow to "cypress/support/commands.js"

Cypress.Commands.add('upload_file', (fileName, selector) => {
    cy.get(selector).then(subject => {
        cy.fixture(fileName).then((content) => {
            const el = subject[0]
            const testFile = new File([content], fileName)
            const dataTransfer = new DataTransfer()

            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
        })
    })
})

This is how I use it in my test:
cy.writeFile('cypress/fixtures/notice.pdf', 'Hi, this content is created by cypress!')
cy.upload_file('notice.pdf', 'input[name=file1]')

Big thanks to @fcurella who code this is from above (love you man! :D).
You have no idea how many hours I tried to get this working.

MysteriousNothing commented Apr 13, 2018

Here is my working example, what I use.

Enter code bellow to "cypress/support/commands.js"

Cypress.Commands.add('upload_file', (fileName, selector) => {
    cy.get(selector).then(subject => {
        cy.fixture(fileName).then((content) => {
            const el = subject[0]
            const testFile = new File([content], fileName)
            const dataTransfer = new DataTransfer()

            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
        })
    })
})

This is how I use it in my test:
cy.writeFile('cypress/fixtures/notice.pdf', 'Hi, this content is created by cypress!')
cy.upload_file('notice.pdf', 'input[name=file1]')

Big thanks to @fcurella who code this is from above (love you man! :D).
You have no idea how many hours I tried to get this working.

@JonRobinsonLush

This comment has been minimized.

Show comment
Hide comment
@JonRobinsonLush

JonRobinsonLush Apr 18, 2018

@MysteriousNothing Thanks. This also worked for me!

Trying to work out how to adapt it so it works with images?

JonRobinsonLush commented Apr 18, 2018

@MysteriousNothing Thanks. This also worked for me!

Trying to work out how to adapt it so it works with images?

@bcurran07

This comment has been minimized.

Show comment
Hide comment
@bcurran07

bcurran07 Apr 18, 2018

@JonRobinsonLush are you getting a broken tile? If so, I was able to solve by using Cypress.Blob.base64StringToBlob. Example below:

cy.fixture('image.jpg').then(content => {
    Cypress.Blob.base64StringToBlob(content, 'image/jpeg').then(blob => {
            const testFile = new File([blob], fileName);
           // the rest of @MysteriousNothing's code goes here...
    });
});

bcurran07 commented Apr 18, 2018

@JonRobinsonLush are you getting a broken tile? If so, I was able to solve by using Cypress.Blob.base64StringToBlob. Example below:

cy.fixture('image.jpg').then(content => {
    Cypress.Blob.base64StringToBlob(content, 'image/jpeg').then(blob => {
            const testFile = new File([blob], fileName);
           // the rest of @MysteriousNothing's code goes here...
    });
});
@toriningen

This comment has been minimized.

Show comment
Hide comment
@toriningen

toriningen Apr 20, 2018

@brian-mann have your team got any estimates on when would you ship native events?

toriningen commented Apr 20, 2018

@brian-mann have your team got any estimates on when would you ship native events?

@simonpweller

This comment has been minimized.

Show comment
Hide comment
@simonpweller

simonpweller Apr 25, 2018

@JonRobinsonLush

Here's my adapted version (thanks to everyone in the thread) that works with images and other files (placed inside commands.js for easy access):

Cypress.Commands.add('upload_file', (fileName, selector) => {
    cy.get(selector).then(subject => {
        cy.fixture(fileName, 'base64').then((content) => {
            const el = subject[0];
            const blob = b64toBlob(content);
            const testFile = new File([blob], fileName);
            const dataTransfer = new DataTransfer();

            dataTransfer.items.add(testFile);
            el.files = dataTransfer.files;
        });
    });
});

function b64toBlob(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;

    var byteCharacters = atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    var blob = new Blob(byteArrays, { type: contentType });
    blob.lastModifiedDate = new Date();
    return blob;
}

use like this in your tests:
cy.upload_file('building.jpg', '#building [type="file"]');

simonpweller commented Apr 25, 2018

@JonRobinsonLush

Here's my adapted version (thanks to everyone in the thread) that works with images and other files (placed inside commands.js for easy access):

Cypress.Commands.add('upload_file', (fileName, selector) => {
    cy.get(selector).then(subject => {
        cy.fixture(fileName, 'base64').then((content) => {
            const el = subject[0];
            const blob = b64toBlob(content);
            const testFile = new File([blob], fileName);
            const dataTransfer = new DataTransfer();

            dataTransfer.items.add(testFile);
            el.files = dataTransfer.files;
        });
    });
});

function b64toBlob(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;

    var byteCharacters = atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    var blob = new Blob(byteArrays, { type: contentType });
    blob.lastModifiedDate = new Date();
    return blob;
}

use like this in your tests:
cy.upload_file('building.jpg', '#building [type="file"]');

@nickolay-smyk

This comment has been minimized.

Show comment
Hide comment
@nickolay-smyk

nickolay-smyk May 7, 2018

@faktotum85 Thank you for this command. However in my case it failed to pass the content type verification on side of app that I was testing. The problem appeared to be caused by the fact that content type is not set on the step of file composing:
const testFile = new File([blob], fileName);

According to MDN it's possible to pass contentType to File too:
https://developer.mozilla.org/en-US/docs/Web/API/File/File

..so I have added content type as parameter to main function too and passed it to File:

Cypress.Commands.add('upload_file', (fileName, selector, contentType) => {
  contentType = contentType || '';
  ...
  const testFile = new File([blob], fileName, {type: contentType});

Hope that it helps someone.

nickolay-smyk commented May 7, 2018

@faktotum85 Thank you for this command. However in my case it failed to pass the content type verification on side of app that I was testing. The problem appeared to be caused by the fact that content type is not set on the step of file composing:
const testFile = new File([blob], fileName);

According to MDN it's possible to pass contentType to File too:
https://developer.mozilla.org/en-US/docs/Web/API/File/File

..so I have added content type as parameter to main function too and passed it to File:

Cypress.Commands.add('upload_file', (fileName, selector, contentType) => {
  contentType = contentType || '';
  ...
  const testFile = new File([blob], fileName, {type: contentType});

Hope that it helps someone.

@SavePointSam

This comment has been minimized.

Show comment
Hide comment
@SavePointSam

SavePointSam May 14, 2018

FYI, @faktotum85's solution will not work with JSON files. It appears that cy.fixture('example.json', 'base64') ignores the encoding param and still gives the raw JSON. I don't personally need to upload JSON files. So, I haven't looked into an update that would allow JSON files to work, but I encountered this issue when playing with the script provided. You'll need to find a way to verify that the content is base64 encoded before running the atob().

SavePointSam commented May 14, 2018

FYI, @faktotum85's solution will not work with JSON files. It appears that cy.fixture('example.json', 'base64') ignores the encoding param and still gives the raw JSON. I don't personally need to upload JSON files. So, I haven't looked into an update that would allow JSON files to work, but I encountered this issue when playing with the script provided. You'll need to find a way to verify that the content is base64 encoded before running the atob().

@SavePointSam

This comment has been minimized.

Show comment
Hide comment
@SavePointSam

SavePointSam May 14, 2018

I've went ahead and added support to provide multiple files as well as added a catch like I described above. I'm going to wrap everything in an NPM package sometime this week to make it easier to implement this.

SavePointSam commented May 14, 2018

I've went ahead and added support to provide multiple files as well as added a catch like I described above. I'm going to wrap everything in an NPM package sometime this week to make it easier to implement this.

@javieraviles

This comment has been minimized.

Show comment
Hide comment
@javieraviles

javieraviles commented May 14, 2018

Will leave this here in case it helps somebody
https://github.com/javieraviles/cypress-upload-file-post-form

@dobromir-hristov

This comment has been minimized.

Show comment
Hide comment
@dobromir-hristov

dobromir-hristov May 17, 2018

Just to clarify, you can use Cypress.Blob.base64StringToBlob to convert the fixture's base64 response to a blob. That returns a promise :) here is the whole thing. Bonus jsdoc + its chainable, so you can do other stuff to the input if needed.

/**
 * Uploads a file to an input
 * @memberOf Cypress.Chainable#
 * @name upload_file
 * @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('upload_file', (selector, fileUrl, type = '') => {
  return cy.get(selector).then(subject => {
    return cy.fixture(fileUrl, 'base64')
      .then(Cypress.Blob.base64StringToBlob)
      .then(blob => {
        const el = subject[0]
        const nameSegments = fileUrl.split('/')
        const name = nameSegments[nameSegments.length - 1]
        const testFile = new File([blob], name, { type })
        const dataTransfer = new DataTransfer()
        dataTransfer.items.add(testFile)
        el.files = dataTransfer.files
        return subject
      })
  })
})

PS: The above code does not work inside the Electron browser. Electron does not support the instantiation of DataTransfer. We need to find another way around this.

Because its 2018 and everyone should use drag and drop uploaders, please use a simple drag event handler on an outer div. Then you can do this:

Cypress.Commands.add('upload_file', (selector, fileUrl, type = '') => {
  return cy.fixture(fileUrl, 'base64')
    .then(Cypress.Blob.base64StringToBlob)
    .then(blob => {
      const nameSegments = fileUrl.split('/')
      const name = nameSegments[nameSegments.length - 1]
      const testFile = new File([blob], name, { type })
      const event = { dataTransfer: { files: [testFile] } }
      return cy.get(selector).trigger('drop', event)
    })
})

dobromir-hristov commented May 17, 2018

Just to clarify, you can use Cypress.Blob.base64StringToBlob to convert the fixture's base64 response to a blob. That returns a promise :) here is the whole thing. Bonus jsdoc + its chainable, so you can do other stuff to the input if needed.

/**
 * Uploads a file to an input
 * @memberOf Cypress.Chainable#
 * @name upload_file
 * @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('upload_file', (selector, fileUrl, type = '') => {
  return cy.get(selector).then(subject => {
    return cy.fixture(fileUrl, 'base64')
      .then(Cypress.Blob.base64StringToBlob)
      .then(blob => {
        const el = subject[0]
        const nameSegments = fileUrl.split('/')
        const name = nameSegments[nameSegments.length - 1]
        const testFile = new File([blob], name, { type })
        const dataTransfer = new DataTransfer()
        dataTransfer.items.add(testFile)
        el.files = dataTransfer.files
        return subject
      })
  })
})

PS: The above code does not work inside the Electron browser. Electron does not support the instantiation of DataTransfer. We need to find another way around this.

Because its 2018 and everyone should use drag and drop uploaders, please use a simple drag event handler on an outer div. Then you can do this:

Cypress.Commands.add('upload_file', (selector, fileUrl, type = '') => {
  return cy.fixture(fileUrl, 'base64')
    .then(Cypress.Blob.base64StringToBlob)
    .then(blob => {
      const nameSegments = fileUrl.split('/')
      const name = nameSegments[nameSegments.length - 1]
      const testFile = new File([blob], name, { type })
      const event = { dataTransfer: { files: [testFile] } }
      return cy.get(selector).trigger('drop', event)
    })
})
@iancrowther

This comment has been minimized.

Show comment
Hide comment
@iancrowther

iancrowther Jul 12, 2018

@brian-mann - I noticed electron recently did a release, did they upgrade chromium enabling cypress to start using native events?

I had a look but was unclear.

I'm keen to start automating input type='file' for csv's, pdfs etc.

Maybe I could have a play with the pre-release work and start to put together a recipe? Any pointers would be awesome as im fairly new to the project

iancrowther commented Jul 12, 2018

@brian-mann - I noticed electron recently did a release, did they upgrade chromium enabling cypress to start using native events?

I had a look but was unclear.

I'm keen to start automating input type='file' for csv's, pdfs etc.

Maybe I could have a play with the pre-release work and start to put together a recipe? Any pointers would be awesome as im fairly new to the project

@danreale

This comment has been minimized.

Show comment
Hide comment
@danreale

danreale Jul 13, 2018

I had a specific need to upload excel files to my application. Just wanted to share the code that worked for me in chrome browser.

Cypress.Commands.add('upload_file', (fileName, selector) => {
    return cy.get(selector).then(subject => {
        return cy.fixture(fileName, 'base64')
        .then(Cypress.Blob.base64StringToBlob)
        .then(blob => {
            const el = subject[0]
            const testFile = new File([blob], fileName, { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
            return subject;
        })
    })
})

example usage:

cy.upload_file('Test.xlsx', '#fileInput')

Test.xlsx needs to be in your fixtures folder in order for this to work

danreale commented Jul 13, 2018

I had a specific need to upload excel files to my application. Just wanted to share the code that worked for me in chrome browser.

Cypress.Commands.add('upload_file', (fileName, selector) => {
    return cy.get(selector).then(subject => {
        return cy.fixture(fileName, 'base64')
        .then(Cypress.Blob.base64StringToBlob)
        .then(blob => {
            const el = subject[0]
            const testFile = new File([blob], fileName, { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
            return subject;
        })
    })
})

example usage:

cy.upload_file('Test.xlsx', '#fileInput')

Test.xlsx needs to be in your fixtures folder in order for this to work

@Moejoe90

This comment has been minimized.

Show comment
Hide comment
@Moejoe90

Moejoe90 Jul 19, 2018

nice @danreale you just solved my problem with multi file upload

Moejoe90 commented Jul 19, 2018

nice @danreale you just solved my problem with multi file upload

@emijmker

This comment has been minimized.

Show comment
Hide comment
@emijmker

emijmker Aug 3, 2018

We were struggling with this for quit a while, also on the part where contributers mention knowing how your application works. Here's is what worked for our application, many thanks to above contributors. It's may be written a bit at dummy level at some points, but with the expectation you understand some about functions (if not I'm sure your dev colleague would be happy to assist:-)). But for some who don't have that much experience yet, maybe it will send you in the right direction.

What we stumbled across were authorization errors/login popups and all sorts. On our old application which had poor architecture, we had to do a lot of GET/PUT/POST requests (couldn't get stubbing to work) in order to set the required state of the application and get the right tokens for the headers in order to be able and mostly authorized to upload a file to a database. You can check out https://docs.cypress.io/api/commands/request.html#URL on how to do that. We ended up feeding the database with a POST call so that the frontend site would know the doc was uploaded and allowed the next button to actually go the next page. Which basically didn't really tested our upload functionality in the frontend.

When we build our new application, we realized we needed to simplify the architecture in order to make it more (automate) testable. Understanding how to get the upload functionality to work did again take some time but we finally figured it out and got the below fixture to work.

Add this in your commands.js

Cypress.Commands.add('upload_file', (fileName, fileType = ' ', selector) => {
    return cy.get(selector).then(subject => {
      cy.fixture(fileName, 'base64')
        .then(Cypress.Blob.base64StringToBlob)
        .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;
        });
    });
  });

In cypress\fixtures folder make sure the document you want to upload is placed in that folder.

In the testfile it looks like below. In a supporting file I created a setUp function that would call to our backend with a POST request, parse the url from it's response body so it could do a cy.visit that testurl which will be the base for our test.
The preserve cookie in the beforeEach finally did the trick, as another required cookie got lost in the process before doing the upload test.

describe('Test upload functionality, () => {
   before(() => {
    setUp({  page: '/example-upload-page' })
  });

  beforeEach(() => {
    Cypress.Cookies.preserveOnce("requiredCookie3");
  });
  it('Should upload a document', () => {
    cy.setCookie('cookie1name', cookie1value)
    cy.setCookie('cookie2name', cookie2value)

    const fileName = 'PDF-sample-8KB.pdf';
    const fileType = 'application/pdf';
    const fileInput = 'input[type=file]';
    cy.upload_file(fileName, fileType, fileInput)
  });
});

Btw, using Postman in all of this was helpful, it gave us feedback whether our call was valid or not, how to format the body etc etc. and therefore do troubleshooting why a call in Cypress wasn't working.

Hope this might help to some!

emijmker commented Aug 3, 2018

We were struggling with this for quit a while, also on the part where contributers mention knowing how your application works. Here's is what worked for our application, many thanks to above contributors. It's may be written a bit at dummy level at some points, but with the expectation you understand some about functions (if not I'm sure your dev colleague would be happy to assist:-)). But for some who don't have that much experience yet, maybe it will send you in the right direction.

What we stumbled across were authorization errors/login popups and all sorts. On our old application which had poor architecture, we had to do a lot of GET/PUT/POST requests (couldn't get stubbing to work) in order to set the required state of the application and get the right tokens for the headers in order to be able and mostly authorized to upload a file to a database. You can check out https://docs.cypress.io/api/commands/request.html#URL on how to do that. We ended up feeding the database with a POST call so that the frontend site would know the doc was uploaded and allowed the next button to actually go the next page. Which basically didn't really tested our upload functionality in the frontend.

When we build our new application, we realized we needed to simplify the architecture in order to make it more (automate) testable. Understanding how to get the upload functionality to work did again take some time but we finally figured it out and got the below fixture to work.

Add this in your commands.js

Cypress.Commands.add('upload_file', (fileName, fileType = ' ', selector) => {
    return cy.get(selector).then(subject => {
      cy.fixture(fileName, 'base64')
        .then(Cypress.Blob.base64StringToBlob)
        .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;
        });
    });
  });

In cypress\fixtures folder make sure the document you want to upload is placed in that folder.

In the testfile it looks like below. In a supporting file I created a setUp function that would call to our backend with a POST request, parse the url from it's response body so it could do a cy.visit that testurl which will be the base for our test.
The preserve cookie in the beforeEach finally did the trick, as another required cookie got lost in the process before doing the upload test.

describe('Test upload functionality, () => {
   before(() => {
    setUp({  page: '/example-upload-page' })
  });

  beforeEach(() => {
    Cypress.Cookies.preserveOnce("requiredCookie3");
  });
  it('Should upload a document', () => {
    cy.setCookie('cookie1name', cookie1value)
    cy.setCookie('cookie2name', cookie2value)

    const fileName = 'PDF-sample-8KB.pdf';
    const fileType = 'application/pdf';
    const fileInput = 'input[type=file]';
    cy.upload_file(fileName, fileType, fileInput)
  });
});

Btw, using Postman in all of this was helpful, it gave us feedback whether our call was valid or not, how to format the body etc etc. and therefore do troubleshooting why a call in Cypress wasn't working.

Hope this might help to some!

@mattblackdev

This comment has been minimized.

Show comment
Hide comment
@mattblackdev

mattblackdev Aug 8, 2018

@dobromir-hristov's solution worked great for me. Except, I had another lib doing a instanceof File check, which would normally pass, but failed because cross-frame/window instanceof checks don't work.

I tweaked it to use the AUT's File constructor:

/**
 * Uploads a file to an input
 * @memberOf Cypress.Chainable#
 * @name upload_file
 * @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 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
        })
      })
  })
})

mattblackdev commented Aug 8, 2018

@dobromir-hristov's solution worked great for me. Except, I had another lib doing a instanceof File check, which would normally pass, but failed because cross-frame/window instanceof checks don't work.

I tweaked it to use the AUT's File constructor:

/**
 * Uploads a file to an input
 * @memberOf Cypress.Chainable#
 * @name upload_file
 * @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 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
        })
      })
  })
})
@robbertvancaem

This comment has been minimized.

Show comment
Hide comment
@robbertvancaem

robbertvancaem Aug 8, 2018

@mattblackdev Thanks, I had a similar problem and this resolved it very nicely! 👍

robbertvancaem commented Aug 8, 2018

@mattblackdev Thanks, I had a similar problem and this resolved it very nicely! 👍

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Aug 16, 2018

@mattblackdev I also had a similar problem, thanks so much for posting! Got close by combining stuff posted by @anned20, @dobromir-hristov, and others, but your solution solved the instanceof checks.

Ran into an issues when trying to use json fixtures (full details below), but that was easily solvable by just adding a function to get the blob from the fixture:

import path from 'path'

/**
 * Converts fixture to 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 (fixes issue invalid Blob issues).
 * @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' || path.extname(fileUrl) === '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 name = fileUrl.split('/').pop()
        const testFile = new win.File([blob], name, { type: blob.type })
        const dataTransfer = new win.DataTransfer()
        dataTransfer.items.add(testFile)
        const el = subject[0]
        el.files = dataTransfer.files
        return subject
      })
    })
  })
})

NOTE: Mostly Copied from @mattblackdev's and @dobromir-hristov's solutions with just a small separate case for JSON files

Full Error Details
The error was saying that the string was not correctly encoded/decode:

InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.

prescottprue commented Aug 16, 2018

@mattblackdev I also had a similar problem, thanks so much for posting! Got close by combining stuff posted by @anned20, @dobromir-hristov, and others, but your solution solved the instanceof checks.

Ran into an issues when trying to use json fixtures (full details below), but that was easily solvable by just adding a function to get the blob from the fixture:

import path from 'path'

/**
 * Converts fixture to 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 (fixes issue invalid Blob issues).
 * @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' || path.extname(fileUrl) === '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 name = fileUrl.split('/').pop()
        const testFile = new win.File([blob], name, { type: blob.type })
        const dataTransfer = new win.DataTransfer()
        dataTransfer.items.add(testFile)
        const el = subject[0]
        el.files = dataTransfer.files
        return subject
      })
    })
  })
})

NOTE: Mostly Copied from @mattblackdev's and @dobromir-hristov's solutions with just a small separate case for JSON files

Full Error Details
The error was saying that the string was not correctly encoded/decode:

InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.

@mattblackdev

This comment has been minimized.

Show comment
Hide comment
@mattblackdev

mattblackdev Aug 16, 2018

Awesome! Seems like we are slowly building up a general solution to this! It might even add up to a PR if the maintainers are interested. Or perhaps a separate package?

mattblackdev commented Aug 16, 2018

Awesome! Seems like we are slowly building up a general solution to this! It might even add up to a PR if the maintainers are interested. Or perhaps a separate package?

@Bkucera

This comment has been minimized.

Show comment
Hide comment
@Bkucera

Bkucera Aug 21, 2018

Member

@mattblackdev the problem is that the solution isn't cross-browser (except for ff and chrome), and to my knowledge isn't supported by a browser spec, so it could cease to work in any browser version release. However we are definitely implementing this with Native Browser events, which will be a cross browser and fully supported solution. Details here: #311

For now I would recommend one of the workarounds mentioned above - if you are comfortable with the instability of the solution

Member

Bkucera commented Aug 21, 2018

@mattblackdev the problem is that the solution isn't cross-browser (except for ff and chrome), and to my knowledge isn't supported by a browser spec, so it could cease to work in any browser version release. However we are definitely implementing this with Native Browser events, which will be a cross browser and fully supported solution. Details here: #311

For now I would recommend one of the workarounds mentioned above - if you are comfortable with the instability of the solution

@smoothdvd

This comment has been minimized.

Show comment
Hide comment
@smoothdvd

smoothdvd Sep 6, 2018

My code:

cy.fixture("logo.jpg").as("logo")
cy.get('input[type=file]').then(subject => {
  // From Cypress document: https://docs.cypress.io/api/utilities/blob.html#Examples  
  return Cypress.Blob.base64StringToBlob(this.logo, "image/jpeg").then((blob) => {
    const el = subject[0]
    const testFile = new File([blob], 'logo.jpg', { type: 'image/jpeg' })
    const dataTransfer = new DataTransfer()
    dataTransfer.items.add(testFile)
    el.files = dataTransfer.files
    })
  })

Thanks @fcurella

smoothdvd commented Sep 6, 2018

My code:

cy.fixture("logo.jpg").as("logo")
cy.get('input[type=file]').then(subject => {
  // From Cypress document: https://docs.cypress.io/api/utilities/blob.html#Examples  
  return Cypress.Blob.base64StringToBlob(this.logo, "image/jpeg").then((blob) => {
    const el = subject[0]
    const testFile = new File([blob], 'logo.jpg', { type: 'image/jpeg' })
    const dataTransfer = new DataTransfer()
    dataTransfer.items.add(testFile)
    el.files = dataTransfer.files
    })
  })

Thanks @fcurella

@kathar1223

This comment has been minimized.

Show comment
Hide comment
@kathar1223

kathar1223 Sep 23, 2018

This works for me. just call this method and replace the fileUrl with your file path and element selector 
uploadFile(){
   let fileUrl = "../fixtures/image37.png";
    let selector = '#imageLoader';
    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:'image/png'})
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
            return subject
          })
   })
  })
}

kathar1223 commented Sep 23, 2018

This works for me. just call this method and replace the fileUrl with your file path and element selector 
uploadFile(){
   let fileUrl = "../fixtures/image37.png";
    let selector = '#imageLoader';
    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:'image/png'})
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
            return subject
          })
   })
  })
}
@piotrpodyma10

This comment has been minimized.

Show comment
Hide comment
@piotrpodyma10

piotrpodyma10 Sep 26, 2018

When I wanted use a code:

cy.fixture("logo.jpg").as("logo")
cy.get('.stylecolor-images-container > :nth-child(1) > .dropzone').then(subject => {
     return Cypress.Blob.base64StringToBlob(this.logo, "image/jpeg").then((blob) => {
                   const el = subject[0]
                   const testFile = new File([blob], 'logo.jpg', { type: 'image/jpeg' })
                   const dataTransfer = new DataTransfer()
                   dataTransfer.items.add(testFile)
                   el.files = dataTransfer.files
       })
})

Then I got an error: TypeError: Cannot read property 'logo' of undefined.
When I gave cy.fixture("logo.jpg") into a const variable then I got an error: InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.

Similar situation I have when I used code from the documentation:

cy.fixture("images/logo.png").as("logo")
cy.get("input[type=file]").then(($input) => {
     // convert the logo base64 string to a blob
     return Cypress.Blob.base64StringToBlob(this.logo, "image/png").then((blob) => {
     // pass the blob to the fileupload jQuery plugin
     // used in your application's code
     // which initiates a programmatic upload
     $input.fileupload("add", { files: blob })
     })
})

Does anyone has a similar problems?

piotrpodyma10 commented Sep 26, 2018

When I wanted use a code:

cy.fixture("logo.jpg").as("logo")
cy.get('.stylecolor-images-container > :nth-child(1) > .dropzone').then(subject => {
     return Cypress.Blob.base64StringToBlob(this.logo, "image/jpeg").then((blob) => {
                   const el = subject[0]
                   const testFile = new File([blob], 'logo.jpg', { type: 'image/jpeg' })
                   const dataTransfer = new DataTransfer()
                   dataTransfer.items.add(testFile)
                   el.files = dataTransfer.files
       })
})

Then I got an error: TypeError: Cannot read property 'logo' of undefined.
When I gave cy.fixture("logo.jpg") into a const variable then I got an error: InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.

Similar situation I have when I used code from the documentation:

cy.fixture("images/logo.png").as("logo")
cy.get("input[type=file]").then(($input) => {
     // convert the logo base64 string to a blob
     return Cypress.Blob.base64StringToBlob(this.logo, "image/png").then((blob) => {
     // pass the blob to the fileupload jQuery plugin
     // used in your application's code
     // which initiates a programmatic upload
     $input.fileupload("add", { files: blob })
     })
})

Does anyone has a similar problems?

@kathar1223

This comment has been minimized.

Show comment
Hide comment
@kathar1223

kathar1223 Sep 26, 2018

This works for me. just call this method and replace the fileUrl with your file path and element selector 
uploadFile(){
   let fileUrl = "../fixtures/image37.png";
    let selector = '#imageLoader';
    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:'image/png'})
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
            return subject
          })
   })
  })
}

to support for all file types except JSON file, just keep the type value empty and in below comments added code to support JSON file
i.e in the above code replace this {type:'image/png'} with {type:''}

kathar1223 commented Sep 26, 2018

This works for me. just call this method and replace the fileUrl with your file path and element selector 
uploadFile(){
   let fileUrl = "../fixtures/image37.png";
    let selector = '#imageLoader';
    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:'image/png'})
            const dataTransfer = new DataTransfer()
            dataTransfer.items.add(testFile)
            el.files = dataTransfer.files
            return subject
          })
   })
  })
}

to support for all file types except JSON file, just keep the type value empty and in below comments added code to support JSON file
i.e in the above code replace this {type:'image/png'} with {type:''}

@abhilashshettigar

This comment has been minimized.

Show comment
Hide comment
@abhilashshettigar

abhilashshettigar Sep 27, 2018

@faktotum85 thanks for post It help me a lot

abhilashshettigar commented Sep 27, 2018

@faktotum85 thanks for post It help me a lot

@kathar1223

This comment has been minimized.

Show comment
Hide comment
@kathar1223

kathar1223 Oct 1, 2018

Used @simonpweller (@faktotum85) code and added few lines to support for JSON file and splitting of file to just get the filename

` newUpload() {
cy.visit("the url")
let fileName = "../fixtures/test.json";
let selector = '#upload-input';

    cy.get(selector).then(subject => {
        cy.fixture(fileName).then((logo) => {
            let json = JSON.stringify(logo);
            const el = subject[0];
            let objJsonB64 = Buffer.from(json).toString("base64");
            const blob = this.b64toBlob(objJsonB64);
            const nameSegments = fileName.split('/')
            const name = nameSegments[nameSegments.length - 1];
            const testFile = new File([blob], name);
            const dataTransfer = new DataTransfer();
            dataTransfer.items.add(testFile);
            el.files = dataTransfer.files;
        })
    });
}

b64toBlob(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;
    var byteCharacters = atob(b64Data);
    var byteArrays = [];
    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);
        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }
        var byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }
    var blob = new Blob(byteArrays, { type: contentType });
    blob.lastModifiedDate = new Date();
    return blob;
}

`

kathar1223 commented Oct 1, 2018

Used @simonpweller (@faktotum85) code and added few lines to support for JSON file and splitting of file to just get the filename

` newUpload() {
cy.visit("the url")
let fileName = "../fixtures/test.json";
let selector = '#upload-input';

    cy.get(selector).then(subject => {
        cy.fixture(fileName).then((logo) => {
            let json = JSON.stringify(logo);
            const el = subject[0];
            let objJsonB64 = Buffer.from(json).toString("base64");
            const blob = this.b64toBlob(objJsonB64);
            const nameSegments = fileName.split('/')
            const name = nameSegments[nameSegments.length - 1];
            const testFile = new File([blob], name);
            const dataTransfer = new DataTransfer();
            dataTransfer.items.add(testFile);
            el.files = dataTransfer.files;
        })
    });
}

b64toBlob(b64Data, contentType, sliceSize) {
    contentType = contentType || '';
    sliceSize = sliceSize || 512;
    var byteCharacters = atob(b64Data);
    var byteArrays = [];
    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);
        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }
        var byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }
    var blob = new Blob(byteArrays, { type: contentType });
    blob.lastModifiedDate = new Date();
    return blob;
}

`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment