1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Console\Tests\Helper;
13
14use PHPUnit\Framework\TestCase;
15use Symfony\Component\Console\Helper\Table;
16use Symfony\Component\Console\Helper\TableCell;
17use Symfony\Component\Console\Helper\TableSeparator;
18use Symfony\Component\Console\Helper\TableStyle;
19use Symfony\Component\Console\Output\StreamOutput;
20
21class TableTest extends TestCase
22{
23    protected $stream;
24
25    protected function setUp()
26    {
27        $this->stream = fopen('php://memory', 'r+');
28    }
29
30    protected function tearDown()
31    {
32        fclose($this->stream);
33        $this->stream = null;
34    }
35
36    /**
37     * @dataProvider renderProvider
38     */
39    public function testRender($headers, $rows, $style, $expected, $decorated = false)
40    {
41        $table = new Table($output = $this->getOutputStream($decorated));
42        $table
43            ->setHeaders($headers)
44            ->setRows($rows)
45            ->setStyle($style)
46        ;
47        $table->render();
48
49        $this->assertEquals($expected, $this->getOutputContent($output));
50    }
51
52    /**
53     * @dataProvider renderProvider
54     */
55    public function testRenderAddRows($headers, $rows, $style, $expected, $decorated = false)
56    {
57        $table = new Table($output = $this->getOutputStream($decorated));
58        $table
59            ->setHeaders($headers)
60            ->addRows($rows)
61            ->setStyle($style)
62        ;
63        $table->render();
64
65        $this->assertEquals($expected, $this->getOutputContent($output));
66    }
67
68    /**
69     * @dataProvider renderProvider
70     */
71    public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $decorated = false)
72    {
73        $table = new Table($output = $this->getOutputStream($decorated));
74        $table
75            ->setHeaders($headers)
76            ->setStyle($style)
77        ;
78        foreach ($rows as $row) {
79            $table->addRow($row);
80        }
81        $table->render();
82
83        $this->assertEquals($expected, $this->getOutputContent($output));
84    }
85
86    public function renderProvider()
87    {
88        $books = [
89            ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
90            ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'],
91            ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
92            ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
93        ];
94
95        return [
96            [
97                ['ISBN', 'Title', 'Author'],
98                $books,
99                'default',
100<<<'TABLE'
101+---------------+--------------------------+------------------+
102| ISBN          | Title                    | Author           |
103+---------------+--------------------------+------------------+
104| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
105| 9971-5-0210-0 | A Tale of Two Cities     | Charles Dickens  |
106| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
107| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
108+---------------+--------------------------+------------------+
109
110TABLE
111            ],
112            [
113                ['ISBN', 'Title', 'Author'],
114                $books,
115                'compact',
116<<<'TABLE'
117 ISBN          Title                    Author
118 99921-58-10-7 Divine Comedy            Dante Alighieri
119 9971-5-0210-0 A Tale of Two Cities     Charles Dickens
120 960-425-059-0 The Lord of the Rings    J. R. R. Tolkien
121 80-902734-1-6 And Then There Were None Agatha Christie
122
123TABLE
124            ],
125            [
126                ['ISBN', 'Title', 'Author'],
127                $books,
128                'borderless',
129<<<'TABLE'
130 =============== ========================== ==================
131  ISBN            Title                      Author
132 =============== ========================== ==================
133  99921-58-10-7   Divine Comedy              Dante Alighieri
134  9971-5-0210-0   A Tale of Two Cities       Charles Dickens
135  960-425-059-0   The Lord of the Rings      J. R. R. Tolkien
136  80-902734-1-6   And Then There Were None   Agatha Christie
137 =============== ========================== ==================
138
139TABLE
140            ],
141            [
142                ['ISBN', 'Title'],
143                [
144                    ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
145                    ['9971-5-0210-0'],
146                    ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
147                    ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
148                ],
149                'default',
150<<<'TABLE'
151+---------------+--------------------------+------------------+
152| ISBN          | Title                    |                  |
153+---------------+--------------------------+------------------+
154| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
155| 9971-5-0210-0 |                          |                  |
156| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
157| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
158+---------------+--------------------------+------------------+
159
160TABLE
161            ],
162            [
163                [],
164                [
165                    ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
166                    ['9971-5-0210-0'],
167                    ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
168                    ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
169                ],
170                'default',
171<<<'TABLE'
172+---------------+--------------------------+------------------+
173| 99921-58-10-7 | Divine Comedy            | Dante Alighieri  |
174| 9971-5-0210-0 |                          |                  |
175| 960-425-059-0 | The Lord of the Rings    | J. R. R. Tolkien |
176| 80-902734-1-6 | And Then There Were None | Agatha Christie  |
177+---------------+--------------------------+------------------+
178
179TABLE
180            ],
181            [
182                ['ISBN', 'Title', 'Author'],
183                [
184                    ['99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'],
185                    ['9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."],
186                    ['9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."],
187                    ['960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"],
188                ],
189                'default',
190<<<'TABLE'
191+---------------+----------------------------+-----------------+
192| ISBN          | Title                      | Author          |
193+---------------+----------------------------+-----------------+
194| 99921-58-10-7 | Divine                     | Dante Alighieri |
195|               | Comedy                     |                 |
196| 9971-5-0210-2 | Harry Potter               | Rowling         |
197|               | and the Chamber of Secrets | Joanne K.       |
198| 9971-5-0210-2 | Harry Potter               | Rowling         |
199|               | and the Chamber of Secrets | Joanne K.       |
200| 960-425-059-0 | The Lord of the Rings      | J. R. R.        |
201|               |                            | Tolkien         |
202+---------------+----------------------------+-----------------+
203
204TABLE
205            ],
206            [
207                ['ISBN', 'Title'],
208                [],
209                'default',
210<<<'TABLE'
211+------+-------+
212| ISBN | Title |
213+------+-------+
214
215TABLE
216            ],
217            [
218                [],
219                [],
220                'default',
221                '',
222            ],
223            'Cell text with tags used for Output styling' => [
224                ['ISBN', 'Title', 'Author'],
225                [
226                    ['<info>99921-58-10-7</info>', '<error>Divine Comedy</error>', '<fg=blue;bg=white>Dante Alighieri</fg=blue;bg=white>'],
227                    ['9971-5-0210-0', 'A Tale of Two Cities', '<info>Charles Dickens</>'],
228                ],
229                'default',
230<<<'TABLE'
231+---------------+----------------------+-----------------+
232| ISBN          | Title                | Author          |
233+---------------+----------------------+-----------------+
234| 99921-58-10-7 | Divine Comedy        | Dante Alighieri |
235| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
236+---------------+----------------------+-----------------+
237
238TABLE
239            ],
240            'Cell text with tags not used for Output styling' => [
241                ['ISBN', 'Title', 'Author'],
242                [
243                    ['<strong>99921-58-10-700</strong>', '<f>Divine Com</f>', 'Dante Alighieri'],
244                    ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'],
245                ],
246                'default',
247<<<'TABLE'
248+----------------------------------+----------------------+-----------------+
249| ISBN                             | Title                | Author          |
250+----------------------------------+----------------------+-----------------+
251| <strong>99921-58-10-700</strong> | <f>Divine Com</f>    | Dante Alighieri |
252| 9971-5-0210-0                    | A Tale of Two Cities | Charles Dickens |
253+----------------------------------+----------------------+-----------------+
254
255TABLE
256            ],
257            'Cell with colspan' => [
258                ['ISBN', 'Title', 'Author'],
259                [
260                    ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
261                    new TableSeparator(),
262                    [new TableCell('Divine Comedy(Dante Alighieri)', ['colspan' => 3])],
263                    new TableSeparator(),
264                    [
265                        new TableCell('Arduino: A Quick-Start Guide', ['colspan' => 2]),
266                        'Mark Schmidt',
267                    ],
268                    new TableSeparator(),
269                    [
270                        '9971-5-0210-0',
271                        new TableCell("A Tale of \nTwo Cities", ['colspan' => 2]),
272                    ],
273                    new TableSeparator(),
274                    [
275                        new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', ['colspan' => 3]),
276                    ],
277                ],
278                'default',
279<<<'TABLE'
280+-------------------------------+-------------------------------+-----------------------------+
281| ISBN                          | Title                         | Author                      |
282+-------------------------------+-------------------------------+-----------------------------+
283| 99921-58-10-7                 | Divine Comedy                 | Dante Alighieri             |
284+-------------------------------+-------------------------------+-----------------------------+
285| Divine Comedy(Dante Alighieri)                                                              |
286+-------------------------------+-------------------------------+-----------------------------+
287| Arduino: A Quick-Start Guide                                  | Mark Schmidt                |
288+-------------------------------+-------------------------------+-----------------------------+
289| 9971-5-0210-0                 | A Tale of                                                   |
290|                               | Two Cities                                                  |
291+-------------------------------+-------------------------------+-----------------------------+
292| Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!       |
293+-------------------------------+-------------------------------+-----------------------------+
294
295TABLE
296            ],
297            'Cell with rowspan' => [
298                ['ISBN', 'Title', 'Author'],
299                [
300                    [
301                        new TableCell('9971-5-0210-0', ['rowspan' => 3]),
302                        new TableCell('Divine Comedy', ['rowspan' => 2]),
303                        'Dante Alighieri',
304                    ],
305                    [],
306                    ["The Lord of \nthe Rings", "J. R. \nR. Tolkien"],
307                    new TableSeparator(),
308                    ['80-902734-1-6', new TableCell("And Then \nThere \nWere None", ['rowspan' => 3]), 'Agatha Christie'],
309                    ['80-902734-1-7', 'Test'],
310                ],
311                'default',
312<<<'TABLE'
313+---------------+---------------+-----------------+
314| ISBN          | Title         | Author          |
315+---------------+---------------+-----------------+
316| 9971-5-0210-0 | Divine Comedy | Dante Alighieri |
317|               |               |                 |
318|               | The Lord of   | J. R.           |
319|               | the Rings     | R. Tolkien      |
320+---------------+---------------+-----------------+
321| 80-902734-1-6 | And Then      | Agatha Christie |
322| 80-902734-1-7 | There         | Test            |
323|               | Were None     |                 |
324+---------------+---------------+-----------------+
325
326TABLE
327            ],
328            'Cell with rowspan and colspan' => [
329                ['ISBN', 'Title', 'Author'],
330                [
331                    [
332                        new TableCell('9971-5-0210-0', ['rowspan' => 2, 'colspan' => 2]),
333                        'Dante Alighieri',
334                    ],
335                    ['Charles Dickens'],
336                    new TableSeparator(),
337                    [
338                        'Dante Alighieri',
339                        new TableCell('9971-5-0210-0', ['rowspan' => 3, 'colspan' => 2]),
340                    ],
341                    ['J. R. R. Tolkien'],
342                    ['J. R. R'],
343                ],
344                'default',
345<<<'TABLE'
346+------------------+---------+-----------------+
347| ISBN             | Title   | Author          |
348+------------------+---------+-----------------+
349| 9971-5-0210-0              | Dante Alighieri |
350|                            | Charles Dickens |
351+------------------+---------+-----------------+
352| Dante Alighieri  | 9971-5-0210-0             |
353| J. R. R. Tolkien |                           |
354| J. R. R          |                           |
355+------------------+---------+-----------------+
356
357TABLE
358            ],
359            'Cell with rowspan and colspan contains new line break' => [
360                ['ISBN', 'Title', 'Author'],
361                [
362                    [
363                        new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
364                        'Dante Alighieri',
365                    ],
366                    ['Charles Dickens'],
367                    new TableSeparator(),
368                    [
369                        'Dante Alighieri',
370                        new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
371                    ],
372                    ['Charles Dickens'],
373                    new TableSeparator(),
374                    [
375                        new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
376                        new TableCell("Dante \nAlighieri", ['rowspan' => 2, 'colspan' => 1]),
377                    ],
378                ],
379                'default',
380<<<'TABLE'
381+-----------------+-------+-----------------+
382| ISBN            | Title | Author          |
383+-----------------+-------+-----------------+
384| 9971                    | Dante Alighieri |
385| -5-                     | Charles Dickens |
386| 021                     |                 |
387| 0-0                     |                 |
388+-----------------+-------+-----------------+
389| Dante Alighieri | 9971                    |
390| Charles Dickens | -5-                     |
391|                 | 021                     |
392|                 | 0-0                     |
393+-----------------+-------+-----------------+
394| 9971                    | Dante           |
395| -5-                     | Alighieri       |
396| 021                     |                 |
397| 0-0                     |                 |
398+-----------------+-------+-----------------+
399
400TABLE
401            ],
402            'Cell with rowspan and colspan without using TableSeparator' => [
403                ['ISBN', 'Title', 'Author'],
404                [
405                    [
406                        new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
407                        'Dante Alighieri',
408                    ],
409                    ['Charles Dickens'],
410                    [
411                        'Dante Alighieri',
412                        new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
413                    ],
414                    ['Charles Dickens'],
415                ],
416                'default',
417<<<'TABLE'
418+-----------------+-------+-----------------+
419| ISBN            | Title | Author          |
420+-----------------+-------+-----------------+
421| 9971                    | Dante Alighieri |
422| -5-                     | Charles Dickens |
423| 021                     |                 |
424| 0-0                     |                 |
425| Dante Alighieri | 9971                    |
426| Charles Dickens | -5-                     |
427|                 | 021                     |
428|                 | 0-0                     |
429+-----------------+-------+-----------------+
430
431TABLE
432            ],
433            'Cell with rowspan and colspan with separator inside a rowspan' => [
434                ['ISBN', 'Author'],
435                [
436                    [
437                        new TableCell('9971-5-0210-0', ['rowspan' => 3, 'colspan' => 1]),
438                        'Dante Alighieri',
439                    ],
440                    [new TableSeparator()],
441                    ['Charles Dickens'],
442                ],
443                'default',
444<<<'TABLE'
445+---------------+-----------------+
446| ISBN          | Author          |
447+---------------+-----------------+
448| 9971-5-0210-0 | Dante Alighieri |
449|               |-----------------|
450|               | Charles Dickens |
451+---------------+-----------------+
452
453TABLE
454            ],
455            'Multiple header lines' => [
456                [
457                    [new TableCell('Main title', ['colspan' => 3])],
458                    ['ISBN', 'Title', 'Author'],
459                ],
460                [],
461                'default',
462<<<'TABLE'
463+------+-------+--------+
464| Main title            |
465+------+-------+--------+
466| ISBN | Title | Author |
467+------+-------+--------+
468
469TABLE
470            ],
471            'Row with multiple cells' => [
472                [],
473                [
474                    [
475                        new TableCell('1', ['colspan' => 3]),
476                        new TableCell('2', ['colspan' => 2]),
477                        new TableCell('3', ['colspan' => 2]),
478                        new TableCell('4', ['colspan' => 2]),
479                    ],
480        ],
481                'default',
482<<<'TABLE'
483+---+--+--+---+--+---+--+---+--+
484| 1       | 2    | 3    | 4    |
485+---+--+--+---+--+---+--+---+--+
486
487TABLE
488            ],
489            'Coslpan and table cells with comment style' => [
490                [
491                    new TableCell('<comment>Long Title</comment>', ['colspan' => 3]),
492                ],
493                [
494                    [
495                        new TableCell('9971-5-0210-0', ['colspan' => 3]),
496                    ],
497                    new TableSeparator(),
498                    [
499                        'Dante Alighieri',
500                        'J. R. R. Tolkien',
501                        'J. R. R',
502                    ],
503                ],
504                'default',
505                <<<TABLE
506+-----------------+------------------+---------+
507|\033[32m \033[39m\033[33mLong Title\033[39m\033[32m                                   \033[39m|
508+-----------------+------------------+---------+
509| 9971-5-0210-0                                |
510+-----------------+------------------+---------+
511| Dante Alighieri | J. R. R. Tolkien | J. R. R |
512+-----------------+------------------+---------+
513
514TABLE
515            ,
516                true,
517            ],
518            'Row with formatted cells containing a newline' => [
519                [],
520                [
521                    [
522                        new TableCell('<error>Dont break'."\n".'here</error>', ['colspan' => 2]),
523                    ],
524                    new TableSeparator(),
525                    [
526                        'foo',
527                         new TableCell('<error>Dont break'."\n".'here</error>', ['rowspan' => 2]),
528                    ],
529                    [
530                        'bar',
531                    ],
532                ],
533                'default',
534                <<<'TABLE'
535+-------+------------+
536| Dont break         |
537| here               |
538+-------+------------+
539| foo   | Dont break |
540| bar   | here       |
541+-------+------------+
542
543TABLE
544            ,
545                true,
546            ],
547        ];
548    }
549
550    public function testRenderMultiByte()
551    {
552        $table = new Table($output = $this->getOutputStream());
553        $table
554            ->setHeaders(['■■'])
555            ->setRows([[1234]])
556            ->setStyle('default')
557        ;
558        $table->render();
559
560        $expected =
561<<<'TABLE'
562+------+
563| ■■   |
564+------+
565| 1234 |
566+------+
567
568TABLE;
569
570        $this->assertEquals($expected, $this->getOutputContent($output));
571    }
572
573    public function testTableCellWithNumericIntValue()
574    {
575        $table = new Table($output = $this->getOutputStream());
576
577        $table->setRows([[new TableCell(12345)]]);
578        $table->render();
579
580        $expected =
581<<<'TABLE'
582+-------+
583| 12345 |
584+-------+
585
586TABLE;
587
588        $this->assertEquals($expected, $this->getOutputContent($output));
589    }
590
591    public function testTableCellWithNumericFloatValue()
592    {
593        $table = new Table($output = $this->getOutputStream());
594
595        $table->setRows([[new TableCell(12345.01)]]);
596        $table->render();
597
598        $expected =
599<<<'TABLE'
600+----------+
601| 12345.01 |
602+----------+
603
604TABLE;
605
606        $this->assertEquals($expected, $this->getOutputContent($output));
607    }
608
609    public function testStyle()
610    {
611        $style = new TableStyle();
612        $style
613            ->setHorizontalBorderChar('.')
614            ->setVerticalBorderChar('.')
615            ->setCrossingChar('.')
616        ;
617
618        Table::setStyleDefinition('dotfull', $style);
619        $table = new Table($output = $this->getOutputStream());
620        $table
621            ->setHeaders(['Foo'])
622            ->setRows([['Bar']])
623            ->setStyle('dotfull');
624        $table->render();
625
626        $expected =
627<<<'TABLE'
628.......
629. Foo .
630.......
631. Bar .
632.......
633
634TABLE;
635
636        $this->assertEquals($expected, $this->getOutputContent($output));
637    }
638
639    public function testRowSeparator()
640    {
641        $table = new Table($output = $this->getOutputStream());
642        $table
643            ->setHeaders(['Foo'])
644            ->setRows([
645                ['Bar1'],
646                new TableSeparator(),
647                ['Bar2'],
648                new TableSeparator(),
649                ['Bar3'],
650            ]);
651        $table->render();
652
653        $expected =
654<<<'TABLE'
655+------+
656| Foo  |
657+------+
658| Bar1 |
659+------+
660| Bar2 |
661+------+
662| Bar3 |
663+------+
664
665TABLE;
666
667        $this->assertEquals($expected, $this->getOutputContent($output));
668
669        $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works');
670    }
671
672    public function testRenderMultiCalls()
673    {
674        $table = new Table($output = $this->getOutputStream());
675        $table->setRows([
676            [new TableCell('foo', ['colspan' => 2])],
677        ]);
678        $table->render();
679        $table->render();
680        $table->render();
681
682        $expected =
683<<<TABLE
684+----+---+
685| foo    |
686+----+---+
687+----+---+
688| foo    |
689+----+---+
690+----+---+
691| foo    |
692+----+---+
693
694TABLE;
695
696        $this->assertEquals($expected, $this->getOutputContent($output));
697    }
698
699    public function testColumnStyle()
700    {
701        $table = new Table($output = $this->getOutputStream());
702        $table
703            ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
704            ->setRows([
705                ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
706                ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'],
707            ]);
708
709        $style = new TableStyle();
710        $style->setPadType(\STR_PAD_LEFT);
711        $table->setColumnStyle(3, $style);
712
713        $table->render();
714
715        $expected =
716            <<<TABLE
717+---------------+----------------------+-----------------+--------+
718| ISBN          | Title                | Author          |  Price |
719+---------------+----------------------+-----------------+--------+
720| 99921-58-10-7 | Divine Comedy        | Dante Alighieri |   9.95 |
721| 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
722+---------------+----------------------+-----------------+--------+
723
724TABLE;
725
726        $this->assertEquals($expected, $this->getOutputContent($output));
727    }
728
729    public function testThrowsWhenTheCellInAnArray()
730    {
731        $this->expectException('Symfony\Component\Console\Exception\InvalidArgumentException');
732        $this->expectExceptionMessage('A cell must be a TableCell, a scalar or an object implementing "__toString()", "array" given.');
733        $table = new Table($output = $this->getOutputStream());
734        $table
735            ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
736            ->setRows([
737                ['99921-58-10-7', [], 'Dante Alighieri', '9.95'],
738            ]);
739
740        $table->render();
741    }
742
743    public function testColumnWidth()
744    {
745        $table = new Table($output = $this->getOutputStream());
746        $table
747            ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
748            ->setRows([
749                ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
750                ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'],
751            ])
752            ->setColumnWidth(0, 15)
753            ->setColumnWidth(3, 10);
754
755        $style = new TableStyle();
756        $style->setPadType(\STR_PAD_LEFT);
757        $table->setColumnStyle(3, $style);
758
759        $table->render();
760
761        $expected =
762            <<<TABLE
763+-----------------+----------------------+-----------------+------------+
764| ISBN            | Title                | Author          |      Price |
765+-----------------+----------------------+-----------------+------------+
766| 99921-58-10-7   | Divine Comedy        | Dante Alighieri |       9.95 |
767| 9971-5-0210-0   | A Tale of Two Cities | Charles Dickens |     139.25 |
768+-----------------+----------------------+-----------------+------------+
769
770TABLE;
771
772        $this->assertEquals($expected, $this->getOutputContent($output));
773    }
774
775    public function testColumnWidths()
776    {
777        $table = new Table($output = $this->getOutputStream());
778        $table
779            ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
780            ->setRows([
781                ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
782                ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'],
783            ])
784            ->setColumnWidths([15, 0, -1, 10]);
785
786        $style = new TableStyle();
787        $style->setPadType(\STR_PAD_LEFT);
788        $table->setColumnStyle(3, $style);
789
790        $table->render();
791
792        $expected =
793            <<<TABLE
794+-----------------+----------------------+-----------------+------------+
795| ISBN            | Title                | Author          |      Price |
796+-----------------+----------------------+-----------------+------------+
797| 99921-58-10-7   | Divine Comedy        | Dante Alighieri |       9.95 |
798| 9971-5-0210-0   | A Tale of Two Cities | Charles Dickens |     139.25 |
799+-----------------+----------------------+-----------------+------------+
800
801TABLE;
802
803        $this->assertEquals($expected, $this->getOutputContent($output));
804    }
805
806    public function testIsNotDefinedStyleException()
807    {
808        $this->expectException('Symfony\Component\Console\Exception\InvalidArgumentException');
809        $this->expectExceptionMessage('Style "absent" is not defined.');
810        $table = new Table($this->getOutputStream());
811        $table->setStyle('absent');
812    }
813
814    public function testGetStyleDefinition()
815    {
816        $this->expectException('Symfony\Component\Console\Exception\InvalidArgumentException');
817        $this->expectExceptionMessage('Style "absent" is not defined.');
818        Table::getStyleDefinition('absent');
819    }
820
821    public function testBoxedStyleWithColspan()
822    {
823        $boxed = new TableStyle();
824        $boxed
825            ->setHorizontalBorderChar('─')
826            ->setVerticalBorderChar('│')
827            ->setCrossingChar('┼')
828        ;
829
830        $table = new Table($output = $this->getOutputStream());
831        $table->setStyle($boxed);
832        $table
833            ->setHeaders(['ISBN', 'Title', 'Author'])
834            ->setRows([
835                ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
836                new TableSeparator(),
837                [new TableCell('This value spans 3 columns.', ['colspan' => 3])],
838            ])
839        ;
840        $table->render();
841
842        $expected =
843            <<<TABLE
844┼───────────────┼───────────────┼─────────────────┼
845│ ISBN          │ Title         │ Author          │
846┼───────────────┼───────────────┼─────────────────┼
847│ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri │
848┼───────────────┼───────────────┼─────────────────┼
849│ This value spans 3 columns.                     │
850┼───────────────┼───────────────┼─────────────────┼
851
852TABLE;
853
854        $this->assertSame($expected, $this->getOutputContent($output));
855    }
856
857    protected function getOutputStream($decorated = false)
858    {
859        return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, $decorated);
860    }
861
862    protected function getOutputContent(StreamOutput $output)
863    {
864        rewind($output->getStream());
865
866        return str_replace(\PHP_EOL, "\n", stream_get_contents($output->getStream()));
867    }
868}
869