3 namespace Drupal\Tests\views\Unit\Controller;
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;
16 * @coversDefaultClass \Drupal\views\Controller\ViewAjaxController
19 class ViewAjaxControllerTest extends UnitTestCase {
21 const USE_AJAX = TRUE;
22 const USE_NO_AJAX = FALSE;
25 * The mocked view entity storage.
27 * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
29 protected $viewStorage;
32 * The mocked executable factory.
34 * @var \Drupal\views\ViewExecutableFactory|\PHPUnit_Framework_MockObject_MockObject
36 protected $executableFactory;
39 * The tested views ajax controller.
41 * @var \Drupal\views\Controller\ViewAjaxController
43 protected $viewAjaxController;
46 * The mocked current path.
48 * @var \Drupal\Core\Path\CurrentPathStack|\PHPUnit_Framework_MockObject_MockObject
50 protected $currentPath;
53 * The redirect destination.
55 * @var \Drupal\Core\Routing\RedirectDestinationInterface|\PHPUnit_Framework_MockObject_MockObject
57 protected $redirectDestination;
62 * @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject
69 protected function setUp() {
70 $this->viewStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
71 $this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory')
72 ->disableOriginalConstructor()
74 $this->renderer = $this->getMock('\Drupal\Core\Render\RendererInterface');
75 $this->renderer->expects($this->any())
77 ->will($this->returnCallback(function (array &$elements) {
78 $elements['#attached'] = [];
79 return isset($elements['#markup']) ? $elements['#markup'] : '';
81 $this->renderer->expects($this->any())
82 ->method('executeInRenderContext')
83 ->willReturnCallback(function (RenderContext $context, callable $callable) {
86 $this->currentPath = $this->getMockBuilder('Drupal\Core\Path\CurrentPathStack')
87 ->disableOriginalConstructor()
89 $this->redirectDestination = $this->getMock('\Drupal\Core\Routing\RedirectDestinationInterface');
91 $this->viewAjaxController = new ViewAjaxController($this->viewStorage, $this->executableFactory, $this->renderer, $this->currentPath, $this->redirectDestination);
93 $element_info_manager = $this->getMock('\Drupal\Core\Render\ElementInfoManagerInterface');
94 $request_stack = new RequestStack();
95 $request_stack->push(new Request());
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'),
104 'required_cache_contexts' => [
105 'languages:language_interface',
110 $this->renderer = $this->getMockBuilder('Drupal\Core\Render\Renderer')
111 ->setConstructorArgs($args)
114 $container = new ContainerBuilder();
115 $container->set('renderer', $this->renderer);
116 \Drupal::setContainer($container);
120 * Tests missing view_name and view_display_id
122 public function testMissingViewName() {
123 $request = new Request();
124 $this->setExpectedException(NotFoundHttpException::class);
125 $this->viewAjaxController->ajaxView($request);
129 * Tests with view_name and view_display_id but not existing view.
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');
136 $this->viewStorage->expects($this->once())
139 ->will($this->returnValue(FALSE));
141 $this->setExpectedException(NotFoundHttpException::class);
142 $this->viewAjaxController->ajaxView($request);
146 * Tests a view without having access to it.
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');
153 $view = $this->getMockBuilder('Drupal\views\Entity\View')
154 ->disableOriginalConstructor()
157 $this->viewStorage->expects($this->once())
160 ->will($this->returnValue($view));
162 $executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
163 ->disableOriginalConstructor()
165 $executable->expects($this->once())
167 ->will($this->returnValue(FALSE));
169 $this->executableFactory->expects($this->once())
172 ->will($this->returnValue($executable));
174 $this->setExpectedException(AccessDeniedHttpException::class);
175 $this->viewAjaxController->ajaxView($request);
179 * Tests a valid view without arguments pagers etc.
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');
190 list($view, $executable) = $this->setupValidMocks();
192 $this->redirectDestination->expects($this->atLeastOnce())
194 ->with('/test-page?type=article');
196 $response = $this->viewAjaxController->ajaxView($request);
197 $this->assertTrue($response instanceof ViewAjaxResponse);
199 $this->assertSame($response->getView(), $executable);
201 $this->assertViewResultCommand($response);
205 * Tests a valid view without ajax enabled.
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');
216 $this->setupValidMocks(static::USE_NO_AJAX);
218 $this->setExpectedException(AccessDeniedHttpException::class);
219 $this->viewAjaxController->ajaxView($request);
223 * Tests a valid view with arguments.
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');
231 list($view, $executable) = $this->setupValidMocks();
232 $executable->expects($this->once())
234 ->with('page_1', ['arg1', 'arg2']);
236 $response = $this->viewAjaxController->ajaxView($request);
237 $this->assertTrue($response instanceof ViewAjaxResponse);
239 $this->assertViewResultCommand($response);
243 * Tests a valid view with arguments.
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/');
252 list($view, $executable) = $this->setupValidMocks();
253 $executable->expects($this->once())
255 ->with('page_1', $this->identicalTo(['arg1', NULL]));
257 $response = $this->viewAjaxController->ajaxView($request);
258 $this->assertTrue($response instanceof ViewAjaxResponse);
260 $this->assertViewResultCommand($response);
264 * Tests a valid view with a pager.
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');
274 list($view, $executable) = $this->setupValidMocks();
276 $display_handler = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
277 ->disableOriginalConstructor()
279 $display_handler->expects($this->once())
280 ->method('setOption', '0')
281 ->with($this->equalTo('pager_element'));
283 $display_collection = $this->getMockBuilder('Drupal\views\DisplayPluginCollection')
284 ->disableOriginalConstructor()
286 $display_collection->expects($this->any())
289 ->will($this->returnValue($display_handler));
290 $executable->displayHandlers = $display_collection;
292 $response = $this->viewAjaxController->ajaxView($request);
293 $this->assertTrue($response instanceof ViewAjaxResponse);
295 $commands = $this->getCommands($response);
296 $this->assertEquals('viewsScrollTop', $commands[0]['command']);
297 $this->assertEquals('.js-view-dom-id-' . $dom_id, $commands[0]['selector']);
299 $this->assertViewResultCommand($response, 1);
303 * Sets up a bunch of valid mocks like the view entity and executable.
305 * @param bool $use_ajax
306 * Whether the 'use_ajax' option is set on the view display. Defaults to
310 * A pair of view storage entity and executable.
312 protected function setupValidMocks($use_ajax = self::USE_AJAX) {
313 $view = $this->getMockBuilder('Drupal\views\Entity\View')
314 ->disableOriginalConstructor()
317 $this->viewStorage->expects($this->once())
320 ->will($this->returnValue($view));
322 $executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
323 ->disableOriginalConstructor()
325 $executable->expects($this->once())
327 ->will($this->returnValue(TRUE));
328 $executable->expects($this->any())
329 ->method('setDisplay')
331 $executable->expects($this->atMost(1))
333 ->will($this->returnValue(['#markup' => 'View result']));
335 $this->executableFactory->expects($this->once())
338 ->will($this->returnValue($executable));
340 $display_handler = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
341 ->disableOriginalConstructor()
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('ajaxEnabled')
348 ->willReturn($use_ajax);
350 $display_collection = $this->getMockBuilder('Drupal\views\DisplayPluginCollection')
351 ->disableOriginalConstructor()
353 $display_collection->expects($this->any())
356 ->will($this->returnValue($display_handler));
358 $executable->display_handler = $display_handler;
359 $executable->displayHandlers = $display_collection;
361 return [$view, $executable];
365 * Gets the commands entry from the response object.
367 * @param \Drupal\views\Ajax\ViewAjaxResponse $response
368 * The views ajax response object.
371 * Returns the commands.
373 protected function getCommands(ViewAjaxResponse $response) {
374 $reflection_property = new \ReflectionProperty('Drupal\views\Ajax\ViewAjaxResponse', 'commands');
375 $reflection_property->setAccessible(TRUE);
376 $commands = $reflection_property->getValue($response);
381 * Ensures that the main view content command is added.
383 * @param \Drupal\views\Ajax\ViewAjaxResponse $response
384 * The response object.
385 * @param int $position
386 * The position where the view content command is expected.
388 protected function assertViewResultCommand(ViewAjaxResponse $response, $position = 0) {
389 $commands = $this->getCommands($response);
390 $this->assertEquals('insert', $commands[$position]['command']);
391 $this->assertEquals('View result', $commands[$position]['data']);