Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / system / tests / src / Kernel / Block / SystemMenuBlockTest.php
1 <?php
2
3 namespace Drupal\Tests\system\Kernel\Block;
4
5 use Drupal\KernelTests\KernelTestBase;
6 use Drupal\system\Entity\Menu;
7 use Drupal\block\Entity\Block;
8 use Drupal\Core\Render\Element;
9 use Drupal\system\Tests\Routing\MockRouteProvider;
10 use Drupal\Tests\Core\Menu\MenuLinkMock;
11 use Drupal\user\Entity\User;
12 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
13 use Symfony\Component\HttpFoundation\Request;
14 use Symfony\Component\Routing\Route;
15 use Symfony\Component\Routing\RouteCollection;
16
17 /**
18  * Tests \Drupal\system\Plugin\Block\SystemMenuBlock.
19  *
20  * @group Block
21  * @todo Expand test coverage to all SystemMenuBlock functionality, including
22  *   block_menu_delete().
23  *
24  * @see \Drupal\system\Plugin\Derivative\SystemMenuBlock
25  * @see \Drupal\system\Plugin\Block\SystemMenuBlock
26  */
27 class SystemMenuBlockTest extends KernelTestBase {
28
29   /**
30    * Modules to enable.
31    *
32    * @var array
33    */
34   public static $modules = [
35     'system',
36     'block',
37     'menu_test',
38     'menu_link_content',
39     'field',
40     'user',
41     'link',
42   ];
43
44   /**
45    * The block under test.
46    *
47    * @var \Drupal\system\Plugin\Block\SystemMenuBlock
48    */
49   protected $block;
50
51   /**
52    * The menu for testing.
53    *
54    * @var \Drupal\system\MenuInterface
55    */
56   protected $menu;
57
58   /**
59    * The menu link tree service.
60    *
61    * @var \Drupal\Core\Menu\MenuLinkTree
62    */
63   protected $linkTree;
64
65   /**
66    * The menu link plugin manager service.
67    *
68    * @var \Drupal\Core\Menu\MenuLinkManagerInterface
69    */
70   protected $menuLinkManager;
71
72   /**
73    * The block manager service.
74    *
75    * @var \Drupal\Core\block\BlockManagerInterface
76    */
77   protected $blockManager;
78
79   /**
80    * {@inheritdoc}
81    */
82   protected function setUp() {
83     parent::setUp();
84     $this->installSchema('system', 'sequences');
85     $this->installEntitySchema('user');
86     $this->installEntitySchema('menu_link_content');
87
88     $account = User::create([
89       'name' => $this->randomMachineName(),
90       'status' => 1,
91     ]);
92     $account->save();
93     $this->container->get('current_user')->setAccount($account);
94
95     $this->menuLinkManager = $this->container->get('plugin.manager.menu.link');
96     $this->linkTree = $this->container->get('menu.link_tree');
97     $this->blockManager = $this->container->get('plugin.manager.block');
98
99     $routes = new RouteCollection();
100     $requirements = ['_access' => 'TRUE'];
101     $options = ['_access_checks' => ['access_check.default']];
102     $routes->add('example1', new Route('/example1', [], $requirements, $options));
103     $routes->add('example2', new Route('/example2', [], $requirements, $options));
104     $routes->add('example3', new Route('/example3', [], $requirements, $options));
105     $routes->add('example4', new Route('/example4', [], $requirements, $options));
106     $routes->add('example5', new Route('/example5', [], $requirements, $options));
107     $routes->add('example6', new Route('/example6', [], $requirements, $options));
108     $routes->add('example7', new Route('/example7', [], $requirements, $options));
109     $routes->add('example8', new Route('/example8', [], $requirements, $options));
110
111     $mock_route_provider = new MockRouteProvider($routes);
112     $this->container->set('router.route_provider', $mock_route_provider);
113
114     // Add a new custom menu.
115     $menu_name = 'mock';
116     $label = $this->randomMachineName(16);
117
118     $this->menu = Menu::create([
119       'id' => $menu_name,
120       'label' => $label,
121       'description' => 'Description text',
122     ]);
123     $this->menu->save();
124
125     // This creates a tree with the following structure:
126     // - 1
127     // - 2
128     //   - 3
129     //     - 4
130     // - 5
131     //   - 7
132     // - 6
133     // - 8
134     // With link 6 being the only external link.
135     $links = [
136       1 => MenuLinkMock::create(['id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '', 'weight' => 0]),
137       2 => MenuLinkMock::create(['id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => '', 'route_parameters' => ['foo' => 'bar'], 'weight' => 1]),
138       3 => MenuLinkMock::create(['id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'weight' => 2]),
139       4 => MenuLinkMock::create(['id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3', 'weight' => 3]),
140       5 => MenuLinkMock::create(['id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '', 'expanded' => TRUE, 'weight' => 4]),
141       6 => MenuLinkMock::create(['id' => 'test.example6', 'route_name' => '', 'url' => 'https://www.drupal.org/', 'title' => 'barbar', 'parent' => '', 'weight' => 5]),
142       7 => MenuLinkMock::create(['id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => 'test.example5', 'weight' => 6]),
143       8 => MenuLinkMock::create(['id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '', 'weight' => 7]),
144     ];
145     foreach ($links as $instance) {
146       $this->menuLinkManager->addDefinition($instance->getPluginId(), $instance->getPluginDefinition());
147     }
148   }
149
150   /**
151    * Tests calculation of a system menu block's configuration dependencies.
152    */
153   public function testSystemMenuBlockConfigDependencies() {
154
155     $block = Block::create([
156       'plugin' => 'system_menu_block:' . $this->menu->id(),
157       'region' => 'footer',
158       'id' => 'machinename',
159       'theme' => 'stark',
160     ]);
161
162     $dependencies = $block->calculateDependencies()->getDependencies();
163     $expected = [
164       'config' => [
165         'system.menu.' . $this->menu->id(),
166       ],
167       'module' => [
168         'system',
169       ],
170       'theme' => [
171         'stark',
172       ],
173     ];
174     $this->assertIdentical($expected, $dependencies);
175   }
176
177   /**
178    * Tests the config start level and depth.
179    */
180   public function testConfigLevelDepth() {
181     // Helper function to generate a configured block instance.
182     $place_block = function ($level, $depth) {
183       return $this->blockManager->createInstance('system_menu_block:' . $this->menu->id(), [
184         'region' => 'footer',
185         'id' => 'machinename',
186         'theme' => 'stark',
187         'level' => $level,
188         'depth' => $depth,
189       ]);
190     };
191
192     // All the different block instances we're going to test.
193     $blocks = [
194       'all' => $place_block(1, 0),
195       'level_1_only' => $place_block(1, 1),
196       'level_2_only' => $place_block(2, 1),
197       'level_3_only' => $place_block(3, 1),
198       'level_1_and_beyond' => $place_block(1, 0),
199       'level_2_and_beyond' => $place_block(2, 0),
200       'level_3_and_beyond' => $place_block(3, 0),
201     ];
202
203     // Scenario 1: test all block instances when there's no active trail.
204     $no_active_trail_expectations = [];
205     $no_active_trail_expectations['all'] = [
206       'test.example1' => [],
207       'test.example2' => [],
208       'test.example5' => [
209         'test.example7' => [],
210        ],
211       'test.example6' => [],
212       'test.example8' => [],
213     ];
214     $no_active_trail_expectations['level_1_only'] = [
215       'test.example1' => [],
216       'test.example2' => [],
217       'test.example5' => [],
218       'test.example6' => [],
219       'test.example8' => [],
220     ];
221     $no_active_trail_expectations['level_2_only'] = [];
222     $no_active_trail_expectations['level_3_only'] = [];
223     $no_active_trail_expectations['level_1_and_beyond'] = $no_active_trail_expectations['all'];
224     $no_active_trail_expectations['level_2_and_beyond'] = $no_active_trail_expectations['level_2_only'];
225     $no_active_trail_expectations['level_3_and_beyond'] = [];
226     foreach ($blocks as $id => $block) {
227       $block_build = $block->build();
228       $items = isset($block_build['#items']) ? $block_build['#items'] : [];
229       $this->assertIdentical($no_active_trail_expectations[$id], $this->convertBuiltMenuToIdTree($items), format_string('Menu block %id with no active trail renders the expected tree.', ['%id' => $id]));
230     }
231
232     // Scenario 2: test all block instances when there's an active trail.
233     $route = $this->container->get('router.route_provider')->getRouteByName('example3');
234     $request = new Request();
235     $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'example3');
236     $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route);
237     $this->container->get('request_stack')->push($request);
238     // \Drupal\Core\Menu\MenuActiveTrail uses the cache collector pattern, which
239     // includes static caching. Since this second scenario simulates a second
240     // request, we must also simulate it for the MenuActiveTrail service, by
241     // clearing the cache collector's static cache.
242     \Drupal::service('menu.active_trail')->clear();
243
244     $active_trail_expectations = [];
245     $active_trail_expectations['all'] = [
246       'test.example1' => [],
247       'test.example2' => [
248         'test.example3' => [
249           'test.example4' => [],
250         ],
251       ],
252       'test.example5' => [
253         'test.example7' => [],
254       ],
255       'test.example6' => [],
256       'test.example8' => [],
257     ];
258     $active_trail_expectations['level_1_only'] = [
259       'test.example1' => [],
260       'test.example2' => [],
261       'test.example5' => [],
262       'test.example6' => [],
263       'test.example8' => [],
264     ];
265     $active_trail_expectations['level_2_only'] = [
266       'test.example3' => [],
267     ];
268     $active_trail_expectations['level_3_only'] = [
269       'test.example4' => [],
270     ];
271     $active_trail_expectations['level_1_and_beyond'] = $active_trail_expectations['all'];
272     $active_trail_expectations['level_2_and_beyond'] = [
273       'test.example3' => [
274         'test.example4' => [],
275       ],
276     ];
277     $active_trail_expectations['level_3_and_beyond'] = $active_trail_expectations['level_3_only'];
278     foreach ($blocks as $id => $block) {
279       $block_build = $block->build();
280       $items = isset($block_build['#items']) ? $block_build['#items'] : [];
281       $this->assertIdentical($active_trail_expectations[$id], $this->convertBuiltMenuToIdTree($items), format_string('Menu block %id with an active trail renders the expected tree.', ['%id' => $id]));
282     }
283   }
284
285   /**
286    * Helper method to allow for easy menu link tree structure assertions.
287    *
288    * Converts the result of MenuLinkTree::build() in a "menu link ID tree".
289    *
290    * @param array $build
291    *   The return value of MenuLinkTree::build()
292    *
293    * @return array
294    *   The "menu link ID tree" representation of the given render array.
295    */
296   protected function convertBuiltMenuToIdTree(array $build) {
297     $level = [];
298     foreach (Element::children($build) as $id) {
299       $level[$id] = [];
300       if (isset($build[$id]['below'])) {
301         $level[$id] = $this->convertBuiltMenuToIdTree($build[$id]['below']);
302       }
303     }
304     return $level;
305   }
306
307 }