first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
<?php
/**
* @file
* Test lazy load update by modifying an image field form display.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
// Add a responsive image style.
$styles = [];
$styles['langcode'] = 'en';
$styles['status'] = TRUE;
$styles['dependencies']['config'][] = 'image.style.large';
$styles['dependencies']['config'][] = 'image.style.medium';
$styles['dependencies']['config'][] = 'image.style.thumbnail';
$styles['id'] = 'responsive_image_style';
$styles['uuid'] = '46225242-eb4c-4b10-9a8c-966130b18630';
$styles['label'] = 'Responsive Image Style';
$styles['breakpoint_group'] = 'responsive_image';
$styles['fallback_image_style'] = 'medium';
$styles['image_style_mappings'] = [
[
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '100vw',
'sizes_image_styles' => [
'large',
'medium',
'thumbnail',
],
],
'breakpoint_id' => 'responsive_image.viewport_sizing',
'multiplier' => '1x',
],
];
$connection->insert('config')
->fields([
'collection',
'name',
'data',
])
->values([
'collection' => '',
'name' => 'responsive_image.styles.responsive_image_style',
'data' => serialize($styles),
])
->execute();
// Update article view display to use responsive_image.
$article_form_display = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.entity_view_display.node.article.default')
->execute()
->fetchField();
$article_form_display = unserialize($article_form_display);
$article_form_display['content']['field_image']['type'] = 'responsive_image';
$article_form_display['content']['field_image']['settings'] = [
'responsive_image_style' => 'responsive_image_style',
'image_link' => '',
];
$connection->update('config')
->fields(['data' => serialize($article_form_display)])
->condition('collection', '')
->condition('name', 'core.entity_view_display.node.article.default')
->execute();

View File

@@ -0,0 +1,71 @@
<?php
/**
* @file
* Test fixture for re-ordering responsive image style multipliers numerically.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
// Add a responsive image style.
$styles = [];
$styles['langcode'] = 'en';
$styles['status'] = TRUE;
$styles['dependencies']['config'][] = 'image.style.large';
$styles['dependencies']['config'][] = 'image.style.medium';
$styles['dependencies']['config'][] = 'image.style.thumbnail';
$styles['id'] = 'responsive_image_style';
$styles['uuid'] = '46225242-eb4c-4b10-9a8c-966130b18630';
$styles['label'] = 'Responsive Image Style';
$styles['breakpoint_group'] = 'responsive_image';
$styles['fallback_image_style'] = 'medium';
$styles['image_style_mappings'] = [
[
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '75vw',
'sizes_image_styles' => [
'medium',
],
],
'breakpoint_id' => 'responsive_image.viewport_sizing',
'multiplier' => '1.5x',
],
[
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '100vw',
'sizes_image_styles' => [
'large',
],
],
'breakpoint_id' => 'responsive_image.viewport_sizing',
'multiplier' => '2x',
],
[
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '50vw',
'sizes_image_styles' => [
'thumbnail',
],
],
'breakpoint_id' => 'responsive_image.viewport_sizing',
'multiplier' => '1x',
],
];
$connection->insert('config')
->fields([
'collection',
'name',
'data',
])
->values([
'collection' => '',
'name' => 'responsive_image.styles.responsive_image_style',
'data' => serialize($styles),
])
->execute();

View File

@@ -0,0 +1,55 @@
<?php
/**
* @file
* Test fixture.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
// Set the schema version.
$connection->merge('key_value')
->fields([
'value' => 'i:8000;',
'name' => 'responsive_image',
'collection' => 'system.schema',
])
->condition('collection', 'system.schema')
->condition('name', 'responsive_image')
->execute();
// Update core.extension.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$extensions['module']['responsive_image'] = 0;
$connection->update('config')
->fields(['data' => serialize($extensions)])
->condition('collection', '')
->condition('name', 'core.extension')
->execute();
// Add all responsive_image_removed_post_updates() as existing updates.
require_once __DIR__ . '/../../../../responsive_image/responsive_image.post_update.php';
$existing_updates = $connection->select('key_value')
->fields('key_value', ['value'])
->condition('collection', 'post_update')
->condition('name', 'existing_updates')
->execute()
->fetchField();
$existing_updates = unserialize($existing_updates);
$existing_updates = array_merge(
$existing_updates,
array_keys(responsive_image_removed_post_updates())
);
$connection->update('key_value')
->fields(['value' => serialize($existing_updates)])
->condition('collection', 'post_update')
->condition('name', 'existing_updates')
->execute();

View File

@@ -0,0 +1,33 @@
responsive_image_test_module.empty:
label: empty
mediaQuery: ''
weight: 0
multipliers:
- 1x
- 1.5x
- 2x
responsive_image_test_module.mobile:
label: mobile
mediaQuery: '(min-width: 0px)'
weight: 1
multipliers:
- 1x
- 1.5x
- 2x
responsive_image_test_module.narrow:
label: narrow
mediaQuery: '(min-width: 560px)'
weight: 2
multipliers:
- 1x
- 1.5x
- 2x
responsive_image_test_module.wide:
label: wide
mediaQuery: '(min-width: 851px)'
weight: 3
multipliers:
- 1x
- 1.5x
- 2x

View File

@@ -0,0 +1,10 @@
name: 'Responsive image test theme'
type: module
description: 'Test theme for responsive image.'
package: Testing
# version: VERSION
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,36 @@
<?php
namespace Drupal\responsive_image_test_module\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\responsive_image\Plugin\Field\FieldFormatter\ResponsiveImageFormatter;
use Drupal\Core\Field\FieldItemListInterface;
/**
* Plugin to test responsive image formatter.
*/
#[FieldFormatter(
id: 'responsive_image_test',
label: new TranslatableMarkup('Responsive image test'),
field_types: [
'image',
],
)]
class ResponsiveImageTestFormatter extends ResponsiveImageFormatter {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);
// Unset #item_attributes to test that the theme function can handle that.
foreach ($elements as &$element) {
if (isset($element['#item_attributes'])) {
unset($element['#item_attributes']);
}
}
return $elements;
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional;
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
/**
* Generic module test for responsive_image.
*
* @group responsive_image
*/
class GenericTest extends GenericModuleTestBase {}

View File

