CKEditor5UpdateImageToolbarItemTest.php

Same filename in other branches
  1. 9 core/modules/ckeditor5/tests/src/Functional/Update/CKEditor5UpdateImageToolbarItemTest.php
  2. 11.x core/modules/ckeditor5/tests/src/Functional/Update/CKEditor5UpdateImageToolbarItemTest.php

Namespace

Drupal\Tests\ckeditor5\Functional\Update

File

core/modules/ckeditor5/tests/src/Functional/Update/CKEditor5UpdateImageToolbarItemTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\ckeditor5\Functional\Update;

use Drupal\ckeditor5\HTMLRestrictions;
use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
use Drupal\editor\EditorInterface;
use Drupal\editor\Entity\Editor;
use Drupal\filter\Entity\FilterFormat;
use Drupal\filter\FilterFormatInterface;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Symfony\Component\Validator\ConstraintViolation;

/**
 * Tests the update path for the CKEditor 5 image toolbar item.
 *
 * @group Update
 * @group #slow
 */
class CKEditor5UpdateImageToolbarItemTest extends UpdatePathTestBase {
    use CKEditor5TestTrait;
    
    /**
     * {@inheritdoc}
     */
    protected $defaultTheme = 'stark';
    
    /**
     * {@inheritdoc}
     */
    protected function setDatabaseDumpFiles() {
        $this->databaseDumpFiles = [
            __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
            __DIR__ . '/../../../fixtures/update/ckeditor5-3222756.php',
        ];
    }
    
    /**
     * Tests that `uploadImage` toolbar item is updated to `drupalInsertImage`.
     *
     * @dataProvider provider
     */
    public function test(bool $filter_html_is_enabled, bool $image_uploads_are_enabled, bool $source_editing_is_already_enabled, array $expected_source_editing_additions) : void {
        // Apply tweaks for the currently provided test case.
        $format = FilterFormat::load('test_format_image');
        if (!$filter_html_is_enabled) {
            $format->setFilterConfig('filter_html', [
                'status' => FALSE,
            ]);
        }
        $editor = Editor::load('test_format_image');
        if (!$image_uploads_are_enabled) {
            $editor->setImageUploadSettings([
                'status' => FALSE,
            ]);
        }
        if (!$source_editing_is_already_enabled) {
            $settings = $editor->getSettings();
            // Remove the `sourceEditing` toolbar item.
            unset($settings['toolbar']['items'][3]);
            // Remove the corresponding plugin settings (allowing `<img data-foo>`).
            unset($settings['plugins']['ckeditor5_sourceEditing']);
            $editor->setSettings($settings);
            if ($filter_html_is_enabled) {
                // Stop allowing `<img data-foo>`.
                $filter_html_config = $format->filters('filter_html')
                    ->getConfiguration();
                $filter_html_config['settings']['allowed_html'] = str_replace('data-foo', '', $filter_html_config['settings']['allowed_html']);
                $format->setFilterConfig('filter_html', $filter_html_config);
            }
        }
        $format->trustData()
            ->save();
        $editor->trustData()
            ->save();
        // Run update path; snapshot the Text Format and Editor before and after.
        $editor_before = Editor::load('test_format_image');
        $filter_format_before = $editor->getFilterFormat();
        $this->runUpdates();
        $editor_after = Editor::load('test_format_image');
        $filter_format_after = $editor->getFilterFormat();
        // 1. Toolbar item: `uploadImage` -> `drupalInsertImage`, position must be
        // unchanged.
        $this->assertContains('uploadImage', $editor_before->getSettings()['toolbar']['items']);
        $this->assertNotContains('drupalInsertImage', $editor_before->getSettings()['toolbar']['items']);
        $this->assertNotContains('uploadImage', $editor_after->getSettings()['toolbar']['items']);
        $this->assertContains('drupalInsertImage', $editor_after->getSettings()['toolbar']['items']);
        $this->assertSame(array_search('uploadImage', $editor_before->getSettings()['toolbar']['items']), array_search('drupalInsertImage', $editor_after->getSettings()['toolbar']['items']));
        // 2. Even though `sourceEditing` may not be enabled before this update, it
        // must be after, at least if image uploads are disabled: extra mark-up will
        // be added to its configuration to avoid breaking backwards compatibility.
        if (!$image_uploads_are_enabled) {
            if (!$source_editing_is_already_enabled) {
                $this->assertNotContains('sourceEditing', $editor_before->getSettings()['toolbar']['items']);
            }
            $this->assertContains('sourceEditing', $editor_after->getSettings()['toolbar']['items']);
            $source_editing_before = $source_editing_is_already_enabled ? static::getSourceEditingRestrictions($editor_before) : HTMLRestrictions::emptySet();
            $source_editing_after = static::getSourceEditingRestrictions($editor_after);
            if ($source_editing_is_already_enabled) {
                // Nothing has been removed from the allowed source editing tags.
                $this->assertFalse($source_editing_before->allowsNothing());
                $this->assertTrue($source_editing_before->diff($source_editing_after)
                    ->allowsNothing());
            }
            $this->assertSame($expected_source_editing_additions, $source_editing_after->diff($source_editing_before)
                ->toCKEditor5ElementsArray());
        }
        else {
            if (!$source_editing_is_already_enabled) {
                $this->assertNotContains('sourceEditing', $editor_before->getSettings()['toolbar']['items']);
            }
            else {
                $this->assertContains('sourceEditing', $editor_before->getSettings()['toolbar']['items']);
                $this->assertSame(static::getSourceEditingRestrictions($editor_before)->toCKEditor5ElementsArray(), static::getSourceEditingRestrictions($editor_after)->toCKEditor5ElementsArray());
            }
        }
        // 3. `filter_html` restrictions MUST remain unchanged.
        if ($filter_html_is_enabled) {
            $filter_html_before = static::getFilterHtmlRestrictions($filter_format_before);
            $filter_html_after = static::getFilterHtmlRestrictions($filter_format_after);
            $this->assertTrue($filter_html_before->diff($filter_html_after)
                ->allowsNothing());
            $this->assertTrue($filter_html_after->diff($filter_html_before)
                ->allowsNothing());
        }
        // 4. After: text format and editor still form a valid pair.
        $this->assertSame([], array_map(function (ConstraintViolation $v) {
            return (string) $v->getMessage();
        }, array_filter(iterator_to_array(CKEditor5::validatePair($editor_after, $filter_format_after)), fn(ConstraintViolation $v) => $v->getMessage() != 'The file storage you selected is not a visible, readable and writable stream wrapper. Possible choices: <em class="placeholder"></em>.')));
    }
    
    /**
     * Data provider for ::test().
     *
     * @return array
     *   The test cases.
     */
    public static function provider() : array {
        // There are 3 aspects that need to be verified, each can be true or false,
        // making for 8 test cases in total.
        $test_cases = [];
        foreach ([
            TRUE,
            FALSE,
        ] as $filter_html_is_enabled) {
            $test_case_label_part_one = sprintf("filter_html %s", $filter_html_is_enabled ? 'enabled' : 'disabled');
            foreach ([
                TRUE,
                FALSE,
            ] as $image_uploads_enabled) {
                $test_case_label_part_two = sprintf("image uploads %s", $image_uploads_enabled ? 'enabled' : 'disabled');
                foreach ([
                    TRUE,
                    FALSE,
                ] as $source_editing_already_enabled) {
                    $test_case_label_part_three = sprintf("sourceEditing initially %s", $source_editing_already_enabled ? 'enabled' : 'disabled');
                    // Generate the test case.
                    $label = implode(', ', [
                        $test_case_label_part_one,
                        $test_case_label_part_two,
                        $test_case_label_part_three,
                    ]);
                    $test_cases[$label] = [
                        'filter_html' => $filter_html_is_enabled,
                        'image uploads' => $image_uploads_enabled,
                        'sourceEditing already enabled' => $source_editing_already_enabled,
                        'expected sourceEditing additions' => $image_uploads_enabled ? [] : [
                            '<img data-entity-uuid data-entity-type>',
                        ],
                    ];
                }
            }
        }
        return $test_cases;
    }
    
    /**
     * Gets the configured HTML restrictions for the Source Editing plugin.
     *
     * @param \Drupal\editor\EditorInterface $editor
     *   Text editor configured to use CKEditor 5, with Source Editing enabled.
     *
     * @return \Drupal\ckeditor5\HTMLRestrictions
     *   The configured HTML restrictions.
     */
    private static function getSourceEditingRestrictions(EditorInterface $editor) : HTMLRestrictions {
        $settings = $editor->getSettings();
        $source_editing_allowed_tags = $settings['plugins']['ckeditor5_sourceEditing']['allowed_tags'];
        return HTMLRestrictions::fromString(implode(' ', $source_editing_allowed_tags));
    }
    
    /**
     * Gets the configured restrictions for the `filter_html` filter plugin.
     *
     * @param \Drupal\filter\FilterFormatInterface $format
     *   Text format configured to use `filter_html`.
     *
     * @return \Drupal\ckeditor5\HTMLRestrictions
     *   The configured HTML restrictions.
     */
    private static function getFilterHtmlRestrictions(FilterFormatInterface $format) : HTMLRestrictions {
        $allowed_html = $format->filters('filter_html')
            ->getConfiguration()['settings']['allowed_html'];
        return HTMLRestrictions::fromString($allowed_html);
    }

}

Classes

Title Deprecated Summary
CKEditor5UpdateImageToolbarItemTest Tests the update path for the CKEditor 5 image toolbar item.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.