Move all files to 2017/
This commit is contained in:
parent
ac7370f67f
commit
2875863330
15717 changed files with 0 additions and 0 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
2017/web/core/modules/image/tests/fixtures/update/test_scale_and_crop_add_anchor.php
vendored
Normal file
19
2017/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,15 @@
|
|||
image.effect.image_module_test_ajax:
|
||||
type: mapping
|
||||
label: 'Ajax test'
|
||||
mapping:
|
||||
test_parameter:
|
||||
type: integer
|
||||
label: 'Test Parameter'
|
||||
|
||||
image.style.*.third_party.image_module_test:
|
||||
type: mapping
|
||||
label: 'Schema for image_module_test module additions to image_style entity'
|
||||
mapping:
|
||||
foo:
|
||||
type: string
|
||||
label: 'Label for foo'
|
|
@ -0,0 +1,6 @@
|
|||
name: 'Image test'
|
||||
type: module
|
||||
description: 'Provides hook implementations for testing Image module functionality.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provides Image module hook implementations for testing purposes.
|
||||
*/
|
||||
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
|
||||
function image_module_test_file_download($uri) {
|
||||
$default_uri = \Drupal::state()->get('image.test_file_download') ?: FALSE;
|
||||
if ($default_uri == $uri) {
|
||||
return ['X-Image-Owned-By' => 'image_module_test'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_image_effect_info_alter().
|
||||
*
|
||||
* Used to keep a count of cache misses in \Drupal\image\ImageEffectManager.
|
||||
*/
|
||||
function image_module_test_image_effect_info_alter(&$effects) {
|
||||
$image_effects_definition_called = &drupal_static(__FUNCTION__, 0);
|
||||
$image_effects_definition_called++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_image_style_presave().
|
||||
*
|
||||
* Used to save test third party settings in the image style entity.
|
||||
*/
|
||||
function image_module_test_image_style_presave(ImageStyleInterface $style) {
|
||||
$style->setThirdPartySetting('image_module_test', 'foo', 'bar');
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\ImageEffect;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\HtmlCommand;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Image\ImageInterface;
|
||||
use Drupal\image\ConfigurableImageEffectBase;
|
||||
|
||||
/**
|
||||
* Provides a test effect using Ajax in the configuration form.
|
||||
*
|
||||
* @ImageEffect(
|
||||
* id = "image_module_test_ajax",
|
||||
* label = @Translation("Ajax test")
|
||||
* )
|
||||
*/
|
||||
class AjaxTestImageEffect extends ConfigurableImageEffectBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function defaultConfiguration() {
|
||||
return [
|
||||
'test_parameter' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form['test_parameter'] = [
|
||||
'#type' => 'number',
|
||||
'#title' => t('Test parameter'),
|
||||
'#default_value' => $this->configuration['test_parameter'],
|
||||
'#min' => 0,
|
||||
];
|
||||
$form['ajax_refresh'] = [
|
||||
'#type' => 'button',
|
||||
'#value' => $this->t('Ajax refresh'),
|
||||
'#ajax' => ['callback' => [$this, 'ajaxCallback']],
|
||||
];
|
||||
$form['ajax_value'] = [
|
||||
'#id' => 'ajax-value',
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Ajax value'),
|
||||
'#markup' => 'bar',
|
||||
];
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback.
|
||||
*/
|
||||
public function ajaxCallback($form, FormStateInterface $form_state) {
|
||||
$item = [
|
||||
'#type' => 'item',
|
||||
'#title' => $this->t('Ajax value'),
|
||||
'#markup' => microtime(),
|
||||
];
|
||||
$response = new AjaxResponse();
|
||||
$response->addCommand(new HtmlCommand('#ajax-value', $item));
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitConfigurationForm($form, $form_state);
|
||||
|
||||
$this->configuration['test_parameter'] = $form_state->getValue('test_parameter');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyEffect(ImageInterface $image) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\ImageEffect;
|
||||
|
||||
use Drupal\Core\Image\ImageInterface;
|
||||
use Drupal\image\ImageEffectBase;
|
||||
|
||||
/**
|
||||
* Performs no operation on an image resource.
|
||||
*
|
||||
* @ImageEffect(
|
||||
* id = "image_module_test_null",
|
||||
* label = @Translation("Image module test")
|
||||
* )
|
||||
*/
|
||||
class NullTestImageEffect extends ImageEffectBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transformDimensions(array &$dimensions, $uri) {
|
||||
// Unset image dimensions.
|
||||
$dimensions['width'] = $dimensions['height'] = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyEffect(ImageInterface $image) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\image_module_test\Plugin\ImageEffect;
|
||||
|
||||
use Drupal\Core\Image\ImageInterface;
|
||||
use Drupal\image\ImageEffectBase;
|
||||
|
||||
/**
|
||||
* Performs an image operation that depends on the URI of the original image.
|
||||
*
|
||||
* @ImageEffect(
|
||||
* id = "image_module_test_uri_dependent",
|
||||
* label = @Translation("URI dependent test image effect")
|
||||
* )
|
||||
*/
|
||||
class UriDependentTestImageEffect extends ImageEffectBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transformDimensions(array &$dimensions, $uri) {
|
||||
$dimensions = $this->getUriDependentDimensions($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyEffect(ImageInterface $image) {
|
||||
$dimensions = $this->getUriDependentDimensions($image->getSource());
|
||||
return $image->resize($dimensions['width'], $dimensions['height']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the image dimensions dependent on the image file extension.
|
||||
*
|
||||
* @param string $uri
|
||||
* Original image file URI.
|
||||
*
|
||||
* @return array
|
||||
* Associative array.
|
||||
* - width: Integer with the derivative image width.
|
||||
* - height: Integer with the derivative image height.
|
||||
*/
|
||||
protected function getUriDependentDimensions($uri) {
|
||||
$dimensions = [];
|
||||
$extension = pathinfo($uri, PATHINFO_EXTENSION);
|
||||
switch (strtolower($extension)) {
|
||||
case 'png':
|
||||
$dimensions['width'] = $dimensions['height'] = 100;
|
||||
break;
|
||||
|
||||
case 'gif':
|
||||
$dimensions['width'] = $dimensions['height'] = 50;
|
||||
break;
|
||||
|
||||
default:
|
||||
$dimensions['width'] = $dimensions['height'] = 20;
|
||||
break;
|
||||
|
||||
}
|
||||
return $dimensions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Image test views'
|
||||
type: module
|
||||
description: 'Provides default views for views image tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:image
|
||||
- drupal:views
|
|
@ -0,0 +1,77 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- file
|
||||
- user
|
||||
id: test_image_user_image_data
|
||||
label: test_image_user_image_data
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: users_field_data
|
||||
base_field: uid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access user profiles'
|
||||
cache:
|
||||
type: tag
|
||||
style:
|
||||
type: table
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
override: true
|
||||
sticky: false
|
||||
caption: ''
|
||||
summary: ''
|
||||
description: ''
|
||||
columns:
|
||||
name: name
|
||||
fid: fid
|
||||
info:
|
||||
name:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
fid:
|
||||
sortable: false
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
default: '-1'
|
||||
empty_table: false
|
||||
row:
|
||||
type: fields
|
||||
options:
|
||||
inline: { }
|
||||
separator: ''
|
||||
hide_empty: false
|
||||
default_field_elements: true
|
||||
relationships:
|
||||
user_picture_target_id:
|
||||
id: user_picture_target_id
|
||||
table: user__user_picture
|
||||
field: user_picture_target_id
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: 'image from user_picture'
|
||||
required: true
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\FunctionalTests\Image\ToolkitTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the image effects pass parameters to the toolkit correctly.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageEffectsTest extends ToolkitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'image_test', 'image_module_test'];
|
||||
|
||||
/**
|
||||
* The image effect manager.
|
||||
*
|
||||
* @var \Drupal\image\ImageEffectManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->manager = $this->container->get('plugin.manager.image.effect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_resize_effect() function.
|
||||
*/
|
||||
public function testResizeEffect() {
|
||||
$this->assertImageEffect('image_resize', [
|
||||
'width' => 1,
|
||||
'height' => 2,
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['resize']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['resize'][0][0], 1, 'Width was passed correctly');
|
||||
$this->assertEqual($calls['resize'][0][1], 2, 'Height was passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_scale_effect() function.
|
||||
*/
|
||||
public function testScaleEffect() {
|
||||
// @todo: need to test upscaling.
|
||||
$this->assertImageEffect('image_scale', [
|
||||
'width' => 10,
|
||||
'height' => 10,
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['scale']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['scale'][0][0], 10, 'Width was passed correctly');
|
||||
$this->assertEqual($calls['scale'][0][1], 10, 'Height was based off aspect ratio and passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_crop_effect() function.
|
||||
*/
|
||||
public function testCropEffect() {
|
||||
// @todo should test the keyword offsets.
|
||||
$this->assertImageEffect('image_crop', [
|
||||
'anchor' => 'top-1',
|
||||
'width' => 3,
|
||||
'height' => 4,
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['crop']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['crop'][0][0], 0, 'X was passed correctly');
|
||||
$this->assertEqual($calls['crop'][0][1], 1, 'Y was passed correctly');
|
||||
$this->assertEqual($calls['crop'][0][2], 3, 'Width was passed correctly');
|
||||
$this->assertEqual($calls['crop'][0][3], 4, 'Height was passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the ConvertImageEffect plugin.
|
||||
*/
|
||||
public function testConvertEffect() {
|
||||
// Test jpeg.
|
||||
$this->assertImageEffect('image_convert', [
|
||||
'extension' => 'jpeg',
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['convert']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['convert'][0][0], 'jpeg', 'Extension was passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_scale_and_crop_effect() function.
|
||||
*/
|
||||
public function testScaleAndCropEffect() {
|
||||
$this->assertImageEffect('image_scale_and_crop', [
|
||||
'width' => 5,
|
||||
'height' => 10,
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['scale_and_crop']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_desaturate_effect() function.
|
||||
*/
|
||||
public function testDesaturateEffect() {
|
||||
$this->assertImageEffect('image_desaturate', []);
|
||||
$this->assertToolkitOperationsCalled(['desaturate']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual(count($calls['desaturate'][0]), 0, 'No parameters were passed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the image_rotate_effect() function.
|
||||
*/
|
||||
public function testRotateEffect() {
|
||||
// @todo: need to test with 'random' => TRUE
|
||||
$this->assertImageEffect('image_rotate', [
|
||||
'degrees' => 90,
|
||||
'bgcolor' => '#fff',
|
||||
]);
|
||||
$this->assertToolkitOperationsCalled(['rotate']);
|
||||
|
||||
// Check the parameters.
|
||||
$calls = $this->imageTestGetAllCalls();
|
||||
$this->assertEqual($calls['rotate'][0][0], 90, 'Degrees were passed correctly');
|
||||
$this->assertEqual($calls['rotate'][0][1], '#fff', 'Background color was passed correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test image effect caching.
|
||||
*/
|
||||
public function testImageEffectsCaching() {
|
||||
$image_effect_definitions_called = &drupal_static('image_module_test_image_effect_info_alter');
|
||||
|
||||
// First call should grab a fresh copy of the data.
|
||||
$manager = $this->container->get('plugin.manager.image.effect');
|
||||
$effects = $manager->getDefinitions();
|
||||
$this->assertTrue($image_effect_definitions_called === 1, 'image_effect_definitions() generated data.');
|
||||
|
||||
// Second call should come from cache.
|
||||
drupal_static_reset('image_module_test_image_effect_info_alter');
|
||||
$cached_effects = $manager->getDefinitions();
|
||||
$this->assertTrue($image_effect_definitions_called === 0, 'image_effect_definitions() returned data from cache.');
|
||||
|
||||
$this->assertTrue($effects == $cached_effects, 'Cached effects are the same as generated effects.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if validation errors are passed plugin form to the parent form.
|
||||
*/
|
||||
public function testEffectFormValidationErrors() {
|
||||
$account = $this->drupalCreateUser(['administer image styles']);
|
||||
$this->drupalLogin($account);
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
$style = ImageStyle::load('thumbnail');
|
||||
// Image Scale is the only effect shipped with 'thumbnail', by default.
|
||||
$uuids = $style->getEffects()->getInstanceIds();
|
||||
$uuid = key($uuids);
|
||||
|
||||
// We are posting the form with both, width and height, empty.
|
||||
$edit = ['data[width]' => '', 'data[height]' => ''];
|
||||
$path = 'admin/config/media/image-styles/manage/thumbnail/effects/' . $uuid;
|
||||
$this->drupalPostForm($path, $edit, t('Update effect'));
|
||||
// Check that the error message has been displayed.
|
||||
$this->assertText(t('Width and height can not both be blank.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the effect processing of an image effect plugin.
|
||||
*
|
||||
* @param string $effect_name
|
||||
* The name of the image effect to test.
|
||||
* @param array $data
|
||||
* The data to pass to the image effect.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertImageEffect($effect_name, array $data) {
|
||||
$effect = $this->manager->createInstance($effect_name, ['data' => $data]);
|
||||
return $this->assertTrue($effect->applyEffect($this->image), 'Function returned the expected value.');
|
||||
}
|
||||
|
||||
}
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* TODO: Test the following functions.
|
||||
*
|
||||
* In file:
|
||||
* - image.effects.inc:
|
||||
* image_style_generate()
|
||||
* \Drupal\image\ImageStyleInterface::createDerivative()
|
||||
*
|
||||
* - image.module:
|
||||
* image_style_options()
|
||||
* \Drupal\image\ImageStyleInterface::flush()
|
||||
* image_filter_keyword()
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class provides methods specifically for testing Image's field handling.
|
||||
*/
|
||||
abstract class ImageFieldTestBase extends BrowserTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preview an image in a node.
|
||||
*
|
||||
* @param \Drupal\Core\Image\ImageInterface $image
|
||||
* A file object representing the image to upload.
|
||||
* @param string $field_name
|
||||
* Name of the image field the image should be attached to.
|
||||
* @param string $type
|
||||
* The type of node to create.
|
||||
*/
|
||||
public function previewNodeImage($image, $field_name, $type) {
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$edit['files[' . $field_name . '_0]'] = \Drupal::service('file_system')->realpath($image->uri);
|
||||
$this->drupalPostForm('node/add/' . $type, $edit, t('Preview'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload an image to a node.
|
||||
*
|
||||
* @param $image
|
||||
* A file object representing the image to upload.
|
||||
* @param $field_name
|
||||
* Name of the image field the image should be attached to.
|
||||
* @param $type
|
||||
* The type of node to create.
|
||||
* @param $alt
|
||||
* The alt text for the image. Use if the field settings require alt text.
|
||||
*/
|
||||
public function uploadNodeImage($image, $field_name, $type, $alt = '') {
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
];
|
||||
$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'));
|
||||
}
|
||||
|
||||
// Retrieve ID of the newly created node from the current URL.
|
||||
$matches = [];
|
||||
preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
|
||||
return isset($matches[1]) ? $matches[1] : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the fid of the last inserted file.
|
||||
*/
|
||||
protected function getLastFileId() {
|
||||
return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests the image field widget.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFieldWidgetTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* Tests file widget element.
|
||||
*/
|
||||
public function testWidgetElement() {
|
||||
// Check for image widget in add/node/article page
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$min_resolution = 50;
|
||||
$max_resolution = 100;
|
||||
$field_settings = [
|
||||
'max_resolution' => $max_resolution . 'x' . $max_resolution,
|
||||
'min_resolution' => $min_resolution . 'x' . $min_resolution,
|
||||
'alt_field' => 0,
|
||||
];
|
||||
$this->createImageField($field_name, 'article', [], $field_settings, [], [], 'Image test on [site:name]');
|
||||
$this->drupalGet('node/add/article');
|
||||
$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,81 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Functional;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
|
||||
/**
|
||||
* Tests image style deletion using the UI.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStyleDeleteTest extends ImageFieldTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create an image field 'foo' having the image style 'medium' as widget
|
||||
// preview and as formatter.
|
||||
$this->createImageField('foo', 'page', [], [], ['preview_image_style' => 'medium'], ['image_style' => 'medium']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests image style deletion.
|
||||
*/
|
||||
public function testDelete() {
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/medium/delete');
|
||||
// Checks that the 'replacement' select element is displayed.
|
||||
$this->assertFieldByName('replacement');
|
||||
// Checks that UI messages are correct.
|
||||
$this->assertRaw(t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.'));
|
||||
$this->assertNoRaw(t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.'));
|
||||
|
||||
// Delete 'medium' image style but replace it with 'thumbnail'. This style
|
||||
// is involved in 'node.page.default' display view and form.
|
||||
$this->drupalPostForm(NULL, ['replacement' => 'thumbnail'], t('Delete'));
|
||||
|
||||
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
|
||||
$view_display = EntityViewDisplay::load('node.page.default');
|
||||
// Checks that the formatter setting is replaced.
|
||||
if ($this->assertNotNull($component = $view_display->getComponent('foo'))) {
|
||||
$this->assertIdentical($component['settings']['image_style'], 'thumbnail');
|
||||
}
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = EntityFormDisplay::load('node.page.default');
|
||||
// Check that the widget setting is replaced.
|
||||
if ($this->assertNotNull($component = $form_display->getComponent('foo'))) {
|
||||
$this->assertIdentical($component['settings']['preview_image_style'], 'thumbnail');
|
||||
}
|
||||
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/thumbnail/delete');
|
||||
// Checks that the 'replacement' select element is displayed.
|
||||
$this->assertFieldByName('replacement');
|
||||
// Checks that UI messages are correct.
|
||||
$this->assertRaw(t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.'));
|
||||
$this->assertNoRaw(t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.'));
|
||||
|
||||
// Delete 'thumbnail' image style. Provide no replacement.
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
|
||||
$view_display = EntityViewDisplay::load('node.page.default');
|
||||
// Checks that the formatter setting is disabled.
|
||||
$this->assertNull($view_display->getComponent('foo'));
|
||||
$this->assertNotNull($view_display->get('hidden')['foo']);
|
||||
// Checks that widget setting is preserved with the image preview disabled.
|
||||
$form_display = EntityFormDisplay::load('node.page.default');
|
||||
$this->assertNotNull($widget = $form_display->getComponent('foo'));
|
||||
$this->assertIdentical($widget['settings']['preview_image_style'], '');
|
||||
|
||||
// Now, there's only one image style configured on the system: 'large'.
|
||||
$this->drupalGet('admin/config/media/image-styles/manage/large/delete');
|
||||
// Checks that the 'replacement' select element is not displayed.
|
||||
$this->assertNoFieldByName('replacement');
|
||||
// Checks that UI messages are correct.
|
||||
$this->assertNoRaw(t('If this style is in use on the site, you may select another style to replace it. All images that have been generated for this style will be permanently deleted. If no replacement style is selected, the dependent configurations might need manual reconfiguration.'));
|
||||
$this->assertRaw(t('All images that have been generated for this style will be permanently deleted. The dependent configurations might need manual reconfiguration.'));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\FunctionalJavascript;
|
||||
|
||||
use Drupal\file\Entity\File;
|
||||
use Drupal\Tests\image\Kernel\ImageFieldCreationTrait;
|
||||
use Drupal\Tests\quickedit\FunctionalJavascript\QuickEditJavascriptTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\image\Plugin\InPlaceEditor\Image
|
||||
* @group image
|
||||
*/
|
||||
class QuickEditImageTest extends QuickEditJavascriptTestBase {
|
||||
|
||||
use ImageFieldCreationTrait;
|
||||
use TestFileCreationTrait;
|
||||
use QuickEditImageEditorTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'image', 'field_ui'];
|
||||
|
||||
/**
|
||||
* 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 toolbar',
|
||||
'access in-place editing',
|
||||
'access content',
|
||||
'create article content',
|
||||
'edit any article content',
|
||||
'delete any article content',
|
||||
]);
|
||||
$this->drupalLogin($this->contentAuthorUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that quick editor works correctly with images.
|
||||
*
|
||||
* @covers ::isCompatible
|
||||
* @covers ::getAttachments
|
||||
*/
|
||||
public function testImageInPlaceEditor() {
|
||||
// Create a field with a basic filetype restriction.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
$field_settings = [
|
||||
'file_extensions' => 'png',
|
||||
];
|
||||
$formatter_settings = [
|
||||
'image_style' => 'large',
|
||||
'image_link' => '',
|
||||
];
|
||||
$this->createImageField($field_name, 'article', [], $field_settings, [], $formatter_settings);
|
||||
|
||||
// Find images that match our field settings.
|
||||
$valid_images = [];
|
||||
foreach ($this->getTestFiles('image') as $image) {
|
||||
// This regex is taken from file_validate_extensions().
|
||||
$regex = '/\.(' . preg_replace('/ +/', '|', preg_quote($field_settings['file_extensions'])) . ')$/i';
|
||||
if (preg_match($regex, $image->filename)) {
|
||||
$valid_images[] = $image;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we have at least two valid images.
|
||||
$this->assertGreaterThanOrEqual(2, count($valid_images));
|
||||
|
||||
// Create a File entity for the initial image.
|
||||
$file = File::create([
|
||||
'uri' => $valid_images[0]->uri,
|
||||
'uid' => $this->contentAuthorUser->id(),
|
||||
'status' => FILE_STATUS_PERMANENT,
|
||||
]);
|
||||
$file->save();
|
||||
|
||||
// Use the first valid image to create a new Node.
|
||||
$image_factory = $this->container->get('image.factory');
|
||||
$image = $image_factory->get($valid_images[0]->uri);
|
||||
$node = $this->drupalCreateNode([
|
||||
'type' => 'article',
|
||||
'title' => t('Test Node'),
|
||||
$field_name => [
|
||||
'target_id' => $file->id(),
|
||||
'alt' => 'Hello world',
|
||||
'title' => '',
|
||||
'width' => $image->getWidth(),
|
||||
'height' => $image->getHeight(),
|
||||
],
|
||||
]);
|
||||
|
||||
// Visit the new Node.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
// Assemble common CSS selectors.
|
||||
$entity_selector = '[data-quickedit-entity-id="node/' . $node->id() . '"]';
|
||||
$field_selector = '[data-quickedit-field-id="node/' . $node->id() . '/' . $field_name . '/' . $node->language()->getId() . '/full"]';
|
||||
$original_image_selector = 'img[src*="' . $valid_images[0]->filename . '"][alt="Hello world"]';
|
||||
$new_image_selector = 'img[src*="' . $valid_images[1]->filename . '"][alt="New text"]';
|
||||
|
||||
// Assert that the initial image is present.
|
||||
$this->assertSession()->elementExists('css', $entity_selector . ' ' . $field_selector . ' ' . $original_image_selector);
|
||||
|
||||
// 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',
|
||||
]);
|
||||
|
||||
// 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);
|
||||
$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',
|
||||
]);
|
||||
|
||||
// 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',
|
||||
]);
|
||||
|
||||
// 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();
|
||||
|
||||
// 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());
|
||||
|
||||
// Check that the new image appears as expected.
|
||||
$this->assertSession()->elementNotExists('css', $entity_selector . ' ' . $field_selector . ' ' . $original_image_selector);
|
||||
$this->assertSession()->elementExists('css', $entity_selector . ' ' . $field_selector . ' ' . $new_image_selector);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Provides a helper method for creating Image fields.
|
||||
*/
|
||||
trait ImageFieldCreationTrait {
|
||||
|
||||
/**
|
||||
* Create a new image field.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the new field (all lowercase), exclude the "field_" prefix.
|
||||
* @param string $type_name
|
||||
* The node type that this field will be added to.
|
||||
* @param array $storage_settings
|
||||
* (optional) A list of field storage settings that will be added to the
|
||||
* defaults.
|
||||
* @param array $field_settings
|
||||
* (optional) A list of instance settings that will be added to the instance
|
||||
* defaults.
|
||||
* @param array $widget_settings
|
||||
* (optional) Widget settings to be added to the widget defaults.
|
||||
* @param array $formatter_settings
|
||||
* (optional) Formatter settings to be added to the formatter defaults.
|
||||
* @param string $description
|
||||
* (optional) A description for the field. Defaults to ''.
|
||||
*/
|
||||
protected function createImageField($name, $type_name, $storage_settings = [], $field_settings = [], $widget_settings = [], $formatter_settings = [], $description = '') {
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'image',
|
||||
'settings' => $storage_settings,
|
||||
'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
|
||||
])->save();
|
||||
|
||||
$field_config = FieldConfig::create([
|
||||
'field_name' => $name,
|
||||
'label' => $name,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type_name,
|
||||
'required' => !empty($field_settings['required']),
|
||||
'settings' => $field_settings,
|
||||
'description' => $description,
|
||||
]);
|
||||
$field_config->save();
|
||||
|
||||
entity_get_form_display('node', $type_name, 'default')
|
||||
->setComponent($name, [
|
||||
'type' => 'image_image',
|
||||
'settings' => $widget_settings,
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_display('node', $type_name, 'default')
|
||||
->setComponent($name, [
|
||||
'type' => 'image',
|
||||
'settings' => $formatter_settings,
|
||||
])
|
||||
->save();
|
||||
|
||||
return $field_config;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests the image field rendering using entity fields of the image field type.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageFormatterTest extends FieldKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['file', 'image'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
|
||||
*/
|
||||
protected $display;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['field']);
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('file');
|
||||
$this->installSchema('file', ['file_usage']);
|
||||
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = mb_strtolower($this->randomMachineName());
|
||||
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => $this->entityType,
|
||||
'field_name' => $this->fieldName,
|
||||
'type' => 'image',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => $this->entityType,
|
||||
'field_name' => $this->fieldName,
|
||||
'bundle' => $this->bundle,
|
||||
'settings' => [
|
||||
'file_extensions' => 'jpg',
|
||||
],
|
||||
])->save();
|
||||
|
||||
$this->display = entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'image',
|
||||
'label' => 'hidden',
|
||||
]);
|
||||
$this->display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags from image formatters.
|
||||
*/
|
||||
public function testImageFormatterCacheTags() {
|
||||
// Create a test entity with the image field set.
|
||||
$entity = EntityTest::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
]);
|
||||
$entity->{$this->fieldName}->generateSampleItems(2);
|
||||
$entity->save();
|
||||
|
||||
// Generate the render array to verify if the cache tags are as expected.
|
||||
$build = $this->display->build($entity);
|
||||
|
||||
$this->assertEquals($entity->{$this->fieldName}[0]->entity->getCacheTags(), $build[$this->fieldName][0]['#cache']['tags'], 'First image cache tags is as expected');
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests config import for Image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageImportTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'image', 'image_module_test'];
|
||||
|
||||
/**
|
||||
* Tests importing image styles.
|
||||
*/
|
||||
public function testImport() {
|
||||
$style = ImageStyle::create([
|
||||
'name' => 'test',
|
||||
]);
|
||||
|
||||
$style->addImageEffect(['id' => 'image_module_test_null']);
|
||||
$style->addImageEffect(['id' => 'image_module_test_null']);
|
||||
$style->save();
|
||||
|
||||
$this->assertEqual(count($style->getEffects()), 2);
|
||||
|
||||
$uuid = \Drupal::service('uuid')->generate();
|
||||
$style->set('effects', [
|
||||
$uuid => [
|
||||
'id' => 'image_module_test_null',
|
||||
],
|
||||
]);
|
||||
$style->save();
|
||||
|
||||
$style = ImageStyle::load('test');
|
||||
$this->assertEqual(count($style->getEffects()), 1);
|
||||
}
|
||||
|
||||
}
|
164
2017/web/core/modules/image/tests/src/Kernel/ImageItemTest.php
Normal file
164
2017/web/core/modules/image/tests/src/Kernel/ImageItemTest.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
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.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageItemTest extends FieldKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['file', 'image'];
|
||||
|
||||
/**
|
||||
* Created file entity.
|
||||
*
|
||||
* @var \Drupal\file\Entity\File
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Image\ImageFactory
|
||||
*/
|
||||
protected $imageFactory;
|
||||
|
||||
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']);
|
||||
|
||||
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',
|
||||
'settings' => [
|
||||
'file_extensions' => 'jpg',
|
||||
],
|
||||
])->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 using entity fields of the image field type.
|
||||
*/
|
||||
public function testImageItem() {
|
||||
// Create a test entity with the image field set.
|
||||
$entity = EntityTest::create();
|
||||
$entity->image_test->target_id = $this->image->id();
|
||||
$entity->image_test->alt = $alt = $this->randomMachineName();
|
||||
$entity->image_test->title = $title = $this->randomMachineName();
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
$entity = EntityTest::load($entity->id());
|
||||
$this->assertTrue($entity->image_test instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->image_test[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->image_test->target_id, $this->image->id());
|
||||
$this->assertEqual($entity->image_test->alt, $alt);
|
||||
$this->assertEqual($entity->image_test->title, $title);
|
||||
$image = $this->imageFactory->get('public://example.jpg');
|
||||
$this->assertEqual($entity->image_test->width, $image->getWidth());
|
||||
$this->assertEqual($entity->image_test->height, $image->getHeight());
|
||||
$this->assertEqual($entity->image_test->entity->id(), $this->image->id());
|
||||
$this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid());
|
||||
|
||||
// Make sure the computed entity reflects updates to the referenced file.
|
||||
file_unmanaged_copy($this->root . '/core/misc/druplicon.png', 'public://example-2.jpg');
|
||||
$image2 = File::create([
|
||||
'uri' => 'public://example-2.jpg',
|
||||
]);
|
||||
$image2->save();
|
||||
|
||||
$entity->image_test->target_id = $image2->id();
|
||||
$entity->image_test->alt = $new_alt = $this->randomMachineName();
|
||||
// The width and height is only updated when width is not set.
|
||||
$entity->image_test->width = NULL;
|
||||
$entity->save();
|
||||
$this->assertEqual($entity->image_test->entity->id(), $image2->id());
|
||||
$this->assertEqual($entity->image_test->entity->getFileUri(), $image2->getFileUri());
|
||||
$image = $this->imageFactory->get('public://example-2.jpg');
|
||||
$this->assertEqual($entity->image_test->width, $image->getWidth());
|
||||
$this->assertEqual($entity->image_test->height, $image->getHeight());
|
||||
$this->assertEqual($entity->image_test->alt, $new_alt);
|
||||
|
||||
// Check that the image item can be set to the referenced file directly.
|
||||
$entity->image_test = $this->image;
|
||||
$this->assertEqual($entity->image_test->target_id, $this->image->id());
|
||||
|
||||
// Delete the image and try to save the entity again.
|
||||
$this->image->delete();
|
||||
$entity = EntityTest::create(['mame' => $this->randomMachineName()]);
|
||||
$entity->save();
|
||||
|
||||
// Test image item properties.
|
||||
$expected = ['target_id', 'entity', 'alt', 'title', 'width', 'height'];
|
||||
$properties = $entity->getFieldDefinition('image_test')->getFieldStorageDefinition()->getPropertyDefinitions();
|
||||
$this->assertEqual(array_keys($properties), $expected);
|
||||
|
||||
// Test the generateSampleValue() method.
|
||||
$entity = EntityTest::create();
|
||||
$entity->image_test->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
$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,111 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\StreamWrapper\PrivateStream;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Drupal\file_test\StreamWrapper\DummyReadOnlyStreamWrapper;
|
||||
use Drupal\file_test\StreamWrapper\DummyRemoteReadOnlyStreamWrapper;
|
||||
use Drupal\file_test\StreamWrapper\DummyStreamWrapper;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests derivative generation with source images using stream wrappers.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStyleCustomStreamWrappersTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['system', 'image'];
|
||||
|
||||
/**
|
||||
* A testing image style entity.
|
||||
*
|
||||
* @var \Drupal\image\ImageStyleInterface
|
||||
*/
|
||||
protected $imageStyle;
|
||||
|
||||
/**
|
||||
* The file system service.
|
||||
*
|
||||
* @var \Drupal\Core\File\FileSystemInterface
|
||||
*/
|
||||
protected $fileSystem;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->fileSystem = $this->container->get('file_system');
|
||||
$this->config('system.file')->set('default_scheme', 'public')->save();
|
||||
$this->imageStyle = ImageStyle::create(['name' => 'test']);
|
||||
$this->imageStyle->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
foreach ($this->providerTestCustomStreamWrappers() as $stream_wrapper) {
|
||||
$scheme = $stream_wrapper[0];
|
||||
$class = $stream_wrapper[2];
|
||||
$container->register("stream_wrapper.$scheme", $class)
|
||||
->addTag('stream_wrapper', ['scheme' => $scheme]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests derivative creation with several source on a local writable stream.
|
||||
*
|
||||
* @dataProvider providerTestCustomStreamWrappers
|
||||
*
|
||||
* @param string $source_scheme
|
||||
* The source stream wrapper scheme.
|
||||
* @param string $expected_scheme
|
||||
* The derivative expected stream wrapper scheme.
|
||||
*/
|
||||
public function testCustomStreamWrappers($source_scheme, $expected_scheme) {
|
||||
$derivative_uri = $this->imageStyle->buildUri("$source_scheme://some/path/image.png");
|
||||
$derivative_scheme = $this->fileSystem->uriScheme($derivative_uri);
|
||||
|
||||
// Check that the derivative scheme is the expected scheme.
|
||||
$this->assertSame($expected_scheme, $derivative_scheme);
|
||||
|
||||
// Check that the derivative URI is the expected one.
|
||||
$expected_uri = "$expected_scheme://styles/{$this->imageStyle->id()}/$source_scheme/some/path/image.png";
|
||||
$this->assertSame($expected_uri, $derivative_uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide test cases for testCustomStreamWrappers().
|
||||
*
|
||||
* Derivatives created from writable source stream wrappers will inherit the
|
||||
* scheme from source. Derivatives created from read-only stream wrappers will
|
||||
* fall-back to the default scheme.
|
||||
*
|
||||
* @return array[]
|
||||
* An array having each element an array with three items:
|
||||
* - The source stream wrapper scheme.
|
||||
* - The derivative expected stream wrapper scheme.
|
||||
* - The stream wrapper service class.
|
||||
*/
|
||||
public function providerTestCustomStreamWrappers() {
|
||||
return [
|
||||
['public', 'public', PublicStream::class],
|
||||
['private', 'private', PrivateStream::class],
|
||||
['dummy', 'dummy', DummyStreamWrapper::class],
|
||||
['dummy-readonly', 'public', DummyReadOnlyStreamWrapper::class],
|
||||
['dummy-remote-readonly', 'public', DummyRemoteReadOnlyStreamWrapper::class],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests the integration of ImageStyle with the core.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStyleIntegrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image', 'file', 'field', 'system', 'user', 'node'];
|
||||
|
||||
/**
|
||||
* Tests the dependency between ImageStyle and entity display components.
|
||||
*/
|
||||
public function testEntityDisplayDependency() {
|
||||
// Create two image styles.
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
$style = ImageStyle::create(['name' => 'main_style']);
|
||||
$style->save();
|
||||
/** @var \Drupal\image\ImageStyleInterface $replacement */
|
||||
$replacement = ImageStyle::create(['name' => 'replacement_style']);
|
||||
$replacement->save();
|
||||
|
||||
// Create a node-type, named 'note'.
|
||||
$node_type = NodeType::create(['type' => 'note']);
|
||||
$node_type->save();
|
||||
|
||||
// Create an image field and attach it to the 'note' node-type.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'sticker',
|
||||
'type' => 'image',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'sticker',
|
||||
'bundle' => 'note',
|
||||
])->save();
|
||||
|
||||
// Create the default entity view display and set the 'sticker' field to use
|
||||
// the 'main_style' images style in formatter.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
|
||||
$view_display = EntityViewDisplay::create([
|
||||
'targetEntityType' => 'node',
|
||||
'bundle' => 'note',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
])->setComponent('sticker', ['settings' => ['image_style' => 'main_style']]);
|
||||
$view_display->save();
|
||||
|
||||
// Create the default entity form display and set the 'sticker' field to use
|
||||
// the 'main_style' images style in the widget.
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
|
||||
$form_display = EntityFormDisplay::create([
|
||||
'targetEntityType' => 'node',
|
||||
'bundle' => 'note',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
])->setComponent('sticker', ['settings' => ['preview_image_style' => 'main_style']]);
|
||||
$form_display->save();
|
||||
|
||||
// Check that the entity displays exists before dependency removal.
|
||||
$this->assertNotNull(EntityViewDisplay::load($view_display->id()));
|
||||
$this->assertNotNull(EntityFormDisplay::load($form_display->id()));
|
||||
|
||||
// Delete the 'main_style' image style. Before that, emulate the UI process
|
||||
// of selecting a replacement style by setting the replacement image style
|
||||
// ID in the image style storage.
|
||||
/** @var \Drupal\image\ImageStyleStorageInterface $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage($style->getEntityTypeId());
|
||||
$storage->setReplacementId('main_style', 'replacement_style');
|
||||
$style->delete();
|
||||
|
||||
// Check that the entity displays exists after dependency removal.
|
||||
$this->assertNotNull($view_display = EntityViewDisplay::load($view_display->id()));
|
||||
$this->assertNotNull($form_display = EntityFormDisplay::load($form_display->id()));
|
||||
// Check that the 'sticker' formatter component exists in both displays.
|
||||
$this->assertNotNull($formatter = $view_display->getComponent('sticker'));
|
||||
$this->assertNotNull($widget = $form_display->getComponent('sticker'));
|
||||
// Check that both displays are using now 'replacement_style' for images.
|
||||
$this->assertSame('replacement_style', $formatter['settings']['image_style']);
|
||||
$this->assertSame('replacement_style', $widget['settings']['preview_image_style']);
|
||||
|
||||
// Delete the 'replacement_style' without setting a replacement image style.
|
||||
$replacement->delete();
|
||||
|
||||
// The entity view and form displays exists after dependency removal.
|
||||
$this->assertNotNull($view_display = EntityViewDisplay::load($view_display->id()));
|
||||
$this->assertNotNull($form_display = EntityFormDisplay::load($form_display->id()));
|
||||
// The 'sticker' formatter component should be hidden in view display.
|
||||
$this->assertNull($view_display->getComponent('sticker'));
|
||||
$this->assertTrue($view_display->get('hidden')['sticker']);
|
||||
// The 'sticker' widget component should be active in form displays, but the
|
||||
// image preview should be disabled.
|
||||
$this->assertNotNull($widget = $form_display->getComponent('sticker'));
|
||||
$this->assertSame('', $widget['settings']['preview_image_style']);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Migrate\d6;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
|
||||
|
||||
/**
|
||||
* Tests migration of ImageCache presets to image styles.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class MigrateImageCacheTest extends MigrateDrupal6TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(['image']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown when ImageCache is not installed.
|
||||
*/
|
||||
public function testMissingTable() {
|
||||
$this->sourceDatabase->update('system')
|
||||
->fields([
|
||||
'status' => 0,
|
||||
])
|
||||
->condition('name', 'imagecache')
|
||||
->condition('type', 'module')
|
||||
->execute();
|
||||
|
||||
try {
|
||||
$this->getMigration('d6_imagecache_presets')
|
||||
->getSourcePlugin()
|
||||
->checkRequirements();
|
||||
$this->fail('Did not catch expected RequirementsException.');
|
||||
}
|
||||
catch (RequirementsException $e) {
|
||||
$this->pass('Caught expected RequirementsException: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic passing migrations.
|
||||
*/
|
||||
public function testPassingMigration() {
|
||||
$this->executeMigration('d6_imagecache_presets');
|
||||
|
||||
/** @var \Drupal\image\Entity\ImageStyle $style */
|
||||
$style = ImageStyle::load('big_blue_cheese');
|
||||
|
||||
// Check basic Style info.
|
||||
$this->assertIdentical('big_blue_cheese', $style->get('name'), 'ImageStyle name set correctly');
|
||||
$this->assertIdentical('big_blue_cheese', $style->get('label'), 'ImageStyle label set correctly');
|
||||
|
||||
// Test effects.
|
||||
$effects = $style->getEffects();
|
||||
|
||||
// Check crop effect.
|
||||
$this->assertImageEffect($effects, 'image_crop', [
|
||||
'width' => 555,
|
||||
'height' => 5555,
|
||||
'anchor' => 'center-center',
|
||||
]);
|
||||
|
||||
// Check resize effect.
|
||||
$this->assertImageEffect($effects, 'image_resize', [
|
||||
'width' => 55,
|
||||
'height' => 55,
|
||||
]);
|
||||
|
||||
// Check rotate effect.
|
||||
$this->assertImageEffect($effects, 'image_rotate', [
|
||||
'degrees' => 55,
|
||||
'random' => FALSE,
|
||||
'bgcolor' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that missing actions causes failures.
|
||||
*/
|
||||
public function testMissingEffectPlugin() {
|
||||
Database::getConnection('default', 'migrate')->insert("imagecache_action")
|
||||
->fields([
|
||||
'presetid',
|
||||
'weight',
|
||||
'module',
|
||||
'action',
|
||||
'data',
|
||||
])
|
||||
->values([
|
||||
'presetid' => '1',
|
||||
'weight' => '0',
|
||||
'module' => 'imagecache',
|
||||
'action' => 'imagecache_deprecated_scale',
|
||||
'data' => 'a:3:{s:3:"fit";s:7:"outside";s:5:"width";s:3:"200";s:6:"height";s:3:"200";}',
|
||||
])->execute();
|
||||
|
||||
$this->startCollectingMessages();
|
||||
$this->executeMigration('d6_imagecache_presets');
|
||||
$messages = $this->migration->getIdMap()->getMessageIterator();
|
||||
$count = 0;
|
||||
foreach ($messages as $message) {
|
||||
$count++;
|
||||
$this->assertEqual($message->message, 'The "image_deprecated_scale" plugin does not exist.');
|
||||
$this->assertEqual($message->level, MigrationInterface::MESSAGE_ERROR);
|
||||
}
|
||||
// There should be only the one message.
|
||||
$this->assertEqual($count, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that missing action's causes failures.
|
||||
*/
|
||||
public function testInvalidCropValues() {
|
||||
Database::getConnection('default', 'migrate')->insert("imagecache_action")
|
||||
->fields([
|
||||
'presetid',
|
||||
'weight',
|
||||
'module',
|
||||
'action',
|
||||
'data',
|
||||
])
|
||||
->values([
|
||||
'presetid' => '1',
|
||||
'weight' => '0',
|
||||
'module' => 'imagecache',
|
||||
'action' => 'imagecache_crop',
|
||||
'data' => serialize([
|
||||
'xoffset' => '10',
|
||||
'yoffset' => '10',
|
||||
]),
|
||||
])->execute();
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a given image effect is migrated.
|
||||
*
|
||||
* @param array $collection
|
||||
* Collection of effects
|
||||
* @param $id
|
||||
* Id that should exist in the collection.
|
||||
* @param $config
|
||||
* Expected configuration for the collection.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function assertImageEffect($collection, $id, $config) {
|
||||
/** @var \Drupal\image\ConfigurableImageEffectBase $effect */
|
||||
foreach ($collection as $key => $effect) {
|
||||
$effect_config = $effect->getConfiguration();
|
||||
|
||||
if ($effect_config['id'] == $id && $effect_config['data'] == $config) {
|
||||
// We found this effect so succeed and return.
|
||||
return $this->pass('Effect ' . $id . ' imported correctly');
|
||||
}
|
||||
}
|
||||
// The loop did not find the effect so we it was not imported correctly.
|
||||
return $this->fail('Effect ' . $id . ' did not import correctly');
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Tests migration of Image variables to configuration.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class MigrateImageSettingsTest extends MigrateDrupal7TestBase {
|
||||
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('d7_image_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the migration.
|
||||
*/
|
||||
public function testMigration() {
|
||||
$config = $this->config('image.settings');
|
||||
// These settings are not recommended...
|
||||
$this->assertTrue($config->get('allow_insecure_derivatives'));
|
||||
$this->assertTrue($config->get('suppress_itok_output'));
|
||||
$this->assertIdentical("core/modules/image/testsample.png", $config->get('preview_image'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
use Drupal\image\ImageEffectBase;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Test image styles migration to config entities.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class MigrateImageStylesTest extends MigrateDrupal7TestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(static::$modules);
|
||||
$this->executeMigration('d7_image_styles');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, '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']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts various aspects of an ImageStyle entity.
|
||||
*
|
||||
* @param string $id
|
||||
* The expected image style ID.
|
||||
* @param string $label
|
||||
* The expected image style label.
|
||||
* @param array $expected_effect_plugins
|
||||
* An array of expected plugins attached to the image style entity
|
||||
* @param array $expected_effect_config
|
||||
* An array of expected configuration for each effect in the image style
|
||||
*/
|
||||
protected function assertEntity($id, $label, array $expected_effect_plugins, array $expected_effect_config) {
|
||||
$style = ImageStyle::load($id);
|
||||
$this->assertTrue($style instanceof ImageStyleInterface);
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
$this->assertIdentical($id, $style->id());
|
||||
$this->assertIdentical($label, $style->label());
|
||||
|
||||
// Check the number of effects associated with the style.
|
||||
$effects = $style->getEffects();
|
||||
$this->assertIdentical(count($effects), count($expected_effect_plugins));
|
||||
|
||||
$index = 0;
|
||||
foreach ($effects as $effect) {
|
||||
$this->assertTrue($effect instanceof ImageEffectBase);
|
||||
$this->assertIdentical($expected_effect_plugins[$index], $effect->getPluginId());
|
||||
$config = $effect->getConfiguration();
|
||||
$this->assertIdentical($expected_effect_config[$index], $config['data']);
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests the d6_imagecache_presets source plugin.
|
||||
*
|
||||
* @covers \Drupal\image\Plugin\migrate\source\d6\ImageCachePreset
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageCachePresetTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image', 'migrate_drupal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// The source data.
|
||||
$tests[0]['source_data']['imagecache_preset'] = [
|
||||
[
|
||||
'presetid' => '1',
|
||||
'presetname' => 'slackjaw_boys',
|
||||
],
|
||||
];
|
||||
$tests[0]['source_data']['imagecache_action'] = [
|
||||
[
|
||||
'actionid' => '3',
|
||||
'presetid' => '1',
|
||||
'weight' => '0',
|
||||
'module' => 'imagecache',
|
||||
'action' => 'imagecache_scale_and_crop',
|
||||
'data' => 'a:2:{s:5:"width";s:4:"100%";s:6:"height";s:4:"100%";}',
|
||||
],
|
||||
];
|
||||
|
||||
// The expected results.
|
||||
$tests[0]['expected_data'] = [
|
||||
[
|
||||
'presetid' => '1',
|
||||
'presetname' => 'slackjaw_boys',
|
||||
'actions' => [
|
||||
[
|
||||
'actionid' => '3',
|
||||
'presetid' => '1',
|
||||
'weight' => '0',
|
||||
'module' => 'imagecache',
|
||||
'action' => 'imagecache_scale_and_crop',
|
||||
'data' => [
|
||||
'width' => '100%',
|
||||
'height' => '100%',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
|
||||
|
||||
/**
|
||||
* Tests the D7 ImageStyles source plugin.
|
||||
*
|
||||
* @covers \Drupal\image\Plugin\migrate\source\d7\ImageStyles
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageStylesTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['image', 'migrate_drupal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// The source data.
|
||||
$tests[0]['source_data']['image_styles'] = [
|
||||
[
|
||||
'isid' => 1,
|
||||
'name' => 'custom_image_style_1',
|
||||
'label' => 'Custom image style 1',
|
||||
],
|
||||
];
|
||||
$tests[0]['source_data']['image_effects'] = [
|
||||
[
|
||||
'ieid' => 1,
|
||||
'isid' => 1,
|
||||
'weight' => 1,
|
||||
'name' => 'image_desaturate',
|
||||
'data' => serialize([]),
|
||||
],
|
||||
];
|
||||
|
||||
// The expected results.
|
||||
$tests[0]['expected_data'] = [
|
||||
[
|
||||
'isid' => 1,
|
||||
'name' => 'custom_image_style_1',
|
||||
'label' => 'Custom image style 1',
|
||||
'effects' => [
|
||||
[
|
||||
'ieid' => 1,
|
||||
'isid' => 1,
|
||||
'weight' => 1,
|
||||
'name' => 'image_desaturate',
|
||||
'data' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Kernel\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests image views data.
|
||||
*
|
||||
* @group image
|
||||
*/
|
||||
class ImageViewsDataTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image', 'file', 'views', 'entity_test', 'user', 'field'];
|
||||
|
||||
/**
|
||||
* Tests views data generated for image field relationship.
|
||||
*
|
||||
* @see image_field_views_data()
|
||||
* @see image_field_views_data_views_data_alter()
|
||||
*/
|
||||
public function testRelationshipViewsData() {
|
||||
// Create image field to entity_test.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_base_image',
|
||||
'type' => 'image',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_base_image',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
// Check the generated views data.
|
||||
$views_data = Views::viewsData()->get('entity_test__field_base_image');
|
||||
$relationship = $views_data['field_base_image_target_id']['relationship'];
|
||||
$this->assertEqual($relationship['id'], 'standard');
|
||||
$this->assertEqual($relationship['base'], 'file_managed');
|
||||
$this->assertEqual($relationship['base field'], 'fid');
|
||||
$this->assertEqual($relationship['entity type'], 'file');
|
||||
// Check the backwards reference.
|
||||
$views_data = Views::viewsData()->get('file_managed');
|
||||
$relationship = $views_data['reverse_field_base_image_entity_test']['relationship'];
|
||||
$this->assertEqual($relationship['id'], 'entity_reverse');
|
||||
$this->assertEqual($relationship['base'], 'entity_test');
|
||||
$this->assertEqual($relationship['base field'], 'id');
|
||||
$this->assertEqual($relationship['field table'], 'entity_test__field_base_image');
|
||||
$this->assertEqual($relationship['field field'], 'field_base_image_target_id');
|
||||
$this->assertEqual($relationship['field_name'], 'field_base_image');
|
||||
$this->assertEqual($relationship['entity_type'], 'entity_test');
|
||||
$this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]);
|
||||
|
||||
// Create image field to entity_test_mul.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_mul',
|
||||
'field_name' => 'field_data_image',
|
||||
'type' => 'image',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_mul',
|
||||
'field_name' => 'field_data_image',
|
||||
'bundle' => 'entity_test_mul',
|
||||
])->save();
|
||||
// Check the generated views data.
|
||||
$views_data = Views::viewsData()->get('entity_test_mul__field_data_image');
|
||||
$relationship = $views_data['field_data_image_target_id']['relationship'];
|
||||
$this->assertEqual($relationship['id'], 'standard');
|
||||
$this->assertEqual($relationship['base'], 'file_managed');
|
||||
$this->assertEqual($relationship['base field'], 'fid');
|
||||
$this->assertEqual($relationship['entity type'], 'file');
|
||||
// Check the backwards reference.
|
||||
$views_data = Views::viewsData()->get('file_managed');
|
||||
$relationship = $views_data['reverse_field_data_image_entity_test_mul']['relationship'];
|
||||
$this->assertEqual($relationship['id'], 'entity_reverse');
|
||||
$this->assertEqual($relationship['base'], 'entity_test_mul_property_data');
|
||||
$this->assertEqual($relationship['base field'], 'id');
|
||||
$this->assertEqual($relationship['field table'], 'entity_test_mul__field_data_image');
|
||||
$this->assertEqual($relationship['field field'], 'field_data_image_target_id');
|
||||
$this->assertEqual($relationship['field_name'], 'field_data_image');
|
||||
$this->assertEqual($relationship['entity_type'], 'entity_test_mul');
|
||||
$this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
237
2017/web/core/modules/image/tests/src/Unit/ImageStyleTest.php
Normal file
237
2017/web/core/modules/image/tests/src/Unit/ImageStyleTest.php
Normal file
|
@ -0,0 +1,237 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Unit;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\image\Entity\ImageStyle
|
||||
*
|
||||
* @group Image
|
||||
*/
|
||||
class ImageStyleTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The entity type used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The entity manager used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The ID of the type of the entity under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId;
|
||||
|
||||
/**
|
||||
* Gets a mocked image style for testing.
|
||||
*
|
||||
* @param string $image_effect_id
|
||||
* The image effect ID.
|
||||
* @param \Drupal\image\ImageEffectInterface|\PHPUnit_Framework_MockObject_MockObject $image_effect
|
||||
* The image effect used for testing.
|
||||
*
|
||||
* @return \Drupal\image\ImageStyleInterface
|
||||
* The mocked image style.
|
||||
*/
|
||||
protected function getImageStyleMock($image_effect_id, $image_effect, $stubs = []) {
|
||||
$effectManager = $this->getMockBuilder('\Drupal\image\ImageEffectManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$effectManager->expects($this->any())
|
||||
->method('createInstance')
|
||||
->with($image_effect_id)
|
||||
->will($this->returnValue($image_effect));
|
||||
$default_stubs = [
|
||||
'getImageEffectPluginManager',
|
||||
'fileUriScheme',
|
||||
'fileUriTarget',
|
||||
'fileDefaultScheme',
|
||||
];
|
||||
$image_style = $this->getMockBuilder('\Drupal\image\Entity\ImageStyle')
|
||||
->setConstructorArgs([
|
||||
['effects' => [$image_effect_id => ['id' => $image_effect_id]]],
|
||||
$this->entityTypeId,
|
||||
])
|
||||
->setMethods(array_merge($default_stubs, $stubs))
|
||||
->getMock();
|
||||
|
||||
$image_style->expects($this->any())
|
||||
->method('getImageEffectPluginManager')
|
||||
->will($this->returnValue($effectManager));
|
||||
$image_style->expects($this->any())
|
||||
->method('fileUriScheme')
|
||||
->will($this->returnCallback([$this, 'fileUriScheme']));
|
||||
$image_style->expects($this->any())
|
||||
->method('fileUriTarget')
|
||||
->will($this->returnCallback([$this, 'fileUriTarget']));
|
||||
$image_style->expects($this->any())
|
||||
->method('fileDefaultScheme')
|
||||
->will($this->returnCallback([$this, 'fileDefaultScheme']));
|
||||
|
||||
return $image_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = $this->randomMachineName();
|
||||
$this->provider = $this->randomMachineName();
|
||||
$this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
|
||||
$this->entityType->expects($this->any())
|
||||
->method('getProvider')
|
||||
->will($this->returnValue($this->provider));
|
||||
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
|
||||
$this->entityManager->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->with($this->entityTypeId)
|
||||
->will($this->returnValue($this->entityType));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDerivativeExtension
|
||||
*/
|
||||
public function testGetDerivativeExtension() {
|
||||
$image_effect_id = $this->randomMachineName();
|
||||
$logger = $this->getMockBuilder('\Psr\Log\LoggerInterface')->getMock();
|
||||
$image_effect = $this->getMockBuilder('\Drupal\image\ImageEffectBase')
|
||||
->setConstructorArgs([[], $image_effect_id, [], $logger])
|
||||
->getMock();
|
||||
$image_effect->expects($this->any())
|
||||
->method('getDerivativeExtension')
|
||||
->will($this->returnValue('png'));
|
||||
|
||||
$image_style = $this->getImageStyleMock($image_effect_id, $image_effect);
|
||||
|
||||
$extensions = ['jpeg', 'gif', 'png'];
|
||||
foreach ($extensions as $extension) {
|
||||
$extensionReturned = $image_style->getDerivativeExtension($extension);
|
||||
$this->assertEquals($extensionReturned, 'png');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildUri
|
||||
*/
|
||||
public function testBuildUri() {
|
||||
// Image style that changes the extension.
|
||||
$image_effect_id = $this->randomMachineName();
|
||||
$logger = $this->getMockBuilder('\Psr\Log\LoggerInterface')->getMock();
|
||||
$image_effect = $this->getMockBuilder('\Drupal\image\ImageEffectBase')
|
||||
->setConstructorArgs([[], $image_effect_id, [], $logger])
|
||||
->getMock();
|
||||
$image_effect->expects($this->any())
|
||||
->method('getDerivativeExtension')
|
||||
->will($this->returnValue('png'));
|
||||
|
||||
$image_style = $this->getImageStyleMock($image_effect_id, $image_effect);
|
||||
$this->assertEquals($image_style->buildUri('public://test.jpeg'), 'public://styles/' . $image_style->id() . '/public/test.jpeg.png');
|
||||
|
||||
// Image style that doesn't change the extension.
|
||||
$image_effect_id = $this->randomMachineName();
|
||||
$image_effect = $this->getMockBuilder('\Drupal\image\ImageEffectBase')
|
||||
->setConstructorArgs([[], $image_effect_id, [], $logger])
|
||||
->getMock();
|
||||
$image_effect->expects($this->any())
|
||||
->method('getDerivativeExtension')
|
||||
->will($this->returnArgument(0));
|
||||
|
||||
$image_style = $this->getImageStyleMock($image_effect_id, $image_effect);
|
||||
$this->assertEquals($image_style->buildUri('public://test.jpeg'), 'public://styles/' . $image_style->id() . '/public/test.jpeg');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getPathToken
|
||||
*/
|
||||
public function testGetPathToken() {
|
||||
$logger = $this->getMockBuilder('\Psr\Log\LoggerInterface')->getMock();
|
||||
$private_key = $this->randomMachineName();
|
||||
$hash_salt = $this->randomMachineName();
|
||||
|
||||
// Image style that changes the extension.
|
||||
$image_effect_id = $this->randomMachineName();
|
||||
$image_effect = $this->getMockBuilder('\Drupal\image\ImageEffectBase')
|
||||
->setConstructorArgs([[], $image_effect_id, [], $logger])
|
||||
->getMock();
|
||||
$image_effect->expects($this->any())
|
||||
->method('getDerivativeExtension')
|
||||
->will($this->returnValue('png'));
|
||||
|
||||
$image_style = $this->getImageStyleMock($image_effect_id, $image_effect, ['getPrivateKey', 'getHashSalt']);
|
||||
$image_style->expects($this->any())
|
||||
->method('getPrivateKey')
|
||||
->will($this->returnValue($private_key));
|
||||
$image_style->expects($this->any())
|
||||
->method('getHashSalt')
|
||||
->will($this->returnValue($hash_salt));
|
||||
|
||||
// Assert the extension has been added to the URI before creating the token.
|
||||
$this->assertEquals($image_style->getPathToken('public://test.jpeg.png'), $image_style->getPathToken('public://test.jpeg'));
|
||||
$this->assertEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg.png', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
|
||||
$this->assertNotEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
|
||||
|
||||
// Image style that doesn't change the extension.
|
||||
$image_effect_id = $this->randomMachineName();
|
||||
$image_effect = $this->getMockBuilder('\Drupal\image\ImageEffectBase')
|
||||
->setConstructorArgs([[], $image_effect_id, [], $logger])
|
||||
->getMock();
|
||||
$image_effect->expects($this->any())
|
||||
->method('getDerivativeExtension')
|
||||
->will($this->returnArgument(0));
|
||||
|
||||
$image_style = $this->getImageStyleMock($image_effect_id, $image_effect, ['getPrivateKey', 'getHashSalt']);
|
||||
$image_style->expects($this->any())
|
||||
->method('getPrivateKey')
|
||||
->will($this->returnValue($private_key));
|
||||
$image_style->expects($this->any())
|
||||
->method('getHashSalt')
|
||||
->will($this->returnValue($hash_salt));
|
||||
// Assert no extension has been added to the uri before creating the token.
|
||||
$this->assertNotEquals($image_style->getPathToken('public://test.jpeg.png'), $image_style->getPathToken('public://test.jpeg'));
|
||||
$this->assertNotEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg.png', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
|
||||
$this->assertEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock function for ImageStyle::fileUriScheme().
|
||||
*/
|
||||
public function fileUriScheme($uri) {
|
||||
if (preg_match('/^([\w\-]+):\/\/|^(data):/', $uri, $matches)) {
|
||||
// The scheme will always be the last element in the matches array.
|
||||
return array_pop($matches);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock function for ImageStyle::fileUriTarget().
|
||||
*/
|
||||
public function fileUriTarget($uri) {
|
||||
// Remove the scheme from the URI and remove erroneous leading or trailing,
|
||||
// forward-slashes and backslashes.
|
||||
$target = trim(preg_replace('/^[\w\-]+:\/\/|^data:/', '', $uri), '\/');
|
||||
|
||||
// If nothing was replaced, the URI doesn't have a valid scheme.
|
||||
return $target !== $uri ? $target : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock function for ImageStyle::fileDefaultScheme().
|
||||
*/
|
||||
public function fileDefaultScheme() {
|
||||
return 'public';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\image\Unit\PageCache;
|
||||
|
||||
use Drupal\Core\PageCache\ResponsePolicyInterface;
|
||||
use Drupal\image\PageCache\DenyPrivateImageStyleDownload;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\image\PageCache\DenyPrivateImageStyleDownload
|
||||
* @group image
|
||||
*/
|
||||
class DenyPrivateImageStyleDownloadTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The response policy under test.
|
||||
*
|
||||
* @var \Drupal\image\PageCache\DenyPrivateImageStyleDownload
|
||||
*/
|
||||
protected $policy;
|
||||
|
||||
/**
|
||||
* A request object.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* A response object.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* The current route match.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteMatch|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $routeMatch;
|
||||
|
||||
protected function setUp() {
|
||||
$this->routeMatch = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
|
||||
$this->policy = new DenyPrivateImageStyleDownload($this->routeMatch);
|
||||
$this->response = new Response();
|
||||
$this->request = new Request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that caching is denied on the private image style download route.
|
||||
*
|
||||
* @dataProvider providerPrivateImageStyleDownloadPolicy
|
||||
* @covers ::check
|
||||
*/
|
||||
public function testPrivateImageStyleDownloadPolicy($expected_result, $route_name) {
|
||||
$this->routeMatch->expects($this->once())
|
||||
->method('getRouteName')
|
||||
->will($this->returnValue($route_name));
|
||||
|
||||
$actual_result = $this->policy->check($this->response, $this->request);
|
||||
$this->assertSame($expected_result, $actual_result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data and expected results for the test method.
|
||||
*
|
||||
* @return array
|
||||
* Data and expected results.
|
||||
*/
|
||||
public function providerPrivateImageStyleDownloadPolicy() {
|
||||
return [
|
||||
[ResponsePolicyInterface::DENY, 'image.style_private'],
|
||||
[NULL, 'some.other.route'],
|
||||
[NULL, NULL],
|
||||
[NULL, FALSE],
|
||||
[NULL, TRUE],
|
||||
[NULL, new \StdClass()],
|
||||
[NULL, [1, 2, 3]],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -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