@@ -0,0 +1,159 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional;
use Drupal\responsive_image\ResponsiveImageStyleInterface;
use Drupal\Tests\BrowserTestBase;
/**
* Thoroughly test the administrative interface of the Responsive Image module.
*
* @group responsive_image
*/
class ResponsiveImageAdminUITest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'responsive_image',
'responsive_image_test_module',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser([
'administer responsive images',
]));
}
/**
* Tests responsive image administration functionality.
*/
public function testResponsiveImageAdmin(): void {
// We start without any default styles.
$this->drupalGet('admin/config/media/responsive-image-style');
$this->assertSession()->pageTextContains('There are no responsive image styles yet.');
// Add a responsive image style.
$this->drupalGet('admin/config/media/responsive-image-style/add');
// The 'Responsive Image' breakpoint group should be selected by default.
$this->assertSession()->fieldValueEquals('breakpoint_group', 'responsive_image');
// Create a new group.
$edit = [
'label' => 'Style One',
'id' => 'style_one',
'breakpoint_group' => 'responsive_image',
'fallback_image_style' => 'thumbnail',
];
$this->drupalGet('admin/config/media/responsive-image-style/add');
$this->submitForm($edit, 'Save');
// Check if the new group is created.
$this->assertSession()->statusCodeEquals(200);
$this->drupalGet('admin/config/media/responsive-image-style');
$this->assertSession()->pageTextNotContains('There are no responsive image styles yet.');
$this->assertSession()->pageTextContains('Style One');
// Edit the breakpoint_group.
$this->drupalGet('admin/config/media/responsive-image-style/style_one');
$this->assertSession()->fieldValueEquals('label', 'Style One');
$this->assertSession()->fieldValueEquals('breakpoint_group', 'responsive_image');
$edit = [
'breakpoint_group' => 'responsive_image_test_module',
];
$this->submitForm($edit, 'Save');
// Edit the group.
$this->drupalGet('admin/config/media/responsive-image-style/style_one');
$this->assertSession()->fieldValueEquals('label', 'Style One');
$this->assertSession()->fieldValueEquals('breakpoint_group', 'responsive_image_test_module');
$this->assertSession()->fieldValueEquals('fallback_image_style', 'thumbnail');
$cases = [
['mobile', '1x'],
['mobile', '2x'],
['narrow', '1x'],
['narrow', '2x'],
['wide', '1x'],
['wide', '2x'],
];
$image_styles = array_merge(
[ResponsiveImageStyleInterface::EMPTY_IMAGE, ResponsiveImageStyleInterface::ORIGINAL_IMAGE],
array_keys(image_style_options(FALSE))
);
foreach ($cases as $case) {
// Check if the radio buttons are present.
$this->assertSession()->fieldExists('keyed_styles[responsive_image_test_module.' . $case[0] . '][' . $case[1] . '][image_mapping_type]');
// Check if the image style dropdowns are present.
$this->assertSession()->fieldExists('keyed_styles[responsive_image_test_module.' . $case[0] . '][' . $case[1] . '][image_style]');
// Check if the sizes textfields are present.
$this->assertSession()->fieldExists('keyed_styles[responsive_image_test_module.' . $case[0] . '][' . $case[1] . '][sizes]');
foreach ($image_styles as $image_style_name) {
// Check if the image styles are available in the dropdowns.
$this->assertSession()->optionExists('keyed_styles[responsive_image_test_module.' . $case[0] . '][' . $case[1] . '][image_style]', $image_style_name);
// Check if the image styles checkboxes are present.
$this->assertSession()->fieldExists('keyed_styles[responsive_image_test_module.' . $case[0] . '][' . $case[1] . '][sizes_image_styles][' . $image_style_name . ']');
}
}
// Save styles for 1x variant only.
$edit = [
'label' => 'Style One',
'breakpoint_group' => 'responsive_image_test_module',
'fallback_image_style' => 'thumbnail',
'keyed_styles[responsive_image_test_module.mobile][1x][image_mapping_type]' => 'image_style',
'keyed_styles[responsive_image_test_module.mobile][1x][image_style]' => 'thumbnail',
'keyed_styles[responsive_image_test_module.narrow][1x][image_mapping_type]' => 'sizes',
// Ensure the Sizes field allows long values.
'keyed_styles[responsive_image_test_module.narrow][1x][sizes]' => '(min-resolution: 192dpi) and (min-width: 170px) 386px, (min-width: 170px) 193px, (min-width: 768px) 18vw, (min-width: 480px) 30vw, 48vw',
'keyed_styles[responsive_image_test_module.narrow][1x][sizes_image_styles][large]' => 'large',
'keyed_styles[responsive_image_test_module.narrow][1x][sizes_image_styles][medium]' => 'medium',
'keyed_styles[responsive_image_test_module.wide][1x][image_mapping_type]' => 'image_style',
'keyed_styles[responsive_image_test_module.wide][1x][image_style]' => 'large',
];
$this->drupalGet('admin/config/media/responsive-image-style/style_one');
$this->submitForm($edit, 'Save');
$this->drupalGet('admin/config/media/responsive-image-style/style_one');
// Check the mapping for multipliers 1x and 2x for the mobile breakpoint.
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.mobile][1x][image_style]', 'thumbnail');
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.mobile][1x][image_mapping_type]', 'image_style');
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.mobile][2x][image_mapping_type]', '_none');
// Check the mapping for multipliers 1x and 2x for the narrow breakpoint.
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.narrow][1x][image_mapping_type]', 'sizes');
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.narrow][1x][sizes]', '(min-resolution: 192dpi) and (min-width: 170px) 386px, (min-width: 170px) 193px, (min-width: 768px) 18vw, (min-width: 480px) 30vw, 48vw');
$this->assertSession()->checkboxChecked('edit-keyed-styles-responsive-image-test-modulenarrow-1x-sizes-image-styles-large');
$this->assertSession()->checkboxChecked('edit-keyed-styles-responsive-image-test-modulenarrow-1x-sizes-image-styles-medium');
$this->assertSession()->checkboxNotChecked('edit-keyed-styles-responsive-image-test-modulenarrow-1x-sizes-image-styles-thumbnail');
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.narrow][2x][image_mapping_type]', '_none');
// Check the mapping for multipliers 1x and 2x for the wide breakpoint.
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.wide][1x][image_style]', 'large');
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.wide][1x][image_mapping_type]', 'image_style');
$this->assertSession()->fieldValueEquals('keyed_styles[responsive_image_test_module.wide][2x][image_mapping_type]', '_none');
// Delete the style.
$this->drupalGet('admin/config/media/responsive-image-style/style_one/delete');
$this->submitForm([], 'Delete');
$this->drupalGet('admin/config/media/responsive-image-style');
$this->assertSession()->pageTextContains('There are no responsive image styles yet.');
}
}

View File

