1<?php 2 3namespace dokuwiki\test\Parsing\ParserMode; 4 5use dokuwiki\Parsing\ModeRegistry; 6use dokuwiki\Parsing\ParserMode\GfmEmphasis; 7 8/** 9 * Tests for the GFM asterisk emphasis mode (`*text*`). 10 */ 11class GfmEmphasisTest extends ParserTestBase 12{ 13 public function setUp(): void 14 { 15 parent::setUp(); 16 global $conf; 17 $conf['syntax'] = 'markdown'; 18 ModeRegistry::reset(); 19 } 20 21 public function tearDown(): void 22 { 23 ModeRegistry::reset(); 24 parent::tearDown(); 25 } 26 27 function testBasicAsterisk() 28 { 29 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 30 $this->P->parse('Foo *Bar* Baz'); 31 $calls = [ 32 ['document_start', []], 33 ['p_open', []], 34 ['cdata', ["\nFoo "]], 35 ['emphasis_open', []], 36 ['cdata', ['Bar']], 37 ['emphasis_close', []], 38 ['cdata', [' Baz']], 39 ['p_close', []], 40 ['document_end', []], 41 ]; 42 $this->assertCalls($calls, $this->H->calls); 43 } 44 45 function testSingleCharacter() 46 { 47 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 48 $this->P->parse('foo *b* bar'); 49 $calls = [ 50 ['document_start', []], 51 ['p_open', []], 52 ['cdata', ["\nfoo "]], 53 ['emphasis_open', []], 54 ['cdata', ['b']], 55 ['emphasis_close', []], 56 ['cdata', [' bar']], 57 ['p_close', []], 58 ['document_end', []], 59 ]; 60 $this->assertCalls($calls, $this->H->calls); 61 } 62 63 function testMultipleWords() 64 { 65 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 66 $this->P->parse('*three four five*'); 67 $calls = [ 68 ['document_start', []], 69 ['p_open', []], 70 ['cdata', ["\n"]], 71 ['emphasis_open', []], 72 ['cdata', ['three four five']], 73 ['emphasis_close', []], 74 ['cdata', ['']], 75 ['p_close', []], 76 ['document_end', []], 77 ]; 78 $this->assertCalls($calls, $this->H->calls); 79 } 80 81 function testTwoSeparateEmphasisOnOneLine() 82 { 83 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 84 $this->P->parse('*one* and *two*'); 85 $calls = [ 86 ['document_start', []], 87 ['p_open', []], 88 ['cdata', ["\n"]], 89 ['emphasis_open', []], 90 ['cdata', ['one']], 91 ['emphasis_close', []], 92 ['cdata', [' and ']], 93 ['emphasis_open', []], 94 ['cdata', ['two']], 95 ['emphasis_close', []], 96 ['cdata', ['']], 97 ['p_close', []], 98 ['document_end', []], 99 ]; 100 $this->assertCalls($calls, $this->H->calls); 101 } 102 103 function testUnmatchedOpenerDoesNotEmphasise() 104 { 105 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 106 $this->P->parse('foo *bar with no closer'); 107 $calls = [ 108 ['document_start', []], 109 ['p_open', []], 110 ['cdata', ["\nfoo *bar with no closer"]], 111 ['p_close', []], 112 ['document_end', []], 113 ]; 114 $this->assertCalls($calls, $this->H->calls); 115 } 116 117 function testOpenerFollowedBySpaceDoesNotEmphasise() 118 { 119 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 120 $this->P->parse('foo * bar* baz'); 121 $calls = [ 122 ['document_start', []], 123 ['p_open', []], 124 ['cdata', ["\nfoo * bar* baz"]], 125 ['p_close', []], 126 ['document_end', []], 127 ]; 128 $this->assertCalls($calls, $this->H->calls); 129 } 130 131 function testEmptyDelimiterDoesNotEmphasise() 132 { 133 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 134 $this->P->parse('foo ** bar'); 135 $calls = [ 136 ['document_start', []], 137 ['p_open', []], 138 ['cdata', ["\nfoo ** bar"]], 139 ['p_close', []], 140 ['document_end', []], 141 ]; 142 $this->assertCalls($calls, $this->H->calls); 143 } 144 145 function testUnderscoreIsNotEmphasised() 146 { 147 // GfmEmphasis handles `*` only — `_` is reserved to avoid the 148 // `__underline__` conflict with DokuWiki's underline syntax; 149 // GfmEmphasisUnderscore handles `_` separately when MD-preferred. 150 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 151 $this->P->parse('foo _bar_ baz'); 152 $calls = [ 153 ['document_start', []], 154 ['p_open', []], 155 ['cdata', ["\nfoo _bar_ baz"]], 156 ['p_close', []], 157 ['document_end', []], 158 ]; 159 $this->assertCalls($calls, $this->H->calls); 160 } 161 162 function testMultilineEmphasis() 163 { 164 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 165 $this->P->parse("*line\nline\nline*"); 166 $calls = [ 167 ['document_start', []], 168 ['p_open', []], 169 ['cdata', ["\n"]], 170 ['emphasis_open', []], 171 ['cdata', ["line\nline\nline"]], 172 ['emphasis_close', []], 173 ['cdata', ['']], 174 ['p_close', []], 175 ['document_end', []], 176 ]; 177 $this->assertCalls($calls, $this->H->calls); 178 } 179 180 function testModeNameIsDistinctFromInstructionName() 181 { 182 // The lexer mode is registered as 'gfm_emphasis' (to avoid collision 183 // with DW Emphasis), but instructions are 'emphasis_open/close' 184 // so the existing XHTML renderer emits <em>. 185 $mode = new GfmEmphasis(); 186 $this->assertSame(80, $mode->getSort()); 187 } 188 189 function testDoesNotSpanParagraphBoundary() 190 { 191 // An unclosed `*` followed by a blank line must stay literal — the 192 // entry pattern's lookahead is paragraph-boundary-safe. 193 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 194 $this->P->parse("*open\n\nclose*"); 195 $modes = array_column($this->H->calls, 0); 196 $this->assertNotContains('emphasis_open', $modes, 197 'GfmEmphasis must not open when the closing `*` is past a blank line'); 198 } 199 200 function testAllowsSingleNewline() 201 { 202 // Single newlines are fine inside emphasis (multi-line emphasis). 203 $this->P->addMode('gfm_emphasis', new GfmEmphasis()); 204 $this->P->parse("*open\nclose*"); 205 $modes = array_column($this->H->calls, 0); 206 $this->assertContains('emphasis_open', $modes, 207 'GfmEmphasis must still match across a single newline'); 208 } 209} 210