TableTest.php 38 KB

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