Search code examples
phpsymfonyseleniumdockerbehat

Behat Mink file upload not finding file on submit


I'm trying to test an image upload using Selenium/Mink with Behat in a Symfony application. The application is running in a Docker container.

I'm attaching the file directly to the NodeElement of the input rather than use $driver->attachFileToField('#id-of-input', $filePath) because we're dealing with a lot of inputs in the context and already have the input in the method being called:

$input->attachFile($this->filesPath . '1.jpg');

The resulting path is:

/var/www/html/src/Resources/TestImages/1.jpg

This file certainly exists at that path in the docker container but when the form is submitted, this error is thrown:

Your file was not found

There are no helpful logs.

I've tried setting the files_path parameter in behat.yml but then I get error during the test running:

unknown error: path is not absolute: 3.jpg

Am I missing something? Is that file path incorrect for the container?

I've tried using the abs path on my machine to no avail (though this approach has serious drawbacks so I'm glad it wasn't the solution):

/Users/its.me/Sites/kbs/src/Resources/TestImages/1.jpg

The local Users dir is mounted into my docker-machine too so that abs path works on the host. I thought it might be permissions related so I set them all to read/write/execute but no cigar! Relative paths don't work.

Where are my images?


Solution

  • Based on the issue posted by @lauda at GitHub, the MinkSeleniumDriver needs some file preparation to work properly. Namely, turning it into a zip file. This comment helped:

    $localFile = $this->filesPath . '01.jpg';
    $tempZip = tempnam('', 'WebDriverZip');
    $zip = new \ZipArchive();
    $zip->open($tempZip, \ZipArchive::CREATE);
    $zip->addFile($localFile, basename($localFile));
    $zip->close();
    
    $remotePath = $this->getSession()->getDriver()->getWebDriverSession()->file([
        'file' => base64_encode(file_get_contents($tempZip))
    ]);
    
    $input->attachFile($remotePath);
    unlink($tempZip);
    

    The above code is based on the upload() method from facebook/php-webdriver:

     /**
       * Upload a local file to the server
       *
       * @param string $local_file
       *
       * @throws WebDriverException
       * @return string The remote path of the file.
       */
      private function upload($local_file) {
        if (!is_file($local_file)) {
          throw new WebDriverException("You may only upload files: " . $local_file);
        }
        // Create a temporary file in the system temp directory.
        $temp_zip = tempnam('', 'WebDriverZip');
        $zip = new ZipArchive();
        if ($zip->open($temp_zip, ZipArchive::CREATE) !== true) {
          return false;
        }
        $info = pathinfo($local_file);
        $file_name = $info['basename'];
        $zip->addFile($local_file, $file_name);
        $zip->close();
        $params = array(
          'file' => base64_encode(file_get_contents($temp_zip)),
        );
        $remote_path = $this->executor->execute(
          DriverCommand::UPLOAD_FILE,
          $params
        );
        unlink($temp_zip);
        return $remote_path;
      }