Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / views / tests / src / Unit / Controller / ViewAjaxControllerTest.php
1 <?php
2
3 namespace Drupal\Tests\views\Unit\Controller;
4
5 use Drupal\Core\Render\RenderContext;
6 use Drupal\Tests\UnitTestCase;
7 use Drupal\views\Ajax\ViewAjaxResponse;
8 use Drupal\views\Controller\ViewAjaxController;
9 use Symfony\Component\HttpFoundation\Request;
10 use Symfony\Component\DependencyInjection\ContainerBuilder;
11 use Symfony\Component\HttpFoundation\RequestStack;
12 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
13 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
14
15 /**
16  * @coversDefaultClass \Drupal\views\Controller\ViewAjaxController
17  * @group views
18  */
19 class ViewAjaxControllerTest extends UnitTestCase {
20
21   const USE_AJAX = TRUE;
22   const USE_NO_AJAX = FALSE;
23
24   /**
25    * The mocked view entity storage.
26    *
27    * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
28    */
29   protected $viewStorage;
30
31   /**
32    * The mocked executable factory.
33    *
34    * @var \Drupal\views\ViewExecutableFactory|\PHPUnit_Framework_MockObject_MockObject
35    */
36   protected $executableFactory;
37
38   /**
39    * The tested views ajax controller.
40    *
41    * @var \Drupal\views\Controller\ViewAjaxController
42    */
43   protected $viewAjaxController;
44
45   /**
46    * The mocked current path.
47    *
48    * @var \Drupal\Core\Path\CurrentPathStack|\PHPUnit_Framework_MockObject_MockObject
49    */
50   protected $currentPath;
51
52   /**
53    * The redirect destination.
54    *
55    * @var \Drupal\Core\Routing\RedirectDestinationInterface|\PHPUnit_Framework_MockObject_MockObject
56    */
57   protected $redirectDestination;
58
59   /**
60    * The renderer.
61    *
62    * @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject
63    */
64   protected $renderer;
65
66   /**
67    * {@inheritdoc}
68    */
69   protected function setUp() {
70     $this->viewStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
71     $this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory')
72       ->disableOriginalConstructor()
73       ->getMock();
74     $this->renderer = $this->getMock('\Drupal\Core\Render\RendererInterface');
75     $this->renderer->expects($this->any())
76       ->method('render')
77       ->will($this->returnCallback(function(array &$elements) {
78         $elements['#attached'] = [];
79         return isset($elements['#markup']) ? $elements['#markup'] : '';
80       }));
81     $this->renderer->expects($this->any())
82       ->method('executeInRenderContext')
83       ->willReturnCallback(function (RenderContext $context, callable $callable) {
84         return $callable();
85       });
86     $this->currentPath = $this->getMockBuilder('Drupal\Core\Path\CurrentPathStack')
87       ->disableOriginalConstructor()
88       ->getMock();
89     $this->redirectDestination = $this->getMock('\Drupal\Core\Routing\RedirectDestinationInterface');
90
91     $this->viewAjaxController = new ViewAjaxController($this->viewStorage, $this->executableFactory, $this->renderer, $this->currentPath, $this->redirectDestination);
92
93     $element_info_manager = $this->getMock('\Drupal\Core\Render\ElementInfoManagerInterface');
94     $request_stack = new RequestStack();
95     $request_stack->push(new Request());
96     $args = [
97       $this->getMock('\Drupal\Core\Controller\ControllerResolverInterface'),
98       $this->getMock('\Drupal\Core\Theme\ThemeManagerInterface'),
99       $element_info_manager,
100       $this->getMock('\Drupal\Core\Render\PlaceholderGeneratorInterface'),
101       $this->getMock('\Drupal\Core\Render\RenderCacheInterface'),
102       $request_stack,
103       [
104         'required_cache_contexts' => [
105           'languages:language_interface',
106           'theme',
107         ],
108       ],
109     ];
110     $this->renderer = $this->getMockBuilder('Drupal\Core\Render\Renderer')
111       ->setConstructorArgs($args)
112       ->setMethods(NULL)
113       ->getMock();
114     $container = new ContainerBuilder();
115     $container->set('renderer', $this->renderer);
116     \Drupal::setContainer($container);
117   }
118
119   /**
120    * Tests missing view_name and view_display_id
121    */
122   public function testMissingViewName() {
123     $request = new Request();
124     $this->setExpectedException(NotFoundHttpException::class);
125     $this->viewAjaxController->ajaxView($request);
126   }
127
128   /**
129    * Tests with view_name and view_display_id but not existing view.
130    */
131   public function testMissingView() {
132     $request = new Request();
133     $request->request->set('view_name', 'test_view');
134     $request->request->set('view_display_id', 'page_1');
135
136     $this->viewStorage->expects($this->once())
137       ->method('load')
138       ->with('test_view')
139       ->will($this->returnValue(FALSE));
140
141     $this->setExpectedException(NotFoundHttpException::class);
142     $this->viewAjaxController->ajaxView($request);
143   }
144
145   /**
146    * Tests a view without having access to it.
147    */
148   public function testAccessDeniedView() {
149     $request = new Request();
150     $request->request->set('view_name', 'test_view');
151     $request->request->set('view_display_id', 'page_1');
152
153     $view = $this->getMockBuilder('Drupal\views\Entity\View')
154       ->disableOriginalConstructor()
155       ->getMock();
156
157     $this->viewStorage->expects($this->once())
158       ->method('load')
159       ->with('test_view')
160       ->will($this->returnValue($view));
161
162     $executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
163       ->disableOriginalConstructor()
164       ->getMock();
165     $executable->expects($this->once())
166       ->method('access')
167       ->will($this->returnValue(FALSE));
168
169     $this->executableFactory->expects($this->once())
170       ->method('get')
171       ->with($view)
172       ->will($this->returnValue($executable));
173
174     $this->setExpectedException(AccessDeniedHttpException::class);
175     $this->viewAjaxController->ajaxView($request);
176   }
177
178   /**
179    * Tests a valid view without arguments pagers etc.
180    */
181   public function testAjaxView() {
182     $request = new Request();
183     $request->request->set('view_name', 'test_view');
184     $request->request->set('view_display_id', 'page_1');
185     $request->request->set('view_path', '/test-page');
186     $request->request->set('_wrapper_format', 'ajax');
187     $request->request->set('ajax_page_state', 'drupal.settings[]');
188     $request->request->set('type', 'article');
189
190     list($view, $executable) = $this->setupValidMocks();
191
192     $this->redirectDestination->expects($this->atLeastOnce())
193       ->method('set')
194       ->with('/test-page?type=article');
195
196     $response = $this->viewAjaxController->ajaxView($request);
197     $this->assertTrue($response instanceof ViewAjaxResponse);
198
199     $this->assertSame($response->getView(), $executable);
200
201     $this->assertViewResultCommand($response);
202   }
203
204   /**
205    * Tests a valid view without ajax enabled.
206    */
207   public function testAjaxViewWithoutAjax() {
208     $request = new Request();
209     $request->request->set('view_name', 'test_view');
210     $request->request->set('view_display_id', 'page_1');
211     $request->request->set('view_path', '/test-page');
212     $request->request->set('_wrapper_format', 'ajax');
213     $request->request->set('ajax_page_state', 'drupal.settings[]');
214     $request->request->set('type', 'article');
215
216     $this->setupValidMocks(static::USE_NO_AJAX);
217
218     $this->setExpectedException(AccessDeniedHttpException::class);
219     $this->viewAjaxController->ajaxView($request);
220   }
221
222   /**
223    * Tests a valid view with arguments.
224    */
225   public function testAjaxViewWithArguments() {
226     $request = new Request();
227     $request->request->set('view_name', 'test_view');
228     $request->request->set('view_display_id', 'page_1');
229     $request->request->set('view_args', 'arg1/arg2');
230
231     list($view, $executable) = $this->setupValidMocks();
232     $executable->expects($this->once())
233       ->method('preview')
234       ->with('page_1', ['arg1', 'arg2']);
235
236     $response = $this->viewAjaxController->ajaxView($request);
237     $this->assertTrue($response instanceof ViewAjaxResponse);
238
239     $this->assertViewResultCommand($response);
240   }
241
242   /**
243    * Tests a valid view with arguments.
244    */
245   public function testAjaxViewWithEmptyArguments() {
246     $request = new Request();
247     $request->request->set('view_name', 'test_view');
248     $request->request->set('view_display_id', 'page_1');
249     // Simulate a request that has a second, empty argument.
250     $request->request->set('view_args', 'arg1/');
251
252     list($view, $executable) = $this->setupValidMocks();
253     $executable->expects($this->once())
254       ->method('preview')
255       ->with('page_1', $this->identicalTo(['arg1', NULL]));
256
257     $response = $this->viewAjaxController->ajaxView($request);
258     $this->assertTrue($response instanceof ViewAjaxResponse);
259
260     $this->assertViewResultCommand($response);
261   }
262
263   /**
264    * Tests a valid view with a pager.
265    */
266   public function testAjaxViewWithPager() {
267     $request = new Request();
268     $request->request->set('view_name', 'test_view');
269     $request->request->set('view_display_id', 'page_1');
270     $dom_id = $this->randomMachineName(20);
271     $request->request->set('view_dom_id', $dom_id);
272     $request->request->set('pager_element', '0');
273
274     list($view, $executable) = $this->setupValidMocks();
275
276     $display_handler = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
277       ->disableOriginalConstructor()
278       ->getMock();
279     $display_handler->expects($this->once())
280       ->method('setOption', '0')
281       ->with($this->equalTo('pager_element'));
282
283     $display_collection = $this->getMockBuilder('Drupal\views\DisplayPluginCollection')
284       ->disableOriginalConstructor()
285       ->getMock();
286     $display_collection->expects($this->any())
287       ->method('get')
288       ->with('page_1')
289       ->will($this->returnValue($display_handler));
290     $executable->displayHandlers = $display_collection;
291
292     $response = $this->viewAjaxController->ajaxView($request);
293     $this->assertTrue($response instanceof ViewAjaxResponse);
294
295     $commands = $this->getCommands($response);
296     $this->assertEquals('viewsScrollTop', $commands[0]['command']);
297     $this->assertEquals('.js-view-dom-id-' . $dom_id, $commands[0]['selector']);
298
299     $this->assertViewResultCommand($response, 1);
300   }
301
302   /**
303    * Sets up a bunch of valid mocks like the view entity and executable.
304    *
305    * @param bool $use_ajax
306    *   Whether the 'use_ajax' option is set on the view display. Defaults to
307    *   using ajax (TRUE).
308    *
309    * @return array
310    *   A pair of view storage entity and executable.
311    */
312   protected function setupValidMocks($use_ajax = self::USE_AJAX) {
313     $view = $this->getMockBuilder('Drupal\views\Entity\View')
314       ->disableOriginalConstructor()
315       ->getMock();
316
317     $this->viewStorage->expects($this->once())
318       ->method('load')
319       ->with('test_view')
320       ->will($this->returnValue($view));
321
322     $executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
323       ->disableOriginalConstructor()
324       ->getMock();
325     $executable->expects($this->once())
326       ->method('access')
327       ->will($this->returnValue(TRUE));
328     $executable->expects($this->any())
329       ->method('setDisplay')
330       ->willReturn(TRUE);
331     $executable->expects($this->atMost(1))
332       ->method('preview')
333       ->will($this->returnValue(['#markup' => 'View result']));
334
335     $this->executableFactory->expects($this->once())
336       ->method('get')
337       ->with($view)
338       ->will($this->returnValue($executable));
339
340     $display_handler = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
341       ->disableOriginalConstructor()
342       ->getMock();
343     // Ensure that the pager element is not set.
344     $display_handler->expects($this->never())
345       ->method('setOption');
346     $display_handler->expects($this->any())
347       ->method('getOption')
348       ->with('use_ajax')
349       ->willReturn($use_ajax);
350
351     $display_collection = $this->getMockBuilder('Drupal\views\DisplayPluginCollection')
352       ->disableOriginalConstructor()
353       ->getMock();
354     $display_collection->expects($this->any())
355       ->method('get')
356       ->with('page_1')
357       ->will($this->returnValue($display_handler));
358
359     $executable->display_handler = $display_handler;
360     $executable->displayHandlers = $display_collection;
361
362     return [$view, $executable];
363   }
364
365   /**
366    * Gets the commands entry from the response object.
367    *
368    * @param \Drupal\views\Ajax\ViewAjaxResponse $response
369    *   The views ajax response object.
370    *
371    * @return mixed
372    *   Returns the commands.
373    */
374   protected function getCommands(ViewAjaxResponse $response) {
375     $reflection_property = new \ReflectionProperty('Drupal\views\Ajax\ViewAjaxResponse', 'commands');
376     $reflection_property->setAccessible(TRUE);
377     $commands = $reflection_property->getValue($response);
378     return $commands;
379   }
380
381   /**
382    * Ensures that the main view content command is added.
383    *
384    * @param \Drupal\views\Ajax\ViewAjaxResponse $response
385    *   The response object.
386    * @param int $position
387    *   The position where the view content command is expected.
388    */
389   protected function assertViewResultCommand(ViewAjaxResponse $response, $position = 0) {
390     $commands = $this->getCommands($response);
391     $this->assertEquals('insert', $commands[$position]['command']);
392     $this->assertEquals('View result', $commands[$position]['data']);
393   }
394
395 }