TableTest.php 30 KB

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