Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -0,0 +1,28 @@
|
|||
langcode: en
|
||||
status: true
|
||||
name: test_scale_and_crop_add_anchor
|
||||
label: test_scale_and_crop_add_anchor
|
||||
effects:
|
||||
8c7170c9-5bcc-40f9-8698-f88a8be6d434:
|
||||
uuid: 8c7170c9-5bcc-40f9-8698-f88a8be6d434
|
||||
id: image_scale_and_crop
|
||||
weight: 1
|
||||
data:
|
||||
width: 100
|
||||
height: 100
|
||||
a8d83b12-abc6-40c8-9c2f-78a4e421cf97:
|
||||
uuid: a8d83b12-abc6-40c8-9c2f-78a4e421cf97
|
||||
id: image_scale_and_crop
|
||||
weight: 2
|
||||
data:
|
||||
width: 100
|
||||
height: 100
|
||||
anchor: left-top
|
||||
1bffd475-19d0-439a-b6a1-7e5850ce40f9:
|
||||
uuid: 1bffd475-19d0-439a-b6a1-7e5850ce40f9
|
||||
id: image_rotate
|
||||
weight: 3
|
||||
data:
|
||||
degrees: 180
|
||||
bgcolor: ''
|
||||
random: false
|
19
web/core/modules/image/tests/fixtures/update/test_scale_and_crop_add_anchor.php
vendored
Normal file
19
web/core/modules/image/tests/fixtures/update/test_scale_and_crop_add_anchor.php
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test fixture.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
$connection->insert('config')
|
||||
->fields([
|
||||
'collection' => '',
|
||||
'name' => 'image.style.test_scale_and_crop_add_anchor',
|
||||
'data' => serialize(Yaml::decode(file_get_contents('core/modules/image/tests/fixtures/update/image.image_style.test_scale_and_crop_add_anchor.yml'))),
|
||||
])
|
||||
->execute();
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FormatterBase;
|
||||
|
||||
/**
|
||||
* Empty renderer for a dummy field with an AJAX handler.
|
||||
*
|
||||
* @FieldFormatter(
|
||||
* id = "image_module_test_dummy_ajax_formatter",
|
||||
* module = "image_module_test",
|
||||
* label = @Translation("Dummy AJAX"),
|
||||
* field_types= {
|
||||
* "image_module_test_dummy_ajax"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class DummyAjaxFormatter extends FormatterBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
$summary = [];
|
||||
$summary[] = t('Renders nothing');
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$element = [];
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
|
||||
/**
|
||||
* Defines a dummy field containing an AJAX handler.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "image_module_test_dummy_ajax",
|
||||
* label = @Translation("Dummy AJAX"),
|
||||
* description = @Translation("A field containing an AJAX handler."),
|
||||
* category = @Translation("Field"),
|
||||
* default_widget = "image_module_test_dummy_ajax_widget",
|
||||
* default_formatter = "image_module_test_dummy_ajax_formatter"
|
||||
* )
|
||||
*/
|
||||
class DummyAjaxItem extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
return [
|
||||
'columns' => [
|
||||
'value' => [
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
return empty($this->get('value')->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(new TranslatableMarkup('Dummy string value'));
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Default widget for Dummy AJAX test.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "image_module_test_dummy_ajax_widget",
|
||||
* label = @Translation("Dummy AJAX widget"),
|
||||
* field_types = {
|
||||
* "image_module_test_dummy_ajax"
|
||||
* },
|
||||
* multiple_values = TRUE,
|
||||
* )
|
||||
*/
|
||||
class DummyAjaxWidget extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element['select_widget'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Dummy select'),
|
||||
'#options' => ['pow' => 'Pow!', 'bam' => 'Bam!'],
|
||||
'#required' => TRUE,
|
||||
'#ajax' => [
|
||||
'callback' => get_called_class() . '::dummyAjaxCallback',
|
||||
'effect' => 'fade',
|
||||
],
|
||||
];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax callback for Dummy AJAX test.
|
||||
*
|
||||
* @param array $form
|
||||
* The build form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return \Drupal\Core\Ajax\AjaxResponse
|
||||
* Ajax response.
|
||||
*/
|
||||
public static function dummyAjaxCallback(array &$form, FormStateInterface $form_state) {
|
||||
return new AjaxResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -5,5 +5,5 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- image
|
||||
- views
|
||||
- drupal:image
|
||||
- drupal:views
|
||||
|
|
58
web/core/modules/image/tests/src/Functional/FileMoveTest.php
Normal file
58
web/core/modules/image/tests/src/Functional/FileMoveTest.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the file move function for images and image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class FileMoveTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* Tests moving a randomly generated image.
|
||||
*/
|
||||
public function testNormal() {
|
||||
// Pick a file for testing.
|
||||
$file = File::create((array) current($this->drupalGetTestFiles('image')));
|
||||
|
||||
// Create derivative image.
|
||||
$styles = ImageStyle::loadMultiple();
|
||||
$style = reset($styles);
|
||||
$original_uri = $file->getFileUri();
|
||||
$derivative_uri = $style->buildUri($original_uri);
|
||||
$style->createDerivative($original_uri, $derivative_uri);
|
||||
|
||||
// Check if derivative image exists.
|
||||
$this->assertTrue(file_exists($derivative_uri), 'Make sure derivative image is generated successfully.');
|
||||
|
||||
// Clone the object so we don't have to worry about the function changing
|
||||
// our reference copy.
|
||||
$desired_filepath = 'public://' . $this->randomMachineName();
|
||||
$result = file_move(clone $file, $desired_filepath, FILE_EXISTS_ERROR);
|
||||
|
||||
// Check if image has been moved.
|
||||
$this->assertTrue(file_exists($result->getFileUri()), 'Make sure image is moved successfully.');
|
||||
|
||||
// Check if derivative image has been flushed.
|
||||
$this->assertFalse(file_exists($derivative_uri), 'Make sure derivative image has been flushed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\image\Functional\Rest\ImageStyleResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ImageStyleHalJsonAnonTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\image\Functional\Rest\ImageStyleResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ImageStyleHalJsonBasicAuthTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\image\Functional\Rest\ImageStyleResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ImageStyleHalJsonCookieTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,506 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests creation, deletion, and editing of image styles and effects.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an image style, generate an image.
|
||||
*/
|
||||
public function createSampleImage(ImageStyleInterface $style) {
|
||||
static $file_path;
|
||||
|
||||
// First, we need to make sure we have an image in our testing
|
||||
// file directory. Copy over an image on the first run.
|
||||
if (!isset($file_path)) {
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = reset($files);
|
||||
$file_path = file_unmanaged_copy($file->uri);
|
||||
}
|
||||
|
||||
return $style->buildUrl($file_path) ? $file_path : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of images currently create for a style.
|
||||
*/
|
||||
public function getImageCount(ImageStyleInterface $style) {
|
||||
return count(file_scan_directory('public://styles/' . $style->id(), '/.*/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating an image style with a numeric name and ensuring it can be
|
||||
* applied to an image.
|
||||
*/
|
||||
public function testNumericStyleName() {
|
||||
$style_name = rand();
|
||||
$style_label = $this->randomString();
|
||||
$edit = [
|
||||
'name' => $style_name,
|
||||
'label' => $style_label,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/media/image-styles/add', $edit, t('Create new style'));
|
||||
$this->assertRaw(t('Style %name was created.', ['%name' => $style_label]));
|
||||
$options = image_style_options();
|
||||
$this->assertTrue(array_key_exists($style_name, $options), format_string('Array key %key exists.', ['%key' => $style_name]));
|
||||
}
|
||||
|
||||
/**
|
||||
* General test to add a style, add/remove/edit effects to it, then delete it.
|
||||
*/
|
||||
public function testStyle() {
|
||||
$admin_path = 'admin/config/media/image-styles';
|
||||
|
||||
// Setup a style to be created and effects to add to it.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style_path = $admin_path . '/manage/' . $style_name;
|
||||
$effect_edits = [
|
||||
'image_resize' => [
|
||||
'width' => 100,
|
||||
'height' => 101,
|
||||
],
|
||||
'image_scale' => [
|
||||
'width' => 110,
|
||||
'height' => 111,
|
||||
'upscale' => 1,
|
||||
],
|
||||
'image_scale_and_crop' => [
|
||||
'width' => 120,
|
||||
'height' => 121,
|
||||
],
|
||||
'image_crop' => [
|
||||
'width' => 130,
|
||||
'height' => 131,
|
||||
'anchor' => 'left-top',
|
||||
],
|
||||
'image_desaturate' => [
|
||||
// No options for desaturate.
|
||||
],
|
||||
'image_rotate' => [
|
||||
'degrees' => 5,
|
||||
'random' => 1,
|
||||
'bgcolor' => '#FFFF00',
|
||||
],
|
||||
];
|
||||
|
||||
// Add style form.
|
||||
|
||||
$edit = [
|
||||
'name' => $style_name,
|
||||
'label' => $style_label,
|
||||
];
|
||||
$this->drupalPostForm($admin_path . '/add', $edit, t('Create new style'));
|
||||
$this->assertRaw(t('Style %name was created.', ['%name' => $style_label]));
|
||||
|
||||
// Ensure that the expected entity operations are there.
|
||||
$this->drupalGet($admin_path);
|
||||
$this->assertLinkByHref($style_path);
|
||||
$this->assertLinkByHref($style_path . '/flush');
|
||||
$this->assertLinkByHref($style_path . '/delete');
|
||||
|
||||
// Add effect form.
|
||||
|
||||
// Add each sample effect to the style.
|
||||
foreach ($effect_edits as $effect => $edit) {
|
||||
$edit_data = [];
|
||||
foreach ($edit as $field => $value) {
|
||||
$edit_data['data[' . $field . ']'] = $value;
|
||||
}
|
||||
// Add the effect.
|
||||
$this->drupalPostForm($style_path, ['new' => $effect], t('Add'));
|
||||
if (!empty($edit)) {
|
||||
$this->drupalPostForm(NULL, $edit_data, t('Add effect'));
|
||||
}
|
||||
}
|
||||
|
||||
// Load the saved image style.
|
||||
$style = ImageStyle::load($style_name);
|
||||
|
||||
// Ensure that third party settings were added to the config entity.
|
||||
// These are added by a hook_image_style_presave() implemented in
|
||||
// image_module_test module.
|
||||
$this->assertEqual('bar', $style->getThirdPartySetting('image_module_test', 'foo'), 'Third party settings were added to the image style.');
|
||||
|
||||
// Ensure that the image style URI matches our expected path.
|
||||
$style_uri_path = $style->url();
|
||||
$this->assertTrue(strpos($style_uri_path, $style_path) !== FALSE, 'The image style URI is correct.');
|
||||
|
||||
// Confirm that all effects on the image style have settings that match
|
||||
// what was saved.
|
||||
$uuids = [];
|
||||
foreach ($style->getEffects() as $uuid => $effect) {
|
||||
// Store the uuid for later use.
|
||||
$uuids[$effect->getPluginId()] = $uuid;
|
||||
$effect_configuration = $effect->getConfiguration();
|
||||
foreach ($effect_edits[$effect->getPluginId()] as $field => $value) {
|
||||
$this->assertEqual($value, $effect_configuration['data'][$field], new FormattableMarkup('The %field field in the %effect effect has the correct value of %value.', ['%field' => $field, '%effect' => $effect->getPluginId(), '%value' => $value]));
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that every effect was saved.
|
||||
foreach (array_keys($effect_edits) as $effect_name) {
|
||||
$this->assertTrue(isset($uuids[$effect_name]), format_string(
|
||||
'A %effect_name effect was saved with ID %uuid',
|
||||
[
|
||||
'%effect_name' => $effect_name,
|
||||
'%uuid' => $uuids[$effect_name],
|
||||
]));
|
||||
}
|
||||
|
||||
// Image style overview form (ordering and renaming).
|
||||
|
||||
// Confirm the order of effects is maintained according to the order we
|
||||
// added the fields.
|
||||
$effect_edits_order = array_keys($effect_edits);
|
||||
$order_correct = TRUE;
|
||||
$index = 0;
|
||||
foreach ($style->getEffects() as $effect) {
|
||||
if ($effect_edits_order[$index] != $effect->getPluginId()) {
|
||||
$order_correct = FALSE;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
$this->assertTrue($order_correct, 'The order of the effects is correctly set by default.');
|
||||
|
||||
// Test the style overview form.
|
||||
// Change the name of the style and adjust the weights of effects.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomMachineName();
|
||||
$weight = count($effect_edits);
|
||||
$edit = [
|
||||
'name' => $style_name,
|
||||
'label' => $style_label,
|
||||
];
|
||||
foreach ($style->getEffects() as $uuid => $effect) {
|
||||
$edit['effects[' . $uuid . '][weight]'] = $weight;
|
||||
$weight--;
|
||||
}
|
||||
|
||||
// Create an image to make sure it gets flushed after saving.
|
||||
$image_path = $this->createSampleImage($style);
|
||||
$this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', ['%style' => $style->label(), '%file' => $image_path]));
|
||||
|
||||
$this->drupalPostForm($style_path, $edit, t('Save'));
|
||||
|
||||
// Note that after changing the style name, the style path is changed.
|
||||
$style_path = 'admin/config/media/image-styles/manage/' . $style_name;
|
||||
|
||||
// Check that the URL was updated.
|
||||
$this->drupalGet($style_path);
|
||||
$this->assertTitle(t('Edit style @name | Drupal', ['@name' => $style_label]));
|
||||
$this->assertResponse(200, format_string('Image style %original renamed to %new', ['%original' => $style->id(), '%new' => $style_name]));
|
||||
|
||||
// Check that the available image effects are properly sorted.
|
||||
$option = $this->xpath('//select[@id=:id]//option', [':id' => 'edit-new--2']);
|
||||
$this->assertEquals('Ajax test', $option[1]->getText(), '"Ajax test" is the first selectable effect.');
|
||||
|
||||
// Check that the image was flushed after updating the style.
|
||||
// This is especially important when renaming the style. Make sure that
|
||||
// the old image directory has been deleted.
|
||||
$this->assertEqual($this->getImageCount($style), 0, format_string('Image style %style was flushed after renaming the style and updating the order of effects.', ['%style' => $style->label()]));
|
||||
|
||||
// Load the style by the new name with the new weights.
|
||||
$style = ImageStyle::load($style_name);
|
||||
|
||||
// Confirm the new style order was saved.
|
||||
$effect_edits_order = array_reverse($effect_edits_order);
|
||||
$order_correct = TRUE;
|
||||
$index = 0;
|
||||
foreach ($style->getEffects() as $effect) {
|
||||
if ($effect_edits_order[$index] != $effect->getPluginId()) {
|
||||
$order_correct = FALSE;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
$this->assertTrue($order_correct, 'The order of the effects is correctly set by default.');
|
||||
|
||||
// Image effect deletion form.
|
||||
|
||||
// Create an image to make sure it gets flushed after deleting an effect.
|
||||
$image_path = $this->createSampleImage($style);
|
||||
$this->assertEqual($this->getImageCount($style), 1, format_string('Image style %style image %file successfully generated.', ['%style' => $style->label(), '%file' => $image_path]));
|
||||
|
||||
// Delete the 'image_crop' effect from the style.
|
||||
$this->drupalPostForm($style_path . '/effects/' . $uuids['image_crop'] . '/delete', [], t('Delete'));
|
||||
// Confirm that the form submission was successful.
|
||||
$this->assertResponse(200);
|
||||
$image_crop_effect = $style->getEffect($uuids['image_crop']);
|
||||
$this->assertRaw(t('The image effect %name has been deleted.', ['%name' => $image_crop_effect->label()]));
|
||||
// Confirm that there is no longer a link to the effect.
|
||||
$this->assertNoLinkByHref($style_path . '/effects/' . $uuids['image_crop'] . '/delete');
|
||||
// Refresh the image style information and verify that the effect was
|
||||
// actually deleted.
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$style = $entity_type_manager->getStorage('image_style')->loadUnchanged($style->id());
|
||||
$this->assertFalse($style->getEffects()->has($uuids['image_crop']), format_string(
|
||||
'Effect with ID %uuid no longer found on image style %style',
|
||||
[
|
||||
'%uuid' => $uuids['image_crop'],
|
||||
'%style' => $style->label(),
|
||||
]));
|
||||
|
||||
// Additional test on Rotate effect, for transparent background.
|
||||
$edit = [
|
||||
'data[degrees]' => 5,
|
||||
'data[random]' => 0,
|
||||
'data[bgcolor]' => '',
|
||||
];
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_rotate'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Add effect'));
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$style = $entity_type_manager->getStorage('image_style')->loadUnchanged($style_name);
|
||||
$this->assertEqual(count($style->getEffects()), 6, 'Rotate effect with transparent background was added.');
|
||||
|
||||
// Style deletion form.
|
||||
|
||||
// Delete the style.
|
||||
$this->drupalPostForm($style_path . '/delete', [], t('Delete'));
|
||||
|
||||
// Confirm the style directory has been removed.
|
||||
$directory = file_default_scheme() . '://styles/' . $style_name;
|
||||
$this->assertFalse(is_dir($directory), format_string('Image style %style directory removed on style deletion.', ['%style' => $style->label()]));
|
||||
|
||||
$this->assertFalse(ImageStyle::load($style_name), format_string('Image style %style successfully deleted.', ['%style' => $style->label()]));
|
||||
|
||||
// Test empty text when there are no image styles.
|
||||
|
||||
// Delete all image styles.
|
||||
foreach (ImageStyle::loadMultiple() as $image_style) {
|
||||
$image_style->delete();
|
||||
}
|
||||
|
||||
// Confirm that the empty text is correct on the image styles page.
|
||||
$this->drupalGet($admin_path);
|
||||
$this->assertRaw(t('There are currently no styles. <a href=":url">Add a new one</a>.', [
|
||||
':url' => \Drupal::url('image.style_add'),
|
||||
]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deleting a style and choosing a replacement style.
|
||||
*/
|
||||
public function testStyleReplacement() {
|
||||
// Create a new style.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style = ImageStyle::create(['name' => $style_name, 'label' => $style_label]);
|
||||
$style->save();
|
||||
$style_path = 'admin/config/media/image-styles/manage/';
|
||||
|
||||
// Create an image field that uses the new style.
|
||||
$field_name = strtolower($this->randomMachineName(10));
|
||||
$this->createImageField($field_name, 'article');
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($field_name, [
|
||||
'type' => 'image',
|
||||
'settings' => ['image_style' => $style_name],
|
||||
])
|
||||
->save();
|
||||
|
||||
// Create a new node with an image attached.
|
||||
$test_image = current($this->drupalGetTestFiles('image'));
|
||||
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $this->randomMachineName());
|
||||
$node = Node::load($nid);
|
||||
|
||||
// Get node field original image URI.
|
||||
$fid = $node->get($field_name)->target_id;
|
||||
$original_uri = File::load($fid)->getFileUri();
|
||||
|
||||
// Test that image is displayed using newly created style.
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), format_string('Image displayed using style @style.', ['@style' => $style_name]));
|
||||
|
||||
// Rename the style and make sure the image field is updated.
|
||||
$new_style_name = strtolower($this->randomMachineName(10));
|
||||
$new_style_label = $this->randomString();
|
||||
$edit = [
|
||||
'name' => $new_style_name,
|
||||
'label' => $new_style_label,
|
||||
];
|
||||
$this->drupalPostForm($style_path . $style_name, $edit, t('Save'));
|
||||
$this->assertText(t('Changes to the style have been saved.'), format_string('Style %name was renamed to %new_name.', ['%name' => $style_name, '%new_name' => $new_style_name]));
|
||||
$this->drupalGet('node/' . $nid);
|
||||
|
||||
// Reload the image style using the new name.
|
||||
$style = ImageStyle::load($new_style_name);
|
||||
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), 'Image displayed using style replacement style.');
|
||||
|
||||
// Delete the style and choose a replacement style.
|
||||
$edit = [
|
||||
'replacement' => 'thumbnail',
|
||||
];
|
||||
$this->drupalPostForm($style_path . $new_style_name . '/delete', $edit, t('Delete'));
|
||||
$message = t('The image style %name has been deleted.', ['%name' => $new_style_label]);
|
||||
$this->assertRaw($message);
|
||||
|
||||
$replacement_style = ImageStyle::load('thumbnail');
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertRaw(file_url_transform_relative($replacement_style->buildUrl($original_uri)), 'Image displayed using style replacement style.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that editing an image effect does not cause it to be duplicated.
|
||||
*/
|
||||
public function testEditEffect() {
|
||||
// Add a scale effect.
|
||||
$style_name = 'test_style_effect_edit';
|
||||
$this->drupalGet('admin/config/media/image-styles/add');
|
||||
$this->drupalPostForm(NULL, ['label' => 'Test style effect edit', 'name' => $style_name], t('Create new style'));
|
||||
$this->drupalPostForm(NULL, ['new' => 'image_scale_and_crop'], t('Add'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '300', 'data[height]' => '200'], t('Add effect'));
|
||||
$this->assertText(t('Scale and crop 300×200'));
|
||||
|
||||
// There should normally be only one edit link on this page initially.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '360', 'data[height]' => '240'], t('Update effect'));
|
||||
$this->assertText(t('Scale and crop 360×240'));
|
||||
|
||||
// Check that the previous effect is replaced.
|
||||
$this->assertNoText(t('Scale and crop 300×200'));
|
||||
|
||||
// Add another scale effect.
|
||||
$this->drupalGet('admin/config/media/image-styles/add');
|
||||
$this->drupalPostForm(NULL, ['label' => 'Test style scale edit scale', 'name' => 'test_style_scale_edit_scale'], t('Create new style'));
|
||||
$this->drupalPostForm(NULL, ['new' => 'image_scale'], t('Add'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '12', 'data[height]' => '19'], t('Add effect'));
|
||||
|
||||
// Edit the scale effect that was just added.
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '24', 'data[height]' => '19'], t('Update effect'));
|
||||
|
||||
// Add another scale effect and make sure both exist. Click through from
|
||||
// the overview to make sure that it is possible to add new effect then.
|
||||
$this->drupalGet('admin/config/media/image-styles');
|
||||
$rows = $this->xpath('//table/tbody/tr');
|
||||
$i = 0;
|
||||
foreach ($rows as $row) {
|
||||
if ($row->find('css', 'td')->getText() === 'Test style scale edit scale') {
|
||||
$this->clickLink('Edit', $i);
|
||||
break;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$this->drupalPostForm(NULL, ['new' => 'image_scale'], t('Add'));
|
||||
$this->drupalPostForm(NULL, ['data[width]' => '12', 'data[height]' => '19'], t('Add effect'));
|
||||
$this->assertText(t('Scale 24×19'));
|
||||
$this->assertText(t('Scale 12×19'));
|
||||
|
||||
// Try to edit a nonexistent effect.
|
||||
$uuid = $this->container->get('uuid');
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/' . $style_name . '/effects/' . $uuid->generate());
|
||||
$this->assertResponse(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test flush user interface.
|
||||
*/
|
||||
public function testFlushUserInterface() {
|
||||
$admin_path = 'admin/config/media/image-styles';
|
||||
|
||||
// Create a new style.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style = ImageStyle::create(['name' => $style_name, 'label' => $this->randomString()]);
|
||||
$style->save();
|
||||
|
||||
// Create an image to make sure it gets flushed.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$image_uri = $files[0]->uri;
|
||||
$derivative_uri = $style->buildUri($image_uri);
|
||||
$this->assertTrue($style->createDerivative($image_uri, $derivative_uri));
|
||||
$this->assertEqual($this->getImageCount($style), 1);
|
||||
|
||||
// Go to image styles list page and check if the flush operation link
|
||||
// exists.
|
||||
$this->drupalGet($admin_path);
|
||||
$flush_path = $admin_path . '/manage/' . $style_name . '/flush';
|
||||
$this->assertLinkByHref($flush_path);
|
||||
|
||||
// Flush the image style derivatives using the user interface.
|
||||
$this->drupalPostForm($flush_path, [], t('Flush'));
|
||||
|
||||
// The derivative image file should have been deleted.
|
||||
$this->assertEqual($this->getImageCount($style), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image style configuration import that does a delete.
|
||||
*/
|
||||
public function testConfigImport() {
|
||||
// Create a new style.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style = ImageStyle::create(['name' => $style_name, 'label' => $style_label]);
|
||||
$style->save();
|
||||
|
||||
// Create an image field that uses the new style.
|
||||
$field_name = strtolower($this->randomMachineName(10));
|
||||
$this->createImageField($field_name, 'article');
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent($field_name, [
|
||||
'type' => 'image',
|
||||
'settings' => ['image_style' => $style_name],
|
||||
])
|
||||
->save();
|
||||
|
||||
// Create a new node with an image attached.
|
||||
$test_image = current($this->drupalGetTestFiles('image'));
|
||||
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $this->randomMachineName());
|
||||
$node = Node::load($nid);
|
||||
|
||||
// Get node field original image URI.
|
||||
$fid = $node->get($field_name)->target_id;
|
||||
$original_uri = File::load($fid)->getFileUri();
|
||||
|
||||
// Test that image is displayed using newly created style.
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertRaw(file_url_transform_relative($style->buildUrl($original_uri)), format_string('Image displayed using style @style.', ['@style' => $style_name]));
|
||||
|
||||
// Copy config to sync, and delete the image style.
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$active = $this->container->get('config.storage');
|
||||
// Remove the image field from the display, to avoid a dependency error
|
||||
// during import.
|
||||
EntityViewDisplay::load('node.article.default')
|
||||
->removeComponent($field_name)
|
||||
->save();
|
||||
$this->copyConfig($active, $sync);
|
||||
$sync->delete('image.style.' . $style_name);
|
||||
$this->configImporter()->import();
|
||||
|
||||
$this->assertFalse(ImageStyle::load($style_name), 'Style deleted after config import.');
|
||||
$this->assertEqual($this->getImageCount($style), 0, 'Image style was flushed after being deleted by config import.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access for the image style listing.
|
||||
*/
|
||||
public function testImageStyleAccess() {
|
||||
$style = ImageStyle::create(['name' => 'style_foo', 'label' => $this->randomString()]);
|
||||
$style->save();
|
||||
|
||||
$this->drupalGet('admin/config/media/image-styles');
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertRaw(t('Select a new effect'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests that images have correct dimensions when styled.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageDimensionsTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'image_module_test'];
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Test styled image dimensions cumulatively.
|
||||
*/
|
||||
public function testImageDimensions() {
|
||||
$image_factory = $this->container->get('image.factory');
|
||||
// Create a working copy of the file.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = reset($files);
|
||||
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
|
||||
|
||||
// Create a style.
|
||||
/** @var $style \Drupal\image\ImageStyleInterface */
|
||||
$style = ImageStyle::create(['name' => 'test', 'label' => 'Test']);
|
||||
$style->save();
|
||||
$generated_uri = 'public://styles/test/public/' . \Drupal::service('file_system')->basename($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
|
||||
$variables = [
|
||||
'#theme' => 'image_style',
|
||||
'#style_name' => 'test',
|
||||
'#uri' => $original_uri,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
];
|
||||
// Verify that the original image matches the hard-coded values.
|
||||
$image_file = $image_factory->get($original_uri);
|
||||
$this->assertEqual($image_file->getWidth(), $variables['#width']);
|
||||
$this->assertEqual($image_file->getHeight(), $variables['#height']);
|
||||
|
||||
// Scale an image that is wider than it is high.
|
||||
$effect = [
|
||||
'id' => 'image_scale',
|
||||
'data' => [
|
||||
'width' => 120,
|
||||
'height' => 90,
|
||||
'upscale' => TRUE,
|
||||
],
|
||||
'weight' => 0,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="120" height="60" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 120);
|
||||
$this->assertEqual($image_file->getHeight(), 60);
|
||||
|
||||
// Rotate 90 degrees anticlockwise.
|
||||
$effect = [
|
||||
'id' => 'image_rotate',
|
||||
'data' => [
|
||||
'degrees' => -90,
|
||||
'random' => FALSE,
|
||||
],
|
||||
'weight' => 1,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="60" height="120" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 60);
|
||||
$this->assertEqual($image_file->getHeight(), 120);
|
||||
|
||||
// Scale an image that is higher than it is wide (rotated by previous effect).
|
||||
$effect = [
|
||||
'id' => 'image_scale',
|
||||
'data' => [
|
||||
'width' => 120,
|
||||
'height' => 90,
|
||||
'upscale' => TRUE,
|
||||
],
|
||||
'weight' => 2,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="45" height="90" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 45);
|
||||
$this->assertEqual($image_file->getHeight(), 90);
|
||||
|
||||
// Test upscale disabled.
|
||||
$effect = [
|
||||
'id' => 'image_scale',
|
||||
'data' => [
|
||||
'width' => 400,
|
||||
'height' => 200,
|
||||
'upscale' => FALSE,
|
||||
],
|
||||
'weight' => 3,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="45" height="90" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 45);
|
||||
$this->assertEqual($image_file->getHeight(), 90);
|
||||
|
||||
// Add a desaturate effect.
|
||||
$effect = [
|
||||
'id' => 'image_desaturate',
|
||||
'data' => [],
|
||||
'weight' => 4,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="45" height="90" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 45);
|
||||
$this->assertEqual($image_file->getHeight(), 90);
|
||||
|
||||
// Add a random rotate effect.
|
||||
$effect = [
|
||||
'id' => 'image_rotate',
|
||||
'data' => [
|
||||
'degrees' => 180,
|
||||
'random' => TRUE,
|
||||
],
|
||||
'weight' => 5,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
|
||||
// Add a crop effect.
|
||||
$effect = [
|
||||
'id' => 'image_crop',
|
||||
'data' => [
|
||||
'width' => 30,
|
||||
'height' => 30,
|
||||
'anchor' => 'center-center',
|
||||
],
|
||||
'weight' => 6,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="30" height="30" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 30);
|
||||
$this->assertEqual($image_file->getHeight(), 30);
|
||||
|
||||
// Rotate to a non-multiple of 90 degrees.
|
||||
$effect = [
|
||||
'id' => 'image_rotate',
|
||||
'data' => [
|
||||
'degrees' => 57,
|
||||
'random' => FALSE,
|
||||
],
|
||||
'weight' => 7,
|
||||
];
|
||||
|
||||
$effect_id = $style->addImageEffect($effect);
|
||||
$style->save();
|
||||
// @todo Uncomment this once
|
||||
// https://www.drupal.org/project/drupal/issues/2670966 is resolved.
|
||||
// $this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="41" height="41" alt="" class="image-style-test" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
// @todo Uncomment this once
|
||||
// https://www.drupal.org/project/drupal/issues/2670966 is resolved.
|
||||
// $this->assertEqual($image_file->getWidth(), 41);
|
||||
// $this->assertEqual($image_file->getHeight(), 41);
|
||||
|
||||
$effect_plugin = $style->getEffect($effect_id);
|
||||
$style->deleteImageEffect($effect_plugin);
|
||||
|
||||
// Ensure that an effect can unset dimensions.
|
||||
$effect = [
|
||||
'id' => 'image_module_test_null',
|
||||
'data' => [],
|
||||
'weight' => 8,
|
||||
];
|
||||
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" alt="" class="image-style-test" />');
|
||||
|
||||
// Test URI dependent image effect.
|
||||
$style = ImageStyle::create(['name' => 'test_uri', 'label' => 'Test URI']);
|
||||
$effect = [
|
||||
'id' => 'image_module_test_uri_dependent',
|
||||
'data' => [],
|
||||
'weight' => 0,
|
||||
];
|
||||
$style->addImageEffect($effect);
|
||||
$style->save();
|
||||
$variables = [
|
||||
'#theme' => 'image_style',
|
||||
'#style_name' => 'test_uri',
|
||||
'#uri' => $original_uri,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
];
|
||||
// PNG original image. Should be resized to 100x100.
|
||||
$generated_uri = 'public://styles/test_uri/public/' . \Drupal::service('file_system')->basename($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="100" height="100" alt="" class="image-style-test-uri" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 100);
|
||||
$this->assertEqual($image_file->getHeight(), 100);
|
||||
// GIF original image. Should be resized to 50x50.
|
||||
$file = $files[1];
|
||||
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
|
||||
$generated_uri = 'public://styles/test_uri/public/' . \Drupal::service('file_system')->basename($original_uri);
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
$variables['#uri'] = $original_uri;
|
||||
$this->assertEqual($this->getImageTag($variables), '<img src="' . $url . '" width="50" height="50" alt="" class="image-style-test-uri" />');
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$this->drupalGet($this->getAbsoluteUrl($url));
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
$image_file = $image_factory->get($generated_uri);
|
||||
$this->assertEqual($image_file->getWidth(), 50);
|
||||
$this->assertEqual($image_file->getHeight(), 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an image style element.
|
||||
*
|
||||
* Function drupal_render() alters the passed $variables array by adding a new
|
||||
* key '#printed' => TRUE. This prevents next call to re-render the element.
|
||||
* We wrap drupal_render() in a helper protected method and pass each time a
|
||||
* fresh array so that $variables won't get altered and the element is
|
||||
* re-rendered each time.
|
||||
*/
|
||||
protected function getImageTag($variables) {
|
||||
return str_replace("\n", NULL, \Drupal::service('renderer')->renderRoot($variables));
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\system\Tests\Image\ToolkitTestBase;
|
||||
use Drupal\FunctionalTests\Image\ToolkitTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the image effects pass parameters to the toolkit correctly.
|
||||
|
@ -111,8 +111,29 @@ class ImageEffectsTest extends ToolkitTestBase {
|
|||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['scale_and_crop'][0][0], 5, 'Width was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][1], 10, 'Height was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][0], 7.5, 'X was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][1], 0, 'Y was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][2], 5, 'Width was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][3], 10, 'Height was computed and passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_scale_and_crop_effect() function with an anchor.
|
||||
*/
|
||||
public function testScaleAndCropEffectWithAnchor() {
|
||||
$this->assertImageEffect('image_scale_and_crop', [
|
||||
'anchor' => 'top-1',
|
||||
'width' => 5,
|
||||
'height' => 10,
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['scale_and_crop']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['scale_and_crop'][0][0], 0, 'X was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][1], 1, 'Y was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][2], 5, 'Width was computed and passed correctly');
|
||||
$this->assertEqual($calls['scale_and_crop'][0][3], 10, 'Height was computed and passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\EntityViewTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests setting up default images both to the field and field storage.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
use EntityViewTrait {
|
||||
buildEntityView as drupalBuildEntityView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_ui'];
|
||||
|
||||
/**
|
||||
* Tests CRUD for fields and field storages with default images.
|
||||
*/
|
||||
public function testDefaultImages() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
// Create files to use as the default images.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
// Create 10 files so the default image fids are not a single value.
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
$filename = $this->randomMachineName() . "$i";
|
||||
$desired_filepath = 'public://' . $filename;
|
||||
file_unmanaged_copy($files[0]->uri, $desired_filepath, FILE_EXISTS_ERROR);
|
||||
$file = File::create(['uri' => $desired_filepath, 'filename' => $filename, 'name' => $filename]);
|
||||
$file->save();
|
||||
}
|
||||
$default_images = [];
|
||||
foreach (['field_storage', 'field', 'field2', 'field_storage_new', 'field_new', 'field_storage_private', 'field_private'] as $image_target) {
|
||||
$file = File::create((array) array_pop($files));
|
||||
$file->save();
|
||||
$default_images[$image_target] = $file;
|
||||
}
|
||||
|
||||
// Create an image field storage and add a field to the article content
|
||||
// type.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$storage_settings['default_image'] = [
|
||||
'uuid' => $default_images['field_storage']->uuid(),
|
||||
'alt' => '',
|
||||
'title' => '',
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
$field_settings['default_image'] = [
|
||||
'uuid' => $default_images['field']->uuid(),
|
||||
'alt' => '',
|
||||
'title' => '',
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
];
|
||||
$widget_settings = [
|
||||
'preview_image_style' => 'medium',
|
||||
];
|
||||
$field = $this->createImageField($field_name, 'article', $storage_settings, $field_settings, $widget_settings);
|
||||
|
||||
// The field default image id should be 2.
|
||||
$this->assertEqual($field->getSetting('default_image')['uuid'], $default_images['field']->uuid());
|
||||
|
||||
// Also test \Drupal\field\Entity\FieldConfig::getSettings().
|
||||
$this->assertEqual($field->getSettings()['default_image']['uuid'], $default_images['field']->uuid());
|
||||
|
||||
$field_storage = $field->getFieldStorageDefinition();
|
||||
|
||||
// The field storage default image id should be 1.
|
||||
$this->assertEqual($field_storage->getSetting('default_image')['uuid'], $default_images['field_storage']->uuid());
|
||||
|
||||
// Also test \Drupal\field\Entity\FieldStorageConfig::getSettings().
|
||||
$this->assertEqual($field_storage->getSettings()['default_image']['uuid'], $default_images['field_storage']->uuid());
|
||||
|
||||
// Add another field with another default image to the page content type.
|
||||
$field2 = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
'label' => $field->label(),
|
||||
'required' => $field->isRequired(),
|
||||
'settings' => [
|
||||
'default_image' => [
|
||||
'uuid' => $default_images['field2']->uuid(),
|
||||
'alt' => '',
|
||||
'title' => '',
|
||||
'width' => 0,
|
||||
'height' => 0,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$field2->save();
|
||||
|
||||
$widget_settings = entity_get_form_display('node', $field->getTargetBundle(), 'default')->getComponent($field_name);
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent($field_name, $widget_settings)
|
||||
->save();
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Confirm the defaults are present on the article field storage settings
|
||||
// form.
|
||||
$field_id = $field->id();
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_storage']->id(),
|
||||
format_string(
|
||||
'Article image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the defaults are present on the article field edit form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field']->id(),
|
||||
format_string(
|
||||
'Article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Confirm the defaults are present on the page field storage settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/page/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_storage']->id(),
|
||||
format_string(
|
||||
'Page image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the defaults are present on the page field edit form.
|
||||
$field2_id = $field2->id();
|
||||
$this->drupalGet("admin/structure/types/manage/page/fields/$field2_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'Page image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Confirm that the image default is shown for a new article node.
|
||||
$article = $this->drupalCreateNode(['type' => 'article']);
|
||||
$article_built = $this->drupalBuildEntityView($article);
|
||||
$this->assertEqual(
|
||||
$article_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field']->id(),
|
||||
format_string(
|
||||
'A new article node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Also check that the field renders without warnings when the label is
|
||||
// hidden.
|
||||
EntityViewDisplay::load('node.article.default')
|
||||
->setComponent($field_name, ['label' => 'hidden', 'type' => 'image'])
|
||||
->save();
|
||||
$this->drupalGet('node/' . $article->id());
|
||||
|
||||
// Confirm that the image default is shown for a new page node.
|
||||
$page = $this->drupalCreateNode(['type' => 'page']);
|
||||
$page_built = $this->drupalBuildEntityView($page);
|
||||
$this->assertEqual(
|
||||
$page_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'A new page node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the field storage.
|
||||
$default_image_settings = $field_storage->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_storage_new']->uuid();
|
||||
$field_storage->setSetting('default_image', $default_image_settings);
|
||||
$field_storage->save();
|
||||
|
||||
// Confirm that the new default is used on the article field storage
|
||||
// settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_storage_new']->id(),
|
||||
format_string(
|
||||
'Updated image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_new']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Reload the nodes and confirm the field defaults are used.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
|
||||
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
|
||||
$this->assertEqual(
|
||||
$article_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field']->id(),
|
||||
format_string(
|
||||
'An existing article node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field']->id()]
|
||||
)
|
||||
);
|
||||
$this->assertEqual(
|
||||
$page_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'An existing page node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the article's field.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_new']->uuid();
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the new field default is used on the article field admin form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_new']->id(),
|
||||
format_string(
|
||||
'Updated article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Reload the nodes.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
|
||||
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
|
||||
|
||||
// Confirm the article uses the new default.
|
||||
$this->assertEqual(
|
||||
$article_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field_new']->id(),
|
||||
format_string(
|
||||
'An existing article node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field_new']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the page remains unchanged.
|
||||
$this->assertEqual(
|
||||
$page_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'An existing page node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Confirm the default image is shown on the node form.
|
||||
$file = File::load($default_images['field_new']->id());
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw($file->getFilename());
|
||||
|
||||
// Remove the field default from articles.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = 0;
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the article field default has been removed.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
'',
|
||||
'Updated article image field default has been successfully removed.'
|
||||
);
|
||||
|
||||
// Reload the nodes.
|
||||
$node_storage->resetCache([$article->id(), $page->id()]);
|
||||
$article_built = $this->drupalBuildEntityView($article = $node_storage->load($article->id()));
|
||||
$page_built = $this->drupalBuildEntityView($page = $node_storage->load($page->id()));
|
||||
// Confirm the article uses the new field storage (not field) default.
|
||||
$this->assertEqual(
|
||||
$article_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field_storage_new']->id(),
|
||||
format_string(
|
||||
'An existing article node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_new']->id()]
|
||||
)
|
||||
);
|
||||
// Confirm the page remains unchanged.
|
||||
$this->assertEqual(
|
||||
$page_built[$field_name][0]['#item']->target_id,
|
||||
$default_images['field2']->id(),
|
||||
format_string(
|
||||
'An existing page node without an image has the expected default image file ID of @fid.',
|
||||
['@fid' => $default_images['field2']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
$non_image = $this->drupalGetTestFiles('text');
|
||||
$this->drupalPostForm(NULL, ['files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($non_image[0]->uri)], t("Upload"));
|
||||
$this->assertText('The specified file text-0.txt could not be uploaded.');
|
||||
$this->assertText('Only files with the following extensions are allowed: png gif jpg jpeg.');
|
||||
|
||||
// Confirm the default image is shown on the node form.
|
||||
$file = File::load($default_images['field_storage_new']->id());
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertRaw($file->getFilename());
|
||||
|
||||
// Change the default image for the field storage and also change the upload
|
||||
// destination to the private filesystem at the same time.
|
||||
$default_image_settings = $field_storage->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_storage_private']->uuid();
|
||||
$field_storage->setSetting('default_image', $default_image_settings);
|
||||
$field_storage->setSetting('uri_scheme', 'private');
|
||||
$field_storage->save();
|
||||
|
||||
// Confirm that the new default is used on the article field storage
|
||||
// settings form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id/storage");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_storage_private']->id(),
|
||||
format_string(
|
||||
'Updated image field storage default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_storage_private']->id()]
|
||||
)
|
||||
);
|
||||
|
||||
// Upload a new default for the article's field after setting the field
|
||||
// storage upload destination to 'private'.
|
||||
$default_image_settings = $field->getSetting('default_image');
|
||||
$default_image_settings['uuid'] = $default_images['field_private']->uuid();
|
||||
$field->setSetting('default_image', $default_image_settings);
|
||||
$field->save();
|
||||
|
||||
// Confirm the new field field default is used on the article field
|
||||
// admin form.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/$field_id");
|
||||
$this->assertFieldByXpath(
|
||||
'//input[@name="settings[default_image][uuid][fids]"]',
|
||||
$default_images['field_private']->id(),
|
||||
format_string(
|
||||
'Updated article image field default equals expected file ID of @fid.',
|
||||
['@fid' => $default_images['field_private']->id()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image field and field storage having an invalid default image.
|
||||
*/
|
||||
public function testInvalidDefaultImage() {
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => mb_strtolower($this->randomMachineName()),
|
||||
'entity_type' => 'node',
|
||||
'type' => 'image',
|
||||
'settings' => [
|
||||
'default_image' => [
|
||||
'uuid' => 100000,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$field_storage->save();
|
||||
$settings = $field_storage->getSettings();
|
||||
// The non-existent default image should not be saved.
|
||||
$this->assertNull($settings['default_image']['uuid']);
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
'label' => $this->randomMachineName(),
|
||||
'settings' => [
|
||||
'default_image' => [
|
||||
'uuid' => 100000,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$field->save();
|
||||
$settings = $field->getSettings();
|
||||
// The non-existent default image should not be saved.
|
||||
$this->assertNull($settings['default_image']['uuid']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,473 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
||||
/**
|
||||
* Tests the display of image fields.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldDisplayTest extends ImageFieldTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_ui'];
|
||||
|
||||
/**
|
||||
* Test image formatters on node display for public files.
|
||||
*/
|
||||
public function testImageFieldFormattersPublic() {
|
||||
$this->_testImageFieldFormatters('public');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image formatters on node display for private files.
|
||||
*/
|
||||
public function testImageFieldFormattersPrivate() {
|
||||
// Remove access content permission from anonymous users.
|
||||
user_role_change_permissions(RoleInterface::ANONYMOUS_ID, ['access content' => FALSE]);
|
||||
$this->_testImageFieldFormatters('private');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image formatters on node display.
|
||||
*/
|
||||
public function _testImageFieldFormatters($scheme) {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$field_settings = ['alt_field_required' => 0];
|
||||
$instance = $this->createImageField($field_name, 'article', ['uri_scheme' => $scheme], $field_settings);
|
||||
|
||||
// Go to manage display page.
|
||||
$this->drupalGet("admin/structure/types/manage/article/display");
|
||||
|
||||
// Test for existence of link to image styles configuration.
|
||||
$this->drupalPostForm(NULL, [], "{$field_name}_settings_edit");
|
||||
$this->assertLinkByHref(\Drupal::url('entity.image_style.collection'), 0, 'Link to image styles configuration is found');
|
||||
|
||||
// Remove 'administer image styles' permission from testing admin user.
|
||||
$admin_user_roles = $this->adminUser->getRoles(TRUE);
|
||||
user_role_change_permissions(reset($admin_user_roles), ['administer image styles' => FALSE]);
|
||||
|
||||
// Go to manage display page again.
|
||||
$this->drupalGet("admin/structure/types/manage/article/display");
|
||||
|
||||
// Test for absence of link to image styles configuration.
|
||||
$this->drupalPostForm(NULL, [], "{$field_name}_settings_edit");
|
||||
$this->assertNoLinkByHref(\Drupal::url('entity.image_style.collection'), 'Link to image styles configuration is absent when permissions are insufficient');
|
||||
|
||||
// Restore 'administer image styles' permission to testing admin user
|
||||
user_role_change_permissions(reset($admin_user_roles), ['administer image styles' => TRUE]);
|
||||
|
||||
// Create a new node with an image attached.
|
||||
$test_image = current($this->drupalGetTestFiles('image'));
|
||||
|
||||
// Ensure that preview works.
|
||||
$this->previewNodeImage($test_image, $field_name, 'article');
|
||||
|
||||
// After previewing, make the alt field required. It cannot be required
|
||||
// during preview because the form validation will fail.
|
||||
$instance->setSetting('alt_field_required', 1);
|
||||
$instance->save();
|
||||
|
||||
// Create alt text for the image.
|
||||
$alt = $this->randomMachineName();
|
||||
|
||||
// Save node.
|
||||
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $alt);
|
||||
$node_storage->resetCache([$nid]);
|
||||
$node = $node_storage->load($nid);
|
||||
|
||||
// Test that the default formatter is being used.
|
||||
$file = $node->{$field_name}->entity;
|
||||
$image_uri = $file->getFileUri();
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $image_uri,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
'#alt' => $alt,
|
||||
];
|
||||
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->assertRaw($default_output, 'Default formatter displaying correctly on full node view.');
|
||||
|
||||
// Test the image linked to file formatter.
|
||||
$display_options = [
|
||||
'type' => 'image',
|
||||
'settings' => ['image_link' => 'file'],
|
||||
];
|
||||
$display = entity_get_display('node', $node->getType(), 'default');
|
||||
$display->setComponent($field_name, $display_options)
|
||||
->save();
|
||||
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $image_uri,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
'#alt' => $alt,
|
||||
];
|
||||
$default_output = '<a href="' . file_create_url($image_uri) . '">' . $renderer->renderRoot($image) . '</a>';
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertCacheTag($file->getCacheTags()[0]);
|
||||
// @todo Remove in https://www.drupal.org/node/2646744.
|
||||
$this->assertCacheContext('url.site');
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
$this->assertRaw($default_output, 'Image linked to file formatter displaying correctly on full node view.');
|
||||
// Verify that the image can be downloaded.
|
||||
$this->assertEqual(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), 'File was downloaded successfully.');
|
||||
if ($scheme == 'private') {
|
||||
// Only verify HTTP headers when using private scheme and the headers are
|
||||
// sent by Drupal.
|
||||
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'image/png', 'Content-Type header was sent.');
|
||||
$this->assertTrue(strstr($this->drupalGetHeader('Cache-Control'), 'private') !== FALSE, 'Cache-Control header was sent.');
|
||||
|
||||
// Log out and try to access the file.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet(file_create_url($image_uri));
|
||||
$this->assertResponse('403', 'Access denied to original image as anonymous user.');
|
||||
|
||||
// Log in again.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
// Test the image linked to content formatter.
|
||||
$display_options['settings']['image_link'] = 'content';
|
||||
$display->setComponent($field_name, $display_options)
|
||||
->save();
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $image_uri,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
];
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertCacheTag($file->getCacheTags()[0]);
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
$elements = $this->xpath(
|
||||
'//a[@href=:path]/img[@src=:url and @alt=:alt and @width=:width and @height=:height]',
|
||||
[
|
||||
':path' => $node->url(),
|
||||
':url' => file_url_transform_relative(file_create_url($image['#uri'])),
|
||||
':width' => $image['#width'],
|
||||
':height' => $image['#height'],
|
||||
':alt' => $alt,
|
||||
]
|
||||
);
|
||||
$this->assertEqual(count($elements), 1, 'Image linked to content formatter displaying correctly on full node view.');
|
||||
|
||||
// Test the image style 'thumbnail' formatter.
|
||||
$display_options['settings']['image_link'] = '';
|
||||
$display_options['settings']['image_style'] = 'thumbnail';
|
||||
$display->setComponent($field_name, $display_options)
|
||||
->save();
|
||||
|
||||
// Ensure the derivative image is generated so we do not have to deal with
|
||||
// image style callback paths.
|
||||
$this->drupalGet(ImageStyle::load('thumbnail')->buildUrl($image_uri));
|
||||
$image_style = [
|
||||
'#theme' => 'image_style',
|
||||
'#uri' => $image_uri,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
'#style_name' => 'thumbnail',
|
||||
'#alt' => $alt,
|
||||
];
|
||||
$default_output = $renderer->renderRoot($image_style);
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$image_style = ImageStyle::load('thumbnail');
|
||||
$this->assertCacheTag($image_style->getCacheTags()[0]);
|
||||
$this->assertRaw($default_output, 'Image style thumbnail formatter displaying correctly on full node view.');
|
||||
|
||||
if ($scheme == 'private') {
|
||||
// Log out and try to access the file.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet(ImageStyle::load('thumbnail')->buildUrl($image_uri));
|
||||
$this->assertResponse('403', 'Access denied to image style thumbnail as anonymous user.');
|
||||
}
|
||||
|
||||
// Test the image URL formatter without an image style.
|
||||
$display_options = [
|
||||
'type' => 'image_url',
|
||||
'settings' => ['image_style' => ''],
|
||||
];
|
||||
$expected_url = file_url_transform_relative(file_create_url($image_uri));
|
||||
$this->assertEqual($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']);
|
||||
|
||||
// Test the image URL formatter with an image style.
|
||||
$display_options['settings']['image_style'] = 'thumbnail';
|
||||
$expected_url = file_url_transform_relative(ImageStyle::load('thumbnail')->buildUrl($image_uri));
|
||||
$this->assertEqual($expected_url, $node->{$field_name}->view($display_options)[0]['#markup']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for image field settings.
|
||||
*/
|
||||
public function testImageFieldSettings() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$test_image = current($this->drupalGetTestFiles('image'));
|
||||
list(, $test_image_extension) = explode('.', $test_image->filename);
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$field_settings = [
|
||||
'alt_field' => 1,
|
||||
'file_extensions' => $test_image_extension,
|
||||
'max_filesize' => '50 KB',
|
||||
'max_resolution' => '100x100',
|
||||
'min_resolution' => '10x10',
|
||||
'title_field' => 1,
|
||||
];
|
||||
$widget_settings = [
|
||||
'preview_image_style' => 'medium',
|
||||
];
|
||||
$field = $this->createImageField($field_name, 'article', [], $field_settings, $widget_settings);
|
||||
|
||||
// Verify that the min/max resolution set on the field are properly
|
||||
// extracted, and displayed, on the image field's configuration form.
|
||||
$this->drupalGet('admin/structure/types/manage/article/fields/' . $field->id());
|
||||
$this->assertFieldByName('settings[max_resolution][x]', '100', 'Expected max resolution X value of 100.');
|
||||
$this->assertFieldByName('settings[max_resolution][y]', '100', 'Expected max resolution Y value of 100.');
|
||||
$this->assertFieldByName('settings[min_resolution][x]', '10', 'Expected min resolution X value of 10.');
|
||||
$this->assertFieldByName('settings[min_resolution][y]', '10', 'Expected min resolution Y value of 10.');
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText(t('50 KB limit.'), 'Image widget max file size is displayed on article form.');
|
||||
$this->assertText(t('Allowed types: @extensions.', ['@extensions' => $test_image_extension]), 'Image widget allowed file types displayed on article form.');
|
||||
$this->assertText(t('Images must be larger than 10x10 pixels. Images larger than 100x100 pixels will be resized.'), 'Image widget allowed resolution displayed on article form.');
|
||||
|
||||
// We have to create the article first and then edit it because the alt
|
||||
// and title fields do not display until the image has been attached.
|
||||
|
||||
// Create alt text for the image.
|
||||
$alt = $this->randomMachineName();
|
||||
|
||||
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $alt);
|
||||
$this->drupalGet('node/' . $nid . '/edit');
|
||||
|
||||
// Verify that the optional fields alt & title are saved & filled.
|
||||
$this->assertFieldByName($field_name . '[0][alt]', $alt, 'Alt field displayed on article form.');
|
||||
$this->assertFieldByName($field_name . '[0][title]', '', 'Title field displayed on article form.');
|
||||
|
||||
// Verify that the attached image is being previewed using the 'medium'
|
||||
// style.
|
||||
$node_storage->resetCache([$nid]);
|
||||
$node = $node_storage->load($nid);
|
||||
$file = $node->{$field_name}->entity;
|
||||
|
||||
$url = file_url_transform_relative(file_create_url(ImageStyle::load('medium')->buildUrl($file->getFileUri())));
|
||||
$this->assertTrue($this->cssSelect('img[width=40][height=20][class=image-style-medium][src="' . $url . '"]'));
|
||||
|
||||
// Add alt/title fields to the image and verify that they are displayed.
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $file->getFileUri(),
|
||||
'#alt' => $alt,
|
||||
'#title' => $this->randomMachineName(),
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
];
|
||||
$edit = [
|
||||
$field_name . '[0][alt]' => $image['#alt'],
|
||||
$field_name . '[0][title]' => $image['#title'],
|
||||
];
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.');
|
||||
|
||||
// Verify that alt/title longer than allowed results in a validation error.
|
||||
$test_size = 2000;
|
||||
$edit = [
|
||||
$field_name . '[0][alt]' => $this->randomMachineName($test_size),
|
||||
$field_name . '[0][title]' => $this->randomMachineName($test_size),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
|
||||
$schema = $field->getFieldStorageDefinition()->getSchema();
|
||||
$this->assertRaw(t('Alternative text cannot be longer than %max characters but is currently %length characters long.', [
|
||||
'%max' => $schema['columns']['alt']['length'],
|
||||
'%length' => $test_size,
|
||||
]));
|
||||
$this->assertRaw(t('Title cannot be longer than %max characters but is currently %length characters long.', [
|
||||
'%max' => $schema['columns']['title']['length'],
|
||||
'%length' => $test_size,
|
||||
]));
|
||||
|
||||
// Set cardinality to unlimited and add upload a second image.
|
||||
// The image widget is extending on the file widget, but the image field
|
||||
// type does not have the 'display_field' setting which is expected by
|
||||
// the file widget. This resulted in notices before when cardinality is not
|
||||
// 1, so we need to make sure the file widget prevents these notices by
|
||||
// providing all settings, even if they are not used.
|
||||
// @see FileWidget::formMultipleElements().
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $field_name . '/storage', ['cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED], t('Save field settings'));
|
||||
$edit = [
|
||||
'files[' . $field_name . '_1][]' => \Drupal::service('file_system')->realpath($test_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
// Add the required alt text.
|
||||
$this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save'));
|
||||
$this->assertText(format_string('Article @title has been updated.', ['@title' => $node->getTitle()]));
|
||||
|
||||
// Assert ImageWidget::process() calls FieldWidget::process().
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$edit = [
|
||||
'files[' . $field_name . '_2][]' => \Drupal::service('file_system')->realpath($test_image->uri),
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, $field_name . '_2_upload_button');
|
||||
$this->assertSession()->elementNotExists('css', 'input[name="files[' . $field_name . '_2][]"]');
|
||||
$this->assertSession()->elementExists('css', 'input[name="files[' . $field_name . '_3][]"]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test use of a default image with an image field.
|
||||
*/
|
||||
public function testImageFieldDefaultImage() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
// Create a new image field.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($field_name, 'article');
|
||||
|
||||
// Create a new node, with no images and verify that no images are
|
||||
// displayed.
|
||||
$node = $this->drupalCreateNode(['type' => 'article']);
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
// Verify that no image is displayed on the page by checking for the class
|
||||
// that would be used on the image field.
|
||||
$this->assertNoPattern('<div class="(.*?)field--name-' . strtr($field_name, '_', '-') . '(.*?)">', 'No image displayed when no image is attached and no default image specified.');
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
|
||||
// Add a default image to the public image field.
|
||||
$images = $this->drupalGetTestFiles('image');
|
||||
$alt = $this->randomString(512);
|
||||
$title = $this->randomString(1024);
|
||||
$edit = [
|
||||
// Get the path of the 'image-test.png' file.
|
||||
'files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[0]->uri),
|
||||
'settings[default_image][alt]' => $alt,
|
||||
'settings[default_image][title]' => $title,
|
||||
];
|
||||
$this->drupalPostForm("admin/structure/types/manage/article/fields/node.article.$field_name/storage", $edit, t('Save field settings'));
|
||||
// Clear field definition cache so the new default image is detected.
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
$default_image = $field_storage->getSetting('default_image');
|
||||
$file = \Drupal::entityManager()->loadEntityByUuid('file', $default_image['uuid']);
|
||||
$this->assertTrue($file->isPermanent(), 'The default image status is permanent.');
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $file->getFileUri(),
|
||||
'#alt' => $alt,
|
||||
'#title' => $title,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
];
|
||||
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertCacheTag($file->getCacheTags()[0]);
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
$this->assertRaw($default_output, 'Default image displayed when no user supplied image is present.');
|
||||
|
||||
// Create a node with an image attached and ensure that the default image
|
||||
// is not displayed.
|
||||
|
||||
// Create alt text for the image.
|
||||
$alt = $this->randomMachineName();
|
||||
|
||||
// Upload the 'image-test.gif' file.
|
||||
$nid = $this->uploadNodeImage($images[2], $field_name, 'article', $alt);
|
||||
$node_storage->resetCache([$nid]);
|
||||
$node = $node_storage->load($nid);
|
||||
$file = $node->{$field_name}->entity;
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $file->getFileUri(),
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
'#alt' => $alt,
|
||||
];
|
||||
$image_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->drupalGet('node/' . $nid);
|
||||
$this->assertCacheTag($file->getCacheTags()[0]);
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
$this->assertNoRaw($default_output, 'Default image is not displayed when user supplied image is present.');
|
||||
$this->assertRaw($image_output, 'User supplied image is displayed.');
|
||||
|
||||
// Remove default image from the field and make sure it is no longer used.
|
||||
// Can't use fillField cause Mink can't fill hidden fields.
|
||||
$this->drupalGet("admin/structure/types/manage/article/fields/node.article.$field_name/storage");
|
||||
$this->getSession()->getPage()->find('css', 'input[name="settings[default_image][uuid][fids]"]')->setValue(0);
|
||||
$this->getSession()->getPage()->pressButton(t('Save field settings'));
|
||||
|
||||
// Clear field definition cache so the new default image is detected.
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
$default_image = $field_storage->getSetting('default_image');
|
||||
$this->assertFalse($default_image['uuid'], 'Default image removed from field.');
|
||||
// Create an image field that uses the private:// scheme and test that the
|
||||
// default image works as expected.
|
||||
$private_field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($private_field_name, 'article', ['uri_scheme' => 'private']);
|
||||
// Add a default image to the new field.
|
||||
$edit = [
|
||||
// Get the path of the 'image-test.gif' file.
|
||||
'files[settings_default_image_uuid]' => \Drupal::service('file_system')->realpath($images[2]->uri),
|
||||
'settings[default_image][alt]' => $alt,
|
||||
'settings[default_image][title]' => $title,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $private_field_name . '/storage', $edit, t('Save field settings'));
|
||||
// Clear field definition cache so the new default image is detected.
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
|
||||
$private_field_storage = FieldStorageConfig::loadByName('node', $private_field_name);
|
||||
$default_image = $private_field_storage->getSetting('default_image');
|
||||
$file = \Drupal::entityManager()->loadEntityByUuid('file', $default_image['uuid']);
|
||||
$this->assertEqual('private', file_uri_scheme($file->getFileUri()), 'Default image uses private:// scheme.');
|
||||
$this->assertTrue($file->isPermanent(), 'The default image status is permanent.');
|
||||
// Create a new node with no image attached and ensure that default private
|
||||
// image is displayed.
|
||||
$node = $this->drupalCreateNode(['type' => 'article']);
|
||||
$image = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => $file->getFileUri(),
|
||||
'#alt' => $alt,
|
||||
'#title' => $title,
|
||||
'#width' => 40,
|
||||
'#height' => 20,
|
||||
];
|
||||
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertCacheTag($file->getCacheTags()[0]);
|
||||
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
|
||||
$this->assertTrue(!preg_match('/ image_style\:/', $cache_tags_header), 'No image style cache tag found.');
|
||||
$this->assertRaw($default_output, 'Default private image displayed when no user supplied image is present.');
|
||||
}
|
||||
|
||||
}
|
|
@ -8,11 +8,12 @@ use Drupal\Tests\BrowserTestBase;
|
|||
/**
|
||||
* TODO: Test the following functions.
|
||||
*
|
||||
* image.effects.inc:
|
||||
* In file:
|
||||
* - image.effects.inc:
|
||||
* image_style_generate()
|
||||
* \Drupal\image\ImageStyleInterface::createDerivative()
|
||||
*
|
||||
* image.module:
|
||||
* - image.module:
|
||||
* image_style_options()
|
||||
* \Drupal\image\ImageStyleInterface::flush()
|
||||
* image_filter_keyword()
|
||||
|
@ -66,7 +67,7 @@ abstract class ImageFieldTestBase extends BrowserTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Preview'));
|
||||
}
|
||||
|
||||
|
@ -86,11 +87,11 @@ abstract class ImageFieldTestBase extends BrowserTestBase {
|
|||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Save and publish'));
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Save'));
|
||||
if ($alt) {
|
||||
// Add alt text.
|
||||
$this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save and publish'));
|
||||
$this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save'));
|
||||
}
|
||||
|
||||
// Retrieve ID of the newly created node from the current URL.
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests validation functions such as min/max resolution.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image validity.
|
||||
*/
|
||||
public function testValid() {
|
||||
$file_system = $this->container->get('file_system');
|
||||
$image_files = $this->drupalGetTestFiles('image');
|
||||
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($field_name, 'article', [], ['file_directory' => 'test-upload']);
|
||||
$expected_path = 'public://test-upload';
|
||||
|
||||
// Create alt text for the image.
|
||||
$alt = $this->randomMachineName();
|
||||
|
||||
// Create a node with a valid image.
|
||||
$node = $this->uploadNodeImage($image_files[0], $field_name, 'article', $alt);
|
||||
$this->assertTrue(file_exists($expected_path . '/' . $image_files[0]->filename));
|
||||
|
||||
// Remove the image.
|
||||
$this->drupalPostForm('node/' . $node . '/edit', [], t('Remove'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
// Get invalid image test files from simpletest.
|
||||
$files = file_scan_directory(drupal_get_path('module', 'simpletest') . '/files', '/invalid-img-.*/');
|
||||
$invalid_image_files = [];
|
||||
foreach ($files as $file) {
|
||||
$invalid_image_files[$file->filename] = $file;
|
||||
}
|
||||
|
||||
// Try uploading a zero-byte image.
|
||||
$zero_size_image = $invalid_image_files['invalid-img-zero-size.png'];
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0]' => $file_system->realpath($zero_size_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node . '/edit', $edit, t('Upload'));
|
||||
$this->assertFalse(file_exists($expected_path . '/' . $zero_size_image->filename));
|
||||
|
||||
// Try uploading an invalid image.
|
||||
$invalid_image = $invalid_image_files['invalid-img-test.png'];
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0]' => $file_system->realpath($invalid_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node . '/edit', $edit, t('Upload'));
|
||||
$this->assertFalse(file_exists($expected_path . '/' . $invalid_image->filename));
|
||||
|
||||
// Upload a valid image again.
|
||||
$valid_image = $image_files[0];
|
||||
$edit = [
|
||||
'files[' . $field_name . '_0]' => $file_system->realpath($valid_image->uri),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node . '/edit', $edit, t('Upload'));
|
||||
$this->assertTrue(file_exists($expected_path . '/' . $valid_image->filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test min/max resolution settings.
|
||||
*/
|
||||
public function testResolution() {
|
||||
$field_names = [
|
||||
0 => strtolower($this->randomMachineName()),
|
||||
1 => strtolower($this->randomMachineName()),
|
||||
2 => strtolower($this->randomMachineName()),
|
||||
];
|
||||
$min_resolution = [
|
||||
'width' => 50,
|
||||
'height' => 50,
|
||||
];
|
||||
$max_resolution = [
|
||||
'width' => 100,
|
||||
'height' => 100,
|
||||
];
|
||||
$no_height_min_resolution = [
|
||||
'width' => 50,
|
||||
'height' => NULL,
|
||||
];
|
||||
$no_height_max_resolution = [
|
||||
'width' => 100,
|
||||
'height' => NULL,
|
||||
];
|
||||
$no_width_min_resolution = [
|
||||
'width' => NULL,
|
||||
'height' => 50,
|
||||
];
|
||||
$no_width_max_resolution = [
|
||||
'width' => NULL,
|
||||
'height' => 100,
|
||||
];
|
||||
$field_settings = [
|
||||
0 => $this->getFieldSettings($min_resolution, $max_resolution),
|
||||
1 => $this->getFieldSettings($no_height_min_resolution, $no_height_max_resolution),
|
||||
2 => $this->getFieldSettings($no_width_min_resolution, $no_width_max_resolution),
|
||||
];
|
||||
$this->createImageField($field_names[0], 'article', [], $field_settings[0]);
|
||||
$this->createImageField($field_names[1], 'article', [], $field_settings[1]);
|
||||
$this->createImageField($field_names[2], 'article', [], $field_settings[2]);
|
||||
|
||||
// We want a test image that is too small, and a test image that is too
|
||||
// big, so cycle through test image files until we have what we need.
|
||||
$image_that_is_too_big = FALSE;
|
||||
$image_that_is_too_small = FALSE;
|
||||
$image_factory = $this->container->get('image.factory');
|
||||
foreach ($this->drupalGetTestFiles('image') as $image) {
|
||||
$image_file = $image_factory->get($image->uri);
|
||||
if ($image_file->getWidth() > $max_resolution['width']) {
|
||||
$image_that_is_too_big = $image;
|
||||
}
|
||||
if ($image_file->getWidth() < $min_resolution['width']) {
|
||||
$image_that_is_too_small = $image;
|
||||
$image_that_is_too_small_file = $image_file;
|
||||
}
|
||||
if ($image_that_is_too_small && $image_that_is_too_big) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->uploadNodeImage($image_that_is_too_small, $field_names[0], 'article');
|
||||
$this->assertRaw(t('The specified file %name could not be uploaded.', ['%name' => $image_that_is_too_small->filename]));
|
||||
$this->assertRaw(t('The image is too small. The minimum dimensions are %dimensions pixels and the image size is %widthx%height pixels.', [
|
||||
'%dimensions' => '50x50',
|
||||
'%width' => $image_that_is_too_small_file->getWidth(),
|
||||
'%height' => $image_that_is_too_small_file->getHeight(),
|
||||
]));
|
||||
$this->uploadNodeImage($image_that_is_too_big, $field_names[0], 'article');
|
||||
$this->assertText(t('The image was resized to fit within the maximum allowed dimensions of 100x100 pixels.'));
|
||||
$this->uploadNodeImage($image_that_is_too_small, $field_names[1], 'article');
|
||||
$this->assertRaw(t('The specified file %name could not be uploaded.', ['%name' => $image_that_is_too_small->filename]));
|
||||
$this->uploadNodeImage($image_that_is_too_big, $field_names[1], 'article');
|
||||
$this->assertText(t('The image was resized to fit within the maximum allowed width of 100 pixels.'));
|
||||
$this->uploadNodeImage($image_that_is_too_small, $field_names[2], 'article');
|
||||
$this->assertRaw(t('The specified file %name could not be uploaded.', ['%name' => $image_that_is_too_small->filename]));
|
||||
$this->uploadNodeImage($image_that_is_too_big, $field_names[2], 'article');
|
||||
$this->assertText(t('The image was resized to fit within the maximum allowed height of 100 pixels.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that required alt/title fields gets validated right.
|
||||
*/
|
||||
public function testRequiredAttributes() {
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$field_settings = [
|
||||
'alt_field' => 1,
|
||||
'alt_field_required' => 1,
|
||||
'title_field' => 1,
|
||||
'title_field_required' => 1,
|
||||
'required' => 1,
|
||||
];
|
||||
$instance = $this->createImageField($field_name, 'article', [], $field_settings);
|
||||
$images = $this->drupalGetTestFiles('image');
|
||||
// Let's just use the first image.
|
||||
$image = $images[0];
|
||||
$this->uploadNodeImage($image, $field_name, 'article');
|
||||
|
||||
// Look for form-required for the alt text.
|
||||
$elements = $this->xpath('//label[@for="edit-' . $field_name . '-0-alt" and @class="js-form-required form-required"]/following-sibling::input[@id="edit-' . $field_name . '-0-alt"]');
|
||||
|
||||
$this->assertTrue(isset($elements[0]), 'Required marker is shown for the required alt text.');
|
||||
|
||||
$elements = $this->xpath('//label[@for="edit-' . $field_name . '-0-title" and @class="js-form-required form-required"]/following-sibling::input[@id="edit-' . $field_name . '-0-title"]');
|
||||
|
||||
$this->assertTrue(isset($elements[0]), 'Required marker is shown for the required title text.');
|
||||
|
||||
$this->assertText(t('Alternative text field is required.'));
|
||||
$this->assertText(t('Title field is required.'));
|
||||
|
||||
$instance->setSetting('alt_field_required', 0);
|
||||
$instance->setSetting('title_field_required', 0);
|
||||
$instance->save();
|
||||
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
$this->assertNoText(t('Alternative text field is required.'));
|
||||
$this->assertNoText(t('Title field is required.'));
|
||||
|
||||
$instance->setSetting('required', 0);
|
||||
$instance->setSetting('alt_field_required', 1);
|
||||
$instance->setSetting('title_field_required', 1);
|
||||
$instance->save();
|
||||
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save'));
|
||||
|
||||
$this->assertNoText(t('Alternative text field is required.'));
|
||||
$this->assertNoText(t('Title field is required.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns field settings.
|
||||
*
|
||||
* @param int[] $min_resolution
|
||||
* The minimum width and height resolution setting.
|
||||
* @param int[] $max_resolution
|
||||
* The maximum width and height resolution setting.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFieldSettings($min_resolution, $max_resolution) {
|
||||
return [
|
||||
'max_resolution' => $max_resolution['width'] . 'x' . $max_resolution['height'],
|
||||
'min_resolution' => $min_resolution['width'] . 'x' . $min_resolution['height'],
|
||||
'alt_field' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests the image field widget.
|
||||
*
|
||||
|
@ -27,6 +29,22 @@ class ImageFieldWidgetTest extends ImageFieldTestBase {
|
|||
$this->assertNotEqual(0, count($this->xpath('//div[contains(@class, "field--widget-image-image")]')), 'Image field widget found on add/node page', 'Browser');
|
||||
$this->assertNotEqual(0, count($this->xpath('//input[contains(@accept, "image/*")]')), 'Image field widget limits accepted files.', 'Browser');
|
||||
$this->assertNoText('Image test on [site:name]');
|
||||
|
||||
// Check for allowed image file extensions - default.
|
||||
$this->assertText('Allowed types: png gif jpg jpeg.');
|
||||
|
||||
// Try adding to the field config an unsupported extension, should not
|
||||
// appear in the allowed types.
|
||||
$field_config = FieldConfig::loadByName('node', 'article', $field_name);
|
||||
$field_config->setSetting('file_extensions', 'png gif jpg jpeg tiff')->save();
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText('Allowed types: png gif jpg jpeg.');
|
||||
|
||||
// Add a supported extension and remove some supported ones, we should see
|
||||
// the intersect of those entered in field config with those supported.
|
||||
$field_config->setSetting('file_extensions', 'png jpe tiff')->save();
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertText('Allowed types: png jpe.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Uploads images to translated nodes.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageOnTranslatedEntityTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation', 'field_ui'];
|
||||
|
||||
/**
|
||||
* The name of the image field used in the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// This test expects unused managed files to be marked as a temporary file.
|
||||
$this->config('file.settings')->set('make_unused_managed_files_temporary', TRUE)->save();
|
||||
|
||||
// Create the "Basic page" node type.
|
||||
// @todo Remove the disabling of new revision creation in
|
||||
// https://www.drupal.org/node/1239558.
|
||||
$this->drupalCreateContentType(['type' => 'basicpage', 'name' => 'Basic page', 'new_revision' => FALSE]);
|
||||
|
||||
// Create a image field on the "Basic page" node type.
|
||||
$this->fieldName = strtolower($this->randomMachineName());
|
||||
$this->createImageField($this->fieldName, 'basicpage', [], ['title_field' => 1]);
|
||||
|
||||
// Create and log in user.
|
||||
$permissions = [
|
||||
'access administration pages',
|
||||
'administer content translation',
|
||||
'administer content types',
|
||||
'administer languages',
|
||||
'administer node fields',
|
||||
'create content translations',
|
||||
'create basicpage content',
|
||||
'edit any basicpage content',
|
||||
'translate any entity',
|
||||
'delete any basicpage content',
|
||||
];
|
||||
$admin_user = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add a second and third language.
|
||||
$edit = [];
|
||||
$edit['predefined_langcode'] = 'fr';
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
|
||||
$edit = [];
|
||||
$edit['predefined_langcode'] = 'nl';
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests synced file fields on translated nodes.
|
||||
*/
|
||||
public function testSyncedImages() {
|
||||
// Enable translation for "Basic page" nodes.
|
||||
$edit = [
|
||||
'entity_types[node]' => 1,
|
||||
'settings[node][basicpage][translatable]' => 1,
|
||||
"settings[node][basicpage][fields][$this->fieldName]" => 1,
|
||||
"settings[node][basicpage][columns][$this->fieldName][file]" => 1,
|
||||
// Explicitly disable alt and title since the javascript disables the
|
||||
// checkboxes on the form.
|
||||
"settings[node][basicpage][columns][$this->fieldName][alt]" => FALSE,
|
||||
"settings[node][basicpage][columns][$this->fieldName][title]" => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/content-language', $edit, 'Save configuration');
|
||||
|
||||
// Verify that the image field on the "Basic basic" node type is
|
||||
// translatable.
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions('node', 'basicpage');
|
||||
$this->assertTrue($definitions[$this->fieldName]->isTranslatable(), 'Node image field is translatable.');
|
||||
|
||||
// Create a default language node.
|
||||
$default_language_node = $this->drupalCreateNode(['type' => 'basicpage', 'title' => 'Lost in translation']);
|
||||
|
||||
// Edit the node to upload a file.
|
||||
$edit = [];
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[0]->uri);
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/edit', $edit, t('Save'));
|
||||
$edit = [$this->fieldName . '[0][alt]' => 'Lost in translation image', $this->fieldName . '[0][title]' => 'Lost in translation image title'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$first_fid = $this->getLastFileId();
|
||||
|
||||
// Translate the node into French: remove the existing file.
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/translations/add/en/fr', [], t('Remove'));
|
||||
|
||||
// Upload a different file.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = 'Scarlett Johansson';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[1]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$edit = [$this->fieldName . '[0][alt]' => 'Scarlett Johansson image', $this->fieldName . '[0][title]' => 'Scarlett Johansson image title'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
// This inspects the HTML after the post of the translation, the image
|
||||
// should be displayed on the original node.
|
||||
$this->assertRaw('alt="Lost in translation image"');
|
||||
$this->assertRaw('title="Lost in translation image title"');
|
||||
$second_fid = $this->getLastFileId();
|
||||
// View the translated node.
|
||||
$this->drupalGet('fr/node/' . $default_language_node->id());
|
||||
$this->assertRaw('alt="Scarlett Johansson image"');
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
/* @var $file \Drupal\file\FileInterface */
|
||||
|
||||
// Ensure the file status of the first file permanent.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the second file is permanent.
|
||||
$file = File::load($second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Translate the node into dutch: remove the existing file.
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/translations/add/en/nl', [], t('Remove'));
|
||||
|
||||
// Upload a different file.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = 'Akiko Takeshita';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[2]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$edit = [$this->fieldName . '[0][alt]' => 'Akiko Takeshita image', $this->fieldName . '[0][title]' => 'Akiko Takeshita image title'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$third_fid = $this->getLastFileId();
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the first file is untouched.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
|
||||
// This inspects the HTML after the post of the translation, the image
|
||||
// should be displayed on the original node.
|
||||
$this->assertRaw('alt="Lost in translation image"');
|
||||
$this->assertRaw('title="Lost in translation image title"');
|
||||
// View the translated node.
|
||||
$this->drupalGet('nl/node/' . $default_language_node->id());
|
||||
$this->assertRaw('alt="Akiko Takeshita image"');
|
||||
$this->assertRaw('title="Akiko Takeshita image title"');
|
||||
|
||||
// Ensure the file status of the second file is permanent.
|
||||
$file = File::load($second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the third file is permanent.
|
||||
$file = File::load($third_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Edit the second translation: remove the existing file.
|
||||
$this->drupalPostForm('fr/node/' . $default_language_node->id() . '/edit', [], t('Remove'));
|
||||
|
||||
// Upload a different file.
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = 'Giovanni Ribisi';
|
||||
$name = 'files[' . $this->fieldName . '_0]';
|
||||
$edit[$name] = \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('image')[3]->uri);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$name = $this->fieldName . '[0][alt]';
|
||||
|
||||
$edit = [$name => 'Giovanni Ribisi image'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
|
||||
$replaced_second_fid = $this->getLastFileId();
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the first and third files are untouched.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
|
||||
|
||||
$file = File::load($third_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the replaced second file is permanent.
|
||||
$file = File::load($replaced_second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Delete the third translation.
|
||||
$this->drupalPostForm('nl/node/' . $default_language_node->id() . '/delete', [], t('Delete Dutch translation'));
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the first and replaced second files are untouched.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
|
||||
|
||||
$file = File::load($replaced_second_fid);
|
||||
$this->assertTrue($file->isPermanent());
|
||||
|
||||
// Ensure the file status of the third file is now temporary.
|
||||
$file = File::load($third_fid);
|
||||
$this->assertTrue($file->isTemporary());
|
||||
|
||||
// Delete the all translations.
|
||||
$this->drupalPostForm('node/' . $default_language_node->id() . '/delete', [], t('Delete all translations'));
|
||||
|
||||
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
|
||||
|
||||
// Ensure the file status of the all files are now temporary.
|
||||
$file = File::load($first_fid);
|
||||
$this->assertTrue($file->isTemporary(), 'First file still exists and is temporary.');
|
||||
|
||||
$file = File::load($replaced_second_fid);
|
||||
$this->assertTrue($file->isTemporary());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests flushing of image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStyleFlushTest extends ImageFieldTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an image style and a wrapper, generate an image.
|
||||
*/
|
||||
public function createSampleImage($style, $wrapper) {
|
||||
static $file;
|
||||
|
||||
if (!isset($file)) {
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = reset($files);
|
||||
}
|
||||
|
||||
// Make sure we have an image in our wrapper testing file directory.
|
||||
$source_uri = file_unmanaged_copy($file->uri, $wrapper . '://');
|
||||
// Build the derivative image.
|
||||
$derivative_uri = $style->buildUri($source_uri);
|
||||
$derivative = $style->createDerivative($source_uri, $derivative_uri);
|
||||
|
||||
return $derivative ? $derivative_uri : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of images currently created for a style in a wrapper.
|
||||
*/
|
||||
public function getImageCount($style, $wrapper) {
|
||||
return count(file_scan_directory($wrapper . '://styles/' . $style->id(), '/.*/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* General test to flush a style.
|
||||
*/
|
||||
public function testFlush() {
|
||||
|
||||
// Setup a style to be created and effects to add to it.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style_path = 'admin/config/media/image-styles/manage/' . $style_name;
|
||||
$effect_edits = [
|
||||
'image_resize' => [
|
||||
'data[width]' => 100,
|
||||
'data[height]' => 101,
|
||||
],
|
||||
'image_scale' => [
|
||||
'data[width]' => 110,
|
||||
'data[height]' => 111,
|
||||
'data[upscale]' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
// Add style form.
|
||||
$edit = [
|
||||
'name' => $style_name,
|
||||
'label' => $style_label,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/media/image-styles/add', $edit, t('Create new style'));
|
||||
|
||||
// Add each sample effect to the style.
|
||||
foreach ($effect_edits as $effect => $edit) {
|
||||
// Add the effect.
|
||||
$this->drupalPostForm($style_path, ['new' => $effect], t('Add'));
|
||||
if (!empty($edit)) {
|
||||
$this->drupalPostForm(NULL, $edit, t('Add effect'));
|
||||
}
|
||||
}
|
||||
|
||||
// Load the saved image style.
|
||||
$style = ImageStyle::load($style_name);
|
||||
|
||||
// Create an image for the 'public' wrapper.
|
||||
$image_path = $this->createSampleImage($style, 'public');
|
||||
// Expecting to find 2 images, one is the sample.png image shown in
|
||||
// image style preview.
|
||||
$this->assertEqual($this->getImageCount($style, 'public'), 2, format_string('Image style %style image %file successfully generated.', ['%style' => $style->label(), '%file' => $image_path]));
|
||||
|
||||
// Create an image for the 'private' wrapper.
|
||||
$image_path = $this->createSampleImage($style, 'private');
|
||||
$this->assertEqual($this->getImageCount($style, 'private'), 1, format_string('Image style %style image %file successfully generated.', ['%style' => $style->label(), '%file' => $image_path]));
|
||||
|
||||
// Remove the 'image_scale' effect and updates the style, which in turn
|
||||
// forces an image style flush.
|
||||
$style_path = 'admin/config/media/image-styles/manage/' . $style->id();
|
||||
$uuids = [];
|
||||
foreach ($style->getEffects() as $uuid => $effect) {
|
||||
$uuids[$effect->getPluginId()] = $uuid;
|
||||
}
|
||||
$this->drupalPostForm($style_path . '/effects/' . $uuids['image_scale'] . '/delete', [], t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$this->drupalPostForm($style_path, [], t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Post flush, expected 1 image in the 'public' wrapper (sample.png).
|
||||
$this->assertEqual($this->getImageCount($style, 'public'), 1, format_string('Image style %style flushed correctly for %wrapper wrapper.', ['%style' => $style->label(), '%wrapper' => 'public']));
|
||||
|
||||
// Post flush, expected no image in the 'private' wrapper.
|
||||
$this->assertEqual($this->getImageCount($style, 'private'), 0, format_string('Image style %style flushed correctly for %wrapper wrapper.', ['%style' => $style->label(), '%wrapper' => 'private']));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the functions for generating paths and URLs for image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStylesPathAndUrlTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'image_module_test', 'language'];
|
||||
|
||||
/**
|
||||
* The image style.
|
||||
*
|
||||
* @var \Drupal\image\ImageStyleInterface
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->style = ImageStyle::create([
|
||||
'name' => 'style_foo',
|
||||
'label' => $this->randomString(),
|
||||
]);
|
||||
$this->style->save();
|
||||
|
||||
// Create a new language.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\image\ImageStyleInterface::buildUri().
|
||||
*/
|
||||
public function testImageStylePath() {
|
||||
$scheme = 'public';
|
||||
$actual = $this->style->buildUri("$scheme://foo/bar.gif");
|
||||
$expected = "$scheme://styles/" . $this->style->id() . "/$scheme/foo/bar.gif";
|
||||
$this->assertEqual($actual, $expected, 'Got the path for a file URI.');
|
||||
|
||||
$actual = $this->style->buildUri('foo/bar.gif');
|
||||
$expected = "$scheme://styles/" . $this->style->id() . "/$scheme/foo/bar.gif";
|
||||
$this->assertEqual($actual, $expected, 'Got the path for a relative file path.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL using the "public://" scheme.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPublic() {
|
||||
$this->doImageStyleUrlAndPathTests('public');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL using the "private://" scheme.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPrivate() {
|
||||
$this->doImageStyleUrlAndPathTests('private');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with the "public://" scheme and unclean URLs.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPublicUnclean() {
|
||||
$this->doImageStyleUrlAndPathTests('public', FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with the "private://" schema and unclean URLs.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPrivateUnclean() {
|
||||
$this->doImageStyleUrlAndPathTests('private', FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with the "public://" schema and language prefix.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPublicLanguage() {
|
||||
$this->doImageStyleUrlAndPathTests('public', TRUE, TRUE, 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with the "private://" schema and language prefix.
|
||||
*/
|
||||
public function testImageStyleUrlAndPathPrivateLanguage() {
|
||||
$this->doImageStyleUrlAndPathTests('private', TRUE, TRUE, 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an image style URL with a file URL that has an extra slash in it.
|
||||
*/
|
||||
public function testImageStyleUrlExtraSlash() {
|
||||
$this->doImageStyleUrlAndPathTests('public', TRUE, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an invalid source image returns a 404.
|
||||
*/
|
||||
public function testImageStyleUrlForMissingSourceImage() {
|
||||
$non_existent_uri = 'public://foo.png';
|
||||
$generated_url = $this->style->buildUrl($non_existent_uri);
|
||||
$this->drupalGet($generated_url);
|
||||
$this->assertResponse(404, 'Accessing an image style URL with a source image that does not exist provides a 404 error response.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests building an image style URL.
|
||||
*/
|
||||
public function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash = FALSE, $langcode = FALSE) {
|
||||
$this->prepareRequestForGenerator($clean_url);
|
||||
|
||||
// Make the default scheme neither "public" nor "private" to verify the
|
||||
// functions work for other than the default scheme.
|
||||
$this->config('system.file')->set('default_scheme', 'temporary')->save();
|
||||
|
||||
// Create the directories for the styles.
|
||||
$directory = $scheme . '://styles/' . $this->style->id();
|
||||
$status = file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
|
||||
$this->assertNotIdentical(FALSE, $status, 'Created the directory for the generated images for the test style.');
|
||||
|
||||
// Override the language to build the URL for the correct language.
|
||||
if ($langcode) {
|
||||
$language_manager = \Drupal::service('language_manager');
|
||||
$language = $language_manager->getLanguage($langcode);
|
||||
$language_manager->setConfigOverrideLanguage($language);
|
||||
}
|
||||
|
||||
// Create a working copy of the file.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = array_shift($files);
|
||||
$original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME);
|
||||
// Let the image_module_test module know about this file, so it can claim
|
||||
// ownership in hook_file_download().
|
||||
\Drupal::state()->set('image.test_file_download', $original_uri);
|
||||
$this->assertNotIdentical(FALSE, $original_uri, 'Created the generated image file.');
|
||||
|
||||
// Get the URL of a file that has not been generated and try to create it.
|
||||
$generated_uri = $this->style->buildUri($original_uri);
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$generate_url = $this->style->buildUrl($original_uri, $clean_url);
|
||||
|
||||
// Make sure that language prefix is never added to the image style URL.
|
||||
if ($langcode) {
|
||||
$this->assertTrue(strpos($generate_url, "/$langcode/") === FALSE, 'Langcode was not found in the image style URL.');
|
||||
}
|
||||
|
||||
// Ensure that the tests still pass when the file is generated by accessing
|
||||
// a poorly constructed (but still valid) file URL that has an extra slash
|
||||
// in it.
|
||||
if ($extra_slash) {
|
||||
$modified_uri = str_replace('://', ':///', $original_uri);
|
||||
$this->assertNotEqual($original_uri, $modified_uri, 'An extra slash was added to the generated file URI.');
|
||||
$generate_url = $this->style->buildUrl($modified_uri, $clean_url);
|
||||
}
|
||||
if (!$clean_url) {
|
||||
$this->assertTrue(strpos($generate_url, 'index.php/') !== FALSE, 'When using non-clean URLS, the system path contains the script name.');
|
||||
}
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
$this->assertResponse(404, 'Image was inaccessible at the URL with an invalid token.');
|
||||
// Change the parameter name so the token is missing.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $generate_url));
|
||||
$this->assertResponse(404, 'Image was inaccessible at the URL with a missing token.');
|
||||
|
||||
// Check that the generated URL is the same when we pass in a relative path
|
||||
// rather than a URI. We need to temporarily switch the default scheme to
|
||||
// match the desired scheme before testing this, then switch it back to the
|
||||
// "temporary" scheme used throughout this test afterwards.
|
||||
$this->config('system.file')->set('default_scheme', $scheme)->save();
|
||||
$relative_path = file_uri_target($original_uri);
|
||||
$generate_url_from_relative_path = $this->style->buildUrl($relative_path, $clean_url);
|
||||
$this->assertEqual($generate_url, $generate_url_from_relative_path);
|
||||
$this->config('system.file')->set('default_scheme', 'temporary')->save();
|
||||
|
||||
// Fetch the URL that generates the file.
|
||||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
$this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
|
||||
// assertRaw can't be used with string containing non UTF-8 chars.
|
||||
$this->assertNotEmpty(file_get_contents($generated_uri), 'URL returns expected file.');
|
||||
$image = $this->container->get('image.factory')->get($generated_uri);
|
||||
$this->assertEqual($this->drupalGetHeader('Content-Type'), $image->getMimeType(), 'Expected Content-Type was reported.');
|
||||
$this->assertEqual($this->drupalGetHeader('Content-Length'), $image->getFileSize(), 'Expected Content-Length was reported.');
|
||||
|
||||
// Check that we did not download the original file.
|
||||
$original_image = $this->container->get('image.factory')
|
||||
->get($original_uri);
|
||||
$this->assertNotEqual($this->drupalGetHeader('Content-Length'), $original_image->getFileSize());
|
||||
|
||||
if ($scheme == 'private') {
|
||||
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
|
||||
$this->assertNotEqual(strpos($this->drupalGetHeader('Cache-Control'), 'no-cache'), FALSE, 'Cache-Control header contains \'no-cache\' to prevent caching.');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', 'Expected custom header has been added.');
|
||||
|
||||
// Make sure that a second request to the already existing derivative
|
||||
// works too.
|
||||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(200, 'Image was generated at the URL.');
|
||||
|
||||
// Check that the second request also returned the generated image.
|
||||
$this->assertEqual($this->drupalGetHeader('Content-Length'), $image->getFileSize());
|
||||
|
||||
// Check that we did not download the original file.
|
||||
$this->assertNotEqual($this->drupalGetHeader('Content-Length'), $original_image->getFileSize());
|
||||
|
||||
// Make sure that access is denied for existing style files if we do not
|
||||
// have access.
|
||||
\Drupal::state()->delete('image.test_file_download');
|
||||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the private image style.');
|
||||
|
||||
// Repeat this with a different file that we do not have access to and
|
||||
// make sure that access is denied.
|
||||
$file_noaccess = array_shift($files);
|
||||
$original_uri_noaccess = file_unmanaged_copy($file_noaccess->uri, $scheme . '://', FILE_EXISTS_RENAME);
|
||||
$generated_uri_noaccess = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . drupal_basename($original_uri_noaccess);
|
||||
$this->assertFalse(file_exists($generated_uri_noaccess), 'Generated file does not exist.');
|
||||
$generate_url_noaccess = $this->style->buildUrl($original_uri_noaccess);
|
||||
|
||||
$this->drupalGet($generate_url_noaccess);
|
||||
$this->assertResponse(403, 'Confirmed that access is denied for the private image style.');
|
||||
// Verify that images are not appended to the response.
|
||||
// Currently this test only uses PNG images.
|
||||
if (strpos($generate_url, '.png') === FALSE) {
|
||||
$this->fail('Confirming that private image styles are not appended require PNG file.');
|
||||
}
|
||||
else {
|
||||
// Check for PNG-Signature
|
||||
// (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2)
|
||||
// in the response body.
|
||||
$raw = $this->getSession()->getPage()->getContent();
|
||||
$this->assertFalse(strpos($raw, chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
|
||||
$this->assertEqual(strpos($this->drupalGetHeader('Cache-Control'), 'no-cache'), FALSE, 'Cache-Control header contains \'no-cache\' to prevent caching.');
|
||||
|
||||
if ($clean_url) {
|
||||
// Add some extra chars to the token.
|
||||
$this->drupalGet(str_replace(IMAGE_DERIVATIVE_TOKEN . '=', IMAGE_DERIVATIVE_TOKEN . '=Zo', $generate_url));
|
||||
$this->assertResponse(200, 'Existing image was accessible at the URL with an invalid token.');
|
||||
}
|
||||
}
|
||||
|
||||
// Allow insecure image derivatives to be created for the remainder of this
|
||||
// test.
|
||||
$this->config('image.settings')
|
||||
->set('allow_insecure_derivatives', TRUE)
|
||||
->save();
|
||||
|
||||
// Create another working copy of the file.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = array_shift($files);
|
||||
$original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME);
|
||||
// Let the image_module_test module know about this file, so it can claim
|
||||
// ownership in hook_file_download().
|
||||
\Drupal::state()->set('image.test_file_download', $original_uri);
|
||||
|
||||
// Suppress the security token in the URL, then get the URL of a file that
|
||||
// has not been created and try to create it. Check that the security token
|
||||
// is not present in the URL but that the image is still accessible.
|
||||
$this->config('image.settings')->set('suppress_itok_output', TRUE)->save();
|
||||
$generated_uri = $this->style->buildUri($original_uri);
|
||||
$this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.');
|
||||
$generate_url = $this->style->buildUrl($original_uri, $clean_url);
|
||||
$this->assertIdentical(strpos($generate_url, IMAGE_DERIVATIVE_TOKEN . '='), FALSE, 'The security token does not appear in the image style URL.');
|
||||
$this->drupalGet($generate_url);
|
||||
$this->assertResponse(200, 'Image was accessible at the URL with a missing token.');
|
||||
|
||||
// Stop suppressing the security token in the URL.
|
||||
$this->config('image.settings')->set('suppress_itok_output', FALSE)->save();
|
||||
// Ensure allow_insecure_derivatives is enabled.
|
||||
$this->assertEqual($this->config('image.settings')
|
||||
->get('allow_insecure_derivatives'), TRUE);
|
||||
// Check that a security token is still required when generating a second
|
||||
// image derivative using the first one as a source.
|
||||
$nested_url = $this->style->buildUrl($generated_uri, $clean_url);
|
||||
$matches_expected_url_format = (boolean) preg_match('/styles\/' . $this->style->id() . '\/' . $scheme . '\/styles\/' . $this->style->id() . '\/' . $scheme . '/', $nested_url);
|
||||
$this->assertTrue($matches_expected_url_format, "URL for a derivative of an image style matches expected format.");
|
||||
$nested_url_with_wrong_token = str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $nested_url);
|
||||
$this->drupalGet($nested_url_with_wrong_token);
|
||||
$this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token.');
|
||||
// Check that this restriction cannot be bypassed by adding extra slashes
|
||||
// to the URL.
|
||||
$this->drupalGet(substr_replace($nested_url_with_wrong_token, '//styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/')));
|
||||
$this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with an extra forward slash in the URL.');
|
||||
$this->drupalGet(substr_replace($nested_url_with_wrong_token, '////styles/', strrpos($nested_url_with_wrong_token, '/styles/'), strlen('/styles/')));
|
||||
$this->assertResponse(404, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token, even with multiple forward slashes in the URL.');
|
||||
// Make sure the image can still be generated if a correct token is used.
|
||||
$this->drupalGet($nested_url);
|
||||
$this->assertResponse(200, 'Image was accessible when a correct token was provided in the URL.');
|
||||
|
||||
// Check that requesting a nonexistent image does not create any new
|
||||
// directories in the file system.
|
||||
$directory = $scheme . '://styles/' . $this->style->id() . '/' . $scheme . '/' . $this->randomMachineName();
|
||||
$this->drupalGet(file_create_url($directory . '/' . $this->randomString()));
|
||||
$this->assertFalse(file_exists($directory), 'New directory was not created in the filesystem when requesting an unauthorized image.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the endpoints used by the "image" in-place editor.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class QuickEditImageControllerTest extends BrowserTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'image', 'quickedit'];
|
||||
|
||||
/**
|
||||
* The machine name of our image field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* A user with permissions to edit articles and use Quick Edit.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $contentAuthorUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the Article node type.
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
|
||||
// Log in as a content author who can use Quick Edit and edit Articles.
|
||||
$this->contentAuthorUser = $this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'access in-place editing',
|
||||
'access content',
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'delete any article content',
|
||||
]);
|
||||
$this->drupalLogin($this->contentAuthorUser);
|
||||
|
||||
// Create a field with basic resolution validators.
|
||||
$this->fieldName = strtolower($this->randomMachineName());
|
||||
$field_settings = [
|
||||
'max_resolution' => '100x',
|
||||
'min_resolution' => '50x',
|
||||
];
|
||||
$this->createImageField($this->fieldName, 'article', [], $field_settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that routes restrict access for un-privileged users.
|
||||
*/
|
||||
public function testAccess() {
|
||||
// Create an anonymous user.
|
||||
$user = $this->createUser();
|
||||
$this->drupalLogin($user);
|
||||
|
||||
// Create a test Node.
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'title' => t('Test Node'),
|
||||
]);
|
||||
$this->drupalGet('quickedit/image/info/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default');
|
||||
$this->assertResponse('403');
|
||||
|
||||
/** @var \Symfony\Component\BrowserKit\Client $client */
|
||||
$client = $this->getSession()->getDriver()->getClient();
|
||||
$client->request('POST', '/quickedit/image/upload/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default');
|
||||
$this->assertEquals('403', $client->getResponse()->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the field info route returns expected data.
|
||||
*/
|
||||
public function testFieldInfo() {
|
||||
// Create a test Node.
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'title' => t('Test Node'),
|
||||
]);
|
||||
$json = $this->drupalGet('quickedit/image/info/node/' . $node->id() . '/' . $this->fieldName . '/' . $node->language()->getId() . '/default', ['query' => ['_format' => 'json']]);
|
||||
$info = Json::decode($json);
|
||||
// Assert that the default settings for our field are respected by our JSON
|
||||
// endpoint.
|
||||
$this->assertTrue($info['alt_field']);
|
||||
$this->assertFalse($info['title_field']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that uploading a valid image works.
|
||||
*/
|
||||
public function testValidImageUpload() {
|
||||
// Create a test Node.
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'title' => t('Test Node'),
|
||||
]);
|
||||
|
||||
// We want a test image that is a valid size.
|
||||
$valid_image = FALSE;
|
||||
$image_factory = $this->container->get('image.factory');
|
||||
foreach ($this->drupalGetTestFiles('image') as $image) {
|
||||
$image_file = $image_factory->get($image->uri);
|
||||
if ($image_file->getWidth() > 50 && $image_file->getWidth() < 100) {
|
||||
$valid_image = $image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($valid_image);
|
||||
|
||||
$this->drupalLogin($this->contentAuthorUser);
|
||||
$this->uploadImage($valid_image, $node->id(), $this->fieldName, $node->language()->getId());
|
||||
$this->assertContains('"fid":"1"', $this->getSession()->getPage()->getContent(), 'Valid upload completed successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that uploading a invalid image does not work.
|
||||
*/
|
||||
public function testInvalidUpload() {
|
||||
// Create a test Node.
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'title' => t('Test Node'),
|
||||
]);
|
||||
|
||||
// We want a test image that will fail validation.
|
||||
$invalid_image = FALSE;
|
||||
/** @var \Drupal\Core\Image\ImageFactory $image_factory */
|
||||
$image_factory = $this->container->get('image.factory');
|
||||
foreach ($this->drupalGetTestFiles('image') as $image) {
|
||||
/** @var \Drupal\Core\Image\ImageInterface $image_file */
|
||||
$image_file = $image_factory->get($image->uri);
|
||||
if ($image_file->getWidth() < 50 || $image_file->getWidth() > 100) {
|
||||
$invalid_image = $image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($invalid_image);
|
||||
|
||||
$this->drupalLogin($this->contentAuthorUser);
|
||||
$this->uploadImage($invalid_image, $node->id(), $this->fieldName, $node->language()->getId());
|
||||
$this->assertContains('"main_error":"The image failed validation."', $this->getSession()->getPage()->getContent(), 'Invalid upload returned errors.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads an image using the image module's Quick Edit route.
|
||||
*
|
||||
* @param object $image
|
||||
* The image to upload.
|
||||
* @param int $nid
|
||||
* The target node ID.
|
||||
* @param string $field_name
|
||||
* The target field machine name.
|
||||
* @param string $langcode
|
||||
* The langcode to use when setting the field's value.
|
||||
*/
|
||||
public function uploadImage($image, $nid, $field_name, $langcode) {
|
||||
$filepath = $this->container->get('file_system')->realpath($image->uri);
|
||||
$path = 'quickedit/image/upload/node/' . $nid . '/' . $field_name . '/' . $langcode . '/default';
|
||||
|
||||
$this->prepareRequest();
|
||||
$client = $this->getSession()->getDriver()->getClient();
|
||||
$client->request('POST', $this->buildUrl($path, []), [], ['files[image]' => $filepath]);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleJsonAnonTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleJsonBasicAuthTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleJsonCookieTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
/**
|
||||
* ResourceTestBase for ImageStyle entity.
|
||||
*/
|
||||
abstract class ImageStyleResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'image_style';
|
||||
|
||||
/**
|
||||
* The ImageStyle entity.
|
||||
*
|
||||
* @var \Drupal\image\ImageStyleInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The effect UUID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $effectUuid;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['administer image styles']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
// Create a "Camelids" image style.
|
||||
$camelids = ImageStyle::create([
|
||||
'name' => 'camelids',
|
||||
'label' => 'Camelids',
|
||||
]);
|
||||
|
||||
// Add an image effect.
|
||||
$effect = [
|
||||
'id' => 'image_scale_and_crop',
|
||||
'data' => [
|
||||
'anchor' => 'center-center',
|
||||
'width' => 120,
|
||||
'height' => 121,
|
||||
],
|
||||
'weight' => 0,
|
||||
];
|
||||
$this->effectUuid = $camelids->addImageEffect($effect);
|
||||
|
||||
$camelids->save();
|
||||
|
||||
return $camelids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return [
|
||||
'dependencies' => [],
|
||||
'effects' => [
|
||||
$this->effectUuid => [
|
||||
'uuid' => $this->effectUuid,
|
||||
'id' => 'image_scale_and_crop',
|
||||
'weight' => 0,
|
||||
'data' => [
|
||||
'anchor' => 'center-center',
|
||||
'width' => 120,
|
||||
'height' => 121,
|
||||
],
|
||||
],
|
||||
],
|
||||
'label' => 'Camelids',
|
||||
'langcode' => 'en',
|
||||
'name' => 'camelids',
|
||||
'status' => TRUE,
|
||||
'uuid' => $this->entity->uuid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedUnauthorizedAccessMessage($method) {
|
||||
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
return "The 'administer image styles' permission is required.";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleXmlAnonTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet() {
|
||||
// @todo Remove this method override in https://www.drupal.org/node/2905655
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleXmlBasicAuthTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet() {
|
||||
// @todo Remove this method override in https://www.drupal.org/node/2905655
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ImageStyleXmlCookieTest extends ImageStyleResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testGet() {
|
||||
// @todo Remove this method override in https://www.drupal.org/node/2905655
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests Image update path.
|
||||
*
|
||||
* @group image
|
||||
* @group legacy
|
||||
*/
|
||||
class ImageUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image_post_update_image_style_dependencies().
|
||||
*
|
||||
* @see image_post_update_image_style_dependencies()
|
||||
*/
|
||||
public function testPostUpdateImageStylesDependencies() {
|
||||
$view = 'core.entity_view_display.node.article.default';
|
||||
$form = 'core.entity_form_display.node.article.default';
|
||||
|
||||
// Check that view display 'node.article.default' doesn't depend on image
|
||||
// style 'image.style.large'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertFalse(in_array('image.style.large', $dependencies));
|
||||
// Check that form display 'node.article.default' doesn't depend on image
|
||||
// style 'image.style.thumbnail'.
|
||||
$dependencies = $this->config($form)->get('dependencies.config');
|
||||
$this->assertFalse(in_array('image.style.thumbnail', $dependencies));
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that view display 'node.article.default' depend on image style
|
||||
// 'image.style.large'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertTrue(in_array('image.style.large', $dependencies));
|
||||
// Check that form display 'node.article.default' depend on image style
|
||||
// 'image.style.thumbnail'.
|
||||
$dependencies = $this->config($view)->get('dependencies.config');
|
||||
$this->assertTrue(in_array('image.style.large', $dependencies));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional\Update;
|
||||
|
||||
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests adding an 'anchor' setting to existing scale and crop image effects.
|
||||
*
|
||||
* @see image_post_update_scale_and_crop_effect_add_anchor()
|
||||
*
|
||||
* @group Update
|
||||
* @group legacy
|
||||
*/
|
||||
class ScaleAndCropAddAnchorUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../fixtures/update/test_scale_and_crop_add_anchor.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that 'anchor' setting is properly added.
|
||||
*/
|
||||
public function testImagePostUpdateScaleAndCropEffectAddAnchor() {
|
||||
// Test that the first effect does not have an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.8c7170c9-5bcc-40f9-8698-f88a8be6d434.data');
|
||||
$this->assertFalse(array_key_exists('anchor', $effect_data));
|
||||
|
||||
// Test that the second effect has an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.a8d83b12-abc6-40c8-9c2f-78a4e421cf97.data');
|
||||
$this->assertTrue(array_key_exists('anchor', $effect_data));
|
||||
|
||||
// Test that the third effect does not have an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.1bffd475-19d0-439a-b6a1-7e5850ce40f9.data');
|
||||
$this->assertFalse(array_key_exists('anchor', $effect_data));
|
||||
|
||||
$this->runUpdates();
|
||||
|
||||
// Test that the first effect now has an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.8c7170c9-5bcc-40f9-8698-f88a8be6d434.data');
|
||||
$this->assertTrue(array_key_exists('anchor', $effect_data));
|
||||
$this->assertEquals('center-center', $effect_data['anchor']);
|
||||
|
||||
// Test that the second effect's 'anchor' setting is unchanged.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.a8d83b12-abc6-40c8-9c2f-78a4e421cf97.data');
|
||||
$this->assertTrue(array_key_exists('anchor', $effect_data));
|
||||
$this->assertEquals('left-top', $effect_data['anchor']);
|
||||
|
||||
// Test that the third effect still does not have an 'anchor' setting.
|
||||
$effect_data = $this->config('image.style.test_scale_and_crop_add_anchor')->get('effects.1bffd475-19d0-439a-b6a1-7e5850ce40f9.data');
|
||||
$this->assertFalse(array_key_exists('anchor', $effect_data));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
|
||||
/**
|
||||
* Tests creation, deletion, and editing of image styles and effects.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageAdminStylesTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* Tests editing Ajax-enabled image effect forms.
|
||||
*/
|
||||
public function testAjaxEnabledEffectForm() {
|
||||
$admin_path = 'admin/config/media/image-styles';
|
||||
|
||||
// Setup a style to be created and effects to add to it.
|
||||
$style_name = strtolower($this->randomMachineName(10));
|
||||
$style_label = $this->randomString();
|
||||
$style_path = $admin_path . '/manage/' . $style_name;
|
||||
$effect_edit = [
|
||||
'data[test_parameter]' => 100,
|
||||
];
|
||||
|
||||
// Add style form.
|
||||
$page = $this->getSession()->getPage();
|
||||
$assert = $this->assertSession();
|
||||
$this->drupalGet($admin_path . '/add');
|
||||
$page->findField('label')->setValue($style_label);
|
||||
$assert->waitForElementVisible('named', ['button', 'Edit'])->press();
|
||||
$assert->waitForElementVisible('named', ['id_or_name', 'name'])->setValue($style_name);
|
||||
$page->pressButton('Create new style');
|
||||
$assert->pageTextContains("Style $style_label was created.");
|
||||
|
||||
// Add two Ajax-enabled test effects.
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_module_test_ajax'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Add effect'));
|
||||
$this->drupalPostForm($style_path, ['new' => 'image_module_test_ajax'], t('Add'));
|
||||
$this->drupalPostForm(NULL, $effect_edit, t('Add effect'));
|
||||
|
||||
// Load the saved image style.
|
||||
$style = ImageStyle::load($style_name);
|
||||
|
||||
// Edit back the effects.
|
||||
foreach ($style->getEffects() as $uuid => $effect) {
|
||||
$effect_path = $admin_path . '/manage/' . $style_name . '/effects/' . $uuid;
|
||||
$this->drupalGet($effect_path);
|
||||
$page->findField('data[test_parameter]')->setValue(111);
|
||||
$ajax_value = $page->find('css', '#ajax-value')->getText();
|
||||
$this->assertSame('Ajax value bar', $ajax_value);
|
||||
$this->getSession()->getPage()->pressButton('Ajax refresh');
|
||||
$this->assertTrue($page->waitFor(10, function ($page) {
|
||||
$ajax_value = $page->find('css', '#ajax-value')->getText();
|
||||
return preg_match('/^Ajax value [0-9.]+ [0-9.]+$/', $ajax_value);
|
||||
}));
|
||||
$page->pressButton('Update effect');
|
||||
$assert->pageTextContains('The image effect was successfully applied.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* This class provides methods specifically for testing Image's field handling.
|
||||
*/
|
||||
abstract class ImageFieldTestBase extends WebDriverTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'node',
|
||||
'image',
|
||||
'field_ui',
|
||||
'image_module_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* An user with permissions to administer content types and image styles.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Basic page and Article node types.
|
||||
if ($this->profile !== 'standard') {
|
||||
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
|
||||
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
|
||||
}
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'access administration pages',
|
||||
'administer site configuration',
|
||||
'administer content types',
|
||||
'administer node fields',
|
||||
'administer nodes',
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'delete any article content',
|
||||
'administer image styles',
|
||||
'administer node display',
|
||||
]);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests validation functions such as min/max resolution.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldValidateTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* Test the validation message is displayed only once for ajax uploads.
|
||||
*/
|
||||
public function testAJAXValidationMessage() {
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$this->createImageField($field_name, 'article', ['cardinality' => -1]);
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
/** @var \Drupal\file\FileInterface[] $text_files */
|
||||
$text_files = $this->drupalGetTestFiles('text');
|
||||
$text_file = reset($text_files);
|
||||
|
||||
$field = $this->getSession()->getPage()->findField('files[' . $field_name . '_0][]');
|
||||
$field->attachFile($this->container->get('file_system')->realpath($text_file->uri));
|
||||
$this->assertSession()->waitForElement('css', '.messages--error');
|
||||
|
||||
$elements = $this->xpath('//div[contains(@class, :class)]', [
|
||||
':class' => 'messages--error',
|
||||
]);
|
||||
$this->assertEqual(count($elements), 1, 'Ajax validation messages are displayed once.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that image field validation works with other form submit handlers.
|
||||
*/
|
||||
public function testFriendlyAjaxValidation() {
|
||||
// Add a custom field to the Article content type that contains an AJAX
|
||||
// handler on a select field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_dummy_select',
|
||||
'type' => 'image_module_test_dummy_ajax',
|
||||
'entity_type' => 'node',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'article',
|
||||
'field_name' => 'field_dummy_select',
|
||||
'label' => t('Dummy select'),
|
||||
])->save();
|
||||
|
||||
\Drupal::entityTypeManager()
|
||||
->getStorage('entity_form_display')
|
||||
->load('node.article.default')
|
||||
->setComponent(
|
||||
'field_dummy_select',
|
||||
[
|
||||
'type' => 'image_module_test_dummy_ajax_widget',
|
||||
'weight' => 1,
|
||||
])
|
||||
->save();
|
||||
|
||||
// Then, add an image field.
|
||||
$this->createImageField('field_dummy_image', 'article');
|
||||
|
||||
// Open an article and trigger the AJAX handler.
|
||||
$this->drupalGet('node/add/article');
|
||||
$id = $this->getSession()->getPage()->find('css', '[name="form_build_id"]')->getValue();
|
||||
$field = $this->getSession()->getPage()->findField('field_dummy_select[select_widget]');
|
||||
$field->setValue('bam');
|
||||
// Make sure that the operation did not end with an exception.
|
||||
$this->assertSession()->waitForElement('css', "[name='form_build_id']:not([value='$id'])");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
/**
|
||||
* @see \Drupal\image\Plugin\InPlaceEditor\Image
|
||||
* @see \Drupal\Tests\quickedit\FunctionalJavascript\QuickEditJavascriptTestBase
|
||||
*/
|
||||
trait QuickEditImageEditorTestTrait {
|
||||
|
||||
/**
|
||||
* Awaits the 'image' in-place editor.
|
||||
*/
|
||||
protected function awaitImageEditor() {
|
||||
$this->assertJsCondition('document.querySelector(".quickedit-image-field-info") !== null', 10000);
|
||||
|
||||
$quickedit_entity_toolbar = $this->getSession()->getPage()->findById('quickedit-entity-toolbar');
|
||||
$this->assertNotNull($quickedit_entity_toolbar->find('css', 'form.quickedit-image-field-info input[name="alt"]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates typing in the 'image' in-place editor 'alt' attribute text input.
|
||||
*
|
||||
* @param string $text
|
||||
* The text to type.
|
||||
*/
|
||||
protected function typeInImageEditorAltTextInput($text) {
|
||||
$quickedit_entity_toolbar = $this->getSession()->getPage()->findById('quickedit-entity-toolbar');
|
||||
$input = $quickedit_entity_toolbar->find('css', 'form.quickedit-image-field-info input[name="alt"]');
|
||||
$input->setValue($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates dragging and dropping an image on the 'image' in-place editor.
|
||||
*
|
||||
* @param string $file_uri
|
||||
* The URI of the image file to drag and drop.
|
||||
*/
|
||||
protected function dropImageOnImageEditor($file_uri) {
|
||||
// Our headless browser can't drag+drop files, but we can mock the event.
|
||||
// Append a hidden upload element to the DOM.
|
||||
$script = 'jQuery("<input id=\"quickedit-image-test-input\" type=\"file\" />").appendTo("body")';
|
||||
$this->getSession()->executeScript($script);
|
||||
|
||||
// Find the element, and set its value to our new image.
|
||||
$input = $this->assertSession()->elementExists('css', '#quickedit-image-test-input');
|
||||
$filepath = $this->container->get('file_system')->realpath($file_uri);
|
||||
$input->attachFile($filepath);
|
||||
|
||||
// Trigger the upload logic with a mock "drop" event.
|
||||
$script = 'var e = jQuery.Event("drop");'
|
||||
. 'e.originalEvent = {dataTransfer: {files: jQuery("#quickedit-image-test-input").get(0).files}};'
|
||||
. 'e.preventDefault = e.stopPropagation = function () {};'
|
||||
. 'jQuery(".quickedit-image-dropzone").trigger(e);';
|
||||
$this->getSession()->executeScript($script);
|
||||
|
||||
// Wait for the dropzone element to be removed (i.e. loading is done).
|
||||
$js_condition = <<<JS
|
||||
function () {
|
||||
var activeFieldID = Drupal.quickedit.collections.entities
|
||||
.findWhere({state:'opened'})
|
||||
.get('fields')
|
||||
.filter(function (fieldModel) {
|
||||
var state = fieldModel.get('state');
|
||||
return state === 'active' || state === 'changed';
|
||||
})[0]
|
||||
.get('fieldID')
|
||||
return document.querySelector('[data-quickedit-field-id="' + activeFieldID + '"] .quickedit-image-dropzone') === null;
|
||||
}();
|
||||
JS;
|
||||
|
||||
$this->assertJsCondition($js_condition, 20000);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -3,24 +3,24 @@
|
|||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\quickedit\FunctionalJavascript\QuickEditJavascriptTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the JavaScript functionality of the "image" in-place editor.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\image\Plugin\InPlaceEditor\Image
|
||||
* @group image
|
||||
*/
|
||||
class QuickEditImageTest extends JavascriptTestBase {
|
||||
class QuickEditImageTest extends QuickEditJavascriptTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait;
|
||||
use QuickEditImageEditorTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'image', 'field_ui', 'contextual', 'quickedit', 'toolbar'];
|
||||
public static $modules = ['node', 'image', 'field_ui'];
|
||||
|
||||
/**
|
||||
* A user with permissions to edit Articles and use Quick Edit.
|
||||
|
@ -52,9 +52,12 @@ class QuickEditImageTest extends JavascriptTestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Tests if an image can be uploaded inline with Quick Edit.
|
||||
* Test that quick editor works correctly with images.
|
||||
*
|
||||
* @covers ::isCompatible
|
||||
* @covers ::getAttachments
|
||||
*/
|
||||
public function testUpload() {
|
||||
public function testImageInPlaceEditor() {
|
||||
// Create a field with a basic filetype restriction.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$field_settings = [
|
||||
|
@ -114,52 +117,82 @@ class QuickEditImageTest extends JavascriptTestBase {
|
|||
// Assert that the initial image is present.
|
||||
$this->assertSession()->elementExists('css', $entity_selector . ' ' . $field_selector . ' ' . $original_image_selector);
|
||||
|
||||
// Wait until Quick Edit loads.
|
||||
$condition = "jQuery('" . $entity_selector . " .quickedit').length > 0";
|
||||
$this->assertJsCondition($condition, 10000);
|
||||
// Initial state.
|
||||
$this->awaitQuickEditForEntity('node', 1);
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'closed',
|
||||
]);
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'inactive',
|
||||
'node/1/uid/en/full' => 'inactive',
|
||||
'node/1/created/en/full' => 'inactive',
|
||||
'node/1/body/en/full' => 'inactive',
|
||||
'node/1/' . $field_name . '/en/full' => 'inactive',
|
||||
]);
|
||||
|
||||
// Initiate Quick Editing.
|
||||
$this->click('.contextual-toolbar-tab button');
|
||||
$this->click($entity_selector . ' [data-contextual-id] > button');
|
||||
$this->click($entity_selector . ' [data-contextual-id] .quickedit > a');
|
||||
// Start in-place editing of the article node.
|
||||
$this->startQuickEditViaToolbar('node', 1, 0);
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'opened',
|
||||
]);
|
||||
$this->assertQuickEditEntityToolbar((string) $node->label(), NULL);
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'candidate',
|
||||
]);
|
||||
|
||||
// Click the image field.
|
||||
$this->click($field_selector);
|
||||
|
||||
// Wait for the field info to load and set new alt text.
|
||||
$condition = "jQuery('.quickedit-image-field-info').length > 0";
|
||||
$this->assertJsCondition($condition, 10000);
|
||||
$input = $this->assertSession()->elementExists('css', '.quickedit-image-field-info input[name="alt"]');
|
||||
$input->setValue('New text');
|
||||
|
||||
// Check that our Dropzone element exists.
|
||||
$this->awaitImageEditor();
|
||||
$this->assertSession()->elementExists('css', $field_selector . ' .quickedit-image-dropzone');
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'active',
|
||||
]);
|
||||
|
||||
// Our headless browser can't drag+drop files, but we can mock the event.
|
||||
// Append a hidden upload element to the DOM.
|
||||
$script = 'jQuery("<input id=\"quickedit-image-test-input\" type=\"file\" />").appendTo("body")';
|
||||
$this->getSession()->executeScript($script);
|
||||
// Type new 'alt' text.
|
||||
$this->typeInImageEditorAltTextInput('New text');
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'changed',
|
||||
]);
|
||||
|
||||
// Find the element, and set its value to our new image.
|
||||
$input = $this->assertSession()->elementExists('css', '#quickedit-image-test-input');
|
||||
$filepath = $this->container->get('file_system')->realpath($valid_images[1]->uri);
|
||||
$input->attachFile($filepath);
|
||||
|
||||
// Trigger the upload logic with a mock "drop" event.
|
||||
$script = 'var e = jQuery.Event("drop");'
|
||||
. 'e.originalEvent = {dataTransfer: {files: jQuery("#quickedit-image-test-input").get(0).files}};'
|
||||
. 'e.preventDefault = e.stopPropagation = function () {};'
|
||||
. 'jQuery(".quickedit-image-dropzone").trigger(e);';
|
||||
$this->getSession()->executeScript($script);
|
||||
|
||||
// Wait for the dropzone element to be removed (i.e. loading is done).
|
||||
$condition = "jQuery('" . $field_selector . " .quickedit-image-dropzone').length == 0";
|
||||
$this->assertJsCondition($condition, 20000);
|
||||
// Drag and drop an image.
|
||||
$this->dropImageOnImageEditor($valid_images[1]->uri);
|
||||
|
||||
// To prevent 403s on save, we re-set our request (cookie) state.
|
||||
$this->prepareRequest();
|
||||
|
||||
// Save the change.
|
||||
$this->click('.quickedit-button.action-save');
|
||||
$this->assertSession()->assertWaitOnAjaxRequest();
|
||||
// Click 'Save'.
|
||||
$this->saveQuickEdit();
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'committing',
|
||||
]);
|
||||
$this->assertEntityInstanceFieldStates('node', 1, 0, [
|
||||
'node/1/title/en/full' => 'candidate',
|
||||
'node/1/uid/en/full' => 'candidate',
|
||||
'node/1/created/en/full' => 'candidate',
|
||||
'node/1/body/en/full' => 'candidate',
|
||||
'node/1/' . $field_name . '/en/full' => 'saving',
|
||||
]);
|
||||
$this->assertEntityInstanceFieldMarkup('node', 1, 0, [
|
||||
'node/1/' . $field_name . '/en/full' => '.quickedit-changed',
|
||||
]);
|
||||
|
||||
// Wait for the saving of the image field to complete.
|
||||
$this->assertJsCondition("Drupal.quickedit.collections.entities.get('node/1[0]').get('state') === 'closed'");
|
||||
$this->assertEntityInstanceStates([
|
||||
'node/1[0]' => 'closed',
|
||||
]);
|
||||
|
||||
// Re-visit the page to make sure the edit worked.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
|
||||
/**
|
||||
|
@ -56,7 +57,7 @@ class ImageFormatterTest extends FieldKernelTestBase {
|
|||
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldName = mb_strtolower($this->randomMachineName());
|
||||
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => $this->entityType,
|
||||
|
@ -99,4 +100,89 @@ class ImageFormatterTest extends FieldKernelTestBase {
|
|||
$this->assertEquals($entity->{$this->fieldName}[1]->entity->getCacheTags(), $build[$this->fieldName][1]['#cache']['tags'], 'Second image cache tags is as expected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ImageFormatter's handling of SVG images.
|
||||
*
|
||||
* @requires extension gd
|
||||
*/
|
||||
public function testImageFormatterSvg() {
|
||||
// Install the default image styles.
|
||||
$this->installConfig(['image']);
|
||||
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
$png = File::create([
|
||||
'uri' => 'public://test-image.png',
|
||||
]);
|
||||
$png->save();
|
||||
|
||||
// We need to create an actual empty PNG, or the GD toolkit will not
|
||||
// consider the image valid.
|
||||
$png_resource = imagecreate(300, 300);
|
||||
imagefill($png_resource, 0, 0, imagecolorallocate($png_resource, 0, 0, 0));
|
||||
imagepng($png_resource, $png->getFileUri());
|
||||
|
||||
$svg = File::create([
|
||||
'uri' => 'public://test-image.svg',
|
||||
]);
|
||||
$svg->save();
|
||||
// We don't have to put any real SVG data in here, because the GD toolkit
|
||||
// won't be able to load it anyway.
|
||||
touch($svg->getFileUri());
|
||||
|
||||
$entity = EntityTest::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
$this->fieldName => [$png, $svg],
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
// Ensure that the display is using the medium image style.
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['settings']['image_style'] = 'medium';
|
||||
$this->display->setComponent($this->fieldName, $component)->save();
|
||||
|
||||
$build = $this->display->build($entity);
|
||||
|
||||
// The first image is a PNG, so it is supported by the GD image toolkit.
|
||||
// The image style should be applied with its cache tags, image derivative
|
||||
// computed with its URI and dimensions.
|
||||
$this->assertCacheTags($build[$this->fieldName][0], ImageStyle::load('medium')->getCacheTags());
|
||||
$renderer->renderRoot($build[$this->fieldName][0]);
|
||||
$this->assertEquals('medium', $build[$this->fieldName][0]['#image_style']);
|
||||
// We check that the image URL contains the expected style directory
|
||||
// structure.
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'styles/medium/public/test-image.png') !== FALSE);
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'width="220"') !== FALSE);
|
||||
$this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'height="220"') !== FALSE);
|
||||
|
||||
// The second image is an SVG, which is not supported by the GD toolkit.
|
||||
// The image style should still be applied with its cache tags, but image
|
||||
// derivative will not be available so <img> tag will point to the original
|
||||
// image.
|
||||
$this->assertCacheTags($build[$this->fieldName][1], ImageStyle::load('medium')->getCacheTags());
|
||||
$renderer->renderRoot($build[$this->fieldName][1]);
|
||||
$this->assertEquals('medium', $build[$this->fieldName][1]['#image_style']);
|
||||
// We check that the image URL does not contain the style directory
|
||||
// structure.
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'styles/medium/public/test-image.svg'));
|
||||
// Since we did not store original image dimensions, width and height
|
||||
// HTML attributes will not be present.
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'width'));
|
||||
$this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'height'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a renderable array has a set of cache tags.
|
||||
*
|
||||
* @param array $renderable
|
||||
* The renderable array. Must have a #cache[tags] element.
|
||||
* @param array $cache_tags
|
||||
* The expected cache tags.
|
||||
*/
|
||||
protected function assertCacheTags(array $renderable, array $cache_tags) {
|
||||
$diff = array_diff($cache_tags, $renderable['#cache']['tags']);
|
||||
$this->assertEmpty($diff);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class ImageImportTest extends KernelTestBase {
|
|||
*/
|
||||
public function testImport() {
|
||||
$style = ImageStyle::create([
|
||||
'name' => 'test'
|
||||
'name' => 'test',
|
||||
]);
|
||||
|
||||
$style->addImageEffect(['id' => 'image_module_test_null']);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
@ -10,6 +11,7 @@ use Drupal\field\Entity\FieldConfig;
|
|||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the image field type.
|
||||
|
@ -40,6 +42,14 @@ class ImageItemTest extends FieldKernelTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['user']);
|
||||
// Give anonymous users permission to access content, so that we can view
|
||||
// and download public file.
|
||||
$anonymous_role = Role::load(Role::ANONYMOUS_ID);
|
||||
$anonymous_role->grantPermission('access content');
|
||||
$anonymous_role->save();
|
||||
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
|
||||
|
@ -57,7 +67,7 @@ class ImageItemTest extends FieldKernelTestBase {
|
|||
'file_extensions' => 'jpg',
|
||||
],
|
||||
])->save();
|
||||
file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
$this->image = File::create([
|
||||
'uri' => 'public://example.jpg',
|
||||
]);
|
||||
|
@ -90,7 +100,7 @@ class ImageItemTest extends FieldKernelTestBase {
|
|||
$this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid());
|
||||
|
||||
// Make sure the computed entity reflects updates to the referenced file.
|
||||
file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example-2.jpg');
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example-2.jpg');
|
||||
$image2 = File::create([
|
||||
'uri' => 'public://example-2.jpg',
|
||||
]);
|
||||
|
@ -129,4 +139,26 @@ class ImageItemTest extends FieldKernelTestBase {
|
|||
$this->assertEqual($entity->image_test->entity->get('filemime')->value, 'image/jpeg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a malformed image.
|
||||
*/
|
||||
public function testImageItemMalformed() {
|
||||
// Validate entity is an image and don't gather dimensions if it is not.
|
||||
$entity = EntityTest::create();
|
||||
$entity->image_test = NULL;
|
||||
$entity->image_test->target_id = 9999;
|
||||
// PHPUnit re-throws E_USER_WARNING as an exception.
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail('Exception did not fail');
|
||||
}
|
||||
catch (EntityStorageException $exception) {
|
||||
$this->assertInstanceOf(\PHPUnit_Framework_Error_Warning::class, $exception->getPrevious());
|
||||
$this->assertEquals($exception->getMessage(), 'Missing file with ID 9999.');
|
||||
$this->assertEmpty($entity->image_test->width);
|
||||
$this->assertEmpty($entity->image_test->height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests image theme functions.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageThemeFunctionTest extends KernelTestBase {
|
||||
|
||||
use TestFileCreationTrait {
|
||||
getTestFiles as drupalGetTestFiles;
|
||||
compareFiles as drupalCompareFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['entity_test', 'field', 'file', 'image', 'system', 'simpletest', 'user'];
|
||||
|
||||
/**
|
||||
* Created file entity.
|
||||
*
|
||||
* @var \Drupal\file\Entity\File
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Image\ImageFactory
|
||||
*/
|
||||
protected $imageFactory;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'image_test',
|
||||
'type' => 'image',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'image_test',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example.jpg');
|
||||
$this->image = File::create([
|
||||
'uri' => 'public://example.jpg',
|
||||
]);
|
||||
$this->image->save();
|
||||
$this->imageFactory = $this->container->get('image.factory');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests usage of the image field formatters.
|
||||
*/
|
||||
public function testImageFormatterTheme() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
// Create an image.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = reset($files);
|
||||
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
|
||||
|
||||
// Create a style.
|
||||
$style = ImageStyle::create(['name' => 'test', 'label' => 'Test']);
|
||||
$style->save();
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
|
||||
// Create a test entity with the image field set.
|
||||
$entity = EntityTest::create();
|
||||
$entity->image_test->target_id = $this->image->id();
|
||||
$entity->image_test->alt = NULL;
|
||||
$entity->image_test->uri = $original_uri;
|
||||
$image = $this->imageFactory->get('public://example.jpg');
|
||||
$entity->save();
|
||||
|
||||
// Create the base element that we'll use in the tests below.
|
||||
$path = $this->randomMachineName();
|
||||
$base_element = [
|
||||
'#theme' => 'image_formatter',
|
||||
'#image_style' => 'test',
|
||||
'#item' => $entity->image_test,
|
||||
'#url' => Url::fromUri('base:' . $path),
|
||||
];
|
||||
|
||||
// Test using theme_image_formatter() with a NULL value for the alt option.
|
||||
$element = $base_element;
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//a[@href=:path]/img[@src=:url and @width=:width and @height=:height]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders with a NULL value for the alt option.');
|
||||
|
||||
// Test using theme_image_formatter() without an image title, alt text, or
|
||||
// link options.
|
||||
$element = $base_element;
|
||||
$element['#item']->alt = '';
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//a[@href=:path]/img[@src=:url and @width=:width and @height=:height and @alt=""]', [':path' => base_path() . $path, ':url' => $url, ':width' => $image->getWidth(), ':height' => $image->getHeight()]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders without title, alt, or path options.');
|
||||
|
||||
// Link the image to a fragment on the page, and not a full URL.
|
||||
$fragment = $this->randomMachineName();
|
||||
$element = $base_element;
|
||||
$element['#url'] = Url::fromRoute('<none>', [], ['fragment' => $fragment]);
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//a[@href=:fragment]/img[@src=:url and @width=:width and @height=:height and @alt=""]', [
|
||||
':fragment' => '#' . $fragment,
|
||||
':url' => $url,
|
||||
':width' => $image->getWidth(),
|
||||
':height' => $image->getHeight(),
|
||||
]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_formatter() correctly renders a link fragment.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests usage of the image style theme function.
|
||||
*/
|
||||
public function testImageStyleTheme() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
// Create an image.
|
||||
$files = $this->drupalGetTestFiles('image');
|
||||
$file = reset($files);
|
||||
$original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME);
|
||||
|
||||
// Create a style.
|
||||
$style = ImageStyle::create(['name' => 'image_test', 'label' => 'Test']);
|
||||
$style->save();
|
||||
$url = file_url_transform_relative($style->buildUrl($original_uri));
|
||||
|
||||
// Create the base element that we'll use in the tests below.
|
||||
$base_element = [
|
||||
'#theme' => 'image_style',
|
||||
'#style_name' => 'image_test',
|
||||
'#uri' => $original_uri,
|
||||
];
|
||||
|
||||
$element = $base_element;
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//img[@src=:url and @alt=""]', [':url' => $url]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly.');
|
||||
|
||||
// Test using theme_image_style() with a NULL value for the alt option.
|
||||
$element = $base_element;
|
||||
$element['#alt'] = NULL;
|
||||
$this->setRawContent($renderer->renderRoot($element));
|
||||
$elements = $this->xpath('//img[@src=:url]', [':url' => $url]);
|
||||
$this->assertEqual(count($elements), 1, 'theme_image_style() renders an image correctly with a NULL value for the alt option.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image alt attribute functionality.
|
||||
*/
|
||||
public function testImageAltFunctionality() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
// Test using alt directly with alt attribute.
|
||||
$image_with_alt_property = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => '/core/themes/bartik/logo.svg',
|
||||
'#alt' => 'Regular alt',
|
||||
'#title' => 'Test title',
|
||||
'#width' => '50%',
|
||||
'#height' => '50%',
|
||||
'#attributes' => ['class' => 'image-with-regular-alt', 'id' => 'my-img'],
|
||||
];
|
||||
|
||||
$this->setRawContent($renderer->renderRoot($image_with_alt_property));
|
||||
$elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', [":class" => "image-with-regular-alt", ":alt" => "Regular alt"]);
|
||||
$this->assertEqual(count($elements), 1, 'Regular alt displays correctly');
|
||||
|
||||
// Test using alt attribute inside attributes.
|
||||
$image_with_alt_attribute_alt_attribute = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => '/core/themes/bartik/logo.svg',
|
||||
'#width' => '50%',
|
||||
'#height' => '50%',
|
||||
'#attributes' => [
|
||||
'class' => 'image-with-attribute-alt',
|
||||
'id' => 'my-img',
|
||||
'title' => 'New test title',
|
||||
'alt' => 'Attribute alt',
|
||||
],
|
||||
];
|
||||
|
||||
$this->setRawContent($renderer->renderRoot($image_with_alt_attribute_alt_attribute));
|
||||
$elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', [":class" => "image-with-attribute-alt", ":alt" => "Attribute alt"]);
|
||||
$this->assertEqual(count($elements), 1, 'Attribute alt displays correctly');
|
||||
|
||||
// Test using alt attribute as property and inside attributes.
|
||||
$image_with_alt_attribute_both = [
|
||||
'#theme' => 'image',
|
||||
'#uri' => '/core/themes/bartik/logo.svg',
|
||||
'#width' => '50%',
|
||||
'#height' => '50%',
|
||||
'#alt' => 'Kitten sustainable',
|
||||
'#attributes' => [
|
||||
'class' => 'image-with-attribute-alt',
|
||||
'id' => 'my-img',
|
||||
'title' => 'New test title',
|
||||
'alt' => 'Attribute alt',
|
||||
],
|
||||
];
|
||||
|
||||
$this->setRawContent($renderer->renderRoot($image_with_alt_attribute_both));
|
||||
$elements = $this->xpath('//img[contains(@class, class) and contains(@alt, :alt)]', [":class" => "image-with-attribute-alt", ":alt" => "Attribute alt"]);
|
||||
$this->assertEqual(count($elements), 1, 'Attribute alt overrides alt property if both set.');
|
||||
}
|
||||
|
||||
}
|
|
@ -141,9 +141,11 @@ class MigrateImageCacheTest extends MigrateDrupal6TestBase {
|
|||
|
||||
$this->startCollectingMessages();
|
||||
$this->executeMigration('d6_imagecache_presets');
|
||||
$this->assertEqual(['error' => [
|
||||
'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.'
|
||||
]], $this->migrateMessages);
|
||||
$this->assertEqual([
|
||||
'error' => [
|
||||
'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.',
|
||||
],
|
||||
], $this->migrateMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase;
|
||||
use Drupal\Tests\file\Kernel\Migrate\d6\FileMigrationTestTrait;
|
||||
|
||||
/**
|
||||
* Image migration test.
|
||||
*
|
||||
* This extends the node test, because the D6 fixture has images; they just
|
||||
* need to be migrated into D8.
|
||||
*
|
||||
* @group migrate_drupal_6
|
||||
*/
|
||||
class MigrateImageTest extends MigrateNodeTestBase {
|
||||
|
||||
use FileMigrationTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['menu_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->setUpMigratedFiles();
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->executeMigrations([
|
||||
'd6_node',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image migration from Drupal 6 to 8.
|
||||
*/
|
||||
public function testNode() {
|
||||
$node = Node::load(9);
|
||||
// Test the image field sub fields.
|
||||
$this->assertSame('2', $node->field_test_imagefield->target_id);
|
||||
$this->assertSame('Test alt', $node->field_test_imagefield->alt);
|
||||
$this->assertSame('Test title', $node->field_test_imagefield->title);
|
||||
$this->assertSame('80', $node->field_test_imagefield->width);
|
||||
$this->assertSame('60', $node->field_test_imagefield->height);
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@ class MigrateImageStylesTest extends MigrateDrupal7TestBase {
|
|||
* Test the image styles migration.
|
||||
*/
|
||||
public function testImageStylesMigration() {
|
||||
$this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55], []]);
|
||||
$this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55, 'anchor' => 'center-center'], []]);
|
||||
$this->assertEntity('custom_image_style_2', "Custom image style 2", ['image_resize', 'image_rotate'], [['width' => 55, 'height' => 100], ['degrees' => 45, 'bgcolor' => '#FFFFFF', 'random' => FALSE]]);
|
||||
$this->assertEntity('custom_image_style_3', "Custom image style 3", ['image_scale', 'image_crop'], [['width' => 150, 'height' => NULL, 'upscale' => FALSE], ['width' => 50, 'height' => 50, 'anchor' => 'left-top']]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests image on user relationship handler.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class RelationshipUserImageDataTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['file', 'field', 'image', 'image_test_views', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_image_user_image_data'];
|
||||
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp($import_test_views);
|
||||
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
// Create the user profile field and instance.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'user',
|
||||
'field_name' => 'user_picture',
|
||||
'type' => 'image',
|
||||
'translatable' => '0',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'label' => 'User Picture',
|
||||
'description' => '',
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
'required' => 0,
|
||||
])->save();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['image_test_views']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using the views image relationship.
|
||||
*/
|
||||
public function testViewsHandlerRelationshipUserImageData() {
|
||||
$file = File::create([
|
||||
'fid' => 2,
|
||||
'uid' => 2,
|
||||
'filename' => 'image-test.jpg',
|
||||
'uri' => "public://image-test.jpg",
|
||||
'filemime' => 'image/jpeg',
|
||||
'created' => 1,
|
||||
'changed' => 1,
|
||||
'status' => FILE_STATUS_PERMANENT,
|
||||
]);
|
||||
$file->enforceIsNew();
|
||||
file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-1.png'));
|
||||
$file->save();
|
||||
|
||||
$account = User::create([
|
||||
'name' => 'foo',
|
||||
]);
|
||||
$account->user_picture->target_id = 2;
|
||||
$account->save();
|
||||
|
||||
$view = Views::getView('test_image_user_image_data');
|
||||
// Tests \Drupal\taxonomy\Plugin\views\relationship\NodeTermData::calculateDependencies().
|
||||
$expected = [
|
||||
'module' => [
|
||||
'file',
|
||||
'user',
|
||||
],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->getDependencies());
|
||||
$this->executeView($view);
|
||||
$expected_result = [
|
||||
[
|
||||
'file_managed_user__user_picture_fid' => '2',
|
||||
],
|
||||
];
|
||||
$column_map = ['file_managed_user__user_picture_fid' => 'file_managed_user__user_picture_fid'];
|
||||
$this->assertIdenticalResultset($view, $expected_result, $column_map);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Unit\Plugin\migrate\field\d7;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\image\Plugin\migrate\field\d7\ImageField;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\image\Plugin\migrate\field\d7\ImageField
|
||||
* @group image
|
||||
* @group legacy
|
||||
*/
|
||||
class ImageFieldTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->plugin = new ImageField([], 'image', []);
|
||||
|
||||
$migration = $this->prophesize(MigrationInterface::class);
|
||||
|
||||
// The plugin's processFieldValues() method will call
|
||||
// mergeProcessOfProperty() and return nothing. So, in order to examine the
|
||||
// process pipeline created by the plugin, we need to ensure that
|
||||
// getProcess() always returns the last input to mergeProcessOfProperty().
|
||||
$migration->mergeProcessOfProperty(Argument::type('string'), Argument::type('array'))
|
||||
->will(function ($arguments) use ($migration) {
|
||||
$migration->getProcess()->willReturn($arguments[1]);
|
||||
});
|
||||
$this->migration = $migration->reveal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::processFieldValues
|
||||
* @expectedDeprecation Deprecated in Drupal 8.6.0, to be removed before Drupal 9.0.0. Use defineValueProcessPipeline() instead. See https://www.drupal.org/node/2944598.
|
||||
*/
|
||||
public function testProcessFieldValues() {
|
||||
$this->plugin->processFieldValues($this->migration, 'somefieldname', []);
|
||||
|
||||
$expected = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => 'somefieldname',
|
||||
'process' => [
|
||||
'target_id' => 'fid',
|
||||
'alt' => 'alt',
|
||||
'title' => 'title',
|
||||
'width' => 'width',
|
||||
'height' => 'height',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expected, $this->migration->getProcess());
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue