1c4bcbc2eSAndreas Gohr<?php 2c4bcbc2eSAndreas Gohr 3c4bcbc2eSAndreas Gohrnamespace dokuwiki\test\Parsing\ParserMode; 4c4bcbc2eSAndreas Gohr 5c4bcbc2eSAndreas Gohruse dokuwiki\Parsing\ModeRegistry; 6c4bcbc2eSAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmEmphasis; 7c4bcbc2eSAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmLinebreak; 8c4bcbc2eSAndreas Gohr 9c4bcbc2eSAndreas Gohr/** 10c4bcbc2eSAndreas Gohr * Tests for the GFM hard-line-break mode. 11c4bcbc2eSAndreas Gohr */ 12c4bcbc2eSAndreas Gohrclass GfmLinebreakTest extends ParserTestBase 13c4bcbc2eSAndreas Gohr{ 14c4bcbc2eSAndreas Gohr public function setUp(): void 15c4bcbc2eSAndreas Gohr { 16c4bcbc2eSAndreas Gohr parent::setUp(); 17c4bcbc2eSAndreas Gohr global $conf; 1813a62f81SAndreas Gohr $conf['syntax'] = 'md'; 19c4bcbc2eSAndreas Gohr ModeRegistry::reset(); 20c4bcbc2eSAndreas Gohr } 21c4bcbc2eSAndreas Gohr 22c4bcbc2eSAndreas Gohr public function tearDown(): void 23c4bcbc2eSAndreas Gohr { 24c4bcbc2eSAndreas Gohr ModeRegistry::reset(); 25c4bcbc2eSAndreas Gohr parent::tearDown(); 26c4bcbc2eSAndreas Gohr } 27c4bcbc2eSAndreas Gohr 28c4bcbc2eSAndreas Gohr function testTwoTrailingSpacesProduceLinebreak() 29c4bcbc2eSAndreas Gohr { 30c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 31c4bcbc2eSAndreas Gohr $this->P->parse("foo \nbar"); 32c4bcbc2eSAndreas Gohr 33c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 34c4bcbc2eSAndreas Gohr $this->assertContains('linebreak', $names, 35c4bcbc2eSAndreas Gohr 'Two trailing spaces before a newline must produce a linebreak call'); 36c4bcbc2eSAndreas Gohr } 37c4bcbc2eSAndreas Gohr 38c4bcbc2eSAndreas Gohr function testManyTrailingSpacesProduceLinebreak() 39c4bcbc2eSAndreas Gohr { 40c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 41c4bcbc2eSAndreas Gohr $this->P->parse("foo \nbar"); 42c4bcbc2eSAndreas Gohr 43c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 44c4bcbc2eSAndreas Gohr $this->assertContains('linebreak', $names, 45c4bcbc2eSAndreas Gohr '7+ trailing spaces before a newline must produce a linebreak call'); 46c4bcbc2eSAndreas Gohr } 47c4bcbc2eSAndreas Gohr 48c4bcbc2eSAndreas Gohr function testBackslashNewlineProducesLinebreak() 49c4bcbc2eSAndreas Gohr { 50c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 51c4bcbc2eSAndreas Gohr $this->P->parse("foo\\\nbar"); 52c4bcbc2eSAndreas Gohr 53c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 54c4bcbc2eSAndreas Gohr $this->assertContains('linebreak', $names, 55c4bcbc2eSAndreas Gohr 'A single backslash before a newline must produce a linebreak call'); 56c4bcbc2eSAndreas Gohr } 57c4bcbc2eSAndreas Gohr 58c4bcbc2eSAndreas Gohr function testLeadingWhitespaceOnNextLineConsumed() 59c4bcbc2eSAndreas Gohr { 60c4bcbc2eSAndreas Gohr // Spec example 656 / 657: leading spaces at the beginning of the 61c4bcbc2eSAndreas Gohr // next line are dropped — the rendered HTML must not carry them. 62c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 63c4bcbc2eSAndreas Gohr $this->P->parse("foo \n bar"); 64c4bcbc2eSAndreas Gohr 65c4bcbc2eSAndreas Gohr $cdata = array_filter($this->H->calls, static fn($c) => $c[0] === 'cdata'); 66c4bcbc2eSAndreas Gohr $joined = implode('', array_map(static fn($c) => $c[1][0], $cdata)); 67c4bcbc2eSAndreas Gohr 68c4bcbc2eSAndreas Gohr $this->assertSame("\nfoobar", $joined, 69c4bcbc2eSAndreas Gohr 'Leading whitespace on the line after a hard break must be consumed'); 70c4bcbc2eSAndreas Gohr } 71c4bcbc2eSAndreas Gohr 72c4bcbc2eSAndreas Gohr function testNoLinebreakAtParagraphBreak() 73c4bcbc2eSAndreas Gohr { 74c4bcbc2eSAndreas Gohr // Spec example 665 (analogue): trailing spaces immediately before 75c4bcbc2eSAndreas Gohr // a paragraph break are not a hard break — the lookahead rejects. 76c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 77c4bcbc2eSAndreas Gohr $this->P->parse("foo \n\nbar"); 78c4bcbc2eSAndreas Gohr 79c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 80c4bcbc2eSAndreas Gohr $this->assertNotContains('linebreak', $names, 81c4bcbc2eSAndreas Gohr 'Trailing spaces before a blank line must not produce a hard break'); 82c4bcbc2eSAndreas Gohr } 83c4bcbc2eSAndreas Gohr 84c4bcbc2eSAndreas Gohr function testNoLinebreakAtEof() 85c4bcbc2eSAndreas Gohr { 86c4bcbc2eSAndreas Gohr // Spec example 665: trailing spaces at end of document are not a 87c4bcbc2eSAndreas Gohr // hard break. The parser appends `\n`, so the lookahead's `\z` arm 88c4bcbc2eSAndreas Gohr // catches this case. 89c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 90c4bcbc2eSAndreas Gohr $this->P->parse('foo '); 91c4bcbc2eSAndreas Gohr 92c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 93c4bcbc2eSAndreas Gohr $this->assertNotContains('linebreak', $names, 94c4bcbc2eSAndreas Gohr 'Trailing spaces at EOF must not produce a hard break'); 95c4bcbc2eSAndreas Gohr } 96c4bcbc2eSAndreas Gohr 97c4bcbc2eSAndreas Gohr function testBackslashAtEofStaysLiteral() 98c4bcbc2eSAndreas Gohr { 99c4bcbc2eSAndreas Gohr // Spec example 664: a single trailing backslash at end of document 100c4bcbc2eSAndreas Gohr // is not a hard break — same paragraph-end rule. 101c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 102c4bcbc2eSAndreas Gohr $this->P->parse('foo\\'); 103c4bcbc2eSAndreas Gohr 104c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 105c4bcbc2eSAndreas Gohr $this->assertNotContains('linebreak', $names, 106c4bcbc2eSAndreas Gohr 'A trailing backslash at EOF must stay literal, not produce a break'); 107c4bcbc2eSAndreas Gohr 108c4bcbc2eSAndreas Gohr $cdata = array_filter($this->H->calls, static fn($c) => $c[0] === 'cdata'); 109c4bcbc2eSAndreas Gohr $joined = implode('', array_map(static fn($c) => $c[1][0], $cdata)); 110c4bcbc2eSAndreas Gohr $this->assertStringContainsString('\\', $joined, 111c4bcbc2eSAndreas Gohr 'The literal backslash must survive in cdata when no break fires'); 112c4bcbc2eSAndreas Gohr } 113c4bcbc2eSAndreas Gohr 114c4bcbc2eSAndreas Gohr function testWorksInsideEmphasis() 115c4bcbc2eSAndreas Gohr { 116c4bcbc2eSAndreas Gohr // Spec example 658: hard breaks fire inside inline containers. 117*d331a839SAndreas Gohr // GfmLinebreak is SUBSTITUTION, GfmEmphasis allows SUBSTITUTION via 118c4bcbc2eSAndreas Gohr // its allowedModes — so the break appears between the open and 119c4bcbc2eSAndreas Gohr // close emphasis calls. 120c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 121c4bcbc2eSAndreas Gohr $this->P->addMode('gfm_linebreak', new GfmLinebreak()); 122c4bcbc2eSAndreas Gohr $this->P->parse("*foo \nbar*"); 123c4bcbc2eSAndreas Gohr 124c4bcbc2eSAndreas Gohr $names = array_column($this->H->calls, 0); 125c4bcbc2eSAndreas Gohr $emOpen = array_search('emphasis_open', $names, true); 126c4bcbc2eSAndreas Gohr $break = array_search('linebreak', $names, true); 127c4bcbc2eSAndreas Gohr $emClose = array_search('emphasis_close', $names, true); 128c4bcbc2eSAndreas Gohr 129c4bcbc2eSAndreas Gohr $this->assertNotFalse($emOpen, 'emphasis_open must fire'); 130c4bcbc2eSAndreas Gohr $this->assertNotFalse($break, 'linebreak must fire inside emphasis'); 131c4bcbc2eSAndreas Gohr $this->assertNotFalse($emClose, 'emphasis_close must fire'); 132c4bcbc2eSAndreas Gohr $this->assertLessThan($break, $emOpen, 133c4bcbc2eSAndreas Gohr 'linebreak must come after the emphasis opener'); 134c4bcbc2eSAndreas Gohr $this->assertLessThan($emClose, $break, 135c4bcbc2eSAndreas Gohr 'linebreak must come before the emphasis closer'); 136c4bcbc2eSAndreas Gohr } 137c4bcbc2eSAndreas Gohr 138c4bcbc2eSAndreas Gohr function testGetSortValue() 139c4bcbc2eSAndreas Gohr { 140c4bcbc2eSAndreas Gohr $mode = new GfmLinebreak(); 141c4bcbc2eSAndreas Gohr $this->assertSame(140, $mode->getSort()); 142c4bcbc2eSAndreas Gohr } 143c4bcbc2eSAndreas Gohr} 144