1*3dabe4e0SAndreas Gohr<?php 2*3dabe4e0SAndreas Gohr 3*3dabe4e0SAndreas Gohrnamespace dokuwiki\test\Parsing\ParserMode; 4*3dabe4e0SAndreas Gohr 5*3dabe4e0SAndreas Gohruse dokuwiki\Parsing\ParserMode\GfmTable; 6*3dabe4e0SAndreas Gohr 7*3dabe4e0SAndreas Gohr/** 8*3dabe4e0SAndreas Gohr * Tests for GFM table blocks. 9*3dabe4e0SAndreas Gohr * 10*3dabe4e0SAndreas Gohr * GfmTable uses an entry/exit lexer state with allowedModes-driven inline 11*3dabe4e0SAndreas Gohr * nesting, then runs a small post-pass rewriter (Handler\GfmTable) that 12*3dabe4e0SAndreas Gohr * derives column alignment from the delimiter row, drops it, pads/truncates 13*3dabe4e0SAndreas Gohr * body rows, and emits the canonical DokuWiki table call sequence. Tests 14*3dabe4e0SAndreas Gohr * here assert against the rewriter's output, not the raw `gfm_table_*` 15*3dabe4e0SAndreas Gohr * tokens it consumes. 16*3dabe4e0SAndreas Gohr */ 17*3dabe4e0SAndreas Gohrclass GfmTableTest extends ParserTestBase 18*3dabe4e0SAndreas Gohr{ 19*3dabe4e0SAndreas Gohr public function testSort() 20*3dabe4e0SAndreas Gohr { 21*3dabe4e0SAndreas Gohr $this->assertSame(55, (new GfmTable())->getSort()); 22*3dabe4e0SAndreas Gohr } 23*3dabe4e0SAndreas Gohr 24*3dabe4e0SAndreas Gohr /** Spec example 198: basic table, no alignment, plain text. */ 25*3dabe4e0SAndreas Gohr public function testBasicTable() 26*3dabe4e0SAndreas Gohr { 27*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 28*3dabe4e0SAndreas Gohr $this->P->parse("| foo | bar |\n| --- | --- |\n| baz | bim |"); 29*3dabe4e0SAndreas Gohr 30*3dabe4e0SAndreas Gohr $expected = [ 31*3dabe4e0SAndreas Gohr ['document_start', []], 32*3dabe4e0SAndreas Gohr ['table_open', [2, 2, 1]], 33*3dabe4e0SAndreas Gohr ['tablethead_open', []], 34*3dabe4e0SAndreas Gohr ['tablerow_open', []], 35*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 36*3dabe4e0SAndreas Gohr ['cdata', ['foo']], 37*3dabe4e0SAndreas Gohr ['tableheader_close', []], 38*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 39*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 40*3dabe4e0SAndreas Gohr ['tableheader_close', []], 41*3dabe4e0SAndreas Gohr ['tablerow_close', []], 42*3dabe4e0SAndreas Gohr ['tablethead_close', []], 43*3dabe4e0SAndreas Gohr ['tabletbody_open', []], 44*3dabe4e0SAndreas Gohr ['tablerow_open', []], 45*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 46*3dabe4e0SAndreas Gohr ['cdata', ['baz']], 47*3dabe4e0SAndreas Gohr ['tablecell_close', []], 48*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 49*3dabe4e0SAndreas Gohr ['cdata', ['bim']], 50*3dabe4e0SAndreas Gohr ['tablecell_close', []], 51*3dabe4e0SAndreas Gohr ['tablerow_close', []], 52*3dabe4e0SAndreas Gohr ['tabletbody_close', []], 53*3dabe4e0SAndreas Gohr ['table_close', [42]], 54*3dabe4e0SAndreas Gohr ['document_end', []], 55*3dabe4e0SAndreas Gohr ]; 56*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 57*3dabe4e0SAndreas Gohr } 58*3dabe4e0SAndreas Gohr 59*3dabe4e0SAndreas Gohr /** Spec example 199: alignment via `:-:` and `---:`, no outer pipes. */ 60*3dabe4e0SAndreas Gohr public function testAlignment() 61*3dabe4e0SAndreas Gohr { 62*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 63*3dabe4e0SAndreas Gohr $this->P->parse("| abc | defghi |\n:-: | -----------:\nbar | baz"); 64*3dabe4e0SAndreas Gohr 65*3dabe4e0SAndreas Gohr $expected = [ 66*3dabe4e0SAndreas Gohr ['document_start', []], 67*3dabe4e0SAndreas Gohr ['table_open', [2, 2, 1]], 68*3dabe4e0SAndreas Gohr ['tablethead_open', []], 69*3dabe4e0SAndreas Gohr ['tablerow_open', []], 70*3dabe4e0SAndreas Gohr ['tableheader_open', [1, 'center', 1]], 71*3dabe4e0SAndreas Gohr ['cdata', ['abc']], 72*3dabe4e0SAndreas Gohr ['tableheader_close', []], 73*3dabe4e0SAndreas Gohr ['tableheader_open', [1, 'right', 1]], 74*3dabe4e0SAndreas Gohr ['cdata', ['defghi']], 75*3dabe4e0SAndreas Gohr ['tableheader_close', []], 76*3dabe4e0SAndreas Gohr ['tablerow_close', []], 77*3dabe4e0SAndreas Gohr ['tablethead_close', []], 78*3dabe4e0SAndreas Gohr ['tabletbody_open', []], 79*3dabe4e0SAndreas Gohr ['tablerow_open', []], 80*3dabe4e0SAndreas Gohr ['tablecell_open', [1, 'center', 1]], 81*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 82*3dabe4e0SAndreas Gohr ['tablecell_close', []], 83*3dabe4e0SAndreas Gohr ['tablecell_open', [1, 'right', 1]], 84*3dabe4e0SAndreas Gohr ['cdata', ['baz']], 85*3dabe4e0SAndreas Gohr ['tablecell_close', []], 86*3dabe4e0SAndreas Gohr ['tablerow_close', []], 87*3dabe4e0SAndreas Gohr ['tabletbody_close', []], 88*3dabe4e0SAndreas Gohr ['table_close', [46]], 89*3dabe4e0SAndreas Gohr ['document_end', []], 90*3dabe4e0SAndreas Gohr ]; 91*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 92*3dabe4e0SAndreas Gohr } 93*3dabe4e0SAndreas Gohr 94*3dabe4e0SAndreas Gohr /** Spec example 200 (partial): a backslash-escaped pipe must not split 95*3dabe4e0SAndreas Gohr * the cell. The unescape itself — turning `\|` into a literal `|` in 96*3dabe4e0SAndreas Gohr * the cell content — is GfmEscape's job and is not yet implemented; 97*3dabe4e0SAndreas Gohr * here we only assert the cell-splitting contract that GfmTable owns. */ 98*3dabe4e0SAndreas Gohr public function testEscapedPipeDoesNotSplitCell() 99*3dabe4e0SAndreas Gohr { 100*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 101*3dabe4e0SAndreas Gohr $this->P->parse("| f\\|oo |\n| ---- |"); 102*3dabe4e0SAndreas Gohr 103*3dabe4e0SAndreas Gohr // One cell, content `f\|oo` literal (escape preserved). When 104*3dabe4e0SAndreas Gohr // GfmEscape lands the same input will collapse to `f|oo` without 105*3dabe4e0SAndreas Gohr // any change here. 106*3dabe4e0SAndreas Gohr $expected = [ 107*3dabe4e0SAndreas Gohr ['document_start', []], 108*3dabe4e0SAndreas Gohr ['table_open', [1, 1, 1]], 109*3dabe4e0SAndreas Gohr ['tablethead_open', []], 110*3dabe4e0SAndreas Gohr ['tablerow_open', []], 111*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 112*3dabe4e0SAndreas Gohr ['cdata', ['f\\|oo']], 113*3dabe4e0SAndreas Gohr ['tableheader_close', []], 114*3dabe4e0SAndreas Gohr ['tablerow_close', []], 115*3dabe4e0SAndreas Gohr ['tablethead_close', []], 116*3dabe4e0SAndreas Gohr ['table_close', [19]], 117*3dabe4e0SAndreas Gohr ['document_end', []], 118*3dabe4e0SAndreas Gohr ]; 119*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 120*3dabe4e0SAndreas Gohr } 121*3dabe4e0SAndreas Gohr 122*3dabe4e0SAndreas Gohr /** Spec example 201: a blockquote line terminates the table. */ 123*3dabe4e0SAndreas Gohr public function testTerminatedByBlockquote() 124*3dabe4e0SAndreas Gohr { 125*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 126*3dabe4e0SAndreas Gohr $this->P->parse("| abc | def |\n| --- | --- |\n| bar | baz |\n> bar"); 127*3dabe4e0SAndreas Gohr 128*3dabe4e0SAndreas Gohr // Expect the table to end at the start of `> bar`; the trailing 129*3dabe4e0SAndreas Gohr // `> bar` is left as cdata (no quote mode added in this test). 130*3dabe4e0SAndreas Gohr $expected = [ 131*3dabe4e0SAndreas Gohr ['document_start', []], 132*3dabe4e0SAndreas Gohr ['table_open', [2, 2, 1]], 133*3dabe4e0SAndreas Gohr ['tablethead_open', []], 134*3dabe4e0SAndreas Gohr ['tablerow_open', []], 135*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 136*3dabe4e0SAndreas Gohr ['cdata', ['abc']], 137*3dabe4e0SAndreas Gohr ['tableheader_close', []], 138*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 139*3dabe4e0SAndreas Gohr ['cdata', ['def']], 140*3dabe4e0SAndreas Gohr ['tableheader_close', []], 141*3dabe4e0SAndreas Gohr ['tablerow_close', []], 142*3dabe4e0SAndreas Gohr ['tablethead_close', []], 143*3dabe4e0SAndreas Gohr ['tabletbody_open', []], 144*3dabe4e0SAndreas Gohr ['tablerow_open', []], 145*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 146*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 147*3dabe4e0SAndreas Gohr ['tablecell_close', []], 148*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 149*3dabe4e0SAndreas Gohr ['cdata', ['baz']], 150*3dabe4e0SAndreas Gohr ['tablecell_close', []], 151*3dabe4e0SAndreas Gohr ['tablerow_close', []], 152*3dabe4e0SAndreas Gohr ['tabletbody_close', []], 153*3dabe4e0SAndreas Gohr ['table_close', [42]], 154*3dabe4e0SAndreas Gohr ['p_open', []], 155*3dabe4e0SAndreas Gohr ['cdata', ['> bar']], 156*3dabe4e0SAndreas Gohr ['p_close', []], 157*3dabe4e0SAndreas Gohr ['document_end', []], 158*3dabe4e0SAndreas Gohr ]; 159*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 160*3dabe4e0SAndreas Gohr } 161*3dabe4e0SAndreas Gohr 162*3dabe4e0SAndreas Gohr /** Spec example 202: short body row gets padded to header column count. */ 163*3dabe4e0SAndreas Gohr public function testShortBodyRowPadded() 164*3dabe4e0SAndreas Gohr { 165*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 166*3dabe4e0SAndreas Gohr $this->P->parse("| abc | def |\n| --- | --- |\n| bar | baz |\nbar"); 167*3dabe4e0SAndreas Gohr 168*3dabe4e0SAndreas Gohr $expected = [ 169*3dabe4e0SAndreas Gohr ['document_start', []], 170*3dabe4e0SAndreas Gohr ['table_open', [2, 3, 1]], 171*3dabe4e0SAndreas Gohr ['tablethead_open', []], 172*3dabe4e0SAndreas Gohr ['tablerow_open', []], 173*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 174*3dabe4e0SAndreas Gohr ['cdata', ['abc']], 175*3dabe4e0SAndreas Gohr ['tableheader_close', []], 176*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 177*3dabe4e0SAndreas Gohr ['cdata', ['def']], 178*3dabe4e0SAndreas Gohr ['tableheader_close', []], 179*3dabe4e0SAndreas Gohr ['tablerow_close', []], 180*3dabe4e0SAndreas Gohr ['tablethead_close', []], 181*3dabe4e0SAndreas Gohr ['tabletbody_open', []], 182*3dabe4e0SAndreas Gohr ['tablerow_open', []], 183*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 184*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 185*3dabe4e0SAndreas Gohr ['tablecell_close', []], 186*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 187*3dabe4e0SAndreas Gohr ['cdata', ['baz']], 188*3dabe4e0SAndreas Gohr ['tablecell_close', []], 189*3dabe4e0SAndreas Gohr ['tablerow_close', []], 190*3dabe4e0SAndreas Gohr ['tablerow_open', []], 191*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 192*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 193*3dabe4e0SAndreas Gohr ['tablecell_close', []], 194*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 195*3dabe4e0SAndreas Gohr ['tablecell_close', []], 196*3dabe4e0SAndreas Gohr ['tablerow_close', []], 197*3dabe4e0SAndreas Gohr ['tabletbody_close', []], 198*3dabe4e0SAndreas Gohr ['table_close', [46]], 199*3dabe4e0SAndreas Gohr ['document_end', []], 200*3dabe4e0SAndreas Gohr ]; 201*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 202*3dabe4e0SAndreas Gohr } 203*3dabe4e0SAndreas Gohr 204*3dabe4e0SAndreas Gohr /** Spec example 204: long body row truncated to header column count. */ 205*3dabe4e0SAndreas Gohr public function testLongBodyRowTruncated() 206*3dabe4e0SAndreas Gohr { 207*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 208*3dabe4e0SAndreas Gohr $this->P->parse("| abc | def |\n| --- | --- |\n| bar |\n| bar | baz | boo |"); 209*3dabe4e0SAndreas Gohr 210*3dabe4e0SAndreas Gohr $expected = [ 211*3dabe4e0SAndreas Gohr ['document_start', []], 212*3dabe4e0SAndreas Gohr ['table_open', [2, 3, 1]], 213*3dabe4e0SAndreas Gohr ['tablethead_open', []], 214*3dabe4e0SAndreas Gohr ['tablerow_open', []], 215*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 216*3dabe4e0SAndreas Gohr ['cdata', ['abc']], 217*3dabe4e0SAndreas Gohr ['tableheader_close', []], 218*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 219*3dabe4e0SAndreas Gohr ['cdata', ['def']], 220*3dabe4e0SAndreas Gohr ['tableheader_close', []], 221*3dabe4e0SAndreas Gohr ['tablerow_close', []], 222*3dabe4e0SAndreas Gohr ['tablethead_close', []], 223*3dabe4e0SAndreas Gohr ['tabletbody_open', []], 224*3dabe4e0SAndreas Gohr ['tablerow_open', []], 225*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 226*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 227*3dabe4e0SAndreas Gohr ['tablecell_close', []], 228*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 229*3dabe4e0SAndreas Gohr ['tablecell_close', []], 230*3dabe4e0SAndreas Gohr ['tablerow_close', []], 231*3dabe4e0SAndreas Gohr ['tablerow_open', []], 232*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 233*3dabe4e0SAndreas Gohr ['cdata', ['bar']], 234*3dabe4e0SAndreas Gohr ['tablecell_close', []], 235*3dabe4e0SAndreas Gohr ['tablecell_open', [1, null, 1]], 236*3dabe4e0SAndreas Gohr ['cdata', ['baz']], 237*3dabe4e0SAndreas Gohr ['tablecell_close', []], 238*3dabe4e0SAndreas Gohr ['tablerow_close', []], 239*3dabe4e0SAndreas Gohr ['tabletbody_close', []], 240*3dabe4e0SAndreas Gohr ['table_close', [56]], 241*3dabe4e0SAndreas Gohr ['document_end', []], 242*3dabe4e0SAndreas Gohr ]; 243*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 244*3dabe4e0SAndreas Gohr } 245*3dabe4e0SAndreas Gohr 246*3dabe4e0SAndreas Gohr /** Spec example 203: header has 2 cells, delimiter has 1 - the regex 247*3dabe4e0SAndreas Gohr * matches but the rewriter detects the mismatch and emits cdata, which 248*3dabe4e0SAndreas Gohr * the Block rewriter wraps in a paragraph. */ 249*3dabe4e0SAndreas Gohr public function testColumnCountMismatchFallback() 250*3dabe4e0SAndreas Gohr { 251*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 252*3dabe4e0SAndreas Gohr $this->P->parse("| abc | def |\n| --- |\n| bar |"); 253*3dabe4e0SAndreas Gohr 254*3dabe4e0SAndreas Gohr $expected = [ 255*3dabe4e0SAndreas Gohr ['document_start', []], 256*3dabe4e0SAndreas Gohr ['p_open', []], 257*3dabe4e0SAndreas Gohr ['cdata', ["| abc | def |\n| --- |\n| bar |"]], 258*3dabe4e0SAndreas Gohr ['p_close', []], 259*3dabe4e0SAndreas Gohr ['document_end', []], 260*3dabe4e0SAndreas Gohr ]; 261*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 262*3dabe4e0SAndreas Gohr } 263*3dabe4e0SAndreas Gohr 264*3dabe4e0SAndreas Gohr /** Spec example 205: header + delimiter only, no body rows. */ 265*3dabe4e0SAndreas Gohr public function testEmptyBody() 266*3dabe4e0SAndreas Gohr { 267*3dabe4e0SAndreas Gohr $this->P->addMode('gfm_table', new GfmTable()); 268*3dabe4e0SAndreas Gohr $this->P->parse("| abc | def |\n| --- | --- |"); 269*3dabe4e0SAndreas Gohr 270*3dabe4e0SAndreas Gohr $expected = [ 271*3dabe4e0SAndreas Gohr ['document_start', []], 272*3dabe4e0SAndreas Gohr ['table_open', [2, 1, 1]], 273*3dabe4e0SAndreas Gohr ['tablethead_open', []], 274*3dabe4e0SAndreas Gohr ['tablerow_open', []], 275*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 276*3dabe4e0SAndreas Gohr ['cdata', ['abc']], 277*3dabe4e0SAndreas Gohr ['tableheader_close', []], 278*3dabe4e0SAndreas Gohr ['tableheader_open', [1, null, 1]], 279*3dabe4e0SAndreas Gohr ['cdata', ['def']], 280*3dabe4e0SAndreas Gohr ['tableheader_close', []], 281*3dabe4e0SAndreas Gohr ['tablerow_close', []], 282*3dabe4e0SAndreas Gohr ['tablethead_close', []], 283*3dabe4e0SAndreas Gohr ['table_close', [28]], 284*3dabe4e0SAndreas Gohr ['document_end', []], 285*3dabe4e0SAndreas Gohr ]; 286*3dabe4e0SAndreas Gohr $this->assertCalls($expected, $this->H->calls); 287*3dabe4e0SAndreas Gohr } 288*3dabe4e0SAndreas Gohr} 289