annotate core/tests/Drupal/Tests/Component/Utility/HtmlTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\Component\Utility;
Chris@0 4
Chris@0 5 use Drupal\Component\Render\MarkupInterface;
Chris@0 6 use Drupal\Component\Render\MarkupTrait;
Chris@0 7 use Drupal\Component\Utility\Html;
Chris@0 8 use Drupal\Component\Utility\Random;
Chris@0 9 use PHPUnit\Framework\TestCase;
Chris@0 10
Chris@0 11 /**
Chris@0 12 * Tests \Drupal\Component\Utility\Html.
Chris@0 13 *
Chris@0 14 * @group Common
Chris@0 15 *
Chris@0 16 * @coversDefaultClass \Drupal\Component\Utility\Html
Chris@0 17 */
Chris@0 18 class HtmlTest extends TestCase {
Chris@0 19
Chris@0 20 /**
Chris@0 21 * {@inheritdoc}
Chris@0 22 */
Chris@0 23 protected function setUp() {
Chris@0 24 parent::setUp();
Chris@0 25
Chris@0 26 $property = new \ReflectionProperty('Drupal\Component\Utility\Html', 'seenIdsInit');
Chris@0 27 $property->setAccessible(TRUE);
Chris@0 28 $property->setValue(NULL);
Chris@0 29 }
Chris@0 30
Chris@0 31 /**
Chris@0 32 * Tests the Html::cleanCssIdentifier() method.
Chris@0 33 *
Chris@0 34 * @param string $expected
Chris@0 35 * The expected result.
Chris@0 36 * @param string $source
Chris@0 37 * The string being transformed to an ID.
Chris@0 38 * @param array|null $filter
Chris@0 39 * (optional) An array of string replacements to use on the identifier. If
Chris@0 40 * NULL, no filter will be passed and a default will be used.
Chris@0 41 *
Chris@0 42 * @dataProvider providerTestCleanCssIdentifier
Chris@0 43 *
Chris@0 44 * @covers ::cleanCssIdentifier
Chris@0 45 */
Chris@0 46 public function testCleanCssIdentifier($expected, $source, $filter = NULL) {
Chris@0 47 if ($filter !== NULL) {
Chris@0 48 $this->assertSame($expected, Html::cleanCssIdentifier($source, $filter));
Chris@0 49 }
Chris@0 50 else {
Chris@0 51 $this->assertSame($expected, Html::cleanCssIdentifier($source));
Chris@0 52 }
Chris@0 53 }
Chris@0 54
Chris@0 55 /**
Chris@0 56 * Provides test data for testCleanCssIdentifier().
Chris@0 57 *
Chris@0 58 * @return array
Chris@0 59 * Test data.
Chris@0 60 */
Chris@0 61 public function providerTestCleanCssIdentifier() {
Chris@0 62 $id1 = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
Chris@0 63 $id2 = '¡¢£¤¥';
Chris@0 64 $id3 = 'css__identifier__with__double__underscores';
Chris@0 65 return [
Chris@0 66 // Verify that no valid ASCII characters are stripped from the identifier.
Chris@0 67 [$id1, $id1, []],
Chris@0 68 // Verify that valid UTF-8 characters are not stripped from the identifier.
Chris@0 69 [$id2, $id2, []],
Chris@12 70 // Verify that double underscores are not stripped from the identifier.
Chris@0 71 [$id3, $id3],
Chris@12 72 // Verify that invalid characters (including non-breaking space) are
Chris@12 73 // stripped from the identifier.
Chris@0 74 ['invalididentifier', 'invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', []],
Chris@0 75 // Verify that an identifier starting with a digit is replaced.
Chris@0 76 ['_cssidentifier', '1cssidentifier', []],
Chris@0 77 // Verify that an identifier starting with a hyphen followed by a digit is
Chris@0 78 // replaced.
Chris@0 79 ['__cssidentifier', '-1cssidentifier', []],
Chris@0 80 // Verify that an identifier starting with two hyphens is replaced.
Chris@0 81 ['__cssidentifier', '--cssidentifier', []],
Chris@0 82 // Verify that passing double underscores as a filter is processed.
Chris@0 83 ['_cssidentifier', '__cssidentifier', ['__' => '_']],
Chris@0 84 ];
Chris@0 85 }
Chris@0 86
Chris@0 87 /**
Chris@0 88 * Tests that Html::getClass() cleans the class name properly.
Chris@0 89 *
Chris@0 90 * @coversDefaultClass ::getClass
Chris@0 91 */
Chris@0 92 public function testHtmlClass() {
Chris@0 93 // Verify Drupal coding standards are enforced.
Chris@0 94 $this->assertSame('class-name--ü', Html::getClass('CLASS NAME_[Ü]'), 'Enforce Drupal coding standards.');
Chris@0 95
Chris@0 96 // Test Html::getClass() handles Drupal\Component\Render\MarkupInterface
Chris@0 97 // input.
Chris@0 98 $markup = HtmlTestMarkup::create('CLASS_FROM_OBJECT');
Chris@0 99 $this->assertSame('class-from-object', Html::getClass($markup), 'Markup object is converted to CSS class.');
Chris@0 100 }
Chris@0 101
Chris@0 102 /**
Chris@0 103 * Tests the Html::getUniqueId() method.
Chris@0 104 *
Chris@0 105 * @param string $expected
Chris@0 106 * The expected result.
Chris@0 107 * @param string $source
Chris@0 108 * The string being transformed to an ID.
Chris@0 109 * @param bool $reset
Chris@0 110 * (optional) If TRUE, reset the list of seen IDs. Defaults to FALSE.
Chris@0 111 *
Chris@0 112 * @dataProvider providerTestHtmlGetUniqueId
Chris@0 113 *
Chris@0 114 * @covers ::getUniqueId
Chris@0 115 */
Chris@0 116 public function testHtmlGetUniqueId($expected, $source, $reset = FALSE) {
Chris@0 117 if ($reset) {
Chris@0 118 Html::resetSeenIds();
Chris@0 119 }
Chris@0 120 $this->assertSame($expected, Html::getUniqueId($source));
Chris@0 121 }
Chris@0 122
Chris@0 123 /**
Chris@0 124 * Provides test data for testHtmlGetId().
Chris@0 125 *
Chris@0 126 * @return array
Chris@0 127 * Test data.
Chris@0 128 */
Chris@0 129 public function providerTestHtmlGetUniqueId() {
Chris@0 130 $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
Chris@0 131 return [
Chris@0 132 // Verify that letters, digits, and hyphens are not stripped from the ID.
Chris@0 133 [$id, $id],
Chris@0 134 // Verify that invalid characters are stripped from the ID.
Chris@0 135 ['invalididentifier', 'invalid,./:@\\^`{Üidentifier'],
Chris@0 136 // Verify Drupal coding standards are enforced.
Chris@0 137 ['id-name-1', 'ID NAME_[1]'],
Chris@0 138 // Verify that a repeated ID is made unique.
Chris@0 139 ['test-unique-id', 'test-unique-id', TRUE],
Chris@0 140 ['test-unique-id--2', 'test-unique-id'],
Chris@0 141 ['test-unique-id--3', 'test-unique-id'],
Chris@0 142 ];
Chris@0 143 }
Chris@0 144
Chris@0 145 /**
Chris@0 146 * Tests the Html::getUniqueId() method.
Chris@0 147 *
Chris@0 148 * @param string $expected
Chris@0 149 * The expected result.
Chris@0 150 * @param string $source
Chris@0 151 * The string being transformed to an ID.
Chris@0 152 *
Chris@0 153 * @dataProvider providerTestHtmlGetUniqueIdWithAjaxIds
Chris@0 154 *
Chris@0 155 * @covers ::getUniqueId
Chris@0 156 */
Chris@0 157 public function testHtmlGetUniqueIdWithAjaxIds($expected, $source) {
Chris@0 158 Html::setIsAjax(TRUE);
Chris@0 159 $id = Html::getUniqueId($source);
Chris@0 160
Chris@0 161 // Note, we truncate two hyphens at the end.
Chris@0 162 // @see \Drupal\Component\Utility\Html::getId()
Chris@0 163 if (strpos($source, '--') !== FALSE) {
Chris@0 164 $random_suffix = substr($id, strlen($source) + 1);
Chris@0 165 }
Chris@0 166 else {
Chris@0 167 $random_suffix = substr($id, strlen($source) + 2);
Chris@0 168 }
Chris@0 169 $expected = $expected . $random_suffix;
Chris@0 170 $this->assertSame($expected, $id);
Chris@0 171 }
Chris@0 172
Chris@0 173 /**
Chris@0 174 * Provides test data for testHtmlGetId().
Chris@0 175 *
Chris@0 176 * @return array
Chris@0 177 * Test data.
Chris@0 178 */
Chris@0 179 public function providerTestHtmlGetUniqueIdWithAjaxIds() {
Chris@0 180 return [
Chris@0 181 ['test-unique-id1--', 'test-unique-id1'],
Chris@0 182 // Note, we truncate two hyphens at the end.
Chris@0 183 // @see \Drupal\Component\Utility\Html::getId()
Chris@0 184 ['test-unique-id1---', 'test-unique-id1--'],
Chris@0 185 ['test-unique-id2--', 'test-unique-id2'],
Chris@0 186 ];
Chris@0 187 }
Chris@0 188
Chris@0 189 /**
Chris@0 190 * Tests the Html::getUniqueId() method.
Chris@0 191 *
Chris@0 192 * @param string $expected
Chris@0 193 * The expected result.
Chris@0 194 * @param string $source
Chris@0 195 * The string being transformed to an ID.
Chris@0 196 *
Chris@0 197 * @dataProvider providerTestHtmlGetId
Chris@0 198 *
Chris@0 199 * @covers ::getId
Chris@0 200 */
Chris@0 201 public function testHtmlGetId($expected, $source) {
Chris@0 202 Html::setIsAjax(FALSE);
Chris@0 203 $this->assertSame($expected, Html::getId($source));
Chris@0 204 }
Chris@0 205
Chris@0 206 /**
Chris@0 207 * Provides test data for testHtmlGetId().
Chris@0 208 *
Chris@0 209 * @return array
Chris@0 210 * Test data.
Chris@0 211 */
Chris@0 212 public function providerTestHtmlGetId() {
Chris@0 213 $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
Chris@0 214 return [
Chris@0 215 // Verify that letters, digits, and hyphens are not stripped from the ID.
Chris@0 216 [$id, $id],
Chris@0 217 // Verify that invalid characters are stripped from the ID.
Chris@0 218 ['invalididentifier', 'invalid,./:@\\^`{Üidentifier'],
Chris@0 219 // Verify Drupal coding standards are enforced.
Chris@0 220 ['id-name-1', 'ID NAME_[1]'],
Chris@0 221 // Verify that a repeated ID is made unique.
Chris@0 222 ['test-unique-id', 'test-unique-id'],
Chris@0 223 ['test-unique-id', 'test-unique-id'],
Chris@0 224 ];
Chris@0 225 }
Chris@0 226
Chris@0 227 /**
Chris@0 228 * Tests Html::decodeEntities().
Chris@0 229 *
Chris@0 230 * @dataProvider providerDecodeEntities
Chris@0 231 * @covers ::decodeEntities
Chris@0 232 */
Chris@0 233 public function testDecodeEntities($text, $expected) {
Chris@0 234 $this->assertEquals($expected, Html::decodeEntities($text));
Chris@0 235 }
Chris@0 236
Chris@0 237 /**
Chris@0 238 * Data provider for testDecodeEntities().
Chris@0 239 *
Chris@0 240 * @see testDecodeEntities()
Chris@0 241 */
Chris@0 242 public function providerDecodeEntities() {
Chris@0 243 return [
Chris@0 244 ['Drupal', 'Drupal'],
Chris@0 245 ['<script>', '<script>'],
Chris@0 246 ['&lt;script&gt;', '<script>'],
Chris@0 247 ['&#60;script&#62;', '<script>'],
Chris@0 248 ['&amp;lt;script&amp;gt;', '&lt;script&gt;'],
Chris@0 249 ['"', '"'],
Chris@0 250 ['&#34;', '"'],
Chris@0 251 ['&amp;#34;', '&#34;'],
Chris@0 252 ['&quot;', '"'],
Chris@0 253 ['&amp;quot;', '&quot;'],
Chris@0 254 ["'", "'"],
Chris@0 255 ['&#39;', "'"],
Chris@0 256 ['&amp;#39;', '&#39;'],
Chris@0 257 ['©', '©'],
Chris@0 258 ['&copy;', '©'],
Chris@0 259 ['&#169;', '©'],
Chris@0 260 ['→', '→'],
Chris@0 261 ['&#8594;', '→'],
Chris@0 262 ['➼', '➼'],
Chris@0 263 ['&#10172;', '➼'],
Chris@0 264 ['&euro;', '€'],
Chris@0 265 ];
Chris@0 266 }
Chris@0 267
Chris@0 268 /**
Chris@0 269 * Tests Html::escape().
Chris@0 270 *
Chris@0 271 * @dataProvider providerEscape
Chris@0 272 * @covers ::escape
Chris@0 273 */
Chris@0 274 public function testEscape($expected, $text) {
Chris@0 275 $this->assertEquals($expected, Html::escape($text));
Chris@0 276 }
Chris@0 277
Chris@0 278 /**
Chris@0 279 * Data provider for testEscape().
Chris@0 280 *
Chris@0 281 * @see testEscape()
Chris@0 282 */
Chris@0 283 public function providerEscape() {
Chris@0 284 return [
Chris@0 285 ['Drupal', 'Drupal'],
Chris@0 286 ['&lt;script&gt;', '<script>'],
Chris@0 287 ['&amp;lt;script&amp;gt;', '&lt;script&gt;'],
Chris@0 288 ['&amp;#34;', '&#34;'],
Chris@0 289 ['&quot;', '"'],
Chris@0 290 ['&amp;quot;', '&quot;'],
Chris@0 291 ['&#039;', "'"],
Chris@0 292 ['&amp;#039;', '&#039;'],
Chris@0 293 ['©', '©'],
Chris@0 294 ['→', '→'],
Chris@0 295 ['➼', '➼'],
Chris@0 296 ['€', '€'],
Chris@0 297 ['Drup�al', "Drup\x80al"],
Chris@0 298 ];
Chris@0 299 }
Chris@0 300
Chris@0 301 /**
Chris@0 302 * Tests relationship between escaping and decoding HTML entities.
Chris@0 303 *
Chris@0 304 * @covers ::decodeEntities
Chris@0 305 * @covers ::escape
Chris@0 306 */
Chris@0 307 public function testDecodeEntitiesAndEscape() {
Chris@0 308 $string = "<em>répét&eacute;</em>";
Chris@0 309 $escaped = Html::escape($string);
Chris@0 310 $this->assertSame('&lt;em&gt;répét&amp;eacute;&lt;/em&gt;', $escaped);
Chris@0 311 $decoded = Html::decodeEntities($escaped);
Chris@0 312 $this->assertSame('<em>répét&eacute;</em>', $decoded);
Chris@0 313 $decoded = Html::decodeEntities($decoded);
Chris@0 314 $this->assertSame('<em>répété</em>', $decoded);
Chris@0 315 $escaped = Html::escape($decoded);
Chris@0 316 $this->assertSame('&lt;em&gt;répété&lt;/em&gt;', $escaped);
Chris@0 317 }
Chris@0 318
Chris@0 319 /**
Chris@0 320 * Tests Html::serialize().
Chris@0 321 *
Chris@0 322 * Resolves an issue by where an empty DOMDocument object sent to serialization would
Chris@0 323 * cause errors in getElementsByTagName() in the serialization function.
Chris@0 324 *
Chris@0 325 * @covers ::serialize
Chris@0 326 */
Chris@0 327 public function testSerialize() {
Chris@0 328 $document = new \DOMDocument();
Chris@0 329 $result = Html::serialize($document);
Chris@0 330 $this->assertSame('', $result);
Chris@0 331 }
Chris@0 332
Chris@0 333 /**
Chris@0 334 * @covers ::transformRootRelativeUrlsToAbsolute
Chris@0 335 * @dataProvider providerTestTransformRootRelativeUrlsToAbsolute
Chris@0 336 */
Chris@0 337 public function testTransformRootRelativeUrlsToAbsolute($html, $scheme_and_host, $expected_html) {
Chris@0 338 $this->assertSame($expected_html ?: $html, Html::transformRootRelativeUrlsToAbsolute($html, $scheme_and_host));
Chris@0 339 }
Chris@0 340
Chris@0 341 /**
Chris@0 342 * @covers ::transformRootRelativeUrlsToAbsolute
Chris@0 343 * @dataProvider providerTestTransformRootRelativeUrlsToAbsoluteAssertion
Chris@0 344 */
Chris@0 345 public function testTransformRootRelativeUrlsToAbsoluteAssertion($scheme_and_host) {
Chris@14 346 if (method_exists($this, 'expectException')) {
Chris@14 347 $this->expectException(\AssertionError::class);
Chris@14 348 }
Chris@14 349 else {
Chris@14 350 $this->setExpectedException(\AssertionError::class);
Chris@14 351 }
Chris@0 352 Html::transformRootRelativeUrlsToAbsolute('', $scheme_and_host);
Chris@0 353 }
Chris@0 354
Chris@0 355 /**
Chris@0 356 * Provides test data for testTransformRootRelativeUrlsToAbsolute().
Chris@0 357 *
Chris@0 358 * @return array
Chris@0 359 * Test data.
Chris@0 360 */
Chris@0 361 public function providerTestTransformRootRelativeUrlsToAbsolute() {
Chris@0 362 $data = [];
Chris@0 363
Chris@0 364 // Random generator.
Chris@0 365 $random = new Random();
Chris@0 366
Chris@0 367 // One random tag name.
Chris@0 368 $tag_name = strtolower($random->name(8, TRUE));
Chris@0 369
Chris@0 370 // A site installed either in the root of a domain or a subdirectory.
Chris@0 371 $base_paths = ['/', '/subdir/' . $random->name(8, TRUE) . '/'];
Chris@0 372
Chris@0 373 foreach ($base_paths as $base_path) {
Chris@0 374 // The only attribute that has more than just a URL as its value, is
Chris@0 375 // 'srcset', so special-case it.
Chris@0 376 $data += [
Chris@0 377 "$tag_name, srcset, $base_path: root-relative" => ["<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, {$base_path}root-relative 300w\">root-relative test</$tag_name>", 'http://example.com', "<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, http://example.com{$base_path}root-relative 300w\">root-relative test</$tag_name>"],
Chris@0 378 "$tag_name, srcset, $base_path: protocol-relative" => ["<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, //example.com{$base_path}protocol-relative 300w\">protocol-relative test</$tag_name>", 'http://example.com', FALSE],
Chris@0 379 "$tag_name, srcset, $base_path: absolute" => ["<$tag_name srcset=\"http://example.com{$base_path}already-absolute 200w, http://example.com{$base_path}absolute 300w\">absolute test</$tag_name>", 'http://example.com', FALSE],
Chris@0 380 ];
Chris@0 381
Chris@0 382 foreach (['href', 'poster', 'src', 'cite', 'data', 'action', 'formaction', 'about'] as $attribute) {
Chris@0 383 $data += [
Chris@0 384 "$tag_name, $attribute, $base_path: root-relative" => ["<$tag_name $attribute=\"{$base_path}root-relative\">root-relative test</$tag_name>", 'http://example.com', "<$tag_name $attribute=\"http://example.com{$base_path}root-relative\">root-relative test</$tag_name>"],
Chris@0 385 "$tag_name, $attribute, $base_path: protocol-relative" => ["<$tag_name $attribute=\"//example.com{$base_path}protocol-relative\">protocol-relative test</$tag_name>", 'http://example.com', FALSE],
Chris@0 386 "$tag_name, $attribute, $base_path: absolute" => ["<$tag_name $attribute=\"http://example.com{$base_path}absolute\">absolute test</$tag_name>", 'http://example.com', FALSE],
Chris@0 387 ];
Chris@0 388 }
Chris@0 389 }
Chris@0 390
Chris@0 391 return $data;
Chris@0 392 }
Chris@0 393
Chris@0 394 /**
Chris@0 395 * Provides test data for testTransformRootRelativeUrlsToAbsoluteAssertion().
Chris@0 396 *
Chris@0 397 * @return array
Chris@0 398 * Test data.
Chris@0 399 */
Chris@0 400 public function providerTestTransformRootRelativeUrlsToAbsoluteAssertion() {
Chris@0 401 return [
Chris@0 402 'only relative path' => ['llama'],
Chris@0 403 'only root-relative path' => ['/llama'],
Chris@0 404 'host and path' => ['example.com/llama'],
Chris@0 405 'scheme, host and path' => ['http://example.com/llama'],
Chris@0 406 ];
Chris@0 407 }
Chris@0 408
Chris@0 409 }
Chris@0 410
Chris@0 411 /**
Chris@0 412 * Marks an object's __toString() method as returning markup.
Chris@0 413 */
Chris@0 414 class HtmlTestMarkup implements MarkupInterface {
Chris@0 415 use MarkupTrait;
Chris@0 416
Chris@0 417 }