0
votes

I'm using Cypress.io to automate one file upload test case based on a react page. The input component(type=file) for file upload is created during runtime when the page is rendered.

Seems the button (by clicking the 'Choose file') opens a native file picker, which cypress Webdriver doesn't seem to support interacting with, so probably trigger an event to simulate file selection can be an option in this case. But the input(type=file) can't be located by Cypress because it is not a part of DOM, which means cy.get('input[type=file]') returns null.

Could you please give me some thoughts how to do it? this button opens a native file picker

I've tried with this -

const testfile = new File(['test data to upload'], 'upload.csv')

cy.get('input[type=file]').trigger('change', {
  force: true,
  data: testfile,
});

this brings no luck,because of CypressError: Timed out retrying: Expected to find element: 'input[type=file]', but never found it.

The source code of the page:

import React, { Component } from 'react'

interface Props {
  text?: string
  type?: string | undefined
  fileID?: string
  onFileSelected: (file: any) => void
}

interface State {
  name: string
}

export default class FileUpload extends Component<Props, State> {
  fileSelector = document.createElement('input')

  state: State = {
    name: '',
  }

  componentDidMount() {
    this.fileSelector = this.buildFileSelector()
  }

  buildFileSelector = () => {
    const { fileID, type } = this.props

    this.fileSelector.setAttribute('type', 'file')
    this.fileSelector.setAttribute('id', fileID || 'file')
    this.fileSelector.setAttribute('multiple', 'multiple')
    this.setAcceptType(type)
    this.fileSelector.onchange = this.handleFileChange
    return this.fileSelector
  }

  setAcceptType = (type: string | undefined) => {
    if (type) {
      type = type[0] === '.' ? type : type.replace(/^/, '.')
      this.fileSelector.setAttribute('accept', type)
    }
  }

  handleFileChange = (event: any) => {
    const file = event.target.files[0]
    if (file) {
      this.setState({ name: file.name })
      this.props.onFileSelected(file)
    }
  }

  render() {
    const { name } = this.state
    return (
      <div>
        <button
          onClick={(event: React.ChangeEvent<any>) => {
            event.preventDefault()
            this.fileSelector.click()
          }}
          style={{ marginRight: 10 }}
        >
          {this.props.text || 'Choose file'}
        </button>
        <label>{name || 'No file chosen'}</label>
      </div>
    )
  }
}

I look forward to receiving suggestions how to automate this 'choose file' action in Cypress. Thanks in advance.

1
I think the problem here is that the fileSelector is NOT in the DOM tree as it is only created by document.createElement('input'), but not really attached to to DOM tree. - Leo

1 Answers

0
votes

I sorted out this issue by putting an input(type=file) element into the DOM, so that cypress can locate the element and manipulate it.

But regarding the issue I had before I still would like to hear some insights from you if this is still possible to be handled in cypress.