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 ['<script>', '<script>'],
|
Chris@0
|
247 ['<script>', '<script>'],
|
Chris@0
|
248 ['&lt;script&gt;', '<script>'],
|
Chris@0
|
249 ['"', '"'],
|
Chris@0
|
250 ['"', '"'],
|
Chris@0
|
251 ['&#34;', '"'],
|
Chris@0
|
252 ['"', '"'],
|
Chris@0
|
253 ['&quot;', '"'],
|
Chris@0
|
254 ["'", "'"],
|
Chris@0
|
255 [''', "'"],
|
Chris@0
|
256 ['&#39;', '''],
|
Chris@0
|
257 ['©', '©'],
|
Chris@0
|
258 ['©', '©'],
|
Chris@0
|
259 ['©', '©'],
|
Chris@0
|
260 ['→', '→'],
|
Chris@0
|
261 ['→', '→'],
|
Chris@0
|
262 ['➼', '➼'],
|
Chris@0
|
263 ['➼', '➼'],
|
Chris@0
|
264 ['€', '€'],
|
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 ['<script>', '<script>'],
|
Chris@0
|
287 ['&lt;script&gt;', '<script>'],
|
Chris@0
|
288 ['&#34;', '"'],
|
Chris@0
|
289 ['"', '"'],
|
Chris@0
|
290 ['&quot;', '"'],
|
Chris@0
|
291 [''', "'"],
|
Chris@0
|
292 ['&#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é</em>";
|
Chris@0
|
309 $escaped = Html::escape($string);
|
Chris@0
|
310 $this->assertSame('<em>répét&eacute;</em>', $escaped);
|
Chris@0
|
311 $decoded = Html::decodeEntities($escaped);
|
Chris@0
|
312 $this->assertSame('<em>répété</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('<em>répété</em>', $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 }
|