diff options
Diffstat (limited to 'modules/simpletest')
-rw-r--r-- | modules/simpletest/drupal_web_test_case.php | 32 | ||||
-rw-r--r-- | modules/simpletest/tests/ajax.test | 58 | ||||
-rw-r--r-- | modules/simpletest/tests/ajax_forms_test.module | 62 | ||||
-rw-r--r-- | modules/simpletest/tests/form.test | 71 | ||||
-rw-r--r-- | modules/simpletest/tests/form_test.module | 86 |
5 files changed, 288 insertions, 21 deletions
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index f2622444d..42ac83058 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -1576,11 +1576,18 @@ class DrupalWebTestCase extends DrupalTestCase { * which is likely different than the $path parameter used for retrieving * the initial form. Defaults to 'system/ajax'. * - triggering_element: If the value for the 'path' key is 'system/ajax' or - * another generic AJAX processing path, this needs to be set to the '/' - * separated path to the element within the server's cached $form array. - * The callback for the generic AJAX processing path uses this to find - * the #ajax information for the element, including which specific - * callback to use for processing the request. + * another generic AJAX processing path, this needs to be set to the name + * of the element. If the name doesn't identify the element uniquely, then + * this should instead be an array with a single key/value pair, + * corresponding to the element name and value. The callback for the + * generic AJAX processing path uses this to find the #ajax information + * for the element, including which specific callback to use for + * processing the request. + * + * This can also be set to NULL in order to emulate an Internet Explorer + * submission of a form with a single text field, and pressing ENTER in that + * textfield: under these conditions, no button information is added to the + * POST data. * @param $options * Options to be forwarded to url(). * @param $headers @@ -1622,7 +1629,7 @@ class DrupalWebTestCase extends DrupalTestCase { // We post only if we managed to handle every field in edit and the // submit button matches. - if (!$edit && $submit_matches) { + if (!$edit && ($submit_matches || !isset($submit))) { $post_array = $post; if ($upload) { // TODO: cURL handles file uploads for us, but the implementation @@ -1643,7 +1650,14 @@ class DrupalWebTestCase extends DrupalTestCase { $post[$key] = urlencode($key) . '=' . urlencode($value); } if ($ajax && isset($submit['triggering_element'])) { - $post['ajax_triggering_element'] = 'ajax_triggering_element=' . urlencode($submit['triggering_element']); + if (is_array($submit['triggering_element'])) { + // Get the first key/value pair in the array. + $post['_triggering_element_value'] = '_triggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $post['_triggering_element_name'] = '_triggering_element_name=' . urlencode(key($submit['triggering_element'])); + } + else { + $post['_triggering_element_name'] = '_triggering_element_name=' . urlencode($submit['triggering_element']); + } } $post = implode('&', $post); } @@ -1666,7 +1680,7 @@ class DrupalWebTestCase extends DrupalTestCase { foreach ($edit as $name => $value) { $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value))); } - if (!$ajax) { + if (!$ajax && isset($submit)) { $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit))); } $this->fail(t('Found the requested form fields at @path', array('@path' => $path))); @@ -1856,7 +1870,7 @@ class DrupalWebTestCase extends DrupalTestCase { break; case 'submit': case 'image': - if ($submit == $value) { + if (isset($submit) && $submit == $value) { $post[$name] = $value; $submit_matches = TRUE; } diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test index d959a4def..ca6f718dd 100644 --- a/modules/simpletest/tests/ajax.test +++ b/modules/simpletest/tests/ajax.test @@ -98,62 +98,62 @@ class AJAXCommandsTestCase extends AJAXTestCase { $edit = array(); // Tests the 'after' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'after_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'after' && $command['data'] == 'This will be placed after', "'after' AJAX command issued with correct data"); // Tests the 'alert' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'alert_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'alert' && $command['text'] == 'Alert', "'alert' AJAX Command issued with correct text"); // Tests the 'append' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'append_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'append' && $command['data'] == 'Appended text', "'append' AJAX command issued with correct data"); // Tests the 'before' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'before_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'before' && $command['data'] == 'Before text', "'before' AJAX command issued with correct data"); // Tests the 'changed' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'changed_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed.")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div', "'changed' AJAX command issued with correct selector"); // Tests the 'changed' command using the second argument. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'changed_command_asterisk_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk.")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'changed' && $command['selector'] == '#changed_div' && $command['asterisk'] == '#changed_div_mark_this', "'changed' AJAX command (with asterisk) issued with correct selector"); // Tests the 'css' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'css_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue.")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'css' && $command['selector'] == '#css_div' && $command['argument']['background-color'] == 'blue', "'css' AJAX command issued with correct selector"); // Tests the 'data' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'data_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command.")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'data' && $command['name'] == 'testkey' && $command['value'] == 'testvalue', "'data' AJAX command issued with correct key and value"); // Tests the 'html' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'html_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector.")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'html' && $command['data'] == 'replacement text', "'html' AJAX command issued with correct data"); // Tests the 'prepend' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'prepend_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'insert' && $command['method'] == 'prepend' && $command['data'] == 'prepended text', "'prepend' AJAX command issued with correct data"); // Tests the 'remove' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'remove_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'remove' && $command['selector'] == '#remove_text', "'remove' AJAX command issued with correct command and selector"); // Tests the 'restripe' command. - $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, 'restripe_command_example')); + $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command")))); $command = $commands[0]; $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector"); } @@ -203,3 +203,37 @@ class AJAXFormValuesTestCase extends AJAXTestCase { } } } + + +/** + * Miscellaneous AJAX tests using ajax_test module. + */ +class AJAXElementValidation extends AJAXTestCase { + public static function getInfo() { + return array( + 'name' => 'Miscellaneous AJAX tests', + 'description' => 'Various tests of AJAX behavior', + 'group' => 'AJAX', + ); + } + + /** + * Try to post an AJAX change to a form that has a validated element. + * + * The drivertext field is AJAX-enabled. An additional field is not, but + * is set to be a required field. In this test the required field is not + * filled in, and we want to see if the activation of the "drivertext" + * AJAX-enabled field fails due to the required field being empty. + */ + function testAJAXElementValidation() { + $web_user = $this->drupalCreateUser(); + $edit = array('drivertext' => t('some dumb text')); + + // Post with 'drivertext' as the triggering element. + $post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext'); + // Look for a validation failure in the resultant JSON. + $this->assertNoText(t('Error message'), t("No error message in resultant JSON")); + $this->assertText('ajax_forms_test_validation_form_callback invoked', t('The correct callback was invoked')); + } +} + diff --git a/modules/simpletest/tests/ajax_forms_test.module b/modules/simpletest/tests/ajax_forms_test.module index 040a1d9b8..5de2b842c 100644 --- a/modules/simpletest/tests/ajax_forms_test.module +++ b/modules/simpletest/tests/ajax_forms_test.module @@ -24,6 +24,12 @@ function ajax_forms_test_menu() { 'page arguments' => array('ajax_forms_test_ajax_commands_form'), 'access callback' => TRUE, ); + $items['ajax_validation_test'] = array( + 'title' => 'AJAX Validation Test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('ajax_forms_test_validation_form'), + 'access callback' => TRUE, + ); return $items; } @@ -340,3 +346,59 @@ function ajax_forms_test_advanced_commands_restripe_callback($form, $form_state) $commands[] = ajax_command_restripe('#restripe_table'); return array('#type' => 'ajax', '#commands' => $commands); } + + + +/** + * This form and its related submit and callback functions demonstrate + * not validating another form element when a single AJAX element is triggered. + * + * The "drivertext" element is an AJAX-enabled textfield, free-form. + * The "required_field" element is a textfield marked required. + * + * The correct behavior is that the AJAX-enabled drivertext element should + * be able to trigger without causing validation of the "required_field". + */ +function ajax_forms_test_validation_form($form, &$form_state) { + + $form['drivertext'] = array( + '#title' => t('AJAX-enabled textfield.'), + '#description' => t("When this one AJAX-triggers and the spare required field is empty, you should not get an error."), + '#type' => 'textfield', + '#default_value' => !empty($form_state['values']['drivertext']) ? $form_state['values']['drivertext'] : "", + '#ajax' => array( + 'callback' => 'ajax_forms_test_validation_form_callback', + 'wrapper' => 'message_area', + 'method' => 'replace', + ), + '#suffix' => '<div id="message_area"></div>', + ); + + $form['spare_required_field'] = array( + '#title' => t("Spare Required Field"), + '#type' => 'textfield', + '#required' => TRUE, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Submit'), + ); + + return $form; +} +/** + * Submit handler for the validation form. + */ +function ajax_forms_test_validation_form_submit($form, $form_state) { + drupal_set_message(t("Validation form submitted")); +} + +/** + * AJAX callback for the 'drivertext' element of the validation form. + */ +function ajax_forms_test_validation_form_callback($form, $form_state) { + drupal_set_message("ajax_forms_test_validation_form_callback invoked"); + drupal_set_message(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", array('%drivertext' => $form_state['values']['drivertext'], '%spare_required_field' => $form_state['values']['spare_required_field']))); + return '<div id="message_area">ajax_forms_test_validation_form_callback at ' . date('c') . '</div>'; +} diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index 3c447d14d..4683fb8d4 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -850,6 +850,77 @@ class FormsProgrammaticTestCase extends DrupalWebTestCase { } } +/** + * Test that FAPI correctly determines $form_state['clicked_button']. + */ +class FormsClickedButtonTestCase extends DrupalWebTestCase { + + function getInfo() { + return array( + 'name' => 'Form clicked button determination', + 'description' => 'Test the determination of $form_state[\'clicked_button\'].', + 'group' => 'Form API', + ); + } + + function setUp() { + parent::setUp('form_test'); + } + + /** + * Test the determination of $form_state['clicked_button'] 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_id = 'form-test-clicked-button'; + + // Ensure submitting a form with no buttons results in no + // $form_state['clicked_button'] and the form submit handler not running. + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path, $edit, NULL, array(), array(), $form_id); + $this->assertText('There is no clicked button.', t('$form_state[\'clicked_button\'] 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['clicked_button'] being set to the first one the user has + // access to. An argument with 'r' in it indicates a restricted + // (#access=FALSE) button. + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path . '/s', $edit, NULL, array(), array(), $form_id); + $this->assertText('The clicked button is button1.', t('$form_state[\'clicked_button\'] set to only button.')); + $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path . '/s/s', $edit, NULL, array(), array(), $form_id); + $this->assertText('The clicked button is button1.', t('$form_state[\'clicked_button\'] set to first button.')); + $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path . '/rs/s', $edit, NULL, array(), array(), $form_id); + $this->assertText('The clicked button is button2.', t('$form_state[\'clicked_button\'] 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['clicked_button'] 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). + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path . '/s/b/i', $edit, NULL, array(), array(), $form_id); + $this->assertText('The clicked button is button1.', t('$form_state[\'clicked_button\'] set to first button.')); + $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path . '/b/s/i', $edit, NULL, array(), array(), $form_id); + $this->assertText('The clicked button is button1.', t('$form_state[\'clicked_button\'] set to first button.')); + $this->assertNoText('Submit handler for form_test_clicked_button executed.', t('Form submit handler did not execute.')); + drupal_static_reset('drupal_html_id'); + $this->drupalPost($path . '/i/s/b', $edit, NULL, array(), array(), $form_id); + $this->assertText('The clicked button is button1.', t('$form_state[\'clicked_button\'] set to first button.')); + $this->assertText('Submit handler for form_test_clicked_button executed.', t('Form submit handler executed.')); + } +} + /** * Tests rebuilding of arbitrary forms by altering them. diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index ad9be7032..b35e43251 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -118,6 +118,14 @@ function form_test_menu() { 'type' => MENU_CALLBACK, ); + $items['form-test/clicked-button'] = array( + 'title' => 'Clicked button test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_clicked_button'), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -923,6 +931,84 @@ function form_test_programmatic_form_submit($form, &$form_state) { $form_state['storage']['programmatic_form_submit'] = $form_state['values']['submitted_field']; } +/** + * Form builder to test button click detection. + */ +function form_test_clicked_button($form, &$form_state) { + // A single text field. In IE, when a form has only one non-button input field + // and the ENTER key is pressed while that field has focus, the form is + // submitted without any information identifying the button responsible for + // the submission. In other browsers, the form is submitted as though the + // first button were clicked. + $form['text'] = array( + '#title' => 'Text', + '#type' => 'textfield', + ); + + // Loop through each path argument, addding buttons based on the information + // in the argument. For example, if the path is + // form-test/clicked-button/s/i/rb, then 3 buttons are added: a 'submit', an + // 'image_button', and a 'button' with #access=FALSE. This enables form.test + // to test a variety of combinations. + $i=0; + $args = array_slice(arg(), 2); + foreach ($args as $arg) { + $name = 'button' . ++$i; + // 's', 'b', or 'i' in the argument define the button type wanted. + if (strpos($arg, 's') !== FALSE) { + $type = 'submit'; + } + elseif (strpos($arg, 'b') !== FALSE) { + $type = 'button'; + } + elseif (strpos($arg, 'i') !== FALSE) { + $type = 'image_button'; + } + else { + $type = NULL; + } + if (isset($type)) { + $form[$name] = array( + '#type' => $type, + '#name' => $name, + ); + // Image buttons need a #src; the others need a #value. + if ($type == 'image_button') { + $form[$name]['#src'] = 'misc/druplicon.png'; + } + else { + $form[$name]['#value'] = $name; + } + // 'r' for restricted, so we can test that button click detection code + // correctly takes #access security into account. + if (strpos($arg, 'r') !== FALSE) { + $form[$name]['#access'] = FALSE; + } + } + } + + return $form; +} + +/** + * Form validation handler for the form_test_clicked_button() form. + */ +function form_test_clicked_button_validate($form, &$form_state) { + if (isset($form_state['clicked_button'])) { + drupal_set_message(t('The clicked button is %name.', array('%name' => $form_state['clicked_button']['#name']))); + } + else { + drupal_set_message('There is no clicked button.'); + } +} + +/** + * Form submit handler for the form_test_clicked_button() form. + */ +function form_test_clicked_button_submit($form, &$form_state) { + drupal_set_message('Submit handler for form_test_clicked_button executed.'); +} + /** * Implements hook_form_FORM_ID_alter() for the registration form. |