diff options
Diffstat (limited to 'modules/simpletest/tests/form.test')
-rw-r--r-- | modules/simpletest/tests/form.test | 1574 |
1 files changed, 0 insertions, 1574 deletions
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test deleted file mode 100644 index 386880e78a1..00000000000 --- a/modules/simpletest/tests/form.test +++ /dev/null @@ -1,1574 +0,0 @@ -<?php - -/** - * @file - * Unit tests for the Drupal Form API. - */ - -class FormsTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form element validation', - 'description' => 'Tests various form element validation mechanisms.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * 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_get_errors() then the test pass. - */ - function testRequiredFields() { - // Originates from http://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->randomName(), '#type' => 'textfield'); - $elements['textfield']['empty_values'] = $empty_strings; - - $elements['password']['element'] = array('#title' => $this->randomName(), '#type' => 'password'); - $elements['password']['empty_values'] = $empty_strings; - - $elements['password_confirm']['element'] = array('#title' => $this->randomName(), '#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->randomName(), '#type' => 'textarea'); - $elements['textarea']['empty_values'] = $empty_strings; - - $elements['radios']['element'] = array('#title' => $this->randomName(), '#type' => 'radios', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); - $elements['radios']['empty_values'] = $empty_arrays; - - $elements['checkbox']['element'] = array('#title' => $this->randomName(), '#type' => 'checkbox', '#required' => TRUE, '#title' => $this->randomName()); - $elements['checkbox']['empty_values'] = $empty_checkbox; - - $elements['checkboxes']['element'] = array('#title' => $this->randomName(), '#type' => 'checkboxes', '#options' => array($this->randomName(), $this->randomName(), $this->randomName())); - $elements['checkboxes']['empty_values'] = $empty_arrays; - - $elements['select']['element'] = array('#title' => $this->randomName(), '#type' => 'select', '#options' => array('' => t('None'), $this->randomName(), $this->randomName(), $this->randomName())); - $elements['select']['empty_values'] = $empty_strings; - - $elements['file']['element'] = array('#title' => $this->randomName(), '#type' => 'file'); - $elements['file']['empty_values'] = $empty_strings; - - // Regular expression to find the expected marker on required elements. - $required_marker_preg = '@<label.*<abbr class="form-required" title="This field is required\.">\*</abbr></label>@'; - - // 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->randomName(); - $form = array(); - $form_state = form_state_defaults(); - form_clear_error(); - $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); - $element = $data['element']['#title']; - $form[$element] = $data['element']; - $form[$element]['#required'] = $required; - $form_state['input'][$element] = $empty; - $form_state['input']['form_id'] = $form_id; - $form_state['method'] = 'post'; - drupal_prepare_form($form_id, $form, $form_state); - drupal_process_form($form_id, $form, $form_state); - $errors = form_get_errors(); - // 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 (http://drupal.org/node/588438). - $form_output = ($type == 'radios') ? '' : drupal_render($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(); - } - - /** - * Test default value handling for checkboxes. - * - * @see _form_test_checkbox() - */ - function testCheckboxProcessing() { - // First, try to submit without the required checkbox. - $edit = array(); - $this->drupalPost('form-test/checkbox', $edit, t('Submit')); - $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory')); - - // Now try to submit the form correctly. - $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit'))); - $expected_values = array( - 'disabled_checkbox_on' => 'disabled_checkbox_on', - 'disabled_checkbox_off' => '', - 'checkbox_on' => 'checkbox_on', - 'checkbox_off' => '', - 'zero_checkbox_on' => '0', - 'zero_checkbox_off' => '', - ); - foreach ($expected_values as $widget => $expected_value) { - $this->assertEqual($values[$widget], $expected_value, t('Checkbox %widget returns expected value (expected: %expected, got: %value)', array( - '%widget' => var_export($widget, TRUE), - '%expected' => var_export($expected_value, TRUE), - '%value' => var_export($values[$widget], TRUE), - ))); - } - } - - /** - * Tests validation of #type 'select' elements. - */ - function testSelect() { - $form = $form_state = array(); - $form = form_test_select($form, $form_state); - $error = '!name field is required.'; - $this->drupalGet('form-test/select'); - - // Posting without any values should throw validation errors. - $this->drupalPost(NULL, array(), 'Submit'); - $this->assertNoText(t($error, array('!name' => $form['select']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['select_required']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['select_optional']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['empty_value']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['empty_value_one']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['no_default_optional']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default_empty_option']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['no_default_empty_option_optional']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default_empty_value']['#title']))); - $this->assertText(t($error, array('!name' => $form['no_default_empty_value_one']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['no_default_empty_value_optional']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['multiple']['#title']))); - $this->assertNoText(t($error, array('!name' => $form['multiple_no_default']['#title']))); - $this->assertText(t($error, array('!name' => $form['multiple_no_default_required']['#title']))); - - // Post values for required fields. - $edit = array( - 'no_default' => 'three', - 'no_default_empty_option' => 'three', - 'no_default_empty_value' => 'three', - 'no_default_empty_value_one' => 'three', - 'multiple_no_default_required[]' => 'three', - ); - $this->drupalPost(NULL, $edit, 'Submit'); - $values = drupal_json_decode($this->drupalGetContent()); - - // Verify expected values. - $expected = array( - 'select' => 'one', - 'empty_value' => 'one', - 'empty_value_one' => 'one', - 'no_default' => 'three', - 'no_default_optional' => 'one', - 'no_default_optional_empty_value' => '', - 'no_default_empty_option' => 'three', - 'no_default_empty_option_optional' => '', - 'no_default_empty_value' => 'three', - 'no_default_empty_value_one' => 'three', - 'no_default_empty_value_optional' => 0, - 'multiple' => array('two' => 'two'), - 'multiple_no_default' => array(), - 'multiple_no_default_required' => array('three' => 'three'), - ); - foreach ($expected as $key => $value) { - $this->assertIdentical($values[$key], $value, t('@name: @actual is equal to @expected.', array( - '@name' => $key, - '@actual' => var_export($values[$key], TRUE), - '@expected' => var_export($value, TRUE), - ))); - } - } - - /** - * Test handling of disabled elements. - * - * @see _form_test_disabled_elements() - */ - function testDisabledElements() { - // Get the raw form in its original state. - $form_state = array(); - $form = _form_test_disabled_elements(array(), $form_state); - - // Build a submission that tries to hijack the form by submitting input for - // elements that are disabled. - $edit = array(); - foreach (element_children($form) as $key) { - if (isset($form[$key]['#test_hijack_value'])) { - if (is_array($form[$key]['#test_hijack_value'])) { - foreach ($form[$key]['#test_hijack_value'] as $subkey => $value) { - $edit[$key . '[' . $subkey . ']'] = $value; - } - } - else { - $edit[$key] = $form[$key]['#test_hijack_value']; - } - } - } - - // Submit the form with no input, as the browser does for disabled elements, - // and fetch the $form_state['values'] that is passed to the submit handler. - $this->drupalPost('form-test/disabled-elements', array(), t('Submit')); - $returned_values['normal'] = drupal_json_decode($this->content); - - // Do the same with input, as could happen if JavaScript un-disables an - // element. drupalPost() emulates a browser by not submitting input for - // disabled elements, so we need to un-disable those elements first. - $this->drupalGet('form-test/disabled-elements'); - $disabled_elements = array(); - foreach ($this->xpath('//*[@disabled]') as $element) { - $disabled_elements[] = (string) $element['name']; - unset($element['disabled']); - } - - // All the elements should be marked as disabled, including the ones below - // the disabled container. - $this->assertEqual(count($disabled_elements), 32, t('The correct elements have the disabled property in the HTML code.')); - - $this->drupalPost(NULL, $edit, t('Submit')); - $returned_values['hijacked'] = drupal_json_decode($this->content); - - // Ensure that the returned values match the form's default values in both - // cases. - foreach ($returned_values as $type => $values) { - $this->assertFormValuesDefault($values, $form); - } - } - - /** - * Assert that the values submitted to a form matches the default values of the elements. - */ - function assertFormValuesDefault($values, $form) { - foreach (element_children($form) as $key) { - if (isset($form[$key]['#default_value'])) { - if (isset($form[$key]['#expected_value'])) { - $expected_value = $form[$key]['#expected_value']; - } - else { - $expected_value = $form[$key]['#default_value']; - } - - if ($key == 'checkboxes_multiple') { - // Checkboxes values are not filtered out. - $values[$key] = array_filter($values[$key]); - } - $this->assertIdentical($expected_value, $values[$key], t('Default value for %type: expected %expected, returned %returned.', array('%type' => $key, '%expected' => var_export($expected_value, TRUE), '%returned' => var_export($values[$key], TRUE)))); - } - - // Recurse children. - $this->assertFormValuesDefault($values, $form[$key]); - } - } - - /** - * Verify markup for disabled form elements. - * - * @see _form_test_disabled_elements() - */ - function testDisabledMarkup() { - $this->drupalGet('form-test/disabled-elements'); - $form_state = array(); - $form = _form_test_disabled_elements(array(), $form_state); - $type_map = array( - 'textarea' => 'textarea', - 'select' => 'select', - 'weight' => 'select', - 'date' => 'select', - ); - - foreach ($form as $name => $item) { - // Skip special #types. - if (!isset($item['#type']) || in_array($item['#type'], array('hidden', 'text_format'))) { - continue; - } - // Setup XPath and CSS class depending on #type. - if (in_array($item['#type'], array('image_button', 'button', 'submit'))) { - $path = "//!type[contains(@class, :div-class) and @value=:value]"; - $class = 'form-button-disabled'; - } - else { - // starts-with() required for checkboxes. - $path = "//div[contains(@class, :div-class)]/descendant::!type[starts-with(@name, :name)]"; - $class = 'form-disabled'; - } - // Replace DOM element name in $path according to #type. - $type = 'input'; - if (isset($type_map[$item['#type']])) { - $type = $type_map[$item['#type']]; - } - $path = strtr($path, array('!type' => $type)); - // Verify that the element exists. - $element = $this->xpath($path, array( - ':name' => check_plain($name), - ':div-class' => $class, - ':value' => isset($item['#value']) ? $item['#value'] : '', - )); - $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => $item['#type']))); - } - - // Verify special element #type text-format. - $element = $this->xpath('//div[contains(@class, :div-class)]/descendant::textarea[@name=:name]', array( - ':name' => 'text_format[value]', - ':div-class' => 'form-disabled', - )); - $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[value]'))); - $element = $this->xpath('//div[contains(@class, :div-class)]/descendant::select[@name=:name]', array( - ':name' => 'text_format[format]', - ':div-class' => 'form-disabled', - )); - $this->assertTrue(isset($element[0]), t('Disabled form element class found for #type %type.', array('%type' => 'text_format[format]'))); - } - - /** - * Test Form API protections against input forgery. - * - * @see _form_test_input_forgery() - */ - function testInputForgery() { - $this->drupalGet('form-test/input-forgery'); - $checkbox = $this->xpath('//input[@name="checkboxes[two]"]'); - $checkbox[0]['value'] = 'FORGERY'; - $this->drupalPost(NULL, array('checkboxes[one]' => TRUE, 'checkboxes[two]' => TRUE), t('Submit')); - $this->assertText('An illegal choice has been detected.', t('Input forgery was detected.')); - } -} - -/** - * Tests building and processing of core form elements. - */ -class FormElementTestCase extends DrupalWebTestCase { - protected $profile = 'testing'; - - public static function getInfo() { - return array( - 'name' => 'Element processing', - 'description' => 'Tests building and processing of core form elements.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp(array('form_test')); - } - - /** - * Tests placeholder text for textfield, password, and textarea. - */ - function testPlaceHolderText() { - $this->drupalGet('form-test/placeholder-text'); - $expected = 'placeholder-text'; - // Test to make sure textfields and passwords have the proper placeholder - // text. - foreach (array('textfield', 'password') as $type) { - $element = $this->xpath('//input[@id=:id and @placeholder=:expected]', array( - ':id' => 'edit-' . $type, - ':expected' => $expected, - )); - $this->assertTrue(!empty($element), t('Placeholder text placed in @type.', array('@type' => $type))); - } - - // Test to make sure textarea has the proper placeholder text. - $element = $this->xpath('//textarea[@id=:id and @placeholder=:expected]', array( - ':id' => 'edit-textarea', - ':expected' => $expected, - )); - $this->assertTrue(!empty($element), t('Placeholder text placed in textarea.')); - } - - /** - * Tests expansion of #options for #type checkboxes and radios. - */ - function testOptions() { - $this->drupalGet('form-test/checkboxes-radios'); - - // Verify that all options appear in their defined order. - foreach (array('checkbox', 'radio') as $type) { - $elements = $this->xpath('//input[@type=:type]', array(':type' => $type)); - $expected_values = array('0', 'foo', '1', 'bar', '>'); - foreach ($elements as $element) { - $expected = array_shift($expected_values); - $this->assertIdentical((string) $element['value'], $expected); - } - } - - // Enable customized option sub-elements. - $this->drupalGet('form-test/checkboxes-radios/customize'); - - // Verify that all options appear in their defined order, taking a custom - // #weight into account. - foreach (array('checkbox', 'radio') as $type) { - $elements = $this->xpath('//input[@type=:type]', array(':type' => $type)); - $expected_values = array('0', 'foo', 'bar', '>', '1'); - foreach ($elements as $element) { - $expected = array_shift($expected_values); - $this->assertIdentical((string) $element['value'], $expected); - } - } - // Verify that custom #description properties are output. - foreach (array('checkboxes', 'radios') as $type) { - $elements = $this->xpath('//input[@id=:id]/following-sibling::div[@class=:class]', array( - ':id' => 'edit-' . $type . '-foo', - ':class' => 'description', - )); - $this->assertTrue(count($elements), t('Custom %type option description found.', array( - '%type' => $type, - ))); - } - } -} - -/** - * Test form alter hooks. - */ -class FormAlterTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form alter hooks', - 'description' => 'Tests hook_form_alter() and hook_form_FORM_ID_alter().', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter(). - */ - function testExecutionOrder() { - $this->drupalGet('form-test/alter'); - // Ensure that the order is first by module, then for a given module, the - // id-specific one after the generic one. - $expected = array( - 'block_form_form_test_alter_form_alter() executed.', - 'form_test_form_alter() executed.', - 'form_test_form_form_test_alter_form_alter() executed.', - 'system_form_form_test_alter_form_alter() executed.', - ); - $content = preg_replace('/\s+/', ' ', filter_xss($this->content, array())); - $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, t('Form alter hooks executed in the expected order.')); - } -} - -/** - * Test form validation handlers. - */ -class FormValidationTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form validation handlers', - 'description' => 'Tests form processing and alteration via form validation handlers.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests form alterations by #element_validate, #validate, and form_set_value(). - */ - function testValidate() { - $this->drupalGet('form-test/validate'); - // Verify that #element_validate handlers can alter the form and submitted - // form values. - $edit = array( - 'name' => 'element_validate', - ); - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertFieldByName('name', '#value changed by #element_validate', t('Form element #value was altered.')); - $this->assertText('Name value: value changed by form_set_value() in #element_validate', t('Form element value in $form_state was altered.')); - - // Verify that #validate handlers can alter the form and submitted - // form values. - $edit = array( - 'name' => 'validate', - ); - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertFieldByName('name', '#value changed by #validate', t('Form element #value was altered.')); - $this->assertText('Name value: value changed by form_set_value() in #validate', t('Form element value in $form_state was altered.')); - - // Verify that #element_validate handlers can make form elements - // inaccessible, but values persist. - $edit = array( - 'name' => 'element_validate_access', - ); - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertNoFieldByName('name', t('Form element was hidden.')); - $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); - - // Verify that value for inaccessible form element persists. - $this->drupalPost(NULL, array(), 'Save'); - $this->assertNoFieldByName('name', t('Form element was hidden.')); - $this->assertText('Name value: element_validate_access', t('Value for inaccessible form element exists.')); - } - - /** - * Tests partial form validation through #limit_validation_errors. - */ - function testValidateLimitErrors() { - $edit = array( - 'test' => 'invalid', - 'test_numeric_index[0]' => 'invalid', - 'test_substring[foo]' => 'invalid', - ); - $path = 'form-test/limit-validation-errors'; - - // Submit the form by pressing the 'Partial validate' button (uses - // #limit_validation_errors) and ensure that the title field is not - // validated, but the #element_validate handler for the 'test' field - // is triggered. - $this->drupalPost($path, $edit, t('Partial validate')); - $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test element is invalid'); - - // Edge case of #limit_validation_errors containing numeric indexes: same - // thing with the 'Partial validate (numeric index)' button and the - // 'test_numeric_index' field. - $this->drupalPost($path, $edit, t('Partial validate (numeric index)')); - $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test (numeric index) element is invalid'); - - // Ensure something like 'foobar' isn't considered "inside" 'foo'. - $this->drupalPost($path, $edit, t('Partial validate (substring)')); - $this->assertNoText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test (substring) foo element is invalid'); - - // Ensure not validated values are not available to submit handlers. - $this->drupalPost($path, array('title' => '', 'test' => 'valid'), t('Partial validate')); - $this->assertText('Only validated values appear in the form values.'); - - // Now test full form validation and ensure that the #element_validate - // handler is still triggered. - $this->drupalPost($path, $edit, t('Full validate')); - $this->assertText(t('!name field is required.', array('!name' => 'Title'))); - $this->assertText('Test element is invalid'); - } -} - -/** - * Test form element labels, required markers and associated output. - */ -class FormsElementsLabelsTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form element and label output test', - 'description' => 'Test form element labels, required markers and associated output.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Test form elements, labels, title attibutes and required marks output - * correctly and have the correct label option class if needed. - */ - function testFormLabels() { - $this->drupalGet('form_test/form-labels'); - - // Check that the checkbox/radio processing is not interfering with - // basic placement. - $elements = $this->xpath('//input[@id="edit-form-checkboxes-test-third-checkbox"]/following-sibling::label[@for="edit-form-checkboxes-test-third-checkbox" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular checkboxes.")); - - $elements = $this->xpath('//input[@id="edit-form-radios-test-second-radio"]/following-sibling::label[@for="edit-form-radios-test-second-radio" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for regular radios.")); - - // Exercise various defaults for checkboxes and modifications to ensure - // appropriate override and correct behaviour. - $elements = $this->xpath('//input[@id="edit-form-checkbox-test"]/following-sibling::label[@for="edit-form-checkbox-test" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label follows field and label option class correct for a checkbox by default.")); - - // Exercise various defaults for textboxes and modifications to ensure - // appropriate override and correct behaviour. - $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required"]/child::abbr[@class="form-required"]/parent::*/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]'); - $this->assertTrue(isset($elements[0]), t("Label precedes textfield, with required marker inside label.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required"]/abbr[@class="form-required"]'); - $this->assertTrue(isset($elements[0]), t("Label tag with required marker precedes required textfield with no title.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="element-invisible"]'); - $this->assertTrue(isset($elements[0]), t("Label preceding field and label class is element-invisible.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-title"]/preceding-sibling::abbr[@class="form-required"]'); - $this->assertFalse(isset($elements[0]), t("No required marker on non-required field.")); - - $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-after"]/following-sibling::label[@for="edit-form-textfield-test-title-after" and @class="option"]'); - $this->assertTrue(isset($elements[0]), t("Label after field and label option class correct for text field.")); - - $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-no-show"]'); - $this->assertFalse(isset($elements[0]), t("No label tag when title set not to display.")); - - // Check #field_prefix and #field_suffix placement. - $elements = $this->xpath('//span[@class="field-prefix"]/following-sibling::div[@id="edit-form-radios-test"]'); - $this->assertTrue(isset($elements[0]), t("Properly placed the #field_prefix element after the label and before the field.")); - - $elements = $this->xpath('//span[@class="field-suffix"]/preceding-sibling::div[@id="edit-form-radios-test"]'); - $this->assertTrue(isset($elements[0]), t("Properly places the #field_suffix element immediately after the form field.")); - - // Check #prefix and #suffix placement. - $elements = $this->xpath('//div[@id="form-test-textfield-title-prefix"]/following-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]'); - $this->assertTrue(isset($elements[0]), t("Properly places the #prefix element before the form item.")); - - $elements = $this->xpath('//div[@id="form-test-textfield-title-suffix"]/preceding-sibling::div[contains(@class, \'form-item-form-textfield-test-title\')]'); - $this->assertTrue(isset($elements[0]), t("Properly places the #suffix element before the form item.")); - } -} - -/** - * Test the tableselect form element for expected behavior. - */ -class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Tableselect form element type test', - 'description' => 'Test the tableselect element for expected behavior', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - - /** - * Test the display of checkboxes when #multiple is TRUE. - */ - function testMultipleTrue() { - - $this->drupalGet('form_test/tableselect/multiple-true'); - - $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.')); - - // Test for the presence of the Select all rows tableheader. - $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Presence of the "Select all" checkbox.')); - - $rows = array('row1', 'row2', 'row3'); - foreach ($rows as $row) { - $this->assertFieldByXPath('//input[@type="checkbox"]', $row, t('Checkbox for value @row.', array('@row' => $row))); - } - } - - /** - * Test the display of radios when #multiple is FALSE. - */ - function testMultipleFalse() { - $this->drupalGet('form_test/tableselect/multiple-false'); - - $this->assertNoText(t('Empty text.'), t('Empty text should not be displayed.')); - - // Test for the absence of the Select all rows tableheader. - $this->assertNoFieldByXPath('//th[@class="select-all"]', '', t('Absence of the "Select all" checkbox.')); - - $rows = array('row1', 'row2', 'row3'); - foreach ($rows as $row) { - $this->assertFieldByXPath('//input[@type="radio"]', $row, t('Radio button for value @row.', array('@row' => $row))); - } - } - - /** - * Test the display of the #empty text when #options is an empty array. - */ - function testEmptyText() { - $this->drupalGet('form_test/tableselect/empty-text'); - $this->assertText(t('Empty text.'), t('Empty text should be displayed.')); - } - - /** - * Test the submission of single and multiple values when #multiple is TRUE. - */ - function testMultipleTrueSubmit() { - - // Test a submission with one checkbox checked. - $edit = array(); - $edit['tableselect[row1]'] = TRUE; - $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit'); - - $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1')); - $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.')); - $this->assertText(t('Submitted: row3 = 0'), t('Unchecked checkbox row3.')); - - // Test a submission with multiple checkboxes checked. - $edit['tableselect[row1]'] = TRUE; - $edit['tableselect[row3]'] = TRUE; - $this->drupalPost('form_test/tableselect/multiple-true', $edit, 'Submit'); - - $this->assertText(t('Submitted: row1 = row1'), t('Checked checkbox row1.')); - $this->assertText(t('Submitted: row2 = 0'), t('Unchecked checkbox row2.')); - $this->assertText(t('Submitted: row3 = row3'), t('Checked checkbox row3.')); - - } - - /** - * Test submission of values when #multiple is FALSE. - */ - function testMultipleFalseSubmit() { - $edit['tableselect'] = 'row1'; - $this->drupalPost('form_test/tableselect/multiple-false', $edit, 'Submit'); - $this->assertText(t('Submitted: row1'), t('Selected radio button')); - } - - /** - * Test the #js_select property. - */ - function testAdvancedSelect() { - // When #multiple = TRUE a Select all checkbox should be displayed by default. - $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-default'); - $this->assertFieldByXPath('//th[@class="select-all"]', NULL, t('Display a "Select all" checkbox by default when #multiple is TRUE.')); - - // When #js_select is set to FALSE, a "Select all" checkbox should not be displayed. - $this->drupalGet('form_test/tableselect/advanced-select/multiple-true-no-advanced-select'); - $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #js_select is FALSE.')); - - // A "Select all" checkbox never makes sense when #multiple = FALSE, regardless of the value of #js_select. - $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-default'); - $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE.')); - - $this->drupalGet('form_test/tableselect/advanced-select/multiple-false-advanced-select'); - $this->assertNoFieldByXPath('//th[@class="select-all"]', NULL, t('Do not display a "Select all" checkbox when #multiple is FALSE, even when #js_select is TRUE.')); - } - - - /** - * Test the whether the option checker gives an error on invalid tableselect values for checkboxes. - */ - function testMultipleTrueOptionchecker() { - - list($header, $options) = _form_test_tableselect_get_data(); - - $form['tableselect'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - ); - - // Test with a valid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => array('row1' => 'row1'))); - $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for checkboxes.')); - - // Test with an invalid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => array('non_existing_value' => 'non_existing_value'))); - $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for checkboxes.')); - - } - - - /** - * Test the whether the option checker gives an error on invalid tableselect values for radios. - */ - function testMultipleFalseOptionchecker() { - - list($header, $options) = _form_test_tableselect_get_data(); - - $form['tableselect'] = array( - '#type' => 'tableselect', - '#header' => $header, - '#options' => $options, - '#multiple' => FALSE, - ); - - // Test with a valid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'row1')); - $this->assertFalse(isset($errors['tableselect']), t('Option checker allows valid values for radio buttons.')); - - // Test with an invalid value. - list($processed_form, $form_state, $errors) = $this->formSubmitHelper($form, array('tableselect' => 'non_existing_value')); - $this->assertTrue(isset($errors['tableselect']), t('Option checker disallows invalid values for radio buttons.')); - } - - - /** - * 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->randomName(); - $form_state = form_state_defaults(); - - $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); - - $form_state['input'] = $edit; - $form_state['input']['form_id'] = $form_id; - - drupal_prepare_form($form_id, $form, $form_state); - - drupal_process_form($form_id, $form, $form_state); - - $errors = form_get_errors(); - - // Clear errors and messages. - drupal_get_messages(); - form_clear_error(); - - // 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); - } - -} - -/** - * Test the vertical_tabs form element for expected behavior. - */ -class FormsElementsVerticalTabsFunctionalTest extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Vertical tabs form element type test', - 'description' => 'Test the vertical_tabs element for expected behavior', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Ensures that vertical-tabs.js is included before collapse.js. - * - * Otherwise, collapse.js adds "SHOW" or "HIDE" labels to the tabs. - */ - function testJavaScriptOrdering() { - $this->drupalGet('form_test/vertical-tabs'); - $position1 = strpos($this->content, 'misc/vertical-tabs.js'); - $position2 = strpos($this->content, 'misc/collapse.js'); - $this->assertTrue($position1 !== FALSE && $position2 !== FALSE && $position1 < $position2, t('vertical-tabs.js is included before collapse.js')); - } -} - -/** - * Test the form storage on a multistep form. - * - * The tested form puts data into the storage during the initial form - * construction. These tests verify that there are no duplicate form - * constructions, with and without manual form caching activiated. Furthermore - * when a validation error occurs, it makes sure that changed form element - * values aren't lost due to a wrong form rebuild. - */ -class FormsFormStorageTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Multistep form using form storage', - 'description' => 'Tests a multistep form using form storage and makes sure validation and caching works right.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - - $this->web_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($this->web_user); - } - - /** - * Tests using the form in a usual way. - */ - function testForm() { - $this->drupalGet('form_test/form-storage'); - $this->assertText('Form constructions: 1'); - - $edit = array('title' => 'new', 'value' => 'value_is_set'); - - // Use form rebuilding triggered by a submit button. - $this->drupalPost(NULL, $edit, 'Continue submit'); - $this->assertText('Form constructions: 2'); - $this->assertText('Form constructions: 3'); - - // Reset the form to the values of the storage, using a form rebuild - // triggered by button of type button. - $this->drupalPost(NULL, array('title' => 'changed'), 'Reset'); - $this->assertFieldByName('title', 'new', 'Values have been resetted.'); - // After rebuilding, the form has been cached. - $this->assertText('Form constructions: 4'); - - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertText('Form constructions: 4'); - $this->assertText('Title: new', t('The form storage has stored the values.')); - } - - /** - * Tests using the form with an activated $form_state['cache'] property. - */ - function testFormCached() { - $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1))); - $this->assertText('Form constructions: 1'); - - $edit = array('title' => 'new', 'value' => 'value_is_set'); - - // Use form rebuilding triggered by a submit button. - $this->drupalPost(NULL, $edit, 'Continue submit'); - $this->assertText('Form constructions: 2'); - - // Reset the form to the values of the storage, using a form rebuild - // triggered by button of type button. - $this->drupalPost(NULL, array('title' => 'changed'), 'Reset'); - $this->assertFieldByName('title', 'new', 'Values have been resetted.'); - $this->assertText('Form constructions: 3'); - - $this->drupalPost(NULL, $edit, 'Save'); - $this->assertText('Form constructions: 3'); - $this->assertText('Title: new', t('The form storage has stored the values.')); - } - - /** - * Tests validation when form storage is used. - */ - function testValidation() { - $this->drupalPost('form_test/form-storage', array('title' => '', 'value' => 'value_is_set'), 'Continue submit'); - $this->assertPattern('/value_is_set/', t('The input values have been kept.')); - } - - /** - * Tests updating cached form storage during form validation. - * - * If form caching is enabled and a form stores data in the form storage, then - * the form storage also has to be updated in case of a validation error in - * the form. This test re-uses the existing form for multi-step tests, but - * triggers a special #element_validate handler to update the form storage - * during form validation, while another, required element in the form - * triggers a form validation error. - */ - function testCachedFormStorageValidation() { - // Request the form with 'cache' query parameter to enable form caching. - $this->drupalGet('form_test/form-storage', array('query' => array('cache' => 1))); - - // Skip step 1 of the multi-step form, since the first step copies over - // 'title' into form storage, but we want to verify that changes in the form - // storage are updated in the cache during form validation. - $edit = array('title' => 'foo'); - $this->drupalPost(NULL, $edit, 'Continue submit'); - - // In step 2, trigger a validation error for the required 'title' field, and - // post the special 'change_title' value for the 'value' field, which - // conditionally invokes the #element_validate handler to update the form - // storage. - $edit = array('title' => '', 'value' => 'change_title'); - $this->drupalPost(NULL, $edit, 'Save'); - - // At this point, the form storage should contain updated values, but we do - // not see them, because the form has not been rebuilt yet due to the - // validation error. Post again and verify that the rebuilt form contains - // the values of the updated form storage. - $this->drupalPost(NULL, array('title' => 'foo', 'value' => 'bar'), 'Save'); - $this->assertText("The thing has been changed.", 'The altered form storage value was updated in cache and taken over.'); - } - - /** - * Tests a form using form state without using 'storage' to pass data from the - * constructor to a submit handler. The data has to persist even when caching - * gets activated, what may happen when a modules alter the form and adds - * #ajax properties. - */ - function testFormStatePersist() { - // Test the form one time with caching activated and one time without. - $run_options = array( - array(), - array('query' => array('cache' => 1)), - ); - foreach ($run_options as $options) { - $this->drupalPost('form-test/state-persist', array(), t('Submit'), $options); - // The submit handler outputs the value in $form_state, assert it's there. - $this->assertText('State persisted.'); - - // Test it again, but first trigger a validation error, then test. - $this->drupalPost('form-test/state-persist', array('title' => ''), t('Submit'), $options); - $this->assertText(t('!name field is required.', array('!name' => 'title'))); - // Submit the form again triggering no validation error. - $this->drupalPost(NULL, array('title' => 'foo'), t('Submit'), $options); - $this->assertText('State persisted.'); - - // Now post to the rebuilt form and verify it's still there afterwards. - $this->drupalPost(NULL, array('title' => 'bar'), t('Submit'), $options); - $this->assertText('State persisted.'); - } - } -} - -/** - * Test wrapper form callbacks. - */ -class FormsFormWrapperTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form wrapper callback', - 'description' => 'Tests form wrapper callbacks to pass a prebuilt form to form builder functions.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests using the form in a usual way. - */ - function testWrapperCallback() { - $this->drupalGet('form_test/wrapper-callback'); - $this->assertText('Form wrapper callback element output.', t('The form contains form wrapper elements.')); - $this->assertText('Form builder element output.', t('The form contains form builder elements.')); - } -} - -/** - * Test $form_state clearance. - */ -class FormStateValuesCleanTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form state values clearance', - 'description' => 'Test proper removal of submitted form values using form_state_values_clean().', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests form_state_values_clean(). - */ - function testFormStateValuesClean() { - $values = drupal_json_decode($this->drupalPost('form_test/form-state-values-clean', array(), t('Submit'))); - - // Setup the expected result. - $result = array( - 'beer' => 1000, - 'baz' => array('beer' => 2000), - ); - - // Verify that all internal Form API elements were removed. - $this->assertFalse(isset($values['form_id']), t('%element was removed.', array('%element' => 'form_id'))); - $this->assertFalse(isset($values['form_token']), t('%element was removed.', array('%element' => 'form_token'))); - $this->assertFalse(isset($values['form_build_id']), t('%element was removed.', array('%element' => 'form_build_id'))); - $this->assertFalse(isset($values['op']), t('%element was removed.', array('%element' => 'op'))); - - // Verify that all buttons were removed. - $this->assertFalse(isset($values['foo']), t('%element was removed.', array('%element' => 'foo'))); - $this->assertFalse(isset($values['bar']), t('%element was removed.', array('%element' => 'bar'))); - $this->assertFalse(isset($values['baz']['foo']), t('%element was removed.', array('%element' => 'foo'))); - $this->assertFalse(isset($values['baz']['baz']), t('%element was removed.', array('%element' => 'baz'))); - - // Verify that nested form value still exists. - $this->assertTrue(isset($values['baz']['beer']), t('Nested form value still exists.')); - - // Verify that actual form values equal resulting form values. - $this->assertEqual($values, $result, t('Expected form values equal actual form values.')); - } -} - -/** - * Tests form rebuilding. - * - * @todo Add tests for other aspects of form rebuilding. - */ -class FormsRebuildTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Form rebuilding', - 'description' => 'Tests functionality of drupal_rebuild_form().', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - - $this->web_user = $this->drupalCreateUser(array('access content')); - $this->drupalLogin($this->web_user); - } - - /** - * Tests preservation of values. - */ - function testRebuildPreservesValues() { - $edit = array( - 'checkbox_1_default_off' => TRUE, - 'checkbox_1_default_on' => FALSE, - 'text_1' => 'foo', - ); - $this->drupalPost('form-test/form-rebuild-preserve-values', $edit, 'Add more'); - - // Verify that initial elements retained their submitted values. - $this->assertFieldChecked('edit-checkbox-1-default-off', t('A submitted checked checkbox retained its checked state during a rebuild.')); - $this->assertNoFieldChecked('edit-checkbox-1-default-on', t('A submitted unchecked checkbox retained its unchecked state during a rebuild.')); - $this->assertFieldById('edit-text-1', 'foo', t('A textfield retained its submitted value during a rebuild.')); - - // Verify that newly added elements were initialized with their default values. - $this->assertFieldChecked('edit-checkbox-2-default-on', t('A newly added checkbox was initialized with a default checked state.')); - $this->assertNoFieldChecked('edit-checkbox-2-default-off', t('A newly added checkbox was initialized with a default unchecked state.')); - $this->assertFieldById('edit-text-2', 'DEFAULT 2', t('A newly added textfield was initialized with its default value.')); - } - - /** - * Tests that a form's action is retained after an Ajax submission. - * - * The 'action' attribute of a form should not change after an Ajax submission - * followed by a non-Ajax submission, which triggers a validation error. - */ - function testPreserveFormActionAfterAJAX() { - // Create a multi-valued field for 'page' nodes to use for Ajax testing. - $field_name = 'field_ajax_test'; - $field = array( - 'field_name' => $field_name, - 'type' => 'text', - 'cardinality' => FIELD_CARDINALITY_UNLIMITED, - ); - field_create_field($field); - $instance = array( - 'field_name' => $field_name, - 'entity_type' => 'node', - 'bundle' => 'page', - ); - field_create_instance($instance); - - // Log in a user who can create 'page' nodes. - $this->web_user = $this->drupalCreateUser(array('create page content')); - $this->drupalLogin($this->web_user); - - // Get the form for adding a 'page' node. Submit an "add another item" Ajax - // submission and verify it worked by ensuring the updated page has two text - // field items in the field for which we just added an item. - $this->drupalGet('node/add/page'); - $this->drupalPostAJAX(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), 'system/ajax', array(), array(), 'page-node-form'); - $this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, t('AJAX submission succeeded.')); - - // Submit the form with the non-Ajax "Save" button, leaving the title field - // blank to trigger a validation error, and ensure that a validation error - // occurred, because this test is for testing what happens when a form is - // re-rendered without being re-built, which is what happens when there's - // a validation error. - $this->drupalPost(NULL, array(), t('Save')); - $this->assertText('Title field is required.', t('Non-AJAX submission correctly triggered a validation error.')); - - // Ensure that the form contains two items in the multi-valued field, so we - // know we're testing a form that was correctly retrieved from cache. - $this->assert(count($this->xpath('//form[contains(@id, "page-node-form")]//div[contains(@class, "form-item-field-ajax-test")]//input[@type="text"]')) == 2, t('Form retained its state from cache.')); - - // Ensure that the form's action is correct. - $forms = $this->xpath('//form[contains(@class, "node-page-form")]'); - $this->assert(count($forms) == 1 && $forms[0]['action'] == url('node/add/page'), t('Re-rendered form contains the correct action value.')); - } -} - -/** - * Test the programmatic form submission behavior. - */ -class FormsProgrammaticTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Programmatic form submissions', - 'description' => 'Test the programmatic form submission behavior.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Test the programmatic form submission workflow. - */ - function testSubmissionWorkflow() { - // Backup the current batch status and reset it to avoid conflicts while - // processing the dummy form submit handler. - $current_batch = $batch =& batch_get(); - $batch = array(); - - // Test that a programmatic form submission is rejected when a required - // textfield is omitted and correctly processed when it is provided. - $this->submitForm(array(), FALSE); - $this->submitForm(array('textfield' => 'test 1'), TRUE); - $this->submitForm(array(), FALSE); - $this->submitForm(array('textfield' => 'test 2'), TRUE); - - // Test that a programmatic form submission can turn on and off checkboxes - // which are, by default, checked. - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => 2)), TRUE); - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => 1, 2 => NULL)), TRUE); - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => 2)), TRUE); - $this->submitForm(array('textfield' => 'dummy value', 'checkboxes' => array(1 => NULL, 2 => NULL)), TRUE); - - // Test that a programmatic form submission can correctly click a button - // that limits validation errors based on user input. Since we do not - // submit any values for "textfield" here and the textfield is required, we - // only expect form validation to pass when validation is limited to a - // different field. - $this->submitForm(array('op' => 'Submit with limited validation', 'field_to_validate' => 'all'), FALSE); - $this->submitForm(array('op' => 'Submit with limited validation', 'field_to_validate' => 'textfield'), FALSE); - $this->submitForm(array('op' => 'Submit with limited validation', 'field_to_validate' => 'field_to_validate'), TRUE); - - // Restore the current batch status. - $batch = $current_batch; - } - - /** - * Helper function used to programmatically submit the form defined in - * form_test.module with the given values. - * - * @param $values - * An array of field values to be submitted. - * @param $valid_input - * A boolean indicating whether or not the form submission is expected to - * be valid. - */ - private function submitForm($values, $valid_input) { - // Programmatically submit the given values. - $form_state = array('values' => $values); - drupal_form_submit('form_test_programmatic_form', $form_state); - - // Check that the form returns an error when expected, and vice versa. - $errors = form_get_errors(); - $valid_form = empty($errors); - $args = array( - '%values' => print_r($values, TRUE), - '%errors' => $valid_form ? t('None') : implode(' ', $errors), - ); - $this->assertTrue($valid_input == $valid_form, t('Input values: %values<br/>Validation handler errors: %errors', $args)); - - // We check submitted values only if we have a valid input. - if ($valid_input) { - // By fetching the values from $form_state['storage'] we ensure that the - // submission handler was properly executed. - $stored_values = $form_state['storage']['programmatic_form_submit']; - foreach ($values as $key => $value) { - $this->assertTrue(isset($stored_values[$key]) && $stored_values[$key] == $value, t('Submission handler correctly executed: %stored_key is %stored_value', array('%stored_key' => $key, '%stored_value' => print_r($value, TRUE)))); - } - } - } -} - -/** - * Test that FAPI correctly determines $form_state['triggering_element']. - */ -class FormsTriggeringElementTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form triggering element determination', - 'description' => 'Test the determination of $form_state[\'triggering_element\'].', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Test the determination of $form_state['triggering_element'] when no button - * information is included in the POST data, as is sometimes the case when - * the ENTER key is pressed in a textfield in Internet Explorer. - */ - function testNoButtonInfoInPost() { - $path = 'form-test/clicked-button'; - $edit = array(); - $form_html_id = 'form-test-clicked-button'; - - // Ensure submitting a form with no buttons results in no - // $form_state['triggering_element'] and the form submit handler not - // running. - $this->drupalPost($path, $edit, NULL, array(), array(), $form_html_id); - $this->assertText('There is no clicked button.', t('$form_state[\'triggering_element\'] set to NULL.')); - $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.')); - - // Ensure submitting a form with one or more submit buttons results in - // $form_state['triggering_element'] being set to the first one the user has - // access to. An argument with 'r' in it indicates a restricted - // (#access=FALSE) button. - $this->drupalPost($path . '/s', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to only button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - $this->drupalPost($path . '/s/s', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - $this->drupalPost($path . '/rs/s', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] set to first available button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - // Ensure submitting a form with buttons of different types results in - // $form_state['triggering_element'] being set to the first button, - // regardless of type. For the FAPI 'button' type, this should result in the - // submit handler not executing. The types are 's'(ubmit), 'b'(utton), and - // 'i'(mage_button). - $this->drupalPost($path . '/s/b/i', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - - $this->drupalPost($path . '/b/s/i', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.')); - - $this->drupalPost($path . '/i/s/b', $edit, NULL, array(), array(), $form_html_id); - $this->assertText('The clicked button is button1.', t('$form_state[\'triggering_element\'] set to first button.')); - $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); - } - - /** - * Test that $form_state['triggering_element'] does not get set to a button - * with #access=FALSE. - */ - function testAttemptAccessControlBypass() { - $path = 'form-test/clicked-button'; - $form_html_id = 'form-test-clicked-button'; - - // Retrieve a form where 'button1' has #access=FALSE and 'button2' doesn't. - $this->drupalGet($path . '/rs/s'); - - // Submit the form with 'button1=button1' in the POST data, which someone - // trying to get around security safeguards could easily do. We have to do - // a little trickery here, to work around the safeguards in drupalPost(): by - // renaming the text field that is in the form to 'button1', we can get the - // data we want into $_POST. - $elements = $this->xpath('//form[@id="' . $form_html_id . '"]//input[@name="text"]'); - $elements[0]['name'] = 'button1'; - $this->drupalPost(NULL, array('button1' => 'button1'), NULL, array(), array(), $form_html_id); - - // Ensure that $form_state['triggering_element'] was not set to the - // restricted button. Do this with both a negative and positive assertion, - // because negative assertions alone can be brittle. See - // testNoButtonInfoInPost() for why the triggering element gets set to - // 'button2'. - $this->assertNoText('The clicked button is button1.', t('$form_state[\'triggering_element\'] not set to a restricted button.')); - $this->assertText('The clicked button is button2.', t('$form_state[\'triggering_element\'] not set to a restricted button.')); - } -} - -/** - * Tests rebuilding of arbitrary forms by altering them. - */ -class FormsArbitraryRebuildTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Rebuild arbitrary forms', - 'description' => 'Tests altering forms to be rebuilt so there are multiple steps.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - // Auto-create a field for testing. - $field = array( - 'field_name' => 'test_multiple', - 'type' => 'text', - 'cardinality' => -1, - 'translatable' => FALSE, - ); - field_create_field($field); - - $instance = array( - 'entity_type' => 'node', - 'field_name' => 'test_multiple', - 'bundle' => 'page', - 'label' => 'Test a multiple valued field', - 'widget' => array( - 'type' => 'text_textfield', - 'weight' => 0, - ), - ); - field_create_instance($instance); - variable_set('user_register', USER_REGISTER_VISITORS); - } - - /** - * Tests a basic rebuild with the user registration form. - */ - function testUserRegistrationRebuild() { - $edit = array( - 'name' => 'foo', - 'mail' => 'bar@example.com', - ); - $this->drupalPost('user/register', $edit, 'Rebuild'); - $this->assertText('Form rebuilt.'); - $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.'); - $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.'); - } - - /** - * Tests a rebuild caused by a multiple value field. - */ - function testUserRegistrationMultipleField() { - $edit = array( - 'name' => 'foo', - 'mail' => 'bar@example.com', - ); - $this->drupalPost('user/register', $edit, t('Add another item'), array('query' => array('field' => TRUE))); - $this->assertText('Test a multiple valued field', 'Form has been rebuilt.'); - $this->assertFieldByName('name', 'foo', 'Entered user name has been kept.'); - $this->assertFieldByName('mail', 'bar@example.com', 'Entered mail address has been kept.'); - } -} - -/** - * Tests form API file inclusion. - */ -class FormsFileInclusionTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form API file inclusion', - 'description' => 'Tests form API file inclusion.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - /** - * Tests loading an include specified in hook_menu(). - */ - function testLoadMenuInclude() { - $this->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), 'system/ajax', array(), array(), 'form-test-load-include-menu'); - $this->assertText('Submit callback called.'); - } - - /** - * Tests loading a custom specified inlcude. - */ - function testLoadCustomInclude() { - $this->drupalPost('form-test/load-include-custom', array(), t('Save')); - $this->assertText('Submit callback called.'); - } -} - -/** - * Tests checkbox element. - */ -class FormCheckboxTestCase extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Form API checkbox', - 'description' => 'Tests form API checkbox handling of various combinations of #default_value and #return_value.', - 'group' => 'Form API', - ); - } - - function setUp() { - parent::setUp('form_test'); - } - - function testFormCheckbox() { - // Ensure that the checked state is determined and rendered correctly for - // tricky combinations of default and return values. - foreach (array(FALSE, NULL, TRUE, 0, '0', '', 1, '1', 'foobar', '1foobar') as $default_value) { - // Only values that can be used for array indeces are supported for - // #return_value, with the exception of integer 0, which is not supported. - // @see form_process_checkbox(). - foreach (array('0', '', 1, '1', 'foobar', '1foobar') as $return_value) { - $form_array = drupal_get_form('form_test_checkbox_type_juggling', $default_value, $return_value); - $form = drupal_render($form_array); - if ($default_value === TRUE) { - $checked = TRUE; - } - elseif ($return_value === '0') { - $checked = ($default_value === '0'); - } - elseif ($return_value === '') { - $checked = ($default_value === ''); - } - elseif ($return_value === 1 || $return_value === '1') { - $checked = ($default_value === 1 || $default_value === '1'); - } - elseif ($return_value === 'foobar') { - $checked = ($default_value === 'foobar'); - } - elseif ($return_value === '1foobar') { - $checked = ($default_value === '1foobar'); - } - $checked_in_html = strpos($form, 'checked') !== FALSE; - $message = t('#default_value is %default_value #return_value is %return_value.', array('%default_value' => var_export($default_value, TRUE), '%return_value' => var_export($return_value, TRUE))); - $this->assertIdentical($checked, $checked_in_html, $message); - } - } - - // Ensure that $form_state['values'] is populated correctly for a checkboxes - // group that includes a 0-indexed array of options. - $results = json_decode($this->drupalPost('form-test/checkboxes-zero', array(), 'Save')); - $this->assertIdentical($results->checkbox_off, array(0, 0, 0), t('All three in checkbox_off are zeroes: off.')); - $this->assertIdentical($results->checkbox_zero_default, array('0', 0, 0), t('The first choice is on in checkbox_zero_default')); - $this->assertIdentical($results->checkbox_string_zero_default, array('0', 0, 0), t('The first choice is on in checkbox_string_zero_default')); - $edit = array('checkbox_off[0]' => '0'); - $results = json_decode($this->drupalPost('form-test/checkboxes-zero', $edit, 'Save')); - $this->assertIdentical($results->checkbox_off, array('0', 0, 0), t('The first choice is on in checkbox_off but the rest is not')); - - // Ensure that each checkbox is rendered correctly for a checkboxes group - // that includes a 0-indexed array of options. - $this->drupalPost('form-test/checkboxes-zero/0', array(), 'Save'); - $checkboxes = $this->xpath('//input[@type="checkbox"]'); - foreach ($checkboxes as $checkbox) { - $checked = isset($checkbox['checked']); - $name = (string) $checkbox['name']; - $this->assertIdentical($checked, $name == 'checkbox_zero_default[0]' || $name == 'checkbox_string_zero_default[0]', t('Checkbox %name correctly checked', array('%name' => $name))); - } - $edit = array('checkbox_off[0]' => '0'); - $this->drupalPost('form-test/checkboxes-zero/0', $edit, 'Save'); - $checkboxes = $this->xpath('//input[@type="checkbox"]'); - foreach ($checkboxes as $checkbox) { - $checked = isset($checkbox['checked']); - $name = (string) $checkbox['name']; - $this->assertIdentical($checked, $name == 'checkbox_off[0]' || $name == 'checkbox_zero_default[0]' || $name == 'checkbox_string_zero_default[0]', t('Checkbox %name correctly checked', array('%name' => $name))); - } - } -} |