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->assertTrue($event->isAllowingCustomResponseCode());
80 $this->assertSame(200, $event->getResponse()->getStatusCode());
84 * @covers ::onException
86 public function testOnExceptionNewBuildId() {
87 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
88 $expected_form = $form + [
89 '#build_id_old' => 'a_new_build_id',
91 $form_state = new FormState();
92 $exception = new FormAjaxException($form, $form_state);
94 $request = new Request([], ['form_build_id' => 'a_new_build_id']);
96 $response = new Response('');
98 $this->formAjaxResponseBuilder->expects($this->once())
99 ->method('buildResponse')
100 ->with($request, $expected_form, $form_state, $commands)
101 ->willReturn($response);
103 $event = $this->assertResponseFromException($request, $exception, $response);
104 $this->assertTrue($event->isAllowingCustomResponseCode());
105 $this->assertSame(200, $event->getResponse()->getStatusCode());
109 * @covers ::onException
111 public function testOnExceptionOtherClass() {
112 $request = new Request();
113 $exception = new \Exception();
115 $this->formAjaxResponseBuilder->expects($this->never())
116 ->method('buildResponse');
118 $this->assertResponseFromException($request, $exception, NULL);
122 * @covers ::onException
124 public function testOnExceptionResponseBuilderException() {
125 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
126 $expected_form = $form + [
127 '#build_id_old' => 'the_build_id',
129 $form_state = new FormState();
130 $exception = new FormAjaxException($form, $form_state);
131 $request = new Request([], ['form_build_id' => 'the_build_id']);
134 $expected_exception = new HttpException(500, 'The specified #ajax callback is empty or not callable.');
135 $this->formAjaxResponseBuilder->expects($this->once())
136 ->method('buildResponse')
137 ->with($request, $expected_form, $form_state, $commands)
138 ->willThrowException($expected_exception);
140 $event = $this->assertResponseFromException($request, $exception, NULL);
141 $this->assertSame($expected_exception, $event->getException());
145 * @covers ::onException
147 public function testOnExceptionBrokenPostRequest() {
148 $this->formAjaxResponseBuilder->expects($this->never())
149 ->method('buildResponse');
150 $this->subscriber = $this->getMockBuilder('\Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber')
151 ->setConstructorArgs([$this->formAjaxResponseBuilder, $this->getStringTranslationStub()])
152 ->setMethods(['drupalSetMessage', 'formatSize'])
154 $this->subscriber->expects($this->once())
155 ->method('drupalSetMessage')
156 ->willReturn('asdf');
157 $this->subscriber->expects($this->once())
158 ->method('formatSize')
161 $rendered_output = 'the rendered output';
162 // CommandWithAttachedAssetsTrait::getRenderedContent() will call the
163 // renderer service via the container.
164 $renderer = $this->getMock('Drupal\Core\Render\RendererInterface');
165 $renderer->expects($this->once())
166 ->method('renderRoot')
168 ->willReturnCallback(function (&$elements) use ($rendered_output) {
169 $elements['#attached'] = [];
170 return $rendered_output;
172 $container = new ContainerBuilder();
173 $container->set('renderer', $renderer);
174 \Drupal::setContainer($container);
176 $exception = new BrokenPostRequestException(32 * 1e6);
177 $request = new Request([FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]);
179 $event = new GetResponseForExceptionEvent($this->httpKernel, $request, HttpKernelInterface::MASTER_REQUEST, $exception);
180 $this->subscriber->onException($event);
181 $this->assertTrue($event->isAllowingCustomResponseCode());
182 $actual_response = $event->getResponse();
183 $this->assertInstanceOf('\Drupal\Core\Ajax\AjaxResponse', $actual_response);
184 $this->assertSame(200, $actual_response->getStatusCode());
185 $expected_commands[] = [
186 'command' => 'insert',
187 'method' => 'prepend',
189 'data' => $rendered_output,
192 $this->assertSame($expected_commands, $actual_response->getCommands());
196 * @covers ::onException
197 * @covers ::getFormAjaxException
199 public function testOnExceptionNestedException() {
200 $form = ['#type' => 'form', '#build_id' => 'the_build_id'];
201 $expected_form = $form + [
202 '#build_id_old' => 'the_build_id',
204 $form_state = new FormState();
205 $form_exception = new FormAjaxException($form, $form_state);
206 $exception = new \Exception('', 0, $form_exception);
208 $request = new Request([], ['form_build_id' => 'the_build_id']);
210 $response = new Response('');
212 $this->formAjaxResponseBuilder->expects($this->once())
213 ->method('buildResponse')
214 ->with($request, $expected_form, $form_state, $commands)
215 ->willReturn($response);
217 $this->assertResponseFromException($request, $exception, $response);
221 * @covers ::getFormAjaxException
223 public function testOnExceptionNestedWrongException() {
224 $nested_exception = new \Exception();
225 $exception = new \Exception('', 0, $nested_exception);
226 $request = new Request();
228 $this->formAjaxResponseBuilder->expects($this->never())
229 ->method('buildResponse');
231 $this->assertResponseFromException($request, $exception, NULL);
235 * Asserts that the expected response is derived from the given exception.
237 * @param \Symfony\Component\HttpFoundation\Request $request
239 * @param \Exception $exception
240 * The exception to pass to the event.
241 * @param \Symfony\Component\HttpFoundation\Response|null $expected_response
242 * The response expected to be set on the event.
244 * @return \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
245 * The event used to derive the response.
247 protected function assertResponseFromException(Request $request, \Exception $exception, $expected_response) {
248 $event = new GetResponseForExceptionEvent($this->httpKernel, $request, HttpKernelInterface::MASTER_REQUEST, $exception);
249 $this->subscriber->onException($event);
251 $this->assertSame($expected_response, $event->getResponse());