TreeHelperTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <?php
  2. namespace Tools\Test\TestCase\View\Helper;
  3. use Cake\Datasource\ConnectionManager;
  4. use Cake\ORM\Entity;
  5. use Cake\View\View;
  6. use Shim\TestSuite\TestCase;
  7. use Tools\View\Helper\TreeHelper;
  8. class TreeHelperTest extends TestCase {
  9. /**
  10. * @var array
  11. */
  12. protected $fixtures = [
  13. 'plugin.Tools.AfterTrees',
  14. ];
  15. /**
  16. * @var \Cake\ORM\Table
  17. */
  18. protected $Table;
  19. /**
  20. * @var \Tools\View\Helper\TreeHelper
  21. */
  22. protected $Tree;
  23. /**
  24. * Initial Tree
  25. *
  26. * - One
  27. * -- One-SubA
  28. * - Two
  29. * -- Two-SubA
  30. * --- Two-SubA-1
  31. * ---- Two-SubA-1-1
  32. * - Three
  33. * - Four
  34. * -- Four-SubA
  35. *
  36. * @return void
  37. */
  38. public function setUp(): void {
  39. parent::setUp();
  40. $this->Tree = new TreeHelper(new View(null));
  41. $this->Table = $this->getTableLocator()->get('AfterTrees');
  42. $this->Table->addBehavior('Tree');
  43. $connection = ConnectionManager::get('test');
  44. $sql = $this->Table->getSchema()->truncateSql($connection);
  45. foreach ($sql as $snippet) {
  46. $connection->execute($snippet);
  47. }
  48. $data = [
  49. ['name' => 'One'],
  50. ['name' => 'Two'],
  51. ['name' => 'Three'],
  52. ['name' => 'Four'],
  53. ['name' => 'One-SubA', 'parent_id' => 1],
  54. ['name' => 'Two-SubA', 'parent_id' => 2],
  55. ['name' => 'Four-SubA', 'parent_id' => 4],
  56. ['name' => 'Two-SubA-1', 'parent_id' => 6],
  57. ['name' => 'Two-SubA-1-1', 'parent_id' => 8],
  58. ];
  59. foreach ($data as $row) {
  60. $row = new Entity($row);
  61. $this->Table->save($row);
  62. }
  63. }
  64. /**
  65. * @return void
  66. */
  67. public function tearDown(): void {
  68. unset($this->Table);
  69. $this->getTableLocator()->clear();
  70. parent::tearDown();
  71. }
  72. /**
  73. * @return void
  74. */
  75. public function testGenerate() {
  76. $tree = $this->Table->find('threaded')->toArray();
  77. $output = $this->Tree->generate($tree);
  78. $expected = <<<TEXT
  79. <ul>
  80. <li>One
  81. <ul>
  82. <li>One-SubA</li>
  83. </ul>
  84. </li>
  85. <li>Two
  86. <ul>
  87. <li>Two-SubA
  88. <ul>
  89. <li>Two-SubA-1
  90. <ul>
  91. <li>Two-SubA-1-1</li>
  92. </ul>
  93. </li>
  94. </ul>
  95. </li>
  96. </ul>
  97. </li>
  98. <li>Three</li>
  99. <li>Four
  100. <ul>
  101. <li>Four-SubA</li>
  102. </ul>
  103. </li>
  104. </ul>
  105. TEXT;
  106. $this->assertTextEquals($expected, $output);
  107. $this->assertTrue(substr_count($output, '<ul>') === substr_count($output, '</ul>'));
  108. $this->assertTrue(substr_count($output, '<li>') === substr_count($output, '</li>'));
  109. }
  110. /**
  111. * @return void
  112. */
  113. public function testGenerateWithFindAll() {
  114. $tree = $this->Table->find('all', ['order' => ['lft' => 'ASC']])->toArray();
  115. $output = $this->Tree->generate($tree);
  116. $expected = <<<TEXT
  117. <ul>
  118. <li>One
  119. <ul>
  120. <li>One-SubA</li>
  121. </ul>
  122. </li>
  123. <li>Two
  124. <ul>
  125. <li>Two-SubA
  126. <ul>
  127. <li>Two-SubA-1
  128. <ul>
  129. <li>Two-SubA-1-1</li>
  130. </ul>
  131. </li>
  132. </ul>
  133. </li>
  134. </ul>
  135. </li>
  136. <li>Three</li>
  137. <li>Four
  138. <ul>
  139. <li>Four-SubA</li>
  140. </ul>
  141. </li>
  142. </ul>
  143. TEXT;
  144. $output = str_replace(["\t", "\r", "\n"], '', $output);
  145. $expected = str_replace(["\t", "\r", "\n"], '', $expected);
  146. $this->assertTextEquals($expected, $output);
  147. }
  148. /**
  149. * @return void
  150. */
  151. public function testGenerateWithDepth() {
  152. $tree = $this->Table->find('threaded')->toArray();
  153. $output = $this->Tree->generate($tree, ['depth' => 1]);
  154. $expected = <<<TEXT
  155. <ul>
  156. <li>One
  157. <ul>
  158. <li>One-SubA</li>
  159. </ul>
  160. </li>
  161. <li>Two
  162. <ul>
  163. <li>Two-SubA
  164. <ul>
  165. <li>Two-SubA-1
  166. <ul>
  167. <li>Two-SubA-1-1</li>
  168. </ul>
  169. </li>
  170. </ul>
  171. </li>
  172. </ul>
  173. </li>
  174. <li>Three</li>
  175. <li>Four
  176. <ul>
  177. <li>Four-SubA</li>
  178. </ul>
  179. </li>
  180. </ul>
  181. TEXT;
  182. $this->assertTextEquals($expected, $output);
  183. }
  184. /**
  185. * @return void
  186. */
  187. public function testGenerateWithSettings() {
  188. $tree = $this->Table->find('threaded')->toArray();
  189. $output = $this->Tree->generate($tree, ['class' => 'navi', 'id' => 'main', 'type' => 'ol']);
  190. $expected = <<<TEXT
  191. <ol class="navi" id="main">
  192. <li>One
  193. <ol>
  194. <li>One-SubA</li>
  195. </ol>
  196. </li>
  197. <li>Two
  198. <ol>
  199. <li>Two-SubA
  200. <ol>
  201. <li>Two-SubA-1
  202. <ol>
  203. <li>Two-SubA-1-1</li>
  204. </ol>
  205. </li>
  206. </ol>
  207. </li>
  208. </ol>
  209. </li>
  210. <li>Three</li>
  211. <li>Four
  212. <ol>
  213. <li>Four-SubA</li>
  214. </ol>
  215. </li>
  216. </ol>
  217. TEXT;
  218. $this->assertTextEquals($expected, $output);
  219. }
  220. /**
  221. * @return void
  222. */
  223. public function testGenerateWithSameTypeAndItemType() {
  224. $tree = $this->Table->find('threaded')->toArray();
  225. $output = $this->Tree->generate($tree, ['type' => 'div', 'itemType' => 'div']);
  226. $expected = <<<TEXT
  227. <div>
  228. <div>One
  229. <div>
  230. <div>One-SubA</div>
  231. </div>
  232. </div>
  233. <div>Two
  234. <div>
  235. <div>Two-SubA
  236. <div>
  237. <div>Two-SubA-1
  238. <div>
  239. <div>Two-SubA-1-1</div>
  240. </div>
  241. </div>
  242. </div>
  243. </div>
  244. </div>
  245. </div>
  246. <div>Three</div>
  247. <div>Four
  248. <div>
  249. <div>Four-SubA</div>
  250. </div>
  251. </div>
  252. </div>
  253. TEXT;
  254. $this->assertTextEquals($expected, $output);
  255. }
  256. /**
  257. * @return void
  258. */
  259. public function testGenerateWithMaxDepth() {
  260. $tree = $this->Table->find('threaded')->toArray();
  261. $output = $this->Tree->generate($tree, ['maxDepth' => 2]);
  262. $expected = <<<TEXT
  263. <ul>
  264. <li>One
  265. <ul>
  266. <li>One-SubA</li>
  267. </ul>
  268. </li>
  269. <li>Two
  270. <ul>
  271. <li>Two-SubA
  272. <ul>
  273. <li>Two-SubA-1</li>
  274. </ul>
  275. </li>
  276. </ul>
  277. </li>
  278. <li>Three</li>
  279. <li>Four
  280. <ul>
  281. <li>Four-SubA</li>
  282. </ul>
  283. </li>
  284. </ul>
  285. TEXT;
  286. $this->assertTextEquals($expected, $output);
  287. }
  288. /**
  289. * @return void
  290. */
  291. public function testGenerateWithAutoPath() {
  292. $tree = $this->Table->find('threaded')->toArray();
  293. $output = $this->Tree->generate($tree, ['autoPath' => [7, 10]]); // Two-SubA-1
  294. $expected = <<<TEXT
  295. <ul>
  296. <li>One
  297. <ul>
  298. <li>One-SubA</li>
  299. </ul>
  300. </li>
  301. <li class="active">Two
  302. <ul>
  303. <li class="active">Two-SubA
  304. <ul>
  305. <li class="active">Two-SubA-1
  306. <ul>
  307. <li>Two-SubA-1-1</li>
  308. </ul>
  309. </li>
  310. </ul>
  311. </li>
  312. </ul>
  313. </li>
  314. <li>Three</li>
  315. <li>Four
  316. <ul>
  317. <li>Four-SubA</li>
  318. </ul>
  319. </li>
  320. </ul>
  321. TEXT;
  322. $this->assertTextEquals($expected, $output);
  323. $output = $this->Tree->generate($tree, ['autoPath' => [8, 9]]); // Two-SubA-1-1
  324. $expected = <<<TEXT
  325. <ul>
  326. <li>One
  327. <ul>
  328. <li>One-SubA</li>
  329. </ul>
  330. </li>
  331. <li class="active">Two
  332. <ul>
  333. <li class="active">Two-SubA
  334. <ul>
  335. <li class="active">Two-SubA-1
  336. <ul>
  337. <li class="active">Two-SubA-1-1</li>
  338. </ul>
  339. </li>
  340. </ul>
  341. </li>
  342. </ul>
  343. </li>
  344. <li>Three</li>
  345. <li>Four
  346. <ul>
  347. <li>Four-SubA</li>
  348. </ul>
  349. </li>
  350. </ul>
  351. TEXT;
  352. $this->assertTextEquals($expected, $output);
  353. }
  354. /**
  355. * @return void
  356. */
  357. public function testGenerateWithAutoPathAsEntity() {
  358. $tree = $this->Table->find('threaded')->toArray();
  359. $entity = new Entity();
  360. $entity->lft = 7;
  361. $entity->rght = 10;
  362. $output = $this->Tree->generate($tree, ['autoPath' => $entity]);
  363. $expected = <<<TEXT
  364. <ul>
  365. <li>One
  366. <ul>
  367. <li>One-SubA</li>
  368. </ul>
  369. </li>
  370. <li class="active">Two
  371. <ul>
  372. <li class="active">Two-SubA
  373. <ul>
  374. <li class="active">Two-SubA-1
  375. <ul>
  376. <li>Two-SubA-1-1</li>
  377. </ul>
  378. </li>
  379. </ul>
  380. </li>
  381. </ul>
  382. </li>
  383. <li>Three</li>
  384. <li>Four
  385. <ul>
  386. <li>Four-SubA</li>
  387. </ul>
  388. </li>
  389. </ul>
  390. TEXT;
  391. $this->assertTextEquals($expected, $output);
  392. }
  393. /**
  394. * - One
  395. * -- One-SubA
  396. * - Two
  397. * -- Two-SubA
  398. * --- Two-SubA-1
  399. * ---- Two-SubA-1-1
  400. * -- Two-SubB
  401. * -- Two-SubC
  402. * - Three
  403. * - Four
  404. * -- Four-SubA
  405. *
  406. * @return void
  407. */
  408. public function testGenerateWithAutoPathAndHideUnrelated() {
  409. $data = [
  410. ['name' => 'Two-SubB', 'parent_id' => 2],
  411. ['name' => 'Two-SubC', 'parent_id' => 2],
  412. ];
  413. foreach ($data as $row) {
  414. $row = new Entity($row);
  415. $this->Table->save($row);
  416. }
  417. $tree = $this->Table->find('threaded')->toArray();
  418. $id = 6;
  419. $nodes = $this->Table->find('path', ['for' => $id]);
  420. $path = $nodes->extract('id')->toArray();
  421. $output = $this->Tree->generate($tree, ['autoPath' => [6, 11], 'hideUnrelated' => true, 'treePath' => $path, 'callback' => [$this, '_myCallback']]); // Two-SubA
  422. $expected = <<<TEXT
  423. <ul>
  424. <li>One</li>
  425. <li class="active">Two (active)
  426. <ul>
  427. <li class="active">Two-SubA (active)
  428. <ul>
  429. <li>Two-SubA-1</li>
  430. </ul>
  431. </li>
  432. <li>Two-SubB</li>
  433. <li>Two-SubC</li>
  434. </ul>
  435. </li>
  436. <li>Three</li>
  437. <li>Four</li>
  438. </ul>
  439. TEXT;
  440. $output = str_replace(["\t"], '', $output);
  441. $expected = str_replace(["\t"], '', $expected);
  442. $this->assertTextEquals($expected, $output);
  443. }
  444. /**
  445. * - One
  446. * -- One-SubA
  447. * - Two
  448. * -- Two-SubA
  449. * --- Two-SubA-1
  450. * ---- Two-SubA-1-1
  451. * -- Two-SubB
  452. * -- Two-SubC
  453. * - Three
  454. * - Four
  455. * -- Four-SubA
  456. *
  457. * @return void
  458. */
  459. public function testGenerateWithAutoPathAndHideUnrelatedAndSiblings() {
  460. $data = [
  461. ['name' => 'Two-SubB', 'parent_id' => 2],
  462. ['name' => 'Two-SubC', 'parent_id' => 2],
  463. ];
  464. foreach ($data as $row) {
  465. $row = new Entity($row);
  466. $this->Table->save($row);
  467. }
  468. $tree = $this->Table->find('threaded')->toArray();
  469. $id = 6; // Two-SubA
  470. $nodes = $this->Table->find('path', ['for' => $id]);
  471. $path = $nodes->extract('id')->toArray();
  472. $output = $this->Tree->generate($tree, [
  473. 'autoPath' => [6, 11], 'hideUnrelated' => true, 'treePath' => $path,
  474. 'callback' => [$this, '_myCallbackSiblings']]);
  475. $expected = <<<TEXT
  476. <ul>
  477. <li>One (sibling)</li>
  478. <li class="active">Two (active)
  479. <ul>
  480. <li class="active">Two-SubA (active)
  481. <ul>
  482. <li>Two-SubA-1</li>
  483. </ul>
  484. </li>
  485. <li>Two-SubB</li>
  486. <li>Two-SubC</li>
  487. </ul>
  488. </li>
  489. <li>Three (sibling)</li>
  490. <li>Four (sibling)</li>
  491. </ul>
  492. TEXT;
  493. $output = str_replace(["\t", "\r", "\n"], '', $output);
  494. $expected = str_replace(["\t", "\r", "\n"], '', $expected);
  495. $this->assertTextEquals($expected, $output);
  496. }
  497. /**
  498. * @param array $data
  499. * @return string|null
  500. */
  501. public function _myCallback(array $data) {
  502. /** @var \Cake\ORM\Entity $entity */
  503. $entity = $data['data'];
  504. if (!empty($entity['hide'])) {
  505. return null;
  506. }
  507. return $data['data']['name'] . ($data['activePathElement'] ? ' (active)' : '');
  508. }
  509. /**
  510. * @param array $data
  511. * @return string|null
  512. */
  513. public function _myCallbackSiblings(array $data) {
  514. /** @var \Cake\ORM\Entity $entity */
  515. $entity = $data['data'];
  516. if (!empty($entity['hide'])) {
  517. return null;
  518. }
  519. if ($data['depth'] == 0 && $data['isSibling']) {
  520. return $entity['name'] . ' (sibling)';
  521. }
  522. return $entity['name'] . ($data['activePathElement'] ? ' (active)' : '');
  523. }
  524. /**
  525. * @return void
  526. */
  527. public function testGenerateProductive() {
  528. $tree = $this->Table->find('threaded')->toArray();
  529. $output = $this->Tree->generate($tree, ['indent' => false]);
  530. $expected = '<ul><li>One<ul><li>One-SubA</li></ul></li><li>Two<ul><li>Two-SubA<ul><li>Two-SubA-1<ul><li>Two-SubA-1-1</li></ul></li></ul></li></ul></li><li>Three</li><li>Four<ul><li>Four-SubA</li></ul></li></ul>';
  531. $this->assertTextEquals($expected, $output);
  532. }
  533. /**
  534. * @return void
  535. */
  536. public function testGenerateWithEntityUsage() {
  537. $data = [
  538. ['name' => 'Two-SubB', 'parent_id' => 2],
  539. ['name' => 'Two-SubC', 'parent_id' => 2],
  540. ];
  541. foreach ($data as $row) {
  542. $row = new Entity($row);
  543. $this->Table->save($row);
  544. }
  545. $tree = $this->Table->find('threaded')->toArray();
  546. $id = 6;
  547. $nodes = $this->Table->find('path', ['for' => $id]);
  548. $path = $nodes->extract('id')->toArray();
  549. $output = $this->Tree->generate($tree, [
  550. 'autoPath' => [6, 11], 'treePath' => $path,
  551. 'callback' => [$this, '_myCallbackEntity']]); // Two-SubA
  552. $expected = <<<TEXT
  553. <ul>
  554. <li>One
  555. <ul>
  556. <li>One-SubA</li>
  557. </ul>
  558. </li>
  559. <li class="active">Two (active)
  560. <ul>
  561. <li class="active">Two-SubA (active)
  562. <ul>
  563. <li>Two-SubA-1
  564. <ul>
  565. <li>Two-SubA-1-1</li>
  566. </ul>
  567. </li>
  568. </ul>
  569. </li>
  570. <li>Two-SubB</li>
  571. <li>Two-SubC</li>
  572. </ul>
  573. </li>
  574. <li>Three</li>
  575. <li>Four
  576. <ul>
  577. <li>Four-SubA</li>
  578. </ul>
  579. </li>
  580. </ul>
  581. TEXT;
  582. $output = str_replace(["\t", "\r", "\n"], '', $output);
  583. $expected = str_replace(["\t", "\r", "\n"], '', $expected);
  584. $this->assertTextEquals($expected, $output);
  585. }
  586. /**
  587. * @param array $data
  588. * @return string|null
  589. */
  590. public function _myCallbackEntity(array $data) {
  591. /** @var \Cake\ORM\Entity $entity */
  592. $entity = $data['data'];
  593. return h($entity->name) . ($data['activePathElement'] ? ' (active)' : '');
  594. }
  595. }