xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmLinebreakTest.php (revision 13a62f810fbd091d15ab734b467eaec0a6bf829a)
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;
18*13a62f81SAndreas 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.
117c4bcbc2eSAndreas Gohr        // GfmLinebreak is SUBSTITION, GfmEmphasis allows SUBSTITION 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