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