xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmHeaderTest.php (revision 8719732d06ab7306149725c7c5ea71deb8ff0382)
1*8719732dSAndreas Gohr<?php
2*8719732dSAndreas Gohr
3*8719732dSAndreas Gohrnamespace dokuwiki\test\Parsing\ParserMode;
4*8719732dSAndreas Gohr
5*8719732dSAndreas Gohruse dokuwiki\Parsing\ModeRegistry;
6*8719732dSAndreas Gohruse dokuwiki\Parsing\ParserMode\Eol;
7*8719732dSAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmHeader;
8*8719732dSAndreas Gohr
9*8719732dSAndreas Gohr/**
10*8719732dSAndreas Gohr * Tests for GFM ATX headings (`# text` through `###### text`).
11*8719732dSAndreas Gohr */
12*8719732dSAndreas Gohrclass GfmHeaderTest extends ParserTestBase
13*8719732dSAndreas Gohr{
14*8719732dSAndreas Gohr    public function setUp(): void
15*8719732dSAndreas Gohr    {
16*8719732dSAndreas Gohr        parent::setUp();
17*8719732dSAndreas Gohr        global $conf;
18*8719732dSAndreas Gohr        $conf['syntax'] = 'markdown';
19*8719732dSAndreas Gohr        ModeRegistry::reset();
20*8719732dSAndreas Gohr    }
21*8719732dSAndreas Gohr
22*8719732dSAndreas Gohr    public function tearDown(): void
23*8719732dSAndreas Gohr    {
24*8719732dSAndreas Gohr        ModeRegistry::reset();
25*8719732dSAndreas Gohr        parent::tearDown();
26*8719732dSAndreas Gohr    }
27*8719732dSAndreas Gohr
28*8719732dSAndreas Gohr    function testLevelOne()
29*8719732dSAndreas Gohr    {
30*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
31*8719732dSAndreas Gohr        $this->P->parse("abc\n# Header\ndef");
32*8719732dSAndreas Gohr        $calls = [
33*8719732dSAndreas Gohr            ['document_start', []],
34*8719732dSAndreas Gohr            ['p_open', []],
35*8719732dSAndreas Gohr            ['cdata', ["\nabc"]],
36*8719732dSAndreas Gohr            ['p_close', []],
37*8719732dSAndreas Gohr            ['header', ['Header', 1, 4]],
38*8719732dSAndreas Gohr            ['section_open', [1]],
39*8719732dSAndreas Gohr            ['p_open', []],
40*8719732dSAndreas Gohr            ['cdata', ["\ndef"]],
41*8719732dSAndreas Gohr            ['p_close', []],
42*8719732dSAndreas Gohr            ['section_close', []],
43*8719732dSAndreas Gohr            ['document_end', []],
44*8719732dSAndreas Gohr        ];
45*8719732dSAndreas Gohr        $this->assertCalls($calls, $this->H->calls);
46*8719732dSAndreas Gohr    }
47*8719732dSAndreas Gohr
48*8719732dSAndreas Gohr    function testAllLevels()
49*8719732dSAndreas Gohr    {
50*8719732dSAndreas Gohr        foreach ([1, 2, 3, 4, 5, 6] as $level) {
51*8719732dSAndreas Gohr            $this->setUp();
52*8719732dSAndreas Gohr            $this->P->addMode('gfm_header', new GfmHeader());
53*8719732dSAndreas Gohr            $marker = str_repeat('#', $level);
54*8719732dSAndreas Gohr            $this->P->parse("$marker foo");
55*8719732dSAndreas Gohr            $calls = array_column($this->H->calls, 0);
56*8719732dSAndreas Gohr            $this->assertContains('header', $calls, "level $level must emit header");
57*8719732dSAndreas Gohr
58*8719732dSAndreas Gohr            $headerCall = array_values(array_filter(
59*8719732dSAndreas Gohr                $this->H->calls,
60*8719732dSAndreas Gohr                static fn($c) => $c[0] === 'header'
61*8719732dSAndreas Gohr            ))[0];
62*8719732dSAndreas Gohr            $this->assertSame('foo', $headerCall[1][0], "level $level title");
63*8719732dSAndreas Gohr            $this->assertSame($level, $headerCall[1][1], "level $level level");
64*8719732dSAndreas Gohr        }
65*8719732dSAndreas Gohr    }
66*8719732dSAndreas Gohr
67*8719732dSAndreas Gohr    function testSevenHashesIsNotAHeading()
68*8719732dSAndreas Gohr    {
69*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
70*8719732dSAndreas Gohr        $this->P->parse('####### foo');
71*8719732dSAndreas Gohr        $modes = array_column($this->H->calls, 0);
72*8719732dSAndreas Gohr        $this->assertNotContains('header', $modes,
73*8719732dSAndreas Gohr            'A run of 7 `#` must not open an ATX heading');
74*8719732dSAndreas Gohr    }
75*8719732dSAndreas Gohr
76*8719732dSAndreas Gohr    function testHashTouchingTextIsNotAHeading()
77*8719732dSAndreas Gohr    {
78*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
79*8719732dSAndreas Gohr        $this->P->parse("#5 bolt\n\n#hashtag");
80*8719732dSAndreas Gohr        $modes = array_column($this->H->calls, 0);
81*8719732dSAndreas Gohr        $this->assertNotContains('header', $modes,
82*8719732dSAndreas Gohr            'A `#` directly followed by a non-space char must not open a heading');
83*8719732dSAndreas Gohr    }
84*8719732dSAndreas Gohr
85*8719732dSAndreas Gohr    function testEmptyHeading()
86*8719732dSAndreas Gohr    {
87*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
88*8719732dSAndreas Gohr        $this->P->parse("#\n");
89*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
90*8719732dSAndreas Gohr        $this->assertCount(1, $headerCalls, 'bare `#` must still emit a heading');
91*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
92*8719732dSAndreas Gohr        $this->assertSame('', $call[1][0]);
93*8719732dSAndreas Gohr        $this->assertSame(1, $call[1][1]);
94*8719732dSAndreas Gohr    }
95*8719732dSAndreas Gohr
96*8719732dSAndreas Gohr    function testEmptyHeadingWithTrailingSpace()
97*8719732dSAndreas Gohr    {
98*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
99*8719732dSAndreas Gohr        $this->P->parse("## \n");
100*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
101*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
102*8719732dSAndreas Gohr        $this->assertSame('', $call[1][0]);
103*8719732dSAndreas Gohr        $this->assertSame(2, $call[1][1]);
104*8719732dSAndreas Gohr    }
105*8719732dSAndreas Gohr
106*8719732dSAndreas Gohr    function testEmptyHeadingWithClosingHashes()
107*8719732dSAndreas Gohr    {
108*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
109*8719732dSAndreas Gohr        $this->P->parse("### ###\n");
110*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
111*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
112*8719732dSAndreas Gohr        $this->assertSame('', $call[1][0]);
113*8719732dSAndreas Gohr        $this->assertSame(3, $call[1][1]);
114*8719732dSAndreas Gohr    }
115*8719732dSAndreas Gohr
116*8719732dSAndreas Gohr    function testOptionalClosingHashesStripped()
117*8719732dSAndreas Gohr    {
118*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
119*8719732dSAndreas Gohr        $this->P->parse("## foo ##\n");
120*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
121*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
122*8719732dSAndreas Gohr        $this->assertSame('foo', $call[1][0]);
123*8719732dSAndreas Gohr        $this->assertSame(2, $call[1][1]);
124*8719732dSAndreas Gohr    }
125*8719732dSAndreas Gohr
126*8719732dSAndreas Gohr    function testClosingNeedNotMatchOpeningLength()
127*8719732dSAndreas Gohr    {
128*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
129*8719732dSAndreas Gohr        $this->P->parse("# foo ##################################\n");
130*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
131*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
132*8719732dSAndreas Gohr        $this->assertSame('foo', $call[1][0]);
133*8719732dSAndreas Gohr        $this->assertSame(1, $call[1][1]);
134*8719732dSAndreas Gohr    }
135*8719732dSAndreas Gohr
136*8719732dSAndreas Gohr    function testTrailingSpacesAfterClosing()
137*8719732dSAndreas Gohr    {
138*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
139*8719732dSAndreas Gohr        $this->P->parse("### foo ###     \n");
140*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
141*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
142*8719732dSAndreas Gohr        $this->assertSame('foo', $call[1][0]);
143*8719732dSAndreas Gohr        $this->assertSame(3, $call[1][1]);
144*8719732dSAndreas Gohr    }
145*8719732dSAndreas Gohr
146*8719732dSAndreas Gohr    function testClosingRunFollowedByTextIsNotClosing()
147*8719732dSAndreas Gohr    {
148*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
149*8719732dSAndreas Gohr        $this->P->parse("### foo ### b\n");
150*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
151*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
152*8719732dSAndreas Gohr        $this->assertSame('foo ### b', $call[1][0]);
153*8719732dSAndreas Gohr        $this->assertSame(3, $call[1][1]);
154*8719732dSAndreas Gohr    }
155*8719732dSAndreas Gohr
156*8719732dSAndreas Gohr    function testClosingHashMustBePrecededBySpace()
157*8719732dSAndreas Gohr    {
158*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
159*8719732dSAndreas Gohr        $this->P->parse("# foo#\n");
160*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
161*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
162*8719732dSAndreas Gohr        $this->assertSame('foo#', $call[1][0]);
163*8719732dSAndreas Gohr        $this->assertSame(1, $call[1][1]);
164*8719732dSAndreas Gohr    }
165*8719732dSAndreas Gohr
166*8719732dSAndreas Gohr    function testIndentedHashIsNotAHeading()
167*8719732dSAndreas Gohr    {
168*8719732dSAndreas Gohr        // GFM tolerates 0-3 spaces of indent; we do not. Any leading
169*8719732dSAndreas Gohr        // whitespace makes the line a paragraph (or preformatted, if
170*8719732dSAndreas Gohr        // it meets that mode's rules).
171*8719732dSAndreas Gohr        foreach ([1, 2, 3] as $indent) {
172*8719732dSAndreas Gohr            $this->setUp();
173*8719732dSAndreas Gohr            $this->P->addMode('gfm_header', new GfmHeader());
174*8719732dSAndreas Gohr            $this->P->parse(str_repeat(' ', $indent) . '### foo');
175*8719732dSAndreas Gohr            $modes = array_column($this->H->calls, 0);
176*8719732dSAndreas Gohr            $this->assertNotContains('header', $modes,
177*8719732dSAndreas Gohr                "indent=$indent must NOT open a heading");
178*8719732dSAndreas Gohr        }
179*8719732dSAndreas Gohr    }
180*8719732dSAndreas Gohr
181*8719732dSAndreas Gohr    function testContentInlineWhitespaceCollapsed()
182*8719732dSAndreas Gohr    {
183*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
184*8719732dSAndreas Gohr        $this->P->parse("#                  foo                     \n");
185*8719732dSAndreas Gohr        $headerCalls = array_filter($this->H->calls, static fn($c) => $c[0] === 'header');
186*8719732dSAndreas Gohr        $call = array_values($headerCalls)[0];
187*8719732dSAndreas Gohr        $this->assertSame('foo', $call[1][0]);
188*8719732dSAndreas Gohr    }
189*8719732dSAndreas Gohr
190*8719732dSAndreas Gohr    function testHeadingCanInterruptParagraph()
191*8719732dSAndreas Gohr    {
192*8719732dSAndreas Gohr        $this->P->addMode('gfm_header', new GfmHeader());
193*8719732dSAndreas Gohr        $this->P->addMode('eol', new Eol());
194*8719732dSAndreas Gohr        $this->P->parse("Foo bar\n# baz\nBar foo");
195*8719732dSAndreas Gohr        $modes = array_column($this->H->calls, 0);
196*8719732dSAndreas Gohr        $this->assertContains('header', $modes,
197*8719732dSAndreas Gohr            'ATX headings must interrupt paragraphs without requiring a blank line');
198*8719732dSAndreas Gohr    }
199*8719732dSAndreas Gohr
200*8719732dSAndreas Gohr    function testSortValue()
201*8719732dSAndreas Gohr    {
202*8719732dSAndreas Gohr        $mode = new GfmHeader();
203*8719732dSAndreas Gohr        $this->assertSame(50, $mode->getSort());
204*8719732dSAndreas Gohr    }
205*8719732dSAndreas Gohr}
206