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