/** * Build all necessary things for child form (form state, etc.). * * @param \Drupal\Core\Entity\EntityFormInterface $controller * Entity form controller for child form. * @param \Drupal\Core\Form\FormStateInterface $form_state * Parent form state object. * @param \Drupal\Core\Entity\EntityInterface $entity * Entity object. * @param string $operation * Operation that is to be performed in inline form. * @param array $parents * Entity form #parents. * * @return \Drupal\Core\Form\FormStateInterface * Child form state object. */ public static function buildChildFormState(EntityFormInterface $controller, FormStateInterface $form_state, EntityInterface $entity, $operation, $parents) { $child_form_state = new FormState(); $child_form_state->addBuildInfo('callback_object', $controller); $child_form_state->addBuildInfo('base_form_id', $controller->getBaseFormID()); $child_form_state->addBuildInfo('form_id', $controller->getFormID()); $child_form_state->addBuildInfo('args', array()); // Copy values to child form. $child_form_state->setCompleteForm($form_state->getCompleteForm()); $child_form_state->setUserInput($form_state->getUserInput()); // Filter out all submitted values that are not directly relevant for this // IEF. Otherwise they might mess things up. $form_state_values = $form_state->getValues(); $form_state_values = static::extractArraySequence($form_state_values, $parents); $child_form_state->setValues($form_state_values); $child_form_state->setStorage($form_state->getStorage()); $value = \Drupal::entityTypeManager()->getStorage('entity_form_display')->load($entity->getEntityTypeId() . '.' . $entity->bundle() . '.' . $operation); $child_form_state->set('form_display', $value); // Since some of the submit handlers are run, redirects need to be disabled. $child_form_state->disableRedirect(); // When a form is rebuilt after Ajax processing, its #build_id and #action // should not change. // @see drupal_rebuild_form() $rebuild_info = $child_form_state->getRebuildInfo(); $rebuild_info['copy']['#build_id'] = TRUE; $rebuild_info['copy']['#action'] = TRUE; $child_form_state->setRebuildInfo($rebuild_info); $child_form_state->set('inline_entity_form', $form_state->get('inline_entity_form')); $child_form_state->set('langcode', $entity->language()->getId()); $child_form_state->set('field', $form_state->get('field')); $child_form_state->setTriggeringElement($form_state->getTriggeringElement()); $child_form_state->setSubmitHandlers($form_state->getSubmitHandlers()); return $child_form_state; }
/** * @covers ::getSelected * * @dataProvider providerTestGetSelected */ public function testGetSelected($expected, $element = [], $parents = [], $user_input = [], $not_rebuilding_expected = NULL) { $not_rebuilding_expected = $not_rebuilding_expected ?: $expected; $form_state = new FormState(); $form_state->setUserInput($user_input); $actual = WizardPluginBase::getSelected($form_state, $parents, 'the_default_value', $element); $this->assertSame($not_rebuilding_expected, $actual); $this->assertSame($user_input, $form_state->getUserInput()); $form_state->setRebuild(); $actual = WizardPluginBase::getSelected($form_state, $parents, 'the_default_value', $element); $this->assertSame($expected, $actual); $this->assertSame($user_input, $form_state->getUserInput()); }
/** * Check several empty values for required forms elements. * * Carriage returns, tabs, spaces, and unchecked checkbox elements are not * valid content for a required field. * * If the form field is found in $form_state->getErrors() then the test pass. */ function testRequiredFields() { // Originates from https://www.drupal.org/node/117748. // Sets of empty strings and arrays. $empty_strings = array('""' => "", '"\\n"' => "\n", '" "' => " ", '"\\t"' => "\t", '" \\n\\t "' => " \n\t ", '"\\n\\n\\n\\n\\n"' => "\n\n\n\n\n"); $empty_arrays = array('array()' => array()); $empty_checkbox = array(NULL); $elements['textfield']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'textfield'); $elements['textfield']['empty_values'] = $empty_strings; $elements['telephone']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'tel'); $elements['telephone']['empty_values'] = $empty_strings; $elements['url']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'url'); $elements['url']['empty_values'] = $empty_strings; $elements['search']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'search'); $elements['search']['empty_values'] = $empty_strings; $elements['password']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'password'); $elements['password']['empty_values'] = $empty_strings; $elements['password_confirm']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'password_confirm'); // Provide empty values for both password fields. foreach ($empty_strings as $key => $value) { $elements['password_confirm']['empty_values'][$key] = array('pass1' => $value, 'pass2' => $value); } $elements['textarea']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'textarea'); $elements['textarea']['empty_values'] = $empty_strings; $elements['radios']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'radios', '#options' => array('' => t('None'), $this->randomMachineName(), $this->randomMachineName(), $this->randomMachineName())); $elements['radios']['empty_values'] = $empty_arrays; $elements['checkbox']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'checkbox', '#required' => TRUE); $elements['checkbox']['empty_values'] = $empty_checkbox; $elements['checkboxes']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'checkboxes', '#options' => array($this->randomMachineName(), $this->randomMachineName(), $this->randomMachineName())); $elements['checkboxes']['empty_values'] = $empty_arrays; $elements['select']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'select', '#options' => array('' => t('None'), $this->randomMachineName(), $this->randomMachineName(), $this->randomMachineName())); $elements['select']['empty_values'] = $empty_strings; $elements['file']['element'] = array('#title' => $this->randomMachineName(), '#type' => 'file'); $elements['file']['empty_values'] = $empty_strings; // Regular expression to find the expected marker on required elements. $required_marker_preg = '@<.*?class=".*?form-required.*?">@'; // Go through all the elements and all the empty values for them. foreach ($elements as $type => $data) { foreach ($data['empty_values'] as $key => $empty) { foreach (array(TRUE, FALSE) as $required) { $form_id = $this->randomMachineName(); $form = array(); $form_state = new FormState(); $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); $element = $data['element']['#title']; $form[$element] = $data['element']; $form[$element]['#required'] = $required; $user_input[$element] = $empty; $user_input['form_id'] = $form_id; $form_state->setUserInput($user_input); $form_state->setFormObject(new StubForm($form_id, $form)); $form_state->setMethod('POST'); // The form token CSRF protection should not interfere with this test, // so we bypass it by setting the token to FALSE. $form['#token'] = FALSE; \Drupal::formBuilder()->prepareForm($form_id, $form, $form_state); \Drupal::formBuilder()->processForm($form_id, $form, $form_state); $errors = $form_state->getErrors(); // Form elements of type 'radios' throw all sorts of PHP notices // when you try to render them like this, so we ignore those for // testing the required marker. // @todo Fix this work-around (https://www.drupal.org/node/588438). $form_output = $type == 'radios' ? '' : \Drupal::service('renderer')->renderRoot($form); if ($required) { // Make sure we have a form error for this element. $this->assertTrue(isset($errors[$element]), "Check empty({$key}) '{$type}' field '{$element}'"); if (!empty($form_output)) { // Make sure the form element is marked as required. $this->assertTrue(preg_match($required_marker_preg, $form_output), "Required '{$type}' field is marked as required"); } } else { if (!empty($form_output)) { // Make sure the form element is *not* marked as required. $this->assertFalse(preg_match($required_marker_preg, $form_output), "Optional '{$type}' field is not marked as required"); } if ($type == 'select') { // Select elements are going to have validation errors with empty // input, since those are illegal choices. Just make sure the // error is not "field is required". $this->assertTrue(empty($errors[$element]) || strpos('field is required', $errors[$element]) === FALSE, "Optional '{$type}' field '{$element}' is not treated as a required element"); } else { // Make sure there is *no* form error for this element. $this->assertTrue(empty($errors[$element]), "Optional '{$type}' field '{$element}' has no errors with empty input"); } } } } } // Clear the expected form error messages so they don't appear as exceptions. drupal_get_messages(); }
/** * Helper function for the option check test to submit a form while collecting errors. * * @param $form_element * A form element to test. * @param $edit * An array containing post data. * * @return * An array containing the processed form, the form_state and any errors. */ private function formSubmitHelper($form, $edit) { $form_id = $this->randomMachineName(); $form_state = new FormState(); $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); // The form token CSRF protection should not interfere with this test, so we // bypass it by setting the token to FALSE. $form['#token'] = FALSE; $edit['form_id'] = $form_id; // Disable page redirect for forms submitted programmatically. This is a // solution to skip the redirect step (there are no pages, then the redirect // isn't possible). $form_state->disableRedirect(); $form_state->setUserInput($edit); $form_state->setFormObject(new StubForm($form_id, $form)); \Drupal::formBuilder()->prepareForm($form_id, $form, $form_state); \Drupal::formBuilder()->processForm($form_id, $form, $form_state); $errors = $form_state->getErrors(); // Clear errors and messages. drupal_get_messages(); $form_state->clearErrors(); // Return the processed form together with form_state and errors // to allow the caller lowlevel access to the form. return array($form, $form_state, $errors); }
/** * Tests the handling of a redirect when FormStateInterface::$response exists. */ public function testHandleRedirectWithResponse() { $form_id = 'test_form_id'; $expected_form = $form_id(); // Set up a response that will be used. $response = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\Response')->disableOriginalConstructor()->getMock(); // Set up a redirect that will not be called. $redirect = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RedirectResponse')->disableOriginalConstructor()->getMock(); $form_arg = $this->getMockForm($form_id, $expected_form); $form_arg->expects($this->any())->method('submitForm')->will($this->returnCallback(function ($form, FormStateInterface $form_state) use($response, $redirect) { // Set both the response and the redirect. $form_state->setResponse($response); $form_state->set('redirect', $redirect); })); $form_state = new FormState(); try { $input['form_id'] = $form_id; $form_state->setUserInput($input); $this->simulateFormSubmission($form_id, $form_arg, $form_state, FALSE); $this->fail('EnforcedResponseException was not thrown.'); } catch (EnforcedResponseException $e) { $this->assertSame($response, $e->getResponse()); } $this->assertSame($response, $form_state->getResponse()); }
/** * @covers ::doBuildForm * * @dataProvider providerTestInvalidToken */ public function testInvalidToken($expected, $valid_token, $user_is_authenticated) { $form_token = 'the_form_token'; $form_id = 'test_form_id'; if (is_bool($valid_token)) { $this->csrfToken->expects($this->any())->method('get')->willReturnArgument(0); $this->csrfToken->expects($this->atLeastOnce())->method('validate')->willReturn($valid_token); } $current_user = $this->prophesize(AccountInterface::class); $current_user->isAuthenticated()->willReturn($user_is_authenticated); $property = new \ReflectionProperty(FormBuilder::class, 'currentUser'); $property->setAccessible(TRUE); $property->setValue($this->formBuilder, $current_user->reveal()); $expected_form = $form_id(); $form_arg = $this->getMockForm($form_id, $expected_form); $form_state = new FormState(); $input['form_id'] = $form_id; $input['form_token'] = $form_token; $form_state->setUserInput($input); $this->simulateFormSubmission($form_id, $form_arg, $form_state, FALSE); $this->assertSame($expected, $form_state->hasInvalidToken()); }
/** * Builds the form state passed to zone members. * * @param array $member_parents * The parents array indicating the position of the member form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The parent form state. * * @return \Drupal\Core\Form\FormStateInterface * The new member form state. */ protected function buildMemberFormState($member_parents, FormStateInterface $form_state) { $member_values = $form_state->getValue($member_parents, []); $member_user_input = (array) NestedArray::getValue($form_state->getUserInput(), $member_parents); $member_form_state = new FormState(); $member_form_state->setValues($member_values); $member_form_state->setUserInput($member_user_input); return $member_form_state; }
/** * Build all necessary things for child form (form state, etc.). * * @param \Drupal\Core\Entity\EntityFormInterface $controller * Entity form controller for child form. * @param \Drupal\Core\Form\FormStateInterface $form_state * Parent form state object. * @param \Drupal\Core\Entity\EntityInterface $entity * Entity object. * @param string $operation * Operation that is to be performed in inline form. * * @return \Drupal\Core\Form\FormStateInterface * Child form state object. */ public static function buildChildFormState(EntityFormInterface $controller, FormStateInterface $form_state, EntityInterface $entity, $operation) { $child_form_state = new FormState(); $child_form_state->addBuildInfo('callback_object', $controller); $child_form_state->addBuildInfo('base_form_id', $controller->getBaseFormID()); $child_form_state->addBuildInfo('form_id', $controller->getFormID()); $child_form_state->addBuildInfo('args', array()); // Copy values to child form. $child_form_state->setUserInput($form_state->getUserInput()); $child_form_state->setValues($form_state->getValues()); $child_form_state->setStorage($form_state->getStorage()); $child_form_state->set('form_display', entity_get_form_display($entity->getEntityTypeId(), $entity->bundle(), $operation)); // Since some of the submit handlers are run, redirects need to be disabled. $child_form_state->disableRedirect(); // When a form is rebuilt after Ajax processing, its #build_id and #action // should not change. // @see drupal_rebuild_form() $rebuild_info = $child_form_state->getRebuildInfo(); $rebuild_info['copy']['#build_id'] = TRUE; $rebuild_info['copy']['#action'] = TRUE; $child_form_state->setRebuildInfo($rebuild_info); $child_form_state->set('inline_entity_form', $form_state->get('inline_entity_form')); $child_form_state->set('langcode', $entity->language()->getId()); $child_form_state->set('field', $form_state->get('field')); $child_form_state->setTriggeringElement($form_state->getTriggeringElement()); $child_form_state->setSubmitHandlers($form_state->getSubmitHandlers()); return $child_form_state; }
/** * Tests selectItemAjax(). * * GIVEN the embridge search form * WHEN the ajax handler for the select button is run * THEN an ajax response should be returned with the expected commands. * * @covers ::selectItemAjax * * @test */ public function selectItemAjaxAjaxCallbackReturnsSaveSearchCloseDialogWithNoErrors() { $form = ['#attached' => ['test']]; $mock_entity_id = 123456789; $input = ['result_chosen' => $mock_entity_id]; $form_state = new FormState(); $form_state->setUserInput($input); $response = $this->form->selectItemAjax($form, $form_state); $this->assertInstanceOf('\\Drupal\\Core\\Ajax\\AjaxResponse', $response); $commands = $response->getCommands(); $this->assertNotEmpty($commands); $this->assertCount(2, $commands); $this->assertEquals('embridgeSearchDialogSave', $commands[0]['command']); $expected_values = ['entity_id' => $mock_entity_id]; $this->assertEquals($expected_values, $commands[0]['values']); $this->assertEquals('closeDialog', $commands[1]['command']); }