summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2010-09-19 18:38:58 +0000
committerDries Buytaert <dries@buytaert.net>2010-09-19 18:38:58 +0000
commit8580169d0e05e25cd1791f538c0a32ec45082642 (patch)
tree7642fe7555e601566d91ff4468b3c825388867c5
parent5c4dfd44f956cbffd7d201fd6c47e9a37e86ad2d (diff)
downloadbrdo-8580169d0e05e25cd1791f538c0a32ec45082642.tar.gz
brdo-8580169d0e05e25cd1791f538c0a32ec45082642.tar.bz2
- Patch #789186 by effulgentsia: improve drupalPostAJAX() to be used more easily, leading to better AJAX test coverage. (Rollback)
-rw-r--r--modules/simpletest/drupal_web_test_case.php210
-rw-r--r--modules/simpletest/tests/ajax.test15
-rw-r--r--modules/simpletest/tests/form.test6
-rw-r--r--modules/simpletest/tests/form_test.file.inc13
4 files changed, 85 insertions, 159 deletions
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index 5b1849dd6..36568a8d4 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -690,13 +690,6 @@ class DrupalWebTestCase extends DrupalTestCase {
protected $plainTextContent;
/**
- * The value of the Drupal.settings JavaScript variable for the page currently loaded in the internal browser.
- *
- * @var Array
- */
- protected $drupalSettings;
-
- /**
* The parsed version of the page.
*
* @var SimpleXMLElement
@@ -1705,14 +1698,8 @@ class DrupalWebTestCase extends DrupalTestCase {
* Note that this is not the Drupal $form_id, but rather the HTML ID of the
* 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.
- * 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").
- */
- 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) {
$submit_matches = FALSE;
$ajax = is_array($submit);
if (isset($path)) {
@@ -1763,7 +1750,23 @@ 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;
+ // For AJAX requests, add '_triggering_element_*' and
+ // 'ajax_html_ids' to the POST data, as ajax.js does.
+ if ($ajax) {
+ 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']);
+ }
+ foreach ($this->xpath('//*[@id]') as $element) {
+ $id = (string) $element['id'];
+ $post[] = urlencode('ajax_html_ids[]') . '=' . urlencode($id);
+ }
+ }
+ $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.
@@ -1800,131 +1803,70 @@ class DrupalWebTestCase extends DrupalTestCase {
*
* @see ajax.js
*/
- protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = 'system/ajax', array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) {
+ protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = 'system/ajax', array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = array()) {
// Get the content of the initial page prior to calling drupalPost(), since
// drupalPost() replaces $this->content.
if (isset($path)) {
$this->drupalGet($path, $options);
}
- $content = $this->content;
- $drupal_settings = $this->drupalSettings;
-
- // Get the AJAX settings bound to the triggering element.
- if (!isset($ajax_settings)) {
- if (is_array($triggering_element)) {
- $xpath = '//*[@name="' . key($triggering_element) . '" and @value="' . current($triggering_element) . '"]';
- }
- else {
- $xpath = '//*[@name="' . $triggering_element . '"]';
- }
- if (isset($form_html_id)) {
- $xpath = '//form[@id="' . $form_html_id . '"]' . $xpath;
- }
- $element = $this->xpath($xpath);
- $element_id = (string) $element[0]['id'];
- $ajax_settings = $drupal_settings['ajax'][$element_id];
- }
-
- // Add extra information to the POST data as ajax.js does.
- $extra_post = '';
- if (isset($ajax_settings['submit'])) {
- foreach ($ajax_settings['submit'] as $key => $value) {
- $extra_post .= '&' . urlencode($key) . '=' . urlencode($value);
- }
- }
- foreach ($this->xpath('//*[@id]') as $element) {
- $id = (string) $element['id'];
- $extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id);
- }
+ $content = $this->drupalGetContent();
+ $return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id));
- // Submit the POST request.
- $return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post));
-
- // Change the page content by applying the returned commands.
+ // We need $ajax_settings['wrapper'] to perform DOM manipulation.
if (!empty($ajax_settings) && !empty($return)) {
- // ajax.js applies some defaults to the settings object, so do the same
- // for what's used by this function.
- $ajax_settings += array(
- 'method' => 'replaceWith',
- );
// DOM can load HTML soup. But, HTML soup can throw warnings, suppress
// them.
@$dom = DOMDocument::loadHTML($content);
foreach ($return as $command) {
- switch ($command['command']) {
- case 'settings':
- $drupal_settings = array_merge_recursive($drupal_settings, $command['settings']);
- break;
-
- case 'insert':
- // @todo ajax.js can process commands that include a 'selector', but
- // these are hard to emulate with DOMDocument. For now, we only
- // implement 'insert' commands that use $ajax_settings['wrapper'].
- if (!isset($command['selector'])) {
- // $dom->getElementById() doesn't work when drupalPostAJAX() is
- // invoked multiple times for a page, so use XPath instead. This
- // also sets us up for adding support for $command['selector'] in
- // the future, once we figure out how to transform a jQuery
- // selector to XPath.
- $xpath = new DOMXPath($dom);
- $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0);
- if ($wrapperNode) {
- // ajax.js adds an enclosing DIV to work around a Safari bug.
- $newDom = new DOMDocument();
- $newDom->loadHTML('<div>' . $command['data'] . '</div>');
- $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);
- $method = isset($command['method']) ? $command['method'] : $ajax_settings['method'];
- // The "method" is a jQuery DOM manipulation function. Emulate
- // each one using PHP's DOMNode API.
- switch ($method) {
- case 'replaceWith':
- $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode);
- break;
- case 'append':
- $wrapperNode->appendChild($newNode);
- break;
- case 'prepend':
- // If no firstChild, insertBefore() falls back to
- // appendChild().
- $wrapperNode->insertBefore($newNode, $wrapperNode->firstChild);
- break;
- case 'before':
- $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode);
- break;
- case 'after':
- // If no nextSibling, insertBefore() falls back to
- // appendChild().
- $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode->nextSibling);
- break;
- case 'html':
- foreach ($wrapperNode->childNodes as $childNode) {
- $wrapperNode->removeChild($childNode);
- }
- $wrapperNode->appendChild($newNode);
- break;
+ // @todo ajax.js can process commands other than 'insert' and can
+ // process commands that include a 'selector', but these are hard to
+ // emulate with DOMDocument. For now, we only implement 'insert'
+ // commands that use $ajax_settings['wrapper'].
+ if ($command['command'] == 'insert' && !isset($command['selector'])) {
+ // $dom->getElementById() doesn't work when drupalPostAJAX() is
+ // invoked multiple times for a page, so use XPath instead. This also
+ // sets us up for adding support for $command['selector'], though it
+ // will require transforming a jQuery selector to XPath.
+ $xpath = new DOMXPath($dom);
+ $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0);
+ if ($wrapperNode) {
+ // ajax.js adds an enclosing DIV to work around a Safari bug.
+ $newDom = new DOMDocument();
+ $newDom->loadHTML('<div>' . $command['data'] . '</div>');
+ $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE);
+ $method = isset($command['method']) ? $command['method'] : $ajax_settings['method'];
+ // The "method" is a jQuery DOM manipulation function. Emulate each
+ // one using PHP's DOMNode API.
+ switch ($method) {
+ case 'replaceWith':
+ $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode);
+ break;
+ case 'append':
+ $wrapperNode->appendChild($newNode);
+ break;
+ case 'prepend':
+ // If no firstChild, insertBefore() falls back to appendChild().
+ $wrapperNode->insertBefore($newNode, $wrapperNode->firstChild);
+ break;
+ case 'before':
+ $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode);
+ break;
+ case 'after':
+ // If no nextSibling, insertBefore() falls back to appendChild().
+ $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode->nextSibling);
+ break;
+ case 'html':
+ foreach ($wrapperNode->childNodes as $childNode) {
+ $wrapperNode->removeChild($childNode);
}
- }
+ $wrapperNode->appendChild($newNode);
+ break;
}
- break;
-
- // @todo Add suitable implementations for these commands in order to
- // have full test coverage of what ajax.js can do.
- case 'remove':
- break;
- case 'changed':
- break;
- case 'css':
- break;
- case 'data':
- break;
- case 'restripe':
- break;
+ }
}
}
- $content = $dom->saveHTML();
+ $this->drupalSetContent($dom->saveHTML());
}
- $this->drupalSetContent($content);
- $this->drupalSetSettings($drupal_settings);
return $return;
}
@@ -2456,13 +2398,6 @@ class DrupalWebTestCase extends DrupalTestCase {
}
/**
- * Gets the value of the Drupal.settings JavaScript variable for the currently loaded page.
- */
- protected function drupalGetSettings() {
- return $this->drupalSettings;
- }
-
- /**
* Gets an array containing all e-mails sent during this test case.
*
* @param $filter
@@ -2500,17 +2435,6 @@ class DrupalWebTestCase extends DrupalTestCase {
$this->url = $url;
$this->plainTextContent = FALSE;
$this->elements = FALSE;
- $this->drupalSettings = array();
- if (preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $content, $matches)) {
- $this->drupalSettings = drupal_json_decode($matches[1]);
- }
- }
-
- /**
- * Sets the value of the Drupal.settings JavaScript variable for the currently loaded page.
- */
- protected function drupalSetSettings($settings) {
- $this->drupalSettings = $settings;
}
/**
diff --git a/modules/simpletest/tests/ajax.test b/modules/simpletest/tests/ajax.test
index 9377f4cdb..91572bda0 100644
--- a/modules/simpletest/tests/ajax.test
+++ b/modules/simpletest/tests/ajax.test
@@ -269,17 +269,24 @@ class AJAXMultiFormTestCase extends AJAXTestCase {
// of field items and "add more" button for the multi-valued field within
// each form.
$this->drupalGet('form-test/two-instances-of-same-form');
- foreach ($field_xpaths as $form_html_id => $field_xpath) {
+ foreach ($field_xpaths as $form_id => $field_xpath) {
$this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
$this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
}
$this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
// Submit the "add more" button of each form twice. After each corresponding
- // page update, ensure the same as above.
- foreach ($field_xpaths as $form_html_id => $field_xpath) {
+ // page update, ensure the same as above. To successfully implement
+ // consecutive AJAX submissions, we need to manage $settings as ajax.js
+ // does for Drupal.settings.
+ preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
+ $settings = drupal_json_decode($matches[1]);
+ foreach ($field_xpaths as $form_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);
+ $button = $this->xpath($field_xpath . $button_xpath_suffix);
+ $button_id = (string) $button[0]['id'];
+ $commands = $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_id, $settings['ajax'][$button_id]);
+ $settings = array_merge_recursive($settings, $commands[0]['settings']);
$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 969f3f398..c3ef7fd9d 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -996,7 +996,11 @@ 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');
+ preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $this->content, $matches);
+ $settings = drupal_json_decode($matches[1]);
+ $button = $this->xpath('//input[@name="field_ajax_test_add_more"]');
+ $button_id = (string) $button[0]['id'];
+ $this->drupalPostAJAX(NULL, array(), array('field_ajax_test_add_more' => t('Add another item')), 'system/ajax', array(), array(), 'page-node-form', $settings['ajax'][$button_id]);
$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
diff --git a/modules/simpletest/tests/form_test.file.inc b/modules/simpletest/tests/form_test.file.inc
index 808863ede..96681eaa8 100644
--- a/modules/simpletest/tests/form_test.file.inc
+++ b/modules/simpletest/tests/form_test.file.inc
@@ -13,17 +13,11 @@
function form_test_load_include_menu($form, &$form_state) {
// Submit the form via AJAX. That way the FAPI has to care about including
// the file specified in hook_menu().
- $ajax_wrapper_id = drupal_html_id('form-test-load-include-menu-ajax-wrapper');
- $form['ajax_wrapper'] = array(
- '#markup' => '<div id="' . $ajax_wrapper_id . '"></div>',
- );
$form['button'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#submit' => array('form_test_load_include_submit'),
'#ajax' => array(
- 'wrapper' => $ajax_wrapper_id,
- 'method' => 'append',
'callback' => 'form_test_load_include_menu_ajax',
),
);
@@ -38,12 +32,9 @@ function form_test_load_include_submit($form, $form_state) {
}
/**
- * Ajax callback for the file inclusion via menu test.
+ * Ajax callback for the file inclusion via menu test. We don't need to return
+ * anything as the messages are added automatically.
*/
function form_test_load_include_menu_ajax($form) {
- // We don't need to return anything, since #ajax['method'] is 'append', which
- // does not remove the original #ajax['wrapper'] element, and status messages
- // are automatically added by the AJAX framework as long as there's a wrapper
- // element to add them to.
return '';
}