@@ -0,0 +1,583 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional;
use Drupal\image\Entity\ImageStyle;
use Drupal\image\ImageStyleInterface;
use Drupal\node\Entity\Node;
use Drupal\file\Entity\File;
use Drupal\responsive_image\Plugin\Field\FieldFormatter\ResponsiveImageFormatter;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\responsive_image\ResponsiveImageStyleInterface;
use Drupal\Tests\image\Functional\ImageFieldTestBase;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\user\RoleInterface;
/**
* Tests responsive image display formatter.
*
* @group responsive_image
* @group #slow
*/
class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase {
use TestFileCreationTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Responsive image style entity instance we test with.
*
* @var \Drupal\responsive_image\Entity\ResponsiveImageStyle
*/
protected $responsiveImgStyle;
/**
* The file URL generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'field_ui',
'responsive_image',
'responsive_image_test_module',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->fileUrlGenerator = $this->container->get('file_url_generator');
// Create user.
$this->adminUser = $this->drupalCreateUser([
'administer responsive images',
'access content',
'access administration pages',
'administer site configuration',
'administer content types',
'administer node display',
'administer nodes',
'create article content',
'edit any article content',
'delete any article content',
'administer image styles',
]);
$this->drupalLogin($this->adminUser);
// Add responsive image style.
$this->responsiveImgStyle = ResponsiveImageStyle::create([
'id' => 'style_one',
'label' => 'Style One',
'breakpoint_group' => 'responsive_image_test_module',
'fallback_image_style' => 'large',
]);
}
/**
* Tests responsive image formatters on node display for public files.
*/
public function testResponsiveImageFieldFormattersPublic(): void {
$this->addTestImageStyleMappings();
$this->doTestResponsiveImageFieldFormatters('public');
}
/**
* Tests responsive image formatters on node display for private files.
*/
public function testResponsiveImageFieldFormattersPrivate(): void {
$this->addTestImageStyleMappings();
// Remove access content permission from anonymous users.
user_role_change_permissions(RoleInterface::ANONYMOUS_ID, ['access content' => FALSE]);
$this->doTestResponsiveImageFieldFormatters('private');
}
/**
* Tests responsive image formatters when image style is empty.
*/
public function testResponsiveImageFieldFormattersEmptyStyle(): void {
$this->addTestImageStyleMappings(TRUE);
$this->doTestResponsiveImageFieldFormatters('public', TRUE);
}
/**
* Add image style mappings to the responsive image style entity.
*
* @param bool $empty_styles
* If true, the image style mappings will get empty image styles.
*/
protected function addTestImageStyleMappings($empty_styles = FALSE) {
if ($empty_styles) {
$this->responsiveImgStyle
->addImageStyleMapping('responsive_image_test_module.mobile', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => '',
])
->addImageStyleMapping('responsive_image_test_module.narrow', '1x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width: 700px) 700px, 100vw',
'sizes_image_styles' => [],
],
])
->addImageStyleMapping('responsive_image_test_module.wide', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => '',
])
->save();
}
else {
$this->responsiveImgStyle
// Test the output of an empty image.
->addImageStyleMapping('responsive_image_test_module.mobile', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => ResponsiveImageStyleInterface::EMPTY_IMAGE,
])
// Test the output with a 1.5x multiplier.
->addImageStyleMapping('responsive_image_test_module.mobile', '1.5x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
])
// Test the output of the 'sizes' attribute.
->addImageStyleMapping('responsive_image_test_module.narrow', '1x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width: 700px) 700px, 100vw',
'sizes_image_styles' => [
'large',
'medium',
],
],
])
// Test the normal output of mapping to an image style.
->addImageStyleMapping('responsive_image_test_module.wide', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
])
// Test the output of the original image.
->addImageStyleMapping('responsive_image_test_module.wide', '3x', [
'image_mapping_type' => 'image_style',
'image_mapping' => ResponsiveImageStyleInterface::ORIGINAL_IMAGE,
])
->save();
}
}
/**
* Tests responsive image formatters on node display.
*
* If the empty styles param is set, then the function only tests for the
* fallback image style (large).
*
* @param string $scheme
* File scheme to use.
* @param bool $empty_styles
* If true, use an empty string for image style names.
* Defaults to false.
*/
protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles = FALSE) {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$field_name = $this->randomMachineName();
$this->createImageField($field_name, 'node', 'article', ['uri_scheme' => $scheme]);
// Create a new node with an image attached. Make sure we use a large image
// so the scale effects of the image styles always have an effect.
$test_image = current($this->getTestFiles('image', 39325));
// Create alt text for the image.
$alt = $this->randomMachineName();
$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.
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
$image = [
'#theme' => 'image',
'#uri' => $image_uri,
'#width' => 360,
'#height' => 240,
'#alt' => $alt,
'#attributes' => ['loading' => 'lazy'],
];
$default_output = str_replace("\n", '', (string) $renderer->renderRoot($image));
$this->assertSession()->responseContains($default_output);
// Test field not being configured. This should not cause a fatal error.
$display_options = [
'type' => 'responsive_image_test',
'settings' => ResponsiveImageFormatter::defaultSettings(),
];
$display = $this->container->get('entity_type.manager')
->getStorage('entity_view_display')
->load('node.article.default');
if (!$display) {
$values = [
'targetEntityType' => 'node',
'bundle' => 'article',
'mode' => 'default',
'status' => TRUE,
];
$display = $this->container->get('entity_type.manager')->getStorage('entity_view_display')->create($values);
}
$display->setComponent($field_name, $display_options)->save();
$this->drupalGet('node/' . $nid);
// Test theme function for responsive image, but using the test formatter.
$display_options = [
'type' => 'responsive_image_test',
'settings' => [
'image_link' => 'file',
'responsive_image_style' => 'style_one',
],
];
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
$display = $display_repository->getViewDisplay('node', 'article');
$display->setComponent($field_name, $display_options)
->save();
$this->drupalGet('node/' . $nid);
// Use the responsive image formatter linked to file formatter.
$display_options = [
'type' => 'responsive_image',
'settings' => [
'image_link' => 'file',
'responsive_image_style' => 'style_one',
],
];
$display = $display_repository->getViewDisplay('node', 'article');
$display->setComponent($field_name, $display_options)
->save();
$this->drupalGet('node/' . $nid);
// No image style cache tag should be found.
$this->assertSession()->responseHeaderNotContains('X-Drupal-Cache-Tags', 'image_style:');
$this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote($this->fileUrlGenerator->generateString($image_uri), '/') . '"(.*?)>\s*<picture/');
// Verify that the image can be downloaded.
$this->assertEquals(file_get_contents($test_image->uri), $this->drupalGet($this->fileUrlGenerator->generateAbsoluteString($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->assertSession()->responseHeaderEquals('Content-Type', 'image/png');
$this->assertSession()->responseHeaderContains('Cache-Control', 'private');
// Log out and ensure the file cannot be accessed.
$this->drupalLogout();
$this->drupalGet($this->fileUrlGenerator->generateAbsoluteString($image_uri));
$this->assertSession()->statusCodeEquals(403);
// Log in again.
$this->drupalLogin($this->adminUser);
}
// Use the responsive image formatter with a responsive image style.
$display_options['settings']['responsive_image_style'] = 'style_one';
$display_options['settings']['image_link'] = '';
$display->setComponent($field_name, $display_options)
->save();
// Create a derivative so at least one MIME type will be known.
$large_style = ImageStyle::load('large');
$large_style->createDerivative($image_uri, $large_style->buildUri($image_uri));
// Output should contain all image styles and all breakpoints.
$this->drupalGet('node/' . $nid);
if (!$empty_styles) {
$this->assertSession()->responseContains('/styles/medium/');
// Assert the empty image is present.
$this->assertSession()->responseContains('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
$thumbnail_style = ImageStyle::load('thumbnail');
// Assert the output of the 'srcset' attribute (small multipliers first).
$this->assertSession()->responseContains('data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x, ' . $this->fileUrlGenerator->transformRelative($thumbnail_style->buildUrl($image_uri)) . ' 1.5x');
$this->assertSession()->responseContains('/styles/medium/');
// Assert the output of the original image.
$this->assertSession()->responseContains($this->fileUrlGenerator->generateString($image_uri) . ' 3x');
// Assert the output of the breakpoints.
$this->assertSession()->responseContains('media="(min-width: 0px)"');
$this->assertSession()->responseContains('media="(min-width: 560px)"');
// Assert the output of the 'sizes' attribute.
$this->assertSession()->responseContains('sizes="(min-width: 700px) 700px, 100vw"');
$this->assertSession()->responseMatches('/media="\(min-width: 560px\)".+?sizes="\(min-width: 700px\) 700px, 100vw"/');
// Assert the output of the 'srcset' attribute (small images first).
$medium_style = ImageStyle::load('medium');
$this->assertSession()->responseContains($this->fileUrlGenerator->transformRelative($medium_style->buildUrl($image_uri)) . ' 220w, ' . $this->fileUrlGenerator->transformRelative($large_style->buildUrl($image_uri)) . ' 360w');
$this->assertSession()->responseContains('media="(min-width: 851px)"');
// Assert the output of the 'width' attribute.
$this->assertSession()->responseContains('width="360"');
// Assert the output of the 'height' attribute.
$this->assertSession()->responseContains('height="240"');
$this->assertSession()->responseContains('loading="lazy"');
}
$this->assertSession()->responseContains('/styles/large/');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:responsive_image.styles.style_one');
if (!$empty_styles) {
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:image.style.medium');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:image.style.thumbnail');
$this->assertSession()->responseContains('type="image/webp"');
}
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Tags', 'config:image.style.large');
// Test the fallback image style. Copy the source image:
$fallback_image = $image;
// Set the fallback image style uri:
$fallback_image['#uri'] = $this->fileUrlGenerator->transformRelative($large_style->buildUrl($image_uri));
// The image.html.twig template has a newline after the <img> tag but
// responsive-image.html.twig doesn't have one after the fallback image, so
// we remove it here.
$default_output = trim((string) $renderer->renderRoot($fallback_image));
$this->assertSession()->responseContains($default_output);
if ($scheme == 'private') {
// Log out and ensure the file cannot be accessed.
$this->drupalLogout();
$this->drupalGet($large_style->buildUrl($image_uri));
$this->assertSession()->statusCodeEquals(403);
$this->assertSession()->responseHeaderNotMatches('X-Drupal-Cache-Tags', '/ image_style\:/');
}
}
/**
* Tests responsive image formatters on node display linked to the file.
*/
public function testResponsiveImageFieldFormattersLinkToFile(): void {
$this->addTestImageStyleMappings();
$this->assertResponsiveImageFieldFormattersLink('file');
}
/**
* Tests responsive image formatters on node display linked to the node.
*/
public function testResponsiveImageFieldFormattersLinkToNode(): void {
$this->addTestImageStyleMappings();
$this->assertResponsiveImageFieldFormattersLink('content');
}
/**
* Tests responsive image formatter on node display with an empty media query.
*/
public function testResponsiveImageFieldFormattersEmptyMediaQuery(): void {
$this->responsiveImgStyle
// Test the output of an empty media query.
->addImageStyleMapping('responsive_image_test_module.empty', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => ResponsiveImageStyleInterface::EMPTY_IMAGE,
])
// Test the output with a 1.5x multiplier.
->addImageStyleMapping('responsive_image_test_module.mobile', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
])
->save();
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$field_name = $this->randomMachineName();
$this->createImageField($field_name, 'node', 'article', ['uri_scheme' => 'public']);
// Create a new node with an image attached.
$test_image = current($this->getTestFiles('image'));
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $this->randomMachineName());
$node_storage->resetCache([$nid]);
// Use the responsive image formatter linked to file formatter.
$display_options = [
'type' => 'responsive_image',
'settings' => [
'image_link' => '',
'responsive_image_style' => 'style_one',
],
];
$display = \Drupal::service('entity_display.repository')
->getViewDisplay('node', 'article');
$display->setComponent($field_name, $display_options)
->save();
// View the node.
$this->drupalGet('node/' . $nid);
// Assert an empty media attribute is not output.
$this->assertSession()->responseNotMatches('@srcset="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== 1x".+?media=".+?/><source@');
// Assert the media attribute is present if it has a value.
$thumbnail_style = ImageStyle::load('thumbnail');
$node = $node_storage->load($nid);
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
$this->assertSession()->responseMatches('/srcset="' . preg_quote($this->fileUrlGenerator->transformRelative($thumbnail_style->buildUrl($image_uri)), '/') . ' 1x".+?media="\(min-width: 0px\)"/');
}
/**
* Tests responsive image formatter on node display with one and two sources.
*/
public function testResponsiveImageFieldFormattersMultipleSources(): void {
// Setup known image style sizes so the test can assert on known sizes.
$large_style = ImageStyle::load('large');
assert($large_style instanceof ImageStyleInterface);
$large_style->addImageEffect([
'id' => 'image_resize',
'data' => [
'width' => '480',
'height' => '480',
],
]);
$large_style->save();
$medium_style = ImageStyle::load('medium');
assert($medium_style instanceof ImageStyleInterface);
$medium_style->addImageEffect([
'id' => 'image_resize',
'data' => [
'width' => '220',
'height' => '220',
],
]);
$medium_style->save();
$this->responsiveImgStyle
// Test the output of an empty media query.
->addImageStyleMapping('responsive_image_test_module.empty', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => $medium_style->id(),
])
->addImageStyleMapping('responsive_image_test_module.empty', '1.5x', [
'image_mapping_type' => 'image_style',
'image_mapping' => $large_style->id(),
])
->addImageStyleMapping('responsive_image_test_module.empty', '2x', [
'image_mapping_type' => 'image_style',
'image_mapping' => $large_style->id(),
])
->save();
$node_storage = $this->container->get('entity_type.manager')->getStorage('node');
$field_name = $this->randomMachineName();
$this->createImageField($field_name, 'node', 'article', ['uri_scheme' => 'public']);
// Create a new node with an image attached.
$test_image = current($this->getTestFiles('image'));
$nid = $this->uploadNodeImage($test_image, $field_name, 'article', $this->randomMachineName());
$node_storage->resetCache([$nid]);
// Use the responsive image formatter linked to file formatter.
$display_options = [
'type' => 'responsive_image',
'settings' => [
'image_link' => '',
'responsive_image_style' => 'style_one',
'image_loading' => [
// Test the image loading default option can be overridden.
'attribute' => 'eager',
],
],
];
$display = \Drupal::service('entity_display.repository')
->getViewDisplay('node', 'article');
$display->setComponent($field_name, $display_options)
->save();
// View the node.
$this->drupalGet('node/' . $nid);
// Assert the img tag has medium and large images and fallback dimensions
// from the large image style are used.
$node = $node_storage->load($nid);
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
$medium_transform_url = $this->fileUrlGenerator->transformRelative($medium_style->buildUrl($image_uri));
$large_transform_url = $this->fileUrlGenerator->transformRelative($large_style->buildUrl($image_uri));
$this->assertSession()->responseMatches('/<img loading="eager" srcset="' . \preg_quote($medium_transform_url, '/') . ' 1x, ' . \preg_quote($large_transform_url, '/') . ' 1.5x, ' . \preg_quote($large_transform_url, '/') . ' 2x" width="220" height="220" src="' . \preg_quote($large_transform_url, '/') . '" alt="\w+" \/>/');
$this->responsiveImgStyle
// Test the output of an empty media query.
->addImageStyleMapping('responsive_image_test_module.wide', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => $large_style->id(),
])
->save();
// Assert the picture tag has source tags that include dimensions.
$this->drupalGet('node/' . $nid);
$this->assertSession()->responseMatches('/<picture>\s+<source srcset="' . \preg_quote($large_transform_url, '/') . ' 1x" media="\(min-width: 851px\)" type="image\/webp" width="480" height="480"\/>\s+<source srcset="' . \preg_quote($medium_transform_url, '/') . ' 1x, ' . \preg_quote($large_transform_url, '/') . ' 1.5x, ' . \preg_quote($large_transform_url, '/') . ' 2x" type="image\/webp" width="220" height="220"\/>\s+<img loading="eager" src="' . \preg_quote($large_transform_url, '/') . '" width="480" height="480" alt="\w+" \/>\s+<\/picture>/');
}
/**
* Tests responsive image formatters linked to the file or node.
*
* @param string $link_type
* The link type to test. Either 'file' or 'content'.
*/
private function assertResponsiveImageFieldFormattersLink(string $link_type): void {
$field_name = $this->randomMachineName();
$field_settings = ['alt_field_required' => 0];
$this->createImageField($field_name, 'node', 'article', ['uri_scheme' => 'public'], $field_settings);
// Create a new node with an image attached.
$test_image = current($this->getTestFiles('image'));
/** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
$display_repository = \Drupal::service('entity_display.repository');
// Test the image linked to file formatter.
$display_options = [
'type' => 'responsive_image',
'settings' => [
'image_link' => $link_type,
'responsive_image_style' => 'style_one',
],
];
$display_repository->getViewDisplay('node', 'article')
->setComponent($field_name, $display_options)
->save();
// Ensure that preview works.
$this->previewNodeImage($test_image, $field_name, 'article');
// Look for a picture tag in the preview output
$this->assertSession()->responseMatches('/picture/');
$nid = $this->uploadNodeImage($test_image, $field_name, 'article');
$this->container->get('entity_type.manager')->getStorage('node')->resetCache([$nid]);
$node = Node::load($nid);
// Use the responsive image formatter linked to file formatter.
$display_options = [
'type' => 'responsive_image',
'settings' => [
'image_link' => $link_type,
'responsive_image_style' => 'style_one',
],
];
$display_repository->getViewDisplay('node', 'article')
->setComponent($field_name, $display_options)
->save();
// Create a derivative so at least one MIME type will be known.
$large_style = ImageStyle::load('large');
$image_uri = File::load($node->{$field_name}->target_id)->getFileUri();
$large_style->createDerivative($image_uri, $large_style->buildUri($image_uri));
// Output should contain all image styles and all breakpoints.
$this->drupalGet('node/' . $nid);
switch ($link_type) {
case 'file':
// Make sure the link to the file is present.
$this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote($this->fileUrlGenerator->generateString($image_uri), '/') . '"(.*?)>\s*<picture/');
break;
case 'content':
// Make sure the link to the node is present.
$this->assertSession()->responseMatches('/<a(.*?)href="' . preg_quote($node->toUrl()->toString(), '/') . '"(.*?)>\s*<picture/');
break;
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests lazy-load upgrade path.
*
* @coversDefaultClass \Drupal\responsive_image\ResponsiveImageConfigUpdater
*
* @group responsive_image
* @group legacy
*/
class ResponsiveImageLazyLoadUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles(): void {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-9.4.0.filled.standard.php.gz',
__DIR__ . '/../../fixtures/update/responsive_image.php',
__DIR__ . '/../../fixtures/update/responsive_image-loading-attribute.php',
];
}
/**
* Test new lazy-load setting upgrade path.
*
* @see responsive_image_post_update_image_loading_attribute
*/
public function testUpdate(): void {
$data = EntityViewDisplay::load('node.article.default')->toArray();
$this->assertArrayNotHasKey('image_loading', $data['content']['field_image']['settings']);
$this->runUpdates();
$data = EntityViewDisplay::load('node.article.default')->toArray();
$this->assertArrayHasKey('image_loading', $data['content']['field_image']['settings']);
$this->assertEquals('eager', $data['content']['field_image']['settings']['image_loading']['attribute']);
}
/**
* Test responsive_image_entity_view_display_presave invokes deprecations.
*
* @covers ::processResponsiveImageField
*/
public function testEntitySave(): void {
$this->expectDeprecation('The responsive image loading attribute update for "node.article.default" is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Configuration should be updated. See https://www.drupal.org/node/3279032');
$view_display = EntityViewDisplay::load('node.article.default');
$this->assertArrayNotHasKey('image_loading', $view_display->toArray()['content']['field_image']['settings']);
$view_display->save();
$view_display = EntityViewDisplay::load('node.article.default');
$this->assertArrayHasKey('image_loading', $view_display->toArray()['content']['field_image']['settings']);
$this->assertEquals('eager', $view_display->toArray()['content']['field_image']['settings']['image_loading']['attribute']);
}
}

View File

@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
/**
* Tests order multipliers numerically upgrade path.
*
* @coversDefaultClass \Drupal\responsive_image\ResponsiveImageConfigUpdater
*
* @group responsive_image
* @group legacy
*/
class ResponsiveImageOrderMultipliersNumericallyUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles(): void {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-9.4.0.filled.standard.php.gz',
__DIR__ . '/../../fixtures/update/responsive_image.php',
__DIR__ . '/../../fixtures/update/responsive_image-order-multipliers-numerically.php',
];
}
/**
* Test order multipliers numerically upgrade path.
*
* @see responsive_image_post_update_order_multiplier_numerically()
*
* @legacy
*/
public function testUpdate(): void {
$mappings = ResponsiveImageStyle::load('responsive_image_style')->getImageStyleMappings();
$this->assertEquals('1.5x', $mappings[0]['multiplier']);
$this->assertEquals('2x', $mappings[1]['multiplier']);
$this->assertEquals('1x', $mappings[2]['multiplier']);
$this->runUpdates();
$mappings = ResponsiveImageStyle::load('responsive_image_style')->getImageStyleMappings();
$this->assertEquals('1x', $mappings[0]['multiplier']);
$this->assertEquals('1.5x', $mappings[1]['multiplier']);
$this->assertEquals('2x', $mappings[2]['multiplier']);
}
/**
* Test ResponsiveImageStyle::preSave correctly orders by multiplier weight.
*
* @covers ::orderMultipliersNumerically
*/
public function testEntitySave(): void {
$this->expectDeprecation('The responsive image style multiplier re-ordering update for "responsive_image_style" is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Profile, module and theme provided Responsive Image configuration should be updated. See https://www.drupal.org/node/3274803');
$image_style = ResponsiveImageStyle::load('responsive_image_style');
$mappings = $image_style->getImageStyleMappings();
$this->assertEquals('1.5x', $mappings[0]['multiplier']);
$this->assertEquals('2x', $mappings[1]['multiplier']);
$this->assertEquals('1x', $mappings[2]['multiplier']);
$image_style->save();
$mappings = ResponsiveImageStyle::load('responsive_image_style')->getImageStyleMappings();
$this->assertEquals('1x', $mappings[0]['multiplier']);
$this->assertEquals('1.5x', $mappings[1]['multiplier']);
$this->assertEquals('2x', $mappings[2]['multiplier']);
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class ResponsiveImageStyleJsonAnonTest extends ResponsiveImageStyleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class ResponsiveImageStyleJsonBasicAuthTest extends ResponsiveImageStyleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class ResponsiveImageStyleJsonCookieTest extends ResponsiveImageStyleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\Tests\rest\Functional\EntityResource\ConfigEntityResourceTestBase;
/**
* ResourceTestBase for ResponsiveImageStyle entity.
*/
abstract class ResponsiveImageStyleResourceTestBase extends ConfigEntityResourceTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['responsive_image'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'responsive_image_style';
/**
* The ResponsiveImageStyle entity.
*
* @var \Drupal\responsive_image\ResponsiveImageStyleInterface
*/
protected $entity;
/**
* The effect UUID.
*
* @var string
*/
protected $effectUuid;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer responsive images']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Camelids" responsive image style.
$camelids = ResponsiveImageStyle::create([
'id' => 'camelids',
'label' => 'Camelids',
]);
$camelids->setBreakpointGroup('test_group');
$camelids->setFallbackImageStyle('fallback');
$camelids->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'small',
]);
$camelids->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'medium' => 'medium',
'large' => 'large',
],
],
]);
$camelids->save();
return $camelids;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'breakpoint_group' => 'test_group',
'dependencies' => [
'config' => [
'image.style.large',
'image.style.medium',
],
],
'fallback_image_style' => 'fallback',
'id' => 'camelids',
'image_style_mappings' => [
0 => [
'breakpoint_id' => 'test_breakpoint',
'image_mapping' => 'small',
'image_mapping_type' => 'image_style',
'multiplier' => '1x',
],
1 => [
'breakpoint_id' => 'test_breakpoint',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
'medium' => 'medium',
],
],
'image_mapping_type' => 'sizes',
'multiplier' => '2x',
],
],
'label' => 'Camelids',
'langcode' => 'en',
'status' => TRUE,
'uuid' => $this->entity->uuid(),
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
return [];
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
return "The 'administer responsive images' permission is required.";
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class ResponsiveImageStyleXmlAnonTest extends ResponsiveImageStyleResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class ResponsiveImageStyleXmlBasicAuthTest extends ResponsiveImageStyleResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class ResponsiveImageStyleXmlCookieTest extends ResponsiveImageStyleResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
}

View File

@@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\Tests\views\Functional\ViewTestBase;
/**
* Tests the integration of responsive image with Views.
*
* @group responsive_image
*/
class ViewsIntegrationTest extends ViewTestBase {
/**
* The responsive image style ID to use.
*/
const RESPONSIVE_IMAGE_STYLE_ID = 'responsive_image_style_id';
/**
* {@inheritdoc}
*/
protected static $modules = [
'views',
'views_ui',
'responsive_image',
'field',
'image',
'file',
'entity_test',
'breakpoint',
'responsive_image_test_module',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The test views to enable.
*/
public static $testViews = ['entity_test_row'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = ['views_test_config']): void {
parent::setUp($import_test_views, $modules);
$this->enableViewsTestModule();
// Create a responsive image style.
$responsive_image_style = ResponsiveImageStyle::create([
'id' => self::RESPONSIVE_IMAGE_STYLE_ID,
'label' => 'Foo',
'breakpoint_group' => 'responsive_image_test_module',
]);
// Create an image field to be used with a responsive image formatter.
FieldStorageConfig::create([
'type' => 'image',
'entity_type' => 'entity_test',
'field_name' => 'bar',
])->save();
FieldConfig::create([
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'field_name' => 'bar',
])->save();
$responsive_image_style
->addImageStyleMapping('responsive_image_test_module.mobile', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
])
->addImageStyleMapping('responsive_image_test_module.narrow', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'medium',
])
// Test the normal output of mapping to an image style.
->addImageStyleMapping('responsive_image_test_module.wide', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
])
->save();
$admin_user = $this->drupalCreateUser(['administer views']);
$this->drupalLogin($admin_user);
}
/**
* Tests integration with Views.
*/
public function testViewsAddResponsiveImageField(): void {
// Add the image field to the View.
$this->drupalGet('admin/structure/views/nojs/add-handler/entity_test_row/default/field');
$this->drupalGet('admin/structure/views/nojs/add-handler/entity_test_row/default/field');
$this->submitForm(['name[entity_test__bar.bar]' => TRUE], 'Add and configure field');
// Set the formatter to 'Responsive image'.
$this->submitForm(['options[type]' => 'responsive_image'], 'Apply');
$this->assertSession()
->responseContains('Responsive image style field is required.');
$this->submitForm(['options[settings][responsive_image_style]' => self::RESPONSIVE_IMAGE_STYLE_ID], 'Apply');
$this->drupalGet('admin/structure/views/nojs/handler/entity_test_row/default/field/bar');
// Make sure the selected value is set.
$this->assertSession()
->fieldValueEquals('options[settings][responsive_image_style]', self::RESPONSIVE_IMAGE_STYLE_ID);
}
}

View File

@@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait;
/**
* Tests the responsive image field UI.
*
* @group responsive_image
*/
class ResponsiveImageFieldUiTest extends WebDriverTestBase {
use FieldUiJSTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Modules to install.
*
* @var array
*/
protected static $modules = [
'node',
'field_ui',
'image',
'responsive_image',
'responsive_image_test_module',
'block',
];
/**
* The content type id.
*
* @var string
*/
protected string $type;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
// Create a test user.
$admin_user = $this->drupalCreateUser([
'access content',
'administer content types',
'administer node fields',
'administer node form display',
'administer node display',
'bypass node access',
]);
$this->drupalLogin($admin_user);
// Create content type, with underscores.
$type_name = $this->randomMachineName(8) . '_test';
$type = $this->drupalCreateContentType([
'name' => $type_name,
'type' => $type_name,
]);
$this->type = $type->id();
}
/**
* Tests formatter settings.
*/
public function testResponsiveImageFormatterUi(): void {
$manage = 'admin/structure/types/manage/' . $this->type;
$manage_display = $manage . '/display';
/** @var \Drupal\FunctionalJavascriptTests\JSWebAssert $assert_session */
$assert_session = $this->assertSession();
$this->fieldUIAddNewFieldJS('admin/structure/types/manage/' . $this->type, 'image', 'Image', 'image');
// Display the "Manage display" page.
$this->drupalGet($manage_display);
// Change the formatter and check that the summary is updated.
$page = $this->getSession()->getPage();
$field_image_type = $page->findField('fields[field_image][type]');
$field_image_type->setValue('responsive_image');
$summary_text = $assert_session->waitForElement('xpath', $this->cssSelectToXpath('#field-image .ajax-new-content .field-plugin-summary'));
$this->assertEquals('Select a responsive image style. Loading attribute: lazy', $summary_text->getText());
$page->pressButton('Save');
$assert_session->responseContains("Select a responsive image style.");
// Create responsive image styles.
$responsive_image_style = ResponsiveImageStyle::create([
'id' => 'style_one',
'label' => 'Style One',
'breakpoint_group' => 'responsive_image_test_module',
'fallback_image_style' => 'thumbnail',
]);
$responsive_image_style
->addImageStyleMapping('responsive_image_test_module.mobile', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
])
->addImageStyleMapping('responsive_image_test_module.narrow', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'medium',
])
// Test the normal output of mapping to an image style.
->addImageStyleMapping('responsive_image_test_module.wide', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
])
->save();
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
// Refresh the page.
$this->drupalGet($manage_display);
$assert_session->responseContains("Select a responsive image style.");
// Click on the formatter settings button to open the formatter settings
// form.
$field_image_type = $page->findField('fields[field_image][type]');
$field_image_type->setValue('responsive_image');
$page->find('css', '#edit-fields-field-image-settings-edit')->click();
$assert_session->waitForField('fields[field_image][settings_edit_form][settings][responsive_image_style]');
// Assert that the correct fields are present.
$fieldnames = [
'fields[field_image][settings_edit_form][settings][responsive_image_style]',
'fields[field_image][settings_edit_form][settings][image_link]',
];
foreach ($fieldnames as $fieldname) {
$assert_session->fieldExists($fieldname);
}
$page->findField('fields[field_image][settings_edit_form][settings][responsive_image_style]')->setValue('style_one');
$page->findField('fields[field_image][settings_edit_form][settings][image_link]')->setValue('content');
// Save the form to save the settings.
$page->pressButton('Save');
$assert_session->responseContains('Responsive image style: Style One');
$assert_session->responseContains('Linked to content');
$page->find('css', '#edit-fields-field-image-settings-edit')->click();
$assert_session->waitForField('fields[field_image][settings_edit_form][settings][responsive_image_style]');
$page->findField('fields[field_image][settings_edit_form][settings][image_link]')->setValue('file');
// Save the form to save the settings.
$page->pressButton('Save');
$assert_session->responseContains('Responsive image style: Style One');
$assert_session->responseContains('Linked to file');
}
}

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Kernel\Migrate\d7;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of responsive image styles.
*
* @group responsive_image
*/
class MigrateResponsiveImageStylesTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['responsive_image', 'breakpoint', 'image'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Ensure the 'picture' module is enabled in the source.
$this->sourceDatabase->update('system')
->condition('name', 'picture')
->fields(['status' => 1])
->execute();
$this->executeMigrations(['d7_image_styles', 'd7_responsive_image_styles']);
}
/**
* Tests the Drupal 7 to Drupal 8 responsive image styles migration.
*/
public function testResponsiveImageStyles(): void {
$expected_image_style_mappings = [
[
'image_mapping_type' => 'image_style',
'image_mapping' => 'custom_image_style_1',
'breakpoint_id' => 'responsive_image.computer',
'multiplier' => 'multiplier_1',
],
[
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '2',
'sizes_image_styles' => [
'custom_image_style_1',
'custom_image_style_2',
],
],
'breakpoint_id' => 'responsive_image.computer',
'multiplier' => 'multiplier_2',
],
[
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '2',
'sizes_image_styles' => [
'custom_image_style_1',
'custom_image_style_2',
],
],
'breakpoint_id' => 'responsive_image.computertwo',
'multiplier' => 'multiplier_2',
],
];
$this->assertSame($expected_image_style_mappings, ResponsiveImageStyle::load('narrow')
->getImageStyleMappings());
}
}

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D7 responsive image styles source plugin.
*
* @covers \Drupal\responsive_image\Plugin\migrate\source\d7\ResponsiveImageStyles
* @group image
*/
class ResponsiveImageStylesTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'migrate_drupal',
'responsive_image',
];
/**
* {@inheritdoc}
*/
public static function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['picture_mapping'] = [
[
'label' => 'Narrow',
'machine_name' => 'narrow',
'breakpoint_group' => 'responsive_image',
'mapping' => 'a:2:{s:38:"breakpoints.theme.my_theme_id.computer";a:3:{s:12:"multiplier_1";a:2:{s:12:"mapping_type";s:11:"image_style";s:11:"image_style";s:20:"custom_image_style_1";}s:12:"multiplier_2";a:3:{s:12:"mapping_type";s:5:"sizes";s:5:"sizes";i:2;s:18:"sizes_image_styles";a:2:{i:0;s:20:"custom_image_style_1";i:1;s:20:"custom_image_style_2";}}s:12:"multiplier_3";a:1:{s:12:"mapping_type";s:5:"_none";}}s:42:"breakpoints.theme.my_theme_id.computer_two";a:1:{s:12:"multiplier_2";a:3:{s:12:"mapping_type";s:5:"sizes";s:5:"sizes";i:2;s:18:"sizes_image_styles";a:2:{i:0;s:20:"custom_image_style_1";i:1;s:20:"custom_image_style_2";}}}}',
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'label' => 'Narrow',
'machine_name' => 'narrow',
'breakpoint_group' => 'responsive_image',
'mapping' => [
'breakpoints.theme.my_theme_id.computer' =>
[
'multiplier_1' =>
[
'mapping_type' => 'image_style',
'image_style' => 'custom_image_style_1',
],
'multiplier_2' =>
[
'mapping_type' => 'sizes',
'sizes' => 2,
'sizes_image_styles' =>
[
0 => 'custom_image_style_1',
1 => 'custom_image_style_2',
],
],
'multiplier_3' =>
[
'mapping_type' => '_none',
],
],
'breakpoints.theme.my_theme_id.computer_two' =>
[
'multiplier_2' =>
[
'mapping_type' => 'sizes',
'sizes' => 2,
'sizes_image_styles' =>
[
0 => 'custom_image_style_1',
1 => 'custom_image_style_2',
],
],
],
],
],
];
return $tests;
}
}

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Kernel;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\KernelTests\KernelTestBase;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
/**
* Tests the integration of responsive image with other components.
*
* @group responsive_image
*/
class ResponsiveImageIntegrationTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'responsive_image',
'field',
'image',
'file',
'entity_test',
'breakpoint',
'responsive_image_test_module',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('entity_test');
}
/**
* Tests integration with entity view display.
*/
public function testEntityViewDisplayDependency(): void {
// Create a responsive image style.
ResponsiveImageStyle::create([
'id' => 'foo',
'label' => 'Foo',
'breakpoint_group' => 'responsive_image_test_module',
])->save();
// Create an image field to be used with a responsive image formatter.
FieldStorageConfig::create([
'type' => 'image',
'entity_type' => 'entity_test',
'field_name' => 'bar',
])->save();
FieldConfig::create([
'entity_type' => 'entity_test',
'bundle' => 'entity_test',
'field_name' => 'bar',
])->save();
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
$display = EntityViewDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'default',
]);
$display->setComponent('bar', [
'type' => 'responsive_image',
'label' => 'hidden',
'settings' => ['responsive_image_style' => 'foo', 'image_link' => ''],
'third_party_settings' => [],
])->save();
// Check that the 'foo' field is on the display.
$this->assertNotNull($display = EntityViewDisplay::load('entity_test.entity_test.default'));
$this->assertNotEmpty($display->getComponent('bar'));
$this->assertArrayNotHasKey('bar', $display->get('hidden'));
// Delete the responsive image style.
ResponsiveImageStyle::load('foo')->delete();
// Check that the view display was not deleted.
$this->assertNotNull($display = EntityViewDisplay::load('entity_test.entity_test.default'));
// Check that the 'foo' field was disabled.
$this->assertNull($display->getComponent('bar'));
$this->assertArrayHasKey('bar', $display->get('hidden'));
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Kernel;
use Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
/**
* Tests validation of responsive_image_style entities.
*
* @group responsive_image
*/
class ResponsiveImageStyleValidationTest extends ConfigEntityValidationTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['breakpoint', 'image', 'responsive_image'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->entity = ResponsiveImageStyle::create([
'id' => 'test',
'label' => 'Test',
]);
$this->entity->save();
}
}

View File

@@ -0,0 +1,404 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\responsive_image\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityTypeRepositoryInterface;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\responsive_image\Entity\ResponsiveImageStyle
* @group block
*/
class ResponsiveImageStyleConfigEntityUnitTest extends UnitTestCase {
/**
* The entity type used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $entityType;
/**
* The entity type manager used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $entityTypeManager;
/**
* The breakpoint manager used for testing.
*
* @var \Drupal\breakpoint\BreakpointManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $breakpointManager;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->entityType = $this->createMock('\Drupal\Core\Entity\EntityTypeInterface');
$this->entityType->expects($this->any())
->method('getProvider')
->willReturn('responsive_image');
$this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
$this->entityTypeManager->expects($this->any())
->method('getDefinition')
->with('responsive_image_style')
->willReturn($this->entityType);
$this->breakpointManager = $this->createMock('\Drupal\breakpoint\BreakpointManagerInterface');
$container = new ContainerBuilder();
$container->set('entity_type.manager', $this->entityTypeManager);
$container->set('breakpoint.manager', $this->breakpointManager);
\Drupal::setContainer($container);
}
/**
* @covers ::calculateDependencies
*/
public function testCalculateDependencies(): void {
// Set up image style loading mock.
$styles = [];
foreach (['fallback', 'small', 'medium', 'large'] as $style) {
$mock = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityInterface');
$mock->expects($this->any())
->method('getConfigDependencyName')
->willReturn('image.style.' . $style);
$styles[$style] = $mock;
}
$storage = $this->createMock('\Drupal\Core\Config\Entity\ConfigEntityStorageInterface');
$storage->expects($this->any())
->method('loadMultiple')
->with(array_keys($styles))
->willReturn($styles);
$this->entityTypeManager->expects($this->any())
->method('getStorage')
->with('image_style')
->willReturn($storage);
$entity_type_repository = $this->createMock(EntityTypeRepositoryInterface::class);
$entity_type_repository->expects($this->any())
->method('getEntityTypeFromClass')
->with('Drupal\image\Entity\ImageStyle')
->willReturn('image_style');
$entity = new ResponsiveImageStyle(['breakpoint_group' => 'test_group']);
$entity->setBreakpointGroup('test_group');
$entity->setFallbackImageStyle('fallback');
$entity->addImageStyleMapping('test_breakpoint', '1x', ['image_mapping_type' => 'image_style', 'image_mapping' => 'small']);
$entity->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'medium' => 'medium',
'large' => 'large',
],
],
]);
$this->breakpointManager->expects($this->any())
->method('getGroupProviders')
->with('test_group')
->willReturn(['olivero' => 'theme', 'toolbar' => 'module']);
\Drupal::getContainer()->set('entity_type.repository', $entity_type_repository);
$dependencies = $entity->calculateDependencies()->getDependencies();
$this->assertEquals(['toolbar'], $dependencies['module']);
$this->assertEquals(['olivero'], $dependencies['theme']);
$this->assertEquals(['image.style.fallback', 'image.style.large', 'image.style.medium', 'image.style.small'], $dependencies['config']);
}
/**
* @covers ::addImageStyleMapping
* @covers ::hasImageStyleMappings
*/
public function testHasImageStyleMappings(): void {
$entity = new ResponsiveImageStyle([]);
$this->assertFalse($entity->hasImageStyleMappings());
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => '',
]);
$this->assertFalse($entity->hasImageStyleMappings());
$entity->removeImageStyleMappings();
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [],
],
]);
$this->assertFalse($entity->hasImageStyleMappings());
$entity->removeImageStyleMappings();
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '',
'sizes_image_styles' => [
'large' => 'large',
],
],
]);
$this->assertFalse($entity->hasImageStyleMappings());
$entity->removeImageStyleMappings();
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$this->assertTrue($entity->hasImageStyleMappings());
$entity->removeImageStyleMappings();
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
]);
$this->assertTrue($entity->hasImageStyleMappings());
}
/**
* @covers ::addImageStyleMapping
* @covers ::getImageStyleMapping
*/
public function testGetImageStyleMapping(): void {
$entity = new ResponsiveImageStyle(['']);
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$expected = [
'breakpoint_id' => 'test_breakpoint',
'multiplier' => '1x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
];
$this->assertEquals($expected, $entity->getImageStyleMapping('test_breakpoint', '1x'));
$this->assertNull($entity->getImageStyleMapping('test_unknown_breakpoint', '1x'));
}
/**
* @covers ::addImageStyleMapping
* @covers ::getKeyedImageStyleMappings
*/
public function testGetKeyedImageStyleMappings(): void {
$entity = new ResponsiveImageStyle(['']);
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$entity->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
]);
$entity->addImageStyleMapping('test_breakpoint2', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
]);
$entity->addImageStyleMapping('test_breakpoint2', '2x', [
'image_mapping_type' => 'image_style',
'image_mapping' => '_original image_',
]);
$expected = [
'test_breakpoint' => [
'1x' => [
'breakpoint_id' => 'test_breakpoint',
'multiplier' => '1x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
],
'2x' => [
'breakpoint_id' => 'test_breakpoint',
'multiplier' => '2x',
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
],
],
'test_breakpoint2' => [
'1x' => [
'breakpoint_id' => 'test_breakpoint2',
'multiplier' => '1x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
],
'2x' => [
'breakpoint_id' => 'test_breakpoint2',
'multiplier' => '2x',
'image_mapping_type' => 'image_style',
'image_mapping' => '_original image_',
],
],
];
$this->assertEquals($expected, $entity->getKeyedImageStyleMappings());
// Add another mapping to ensure keyed mapping static cache is rebuilt.
$entity->addImageStyleMapping('test_breakpoint2', '2x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'medium',
]);
$expected['test_breakpoint2']['2x'] = [
'breakpoint_id' => 'test_breakpoint2',
'multiplier' => '2x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'medium',
];
$this->assertEquals($expected, $entity->getKeyedImageStyleMappings());
// Overwrite a mapping to ensure keyed mapping static cache is rebuilt.
$entity->addImageStyleMapping('test_breakpoint2', '2x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$expected['test_breakpoint2']['2x'] = [
'breakpoint_id' => 'test_breakpoint2',
'multiplier' => '2x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
];
$this->assertEquals($expected, $entity->getKeyedImageStyleMappings());
}
/**
* @covers ::addImageStyleMapping
* @covers ::getImageStyleMappings
*/
public function testGetImageStyleMappings(): void {
$entity = new ResponsiveImageStyle(['']);
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$entity->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
]);
$entity->addImageStyleMapping('test_breakpoint2', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
]);
$expected = [
[
'breakpoint_id' => 'test_breakpoint',
'multiplier' => '1x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
],
[
'breakpoint_id' => 'test_breakpoint',
'multiplier' => '2x',
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
],
[
'breakpoint_id' => 'test_breakpoint2',
'multiplier' => '1x',
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
],
];
$this->assertEquals($expected, $entity->getImageStyleMappings());
}
/**
* @covers ::addImageStyleMapping
* @covers ::removeImageStyleMappings
*/
public function testRemoveImageStyleMappings(): void {
$entity = new ResponsiveImageStyle(['']);
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$entity->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
]);
$entity->addImageStyleMapping('test_breakpoint2', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
]);
$this->assertTrue($entity->hasImageStyleMappings());
$entity->removeImageStyleMappings();
$this->assertEmpty($entity->getImageStyleMappings());
$this->assertEmpty($entity->getKeyedImageStyleMappings());
$this->assertFalse($entity->hasImageStyleMappings());
}
/**
* @covers ::setBreakpointGroup
* @covers ::getBreakpointGroup
*/
public function testSetBreakpointGroup(): void {
$entity = new ResponsiveImageStyle(['breakpoint_group' => 'test_group']);
$entity->addImageStyleMapping('test_breakpoint', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'large',
]);
$entity->addImageStyleMapping('test_breakpoint', '2x', [
'image_mapping_type' => 'sizes',
'image_mapping' => [
'sizes' => '(min-width:700px) 700px, 100vw',
'sizes_image_styles' => [
'large' => 'large',
],
],
]);
$entity->addImageStyleMapping('test_breakpoint2', '1x', [
'image_mapping_type' => 'image_style',
'image_mapping' => 'thumbnail',
]);
// Ensure that setting to same group does not remove mappings.
$entity->setBreakpointGroup('test_group');
$this->assertTrue($entity->hasImageStyleMappings());
$this->assertEquals('test_group', $entity->getBreakpointGroup());
// Ensure that changing the group removes mappings.
$entity->setBreakpointGroup('test_group2');
$this->assertEquals('test_group2', $entity->getBreakpointGroup());
$this->assertFalse($entity->hasImageStyleMappings());
}
}