4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Console\Tests\Helper;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Console\Helper\Table;
16 use Symfony\Component\Console\Helper\TableStyle;
17 use Symfony\Component\Console\Helper\TableSeparator;
18 use Symfony\Component\Console\Helper\TableCell;
19 use Symfony\Component\Console\Output\StreamOutput;
21 class TableTest extends TestCase
25 protected function setUp()
27 $this->stream = fopen('php://memory', 'r+');
30 protected function tearDown()
32 fclose($this->stream);
37 * @dataProvider renderProvider
39 public function testRender($headers, $rows, $style, $expected, $decorated = false)
41 $table = new Table($output = $this->getOutputStream($decorated));
43 ->setHeaders($headers)
49 $this->assertEquals($expected, $this->getOutputContent($output));
53 * @dataProvider renderProvider
55 public function testRenderAddRows($headers, $rows, $style, $expected, $decorated = false)
57 $table = new Table($output = $this->getOutputStream($decorated));
59 ->setHeaders($headers)
65 $this->assertEquals($expected, $this->getOutputContent($output));
69 * @dataProvider renderProvider
71 public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $decorated = false)
73 $table = new Table($output = $this->getOutputStream($decorated));
75 ->setHeaders($headers)
78 foreach ($rows as $row) {
83 $this->assertEquals($expected, $this->getOutputContent($output));
86 public function renderProvider()
89 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
90 array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
91 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
92 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
97 array('ISBN', 'Title', 'Author'),
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 +---------------+--------------------------+------------------+
113 array('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
126 array('ISBN', 'Title', 'Author'),
130 =============== ========================== ==================
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 =============== ========================== ==================
142 array('ISBN', 'Title'),
144 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
145 array('9971-5-0210-0'),
146 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
147 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
151 +---------------+--------------------------+------------------+
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 +---------------+--------------------------+------------------+
165 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
166 array('9971-5-0210-0'),
167 array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'),
168 array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'),
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 +---------------+--------------------------+------------------+
182 array('ISBN', 'Title', 'Author'),
184 array('99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'),
185 array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."),
186 array('9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."),
187 array('960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"),
191 +---------------+----------------------------+-----------------+
192 | ISBN | Title | Author |
193 +---------------+----------------------------+-----------------+
194 | 99921-58-10-7 | Divine | Dante Alighieri |
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. |
202 +---------------+----------------------------+-----------------+
207 array('ISBN', 'Title'),
223 'Cell text with tags used for Output styling' => array(
224 array('ISBN', 'Title', 'Author'),
226 array('<info>99921-58-10-7</info>', '<error>Divine Comedy</error>', '<fg=blue;bg=white>Dante Alighieri</fg=blue;bg=white>'),
227 array('9971-5-0210-0', 'A Tale of Two Cities', '<info>Charles Dickens</>'),
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 +---------------+----------------------+-----------------+
240 'Cell text with tags not used for Output styling' => array(
241 array('ISBN', 'Title', 'Author'),
243 array('<strong>99921-58-10-700</strong>', '<f>Divine Com</f>', 'Dante Alighieri'),
244 array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'),
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 +----------------------------------+----------------------+-----------------+
257 'Cell with colspan' => array(
258 array('ISBN', 'Title', 'Author'),
260 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'),
261 new TableSeparator(),
262 array(new TableCell('Divine Comedy(Dante Alighieri)', array('colspan' => 3))),
263 new TableSeparator(),
265 new TableCell('Arduino: A Quick-Start Guide', array('colspan' => 2)),
268 new TableSeparator(),
271 new TableCell("A Tale of \nTwo Cities", array('colspan' => 2)),
273 new TableSeparator(),
275 new TableCell('Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil!', array('colspan' => 3)),
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 |
291 +-------------------------------+-------------------------------+-----------------------------+
292 | Cupiditate dicta atque porro, tempora exercitationem modi animi nulla nemo vel nihil! |
293 +-------------------------------+-------------------------------+-----------------------------+
297 'Cell with rowspan' => array(
298 array('ISBN', 'Title', 'Author'),
301 new TableCell('9971-5-0210-0', array('rowspan' => 3)),
302 new TableCell('Divine Comedy', array('rowspan' => 2)),
306 array("The Lord of \nthe Rings", "J. R. \nR. Tolkien"),
307 new TableSeparator(),
308 array('80-902734-1-6', new TableCell("And Then \nThere \nWere None", array('rowspan' => 3)), 'Agatha Christie'),
309 array('80-902734-1-7', 'Test'),
313 +---------------+---------------+-----------------+
314 | ISBN | Title | Author |
315 +---------------+---------------+-----------------+
316 | 9971-5-0210-0 | Divine Comedy | Dante Alighieri |
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 |
324 +---------------+---------------+-----------------+
328 'Cell with rowspan and colspan' => array(
329 array('ISBN', 'Title', 'Author'),
332 new TableCell('9971-5-0210-0', array('rowspan' => 2, 'colspan' => 2)),
335 array('Charles Dickens'),
336 new TableSeparator(),
339 new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 2)),
341 array('J. R. R. Tolkien'),
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 | |
355 +------------------+---------+-----------------+
359 'Cell with rowspan and colspan contains new line break' => array(
360 array('ISBN', 'Title', 'Author'),
363 new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
366 array('Charles Dickens'),
367 new TableSeparator(),
370 new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
372 array('Charles Dickens'),
373 new TableSeparator(),
375 new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
376 new TableCell("Dante \nAlighieri", array('rowspan' => 2, 'colspan' => 1)),
381 +-----------------+-------+-----------------+
382 | ISBN | Title | Author |
383 +-----------------+-------+-----------------+
384 | 9971 | Dante Alighieri |
385 | -5- | Charles Dickens |
388 +-----------------+-------+-----------------+
389 | Dante Alighieri | 9971 |
390 | Charles Dickens | -5- |
393 +-----------------+-------+-----------------+
398 +-----------------+-------+-----------------+
402 'Cell with rowspan and colspan without using TableSeparator' => array(
403 array('ISBN', 'Title', 'Author'),
406 new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
409 array('Charles Dickens'),
412 new TableCell("9971\n-5-\n021\n0-0", array('rowspan' => 2, 'colspan' => 2)),
414 array('Charles Dickens'),
418 +-----------------+-------+-----------------+
419 | ISBN | Title | Author |
420 +-----------------+-------+-----------------+
421 | 9971 | Dante Alighieri |
422 | -5- | Charles Dickens |
425 | Dante Alighieri | 9971 |
426 | Charles Dickens | -5- |
429 +-----------------+-------+-----------------+
433 'Cell with rowspan and colspan with separator inside a rowspan' => array(
434 array('ISBN', 'Author'),
437 new TableCell('9971-5-0210-0', array('rowspan' => 3, 'colspan' => 1)),
440 array(new TableSeparator()),
441 array('Charles Dickens'),
445 +---------------+-----------------+
447 +---------------+-----------------+
448 | 9971-5-0210-0 | Dante Alighieri |
449 | |-----------------|
450 | | Charles Dickens |
451 +---------------+-----------------+
455 'Multiple header lines' => array(
457 array(new TableCell('Main title', array('colspan' => 3))),
458 array('ISBN', 'Title', 'Author'),
463 +------+-------+--------+
465 +------+-------+--------+
466 | ISBN | Title | Author |
467 +------+-------+--------+
471 'Row with multiple cells' => array(
475 new TableCell('1', array('colspan' => 3)),
476 new TableCell('2', array('colspan' => 2)),
477 new TableCell('3', array('colspan' => 2)),
478 new TableCell('4', array('colspan' => 2)),
483 +---+--+--+---+--+---+--+---+--+
485 +---+--+--+---+--+---+--+---+--+
489 'Coslpan and table cells with comment style' => array(
491 new TableCell('<comment>Long Title</comment>', array('colspan' => 3)),
495 new TableCell('9971-5-0210-0', array('colspan' => 3)),
497 new TableSeparator(),
506 +-----------------+------------------+---------+
507 |\033[32m \033[39m\033[33mLong Title\033[39m\033[32m \033[39m|
508 +-----------------+------------------+---------+
510 +-----------------+------------------+---------+
511 | Dante Alighieri | J. R. R. Tolkien | J. R. R |
512 +-----------------+------------------+---------+
518 'Row with formatted cells containing a newline' => array(
522 new TableCell('<error>Dont break'."\n".'here</error>', array('colspan' => 2)),
524 new TableSeparator(),
527 new TableCell('<error>Dont break'."\n".'here</error>', array('rowspan' => 2)),
535 +-------+------------+
536 \e[39;49m|
\e[39;49m
\e[37;41mDont break
\e[39;49m
\e[39;49m |
\e[39;49m
537 \e[39;49m|
\e[39;49m
\e[37;41mhere
\e[39;49m |
538 +-------+------------+
539 \e[39;49m| foo |
\e[39;49m
\e[37;41mDont break
\e[39;49m
\e[39;49m |
\e[39;49m
540 \e[39;49m| bar |
\e[39;49m
\e[37;41mhere
\e[39;49m |
541 +-------+------------+
550 public function testRenderMultiByte()
552 $table = new Table($output = $this->getOutputStream());
554 ->setHeaders(array('■■'))
555 ->setRows(array(array(1234)))
556 ->setStyle('default')
570 $this->assertEquals($expected, $this->getOutputContent($output));
573 public function testTableCellWithNumericIntValue()
575 $table = new Table($output = $this->getOutputStream());
577 $table->setRows(array(array(new TableCell(12345))));
588 $this->assertEquals($expected, $this->getOutputContent($output));
591 public function testTableCellWithNumericFloatValue()
593 $table = new Table($output = $this->getOutputStream());
595 $table->setRows(array(array(new TableCell(12345.01))));
606 $this->assertEquals($expected, $this->getOutputContent($output));
609 public function testStyle()
611 $style = new TableStyle();
613 ->setHorizontalBorderChar('.')
614 ->setVerticalBorderChar('.')
615 ->setCrossingChar('.')
618 Table::setStyleDefinition('dotfull', $style);
619 $table = new Table($output = $this->getOutputStream());
621 ->setHeaders(array('Foo'))
622 ->setRows(array(array('Bar')))
623 ->setStyle('dotfull');
636 $this->assertEquals($expected, $this->getOutputContent($output));
639 public function testRowSeparator()
641 $table = new Table($output = $this->getOutputStream());
643 ->setHeaders(array('Foo'))
646 new TableSeparator(),
648 new TableSeparator(),
667 $this->assertEquals($expected, $this->getOutputContent($output));
669 $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works');
672 public function testRenderMultiCalls()
674 $table = new Table($output = $this->getOutputStream());
675 $table->setRows(array(
676 array(new TableCell('foo', array('colspan' => 2))),
696 $this->assertEquals($expected, $this->getOutputContent($output));
699 public function testColumnStyle()
701 $table = new Table($output = $this->getOutputStream());
703 ->setHeaders(array('ISBN', 'Title', 'Author', 'Price'))
705 array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'),
706 array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'),
709 $style = new TableStyle();
710 $style->setPadType(STR_PAD_LEFT);
711 $table->setColumnStyle(3, $style);
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 +---------------+----------------------+-----------------+--------+
726 $this->assertEquals($expected, $this->getOutputContent($output));
730 * @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
731 * @expectedExceptionMessage Style "absent" is not defined.
733 public function testIsNotDefinedStyleException()
735 $table = new Table($this->getOutputStream());
736 $table->setStyle('absent');
740 * @expectedException \Symfony\Component\Console\Exception\InvalidArgumentException
741 * @expectedExceptionMessage Style "absent" is not defined.
743 public function testGetStyleDefinition()
745 Table::getStyleDefinition('absent');
748 protected function getOutputStream($decorated = false)
750 return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, $decorated);
753 protected function getOutputContent(StreamOutput $output)
755 rewind($output->getStream());
757 return str_replace(PHP_EOL, "\n", stream_get_contents($output->getStream()));