18719732dSAndreas Gohr<?php 28719732dSAndreas Gohr 38719732dSAndreas Gohrnamespace dokuwiki\test\Parsing\ParserMode; 48719732dSAndreas Gohr 58719732dSAndreas Gohruse dokuwiki\Parsing\ParserMode\Eol; 68719732dSAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmHeader; 78719732dSAndreas Gohr 88719732dSAndreas Gohr/** 98719732dSAndreas Gohr * Tests for GFM ATX headings (`# text` through `###### text`). 108719732dSAndreas Gohr */ 118719732dSAndreas Gohrclass GfmHeaderTest extends ParserTestBase 128719732dSAndreas Gohr{ 138719732dSAndreas Gohr public function setUp(): void 148719732dSAndreas Gohr { 158719732dSAndreas Gohr parent::setUp(); 16*47a02a10SAndreas Gohr $this->setSyntax('md'); 178719732dSAndreas Gohr } 188719732dSAndreas Gohr 198719732dSAndreas Gohr function testLevelOne() 208719732dSAndreas Gohr { 218719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 228719732dSAndreas Gohr $this->P->parse("abc\n# Header\ndef"); 238719732dSAndreas Gohr $calls = [ 248719732dSAndreas Gohr ['document_start', []], 258719732dSAndreas Gohr ['p_open', []], 268719732dSAndreas Gohr ['cdata', ["\nabc"]], 278719732dSAndreas Gohr ['p_close', []], 288719732dSAndreas Gohr ['header', ['Header', 1, 4]], 298719732dSAndreas Gohr ['section_open', [1]], 308719732dSAndreas Gohr ['p_open', []], 318719732dSAndreas Gohr ['cdata', ["\ndef"]], 328719732dSAndreas Gohr ['p_close', []], 338719732dSAndreas Gohr ['section_close', []], 348719732dSAndreas Gohr ['document_end', []], 358719732dSAndreas Gohr ]; 368719732dSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 378719732dSAndreas Gohr } 388719732dSAndreas Gohr 398719732dSAndreas Gohr function testAllLevels() 408719732dSAndreas Gohr { 418719732dSAndreas Gohr foreach ([1, 2, 3, 4, 5, 6] as $level) { 428719732dSAndreas Gohr $this->setUp(); 438719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 448719732dSAndreas Gohr $marker = str_repeat('#', $level); 458719732dSAndreas Gohr $this->P->parse("$marker foo"); 468719732dSAndreas Gohr $calls = array_column($this->H->calls, 0); 478719732dSAndreas Gohr $this->assertContains('header', $calls, "level $level must emit header"); 488719732dSAndreas Gohr 498719732dSAndreas Gohr $headerCall = array_values(array_filter( 508719732dSAndreas Gohr $this->H->calls, 518719732dSAndreas Gohr static fn($c) => $c[0] === 'header' 528719732dSAndreas Gohr ))[0]; 538719732dSAndreas Gohr $this->assertSame('foo', $headerCall[1][0], "level $level title"); 548719732dSAndreas Gohr $this->assertSame($level, $headerCall[1][1], "level $level level"); 558719732dSAndreas Gohr } 568719732dSAndreas Gohr } 578719732dSAndreas Gohr 588719732dSAndreas Gohr function testSevenHashesIsNotAHeading() 598719732dSAndreas Gohr { 608719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 618719732dSAndreas Gohr $this->P->parse('####### foo'); 628719732dSAndreas Gohr $modes = array_column($this->H->calls, 0); 638719732dSAndreas Gohr $this->assertNotContains('header', $modes, 648719732dSAndreas Gohr 'A run of 7 `#` must not open an ATX heading'); 658719732dSAndreas Gohr } 668719732dSAndreas Gohr 678719732dSAndreas Gohr function testHashTouchingTextIsNotAHeading() 688719732dSAndreas Gohr { 698719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 708719732dSAndreas Gohr $this->P->parse("#5 bolt\n\n#hashtag"); 718719732dSAndreas Gohr $modes = array_column($this->H->calls, 0); 728719732dSAndreas Gohr $this->assertNotContains('header', $modes, 738719732dSAndreas Gohr 'A `#` directly followed by a non-space char must not open a heading'); 748719732dSAndreas Gohr } 758719732dSAndreas Gohr 768719732dSAndreas Gohr function testEmptyHeading() 778719732dSAndreas Gohr { 788719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 798719732dSAndreas Gohr $this->P->parse("#\n"); 808719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 818719732dSAndreas Gohr $this->assertCount(1, $headerCalls, 'bare `#` must still emit a heading'); 828719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 838719732dSAndreas Gohr $this->assertSame('', $call[1][0]); 848719732dSAndreas Gohr $this->assertSame(1, $call[1][1]); 858719732dSAndreas Gohr } 868719732dSAndreas Gohr 878719732dSAndreas Gohr function testEmptyHeadingWithTrailingSpace() 888719732dSAndreas Gohr { 898719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 908719732dSAndreas Gohr $this->P->parse("## \n"); 918719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 928719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 938719732dSAndreas Gohr $this->assertSame('', $call[1][0]); 948719732dSAndreas Gohr $this->assertSame(2, $call[1][1]); 958719732dSAndreas Gohr } 968719732dSAndreas Gohr 978719732dSAndreas Gohr function testEmptyHeadingWithClosingHashes() 988719732dSAndreas Gohr { 998719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1008719732dSAndreas Gohr $this->P->parse("### ###\n"); 1018719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1028719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1038719732dSAndreas Gohr $this->assertSame('', $call[1][0]); 1048719732dSAndreas Gohr $this->assertSame(3, $call[1][1]); 1058719732dSAndreas Gohr } 1068719732dSAndreas Gohr 1078719732dSAndreas Gohr function testOptionalClosingHashesStripped() 1088719732dSAndreas Gohr { 1098719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1108719732dSAndreas Gohr $this->P->parse("## foo ##\n"); 1118719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1128719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1138719732dSAndreas Gohr $this->assertSame('foo', $call[1][0]); 1148719732dSAndreas Gohr $this->assertSame(2, $call[1][1]); 1158719732dSAndreas Gohr } 1168719732dSAndreas Gohr 1178719732dSAndreas Gohr function testClosingNeedNotMatchOpeningLength() 1188719732dSAndreas Gohr { 1198719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1208719732dSAndreas Gohr $this->P->parse("# foo ##################################\n"); 1218719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1228719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1238719732dSAndreas Gohr $this->assertSame('foo', $call[1][0]); 1248719732dSAndreas Gohr $this->assertSame(1, $call[1][1]); 1258719732dSAndreas Gohr } 1268719732dSAndreas Gohr 1278719732dSAndreas Gohr function testTrailingSpacesAfterClosing() 1288719732dSAndreas Gohr { 1298719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1308719732dSAndreas Gohr $this->P->parse("### foo ### \n"); 1318719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1328719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1338719732dSAndreas Gohr $this->assertSame('foo', $call[1][0]); 1348719732dSAndreas Gohr $this->assertSame(3, $call[1][1]); 1358719732dSAndreas Gohr } 1368719732dSAndreas Gohr 1378719732dSAndreas Gohr function testClosingRunFollowedByTextIsNotClosing() 1388719732dSAndreas Gohr { 1398719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1408719732dSAndreas Gohr $this->P->parse("### foo ### b\n"); 1418719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1428719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1438719732dSAndreas Gohr $this->assertSame('foo ### b', $call[1][0]); 1448719732dSAndreas Gohr $this->assertSame(3, $call[1][1]); 1458719732dSAndreas Gohr } 1468719732dSAndreas Gohr 1478719732dSAndreas Gohr function testClosingHashMustBePrecededBySpace() 1488719732dSAndreas Gohr { 1498719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1508719732dSAndreas Gohr $this->P->parse("# foo#\n"); 1518719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1528719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1538719732dSAndreas Gohr $this->assertSame('foo#', $call[1][0]); 1548719732dSAndreas Gohr $this->assertSame(1, $call[1][1]); 1558719732dSAndreas Gohr } 1568719732dSAndreas Gohr 1578719732dSAndreas Gohr function testIndentedHashIsNotAHeading() 1588719732dSAndreas Gohr { 1598719732dSAndreas Gohr // GFM tolerates 0-3 spaces of indent; we do not. Any leading 1608719732dSAndreas Gohr // whitespace makes the line a paragraph (or preformatted, if 1618719732dSAndreas Gohr // it meets that mode's rules). 1628719732dSAndreas Gohr foreach ([1, 2, 3] as $indent) { 1638719732dSAndreas Gohr $this->setUp(); 1648719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1658719732dSAndreas Gohr $this->P->parse(str_repeat(' ', $indent) . '### foo'); 1668719732dSAndreas Gohr $modes = array_column($this->H->calls, 0); 1678719732dSAndreas Gohr $this->assertNotContains('header', $modes, 1688719732dSAndreas Gohr "indent=$indent must NOT open a heading"); 1698719732dSAndreas Gohr } 1708719732dSAndreas Gohr } 1718719732dSAndreas Gohr 1728719732dSAndreas Gohr function testContentInlineWhitespaceCollapsed() 1738719732dSAndreas Gohr { 1748719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1758719732dSAndreas Gohr $this->P->parse("# foo \n"); 1768719732dSAndreas Gohr $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header'); 1778719732dSAndreas Gohr $call = array_values($headerCalls)[0]; 1788719732dSAndreas Gohr $this->assertSame('foo', $call[1][0]); 1798719732dSAndreas Gohr } 1808719732dSAndreas Gohr 1818719732dSAndreas Gohr function testHeadingCanInterruptParagraph() 1828719732dSAndreas Gohr { 1838719732dSAndreas Gohr $this->P->addMode('gfm_header', new GfmHeader()); 1848719732dSAndreas Gohr $this->P->addMode('eol', new Eol()); 1858719732dSAndreas Gohr $this->P->parse("Foo bar\n# baz\nBar foo"); 1868719732dSAndreas Gohr $modes = array_column($this->H->calls, 0); 1878719732dSAndreas Gohr $this->assertContains('header', $modes, 1888719732dSAndreas Gohr 'ATX headings must interrupt paragraphs without requiring a blank line'); 1898719732dSAndreas Gohr } 1908719732dSAndreas Gohr 1918719732dSAndreas Gohr function testSortValue() 1928719732dSAndreas Gohr { 1938719732dSAndreas Gohr $mode = new GfmHeader(); 1948719732dSAndreas Gohr $this->assertSame(50, $mode->getSort()); 1958719732dSAndreas Gohr } 1968719732dSAndreas Gohr} 197