xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmBacktickDoubleTest.php (revision 13a62f810fbd091d15ab734b467eaec0a6bf829a)
1<?php
2
3namespace dokuwiki\test\Parsing\ParserMode;
4
5use dokuwiki\Parsing\ModeRegistry;
6use dokuwiki\Parsing\ParserMode\GfmBacktickDouble;
7
8/**
9 * Tests for the GFM double-backtick code-span mode.
10 *
11 * The whole point of this length is being able to embed a lone
12 * backtick in inline code — input ``foo`bar`` renders as
13 * <code>foo`bar</code>. Combined with the edge-space strip rule,
14 * the boundaries can hold backticks too: input `` `foo` ``
15 * renders as <code>`foo`</code>.
16 */
17class GfmBacktickDoubleTest extends ParserTestBase
18{
19    public function setUp(): void
20    {
21        parent::setUp();
22        global $conf;
23        $conf['syntax'] = 'md';
24        ModeRegistry::reset();
25    }
26
27    public function tearDown(): void
28    {
29        ModeRegistry::reset();
30        parent::tearDown();
31    }
32
33    function testBasicSpan()
34    {
35        $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble());
36        $this->P->parse('foo ``bar`` baz');
37        $calls = [
38            ['document_start', []],
39            ['p_open', []],
40            ['cdata', ["\nfoo "]],
41            ['monospace_open', []],
42            ['unformatted', ['bar']],
43            ['monospace_close', []],
44            ['cdata', [' baz']],
45            ['p_close', []],
46            ['document_end', []],
47        ];
48        $this->assertCalls($calls, $this->H->calls);
49    }
50
51    function testAllowsInteriorSingleBacktick()
52    {
53        // GFM example 349. Input ``foo`bar`` — a lone backtick in the
54        // body cannot be a valid n=2 closer, so it stays as content.
55        $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble());
56        $this->P->parse('``foo`bar``');
57        $calls = [
58            ['document_start', []],
59            ['p_open', []],
60            ['cdata', ["\n"]],
61            ['monospace_open', []],
62            ['unformatted', ['foo`bar']],
63            ['monospace_close', []],
64            ['cdata', ['']],
65            ['p_close', []],
66            ['document_end', []],
67        ];
68        $this->assertCalls($calls, $this->H->calls);
69    }
70
71    function testStripsEdgeSpaces()
72    {
73        // GFM example 339. Input `` foo ` bar `` — one leading and one
74        // trailing space stripped; the interior lone backtick stays.
75        $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble());
76        $this->P->parse('`` foo ` bar ``');
77        $calls = [
78            ['document_start', []],
79            ['p_open', []],
80            ['cdata', ["\n"]],
81            ['monospace_open', []],
82            ['unformatted', ['foo ` bar']],
83            ['monospace_close', []],
84            ['cdata', ['']],
85            ['p_close', []],
86            ['document_end', []],
87        ];
88        $this->assertCalls($calls, $this->H->calls);
89    }
90
91    function testConvertsNewlinesToSpaces()
92    {
93        // GFM example 345: newlines inside a span become single spaces,
94        // then edge-space stripping applies to the normalized body.
95        $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble());
96        $this->P->parse("``\nfoo\nbar  \nbaz\n``");
97        $modes = array_column($this->H->calls, 0);
98        $this->assertContains('monospace_open', $modes);
99
100        $unformatted = array_values(array_filter(
101            $this->H->calls,
102            static fn($c) => $c[0] === 'unformatted'
103        ));
104        $this->assertCount(1, $unformatted);
105        $this->assertSame('foo bar   baz', $unformatted[0][1][0]);
106    }
107
108    function testAllWhitespaceBodyIsPreserved()
109    {
110        $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble());
111        $this->P->parse('a ``   `` b');
112        $calls = [
113            ['document_start', []],
114            ['p_open', []],
115            ['cdata', ["\na "]],
116            ['monospace_open', []],
117            ['unformatted', ['   ']],
118            ['monospace_close', []],
119            ['cdata', [' b']],
120            ['p_close', []],
121            ['document_end', []],
122        ];
123        $this->assertCalls($calls, $this->H->calls);
124    }
125
126    function testEmptyDelimiterDoesNotMatch()
127    {
128        // A run of four backticks — the length-boundary guards reject it
129        // as an n=2 opener followed immediately by an n=2 closer with
130        // empty body.
131        $this->P->addMode('gfm_backtick_double', new GfmBacktickDouble());
132        $this->P->parse('foo ```` bar');
133        $modes = array_column($this->H->calls, 0);
134        $this->assertNotContains('monospace_open', $modes,
135            'Run of 4 backticks must stay literal');
136    }
137
138    function testSortValue()
139    {
140        // Shares the n=1 sort — the length-boundary guards on both modes
141        // mean they never compete for the same input anyway.
142        $mode = new GfmBacktickDouble();
143        $this->assertSame(165, $mode->getSort());
144    }
145}
146