xref: /dokuwiki/_test/tests/Parsing/ParserMode/GfmTableTest.php (revision 3dabe4e0a0d70b79a7aced8ac8a36d4b37a61024)
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