Test framework for AHAH.

From: Damien Tournoud <damien@tournoud.net>


---

 modules/simpletest/drupal_web_test_case.php |   92 ++++++++++++++++++++++++++-
 1 files changed, 88 insertions(+), 4 deletions(-)


diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index 3cbb3af..425d070 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -1027,21 +1027,26 @@ class DrupalWebTestCase {
   /**
    * Parse content returned from curlExec using DOM and SimpleXML.
    *
+   * @param $with_dom
+   *   Also generate a DOM object in $this->domElements.
    * @return
    *   A SimpleXMLElement or FALSE on failure.
    */
-  protected function parse() {
-    if (!$this->elements) {
-      // DOM can load HTML soup. But, HTML soup can throw warnings, supress
-      // them.
+  protected function parse($with_dom = FALSE) {
+    if (!$this->elements || ($with_dom && !$this->domElements)) {
+      // DOM can load HTML soup but that can throw warnings, suppress them.
       @$htmlDom = DOMDocument::loadHTML($this->content);
       if ($htmlDom) {
         $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser'));
         // It's much easier to work with simplexml than DOM, luckily enough
         // we can just simply import our DOM tree.
         $this->elements = simplexml_import_dom($htmlDom);
+        if ($with_dom) {
+          $this->domElements = $htmlDom;
+        }
       }
     }
+
     if (!$this->elements) {
       $this->fail(t('Parsed page successfully.'), t('Browser'));
     }
@@ -1252,6 +1257,85 @@ class DrupalWebTestCase {
   }
 
   /**
+   * Perform an AHAH request on a given form element.
+   *
+   * @param $action
+   *   The URL to post to, or NULL to post to the current page.
+   * @param $node_path
+   *   The XPath of the nodes to replace.
+   *   Use "//div[@id = 'my_element_id']" to select an element by ID.
+   * @param $method
+   *   The AHAH replacement method. Possible values: 'replace' (default),
+   *   'after', 'append', 'before' and 'prepend'.
+   * @param  $post
+   *   Field data in an assocative array. A checkbox can be set to TRUE to be
+   *   checked and FALSE to be unchecked. Note that when a form contains file 
+   *   upload fields, other fields cannot start with the '@' character.
+   *
+   *   Multiple select fields can be set using name[] and setting each of the
+   *   possible values. Example:
+   *   $edit = array();
+   *   $edit['name[]'] = array('value1', 'value2');
+   * @param $upload
+   *   An associative array of files to upload.
+   * @return
+   *   TRUE on success, FALSE on failure.
+   */
+  protected function drupalAHAH($action, $node_path, $method = 'replace', array $post = array(), array $upload = array()) {
+    if ($this->parse(TRUE)) {
+      $response = $this->drupalPostDirect($action, $post, $upload);
+
+      // The response is generated by drupal_to_js: we need to undo some escaping.
+      $response = json_decode(str_replace(array('\x3c', '\x3e', '\x26'), array("<", ">", "&"), $response));
+
+      if (!$this->assertFalse(empty($response->status), t("JSON status is TRUE.")) {
+        return FALSE;
+      }
+
+      if (!$this->assertFalse(empty($response->data), t("JSON contains a valid answer."))) {
+        return FALSE;
+      }
+
+      // Import the resulting HTML.
+      @$result = DOMDocument::loadHTML($response->data);
+      $new_node = $dom->importNode($result->firstChild, TRUE);
+
+      // Select the node.
+      $xpath = new DOMXPath($this->domElements);
+      $nodes = $xpath->query($node_path);
+
+      foreach ($nodes as $node) {
+        switch ($method) {
+          case 'replace':
+            $node->parentNode->replaceChild($new_node, $node);
+            break;
+          case 'after':
+            if ($node->nextSibling) {
+              $node->parentNode->insertBefore($new_node, $node->nextSibling);
+            }
+            else {
+              $node->parentNode->appendChild($new_node);
+            }
+            break;
+          case 'append':
+            $node->appendChild($new_node);
+            break;
+          case 'before':
+            $node->parentNode->insertBefore($new_node, $node);
+            break;
+        }
+
+      }
+
+      // Finally, replace the content.
+      $this->content = $this->domElements->saveHTML();
+      $this->elements = simplexml_import_dom($this->domElements);
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
    * Retrieves only the headers for a Drupal path or an absolute path.
    *
    * @param $path
