1<?php 2 3namespace dokuwiki\test\Parsing\ParserMode; 4 5use dokuwiki\Parsing\ParserMode\Code; 6use dokuwiki\Parsing\ParserMode\Eol; 7use dokuwiki\Parsing\ParserMode\File; 8use dokuwiki\Parsing\ParserMode\Header; 9use dokuwiki\Parsing\ParserMode\Listblock; 10use dokuwiki\Parsing\ParserMode\Preformatted; 11 12class PreformattedTest extends ParserTestBase 13{ 14 15 function testFile() { 16 $this->P->addMode('file',new File()); 17 $this->P->parse('Foo <file>testing</file> Bar'); 18 $calls = [ 19 ['document_start',[]], 20 ['p_open',[]], 21 ['cdata',["\n".'Foo ']], 22 ['p_close',[]], 23 ['file',['testing',null,null]], 24 ['p_open',[]], 25 ['cdata',[' Bar']], 26 ['p_close',[]], 27 ['document_end',[]], 28 ]; 29 30 $this->assertCalls($calls, $this->H->calls); 31 } 32 33 function testCode() { 34 $this->P->addMode('code',new Code()); 35 $this->P->parse('Foo <code>testing</code> Bar'); 36 $calls = [ 37 ['document_start',[]], 38 ['p_open',[]], 39 ['cdata',["\n".'Foo ']], 40 ['p_close',[]], 41 ['code',['testing', null, null]], 42 ['p_open',[]], 43 ['cdata',[' Bar']], 44 ['p_close',[]], 45 ['document_end',[]], 46 ]; 47 $this->assertCalls($calls, $this->H->calls); 48 } 49 50 function testCodeWhitespace() { 51 $this->P->addMode('code',new Code()); 52 $this->P->parse("Foo <code \n>testing</code> Bar"); 53 $calls = [ 54 ['document_start',[]], 55 ['p_open',[]], 56 ['cdata',["\n".'Foo ']], 57 ['p_close',[]], 58 ['code',['testing', null, null]], 59 ['p_open',[]], 60 ['cdata',[' Bar']], 61 ['p_close',[]], 62 ['document_end',[]], 63 ]; 64 $this->assertCalls($calls, $this->H->calls); 65 } 66 67 function testCodeLang() { 68 $this->P->addMode('code',new Code()); 69 $this->P->parse("Foo <code php>testing</code> Bar"); 70 $calls = [ 71 ['document_start',[]], 72 ['p_open',[]], 73 ['cdata',["\n".'Foo ']], 74 ['p_close',[]], 75 ['code',['testing', 'php', null]], 76 ['p_open',[]], 77 ['cdata',[' Bar']], 78 ['p_close',[]], 79 ['document_end',[]], 80 ]; 81 $this->assertCalls($calls, $this->H->calls); 82 } 83 84 function testPreformatted() { 85 $this->P->addMode('preformatted',new Preformatted()); 86 $this->P->parse("F oo\n x \n y \nBar\n"); 87 $calls = [ 88 ['document_start',[]], 89 ['p_open',[]], 90 ['cdata',["\nF oo"]], 91 ['p_close',[]], 92 ['preformatted',["x \n y "]], 93 ['p_open',[]], 94 ['cdata',['Bar']], 95 ['p_close',[]], 96 ['document_end',[]], 97 ]; 98 $this->assertCalls($calls, $this->H->calls); 99 } 100 101 function testPreformattedWinEOL() { 102 $this->P->addMode('preformatted',new Preformatted()); 103 $this->P->parse("F oo\r\n x \r\n y \r\nBar\r\n"); 104 $calls = [ 105 ['document_start',[]], 106 ['p_open',[]], 107 ['cdata',["\nF oo"]], 108 ['p_close',[]], 109 ['preformatted',["x \n y "]], 110 ['p_open',[]], 111 ['cdata',['Bar']], 112 ['p_close',[]], 113 ['document_end',[]], 114 ]; 115 $this->assertCalls($calls, $this->H->calls); 116 } 117 118 function testPreformattedTab() { 119 $this->P->addMode('preformatted',new Preformatted()); 120 $this->P->parse("F oo\n\tx\t\n\t\ty\t\nBar\n"); 121 $calls = [ 122 ['document_start',[]], 123 ['p_open',[]], 124 ['cdata',["\nF oo"]], 125 ['p_close',[]], 126 ['preformatted',["x\t\n\ty\t"]], 127 ['p_open',[]], 128 ['cdata',["Bar"]], 129 ['p_close',[]], 130 ['document_end',[]], 131 ]; 132 $this->assertCalls($calls, $this->H->calls); 133 } 134 135 function testPreformattedTabWinEOL() { 136 $this->P->addMode('preformatted',new Preformatted()); 137 $this->P->parse("F oo\r\n\tx\t\r\n\t\ty\t\r\nBar\r\n"); 138 $calls = [ 139 ['document_start',[]], 140 ['p_open',[]], 141 ['cdata',["\nF oo"]], 142 ['p_close',[]], 143 ['preformatted',["x\t\n\ty\t"]], 144 ['p_open',[]], 145 ['cdata',["Bar"]], 146 ['p_close',[]], 147 ['document_end',[]], 148 ]; 149 $this->assertCalls($calls, $this->H->calls); 150 } 151 152 function testPreformattedList() { 153 // Listblock (sort 10) must be added before Preformatted (sort 20) so 154 // the resulting PCRE alternation matches the canonical mode order. 155 // PCRE picks the first alternative that matches at a given position, 156 // and an indented bullet line like " - x" matches both modes at the 157 // same offset; Listblock has to come first to win the tie. 158 $this->P->addMode('listblock',new Listblock()); 159 $this->P->addMode('preformatted',new Preformatted()); 160 $this->P->parse(" - x \n * y \nF oo\n x \n y \n -X\n *Y\nBar\n"); 161 $calls = [ 162 ['document_start',[]], 163 ['listo_open',[]], 164 ['listitem_open',[1]], 165 ['listcontent_open',[]], 166 ['cdata',[" x "]], 167 ['listcontent_close',[]], 168 ['listitem_close',[]], 169 ['listo_close',[]], 170 ['listu_open',[]], 171 ['listitem_open',[1]], 172 ['listcontent_open',[]], 173 ['cdata',[" y "]], 174 ['listcontent_close',[]], 175 ['listitem_close',[]], 176 ['listu_close',[]], 177 ['p_open',[]], 178 ['cdata',["F oo"]], 179 ['p_close',[]], 180 ['preformatted',["x \n y \n-X\n*Y"]], 181 ['p_open',[]], 182 ['cdata',["Bar"]], 183 ['p_close',[]], 184 ['document_end',[]], 185 ]; 186 $this->assertCalls($calls, $this->H->calls); 187 } 188 189 190 function testMarkdownPreferredUsesFourSpaces() { 191 // In `md` and `md+dw` settings the indent threshold is 4, 192 // matching GFM's indented code block rule. Lines with only 2-3 193 // leading spaces stay as paragraph text. 194 global $conf; 195 $conf['syntax'] = 'md'; 196 $this->P->addMode('preformatted', new Preformatted()); 197 $this->P->parse("F oo\n x \n y \nBar\n"); 198 $calls = [ 199 ['document_start', []], 200 ['p_open', []], 201 ['cdata', ["\nF oo"]], 202 ['p_close', []], 203 ['preformatted', ["x \n y "]], 204 ['p_open', []], 205 ['cdata', ['Bar']], 206 ['p_close', []], 207 ['document_end', []], 208 ]; 209 $this->assertCalls($calls, $this->H->calls); 210 } 211 212 function testMarkdownPreferredRejectsTwoSpaces() { 213 // 2-space indent in MD-preferred mode does NOT trigger preformatted. 214 global $conf; 215 $conf['syntax'] = 'md'; 216 $this->P->addMode('preformatted', new Preformatted()); 217 $this->P->parse("F oo\n x\nBar\n"); 218 $modes = array_column($this->H->calls, 0); 219 $this->assertNotContains('preformatted', $modes, 220 '2-space indent must not trigger preformatted when Markdown is preferred'); 221 } 222 223 function testMarkdownPreferredTabStillTriggers() { 224 // Tab is a trigger regardless of the space threshold. 225 global $conf; 226 $conf['syntax'] = 'md'; 227 $this->P->addMode('preformatted', new Preformatted()); 228 $this->P->parse("F oo\n\tx\nBar\n"); 229 $modes = array_column($this->H->calls, 0); 230 $this->assertContains('preformatted', $modes, 231 'A single tab must still trigger preformatted in MD-preferred mode'); 232 } 233 234 function testPreformattedPlusHeaderAndEol() { 235 // Note that EOL must come after preformatted! 236 $this->P->addMode('preformatted',new Preformatted()); 237 $this->P->addMode('header',new Header()); 238 $this->P->addMode('eol',new Eol()); 239 $this->P->parse("F oo\n ==Test==\n y \nBar\n"); 240 $calls = [ 241 ['document_start',[]], 242 ['p_open',[]], 243 ['cdata',["F oo"]], 244 ['p_close',[]], 245 ['preformatted',["==Test==\n y "]], 246 ['p_open',[]], 247 ['cdata',['Bar']], 248 ['p_close',[]], 249 ['document_end',[]], 250 ]; 251 $this->assertCalls($calls, $this->H->calls); 252 } 253} 254