abramenal / cypress-file-upload

File upload testing made easy
https://npm.im/cypress-file-upload
MIT License
498 stars 90 forks source link

[Feature] Folder upload #69

Closed artemgurzhii closed 5 years ago

artemgurzhii commented 5 years ago

Add the ability to upload folders.

We have this functionality in one of our apps. We take the path from the webkitRelativePath from the File API which can't be overridden.

This is working fix for it:

Cypress.Commands.add('uploadFolder', ({ path, fileName, folderName }) => {
  cy.window().then(window => {
    class FileCopy extends File {
      get webkitRelativePath() {
        return `${folderName}/${fileName}`;
      }
    }

    window.File = FileCopy;

    cy.fixture(path).then(fileContent => {
      cy.get('input[type="file"].d-none').upload(...args);
    });
  });
});
abramenal commented 5 years ago

Hi @artemgurzhii Thanks for submitting the issue!

I think it's not quite a common case, but let's think on it.

At first, you of course may read a folder with Node.js fs module and iterate through the files executing our upload command.

If not, how do you think the command design should look like? To me a folder often contain different file formats & extensions. Right now it's not possible to automate the mime type determination (but actually doable). At the same time the new API should not break the existing one but extend it seamlessly. Or, like you shared, be a separate command.

Regarding the command you shared – I'm not getting the API. In my understanding uploading a folder should only require 1 param – a folder name (or path). Any other things provided may confuse the end user (like fileName, wtf I'm uploading a folder).

So if we can form a correct proposal here I can take a look on how it affects the code base and then decide is it worth implementing or not. Happy to hear your thoughts.

artemgurzhii commented 5 years ago

@abramenal

At first, you of course may read a folder with Node.js fs module and iterate through the files executing our upload command.

It's not really our case, we have something similar to the dropbox or google drive functionality where when user uploads folder - folder is created with files in it.

https://github.com/abramenal/cypress-file-upload/blob/master/src/upload.js#L7

How about this implementation:

export default (subject, fileOrArray, { subjectType = 'input', force = false }) =>
  cy.window({ log: false }).then(async window => {
    const filesToProcess = Array.isArray(fileOrArray) ? fileOrArray : [fileOrArray];
    const OriginalFile = window.File;

    const processedFiles = await Cypress.Promise.all(
      filesToProcess.map(async ({ 
        fileContent, 
        fileName, 
        mimeType, 
        encoding, 
        folderPath = null 
      }) => {
        if (!fileName) {
          throw new InternalError(ERR_TYPES.MISSING_FILENAME);
        }

        const validEncoding = encoding || getValidEncoding(fileName);
        if (!validEncoding) {
          throw new InternalError(ERR_TYPES.MISSING_ENCODING);
        }

        if (folderPath) {
          class FileCopy extends window.File {
            get webkitRelativePath() {
              return `${folderPath}/${fileName}`;
            }
          }

          window.File = FileCopy;
        }

        const blob = await getFileBlobAsync({ fileContent, mimeType, encoding: validEncoding });
        const file =  new window.File([blob], fileName, { type: mimeType });

        window.File = OriginalFile;

        return file;
      }),
    );
  });

Usage/Api:

cy.fixture(fixturePath).then(fileContent => {
  cy.get('input[type="file"].d-none').upload(
    { fileContent, fileName, mimeType, folderPath: 'folder/name' },
    { subjectType: 'input' },
  );
});

P.S. This is mostly pseudo code and copy paste from our project, so I'm not sure if this will work, if you are ok with the following API, I can try to implement it and open a PR for it.

abramenal commented 5 years ago

Well, this is a hackish enough one, I would actually not overwrite your custom FileCopy but use it instead when creating a file: const file = new CustomFile([blob], fileName, { type: mimeType });

In this case I am fine with the pseudo implementation. One thing that needs to be adjusted is conditional logic based on whether we need this folder path.

I would be happy if you can try out coding that as well as testing on your case and then opening a PR. And of course then you'll appear on our honored Contributors list 😈

Please make sure referencing this issue in your PR so it'll automatically close. After that we'll think on updating the documentation for exposed API, for sure we need an extra example showing this ability (as it's non-trivial one), maybe even describing a business need under that (cloud file storage, like you mentioned), etc.

abramenal commented 5 years ago

@artemgurzhii is the issue still relevant so I can pay attention on designing and coding?

artemgurzhii commented 5 years ago

@abramenal sorry, totally forgot about this issue, yes, it's still relevant, we currently use this hack to accomplish it, but it would be great to have it as a built-in feature

Phillipe-Bojorquez commented 5 years ago

This is relevant for me as I need to test bulk upload for drag and drop for our web application. I'm just learning how to code and got stuck somewhere around here:

it.only("Uploads multiple files", () => {
        let uploadFiles = [
            {
                fileName: "test.pdf",
                encoding: "base64",
                mimeType: "application/pdf"
            },
            {
                fileName: "test.pptt",
                encoding: "base64",
                mimeType:
                    "application/vnd.openxmlformats-officedocument.presentationml.presentation"
            },
            {
                fileName: "test.docx",
                encoding: "base64",
                mimeType: "application/msword"
            }
        ];

        var newArray = [];
        uploadFiles.forEach(function(...element) {
            newArray.push(element);
            cy.fixture(element[0].fileName).then(fileContent => {
            cy.get(selectors.tableRow)
                .eq(2)
                .upload(newArray, { subjectType: "drag-n-drop" });
        });
        });

    });
abramenal commented 5 years ago

@Phillipe-Bojorquez hey there! If the issue is still relevant to you please feel free to open a new issue with your details so we could take a look together quickly.

@artemgurzhii I feel this feature obsolete as for now as no other users were requesting this as well. Feel free to open a PR with these changes if you still feel them reasonable so we could decide. Right now I will close the issue as "won't do" thing (at least for now). Thank you for understanding.

yslopezm commented 4 years ago

@abramenal do you have a solution? I am facing the same situation. Thanks in advance.

abramenal commented 4 years ago

@yslopezm not yet, this needs someone to contribute to since I'm not able to support with that change now

vishnuprabhu-95 commented 3 years ago

Can anyone help me with the folder upload. Please help with the same.

abramenal commented 3 years ago

@vishnuprabhu-95 this hasn't been implemented yet as of #288

Please follow #141 for further updates