diff --git a/includes/ajax.inc b/includes/ajax.inc index f059209bce..cdce76ea73 100644 --- a/includes/ajax.inc +++ b/includes/ajax.inc @@ -384,7 +384,16 @@ function ajax_get_form() { function ajax_form_callback() { list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form(); drupal_process_form($form['#form_id'], $form, $form_state); + return ajax_form_return_commands($form, $form_state, $commands); +} +/** + * Return a set of AJAX commands for updating a form. + * + * @see ajax_form_callback() + * @see drupal_get_form() + */ +function ajax_form_return_commands($form, $form_state, $commands = array()) { // We need to return the part of the form (or some other content) that needs // to be re-rendered so the browser can update the page with changed content. // Since this is the generic menu callback used by many Ajax elements, it is @@ -738,10 +747,21 @@ function ajax_pre_render_element($element) { $settings = $element['#ajax']; + $path = 'system/ajax'; + $options = array(); + + if (isset($element['#ajax']['callback']) && !isset($element['#ajax']['path'])) { + $query_parameters = drupal_get_query_parameters($_GET, array('q')); + $query_parameters['_drupal_ajax'] = 1; + + $path = $_GET['q']; + $options['query'] = $query_parameters; + } + // Assign default settings. $settings += array( - 'path' => 'system/ajax', - 'options' => array(), + 'path' => $path, + 'options' => $options, ); // @todo Legacy support. Remove in Drupal 8. diff --git a/includes/form.inc b/includes/form.inc index e4ab8c8dac..5f24cd7d20 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -319,6 +319,14 @@ function drupal_build_form($form_id, &$form_state) { $form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST; } + // Disable redirection if the request was made via AJAX. + $ajax_post = isset($_POST['ajax_page_state']) && $_POST['form_id'] === $form_id; + if ($ajax_post) { + $form_state['no_redirect'] = TRUE; + $form_state['rebuild_info']['copy']['#build_id'] = TRUE; + $form_state['rebuild_info']['copy']['#action'] = TRUE; + } + if (isset($_SESSION['batch_form_state'])) { // We've been redirected here after a batch processing. The form has // already been processed, but needs to be rebuilt. See _batch_finished(). @@ -385,6 +393,25 @@ function drupal_build_form($form_id, &$form_state) { // can use it to know or update information about the state of the form. drupal_process_form($form_id, $form, $form_state); + // In the event that this form was submitted via AJAX, we need to return a + // set of AJAX commands to update the original form. Generate the JSON + // response and end the request. + if ($ajax_post && $form_state['process_input']) { + $commands = array(); + if (isset($form['#build_id_old']) && $form['#build_id_old'] !== $form['#build_id']) { + $commands[] = ajax_command_update_build_id($form); + } + $commands = ajax_form_return_commands($form, $form_state, $commands); + + $status = ob_get_status(); + if (!empty($status)) { + ob_clean(); + } + + ajax_deliver($commands); + exit(); + } + // If this was a successful submission of a single-step form or the last step // of a multi-step form, then drupal_process_form() issued a redirect to // another page, or back to this page, but as a new request. Therefore, if @@ -409,6 +436,7 @@ function form_state_defaults() { 'build_info' => array( 'args' => array(), 'files' => array(), + 'from_cache' => FALSE, ), 'temporary' => array(), 'submitted' => FALSE, @@ -464,6 +492,15 @@ function form_state_defaults() { function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { $form = drupal_retrieve_form($form_id, $form_state); + $copy_build_id = !empty($form_state['rebuild_info']['copy']['#build_id']); + + // If this form was not built from cache but needs to maintain the same build + // ID, copy the value from input onto the old form. + if (empty($form_state['build_info']['from_cache']) && isset($form_state['input']['form_build_id'])) { + $old_form['#build_id'] = $form_state['input']['form_build_id']; + $copy_build_id = FALSE; + } + // If only parts of the form will be returned to the browser (e.g., Ajax or // RIA clients), or if the form already had a new build ID regenerated when it // was retrieved from the form cache, reuse the existing #build_id. @@ -471,7 +508,7 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { // build's data in the form cache; also allowing the user to go back to an // earlier build, make changes, and re-submit. // @see drupal_prepare_form() - $enforce_old_build_id = isset($old_form['#build_id']) && !empty($form_state['rebuild_info']['copy']['#build_id']); + $enforce_old_build_id = isset($old_form['#build_id']) && $copy_build_id; $old_form_is_mutable_copy = isset($old_form['#build_id_old']); if ($enforce_old_build_id || $old_form_is_mutable_copy) { $form['#build_id'] = $old_form['#build_id']; @@ -524,6 +561,9 @@ function form_get_cache($form_build_id, &$form_state) { // Re-populate $form_state for subsequent rebuilds. $form_state = $cached->data + $form_state; + // Indicate this build was loaded from the cache, not a fresh build. + $form_state['build_info']['from_cache'] = TRUE; + // If the original form is contained in include files, load the files. // @see form_load_include() $form_state['build_info'] += array('files' => array()); diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index d592381bd5..0d5a138439 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -662,12 +662,10 @@ function file_field_widget_process($element, &$form_state, $form) { // file, the entire group of file fields is updated together. if ($field['cardinality'] != 1) { $parents = array_slice($element['#array_parents'], 0, -1); - $new_path = 'file/ajax/' . implode('/', $parents) . '/' . $form['form_build_id']['#value']; $field_element = drupal_array_get_nested_value($form, $parents); $new_wrapper = $field_element['#id'] . '-ajax-wrapper'; foreach (element_children($element) as $key) { if (isset($element[$key]['#ajax'])) { - $element[$key]['#ajax']['path'] = $new_path; $element[$key]['#ajax']['wrapper'] = $new_wrapper; } } diff --git a/modules/file/file.module b/modules/file/file.module index eea58470fa..94502b592d 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -234,6 +234,13 @@ function file_file_download($uri, $field_type = 'file') { * form processing is properly encapsulated in the widget element the form * should rebuild correctly using FAPI without the need for additional callbacks * or processing. + * + * This function is deprecated. This menu handler is no longer used by + * File module itself, but it exists for compatibility with contributed + * modules. Modules should now replace any use of $element['#ajax']['path'] + * with $element['#ajax']['callback'] = 'file_managed_file_ajax'. + * + * @see file_managed_file_ajax() */ function file_ajax_upload() { $form_parents = func_get_args(); @@ -374,7 +381,7 @@ function file_managed_file_process($element, &$form_state, $form) { $element['#tree'] = TRUE; $ajax_settings = array( - 'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'], + 'callback' => 'file_managed_file_ajax', 'wrapper' => $original_id . '-ajax-wrapper', 'effect' => 'fade', 'progress' => array( @@ -704,6 +711,42 @@ function file_managed_file_save_upload($element) { return $file; } +/** + * AJAX callback for managed file elements. + */ +function file_managed_file_ajax($form, $form_state) { + $button = $form_state['triggering_element']; + + // Get the list of parents, minus the button pressed and field delta. + $parents = $button['#array_parents']; + $button_key = array_pop($parents); // Button pressed. + $delta_key = array_pop($parents); // The delta for the pressed button. + + // Drill down to the element being updated. + $element = $form; + foreach ($parents as $parent_key) { + $element = $element[$parent_key]; + } + + // Add the special Ajax class if a new file was added. + if (isset($element['#file_upload_delta']) && $delta_key < $element['#file_upload_delta']) { + $element[$delta_key]['#attributes']['class'][] = 'ajax-new-content'; + } + // Otherwise just add the new content class on a placeholder. + else { + $element[$delta_key][$button_key]['#suffix'] = ''; + } + + $output = drupal_render($element); + $commands = array(); + $commands[] = ajax_command_replace('#' . $button['#ajax']['wrapper'], $output); + $commands[] = ajax_command_prepend(NULL, theme('status_messages')); + return array( + '#type' => 'ajax', + '#commands' => $commands, + ); +} + /** * Returns HTML for a managed file element. * diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index f764a90332..1d58a7f0c9 100644 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -373,6 +373,8 @@ class FileTaxonomyTermTestCase extends DrupalWebTestCase { * that aren't related to fields into it. */ class FileManagedFileElementTestCase extends FileFieldTestCase { + protected $profile = 'minimal'; + public static function getInfo() { return array( 'name' => 'Managed file element test', diff --git a/modules/poll/poll.module b/modules/poll/poll.module index 336e44563d..a04e770057 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -366,7 +366,7 @@ function poll_form($node, &$form_state) { function poll_more_choices_submit($form, &$form_state) { // If this is a Ajax POST, add 1, otherwise add 5 more choices to the form. if ($form_state['values']['poll_more']) { - $n = $_GET['q'] == 'system/ajax' ? 1 : 5; + $n = isset($_POST['ajax_page_state']) ? 1 : 5; $form_state['choice_count'] = count($form_state['values']['choice']) + $n; } // Renumber the choices. This invalidates the corresponding key/value diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 3124ffe82a..4b7ae4f8b0 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -2043,7 +2043,7 @@ class DrupalWebTestCase extends DrupalTestCase { * case, this value needs to be an array with the following keys: * - path: A path to submit the form values to for Ajax-specific processing, * which is likely different than the $path parameter used for retrieving - * the initial form. Defaults to 'system/ajax'. + * the initial form. Defaults to the current path. * - 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 name * of the element. If the name doesn't identify the element uniquely, then @@ -2070,15 +2070,26 @@ class DrupalWebTestCase extends DrupalTestCase { * form, which is typically the same thing but with hyphens replacing the * underscores. * @param $extra_post - * (optional) A string of additional data to append to the POST submission. + * (optional) An array of additional data to append to the POST submission. * This can be used to add POST data for which there are no HTML fields, as - * is done by drupalPostAJAX(). This string is literally appended to the - * POST data, so it must already be urlencoded and contain a leading "&" - * (e.g., "&extra_var1=hello+world&extra_var2=you%26me"). + * is done by drupalPostAJAX(). */ - protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { + protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = array()) { $submit_matches = FALSE; $ajax = is_array($submit); + + // Backwards-compatibility, convert $extra_post string to an array. + if (is_string($extra_post)) { + $extra_post_array = explode('&', $extra_post); + $extra_post = array(); + foreach ($extra_post_array as $value) { + if ($value) { + list($key, $value) = explode('=', $value); + $extra_post[urldecode($key)] = urldecode($value); + } + } + } + if (isset($path)) { $this->drupalGet($path, $options); } @@ -2096,9 +2107,12 @@ class DrupalWebTestCase extends DrupalTestCase { $post = array(); $upload = array(); $submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form); + $post += $extra_post; $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl(); if ($ajax) { - $action = $this->getAbsoluteUrl(!empty($submit['path']) ? $submit['path'] : 'system/ajax'); + if ($submit['path']) { + $action = $this->getAbsoluteUrl($submit['path']); + } // Ajax callbacks verify the triggering element if necessary, so while // we may eventually want extra code that verifies it in the // handleForm() function, it's not currently a requirement. @@ -2108,7 +2122,6 @@ 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 || !isset($submit))) { - $post_array = $post; if ($upload) { // TODO: cURL handles file uploads for us, but the implementation // is broken. This is a less than elegant workaround. Alternatives @@ -2134,7 +2147,7 @@ class DrupalWebTestCase extends DrupalTestCase { // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 $post[$key] = urlencode($key) . '=' . urlencode($value); } - $post = implode('&', $post) . $extra_post; + $post = implode('&', $post); } $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers)); // Ensure that any changes to variables in the other thread are picked up. @@ -2146,7 +2159,7 @@ class DrupalWebTestCase extends DrupalTestCase { } $this->verbose('POST request to: ' . $path . '
Ending URL: ' . $this->getUrl() . - '
Fields: ' . highlight_string('Fields: ' . highlight_string('' . $out); return $out; } @@ -2182,8 +2195,7 @@ class DrupalWebTestCase extends DrupalTestCase { * and the value is the button label. i.e.) array('op' => t('Refresh')). * @param $ajax_path * (optional) Override the path set by the Ajax settings of the triggering - * element. In the absence of both the triggering element's Ajax path and - * $ajax_path 'system/ajax' will be used. + * element. * @param $options * (optional) Options to be forwarded to url(). * @param $headers @@ -2210,8 +2222,10 @@ class DrupalWebTestCase extends DrupalTestCase { if (isset($path)) { $this->drupalGet($path, $options); } + $content = $this->content; $drupal_settings = $this->drupalSettings; + $current_url = $this->url; // Get the Ajax settings bound to the triggering element. if (!isset($ajax_settings)) { @@ -2230,31 +2244,31 @@ class DrupalWebTestCase extends DrupalTestCase { } // Add extra information to the POST data as ajax.js does. - $extra_post = ''; + $extra_post = array(); if (isset($ajax_settings['submit'])) { foreach ($ajax_settings['submit'] as $key => $value) { - $extra_post .= '&' . urlencode($key) . '=' . urlencode($value); + $extra_post[$key] = $value; } } - foreach ($this->xpath('//*[@id]') as $element) { + foreach ($this->xpath('//*[@id]') as $key => $element) { $id = (string) $element['id']; - $extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id); + $extra_post["ajax_html_ids[$key]"] = $id; } if (isset($drupal_settings['ajaxPageState'])) { - $extra_post .= '&' . urlencode('ajax_page_state[theme]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme']); - $extra_post .= '&' . urlencode('ajax_page_state[theme_token]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme_token']); + $extra_post['ajax_page_state[theme]'] = $drupal_settings['ajaxPageState']['theme']; + $extra_post['ajax_page_state[theme_token]'] = $drupal_settings['ajaxPageState']['theme_token']; foreach ($drupal_settings['ajaxPageState']['css'] as $key => $value) { - $extra_post .= '&' . urlencode("ajax_page_state[css][$key]") . '=1'; + $extra_post["ajax_page_state[css][$key]"] = '1'; } foreach ($drupal_settings['ajaxPageState']['js'] as $key => $value) { - $extra_post .= '&' . urlencode("ajax_page_state[js][$key]") . '=1'; + $extra_post["ajax_page_state[js][$key]"] = '1'; } } // Unless a particular path is specified, use the one specified by the - // Ajax settings, or else 'system/ajax'. + // Ajax settings, or else the current system path. if (!isset($ajax_path)) { - $ajax_path = isset($ajax_settings['url']) ? $ajax_settings['url'] : 'system/ajax'; + $ajax_path = isset($ajax_settings['url']) ? $ajax_settings['url'] : NULL; } // Submit the POST request. @@ -2284,8 +2298,14 @@ class DrupalWebTestCase extends DrupalTestCase { $wrapperNode = NULL; // When a command doesn't specify a selector, use the // #ajax['wrapper'] which is always an HTML ID. - if (!isset($command['selector'])) { - $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0); + if (isset($ajax_settings['wrapper'])) { + $wrapper_id = $ajax_settings['wrapper']; + } + elseif (strpos($command['selector'], '#') === 0) { + $wrapper_id = str_replace('#', '', $command['selector']); + } + if (isset($wrapper_id)) { + $wrapperNode = $xpath->query('//*[@id="' . $wrapper_id . '"]')->item(0); } // @todo Ajax commands can target any jQuery selector, but these are // hard to fully emulate with XPath. For now, just handle 'head' @@ -2293,12 +2313,15 @@ class DrupalWebTestCase extends DrupalTestCase { elseif (in_array($command['selector'], array('head', 'body'))) { $wrapperNode = $xpath->query('//' . $command['selector'])->item(0); } + else { + $this->fail(format_string('The Ajax selector %string is not supported by the testing framework.', array('%string' => $command['selector']))); + } if ($wrapperNode) { // ajax.js adds an enclosing DIV to work around a Safari bug. $newDom = new DOMDocument(); // DOM can load HTML soup. But, HTML soup can throw warnings, // suppress them. - $newDom->loadHTML('
' . $command['data'] . '
'); + @$newDom->loadHTML('
' . $command['data'] . '
'); // Suppress warnings thrown when duplicate HTML IDs are // encountered. This probably means we are replacing an element // with the same ID. @@ -2361,7 +2384,7 @@ class DrupalWebTestCase extends DrupalTestCase { } $content = $dom->saveHTML(); } - $this->drupalSetContent($content); + $this->drupalSetContent($content, $current_url); $this->drupalSetSettings($drupal_settings); $verbose = 'AJAX POST request to: ' . $path; diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test index afe02306bd..0300cbe9a5 100644 --- a/modules/simpletest/tests/ajax.test +++ b/modules/simpletest/tests/ajax.test @@ -496,7 +496,7 @@ class AJAXMultiFormTestCase extends AJAXTestCase { // page update, ensure the same as above. foreach ($field_xpaths as $form_html_id => $field_xpath) { for ($i = 0; $i < 2; $i++) { - $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_html_id); + $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), NULL, array(), array(), $form_html_id); $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.')); $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.')); $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other'); diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test index e52c8c42e1..a411e6ed21 100644 --- a/modules/simpletest/tests/form.test +++ b/modules/simpletest/tests/form.test @@ -628,6 +628,8 @@ class FormAlterTestCase extends DrupalWebTestCase { * Test form validation handlers. */ class FormValidationTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Form validation handlers', @@ -756,6 +758,7 @@ class FormValidationTestCase extends DrupalWebTestCase { * Test form element labels, required markers and associated output. */ class FormsElementsLabelsTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -843,6 +846,7 @@ class FormsElementsLabelsTestCase extends DrupalWebTestCase { * Test the tableselect form element for expected behavior. */ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -1068,6 +1072,7 @@ class FormsElementsTableSelectFunctionalTest extends DrupalWebTestCase { * Test the vertical_tabs form element for expected behavior. */ class FormsElementsVerticalTabsFunctionalTest extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -1104,6 +1109,7 @@ class FormsElementsVerticalTabsFunctionalTest extends DrupalWebTestCase { * values aren't lost due to a wrong form rebuild. */ class FormsFormStorageTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -1114,7 +1120,7 @@ class FormsFormStorageTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp('form_test'); + parent::setUp('form_test', 'dblog'); $this->web_user = $this->drupalCreateUser(array('access content')); $this->drupalLogin($this->web_user); @@ -1477,6 +1483,8 @@ class FormsFormCacheTestCase extends DrupalWebTestCase { * Test wrapper form callbacks. */ class FormsFormWrapperTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Form wrapper callback', @@ -1503,6 +1511,8 @@ class FormsFormWrapperTestCase extends DrupalWebTestCase { * Test $form_state clearance. */ class FormStateValuesCleanTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Form state values clearance', @@ -1512,7 +1522,7 @@ class FormStateValuesCleanTestCase extends DrupalWebTestCase { } function setUp() { - parent::setUp('form_test'); + parent::setUp('form_test', 'file'); } /** @@ -1551,6 +1561,8 @@ class FormStateValuesCleanTestCase extends DrupalWebTestCase { * Tests $form_state clearance with form elements having buttons. */ class FormStateValuesCleanAdvancedTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + /** * An image file path for uploading. */ @@ -1598,6 +1610,7 @@ class FormStateValuesCleanAdvancedTestCase extends DrupalWebTestCase { * @todo Add tests for other aspects of form rebuilding. */ class FormsRebuildTestCase extends DrupalWebTestCase { + public static function getInfo() { return array( 'name' => 'Form rebuilding', @@ -1665,7 +1678,7 @@ class FormsRebuildTestCase extends DrupalWebTestCase { // 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->drupalPostAJAX(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), NULL, array(), array(), 'page-node-form'); $this->assert(count($this->xpath('//div[contains(@class, "field-name-field-ajax-test")]//input[@type="text"]')) == 2, 'AJAX submission succeeded.'); // Submit the form with the non-Ajax "Save" button, leaving the title field @@ -1690,6 +1703,7 @@ class FormsRebuildTestCase extends DrupalWebTestCase { * Tests form redirection. */ class FormsRedirectTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -1765,6 +1779,7 @@ class FormsRedirectTestCase extends DrupalWebTestCase { * Test the programmatic form submission behavior. */ class FormsProgrammaticTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -1875,6 +1890,7 @@ class FormsProgrammaticTestCase extends DrupalWebTestCase { * Test that FAPI correctly determines $form_state['triggering_element']. */ class FormsTriggeringElementTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -1973,6 +1989,8 @@ class FormsTriggeringElementTestCase extends DrupalWebTestCase { * Tests rebuilding of arbitrary forms by altering them. */ class FormsArbitraryRebuildTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( 'name' => 'Rebuild arbitrary forms', @@ -2039,6 +2057,7 @@ class FormsArbitraryRebuildTestCase extends DrupalWebTestCase { * Tests form API file inclusion. */ class FormsFileInclusionTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( @@ -2056,7 +2075,7 @@ class FormsFileInclusionTestCase extends DrupalWebTestCase { * 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->drupalPostAJAX('form-test/load-include-menu', array(), array('op' => t('Save')), NULL, array(), array(), 'form-test-load-include-menu'); $this->assertText('Submit callback called.'); } @@ -2154,6 +2173,7 @@ class FormCheckboxTestCase extends DrupalWebTestCase { * Tests uniqueness of generated HTML IDs. */ class HTMLIdTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; public static function getInfo() { return array( diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module index 9f071826e5..5037115d7f 100644 --- a/modules/simpletest/tests/form_test.module +++ b/modules/simpletest/tests/form_test.module @@ -1922,6 +1922,15 @@ function form_test_two_instances() { return $return; } +/** + * Implements hook_form_FORM_ID_alter(). + */ +function form_test_form_page_node_form_alter(&$form, &$form_state) { + // @todo: AJAXMultiFormTestCase depends on $form_state['cache'] being true. + // See https://www.drupal.org/node/2821852. + $form_state['cache'] = TRUE; +} + /** * Menu callback for testing custom form includes. */