AttributesHelperTest.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. /*
  3. * This file is part of the league/commonmark package.
  4. *
  5. * (c) Colin O'Dell <colinodell@gmail.com>
  6. * (c) 2015 Martin Hasoň <martin.hason@gmail.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. declare(strict_types=1);
  12. namespace League\CommonMark\Tests\Unit\Extension\Attributes\Util;
  13. use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
  14. use League\CommonMark\Node\Block\AbstractBlock;
  15. use League\CommonMark\Node\Inline\AbstractInline;
  16. use League\CommonMark\Parser\Cursor;
  17. use League\CommonMark\Tests\Unit\Environment\FakeBlock1;
  18. use League\CommonMark\Tests\Unit\Environment\FakeInline1;
  19. use PHPUnit\Framework\TestCase;
  20. final class AttributesHelperTest extends TestCase
  21. {
  22. /**
  23. * @dataProvider dataForTestParseAttributes
  24. *
  25. * @param array<string, mixed> $expectedResult
  26. */
  27. public function testParseAttributes(Cursor $input, array $expectedResult, string $expectedRemainder = ''): void
  28. {
  29. $this->assertSame($expectedResult, AttributesHelper::parseAttributes($input));
  30. $this->assertSame($expectedRemainder, $input->getRemainder());
  31. }
  32. /**
  33. * @return iterable<Cursor|array<string, mixed>>
  34. */
  35. public function dataForTestParseAttributes(): iterable
  36. {
  37. yield [new Cursor(''), [], ''];
  38. yield [new Cursor('{}'), [], '{}'];
  39. yield [new Cursor('{ }'), [], '{ }'];
  40. // Examples with colons
  41. yield [new Cursor('{:title="My Title"}'), ['title' => 'My Title']];
  42. yield [new Cursor('{: title="My Title"}'), ['title' => 'My Title']];
  43. yield [new Cursor('{:title="My Title" }'), ['title' => 'My Title']];
  44. yield [new Cursor('{: title="My Title" }'), ['title' => 'My Title']];
  45. yield [new Cursor('{: title="My Title" }'), ['title' => 'My Title']];
  46. yield [new Cursor('{: #custom-id }'), ['id' => 'custom-id']];
  47. yield [new Cursor('{: #custom-id #another-id }'), ['id' => 'another-id']];
  48. yield [new Cursor('{: .class1 .class2 }'), ['class' => 'class1 class2']];
  49. yield [new Cursor('{: #custom-id .class1 .class2 title="My Title" }'), ['id' => 'custom-id', 'class' => 'class1 class2', 'title' => 'My Title']];
  50. yield [new Cursor('{:target=_blank}'), ['target' => '_blank']];
  51. yield [new Cursor('{: target=_blank}'), ['target' => '_blank']];
  52. yield [new Cursor('{: target=_blank }'), ['target' => '_blank']];
  53. yield [new Cursor('{: target=_blank }'), ['target' => '_blank']];
  54. // Examples without colons
  55. yield [new Cursor('{title="My Title"}'), ['title' => 'My Title']];
  56. yield [new Cursor('{ title="My Title"}'), ['title' => 'My Title']];
  57. yield [new Cursor('{title="My Title" }'), ['title' => 'My Title']];
  58. yield [new Cursor('{ title="My Title" }'), ['title' => 'My Title']];
  59. yield [new Cursor('{ title="My Title" }'), ['title' => 'My Title']];
  60. yield [new Cursor('{ #custom-id }'), ['id' => 'custom-id']];
  61. yield [new Cursor('{ #custom-id #another-id }'), ['id' => 'another-id']];
  62. yield [new Cursor('{ .class1 .class2 }'), ['class' => 'class1 class2']];
  63. yield [new Cursor('{ #custom-id .class1 .class2 title="My Title" }'), ['id' => 'custom-id', 'class' => 'class1 class2', 'title' => 'My Title']];
  64. yield [new Cursor('{target=_blank}'), ['target' => '_blank']];
  65. yield [new Cursor('{ target=_blank}'), ['target' => '_blank']];
  66. yield [new Cursor('{target=_blank }'), ['target' => '_blank']];
  67. yield [new Cursor('{ target=_blank }'), ['target' => '_blank']];
  68. // Stuff at the beginning
  69. yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']];
  70. yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']];
  71. yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']];
  72. // Note that this method doesn't enforce indentation rules - that should be checked elsewhere
  73. yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']];
  74. yield [new Cursor(' {: #custom-id }'), ['id' => 'custom-id']];
  75. // Stuff on the end
  76. yield [new Cursor('{: #custom-id } '), ['id' => 'custom-id'], ' '];
  77. // Note that this method doesn't abort if non-attribute things are found at the end - that should be checked elsewhere
  78. yield [new Cursor('{: #custom-id } foo'), ['id' => 'custom-id'], ' foo'];
  79. yield [new Cursor('{: #custom-id }.'), ['id' => 'custom-id'], '.'];
  80. // Missing curly brace on end
  81. yield [new Cursor('{: #custom-id'), [], '{: #custom-id'];
  82. // Two sets of attributes in one string - we stop after the first one
  83. yield [new Cursor('{: #id1 } {: #id2 }'), ['id' => 'id1'], ' {: #id2 }'];
  84. // Curly braces inside of values
  85. yield [new Cursor('{: data-json="{1,2,3}" }'), ['data-json' => '{1,2,3}']];
  86. yield [new Cursor('{data-json={1,2,3}} test'), ['data-json' => '{1,2,3}'], ' test'];
  87. }
  88. /**
  89. * @dataProvider dataForTestMergeAttributes
  90. *
  91. * @param AbstractBlock|AbstractInline|array<string, mixed> $a1
  92. * @param AbstractBlock|AbstractInline|array<string, mixed> $a2
  93. * @param array<string, mixed> $expected
  94. */
  95. public function testMergeAttributes($a1, $a2, array $expected): void
  96. {
  97. $this->assertEquals($expected, AttributesHelper::mergeAttributes($a1, $a2));
  98. }
  99. /**
  100. * @return iterable<AbstractBlock|AbstractInline|array<string, mixed>>
  101. */
  102. public function dataForTestMergeAttributes(): iterable
  103. {
  104. yield [
  105. [],
  106. [],
  107. [],
  108. ];
  109. // The second set of attributes overrides the first one (for matching keys)
  110. yield [
  111. ['a' => '1', 'b' => 1],
  112. ['a' => '2', 'c' => 2],
  113. ['a' => '2', 'b' => 1, 'c' => 2],
  114. ];
  115. // Special handling for the class attribute
  116. yield [
  117. ['id' => 'foo', 'class' => 'foo'],
  118. ['id' => 'bar', 'class' => 'bar'],
  119. ['id' => 'bar', 'class' => 'foo bar'],
  120. ];
  121. $block = new FakeBlock1();
  122. $block->data->set('attributes', ['id' => 'block', 'class' => 'block']);
  123. yield [
  124. $block,
  125. ['id' => 'foo', 'class' => 'foo'],
  126. ['id' => 'foo', 'class' => 'block foo'],
  127. ];
  128. yield [
  129. ['id' => 'foo', 'class' => 'foo'],
  130. $block,
  131. ['id' => 'block', 'class' => 'foo block'],
  132. ];
  133. $inline = new FakeInline1();
  134. $inline->data->set('attributes', ['id' => 'inline', 'class' => 'inline']);
  135. yield [
  136. $inline,
  137. ['id' => 'foo', 'class' => 'foo'],
  138. ['id' => 'foo', 'class' => 'inline foo'],
  139. ];
  140. yield [
  141. ['id' => 'foo', 'class' => 'foo'],
  142. $inline,
  143. ['id' => 'inline', 'class' => 'foo inline'],
  144. ];
  145. yield [
  146. $block,
  147. $inline,
  148. ['id' => 'inline', 'class' => 'block inline'],
  149. ];
  150. yield [
  151. $inline,
  152. $block,
  153. ['id' => 'block', 'class' => 'inline block'],
  154. ];
  155. }
  156. }