diff --git a/src/Palantirnet/PalantirBehatExtension/Context/DrupalFileContext.php b/src/Palantirnet/PalantirBehatExtension/Context/DrupalFileContext.php index 98ebdac..f6c9303 100644 --- a/src/Palantirnet/PalantirBehatExtension/Context/DrupalFileContext.php +++ b/src/Palantirnet/PalantirBehatExtension/Context/DrupalFileContext.php @@ -8,8 +8,11 @@ namespace Palantirnet\PalantirBehatExtension\Context; use Behat\Gherkin\Node\TableNode; -use Palantirnet\PalantirBehatExtension\NotUpdatedException; +use Behat\Mink\Element\NodeElement; +use Drupal; use Drupal\file\Entity\File; +use Drupal\DrupalExtension\Hook\Scope\EntityScope; +use PHPUnit_Framework_Assert as Assert; /** * Behat context class with additional file-related steps. @@ -19,60 +22,216 @@ class DrupalFileContext extends SharedDrupalContext /** - * Create a Drupal file record. + * Register fields which hold files so we can grab the fid on node save. * - * @Given the file :filename + * @var array File fields used in this context. + */ + protected $fileFields = []; + + /** + * Keep track of files so they can be cleaned up. * - * @param string $filename The name of a file within the MinkExtension's files_path directory. - * @param int $status FILE_STATUS_PERMANENT or 0 if the file is temporary. Defaults to FILE_STATUS_PERMANENT. + * @var array + */ + protected $files = []; + + /** + * Get the files managed in this context. + * @return array + */ + public function getFiles() + { + return $this->files; + }//end getFiles() + + + /** + * Add a file to manage. * - * @return void + * @param string $filename The name of the file to manage. + * @param File $file The file to manage. + * + * @return $this */ - public function createFile($filename, $status = FILE_STATUS_PERMANENT) + public function addFile($filename, File $file) { - $file = new File(array(), 'file'); - $file->setFilename($filename); - $file->set('status', $status); + $this->files[$filename] = $file->id(); + + return $this; + + }//end addFile() + + /** + * Add a file based on file id. Looks up the filename if not passed. + * + * @param int $fid The file id. + * @param string $filename [optional] The name of the file. + * + * @return $this + */ + public function addFileById($fid, $filename = '') + { - $file = $this->expandFile($file); + $filename = $filename ?: File::load($fid)->getFilename(); + $this->files[$filename] = $fid; - $this->fileCreate($file); + return $this; - }//end createFile() + }//end addFileById() /** - * Create a set of Drupal file records. + * Get a specific file from the managed files. * - * @Given files: + * @param String $filename Original name of the file to get from behat step. * - * Given files: - * | filename | status | author | - * | example.pdf | 1 | Somebody | - * | test.png | 0 | Admin | - * | ... | ... | ... | + * @return File + */ + public function getFile($filename) + { + return File::load($this->files[$filename]); + }//end getFile() + + + /** + * Get the fields which may hold files. * - * @param TableNode $filesTable A hash of file property objects. + * @return array + */ + public function getFileFields() + { + return $this->fileFields; + }//end getFileFields() + + + /** + * Add a field which may hold files. * - * @return void + * @param String $filename Original name of the file to get from behat step. + * @param String $fileField The field machine name. + * + * @return DrupalFileContext $this */ - public function createFiles(TableNode $filesTable) + public function addFileField($filename, $fileField) { + $this->fileFields[$filename] = $fileField; + return $this; + }//end addFileField() - foreach ($filesTable->getHash() as $fileHash) { - $file = new File(array(), 'file'); - $file->setFilename($fileHash['filename']); - $status = isset($fileHash['status']) ?: FILE_STATUS_PERMANENT; - $file->set('status', $status); + /** + * Replace file fields in an entity. + * + * @beforeNodeCreate + * + * @param EntityScope $scope The BeforeNodeCreateScope for this hook. + * + * @return \stdClass + */ + public function replaceFiles(EntityScope $scope) + { + $fields = (array) $scope->getEntity(); + $node = $scope->getEntity(); + $prefix = 'file: {'; + foreach ($fields as $fieldname => $file) { + if (TRUE === is_string($file) && 0 === strpos($file, $prefix)) { + $filename = substr($file, strlen($prefix), strlen($file) - strlen($prefix) - 1); + $this->addFileField($filename, $fieldname); + $node->$fieldname = rtrim(realpath($this->getMinkParameter('files_path')), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$filename; + } + } + }//end replaceFiles() + - $file = $this->expandFile($file); + /** + * Register files created on node create so we can delete them later. + * + * @afterNodeCreate + * + * @param EntityScope $scope The AfterNodeCreateScope of this hook. + * + * @return null + */ + public function registerFiles(EntityScope $scope) + { - $this->fileCreate($file); + $node = $scope->getEntity(); + foreach ($this->getFileFields() as $filename => $field) { + $value = $node->{$field}; + $this->addFileById($value['target_id'], $filename); } - }//end createFiles() + }//end registerFiles() + + + /** + * Assert an image is displayed on the page. + * + * @Then I should see the image :filename + * + * @param String $filename The filename of the image we expect to find. + * + * @return bool + * + * @throws \Exception + */ + public function assertImage($filename) + { + + // @var File $file + $file = $this->getFile($filename); + $filename = $file->getFilename(); + + $imageElements = $this->getSession()->getPage()->findAll('css', 'img'); + $found = false; + + // @var NodeElement $image + foreach ($imageElements as $image) { + $imageUrl = $image->getAttribute('src'); + $imageFilename = basename(parse_url($imageUrl, PHP_URL_PATH)); + + + if ($imageFilename === $filename) { + $found = true; + $this->getSession()->visit($imageUrl); + $statusCode = $this->getSession()->getStatusCode(); + $message = "Expected to find the image, $filename at $imageUrl. Got status code: {$statusCode}."; + Assert::assertEquals("200", $statusCode, $message); + } + } + + Assert::assertTrue($found, "Could not find image: $filename"); + }//end assertImage() + + + + /** + * Remove any created files. + * + * @AfterScenario + * + * @return void + */ + public function cleanFiles() + { + $files = array_map( + function ($filename) { + return $this->getFile($filename); + }, + array_keys($this->files) + ); + + $manager = Drupal::entityTypeManager()->getStorage('file'); + @$manager->delete($files); + + foreach ($files as $filename => $file) { + $file->delete(); + } + + $this->files = []; + + }//end cleanFiles() }//end class diff --git a/src/Palantirnet/PalantirBehatExtension/Context/DrupalSetupContext.php b/src/Palantirnet/PalantirBehatExtension/Context/DrupalSetupContext.php index 504ad20..6ef10b1 100644 --- a/src/Palantirnet/PalantirBehatExtension/Context/DrupalSetupContext.php +++ b/src/Palantirnet/PalantirBehatExtension/Context/DrupalSetupContext.php @@ -25,7 +25,6 @@ class DrupalSetupContext extends SharedDrupalContext */ public function assertDrupal() { - throw new NotUpdatedException(); if ($this->getDriver()->isBootstrapped() === false) { throw new \Exception('The Drupal site is not bootstrapped.'); diff --git a/src/Palantirnet/PalantirBehatExtension/Context/SharedDrupalContext.php b/src/Palantirnet/PalantirBehatExtension/Context/SharedDrupalContext.php index f9b4fd5..10aba81 100644 --- a/src/Palantirnet/PalantirBehatExtension/Context/SharedDrupalContext.php +++ b/src/Palantirnet/PalantirBehatExtension/Context/SharedDrupalContext.php @@ -13,9 +13,11 @@ namespace Palantirnet\PalantirBehatExtension\Context; +use Behat\Gherkin\Node\TableNode; use Drupal\DrupalExtension\Context\RawDrupalContext; -use Palantirnet\PalantirBehatExtension\NotUpdatedException; +use Drupal\DrupalExtension\Hook\Scope\EntityScope; use Drupal\file\Entity\File; +use Palantirnet\PalantirBehatExtension\NotUpdatedException; /** * Behat context class with functionality that is shared across custom contexts. @@ -153,110 +155,4 @@ public function findUserByName($userName) }//end findUserByName() - /** - * Save a file. - * - * @param stdclass $file A simple object representing file data. - * Properties should be scalar values, and files may use either the 'uid' or - * 'author' fields to attribute the file to a particular Drupal user. - * - * @return stdclass - * A Drupal file object. - */ - public function fileCreate($file) - { - // Save the file and overwrite if it already exists. - $dest = file_build_uri(drupal_basename($file->GetFileUri())); - $result = file_copy($file, $dest, FILE_EXISTS_REPLACE); - - // Stash the file object for later cleanup. - if (empty($result->id()) === false) { - $this->files[] = $result; - } else { - throw new \Exception(sprintf('File "%s" could not be copied from "%s" to "%s".', $file->getFilename(), $file->GetFileUri(), $result->GetFileUri())); - } - - return $result; - - }//end fileCreate() - - - /** - * Add required file properties. - * - * @param stdclass $file A simple object representing file data. The 'filename' - * property is required. - * - * @return stdclass - * A file object with at least the filename, uri, uid, and status - * properties. - */ - public function expandFile($file) - { - if (empty($file->getFilename()) === true) { - throw new \Exception("Can't create file with no source filename; this should be the name of a file within the MinkExtension's files_path directory."); - } - - // Set the URI to the path to the file within the MinkExtension's - // files_path parameter. - $file->setFileUri(rtrim(realpath($this->getMinkParameter('files_path')), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file->getFilename()); - - $file->set('langcode', $file->language()->getId()); - - $file->setChangedTime(time()); - - // Assign authorship if none exists and `author` is passed. - /* - if (isset($file->getOwnerId()) === false && empty($file->author) === false) { - $account = user_load_by_name($file->author); - if ($account !== false) { - $file->uid = $account->uid; - } - } - - Add default values. - $defaults = array( - 'uid' => 0, - 'status' => 1, - ); - - foreach ($defaults as $key => $default) { - if (isset($file->$key) === false) { - $file->$key = $default; - } - } */ - - return $file; - - }//end expandFile() - - - /** - * Keep track of files so they can be cleaned up. - * - * @var array - */ - protected $files = array(); - - - /** - * Remove any created files. - * - * @AfterScenario - * - * @return void - */ - public function cleanFiles() - { - throw new NotUpdatedException(); - - foreach ($this->files as $file) { - file_delete($file, true); - } - - $this->files = array(); - - }//end cleanFiles() - - }//end class