1<?php 2 3namespace dokuwiki\test\Parsing\ParserMode; 4 5use dokuwiki\Parsing\ParserMode\GfmDeleted; 6 7/** 8 * Tests for the GFM strikethrough mode (`~~text~~`). 9 */ 10class GfmDeletedTest extends ParserTestBase 11{ 12 public function setUp(): void 13 { 14 parent::setUp(); 15 $this->setSyntax('md'); 16 } 17 18 function testBasicStrikethrough() 19 { 20 $this->P->addMode('gfm_deleted', new GfmDeleted()); 21 $this->P->parse('Foo ~~Bar~~ Baz'); 22 $calls = [ 23 ['document_start', []], 24 ['p_open', []], 25 ['cdata', ["\nFoo "]], 26 ['deleted_open', []], 27 ['cdata', ['Bar']], 28 ['deleted_close', []], 29 ['cdata', [' Baz']], 30 ['p_close', []], 31 ['document_end', []], 32 ]; 33 $this->assertCalls($calls, $this->H->calls); 34 } 35 36 function testSingleCharacterBody() 37 { 38 $this->P->addMode('gfm_deleted', new GfmDeleted()); 39 $this->P->parse('foo ~~b~~ bar'); 40 $calls = [ 41 ['document_start', []], 42 ['p_open', []], 43 ['cdata', ["\nfoo "]], 44 ['deleted_open', []], 45 ['cdata', ['b']], 46 ['deleted_close', []], 47 ['cdata', [' bar']], 48 ['p_close', []], 49 ['document_end', []], 50 ]; 51 $this->assertCalls($calls, $this->H->calls); 52 } 53 54 function testMultipleWords() 55 { 56 $this->P->addMode('gfm_deleted', new GfmDeleted()); 57 $this->P->parse('~~three four five~~'); 58 $calls = [ 59 ['document_start', []], 60 ['p_open', []], 61 ['cdata', ["\n"]], 62 ['deleted_open', []], 63 ['cdata', ['three four five']], 64 ['deleted_close', []], 65 ['cdata', ['']], 66 ['p_close', []], 67 ['document_end', []], 68 ]; 69 $this->assertCalls($calls, $this->H->calls); 70 } 71 72 function testTwoSeparateStrikethroughsOnOneLine() 73 { 74 $this->P->addMode('gfm_deleted', new GfmDeleted()); 75 $this->P->parse('~~one~~ and ~~two~~'); 76 $calls = [ 77 ['document_start', []], 78 ['p_open', []], 79 ['cdata', ["\n"]], 80 ['deleted_open', []], 81 ['cdata', ['one']], 82 ['deleted_close', []], 83 ['cdata', [' and ']], 84 ['deleted_open', []], 85 ['cdata', ['two']], 86 ['deleted_close', []], 87 ['cdata', ['']], 88 ['p_close', []], 89 ['document_end', []], 90 ]; 91 $this->assertCalls($calls, $this->H->calls); 92 } 93 94 function testUnmatchedOpenerDoesNotStrike() 95 { 96 $this->P->addMode('gfm_deleted', new GfmDeleted()); 97 $this->P->parse('foo ~~bar with no closer'); 98 $calls = [ 99 ['document_start', []], 100 ['p_open', []], 101 ['cdata', ["\nfoo ~~bar with no closer"]], 102 ['p_close', []], 103 ['document_end', []], 104 ]; 105 $this->assertCalls($calls, $this->H->calls); 106 } 107 108 function testOpenerFollowedBySpaceDoesNotStrike() 109 { 110 $this->P->addMode('gfm_deleted', new GfmDeleted()); 111 $this->P->parse('foo ~~ bar~~ baz'); 112 $calls = [ 113 ['document_start', []], 114 ['p_open', []], 115 ['cdata', ["\nfoo ~~ bar~~ baz"]], 116 ['p_close', []], 117 ['document_end', []], 118 ]; 119 $this->assertCalls($calls, $this->H->calls); 120 } 121 122 function testEmptyDelimiterDoesNotStrike() 123 { 124 $this->P->addMode('gfm_deleted', new GfmDeleted()); 125 $this->P->parse('foo ~~~~ bar'); 126 $modes = array_column($this->H->calls, 0); 127 $this->assertNotContains('deleted_open', $modes, 128 'Empty `~~~~` must stay literal'); 129 } 130 131 function testTripleTildeDoesNotStrike() 132 { 133 // `~~~` is the GFM fenced-code-block marker; strikethrough must not 134 // consume a run of three or more tildes. 135 $this->P->addMode('gfm_deleted', new GfmDeleted()); 136 $this->P->parse('foo ~~~bar~~~ baz'); 137 $modes = array_column($this->H->calls, 0); 138 $this->assertNotContains('deleted_open', $modes, 139 'Run of 3+ tildes must not trigger strikethrough'); 140 } 141 142 function testMultilineStrikethrough() 143 { 144 $this->P->addMode('gfm_deleted', new GfmDeleted()); 145 $this->P->parse("~~line\nline\nline~~"); 146 $calls = [ 147 ['document_start', []], 148 ['p_open', []], 149 ['cdata', ["\n"]], 150 ['deleted_open', []], 151 ['cdata', ["line\nline\nline"]], 152 ['deleted_close', []], 153 ['cdata', ['']], 154 ['p_close', []], 155 ['document_end', []], 156 ]; 157 $this->assertCalls($calls, $this->H->calls); 158 } 159 160 function testDoesNotSpanParagraphBoundary() 161 { 162 // An unclosed `~~` followed by a blank line must stay literal. 163 // Mirrors GFM spec example 492. 164 $this->P->addMode('gfm_deleted', new GfmDeleted()); 165 $this->P->parse("This ~~has a\n\nnew paragraph~~."); 166 $modes = array_column($this->H->calls, 0); 167 $this->assertNotContains('deleted_open', $modes, 168 'GfmDeleted must not open when the closing `~~` is past a blank line'); 169 } 170 171 function testAllowsSingleNewline() 172 { 173 $this->P->addMode('gfm_deleted', new GfmDeleted()); 174 $this->P->parse("~~open\nclose~~"); 175 $modes = array_column($this->H->calls, 0); 176 $this->assertContains('deleted_open', $modes, 177 'GfmDeleted must still match across a single newline'); 178 } 179 180 function testTrailingWhitespaceBeforeCloserDoesNotStrike() 181 { 182 $this->P->addMode('gfm_deleted', new GfmDeleted()); 183 $this->P->parse('~~foo bar ~~'); 184 $modes = array_column($this->H->calls, 0); 185 $this->assertNotContains('deleted_open', $modes, 186 'Closer preceded by whitespace must not match'); 187 } 188 189 function testSortValue() 190 { 191 $mode = new GfmDeleted(); 192 $this->assertSame(130, $mode->getSort()); 193 } 194} 195