xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmLinebreakTest.php (revision 884caed926ca0aa0af6ce3f34ae3aa7317a3361a)
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