1*e89aeebdSAndreas Gohr<?php 2*e89aeebdSAndreas Gohr 3*e89aeebdSAndreas Gohrnamespace dokuwiki\test\Parsing\ParserMode; 4*e89aeebdSAndreas Gohr 5*e89aeebdSAndreas Gohruse dokuwiki\Parsing\ModeRegistry; 6*e89aeebdSAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmLink; 7*e89aeebdSAndreas Gohruse dokuwiki\Parsing\ParserMode\Internallink; 8*e89aeebdSAndreas Gohr 9*e89aeebdSAndreas Gohr/** 10*e89aeebdSAndreas Gohr * Tests for GFM inline links `[text](url)` dispatching to DokuWiki's 11*e89aeebdSAndreas Gohr * internal / external / interwiki / email / windowsshare / local link 12*e89aeebdSAndreas Gohr * handler instructions. 13*e89aeebdSAndreas Gohr */ 14*e89aeebdSAndreas Gohrclass GfmLinkTest extends ParserTestBase 15*e89aeebdSAndreas Gohr{ 16*e89aeebdSAndreas Gohr public function setUp(): void 17*e89aeebdSAndreas Gohr { 18*e89aeebdSAndreas Gohr parent::setUp(); 19*e89aeebdSAndreas Gohr global $conf; 20*e89aeebdSAndreas Gohr $conf['syntax'] = 'markdown'; 21*e89aeebdSAndreas Gohr ModeRegistry::reset(); 22*e89aeebdSAndreas Gohr } 23*e89aeebdSAndreas Gohr 24*e89aeebdSAndreas Gohr public function tearDown(): void 25*e89aeebdSAndreas Gohr { 26*e89aeebdSAndreas Gohr ModeRegistry::reset(); 27*e89aeebdSAndreas Gohr parent::tearDown(); 28*e89aeebdSAndreas Gohr } 29*e89aeebdSAndreas Gohr 30*e89aeebdSAndreas Gohr function testInternalPage() 31*e89aeebdSAndreas Gohr { 32*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 33*e89aeebdSAndreas Gohr $this->P->parse('Foo [text](page) Bar'); 34*e89aeebdSAndreas Gohr $calls = [ 35*e89aeebdSAndreas Gohr ['document_start', []], 36*e89aeebdSAndreas Gohr ['p_open', []], 37*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 38*e89aeebdSAndreas Gohr ['internallink', ['page', 'text']], 39*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 40*e89aeebdSAndreas Gohr ['p_close', []], 41*e89aeebdSAndreas Gohr ['document_end', []], 42*e89aeebdSAndreas Gohr ]; 43*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 44*e89aeebdSAndreas Gohr } 45*e89aeebdSAndreas Gohr 46*e89aeebdSAndreas Gohr function testInternalPageWithNamespace() 47*e89aeebdSAndreas Gohr { 48*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 49*e89aeebdSAndreas Gohr $this->P->parse('Foo [Syntax](wiki:syntax#internal) Bar'); 50*e89aeebdSAndreas Gohr $calls = [ 51*e89aeebdSAndreas Gohr ['document_start', []], 52*e89aeebdSAndreas Gohr ['p_open', []], 53*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 54*e89aeebdSAndreas Gohr ['internallink', ['wiki:syntax#internal', 'Syntax']], 55*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 56*e89aeebdSAndreas Gohr ['p_close', []], 57*e89aeebdSAndreas Gohr ['document_end', []], 58*e89aeebdSAndreas Gohr ]; 59*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 60*e89aeebdSAndreas Gohr } 61*e89aeebdSAndreas Gohr 62*e89aeebdSAndreas Gohr function testExternalLink() 63*e89aeebdSAndreas Gohr { 64*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 65*e89aeebdSAndreas Gohr $this->P->parse('Foo [Google](http://google.com) Bar'); 66*e89aeebdSAndreas Gohr $calls = [ 67*e89aeebdSAndreas Gohr ['document_start', []], 68*e89aeebdSAndreas Gohr ['p_open', []], 69*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 70*e89aeebdSAndreas Gohr ['externallink', ['http://google.com', 'Google']], 71*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 72*e89aeebdSAndreas Gohr ['p_close', []], 73*e89aeebdSAndreas Gohr ['document_end', []], 74*e89aeebdSAndreas Gohr ]; 75*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 76*e89aeebdSAndreas Gohr } 77*e89aeebdSAndreas Gohr 78*e89aeebdSAndreas Gohr function testInterwikiLink() 79*e89aeebdSAndreas Gohr { 80*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 81*e89aeebdSAndreas Gohr $this->P->parse('Foo [callbacks](wp>Callback) Bar'); 82*e89aeebdSAndreas Gohr $calls = [ 83*e89aeebdSAndreas Gohr ['document_start', []], 84*e89aeebdSAndreas Gohr ['p_open', []], 85*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 86*e89aeebdSAndreas Gohr ['interwikilink', ['wp>Callback', 'callbacks', 'wp', 'Callback']], 87*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 88*e89aeebdSAndreas Gohr ['p_close', []], 89*e89aeebdSAndreas Gohr ['document_end', []], 90*e89aeebdSAndreas Gohr ]; 91*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 92*e89aeebdSAndreas Gohr } 93*e89aeebdSAndreas Gohr 94*e89aeebdSAndreas Gohr function testInterwikiLinkCaseNormalized() 95*e89aeebdSAndreas Gohr { 96*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 97*e89aeebdSAndreas Gohr $this->P->parse('Foo [Page](IW>somepage) Bar'); 98*e89aeebdSAndreas Gohr $calls = [ 99*e89aeebdSAndreas Gohr ['document_start', []], 100*e89aeebdSAndreas Gohr ['p_open', []], 101*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 102*e89aeebdSAndreas Gohr ['interwikilink', ['IW>somepage', 'Page', 'iw', 'somepage']], 103*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 104*e89aeebdSAndreas Gohr ['p_close', []], 105*e89aeebdSAndreas Gohr ['document_end', []], 106*e89aeebdSAndreas Gohr ]; 107*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 108*e89aeebdSAndreas Gohr } 109*e89aeebdSAndreas Gohr 110*e89aeebdSAndreas Gohr function testEmailLink() 111*e89aeebdSAndreas Gohr { 112*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 113*e89aeebdSAndreas Gohr $this->P->parse('Foo [mail](user@example.com) Bar'); 114*e89aeebdSAndreas Gohr $calls = [ 115*e89aeebdSAndreas Gohr ['document_start', []], 116*e89aeebdSAndreas Gohr ['p_open', []], 117*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 118*e89aeebdSAndreas Gohr ['emaillink', ['user@example.com', 'mail']], 119*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 120*e89aeebdSAndreas Gohr ['p_close', []], 121*e89aeebdSAndreas Gohr ['document_end', []], 122*e89aeebdSAndreas Gohr ]; 123*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 124*e89aeebdSAndreas Gohr } 125*e89aeebdSAndreas Gohr 126*e89aeebdSAndreas Gohr function testLocalAnchor() 127*e89aeebdSAndreas Gohr { 128*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 129*e89aeebdSAndreas Gohr $this->P->parse('Foo [section](#anchor) Bar'); 130*e89aeebdSAndreas Gohr $calls = [ 131*e89aeebdSAndreas Gohr ['document_start', []], 132*e89aeebdSAndreas Gohr ['p_open', []], 133*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 134*e89aeebdSAndreas Gohr ['locallink', ['anchor', 'section']], 135*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 136*e89aeebdSAndreas Gohr ['p_close', []], 137*e89aeebdSAndreas Gohr ['document_end', []], 138*e89aeebdSAndreas Gohr ]; 139*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 140*e89aeebdSAndreas Gohr } 141*e89aeebdSAndreas Gohr 142*e89aeebdSAndreas Gohr function testWindowsShare() 143*e89aeebdSAndreas Gohr { 144*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 145*e89aeebdSAndreas Gohr $this->P->parse('Foo [share](\\\\server\\share) Bar'); 146*e89aeebdSAndreas Gohr $calls = [ 147*e89aeebdSAndreas Gohr ['document_start', []], 148*e89aeebdSAndreas Gohr ['p_open', []], 149*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 150*e89aeebdSAndreas Gohr ['windowssharelink', ['\\\\server\\share', 'share']], 151*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 152*e89aeebdSAndreas Gohr ['p_close', []], 153*e89aeebdSAndreas Gohr ['document_end', []], 154*e89aeebdSAndreas Gohr ]; 155*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 156*e89aeebdSAndreas Gohr } 157*e89aeebdSAndreas Gohr 158*e89aeebdSAndreas Gohr function testTitleInDoubleQuotesIsDiscarded() 159*e89aeebdSAndreas Gohr { 160*e89aeebdSAndreas Gohr // GFM allows [text](url "title") but DokuWiki's link handler 161*e89aeebdSAndreas Gohr // instructions have no title-attribute slot. The title parses 162*e89aeebdSAndreas Gohr // cleanly but is dropped; the resulting handler call is identical 163*e89aeebdSAndreas Gohr // to the no-title case. 164*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 165*e89aeebdSAndreas Gohr $this->P->parse('Foo [Google](http://google.com "Search engine") Bar'); 166*e89aeebdSAndreas Gohr $calls = [ 167*e89aeebdSAndreas Gohr ['document_start', []], 168*e89aeebdSAndreas Gohr ['p_open', []], 169*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 170*e89aeebdSAndreas Gohr ['externallink', ['http://google.com', 'Google']], 171*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 172*e89aeebdSAndreas Gohr ['p_close', []], 173*e89aeebdSAndreas Gohr ['document_end', []], 174*e89aeebdSAndreas Gohr ]; 175*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 176*e89aeebdSAndreas Gohr } 177*e89aeebdSAndreas Gohr 178*e89aeebdSAndreas Gohr function testTitleInSingleQuotesIsDiscarded() 179*e89aeebdSAndreas Gohr { 180*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 181*e89aeebdSAndreas Gohr $this->P->parse("Foo [page](target 'a title') Bar"); 182*e89aeebdSAndreas Gohr $calls = [ 183*e89aeebdSAndreas Gohr ['document_start', []], 184*e89aeebdSAndreas Gohr ['p_open', []], 185*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 186*e89aeebdSAndreas Gohr ['internallink', ['target', 'page']], 187*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 188*e89aeebdSAndreas Gohr ['p_close', []], 189*e89aeebdSAndreas Gohr ['document_end', []], 190*e89aeebdSAndreas Gohr ]; 191*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 192*e89aeebdSAndreas Gohr } 193*e89aeebdSAndreas Gohr 194*e89aeebdSAndreas Gohr function testSpaceBetweenBracketsAndParensIsNotALink() 195*e89aeebdSAndreas Gohr { 196*e89aeebdSAndreas Gohr // GFM explicitly forbids whitespace between `]` and `(`. 197*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 198*e89aeebdSAndreas Gohr $this->P->parse('[foo] (bar)'); 199*e89aeebdSAndreas Gohr $modes = array_column($this->H->calls, 0); 200*e89aeebdSAndreas Gohr $this->assertNotContains('internallink', $modes); 201*e89aeebdSAndreas Gohr $this->assertNotContains('externallink', $modes); 202*e89aeebdSAndreas Gohr } 203*e89aeebdSAndreas Gohr 204*e89aeebdSAndreas Gohr function testDwDoubleBracketNotConsumedByGfmLink() 205*e89aeebdSAndreas Gohr { 206*e89aeebdSAndreas Gohr // With both gfm_link and DW internallink loaded (mixed syntax), 207*e89aeebdSAndreas Gohr // `[[foo]]` must go to Internallink. GfmLink's `\[(?!\[)` guard 208*e89aeebdSAndreas Gohr // refuses single-bracket matches that are actually part of `[[`. 209*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 210*e89aeebdSAndreas Gohr $this->P->addMode('internallink', new Internallink()); 211*e89aeebdSAndreas Gohr $this->P->parse('Foo [[bar]] Baz'); 212*e89aeebdSAndreas Gohr $calls = [ 213*e89aeebdSAndreas Gohr ['document_start', []], 214*e89aeebdSAndreas Gohr ['p_open', []], 215*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 216*e89aeebdSAndreas Gohr ['internallink', ['bar', null]], 217*e89aeebdSAndreas Gohr ['cdata', [' Baz']], 218*e89aeebdSAndreas Gohr ['p_close', []], 219*e89aeebdSAndreas Gohr ['document_end', []], 220*e89aeebdSAndreas Gohr ]; 221*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 222*e89aeebdSAndreas Gohr } 223*e89aeebdSAndreas Gohr 224*e89aeebdSAndreas Gohr function testMultibyteLinkText() 225*e89aeebdSAndreas Gohr { 226*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 227*e89aeebdSAndreas Gohr $this->P->parse('Foo [日本語](page) Bar'); 228*e89aeebdSAndreas Gohr $calls = [ 229*e89aeebdSAndreas Gohr ['document_start', []], 230*e89aeebdSAndreas Gohr ['p_open', []], 231*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 232*e89aeebdSAndreas Gohr ['internallink', ['page', '日本語']], 233*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 234*e89aeebdSAndreas Gohr ['p_close', []], 235*e89aeebdSAndreas Gohr ['document_end', []], 236*e89aeebdSAndreas Gohr ]; 237*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 238*e89aeebdSAndreas Gohr } 239*e89aeebdSAndreas Gohr 240*e89aeebdSAndreas Gohr function testReferenceStyleLinkNotMatched() 241*e89aeebdSAndreas Gohr { 242*e89aeebdSAndreas Gohr // `[foo][bar]` (reference-style) requires a reference definition 243*e89aeebdSAndreas Gohr // we do not support; each `[...]` should stay literal text. 244*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 245*e89aeebdSAndreas Gohr $this->P->parse('[foo][bar]'); 246*e89aeebdSAndreas Gohr $modes = array_column($this->H->calls, 0); 247*e89aeebdSAndreas Gohr $this->assertNotContains('internallink', $modes); 248*e89aeebdSAndreas Gohr $this->assertNotContains('externallink', $modes); 249*e89aeebdSAndreas Gohr } 250*e89aeebdSAndreas Gohr 251*e89aeebdSAndreas Gohr function testTwoLinksInOneLine() 252*e89aeebdSAndreas Gohr { 253*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 254*e89aeebdSAndreas Gohr $this->P->parse('Foo [one](a) and [two](b) Bar'); 255*e89aeebdSAndreas Gohr $calls = [ 256*e89aeebdSAndreas Gohr ['document_start', []], 257*e89aeebdSAndreas Gohr ['p_open', []], 258*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 259*e89aeebdSAndreas Gohr ['internallink', ['a', 'one']], 260*e89aeebdSAndreas Gohr ['cdata', [' and ']], 261*e89aeebdSAndreas Gohr ['internallink', ['b', 'two']], 262*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 263*e89aeebdSAndreas Gohr ['p_close', []], 264*e89aeebdSAndreas Gohr ['document_end', []], 265*e89aeebdSAndreas Gohr ]; 266*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 267*e89aeebdSAndreas Gohr } 268*e89aeebdSAndreas Gohr 269*e89aeebdSAndreas Gohr function testFragmentInExternalUrl() 270*e89aeebdSAndreas Gohr { 271*e89aeebdSAndreas Gohr $this->P->addMode('gfm_link', new GfmLink()); 272*e89aeebdSAndreas Gohr $this->P->parse('Foo [x](http://example.com#fragment) Bar'); 273*e89aeebdSAndreas Gohr $calls = [ 274*e89aeebdSAndreas Gohr ['document_start', []], 275*e89aeebdSAndreas Gohr ['p_open', []], 276*e89aeebdSAndreas Gohr ['cdata', ["\nFoo "]], 277*e89aeebdSAndreas Gohr ['externallink', ['http://example.com#fragment', 'x']], 278*e89aeebdSAndreas Gohr ['cdata', [' Bar']], 279*e89aeebdSAndreas Gohr ['p_close', []], 280*e89aeebdSAndreas Gohr ['document_end', []], 281*e89aeebdSAndreas Gohr ]; 282*e89aeebdSAndreas Gohr $this->assertCalls($calls, $this->H->calls); 283*e89aeebdSAndreas Gohr } 284*e89aeebdSAndreas Gohr 285*e89aeebdSAndreas Gohr function testSortValue() 286*e89aeebdSAndreas Gohr { 287*e89aeebdSAndreas Gohr $this->assertSame(300, (new GfmLink())->getSort()); 288*e89aeebdSAndreas Gohr } 289*e89aeebdSAndreas Gohr} 290