TableTest.php 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631
  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\Exception\InvalidArgumentException;
  13. use Symfony\Component\Console\Exception\RuntimeException;
  14. use Symfony\Component\Console\Formatter\OutputFormatter;
  15. use Symfony\Component\Console\Helper\Table;
  16. use Symfony\Component\Console\Helper\TableCell;
  17. use Symfony\Component\Console\Helper\TableCellStyle;
  18. use Symfony\Component\Console\Helper\TableSeparator;
  19. use Symfony\Component\Console\Helper\TableStyle;
  20. use Symfony\Component\Console\Output\ConsoleSectionOutput;
  21. use Symfony\Component\Console\Output\StreamOutput;
  22. class TableTest extends TestCase
  23. {
  24. protected $stream;
  25. protected function setUp(): void
  26. {
  27. $this->stream = fopen('php://memory', 'r+');
  28. }
  29. protected function tearDown(): void
  30. {
  31. fclose($this->stream);
  32. $this->stream = null;
  33. }
  34. /**
  35. * @dataProvider renderProvider
  36. */
  37. public function testRender($headers, $rows, $style, $expected, $decorated = false)
  38. {
  39. $table = new Table($output = $this->getOutputStream($decorated));
  40. $table
  41. ->setHeaders($headers)
  42. ->setRows($rows)
  43. ->setStyle($style)
  44. ;
  45. $table->render();
  46. $this->assertEquals($expected, $this->getOutputContent($output));
  47. }
  48. /**
  49. * @dataProvider renderProvider
  50. */
  51. public function testRenderAddRows($headers, $rows, $style, $expected, $decorated = false)
  52. {
  53. $table = new Table($output = $this->getOutputStream($decorated));
  54. $table
  55. ->setHeaders($headers)
  56. ->addRows($rows)
  57. ->setStyle($style)
  58. ;
  59. $table->render();
  60. $this->assertEquals($expected, $this->getOutputContent($output));
  61. }
  62. /**
  63. * @dataProvider renderProvider
  64. */
  65. public function testRenderAddRowsOneByOne($headers, $rows, $style, $expected, $decorated = false)
  66. {
  67. $table = new Table($output = $this->getOutputStream($decorated));
  68. $table
  69. ->setHeaders($headers)
  70. ->setStyle($style)
  71. ;
  72. foreach ($rows as $row) {
  73. $table->addRow($row);
  74. }
  75. $table->render();
  76. $this->assertEquals($expected, $this->getOutputContent($output));
  77. }
  78. public static function renderProvider()
  79. {
  80. $books = [
  81. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  82. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'],
  83. ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
  84. ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
  85. ];
  86. return [
  87. [
  88. ['ISBN', 'Title', 'Author'],
  89. $books,
  90. 'default',
  91. <<<'TABLE'
  92. +---------------+--------------------------+------------------+
  93. | ISBN | Title | Author |
  94. +---------------+--------------------------+------------------+
  95. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  96. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
  97. | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  98. | 80-902734-1-6 | And Then There Were None | Agatha Christie |
  99. +---------------+--------------------------+------------------+
  100. TABLE
  101. ],
  102. [
  103. ['ISBN', 'Title', 'Author'],
  104. $books,
  105. 'compact',
  106. implode("\n", [
  107. 'ISBN Title Author ',
  108. '99921-58-10-7 Divine Comedy Dante Alighieri ',
  109. '9971-5-0210-0 A Tale of Two Cities Charles Dickens ',
  110. '960-425-059-0 The Lord of the Rings J. R. R. Tolkien ',
  111. '80-902734-1-6 And Then There Were None Agatha Christie ',
  112. '',
  113. ]),
  114. ],
  115. [
  116. ['ISBN', 'Title', 'Author'],
  117. $books,
  118. 'borderless',
  119. implode("\n", [
  120. ' =============== ========================== ================== ',
  121. ' ISBN Title Author ',
  122. ' =============== ========================== ================== ',
  123. ' 99921-58-10-7 Divine Comedy Dante Alighieri ',
  124. ' 9971-5-0210-0 A Tale of Two Cities Charles Dickens ',
  125. ' 960-425-059-0 The Lord of the Rings J. R. R. Tolkien ',
  126. ' 80-902734-1-6 And Then There Were None Agatha Christie ',
  127. ' =============== ========================== ================== ',
  128. '',
  129. ]),
  130. ],
  131. [
  132. ['ISBN', 'Title', 'Author'],
  133. $books,
  134. 'box',
  135. <<<'TABLE'
  136. ┌───────────────┬──────────────────────────┬──────────────────┐
  137. │ ISBN │ Title │ Author │
  138. ├───────────────┼──────────────────────────┼──────────────────┤
  139. │ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri │
  140. │ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens │
  141. │ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien │
  142. │ 80-902734-1-6 │ And Then There Were None │ Agatha Christie │
  143. └───────────────┴──────────────────────────┴──────────────────┘
  144. TABLE
  145. ],
  146. [
  147. ['ISBN', 'Title', 'Author'],
  148. [
  149. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  150. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'],
  151. new TableSeparator(),
  152. ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
  153. ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
  154. ],
  155. 'box-double',
  156. <<<'TABLE'
  157. ╔═══════════════╤══════════════════════════╤══════════════════╗
  158. ║ ISBN │ Title │ Author ║
  159. ╠═══════════════╪══════════════════════════╪══════════════════╣
  160. ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║
  161. ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║
  162. ╟───────────────┼──────────────────────────┼──────────────────╢
  163. ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║
  164. ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
  165. ╚═══════════════╧══════════════════════════╧══════════════════╝
  166. TABLE
  167. ],
  168. [
  169. ['ISBN', 'Title'],
  170. [
  171. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  172. ['9971-5-0210-0'],
  173. ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
  174. ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
  175. ],
  176. 'default',
  177. <<<'TABLE'
  178. +---------------+--------------------------+------------------+
  179. | ISBN | Title | |
  180. +---------------+--------------------------+------------------+
  181. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  182. | 9971-5-0210-0 | | |
  183. | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  184. | 80-902734-1-6 | And Then There Were None | Agatha Christie |
  185. +---------------+--------------------------+------------------+
  186. TABLE
  187. ],
  188. [
  189. [],
  190. [
  191. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  192. ['9971-5-0210-0'],
  193. ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
  194. ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
  195. ],
  196. 'default',
  197. <<<'TABLE'
  198. +---------------+--------------------------+------------------+
  199. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  200. | 9971-5-0210-0 | | |
  201. | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  202. | 80-902734-1-6 | And Then There Were None | Agatha Christie |
  203. +---------------+--------------------------+------------------+
  204. TABLE
  205. ],
  206. [
  207. ['ISBN', 'Title', 'Author'],
  208. [
  209. ['99921-58-10-7', "Divine\nComedy", 'Dante Alighieri'],
  210. ['9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."],
  211. ['9971-5-0210-2', "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."],
  212. ['960-425-059-0', 'The Lord of the Rings', "J. R. R.\nTolkien"],
  213. ],
  214. 'default',
  215. <<<'TABLE'
  216. +---------------+----------------------------+-----------------+
  217. | ISBN | Title | Author |
  218. +---------------+----------------------------+-----------------+
  219. | 99921-58-10-7 | Divine | Dante Alighieri |
  220. | | Comedy | |
  221. | 9971-5-0210-2 | Harry Potter | Rowling |
  222. | | and the Chamber of Secrets | Joanne K. |
  223. | 9971-5-0210-2 | Harry Potter | Rowling |
  224. | | and the Chamber of Secrets | Joanne K. |
  225. | 960-425-059-0 | The Lord of the Rings | J. R. R. |
  226. | | | Tolkien |
  227. +---------------+----------------------------+-----------------+
  228. TABLE
  229. ],
  230. [
  231. ['ISBN', 'Title'],
  232. [],
  233. 'default',
  234. <<<'TABLE'
  235. +------+-------+
  236. | ISBN | Title |
  237. +------+-------+
  238. TABLE
  239. ],
  240. [
  241. [],
  242. [],
  243. 'default',
  244. '',
  245. ],
  246. 'Cell text with tags used for Output styling' => [
  247. ['ISBN', 'Title', 'Author'],
  248. [
  249. ['<info>99921-58-10-7</info>', '<error>Divine Comedy</error>', '<fg=blue;bg=white>Dante Alighieri</fg=blue;bg=white>'],
  250. ['9971-5-0210-0', 'A Tale of Two Cities', '<info>Charles Dickens</>'],
  251. ],
  252. 'default',
  253. <<<'TABLE'
  254. +---------------+----------------------+-----------------+
  255. | ISBN | Title | Author |
  256. +---------------+----------------------+-----------------+
  257. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  258. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
  259. +---------------+----------------------+-----------------+
  260. TABLE
  261. ],
  262. 'Cell text with tags not used for Output styling' => [
  263. ['ISBN', 'Title', 'Author'],
  264. [
  265. ['<strong>99921-58-10-700</strong>', '<f>Divine Com</f>', 'Dante Alighieri'],
  266. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'],
  267. ],
  268. 'default',
  269. <<<'TABLE'
  270. +----------------------------------+----------------------+-----------------+
  271. | ISBN | Title | Author |
  272. +----------------------------------+----------------------+-----------------+
  273. | <strong>99921-58-10-700</strong> | <f>Divine Com</f> | Dante Alighieri |
  274. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
  275. +----------------------------------+----------------------+-----------------+
  276. TABLE
  277. ],
  278. 'Cell with colspan' => [
  279. ['ISBN', 'Title', 'Author'],
  280. [
  281. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  282. new TableSeparator(),
  283. [new TableCell('Divine Comedy(Dante Alighieri)', ['colspan' => 3])],
  284. new TableSeparator(),
  285. [
  286. new TableCell('Arduino: A Quick-Start Guide', ['colspan' => 2]),
  287. 'Mark Schmidt',
  288. ],
  289. new TableSeparator(),
  290. [
  291. '9971-5-0210-0',
  292. new TableCell("A Tale of \nTwo Cities", ['colspan' => 2]),
  293. ],
  294. new TableSeparator(),
  295. [
  296. new TableCell('Cupìdĭtâte díctá âtquè pôrrò, tèmpórà exercitátìónèm mòdí ânìmí núllà nèmò vèl níhìl!', ['colspan' => 3]),
  297. ],
  298. ],
  299. 'default',
  300. <<<'TABLE'
  301. +-------------------------------+-------------------------------+-----------------------------+
  302. | ISBN | Title | Author |
  303. +-------------------------------+-------------------------------+-----------------------------+
  304. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  305. +-------------------------------+-------------------------------+-----------------------------+
  306. | Divine Comedy(Dante Alighieri) |
  307. +-------------------------------+-------------------------------+-----------------------------+
  308. | Arduino: A Quick-Start Guide | Mark Schmidt |
  309. +-------------------------------+-------------------------------+-----------------------------+
  310. | 9971-5-0210-0 | A Tale of |
  311. | | Two Cities |
  312. +-------------------------------+-------------------------------+-----------------------------+
  313. | Cupìdĭtâte díctá âtquè pôrrò, tèmpórà exercitátìónèm mòdí ânìmí núllà nèmò vèl níhìl! |
  314. +-------------------------------+-------------------------------+-----------------------------+
  315. TABLE
  316. ],
  317. 'Cell after colspan contains new line break' => [
  318. ['Foo', 'Bar', 'Baz'],
  319. [
  320. [
  321. new TableCell("foo\nbar", ['colspan' => 2]),
  322. "baz\nqux",
  323. ],
  324. ],
  325. 'default',
  326. <<<'TABLE'
  327. +-----+-----+-----+
  328. | Foo | Bar | Baz |
  329. +-----+-----+-----+
  330. | foo | baz |
  331. | bar | qux |
  332. +-----+-----+-----+
  333. TABLE
  334. ],
  335. 'Cell after colspan contains multiple new lines' => [
  336. ['Foo', 'Bar', 'Baz'],
  337. [
  338. [
  339. new TableCell("foo\nbar", ['colspan' => 2]),
  340. "baz\nqux\nquux",
  341. ],
  342. ],
  343. 'default',
  344. <<<'TABLE'
  345. +-----+-----+------+
  346. | Foo | Bar | Baz |
  347. +-----+-----+------+
  348. | foo | baz |
  349. | bar | qux |
  350. | | quux |
  351. +-----+-----+------+
  352. TABLE
  353. ],
  354. 'Cell with rowspan' => [
  355. ['ISBN', 'Title', 'Author'],
  356. [
  357. [
  358. new TableCell('9971-5-0210-0', ['rowspan' => 3]),
  359. new TableCell('Divine Comedy', ['rowspan' => 2]),
  360. 'Dante Alighieri',
  361. ],
  362. [],
  363. ["The Lord of \nthe Rings", "J. R. \nR. Tolkien"],
  364. new TableSeparator(),
  365. ['80-902734-1-6', new TableCell("And Then \nThere \nWere None", ['rowspan' => 3]), 'Agatha Christie'],
  366. ['80-902734-1-7', 'Test'],
  367. ],
  368. 'default',
  369. <<<'TABLE'
  370. +---------------+---------------+-----------------+
  371. | ISBN | Title | Author |
  372. +---------------+---------------+-----------------+
  373. | 9971-5-0210-0 | Divine Comedy | Dante Alighieri |
  374. | | | |
  375. | | The Lord of | J. R. |
  376. | | the Rings | R. Tolkien |
  377. +---------------+---------------+-----------------+
  378. | 80-902734-1-6 | And Then | Agatha Christie |
  379. | 80-902734-1-7 | There | Test |
  380. | | Were None | |
  381. +---------------+---------------+-----------------+
  382. TABLE
  383. ],
  384. 'Cell with rowspan and colspan' => [
  385. ['ISBN', 'Title', 'Author'],
  386. [
  387. [
  388. new TableCell('9971-5-0210-0', ['rowspan' => 2, 'colspan' => 2]),
  389. 'Dante Alighieri',
  390. ],
  391. ['Charles Dickens'],
  392. new TableSeparator(),
  393. [
  394. 'Dante Alighieri',
  395. new TableCell('9971-5-0210-0', ['rowspan' => 3, 'colspan' => 2]),
  396. ],
  397. ['J. R. R. Tolkien'],
  398. ['J. R. R'],
  399. ],
  400. 'default',
  401. <<<'TABLE'
  402. +------------------+---------+-----------------+
  403. | ISBN | Title | Author |
  404. +------------------+---------+-----------------+
  405. | 9971-5-0210-0 | Dante Alighieri |
  406. | | Charles Dickens |
  407. +------------------+---------+-----------------+
  408. | Dante Alighieri | 9971-5-0210-0 |
  409. | J. R. R. Tolkien | |
  410. | J. R. R | |
  411. +------------------+---------+-----------------+
  412. TABLE
  413. ],
  414. 'Cell with rowspan and colspan contains new line break' => [
  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. new TableSeparator(),
  423. [
  424. 'Dante Alighieri',
  425. new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
  426. ],
  427. ['Charles Dickens'],
  428. new TableSeparator(),
  429. [
  430. new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
  431. new TableCell("Dante \nAlighieri", ['rowspan' => 2, 'colspan' => 1]),
  432. ],
  433. ],
  434. 'default',
  435. <<<'TABLE'
  436. +-----------------+-------+-----------------+
  437. | ISBN | Title | Author |
  438. +-----------------+-------+-----------------+
  439. | 9971 | Dante Alighieri |
  440. | -5- | Charles Dickens |
  441. | 021 | |
  442. | 0-0 | |
  443. +-----------------+-------+-----------------+
  444. | Dante Alighieri | 9971 |
  445. | Charles Dickens | -5- |
  446. | | 021 |
  447. | | 0-0 |
  448. +-----------------+-------+-----------------+
  449. | 9971 | Dante |
  450. | -5- | Alighieri |
  451. | 021 | |
  452. | 0-0 | |
  453. +-----------------+-------+-----------------+
  454. TABLE
  455. ],
  456. 'Cell with rowspan and colspan without using TableSeparator' => [
  457. ['ISBN', 'Title', 'Author'],
  458. [
  459. [
  460. new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
  461. 'Dante Alighieri',
  462. ],
  463. ['Charles Dickens'],
  464. [
  465. 'Dante Alighieri',
  466. new TableCell("9971\n-5-\n021\n0-0", ['rowspan' => 2, 'colspan' => 2]),
  467. ],
  468. ['Charles Dickens'],
  469. ],
  470. 'default',
  471. <<<'TABLE'
  472. +-----------------+-------+-----------------+
  473. | ISBN | Title | Author |
  474. +-----------------+-------+-----------------+
  475. | 9971 | Dante Alighieri |
  476. | -5- | Charles Dickens |
  477. | 021 | |
  478. | 0-0 | |
  479. | Dante Alighieri | 9971 |
  480. | Charles Dickens | -5- |
  481. | | 021 |
  482. | | 0-0 |
  483. +-----------------+-------+-----------------+
  484. TABLE
  485. ],
  486. 'Cell with rowspan and colspan with separator inside a rowspan' => [
  487. ['ISBN', 'Author'],
  488. [
  489. [
  490. new TableCell('9971-5-0210-0', ['rowspan' => 3, 'colspan' => 1]),
  491. 'Dante Alighieri',
  492. ],
  493. [new TableSeparator()],
  494. ['Charles Dickens'],
  495. ],
  496. 'default',
  497. <<<'TABLE'
  498. +---------------+-----------------+
  499. | ISBN | Author |
  500. +---------------+-----------------+
  501. | 9971-5-0210-0 | Dante Alighieri |
  502. | |-----------------|
  503. | | Charles Dickens |
  504. +---------------+-----------------+
  505. TABLE
  506. ],
  507. 'Multiple header lines' => [
  508. [
  509. [new TableCell('Main title', ['colspan' => 3])],
  510. ['ISBN', 'Title', 'Author'],
  511. ],
  512. [],
  513. 'default',
  514. <<<'TABLE'
  515. +------+-------+--------+
  516. | Main title |
  517. +------+-------+--------+
  518. | ISBN | Title | Author |
  519. +------+-------+--------+
  520. TABLE
  521. ],
  522. 'Row with multiple cells' => [
  523. [],
  524. [
  525. [
  526. new TableCell('1', ['colspan' => 3]),
  527. new TableCell('2', ['colspan' => 2]),
  528. new TableCell('3', ['colspan' => 2]),
  529. new TableCell('4', ['colspan' => 2]),
  530. ],
  531. ],
  532. 'default',
  533. <<<'TABLE'
  534. +---+--+--+---+--+---+--+---+--+
  535. | 1 | 2 | 3 | 4 |
  536. +---+--+--+---+--+---+--+---+--+
  537. TABLE
  538. ],
  539. 'Coslpan and table cells with comment style' => [
  540. [
  541. new TableCell('<comment>Long Title</comment>', ['colspan' => 3]),
  542. ],
  543. [
  544. [
  545. new TableCell('9971-5-0210-0', ['colspan' => 3]),
  546. ],
  547. new TableSeparator(),
  548. [
  549. 'Dante Alighieri',
  550. 'J. R. R. Tolkien',
  551. 'J. R. R',
  552. ],
  553. ],
  554. 'default',
  555. <<<TABLE
  556. +-----------------+------------------+---------+
  557. |\033[32m \033[39m\033[33mLong Title\033[39m\033[32m \033[39m|
  558. +-----------------+------------------+---------+
  559. | 9971-5-0210-0 |
  560. +-----------------+------------------+---------+
  561. | Dante Alighieri | J. R. R. Tolkien | J. R. R |
  562. +-----------------+------------------+---------+
  563. TABLE
  564. ,
  565. true,
  566. ],
  567. 'Row with formatted cells containing a newline' => [
  568. [],
  569. [
  570. [
  571. new TableCell('<error>Dont break'."\n".'here</error>', ['colspan' => 2]),
  572. ],
  573. new TableSeparator(),
  574. [
  575. 'foo',
  576. new TableCell('<error>Dont break'."\n".'here</error>', ['rowspan' => 2]),
  577. ],
  578. [
  579. 'bar',
  580. ],
  581. ],
  582. 'default',
  583. <<<'TABLE'
  584. +-------+------------+
  585. | Dont break |
  586. | here |
  587. +-------+------------+
  588. | foo | Dont break |
  589. | bar | here |
  590. +-------+------------+
  591. TABLE
  592. ,
  593. true,
  594. ],
  595. 'TabeCellStyle with align. Also with rowspan and colspan > 1' => [
  596. [
  597. new TableCell(
  598. 'ISBN',
  599. [
  600. 'style' => new TableCellStyle([
  601. 'align' => 'right',
  602. ]),
  603. ]
  604. ),
  605. 'Title',
  606. new TableCell(
  607. 'Author',
  608. [
  609. 'style' => new TableCellStyle([
  610. 'align' => 'center',
  611. ]),
  612. ]
  613. ),
  614. ],
  615. [
  616. [
  617. new TableCell(
  618. '<fg=red>978</>',
  619. [
  620. 'style' => new TableCellStyle([
  621. 'align' => 'center',
  622. ]),
  623. ]
  624. ),
  625. 'De Monarchia',
  626. new TableCell(
  627. "Dante Alighieri \nspans multiple rows rows Dante Alighieri \nspans multiple rows rows",
  628. [
  629. 'rowspan' => 2,
  630. 'style' => new TableCellStyle([
  631. 'align' => 'center',
  632. ]),
  633. ]
  634. ),
  635. ],
  636. [
  637. '<info>99921-58-10-7</info>',
  638. 'Divine Comedy',
  639. ],
  640. new TableSeparator(),
  641. [
  642. new TableCell(
  643. '<error>test</error>',
  644. [
  645. 'colspan' => 2,
  646. 'style' => new TableCellStyle([
  647. 'align' => 'center',
  648. ]),
  649. ]
  650. ),
  651. new TableCell(
  652. 'tttt',
  653. [
  654. 'style' => new TableCellStyle([
  655. 'align' => 'right',
  656. ]),
  657. ]
  658. ),
  659. ],
  660. ],
  661. 'default',
  662. <<<'TABLE'
  663. +---------------+---------------+-------------------------------------------+
  664. | ISBN | Title | Author |
  665. +---------------+---------------+-------------------------------------------+
  666. | 978 | De Monarchia | Dante Alighieri |
  667. | 99921-58-10-7 | Divine Comedy | spans multiple rows rows Dante Alighieri |
  668. | | | spans multiple rows rows |
  669. +---------------+---------------+-------------------------------------------+
  670. | test | tttt |
  671. +---------------+---------------+-------------------------------------------+
  672. TABLE
  673. ,
  674. ],
  675. 'TabeCellStyle with fg,bg. Also with rowspan and colspan > 1' => [
  676. [],
  677. [
  678. [
  679. new TableCell(
  680. '<fg=red>978</>',
  681. [
  682. 'style' => new TableCellStyle([
  683. 'fg' => 'black',
  684. 'bg' => 'green',
  685. ]),
  686. ]
  687. ),
  688. 'De Monarchia',
  689. new TableCell(
  690. "Dante Alighieri \nspans multiple rows rows Dante Alighieri \nspans multiple rows rows",
  691. [
  692. 'rowspan' => 2,
  693. 'style' => new TableCellStyle([
  694. 'fg' => 'red',
  695. 'bg' => 'green',
  696. 'align' => 'center',
  697. ]),
  698. ]
  699. ),
  700. ],
  701. [
  702. '<info>99921-58-10-7</info>',
  703. 'Divine Comedy',
  704. ],
  705. new TableSeparator(),
  706. [
  707. new TableCell(
  708. '<error>test</error>',
  709. [
  710. 'colspan' => 2,
  711. 'style' => new TableCellStyle([
  712. 'fg' => 'red',
  713. 'bg' => 'green',
  714. 'align' => 'center',
  715. ]),
  716. ]
  717. ),
  718. new TableCell(
  719. 'tttt',
  720. [
  721. 'style' => new TableCellStyle([
  722. 'fg' => 'red',
  723. 'bg' => 'green',
  724. 'align' => 'right',
  725. ]),
  726. ]
  727. ),
  728. ],
  729. ],
  730. 'default',
  731. <<<'TABLE'
  732. +---------------+---------------+-------------------------------------------+
  733. | 978 | De Monarchia | Dante Alighieri |
  734. | 99921-58-10-7 | Divine Comedy | spans multiple rows rows Dante Alighieri |
  735. | | | spans multiple rows rows |
  736. +---------------+---------------+-------------------------------------------+
  737. | test | tttt |
  738. +---------------+---------------+-------------------------------------------+
  739. TABLE
  740. ,
  741. true,
  742. ],
  743. 'TabeCellStyle with cellFormat. Also with rowspan and colspan > 1' => [
  744. [
  745. new TableCell(
  746. 'ISBN',
  747. [
  748. 'style' => new TableCellStyle([
  749. 'cellFormat' => '<fg=black;bg=cyan>%s</>',
  750. ]),
  751. ]
  752. ),
  753. 'Title',
  754. 'Author',
  755. ],
  756. [
  757. [
  758. '978-0521567817',
  759. 'De Monarchia',
  760. new TableCell(
  761. "Dante Alighieri\nspans multiple rows",
  762. [
  763. 'rowspan' => 2,
  764. 'style' => new TableCellStyle([
  765. 'cellFormat' => '<info>%s</info>',
  766. ]),
  767. ]
  768. ),
  769. ],
  770. ['978-0804169127', 'Divine Comedy'],
  771. [
  772. new TableCell(
  773. 'test',
  774. [
  775. 'colspan' => 2,
  776. 'style' => new TableCellStyle([
  777. 'cellFormat' => '<error>%s</error>',
  778. ]),
  779. ]
  780. ),
  781. 'tttt',
  782. ],
  783. ],
  784. 'default',
  785. <<<'TABLE'
  786. +----------------+---------------+---------------------+
  787. | ISBN | Title | Author |
  788. +----------------+---------------+---------------------+
  789. | 978-0521567817 | De Monarchia | Dante Alighieri |
  790. | 978-0804169127 | Divine Comedy | spans multiple rows |
  791. | test | tttt |
  792. +----------------+---------------+---------------------+
  793. TABLE
  794. ,
  795. true,
  796. ],
  797. ];
  798. }
  799. public function testRenderMultiByte()
  800. {
  801. $table = new Table($output = $this->getOutputStream());
  802. $table
  803. ->setHeaders(['■■'])
  804. ->setRows([[1234]])
  805. ->setStyle('default')
  806. ;
  807. $table->render();
  808. $expected =
  809. <<<'TABLE'
  810. +------+
  811. | ■■ |
  812. +------+
  813. | 1234 |
  814. +------+
  815. TABLE;
  816. $this->assertEquals($expected, $this->getOutputContent($output));
  817. }
  818. public function testTableCellWithNumericIntValue()
  819. {
  820. $table = new Table($output = $this->getOutputStream());
  821. $table->setRows([[new TableCell(12345)]]);
  822. $table->render();
  823. $expected =
  824. <<<'TABLE'
  825. +-------+
  826. | 12345 |
  827. +-------+
  828. TABLE;
  829. $this->assertEquals($expected, $this->getOutputContent($output));
  830. }
  831. public function testTableCellWithNumericFloatValue()
  832. {
  833. $table = new Table($output = $this->getOutputStream());
  834. $table->setRows([[new TableCell(12345.01)]]);
  835. $table->render();
  836. $expected =
  837. <<<'TABLE'
  838. +----------+
  839. | 12345.01 |
  840. +----------+
  841. TABLE;
  842. $this->assertEquals($expected, $this->getOutputContent($output));
  843. }
  844. public function testStyle()
  845. {
  846. $style = new TableStyle();
  847. $style
  848. ->setHorizontalBorderChars('.')
  849. ->setVerticalBorderChars('.')
  850. ->setDefaultCrossingChar('.')
  851. ;
  852. Table::setStyleDefinition('dotfull', $style);
  853. $table = new Table($output = $this->getOutputStream());
  854. $table
  855. ->setHeaders(['Foo'])
  856. ->setRows([['Bar']])
  857. ->setStyle('dotfull');
  858. $table->render();
  859. $expected =
  860. <<<'TABLE'
  861. .......
  862. . Foo .
  863. .......
  864. . Bar .
  865. .......
  866. TABLE;
  867. $this->assertEquals($expected, $this->getOutputContent($output));
  868. }
  869. public function testRowSeparator()
  870. {
  871. $table = new Table($output = $this->getOutputStream());
  872. $table
  873. ->setHeaders(['Foo'])
  874. ->setRows([
  875. ['Bar1'],
  876. new TableSeparator(),
  877. ['Bar2'],
  878. new TableSeparator(),
  879. ['Bar3'],
  880. ]);
  881. $table->render();
  882. $expected =
  883. <<<'TABLE'
  884. +------+
  885. | Foo |
  886. +------+
  887. | Bar1 |
  888. +------+
  889. | Bar2 |
  890. +------+
  891. | Bar3 |
  892. +------+
  893. TABLE;
  894. $this->assertEquals($expected, $this->getOutputContent($output));
  895. $this->assertEquals($table, $table->addRow(new TableSeparator()), 'fluent interface on addRow() with a single TableSeparator() works');
  896. }
  897. public function testRenderMultiCalls()
  898. {
  899. $table = new Table($output = $this->getOutputStream());
  900. $table->setRows([
  901. [new TableCell('foo', ['colspan' => 2])],
  902. ]);
  903. $table->render();
  904. $table->render();
  905. $table->render();
  906. $expected =
  907. <<<TABLE
  908. +----+---+
  909. | foo |
  910. +----+---+
  911. +----+---+
  912. | foo |
  913. +----+---+
  914. +----+---+
  915. | foo |
  916. +----+---+
  917. TABLE;
  918. $this->assertEquals($expected, $this->getOutputContent($output));
  919. }
  920. public function testColumnStyle()
  921. {
  922. $table = new Table($output = $this->getOutputStream());
  923. $table
  924. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  925. ->setRows([
  926. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
  927. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'],
  928. ]);
  929. $style = new TableStyle();
  930. $style->setPadType(\STR_PAD_LEFT);
  931. $table->setColumnStyle(3, $style);
  932. $table->render();
  933. $expected =
  934. <<<TABLE
  935. +---------------+----------------------+-----------------+--------+
  936. | ISBN | Title | Author | Price |
  937. +---------------+----------------------+-----------------+--------+
  938. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  939. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  940. +---------------+----------------------+-----------------+--------+
  941. TABLE;
  942. $this->assertEquals($expected, $this->getOutputContent($output));
  943. }
  944. public function testThrowsWhenTheCellInAnArray()
  945. {
  946. $table = new Table($this->getOutputStream());
  947. $table
  948. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  949. ->setRows([
  950. ['99921-58-10-7', [], 'Dante Alighieri', '9.95'],
  951. ]);
  952. $this->expectException(InvalidArgumentException::class);
  953. $this->expectExceptionMessage('A cell must be a TableCell, a scalar or an object implementing "__toString()", "array" given.');
  954. $table->render();
  955. }
  956. public function testColumnWidth()
  957. {
  958. $table = new Table($output = $this->getOutputStream());
  959. $table
  960. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  961. ->setRows([
  962. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
  963. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'],
  964. ])
  965. ->setColumnWidth(0, 15)
  966. ->setColumnWidth(3, 10);
  967. $style = new TableStyle();
  968. $style->setPadType(\STR_PAD_LEFT);
  969. $table->setColumnStyle(3, $style);
  970. $table->render();
  971. $expected =
  972. <<<TABLE
  973. +-----------------+----------------------+-----------------+------------+
  974. | ISBN | Title | Author | Price |
  975. +-----------------+----------------------+-----------------+------------+
  976. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  977. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  978. +-----------------+----------------------+-----------------+------------+
  979. TABLE;
  980. $this->assertEquals($expected, $this->getOutputContent($output));
  981. }
  982. public function testColumnWidths()
  983. {
  984. $table = new Table($output = $this->getOutputStream());
  985. $table
  986. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  987. ->setRows([
  988. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
  989. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25'],
  990. ])
  991. ->setColumnWidths([15, 0, -1, 10]);
  992. $style = new TableStyle();
  993. $style->setPadType(\STR_PAD_LEFT);
  994. $table->setColumnStyle(3, $style);
  995. $table->render();
  996. $expected =
  997. <<<TABLE
  998. +-----------------+----------------------+-----------------+------------+
  999. | ISBN | Title | Author | Price |
  1000. +-----------------+----------------------+-----------------+------------+
  1001. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  1002. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  1003. +-----------------+----------------------+-----------------+------------+
  1004. TABLE;
  1005. $this->assertEquals($expected, $this->getOutputContent($output));
  1006. }
  1007. public function testSectionOutput()
  1008. {
  1009. $sections = [];
  1010. $stream = $this->getOutputStream(true);
  1011. $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  1012. $table = new Table($output);
  1013. $table
  1014. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  1015. ->setRows([
  1016. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
  1017. ]);
  1018. $table->render();
  1019. $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']);
  1020. $expected =
  1021. <<<TABLE
  1022. +---------------+---------------+-----------------+-------+
  1023. |\033[32m ISBN \033[39m|\033[32m Title \033[39m|\033[32m Author \033[39m|\033[32m Price \033[39m|
  1024. +---------------+---------------+-----------------+-------+
  1025. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  1026. +---------------+---------------+-----------------+-------+
  1027. \x1b[5A\x1b[0J+---------------+----------------------+-----------------+--------+
  1028. |\033[32m ISBN \033[39m|\033[32m Title \033[39m|\033[32m Author \033[39m|\033[32m Price \033[39m|
  1029. +---------------+----------------------+-----------------+--------+
  1030. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  1031. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  1032. +---------------+----------------------+-----------------+--------+
  1033. TABLE;
  1034. $this->assertEquals($expected, $this->getOutputContent($output));
  1035. }
  1036. public function testSectionOutputDoesntClearIfTableIsntRendered()
  1037. {
  1038. $sections = [];
  1039. $stream = $this->getOutputStream(true);
  1040. $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  1041. $table = new Table($output);
  1042. $table
  1043. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  1044. ->setRows([
  1045. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
  1046. ]);
  1047. $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']);
  1048. $expected =
  1049. <<<TABLE
  1050. +---------------+----------------------+-----------------+--------+
  1051. |\033[32m ISBN \033[39m|\033[32m Title \033[39m|\033[32m Author \033[39m|\033[32m Price \033[39m|
  1052. +---------------+----------------------+-----------------+--------+
  1053. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  1054. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  1055. +---------------+----------------------+-----------------+--------+
  1056. TABLE;
  1057. $this->assertEquals($expected, $this->getOutputContent($output));
  1058. }
  1059. public function testSectionOutputWithoutDecoration()
  1060. {
  1061. $sections = [];
  1062. $stream = $this->getOutputStream();
  1063. $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  1064. $table = new Table($output);
  1065. $table
  1066. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  1067. ->setRows([
  1068. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri', '9.95'],
  1069. ]);
  1070. $table->render();
  1071. $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']);
  1072. $expected =
  1073. <<<TABLE
  1074. +---------------+---------------+-----------------+-------+
  1075. | ISBN | Title | Author | Price |
  1076. +---------------+---------------+-----------------+-------+
  1077. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  1078. +---------------+---------------+-----------------+-------+
  1079. +---------------+----------------------+-----------------+--------+
  1080. | ISBN | Title | Author | Price |
  1081. +---------------+----------------------+-----------------+--------+
  1082. | 99921-58-10-7 | Divine Comedy | Dante Alighieri | 9.95 |
  1083. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  1084. +---------------+----------------------+-----------------+--------+
  1085. TABLE;
  1086. $this->assertEquals($expected, $this->getOutputContent($output));
  1087. }
  1088. public function testAppendRowWithoutSectionOutput()
  1089. {
  1090. $this->expectException(RuntimeException::class);
  1091. $this->expectExceptionMessage('Output should be an instance of "Symfony\Component\Console\Output\ConsoleSectionOutput" when calling "Symfony\Component\Console\Helper\Table::appendRow".');
  1092. $table = new Table($this->getOutputStream());
  1093. $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']);
  1094. }
  1095. public function testSectionOutputHandlesZeroRowsAfterRender()
  1096. {
  1097. $sections = [];
  1098. $stream = $this->getOutputStream(true);
  1099. $output = new ConsoleSectionOutput($stream->getStream(), $sections, $stream->getVerbosity(), $stream->isDecorated(), new OutputFormatter());
  1100. $output->writeln('My Table');
  1101. $table = new Table($output);
  1102. $table
  1103. ->setHeaders(['ISBN', 'Title', 'Author', 'Price'])
  1104. ->setRows([]);
  1105. $table->render();
  1106. $table->appendRow(['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens', '139.25']);
  1107. $expected =
  1108. <<<TABLE
  1109. My Table
  1110. +------+-------+--------+-------+
  1111. |\033[32m ISBN \033[39m|\033[32m Title \033[39m|\033[32m Author \033[39m|\033[32m Price \033[39m|
  1112. +------+-------+--------+-------+
  1113. \x1b[3A\x1b[0J+---------------+----------------------+-----------------+--------+
  1114. |\033[32m ISBN \033[39m|\033[32m Title \033[39m|\033[32m Author \033[39m|\033[32m Price \033[39m|
  1115. +---------------+----------------------+-----------------+--------+
  1116. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | 139.25 |
  1117. +---------------+----------------------+-----------------+--------+
  1118. TABLE;
  1119. $this->assertEquals($expected, $this->getOutputContent($output));
  1120. }
  1121. public function testIsNotDefinedStyleException()
  1122. {
  1123. $this->expectException(InvalidArgumentException::class);
  1124. $this->expectExceptionMessage('Style "absent" is not defined.');
  1125. $table = new Table($this->getOutputStream());
  1126. $table->setStyle('absent');
  1127. }
  1128. public function testGetStyleDefinition()
  1129. {
  1130. $this->expectException(InvalidArgumentException::class);
  1131. $this->expectExceptionMessage('Style "absent" is not defined.');
  1132. Table::getStyleDefinition('absent');
  1133. }
  1134. /**
  1135. * @dataProvider renderSetTitle
  1136. */
  1137. public function testSetTitle($headerTitle, $footerTitle, $style, $expected)
  1138. {
  1139. (new Table($output = $this->getOutputStream()))
  1140. ->setHeaderTitle($headerTitle)
  1141. ->setFooterTitle($footerTitle)
  1142. ->setHeaders(['ISBN', 'Title', 'Author'])
  1143. ->setRows([
  1144. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  1145. ['9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'],
  1146. ['960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'],
  1147. ['80-902734-1-6', 'And Then There Were None', 'Agatha Christie'],
  1148. ])
  1149. ->setStyle($style)
  1150. ->render()
  1151. ;
  1152. $this->assertEquals($expected, $this->getOutputContent($output));
  1153. }
  1154. public static function renderSetTitle()
  1155. {
  1156. return [
  1157. [
  1158. 'Books',
  1159. 'Page 1/2',
  1160. 'default',
  1161. <<<'TABLE'
  1162. +---------------+----------- Books --------+------------------+
  1163. | ISBN | Title | Author |
  1164. +---------------+--------------------------+------------------+
  1165. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  1166. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
  1167. | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  1168. | 80-902734-1-6 | And Then There Were None | Agatha Christie |
  1169. +---------------+--------- Page 1/2 -------+------------------+
  1170. TABLE
  1171. ,
  1172. true,
  1173. ],
  1174. 'header contains multiple lines' => [
  1175. 'Multiline'."\n".'header'."\n".'here',
  1176. 'footer',
  1177. 'default',
  1178. <<<'TABLE'
  1179. +---------------+---- Multiline
  1180. header
  1181. here -+------------------+
  1182. | ISBN | Title | Author |
  1183. +---------------+--------------------------+------------------+
  1184. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  1185. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
  1186. | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  1187. | 80-902734-1-6 | And Then There Were None | Agatha Christie |
  1188. +---------------+---------- footer --------+------------------+
  1189. TABLE
  1190. ],
  1191. [
  1192. 'Books',
  1193. 'Page 1/2',
  1194. 'box',
  1195. <<<'TABLE'
  1196. ┌───────────────┬─────────── Books ────────┬──────────────────┐
  1197. │ ISBN │ Title │ Author │
  1198. ├───────────────┼──────────────────────────┼──────────────────┤
  1199. │ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri │
  1200. │ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens │
  1201. │ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien │
  1202. │ 80-902734-1-6 │ And Then There Were None │ Agatha Christie │
  1203. └───────────────┴───────── Page 1/2 ───────┴──────────────────┘
  1204. TABLE
  1205. ],
  1206. [
  1207. 'Boooooooooooooooooooooooooooooooooooooooooooooooooooooooks',
  1208. 'Page 1/999999999999999999999999999999999999999999999999999',
  1209. 'default',
  1210. <<<'TABLE'
  1211. +- Booooooooooooooooooooooooooooooooooooooooooooooooooooo... -+
  1212. | ISBN | Title | Author |
  1213. +---------------+--------------------------+------------------+
  1214. | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
  1215. | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
  1216. | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
  1217. | 80-902734-1-6 | And Then There Were None | Agatha Christie |
  1218. +- Page 1/99999999999999999999999999999999999999999999999... -+
  1219. TABLE
  1220. ],
  1221. ];
  1222. }
  1223. public function testSetTitleWithoutHeaders()
  1224. {
  1225. (new Table($output = $this->getOutputStream()))
  1226. ->setHeaderTitle('Reproducer')
  1227. ->setRows([
  1228. ['Value', '123-456'],
  1229. ['Some other value', '789-0'],
  1230. ])
  1231. ->render();
  1232. $expected = <<<'TABLE'
  1233. +-------- Reproducer --------+
  1234. | Value | 123-456 |
  1235. | Some other value | 789-0 |
  1236. +------------------+---------+
  1237. TABLE;
  1238. $this->assertSame($expected, $this->getOutputContent($output));
  1239. }
  1240. public function testColumnMaxWidths()
  1241. {
  1242. $table = new Table($output = $this->getOutputStream());
  1243. $table
  1244. ->setRows([
  1245. ['Divine Comedy', 'A Tale of Two Cities', 'The Lord of the Rings', 'And Then There Were None'],
  1246. ])
  1247. ->setColumnMaxWidth(1, 5)
  1248. ->setColumnMaxWidth(2, 10)
  1249. ->setColumnMaxWidth(3, 15);
  1250. $table->render();
  1251. $expected =
  1252. <<<TABLE
  1253. +---------------+-------+----------+----------------+
  1254. | Divine Comedy | A | The Lord | And Then There |
  1255. | | Tale | of the | Were None |
  1256. | | of | Rings | |
  1257. | | Two | | |
  1258. | | Citie | | |
  1259. | | s | | |
  1260. +---------------+-------+----------+----------------+
  1261. TABLE;
  1262. $this->assertEquals($expected, $this->getOutputContent($output));
  1263. }
  1264. public function testColumnMaxWidthsHeaders()
  1265. {
  1266. $table = new Table($output = $this->getOutputStream());
  1267. $table
  1268. ->setHeaders([
  1269. [
  1270. 'Publication',
  1271. 'Very long header with a lot of information',
  1272. ],
  1273. ])
  1274. ->setRows([
  1275. [
  1276. '1954',
  1277. 'The Lord of the Rings, by J.R.R. Tolkien',
  1278. ],
  1279. ])
  1280. ->setColumnMaxWidth(1, 30);
  1281. $table->render();
  1282. $expected =
  1283. <<<TABLE
  1284. +-------------+--------------------------------+
  1285. | Publication | Very long header with a lot of |
  1286. | | information |
  1287. +-------------+--------------------------------+
  1288. | 1954 | The Lord of the Rings, by |
  1289. | | J.R.R. Tolkien |
  1290. +-------------+--------------------------------+
  1291. TABLE;
  1292. $this->assertEquals($expected, $this->getOutputContent($output));
  1293. }
  1294. public function testColumnMaxWidthsWithTrailingBackslash()
  1295. {
  1296. (new Table($output = $this->getOutputStream()))
  1297. ->setColumnMaxWidth(0, 5)
  1298. ->setRows([['1234\6']])
  1299. ->render()
  1300. ;
  1301. $expected =
  1302. <<<'TABLE'
  1303. +-------+
  1304. | 1234\ |
  1305. | 6 |
  1306. +-------+
  1307. TABLE;
  1308. $this->assertEquals($expected, $this->getOutputContent($output));
  1309. }
  1310. public function testBoxedStyleWithColspan()
  1311. {
  1312. $boxed = new TableStyle();
  1313. $boxed
  1314. ->setHorizontalBorderChars('─')
  1315. ->setVerticalBorderChars('│')
  1316. ->setCrossingChars('┼', '┌', '┬', '┐', '┤', '┘', '┴', '└', '├')
  1317. ;
  1318. $table = new Table($output = $this->getOutputStream());
  1319. $table->setStyle($boxed);
  1320. $table
  1321. ->setHeaders(['ISBN', 'Title', 'Author'])
  1322. ->setRows([
  1323. ['99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'],
  1324. new TableSeparator(),
  1325. [new TableCell('This value spans 3 columns.', ['colspan' => 3])],
  1326. ])
  1327. ;
  1328. $table->render();
  1329. $expected =
  1330. <<<TABLE
  1331. ┌───────────────┬───────────────┬─────────────────┐
  1332. │ ISBN │ Title │ Author │
  1333. ├───────────────┼───────────────┼─────────────────┤
  1334. │ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri │
  1335. ├───────────────┼───────────────┼─────────────────┤
  1336. │ This value spans 3 columns. │
  1337. └───────────────┴───────────────┴─────────────────┘
  1338. TABLE;
  1339. $this->assertSame($expected, $this->getOutputContent($output));
  1340. }
  1341. public static function provideRenderHorizontalTests()
  1342. {
  1343. $headers = ['foo', 'bar', 'baz'];
  1344. $rows = [['one', 'two', 'tree'], ['1', '2', '3']];
  1345. $expected = <<<EOTXT
  1346. +-----+------+---+
  1347. | foo | one | 1 |
  1348. | bar | two | 2 |
  1349. | baz | tree | 3 |
  1350. +-----+------+---+
  1351. EOTXT;
  1352. yield [$headers, $rows, $expected];
  1353. $headers = ['foo', 'bar', 'baz'];
  1354. $rows = [['one', 'two'], ['1']];
  1355. $expected = <<<EOTXT
  1356. +-----+-----+---+
  1357. | foo | one | 1 |
  1358. | bar | two | |
  1359. | baz | | |
  1360. +-----+-----+---+
  1361. EOTXT;
  1362. yield [$headers, $rows, $expected];
  1363. $headers = ['foo', 'bar', 'baz'];
  1364. $rows = [['one', 'two', 'tree'], new TableSeparator(), ['1', '2', '3']];
  1365. $expected = <<<EOTXT
  1366. +-----+------+---+
  1367. | foo | one | 1 |
  1368. | bar | two | 2 |
  1369. | baz | tree | 3 |
  1370. +-----+------+---+
  1371. EOTXT;
  1372. yield [$headers, $rows, $expected];
  1373. }
  1374. /**
  1375. * @dataProvider provideRenderHorizontalTests
  1376. */
  1377. public function testRenderHorizontal(array $headers, array $rows, string $expected)
  1378. {
  1379. $table = new Table($output = $this->getOutputStream());
  1380. $table
  1381. ->setHeaders($headers)
  1382. ->setRows($rows)
  1383. ->setHorizontal()
  1384. ;
  1385. $table->render();
  1386. $this->assertEquals($expected, $this->getOutputContent($output));
  1387. }
  1388. protected function getOutputStream($decorated = false)
  1389. {
  1390. return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, $decorated);
  1391. }
  1392. protected function getOutputContent(StreamOutput $output)
  1393. {
  1394. rewind($output->getStream());
  1395. return str_replace(\PHP_EOL, "\n", stream_get_contents($output->getStream()));
  1396. }
  1397. public function testWithColspanAndMaxWith()
  1398. {
  1399. $table = new Table($output = $this->getOutputStream());
  1400. $table->setColumnMaxWidth(0, 15);
  1401. $table->setColumnMaxWidth(1, 15);
  1402. $table->setColumnMaxWidth(2, 15);
  1403. $table->setRows([
  1404. [new TableCell('Lorem ipsum dolor sit amet, <fg=white;bg=green>consectetur</> adipiscing elit, <fg=white;bg=red>sed</> do <fg=white;bg=red>eiusmod</> tempor', ['colspan' => 3])],
  1405. new TableSeparator(),
  1406. [new TableCell('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor', ['colspan' => 3])],
  1407. new TableSeparator(),
  1408. [new TableCell('Lorem ipsum <fg=white;bg=red>dolor</> sit amet, consectetur ', ['colspan' => 2]), 'hello world'],
  1409. new TableSeparator(),
  1410. ['hello <fg=white;bg=green>world</>', new TableCell('Lorem ipsum dolor sit amet, <fg=white;bg=green>consectetur</> adipiscing elit', ['colspan' => 2])],
  1411. new TableSeparator(),
  1412. ['hello ', new TableCell('world', ['colspan' => 1]), 'Lorem ipsum dolor sit amet, consectetur'],
  1413. new TableSeparator(),
  1414. ['Symfony ', new TableCell('Test', ['colspan' => 1]), 'Lorem <fg=white;bg=green>ipsum</> dolor sit amet, consectetur'],
  1415. ])
  1416. ;
  1417. $table->render();
  1418. $expected =
  1419. <<<TABLE
  1420. +-----------------+-----------------+-----------------+
  1421. | Lorem ipsum dolor sit amet, consectetur adipi |
  1422. | scing elit, sed do eiusmod tempor |
  1423. +-----------------+-----------------+-----------------+
  1424. | Lorem ipsum dolor sit amet, consectetur |
  1425. | adipiscing elit, sed do eiusmod tempor |
  1426. +-----------------+-----------------+-----------------+
  1427. | Lorem ipsum dolor sit amet, co | hello world |
  1428. | nsectetur | |
  1429. +-----------------+-----------------+-----------------+
  1430. | hello world | Lorem ipsum dolor sit amet, co |
  1431. | | nsectetur adipiscing elit |
  1432. +-----------------+-----------------+-----------------+
  1433. | hello | world | Lorem ipsum |
  1434. | | | dolor sit amet, |
  1435. | | | consectetur |
  1436. +-----------------+-----------------+-----------------+
  1437. | Symfony | Test | Lorem ipsum dol |
  1438. | | | or sit amet, |
  1439. | | | consectetur |
  1440. +-----------------+-----------------+-----------------+
  1441. TABLE;
  1442. $this->assertSame($expected, $this->getOutputContent($output));
  1443. }
  1444. public function testWithHyperlinkAndMaxWidth()
  1445. {
  1446. $table = new Table($output = $this->getOutputStream(true));
  1447. $table
  1448. ->setRows([
  1449. ['<href=Lorem>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor</>'],
  1450. ])
  1451. ;
  1452. $table->setColumnMaxWidth(0, 20);
  1453. $table->render();
  1454. $expected =
  1455. <<<TABLE
  1456. +----------------------+
  1457. | \033]8;;Lorem\033\\Lorem ipsum dolor\033]8;;\033\\ |
  1458. | \033]8;;Lorem\033\\sit amet,\033]8;;\033\\ |
  1459. | \033]8;;Lorem\033\\consectetur\033]8;;\033\\ |
  1460. | \033]8;;Lorem\033\\adipiscing elit, sed\033]8;;\033\\ |
  1461. | \033]8;;Lorem\033\\do eiusmod tempor\033]8;;\033\\ |
  1462. +----------------------+
  1463. TABLE;
  1464. $this->assertSame($expected, $this->getOutputContent($output));
  1465. }
  1466. }