Skip to content

Instantly share code, notes, and snippets.

@kerasai
Created June 28, 2017 20:52
Show Gist options
  • Save kerasai/d40ebaef6854b9938759a174327b4d23 to your computer and use it in GitHub Desktop.
Save kerasai/d40ebaef6854b9938759a174327b4d23 to your computer and use it in GitHub Desktop.
Behat context for working with files in Drupal 8.
<?php
use Behat\Gherkin\Node\PyStringNode;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Mink;
use Behat\MinkExtension\Context\MinkAwareContext;
/**
* Class FileContext.
*/
class FileContext implements MinkAwareContext {
/**
* Files created by this context.
*
* @var \Drupal\file\Entity\File[]
*/
protected $files = [];
/**
* Mink parameters.
*
* @var \Behat\Mink\Mink
*/
protected $mink;
/**
* Mink instance.
*
* @var array
*/
protected $minkParameters = [];
/**
* Working dir for the Behat process.
*
* @var string
*/
protected $workingDir;
/**
* Prepares test folders in the temporary directory.
*
* @BeforeScenario
*/
public function prepare() {
$this->workingDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . md5(microtime() * rand(0, 10000));
}
/**
* Prepares test folders in the temporary directory.
*
* @AfterScenario
*/
public function cleanup() {
foreach ($this->files as $file) {
$file->delete();
}
$this->files = [];
}
/**
* {@inheritdoc}
*/
public function setMink(Mink $mink) {
$this->mink = $mink;
}
/**
* {@inheritdoc}
*/
public function setMinkParameters(array $parameters) {
$this->minkParameters = $parameters;
}
/**
* Creates a file with specified name and context in current workdir.
*
* @Given /^(?:there is )?a file named "([^"]*)" with:$/
*
* @param string $filename
* Name of the file (relative path)
* @param PyStringNode $content
* File content.
*/
public function assertFileNamed($filename, PyStringNode $content) {
$content = strtr((string) $content, array("'''" => '"""'));
$this->createFile($this->workingDir . '/' . $filename, $content);
}
/**
* Create a file.
*
* @param string $filename
* Name of the file.
* @param string $content
* File content.
*/
private function createFile($filename, $content) {
$path = dirname($filename);
if (!is_dir($path)) {
mkdir($path, 0777, true);
}
file_put_contents($filename, $content);
}
/**
* Selects files for entity browser.
*
* @When I select :file entity for :field
* @When I select :file entity for :field with alt :alt
* @When I select :file entity for :field with description :desc
*/
public function assertEntityFromFile($file, $field, $alt = NULL, $desc = NULL) {
// Unfortunately ::findField does not seem to work on hidden inputs, so
// we'll look for the input by name.
$name = $field . '[target_id]';
$fieldEl = $this->mink->getSession()->getPage()->find('css', 'input[name="' . $name . '"]');
if (!$fieldEl) {
throw new \Exception(sprintf('Unable to find field "%s".', $field));
}
// Create an entity for the file.
$source = $this->workingDir . '/' . $file;
$dest = "public://behat/$file";
$dir = dirname($dest);
file_prepare_directory($dir, FILE_CREATE_DIRECTORY);
$file = file_save_data(file_get_contents($source), $dest, FILE_EXISTS_RENAME);
if (!$file) {
throw new \Exception(sprintf('Unable to save file "%s".', $dest));
}
$this->files[] = $file;
$file->save();
// Add the entity to the field value.
$value = array_filter(array_map('trim', explode(' ', $fieldEl->getValue())));
$value[] = "file:{$file->id()}";
$fieldEl->setValue(implode(' ', $value));
$weightName = $field . '[current][' . $file->id() .'][_weight]';
$weightEl = $this->mink->getSession()->getPage()->find('css', 'input[name="' . $weightName . '"]');
if ($weightEl) {
$weightEl->setValue(0);
}
else {
$this->createEntityBrowserInputNode($fieldEl, $weightName, 0);
}
// Create the "alt" input if specified.
if ($alt) {
$altName = $field . '[current][' . $file->id() .'][meta][alt]';
$altEl = $this->mink->getSession()->getPage()->find('css', 'input[name="' . $altName . '"]');
if ($altEl) {
$altEl->setValue($alt);
}
else {
$this->createEntityBrowserInputNode($fieldEl, $altName, $alt);
}
}
// Create the "desc" input if specified.
if ($desc) {
$descName = $field . '[current][' . $file->id() .'][meta][description]';
$descEl = $this->mink->getSession()->getPage()->find('css', 'input[name="' . $descName . '"]');
if ($descEl) {
$descEl->setValue($desc);
}
else {
$this->createEntityBrowserInputNode($fieldEl, $descName, $desc);
}
}
}
/**
* Inserts a DOMNode into the form to mimic entity browser input.
*
* Likely only works when utilizing the GoutteDriver.
*
* @param \Behat\Mink\Element\NodeElement $fieldEl
* The entity browser field element.
* @param string $inputName
* The name of the node.
* @param string $value
* The value to set on the node.
*/
protected function createEntityBrowserInputNode(NodeElement $fieldEl, $inputName, $value) {
/** @var \Behat\Mink\Driver\GoutteDriver $driver */
$driver = $this->mink->getSession()->getDriver();
$client = $driver->getClient();
$crawler = $client->getCrawler();
/** @var \DOMDocument $doc */
$doc = $crawler->getNode(0)->ownerDocument;
$node = $doc->createElement('input');
$node->setAttribute('type', 'hidden');
$node->setAttribute('name', $inputName);
$node->setAttribute('value', $value);
$query = new \DOMXPath($doc);
$result = $query->query($fieldEl->getXpath())->item(0);
$result->insertBefore($node);
}
/**
* Produces a Fill Murray placeholder image.
*
* @param string $size
* Size of the image in pixels, ie. "800 , 600".
*
* @Given there is a Fill Murray image :size
*/
public function assertFillMurray($size) {
$dimensions = array_map('trim', explode(',', $size));
$fileName = 'fm_' . implode('_', $dimensions) . '.jpg';
// Check temp space, to avoid re-downloading on subsequent scenarios.
$temp = '/tmp/behat/fm/' . $fileName;
if (!file_exists($temp)) {
$dir = dirname($temp);
if (!is_dir($dir)) {
@mkdir($dir, 0777, TRUE);
}
$url = 'https://www.fillmurray.com/' . implode('/', $dimensions);
$contents = file_get_contents($url);
file_put_contents($temp, $contents);
}
$contents = file_get_contents($temp);
$this->createFile($this->workingDir . '/' . $fileName, $contents);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment