ckeditor5-1.0.x-dev/tests/src/Kernel/ValidatorsTest.php
tests/src/Kernel/ValidatorsTest.php
<?php
declare(strict_types = 1);
namespace Drupal\Tests\ckeditor5\Kernel;
use Drupal\ckeditor5\Plugin\Editor\CKEditor5;
use Drupal\Component\Utility\Html;
use Drupal\editor\EditorInterface;
use Drupal\editor\Entity\Editor;
use Drupal\filter\Entity\FilterFormat;
use Drupal\filter\FilterFormatInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\SchemaCheckTestTrait;
/**
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\ToolbarItemConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\ToolbarItemDependencyConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\EnabledConfigurablePluginsConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Editor\CKEditor5::validatePair()
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\FundamentalCompatibilityConstraintValidator
* @group ckeditor5
*/
class ValidatorsTest extends KernelTestBase {
use SchemaCheckTestTrait;
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfig;
/**
* {@inheritdoc}
*/
protected static $modules = [
'ckeditor5',
'editor',
'filter',
'filter_test',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->typedConfig = $this->container->get('config.typed');
}
/**
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\ToolbarItemConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\ToolbarItemDependencyConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\EnabledConfigurablePluginsConstraintValidator
* @dataProvider provider
*
* @param array $ckeditor5_settings
* The CKEditor 5 settings to test.
* @param array $expected_violations
* All expected violations for the given CKEditor 5 settings, with property
* path as keys and message as values.
*/
public function test(array $ckeditor5_settings, array $expected_violations) {
FilterFormat::create([
'format' => 'dummy',
'name' => 'Dummy',
])->save();
$editor = Editor::create([
'format' => 'dummy',
'editor' => 'ckeditor5',
'settings' => $ckeditor5_settings,
'image_upload' => [],
]);
$typed_config = $this->typedConfig->createFromNameAndData(
$editor->getConfigDependencyName(),
$editor->toArray(),
);
$this->assertConfigSchema(
$this->typedConfig,
$editor->getConfigDependencyName(),
$typed_config->getValue()
);
$violations = $typed_config->validate();
$actual_violations = [];
foreach ($violations as $violation) {
$actual_violations[$violation->getPropertyPath()] = (string) $violation->getMessage();
}
$this->assertSame($expected_violations, $actual_violations);
}
/**
* Provides a list of Text Editor config entities using CKEditor 5 to test.
*/
public function provider() : array {
$data = [];
$data['non-existent toolbar button'] = [
'settings' => [
'toolbar' => [
'items' => [
'underline',
'bold',
'italic',
'-',
'bulletedList',
'foobar',
],
],
'plugins' => [],
],
'violations' => [
'settings.toolbar.items.5' => 'The provided toolbar item <em class="placeholder">foobar</em> is not valid.',
],
];
$data['missing heading plugin configuration'] = [
'settings' => [
'toolbar' => [
'items' => [
'heading',
],
],
'plugins' => [],
],
'violations' => [
'settings.plugins.ckeditor5heading' => 'Configuration for the enabled plugin "<em class="placeholder">Headings</em>" (<em class="placeholder">ckeditor5heading</em>) is missing.',
],
];
$data['missing language plugin configuration'] = [
'settings' => [
'toolbar' => [
'items' => [
'textPartLanguage',
],
],
'plugins' => [],
],
'violations' => [
'settings.plugins.language' => 'Configuration for the enabled plugin "<em class="placeholder">Language</em>" (<em class="placeholder">language</em>) is missing.',
],
];
$data['empty language plugin configuration'] = [
'settings' => [
'toolbar' => [
'items' => [
'textPartLanguage',
],
],
'plugins' => [
'language' => [],
],
],
'violations' => [
'settings.plugins.language' => 'Configuration for the enabled plugin "<em class="placeholder">Language</em>" (<em class="placeholder">language</em>) is missing.',
],
];
$data['valid language plugin configuration: un'] = [
'settings' => [
'toolbar' => [
'items' => [
'textPartLanguage',
],
],
'plugins' => [
'language' => [
'language_list' => 'un',
],
],
],
'violations' => [],
];
$data['valid language plugin configuration: all'] = [
'settings' => [
'toolbar' => [
'items' => [
'textPartLanguage',
],
],
'plugins' => [
'language' => [
'language_list' => 'all',
],
],
],
'violations' => [],
];
$data['invalid language plugin configuration: textPartLanguage button not enabled'] = [
'settings' => [
'toolbar' => [
'items' => [
'bold',
],
],
'plugins' => [
'language' => [
'language_list' => 'all',
],
],
],
'violations' => [
'settings.plugins.language.language_list' => 'Depends on <em class="placeholder">textPartLanguage</em>, which is not enabled.',
],
];
$data['invalid language plugin configuration: invalid language_list setting'] = [
'settings' => [
'toolbar' => [
'items' => [
'textPartLanguage',
],
],
'plugins' => [
'language' => [
'language_list' => 'foo',
],
],
],
'violations' => [
'settings.plugins.language.language_list' => 'The value you selected is not a valid choice.',
],
];
return $data;
}
/**
* @covers \Drupal\ckeditor5\Plugin\Editor\CKEditor5::validatePair()
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\FundamentalCompatibilityConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\ToolbarItemConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\ToolbarItemDependencyConstraintValidator
* @covers \Drupal\ckeditor5\Plugin\Validation\Constraint\EnabledConfigurablePluginsConstraintValidator
* @dataProvider providerPair
*
* @param array $ckeditor5_settings
* The paired text editor's CKEditor 5 settings to test.
* @param array $editor_image_upload_settings
* The paired text editor's image upload settings to test.
* @param array $filters
* The paired text format's filters and filter settings.
* @param array $expected_violations
* All expected violations for the pair.
*/
public function testPair(array $ckeditor5_settings, array $editor_image_upload_settings, array $filters, array $expected_violations) {
$text_editor = Editor::create([
'format' => 'dummy',
'editor' => 'ckeditor5',
'settings' => $ckeditor5_settings,
'image_upload' => $editor_image_upload_settings,
]);
assert($text_editor instanceof EditorInterface);
$this->assertConfigSchema(
$this->typedConfig,
$text_editor->getConfigDependencyName(),
$text_editor->toArray()
);
$text_format = FilterFormat::create([
'filters' => $filters,
]);
assert($text_format instanceof FilterFormatInterface);
$violations = CKEditor5::validatePair($text_editor, $text_format);
$actual_violations = [];
foreach ($violations as $violation) {
if (!isset($actual_violations[$violation->getPropertyPath()])) {
$actual_violations[$violation->getPropertyPath()] = (string) $violation->getMessage();
}
else {
// Transform value from string to array.
if (is_string($actual_violations[$violation->getPropertyPath()])) {
$actual_violations[$violation->getPropertyPath()] = (array) $actual_violations[$violation->getPropertyPath()];
}
// And append.
$actual_violations[$violation->getPropertyPath()][] = (string) $violation->getMessage();
}
}
$this->assertSame($expected_violations, $actual_violations);
}
/**
* Provides a list of Text Editor + Text Format pairs to test.
*/
public function providerPair() : array {
$data = [];
$data['INVALID: non-HTML format: filter_autop'] = [
'settings' => [
'toolbar' => [
'items' => [
'bold',
],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_autop' => [
'id' => 'filter_autop',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [],
],
],
'violations' => [
'' => 'CKEditor 5 only works with HTML-based text formats. The "<em class="placeholder">Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)</em>" (<em class="placeholder">filter_autop</em>) filter implies this text format is not HTML anymore.',
],
];
$data['INVALID: non-HTML format: filter_autop + filter_url'] = [
'settings' => [
'toolbar' => [
'items' => [
'bold',
],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_autop' => [
'id' => 'filter_autop',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [],
],
'filter_url' => [
'id' => 'filter_url',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
'filter_url_length' => 72,
],
],
],
'violations' => [
'' => [
'CKEditor 5 only works with HTML-based text formats. The "<em class="placeholder">Convert URLs into links</em>" (<em class="placeholder">filter_url</em>) filter implies this text format is not HTML anymore.',
'CKEditor 5 only works with HTML-based text formats. The "<em class="placeholder">Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)</em>" (<em class="placeholder">filter_autop</em>) filter implies this text format is not HTML anymore.',
],
],
];
$data['INVALID: forbidden tags'] = [
'settings' => [
'toolbar' => [
'items' => [],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_test_restrict_tags_and_attributes' => [
'id' => 'filter_test_restrict_tags_and_attributes',
'provider' => 'filter_test',
'status' => TRUE,
'weight' => 0,
'settings' => [
'restrictions' => [
'forbidden_tags' => ['p' => FALSE],
],
],
],
],
'violations' => [
'' => 'CKEditor 5 needs at least the <p> and <br> tags to be allowed to be able to function. They are forbidden by the "<em class="placeholder">Tag and attribute restricting filter</em>" (<em class="placeholder">filter_test_restrict_tags_and_attributes</em>) filter.',
],
];
// @todo read this directly from core/profiles/standard/config/install/filter.format.restricted_html.yml, but it seems that PHPUnit forbids us from doing that?
$restricted_html_format_filters = [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => -10,
'settings' => [
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
'filter_autop' => [
'id' => 'filter_autop',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [],
],
'filter_url' => [
'id' => 'filter_url',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
'filter_url_length' => 72,
],
],
];
$data['INVALID: the default restricted_html text format'] = [
'settings' => [
'toolbar' => [
'items' => [],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => $restricted_html_format_filters,
'violations' => [
'' => [
'CKEditor 5 only works with HTML-based text formats. The "<em class="placeholder">Convert URLs into links</em>" (<em class="placeholder">filter_url</em>) filter implies this text format is not HTML anymore.',
'CKEditor 5 only works with HTML-based text formats. The "<em class="placeholder">Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)</em>" (<em class="placeholder">filter_autop</em>) filter implies this text format is not HTML anymore.',
],
],
];
$data['INVALID: the modified restricted_html text format (with filter_autop and filter_url removed)'] = [
'settings' => [
'toolbar' => [
'items' => [],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => array_diff_key(
$restricted_html_format_filters,
['filter_autop' => TRUE, 'filter_url' => TRUE]
),
'violations' => [
'' => 'CKEditor 5 needs at least the <p> and <br> tags to be allowed to be able to function. They are not allowed by the "<em class="placeholder">Limit allowed HTML tags and correct faulty HTML</em>" (<em class="placeholder">filter_html</em>) filter.',
],
];
$data['VALID: HTML format: empty toolbar + minimal allowed HTML'] = [
'settings' => [
'toolbar' => [
'items' => [],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
'allowed_html' => "<p> <br>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
],
'violations' => [],
];
$data['VALID: HTML format: very minimal toolbar + minimal allowed HTML'] = [
'settings' => [
'toolbar' => [
'items' => [
'bold',
],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
'allowed_html' => "<p> <br> <strong>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
],
'violations' => [],
];
$data['INVALID: HTML format: empty toolbar + default allowed HTML tags + <p> + <br>'] = [
'settings' => [
'toolbar' => [
'items' => [],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>" . "<p> <br>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
],
'violations' => [
'filters.filter_html' => sprintf(
'The current CKEditor 5 build requires the following elements and attributes: <br><code>%s</code><br>The following elements are not supported: <br><code>%s</code>',
Html::escape('<p> <br>'),
Html::escape('<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type="1 A I"> <li> <dl> <dt> <dd> <h2 id="jump-*"> <h3 id> <h4 id> <h5 id> <h6 id>'),
),
],
];
$data['INVALID: HTML format: empty toolbar + default allowed HTML tags'] = [
'settings' => [
'toolbar' => [
'items' => [],
],
'plugins' => [],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
],
'violations' => [
'' => 'CKEditor 5 needs at least the <p> and <br> tags to be allowed to be able to function. They are not allowed by the "<em class="placeholder">Limit allowed HTML tags and correct faulty HTML</em>" (<em class="placeholder">filter_html</em>) filter.',
],
];
$data['VALID: HTML format: toolbar to match the filter_html default allowed HTML tags as closely as possible'] = [
'settings' => [
'toolbar' => [
'items' => [
'heading',
'bold',
'italic',
'code',
'link',
'bulletedList',
'numberedList',
'blockQuote',
],
],
'plugins' => [
'ckeditor5heading' => [
'enabled_headings' => [
'heading3',
'heading4',
'heading5',
],
],
],
],
'image_upload' => [
'status' => FALSE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
// @codingStandardsIgnoreLine
// Omitted from the filter_html default: <cite> <ul type> <ol start type='1 A I'> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>
// Added for CKEditor: <p> <br> <h1>.
'allowed_html' => "<a href hreflang> <strong> <em> <blockquote cite> <code> <ul> <ol> <li> <h2> <h3> <h4> <h5> <h6>" . "<p> <br> <h1>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
],
'violations' => [],
];
// @todo read this directly from core/profiles/standard/config/install/filter.format.basic_html.yml, but it seems that PHPUnit forbids us from doing that?
$basic_html_format_filters = [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => -10,
'settings' => [
'allowed_html' => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <br> <span> <img src alt height width data-entity-type data-entity-uuid data-align data-caption>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
'filter_align' => [
'id' => 'filter_align',
'provider' => 'filter',
'status' => TRUE,
'weight' => 7,
'settings' => [],
],
'filter_caption' => [
'id' => 'filter_caption',
'provider' => 'filter',
'status' => TRUE,
'weight' => 8,
'settings' => [],
],
'filter_html_image_secure' => [
'id' => 'filter_html_image_secure',
'provider' => 'filter',
'status' => TRUE,
'weight' => 9,
'settings' => [],
],
'editor_file_reference' => [
'id' => 'editor_file_reference',
'provider' => 'filter',
'status' => TRUE,
'weight' => 11,
'settings' => [],
],
];
$data['VALID: HTML format: toolbar to match the basic_html text format as closely as possible'] = [
'settings' => [
'toolbar' => [
'items' => [
'heading',
'bold',
'italic',
'code',
'link',
'bulletedList',
'numberedList',
'blockQuote',
'textPartLanguage',
'uploadImage',
],
],
'plugins' => [
'ckeditor5heading' => [
'enabled_headings' => [
'heading3',
'heading4',
'heading5',
],
],
'language' => [
'language_list' => 'un',
],
],
],
'image_upload' => [
'status' => TRUE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
// @codingStandardsIgnoreLine
// Omitted from the filter_html default: <cite> <ul type> <ol start type> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>
// Added for CKEditor: <h1> <h5> <h6> <span lang dir>
'allowed_html' => "<a href hreflang> <strong> <em> <blockquote cite> <code> <ul> <ol> <li> <h2> <h3> <h4> <p> <br>" . "<h1> <h5> <h6> <span lang dir> <img src alt data-entity-type data-entity-uuid width height data-caption data-align>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
],
],
] + $basic_html_format_filters,
'violations' => [],
];
$data['VALID: HTML format: toolbar to match the basic_html text format as closely as possible (with filter_caption removed => disallows <img data-caption>)'] = [
'settings' => [
'toolbar' => [
'items' => [
'heading',
'bold',
'italic',
'code',
'link',
'bulletedList',
'numberedList',
'blockQuote',
'textPartLanguage',
'uploadImage',
],
],
'plugins' => [
'ckeditor5heading' => [
'enabled_headings' => [
'heading3',
'heading4',
'heading5',
],
],
'language' => [
'language_list' => 'un',
],
],
],
'image_upload' => [
'status' => TRUE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
// Omitted from the filter_html default: <cite> <ul type> <ol start type> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>
// Added for CKEditor: <h1> <h5> <h6> <span lang dir>
'allowed_html' => "<a href hreflang> <strong> <em> <blockquote cite> <code> <ul> <ol> <li> <h2> <h3> <h4> <p> <br>" . "<h1> <h5> <h6> <span lang dir> <img src alt data-entity-type width height data-entity-uuid data-align>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
] + $basic_html_format_filters,
],
] + array_diff_key($basic_html_format_filters, ['filter_caption' => TRUE]),
'violations' => [],
];
$data['VALID: HTML format: toolbar to match the basic_html text format as closely as possible (with filter_align removed => disallows <img data-align>)'] = [
'settings' => [
'toolbar' => [
'items' => [
'heading',
'bold',
'italic',
'code',
'link',
'bulletedList',
'numberedList',
'blockQuote',
'textPartLanguage',
'uploadImage',
],
],
'plugins' => [
'ckeditor5heading' => [
'enabled_headings' => [
'heading3',
'heading4',
'heading5',
],
],
'language' => [
'language_list' => 'un',
],
],
],
'image_upload' => [
'status' => TRUE,
],
'filters' => [
'filter_html' => [
'id' => 'filter_html',
'provider' => 'filter',
'status' => TRUE,
'weight' => 0,
'settings' => [
// Omitted from the filter_html default: <cite> <ul type> <ol start type> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>
// Added for CKEditor: <h1> <h5> <h6> <span lang dir>
'allowed_html' => "<a href hreflang> <strong> <em> <blockquote cite> <code> <ul> <ol> <li> <h2> <h3> <h4> <p> <br>" . "<h1> <h5> <h6> <span lang dir> <img src alt data-entity-type data-entity-uuid width height data-caption>",
'filter_html_help' => TRUE,
'filter_html_nofollow' => TRUE,
] + $basic_html_format_filters,
],
] + array_diff_key($basic_html_format_filters, ['filter_align' => TRUE]),
'violations' => [],
];
return $data;
}
}
