comparison core/tests/Drupal/Tests/Component/Utility/HtmlTest.php @ 0:4c8ae668cc8c

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