3 namespace Drupal\Tests\Core\Form\EventSubscriber;
5 use Drupal\Core\DependencyInjection\ContainerBuilder;
6 use Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber;
7 use Drupal\Core\Form\Exception\BrokenPostRequestException;
8 use Drupal\Core\Form\FormAjaxException;
9 use Drupal\Core\Form\FormBuilderInterface;
10 use Drupal\Core\Form\FormState;
11 use Drupal\Tests\UnitTestCase;
12 use Symfony\Component\HttpFoundation\Request;
13 use Symfony\Component\HttpFoundation\Response;
14 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
15 use Symfony\Component\HttpKernel\Exception\HttpException;
16 use Symfony\Component\HttpKernel\HttpKernelInterface;
19 * @coversDefaultClass \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
20 * @group EventSubscriber
22 class FormAjaxSubscriberTest extends UnitTestCase {
25 * @var \Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
27 protected $subscriber;
30 * @var \Drupal\Core\Form\FormAjaxResponseBuilderInterface|\PHPUnit_Framework_MockObject_MockObject
32 protected $formAjaxResponseBuilder;
35 * @var \Symfony\Component\HttpKernel\HttpKernelInterface|\PHPUnit_Framework_MockObject_MockObject
37 protected $httpKernel;
40 * The mocked string translation.
42 * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
44 protected $stringTranslation;
49 protected function setUp() {
52 $this->httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
53 $this->formAjaxResponseBuilder = $this->getMock('Drupal\Core\Form\FormAjaxResponseBuilderInterface');
54 $this->stringTranslation = $this->getStringTranslationStub();
55 $this->subscriber = new FormAjaxSubscriber($this->formAjaxResponseBuilder, $this->stringTranslation);
59 * @covers ::onException
61 public function testOnException() {
62 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
63 $expected_form = $form + [
64 '#build_id_old' => 'the_build_id',
66 $form_state = new FormState();
67 $exception = new FormAjaxException($form, $form_state);
69 $request = new Request([], ['form_build_id' => 'the_build_id']);
71 $response = new Response('');
73 $this->formAjaxResponseBuilder->expects($this->once())
74 ->method('buildResponse')
75 ->with($request, $expected_form, $form_state, $commands)
76 ->willReturn($response);
78 $event = $this->assertResponseFromException($request, $exception, $response);
79 $this->assertSame(200, $event->getResponse()->headers->get('X-Status-Code'));
83 * @covers ::onException
85 public function testOnExceptionNewBuildId() {
86 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
87 $expected_form = $form + [
88 '#build_id_old' => 'a_new_build_id',
90 $form_state = new FormState();
91 $exception = new FormAjaxException($form, $form_state);
93 $request = new Request([], ['form_build_id' => 'a_new_build_id']);
95 $response = new Response('');
97 $this->formAjaxResponseBuilder->expects($this->once())
98 ->method('buildResponse')
99 ->with($request, $expected_form, $form_state, $commands)
100 ->willReturn($response);
102 $event = $this->assertResponseFromException($request, $exception, $response);
103 $this->assertSame(200, $event->getResponse()->headers->get('X-Status-Code'));
107 * @covers ::onException
109 public function testOnExceptionOtherClass() {
110 $request = new Request();
111 $exception = new \Exception();
113 $this->formAjaxResponseBuilder->expects($this->never())
114 ->method('buildResponse');
116 $this->assertResponseFromException($request, $exception, NULL);
120 * @covers ::onException
122 public function testOnExceptionResponseBuilderException() {
123 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
124 $expected_form = $form + [
125 '#build_id_old' => 'the_build_id',
127 $form_state = new FormState();
128 $exception = new FormAjaxException($form, $form_state);
129 $request = new Request([], ['form_build_id' => 'the_build_id']);
132 $expected_exception = new HttpException(500, 'The specified #ajax callback is empty or not callable.');
133 $this->formAjaxResponseBuilder->expects($this->once())
134 ->method('buildResponse')
135 ->with($request, $expected_form, $form_state, $commands)
136 ->willThrowException($expected_exception);
138 $event = $this->assertResponseFromException($request, $exception, NULL);
139 $this->assertSame($expected_exception, $event->getException());
143 * @covers ::onException
145 public function testOnExceptionBrokenPostRequest() {
146 $this->formAjaxResponseBuilder->expects($this->never())
147 ->method('buildResponse');
148 $this->subscriber = $this->getMockBuilder('\Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber')
149 ->setConstructorArgs([$this->formAjaxResponseBuilder, $this->getStringTranslationStub()])
150 ->setMethods(['drupalSetMessage', 'formatSize'])
152 $this->subscriber->expects($this->once())
153 ->method('drupalSetMessage')
154 ->willReturn('asdf');
155 $this->subscriber->expects($this->once())
156 ->method('formatSize')
159 $rendered_output = 'the rendered output';
160 // CommandWithAttachedAssetsTrait::getRenderedContent() will call the
161 // renderer service via the container.
162 $renderer = $this->getMock('Drupal\Core\Render\RendererInterface');
163 $renderer->expects($this->once())
164 ->method('renderRoot')
166 ->willReturnCallback(function (&$elements) use ($rendered_output) {
167 $elements['#attached'] = [];
168 return $rendered_output;
170 $container = new ContainerBuilder();
171 $container->set('renderer', $renderer);
172 \Drupal::setContainer($container);
174 $exception = new BrokenPostRequestException(32 * 1e6);
175 $request = new Request([FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]);
177 $event = new GetResponseForExceptionEvent($this->httpKernel, $request, HttpKernelInterface::MASTER_REQUEST, $exception);
178 $this->subscriber->onException($event);
179 $actual_response = $event->getResponse();
180 $this->assertInstanceOf('\Drupal\Core\Ajax\AjaxResponse', $actual_response);
181 $this->assertSame(200, $actual_response->headers->get('X-Status-Code'));
182 $expected_commands[] = [
183 'command' => 'insert',
184 'method' => 'replaceWith',
186 'data' => $rendered_output,
189 $this->assertSame($expected_commands, $actual_response->getCommands());
193 * @covers ::onException
194 * @covers ::getFormAjaxException
196 public function testOnExceptionNestedException() {
197 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
198 $expected_form = $form + [
199 '#build_id_old' => 'the_build_id',
201 $form_state = new FormState();
202 $form_exception = new FormAjaxException($form, $form_state);
203 $exception = new \Exception('', 0, $form_exception);
205 $request = new Request([], ['form_build_id' => 'the_build_id']);
207 $response = new Response('');
209 $this->formAjaxResponseBuilder->expects($this->once())
210 ->method('buildResponse')
211 ->with($request, $expected_form, $form_state, $commands)
212 ->willReturn($response);
214 $this->assertResponseFromException($request, $exception, $response);
218 * @covers ::getFormAjaxException
220 public function testOnExceptionNestedWrongException() {
221 $nested_exception = new \Exception();
222 $exception = new \Exception('', 0, $nested_exception);
223 $request = new Request();
225 $this->formAjaxResponseBuilder->expects($this->never())
226 ->method('buildResponse');
228 $this->assertResponseFromException($request, $exception, NULL);
232 * Asserts that the expected response is derived from the given exception.
234 * @param \Symfony\Component\HttpFoundation\Request $request
236 * @param \Exception $exception
237 * The exception to pass to the event.
238 * @param \Symfony\Component\HttpFoundation\Response|null $expected_response
239 * The response expected to be set on the event.
241 * @return \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
242 * The event used to derive the response.
244 protected function assertResponseFromException(Request $request, \Exception $exception, $expected_response) {
245 $event = new GetResponseForExceptionEvent($this->httpKernel, $request, HttpKernelInterface::MASTER_REQUEST, $exception);
246 $this->subscriber->onException($event);
248 $this->assertSame($expected_response, $event->getResponse());