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