1<?php 2 3namespace dokuwiki\test\Parsing\ParserMode; 4 5use dokuwiki\Parsing\ModeRegistry; 6use dokuwiki\Parsing\ParserMode\GfmStrongUnderscore; 7 8/** 9 * Tests for GFM strong emphasis via double underscores (`__text__`). 10 * 11 * Only loaded when Markdown is the only or preferred syntax. Combines: 12 * - intraword word-boundary rule (multibyte-safe) 13 * - flanking-whitespace rule (no leading/trailing space inside delimiters) 14 * - paragraph-boundary rule (no crossing blank lines) 15 */ 16class GfmStrongUnderscoreTest extends ParserTestBase 17{ 18 public function setUp(): void 19 { 20 parent::setUp(); 21 global $conf; 22 $conf['syntax'] = 'markdown'; 23 ModeRegistry::reset(); 24 } 25 26 public function tearDown(): void 27 { 28 ModeRegistry::reset(); 29 parent::tearDown(); 30 } 31 32 function testBasic() 33 { 34 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 35 $this->P->parse('Foo __Bar__ Baz'); 36 $calls = [ 37 ['document_start', []], 38 ['p_open', []], 39 ['cdata', ["\nFoo "]], 40 ['strong_open', []], 41 ['cdata', ['Bar']], 42 ['strong_close', []], 43 ['cdata', [' Baz']], 44 ['p_close', []], 45 ['document_end', []], 46 ]; 47 $this->assertCalls($calls, $this->H->calls); 48 } 49 50 function testSingleCharacter() 51 { 52 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 53 $this->P->parse('__x__'); 54 $modes = array_column($this->H->calls, 0); 55 $this->assertContains('strong_open', $modes); 56 $this->assertContains('strong_close', $modes); 57 } 58 59 function testMultipleWords() 60 { 61 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 62 $this->P->parse('__one two three__'); 63 $modes = array_column($this->H->calls, 0); 64 $this->assertContains('strong_open', $modes); 65 } 66 67 function testIntrawordDoesNotOpen() 68 { 69 // `foo__bar__` — opening `__` intraword (preceded by `o`). 70 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 71 $this->P->parse('foo__bar__'); 72 $this->assertNotContains('strong_open', array_column($this->H->calls, 0)); 73 } 74 75 function testLeadingWhitespaceDoesNotOpen() 76 { 77 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 78 $this->P->parse('__ foo bar__'); 79 $this->assertNotContains('strong_open', array_column($this->H->calls, 0)); 80 } 81 82 function testTrailingWhitespaceDoesNotClose() 83 { 84 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 85 $this->P->parse('__foo bar __'); 86 $this->assertNotContains('strong_open', array_column($this->H->calls, 0)); 87 } 88 89 function testEmptyDelimiterDoesNotMatch() 90 { 91 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 92 $this->P->parse('____'); 93 $this->assertNotContains('strong_open', array_column($this->H->calls, 0)); 94 } 95 96 function testMultibyteIntrawordDoesNotMatch() 97 { 98 // Cyrillic spec example: `пристаням__стремятся__` — intraword, literal. 99 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 100 $this->P->parse('пристаням__стремятся__'); 101 $this->assertNotContains('strong_open', array_column($this->H->calls, 0)); 102 } 103 104 function testMultibyteContentInsideStrongWorks() 105 { 106 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 107 $this->P->parse('foo __für etwas__ bar'); 108 $this->assertContains('strong_open', array_column($this->H->calls, 0)); 109 $this->assertContains('strong_close', array_column($this->H->calls, 0)); 110 } 111 112 function testDoesNotSpanParagraphBoundary() 113 { 114 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 115 $this->P->parse("__open\n\nclose__"); 116 $this->assertNotContains('strong_open', array_column($this->H->calls, 0)); 117 } 118 119 function testSortValue() 120 { 121 $this->assertSame(70, (new GfmStrongUnderscore())->getSort()); 122 } 123 124 function testInstructionNameIsStrong() 125 { 126 // The mode name is distinct (so it coexists with DW Strong in the 127 // lexer) but it must emit the same `strong_open`/`strong_close` 128 // instructions so the XHTML renderer outputs <strong>. 129 $this->P->addMode('gfm_strong_underscore', new GfmStrongUnderscore()); 130 $this->P->parse('__x__'); 131 $modes = array_column($this->H->calls, 0); 132 $this->assertContains('strong_open', $modes); 133 $this->assertNotContains('gfm_strong_underscore_open', $modes); 134 } 135} 136