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