diff --git includes/ajax.inc includes/ajax.inc
index 74b4e73..56d552d 100644
--- includes/ajax.inc
+++ includes/ajax.inc
@@ -193,12 +193,12 @@
  *   functions.
  */
 function ajax_render($commands = array()) {
-  // Automatically extract any 'settings' added via drupal_add_js() and make
-  // them the first command.
-  $scripts = drupal_add_js(NULL, NULL);
-  if (!empty($scripts['settings'])) {
-    array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $scripts['settings']['data'])));
-  }
+  // AJAX responses are returned as JSON and not themed via html.tpl.php. Thus,
+  // markup returned by drupal_get_js() is not contained, but the client needs
+  // to know the JavaScript settings and files required by the new content, so
+  // we call ajax_get_js() to get that.
+  $commands = array_merge(ajax_get_js(), $commands);
+  $commands = array_merge(ajax_get_css(), $commands);
 
   // Allow modules to alter any AJAX response.
   drupal_alter('ajax_render', $commands);
@@ -207,6 +207,246 @@ function ajax_render($commands = array()) {
 }
 
 /**
+ * Return javascript files on the current page for use with with AJAX.
+ *
+ * This function is invoked during normal HTML page loads to tell the client
+ * what javascript files are in use on the page. This function is also invoked
+ * during AJAX operations to tell the client what javascript files may
+ * need to be added to the page.
+ *
+ * This function is the equivalent of drupal_get_js(), with the following
+ * differences:
+ *
+ * - By default, all scopes are processed at once, as AJAX responses typically
+ *   do not contain the same kind of 'header' and 'footer' scopes like full
+ *   page responses.
+ * - Files are not aggregated, because we do not know, which source files have
+ *   already been loaded as part of the base page. The client-side code will
+ *   determine new files and only load those.
+ * - Query_strings are not used here as they can cause files to be loaded via
+ *   ajax more than once if they change. We never want this,
+ *   even if the file has changed.
+ *
+ * @param $type
+ *   Either 'command' to get a command to send to the browser, or 'files' to
+ *   get a list of javascript files.
+ * @param $scope
+ *   (optional) The scope for which the JavaScript code should be returned. If
+ *   not specified, returns the code for all scopes.
+ * @param $javascript
+ *   (optional) An array with all JavaScript code. Defaults to the default
+ *   JavaScript array for the given scope.
+ *
+ * @return
+ *   An array of commands suitable for use with the ajax_render() function.
+ *
+ * @see drupal_get_js()
+ */
+function ajax_get_js($type = 'command', $scope = NULL, $javascript = NULL) {
+  if (!isset($javascript)) {
+    // Get the static directly so that the 'alter' if used below will
+    // be permanent.
+    $javascript = &drupal_static('drupal_add_js', array());
+  }
+  if (empty($javascript)) {
+    return array();
+  }
+
+  if ($type == 'command') {
+    // Allow modules to alter the JavaScript.
+    drupal_alter('js', $javascript);
+  }
+
+  // Filter out elements of the given scope.
+  if (isset($scope)) {
+    $items = array();
+    foreach ($javascript as $item) {
+      if ($item['scope'] == $scope) {
+        $items[] = $item;
+      }
+    }
+  }
+  else {
+    $items = array_values($javascript);
+  }
+
+  uasort($items, 'drupal_sort_weight');
+
+  foreach ($items as $item) {
+    switch ($item['type']) {
+      case 'setting':
+        // drupal_add_js() puts all settings into a single $item of type
+        // 'setting', so we can do a simple assignment here, rather than adding
+        // to an array.
+        $settings = $item['data'];
+        break;
+
+      case 'inline':
+        // @todo Presently, inline JavaScript code is ignored and not returned
+        //   to the client. Evaluate the use-cases of AJAX responses that depend
+        //   on inline JavaScript code to determine how to best handle that.
+        break;
+
+      case 'file':
+        $file = file_create_url($item['data']);
+        $js_files[$file] = $file;
+        break;
+
+      case 'external':
+        $js_files[$item['data']] = $item['data'];
+        break;
+    }
+  }
+
+  switch ($type) {
+    case 'command':
+      $commands = array();
+      if (!empty($settings)) {
+        $commands[] = ajax_command_settings(call_user_func_array('array_merge_recursive', $settings));
+      }
+      if (!empty($js_files)) {
+        $commands[] = ajax_command_scripts($js_files);
+      }
+      return $commands;
+
+    case 'files':
+    default:
+      return $js_files;
+  }
+}
+
+/**
+ * Return a list of all CSS files used in the current page load for use with AJAX.
+ *
+ * This function is invoked during normal HTML page loads to tell the client
+ * what javascript files are in use on the page. This function is also invoked
+ * during AJAX operations to tell the client what javascript files may
+ * need to be added to the page.
+ *
+ * This function is equivalent to drupal_get_css() with the following differences:
+ *
+ * - Files are not aggregated.
+ * - Query strings are ignored.
+ *
+ * @param $type
+ *   Either 'command' to get a command to send to the browser, or 'files' to
+ *   get a list of css files.
+ * @param $css
+ *   (optional) An array of CSS files. If no array is provided, the default
+ *   stylesheets array is used instead.
+ *
+ * @return
+ *   A string of XHTML CSS tags.
+ */
+function ajax_get_css($type = 'command', $css = NULL) {
+  if (!isset($css)) {
+    // Get the static directly so that the 'alter' if used below will
+    // be permanent.
+    $css = &drupal_static('drupal_add_css', array());
+  }
+
+  if ($type == 'command') {
+    // Allow modules and themes to alter the CSS items.
+    drupal_alter('css', $css);
+  }
+
+  // Sort CSS items according to their weights.
+  uasort($css, 'drupal_sort_weight');
+
+  // Remove the overridden CSS files. Later CSS files override former ones.
+  $previous_item = array();
+  foreach ($css as $key => $item) {
+    // Once the weight gets to 100, we're in theme CSS. We must stop here,
+    // because theme CSS files will not be added via AJAX.
+    if ($item['weight'] >= 100) {
+      break;
+    }
+
+    if ($item['type'] == 'file') {
+      // If defined, force a unique basename for this file.
+      $basename = isset($item['basename']) ? $item['basename'] : basename($item['data']);
+      if (isset($previous_item[$basename])) {
+        // Remove the previous item that shared the same base name.
+        unset($css[$previous_item[$basename]]);
+      }
+      $previous_item[$basename] = $key;
+    }
+  }
+
+  $css_files = array();
+  foreach ($css as $name => $file) {
+    if ($file['type'] == 'file' || $file['type'] == 'external') {
+      $css_files[$name] = array(
+        'href' => $file['data'],
+        'media' => $file['media'],
+        'browsers' => $file['browsers'],
+      );
+    }
+  }
+
+  switch ($type) {
+    case 'command':
+      $commands = array();
+      if (!empty($css_files)) {
+        $commands[] = ajax_command_css_files($css_files);
+      }
+      return $commands;
+
+    case 'files':
+    default:
+      return $css_files;
+  }
+
+}
+
+/**
+ * Add any last additional info to the footer needed for AJAX operations.
+ *
+ * If javascript is enabled on the page, we need to provide a list of all
+ * CSS and javascript files that we know about on the page so that they will
+ * not be included again in ajax operations.
+ *
+ * @return
+ *   A string that is to be appended in the footer of the page containing
+ *   javascript to inform the browser which .js and .css files are known
+ *   to be in use.
+ */
+function ajax_get_footer() {
+  $output = '';
+  $js_files = ajax_get_js('files');
+  $css_files = array();
+
+  // Only bother doing this if there are javascript files.
+  if ($js_files) {
+    // For inline Javascript to validate as XHTML, all Javascript containing
+    // XHTML needs to be wrapped in CDATA. To make that backwards compatible
+    // with HTML 4, we need to comment out the CDATA-tag.
+    $loaded = array('ajaxFiles' => array('scripts' => $js_files));
+
+    $css_files = ajax_get_css('files');
+    if ($css_files) {
+      $loaded['ajaxFiles']['css'] = $css_files;
+    }
+
+
+    $element = array(
+      '#tag' => 'script',
+      '#value' => '',
+      '#value_prefix' => "\n<!--//--><![CDATA[//><!--\n",
+      '#value_suffix' => "\n//--><!]]>\n",
+      '#value' => 'jQuery.extend(Drupal.settings, ' . drupal_json_encode($loaded) . ");",
+      '#attributes' => array(
+        'type' => 'text/javascript',
+      ),
+    );
+
+    $output .= theme('html_tag', array('element' => $element));
+  }
+
+  return $output;
+}
+
+/**
  * Get a form submitted via #ajax during an AJAX callback.
  *
  * This will load a form from the form cache used during AJAX operations. It
@@ -955,3 +1195,50 @@ function ajax_command_restripe($selector) {
   );
 }
 
+/**
+ * Creates scripts command for the AJAX responder.
+ *
+ * This will add JavaScript files to the output. Files that have already been
+ * processed will not be processed again.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.scripts()
+ * defined in misc/ajax.js.
+ *
+ * @param $files
+ *   The $files array is a simple array of filenames that match, exactly, the
+ *   scripts as they are added to the page or will be added to the page in the
+ *   future.
+ *
+ * @return
+ *   An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_scripts($files) {
+  return array(
+    'command' => 'scripts',
+    'files' => $files,
+  );
+}
+
+/**
+ * Creates css command for the AJAX responder.
+ *
+ * This will add CSS files to the output. Files that have already been
+ * processed will not be processed again.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.css()
+ * defined in misc/ajax.js.
+ *
+ * @param $files
+ *   The $files array is a simple array of filenames that match, exactly, the
+ *   css files as they are added to the page or will be added to the page in the
+ *   future.
+ *
+ * @return
+ *   An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_css_files($files) {
+  return array(
+    'command' => 'css_files',
+    'files' => $files,
+  );
+}
diff --git includes/common.inc includes/common.inc
index 9fc82bb..c2dd076 100644
--- includes/common.inc
+++ includes/common.inc
@@ -2816,7 +2816,7 @@ function drupal_add_css($data = NULL, $options = NULL) {
  */
 function drupal_get_css($css = NULL) {
   if (!isset($css)) {
-    $css = drupal_add_css();
+    $css = &drupal_static('drupal_add_css', array());
   }
 
   // Allow modules and themes to alter the CSS items.
@@ -3788,7 +3788,9 @@ function drupal_js_defaults($data = NULL) {
  */
 function drupal_get_js($scope = 'header', $javascript = NULL) {
   if (!isset($javascript)) {
-    $javascript = drupal_add_js();
+    // Get the static directly so that the 'alter' if used below will
+    // be permanent.
+    $javascript = &drupal_static('drupal_add_js', array());
   }
   if (empty($javascript)) {
     return '';
@@ -3842,6 +3844,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
       'type' => 'text/javascript',
     ),
   );
+
   foreach ($items as $item) {
     $query_string =  empty($item['version']) ? $default_query_string : $js_version_string . $item['version'];
 
@@ -3867,12 +3870,13 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
 
       case 'file':
         $js_element = $element;
+        if ($item['defer']) {
+          $js_element['#attributes']['defer'] = 'defer';
+        }
+        $query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
+        $uri = file_create_url($item['data']) . $query_string_separator . ($item['cache'] ? $query_string : REQUEST_TIME);
         if (!$item['preprocess'] || !$preprocess_js) {
-          if ($item['defer']) {
-            $js_element['#attributes']['defer'] = 'defer';
-          }
-          $query_string_separator = (strpos($item['data'], '?') !== FALSE) ? '&' : '?';
-          $js_element['#attributes']['src'] = file_create_url($item['data']) . $query_string_separator . ($item['cache'] ? $query_string : REQUEST_TIME);
+          $js_element['#attributes']['src'] = $uri;
           $processed[$index++] = theme('html_tag', array('element' => $js_element));
         }
         else {
@@ -3881,6 +3885,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
           $key = 'aggregate' . $index;
           $processed[$key] = '';
           $files[$key][$item['data']] = $item;
+          $reported_files[$uri] = 1;
         }
         break;
 
@@ -3900,7 +3905,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
   if ($preprocess_js && count($files) > 0) {
     foreach ($files as $key => $file_set) {
       $uri = drupal_build_js_cache($file_set);
-      // Only include the file if was written successfully. Errors are logged
+      // Only include the file if it was written successfully. Errors are logged
       // using watchdog.
       if ($uri) {
         $preprocess_file = file_create_url($uri);
@@ -6134,7 +6139,7 @@ function drupal_write_record($table, &$record, $primary_keys = array()) {
     }
 
     if (!property_exists($object, $field)) {
-      // Skip fields that are not provided, default values are already known 
+      // Skip fields that are not provided, default values are already known
       // by the database.
       continue;
     }
diff --git includes/theme.inc includes/theme.inc
index e325ad7..5d623fc 100644
--- includes/theme.inc
+++ includes/theme.inc
@@ -2002,7 +2002,7 @@ function theme_indentation($variables) {
 
 /**
  * Returns HTML output for a single table cell for theme_table().
- * 
+ *
  * @param $cell
  *   Array of cell information, or string to display in cell.
  * @param bool $header
@@ -2292,6 +2292,7 @@ function template_process_html(&$variables) {
   // Place the rendered HTML for the page body into a top level variable.
   $variables['page']              = $variables['page']['#children'];
   $variables['page_bottom'] .= drupal_get_js('footer');
+  $variables['page_bottom'] .= ajax_get_footer();
 
   $variables['head']    = drupal_get_html_head();
   $variables['css']     = drupal_add_css();
diff --git misc/ajax.js misc/ajax.js
index 553d2cc..b544147 100644
--- misc/ajax.js
+++ misc/ajax.js
@@ -16,6 +16,14 @@
 Drupal.ajax = Drupal.ajax || {};
 
 /**
+ * Holds a list of files known to be on the page so that AJAX requests can
+ * selectively load additionally required files later on.
+ *
+ * @see Drupal.ajax.prototype.commands.scripts()
+ */
+Drupal.ajaxFiles = Drupal.ajaxFiles || { scripts: {}, css: {} };
+
+/**
  * Attaches the AJAX behavior to each AJAX form element.
  */
 Drupal.behaviors.AJAX = {
@@ -427,6 +435,69 @@ Drupal.ajax.prototype.commands = {
   },
 
   /**
+   * Command to add scripts to the page.
+   *
+   * Keeps track of files already on the page and attempts to only load
+   * additionally required scripts.
+   */
+  scripts: function (ajax, response, status) {
+    // Build a list of scripts already loaded. Aggregated files have already
+    // been registered in Drupal.ajaxFiles.scripts via drupal_get_js().
+    // Non-aggregated files can simply be enumerated by script tags on the page.
+    $('script').each(function () {
+      Drupal.ajaxFiles.scripts[Drupal.getPath(this.src)] = this.src;
+    });
+
+    var html = '';
+    for (var file in response.files) {
+      // @todo Files in this stack are based on their 'src' attribute and/or a
+      //   previous AJAX request. Their URIs may contain query strings, domain
+      //   names, and other additions that can conflict with this simple key
+      //   check. Consider to store and use basenames?
+      if (!Drupal.ajaxFiles.scripts[file]) {
+        Drupal.ajaxFiles.scripts[file] = file;
+        html += '<script type="text/javascript" src="' + file + '"></script>';
+      }
+    }
+    if (html) {
+      $('html').prepend(html);
+    }
+  },
+
+  /**
+   * Command to add CSS files to the page.
+   *
+   * Keeps track of files already on the page. Additionally, if a previous
+   * css_files command (During AJAX) added CSS to the page, this will be
+   * removed before new files are added, in order to keep IE from exploding
+   * with file after file.
+   */
+  css_files: function(ajax, response, status) {
+    // Build a list of css files already loaded:
+    $('link:not(.drupal-temporary-css)').each(function () {
+      if ($(this).attr('type') == 'text/css') {
+        var link = Drupal.getPath($(this).attr('href'));
+        if (link) {
+          Drupal.ajaxFiles.css[link] = $(this).attr('href');
+        }
+      }
+    });
+
+    var html = '';
+    for (var i in response.files) {
+      if (!Drupal.ajaxFiles.css[response.files[i].href]) {
+        html += '<link class="drupal-temporary-css" type="text/css" rel="stylesheet" media="' + response.files[i].media +
+          '" href="' + response.files[i].href + '" />';
+      }
+    }
+
+    if (html) {
+      $('link.drupal-temporary-css').remove();
+      $('body').append($(html));
+    }
+  },
+
+  /**
    * Command to attach data using jQuery's data API.
    */
   data: function (ajax, response, status) {
@@ -447,4 +518,14 @@ Drupal.ajax.prototype.commands = {
   }
 };
 
+/**
+ * Only on the beginning of the page, not using a behavior, add files that
+ * may have been specified in the footer of the page to our internal array.
+ */
+
+$(function () {
+  if (Drupal.settings.ajaxFiles && Drupal.settings.ajaxFiles.scripts) {
+    $.extend(Drupal.ajaxFiles.scripts, Drupal.settings.ajaxFiles.scripts);
+  }
+});
 })(jQuery);
diff --git misc/drupal.js misc/drupal.js
index 7d0d068..ef45499 100644
--- misc/drupal.js
+++ misc/drupal.js
@@ -325,6 +325,22 @@ Drupal.ajaxError = function (xmlhttp, uri) {
   return message;
 };
 
+/**
+ * Get the actual path of a link, dropping items after the ?
+ */
+Drupal.getPath = function (link) {
+  if (!link) {
+    return;
+  }
+
+  var index = link.indexOf('?');
+  if (index != -1) {
+    link = link.substr(0, index);
+  }
+
+  return link;
+}
+
 // Class indicating that JS is enabled; used for styling purpose.
 $('html').addClass('js');
 
diff --git modules/field/tests/field.test modules/field/tests/field.test
index 8e8bd73..a0d5044 100644
--- modules/field/tests/field.test
+++ modules/field/tests/field.test
@@ -1439,7 +1439,7 @@ class FieldFormTestCase extends FieldTestCase {
     // Press 'add more' button through AJAX, and place the expected HTML result
     // as the tested content.
     $commands = $this->drupalPostAJAX(NULL, $edit, $this->field_name . '_add_more');
-    $this->content = $commands[1]['data'];
+    $this->content = $commands[2]['data'];
 
     for ($delta = 0; $delta <= $delta_range; $delta++) {
       $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
diff --git modules/poll/poll.test modules/poll/poll.test
index b16a676..3918dc9 100644
--- modules/poll/poll.test
+++ modules/poll/poll.test
@@ -342,7 +342,7 @@ class PollJSAddChoice extends DrupalWebTestCase {
     // Press 'add choice' button through AJAX, and place the expected HTML result
     // as the tested content.
     $commands = $this->drupalPostAJAX(NULL, $edit, array('op' => t('More choices')));
-    $this->content = $commands[1]['data'];
+    $this->content = $commands[2]['data'];
 
     $this->assertFieldByName('choice[chid:0][chtext]', $edit['choice[new:0][chtext]'], t('Field !i found', array('!i' => 0)));
     $this->assertFieldByName('choice[chid:1][chtext]', $edit['choice[new:1][chtext]'], t('Field !i found', array('!i' => 1)));
diff --git modules/simpletest/tests/ajax.test modules/simpletest/tests/ajax.test
index 232074f..f227d5d 100644
--- modules/simpletest/tests/ajax.test
+++ modules/simpletest/tests/ajax.test
@@ -1,5 +1,6 @@
 <?php
-// $Id: ajax.test,v 1.14 2010/08/05 23:53:38 webchick Exp $
+// $Id$
+
 
 class AJAXTestCase extends DrupalWebTestCase {
   function setUp() {
@@ -11,19 +12,51 @@ class AJAXTestCase extends DrupalWebTestCase {
   }
 
   /**
-   * Returns the passed-in commands array without the initial settings command.
+   * Returns the passed-in commands array without the commands automatically prepended by ajax_render().
    *
    * Depending on factors that may be irrelevant to a particular test,
-   * ajax_render() may prepend a settings command. This function allows the test
-   * to only have to concern itself with the commands that were passed to
-   * ajax_render().
+   * ajax_render() may prepend some commands for adding JavaScript files and
+   * settings. This function allows the test to only have to concern itself with
+   * the commands that were passed to ajax_render().
+   *
+   * @todo This function is named discardSettings() for legacy reasons, because
+   *   at one time, ajax_render() only added a 'settings' command. For Drupal 8,
+   *   or when a BC break of Drupal 7 tests is acceptable, rename this function
+   *   to be more accurate.
    */
   protected function discardSettings($commands) {
     if ($commands[0]['command'] == 'settings') {
       array_shift($commands);
     }
+    if ($commands[0]['command'] == 'scripts') {
+      array_shift($commands);
+    }
     return $commands;
   }
+
+  /**
+  * Utility: Search through a settings return; find/return a setting.
+  * @param $needle
+  *   Array of info we're looking to find in the return.
+  * @param $json
+  *   The json return that has settings and commands in it.
+  */
+  function findJSONKey($conditions, $json) {
+    $found = FALSE;
+    foreach ($json as $item) {
+      $count_success = 0;
+      foreach ($conditions as $key => $value) {
+        //compare both key and value towards our data that was given
+        if (isset($item[$key]) && ($item[$key] == $value)) {
+          $count_success++;
+        }
+      }
+      if(sizeof($conditions) == $count_success) {
+        $found = TRUE;
+      }
+    }
+    return $found;
+  }
 }
 
 /**
@@ -42,27 +75,50 @@ class AJAXFrameworkTestCase extends AJAXTestCase {
    * Test proper passing of JavaScript settings via ajax_render().
    */
   function testAJAXRender() {
-    $result = $this->drupalGetAJAX('ajax-test/render');
-    // Verify that JavaScript settings are contained (always first).
-    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
-    // Verify that basePath is contained in JavaScript settings.
-    $this->assertEqual($result[0]['settings']['basePath'], base_path(), t('Base path is contained in JavaScript settings.'));
+    $commands = $this->drupalGetAJAX('ajax-test/render');
+
+    // Verify that there is a command to load settings added with
+    // drupal_add_js().
+    $result = $this->findJSONKey(array('command' => 'settings'), $commands);
+    $this->assertTrue($result, t('ajax_render() added a settings command to load settings added with drupal_add_js().'));
+    $result = $this->findJSONKey(array('settings' => array('basePath' => base_path(), 'ajax' => 'test')), $commands);
+    $this->assertTrue($result, t('The %setting setting is included.', array('%setting' => 'basePath')));
+
+    // Verify that there is a command to load script files added with
+    // drupal_add_js().
+    $result = $this->findJSONKey(array('command' => 'scripts'), $commands);
+    $this->assertTrue($result, t('ajax_render() added a scripts command to load files added with drupal_add_js().'));
+
+    $file = file_create_url('misc/drupal.js');
+
+    //find the files array
+    foreach ($commands as $command) {
+      if($command['command'] == 'scripts') {
+          //Verify that our file is included in the list
+          $this->assertEqual($command['files'][$file], $file, t('The %file file is included.', array('%file' => 'misc/drupal.js')));
+      }
+    }
   }
 
   /**
    * Test behavior of ajax_render_error().
    */
   function testAJAXRenderError() {
-    $result = $this->discardSettings($this->drupalGetAJAX('ajax-test/render-error'));
+    $commands = $this->discardSettings($this->drupalGetAJAX('ajax-test/render-error'));
     // Verify default error message.
-    $this->assertEqual($result[0]['command'], 'alert', t('ajax_render_error() invokes alert command.'));
-    $this->assertEqual($result[0]['text'], t('An error occurred while handling the request: The server received invalid input.'), t('Default error message is output.'));
+    $result = $this->findJSONKey(array('command' => 'alert', 'text' => t('An error occurred while handling the request: The server received invalid input.')), $commands);
+    $this->assertTrue($result, t('ajax_render_error() invokes alert command.'));
+
+    //$this->assertEqual($result[0]['command'], 'alert', t('ajax_render_error() invokes alert command.'));
+    //$this->assertEqual($result[0]['text'], t('An error occurred while handling the request: The server received invalid input.'), t('Default error message is output.'));
     // Verify custom error message.
     $edit = array(
       'message' => 'Custom error message.',
     );
-    $result = $this->discardSettings($this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit)));
-    $this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.'));
+    $commands = $this->discardSettings($this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit)));
+    $result = $this->findJSONKey(array('text' => $edit['message']), $commands);
+    $this->assertTrue($result, t('Custom error message is output.'));
+    //$this->assertEqual($result[0]['text'], $edit['message'], t('Custom error message is output.'));
   }
 }
 
@@ -84,11 +140,12 @@ class AJAXCommandsTestCase extends AJAXTestCase {
   function testAJAXRender() {
     $commands = array();
     $commands[] = ajax_command_settings(array('foo' => 42));
-    $result = $this->drupalGetAJAX('ajax-test/render', array('query' => array('commands' => $commands)));
-    // Verify that JavaScript settings are contained (always first).
-    $this->assertIdentical($result[0]['command'], 'settings', t('drupal_add_js() settings are contained first.'));
+    // discardSettings() discards only what is automatically added by
+    // ajax_render(), not the one added by the ajax-test/render callback.
+    $result = $this->discardSettings($this->drupalGetAJAX('ajax-test/render', array('query' => array('commands' => $commands))));
     // Verify that the custom setting is contained.
-    $this->assertEqual($result[1]['settings']['foo'], 42, t('Custom setting is output.'));
+    $result = $this->findJSONKey(array('settings' => array('foo' => 42)), $commands);
+    $this->assertTrue($result, t('Custom setting is output.'));
   }
 
   /**
@@ -103,68 +160,69 @@ class AJAXCommandsTestCase extends AJAXTestCase {
 
     // Tests the 'after' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'insert', 'method' => 'after', 'data' => 'This will be placed after'), $commands);
+    $this->assertTrue($result, "'after' AJAX command issued with correct data");
 
     // Tests the 'alert' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'alert', 'text' => 'Alert'), $commands);
+    $this->assertTrue($result, "'alert' AJAX Command issued with correct text");
 
     // Tests the 'append' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'insert', 'method' => 'append', 'data' => 'Appended text'), $commands);
+    $this->assertTrue($result, "'append' AJAX command issued with correct data");
 
     // Tests the 'before' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'insert', 'method' => 'before', 'data' => 'Before text'), $commands);
+    $this->assertTrue($result, "'before' AJAX command issued with correct data");
 
     // Tests the 'changed' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'changed', 'selector' => '#changed_div'), $commands);
+    $this->assertTrue($result, "'changed' AJAX command issued with correct selector");
 
     // Tests the 'changed' command using the second argument.
     $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");
+    $result = $this->findJSONKey(array('command' => 'changed', 'selector' => '#changed_div', 'asterisk' => '#changed_div_mark_this'), $commands);
+    $this->assertTrue($result, "'changed' AJAX command (with asterisk) issued with correct selector");
 
     // Tests the 'css' command.
     $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");
+
+    $result = $this->findJSONKey(array('command' => 'css', 'selector' => '#css_div', 'argument' => array('background-color' => 'blue')), $commands);
+    $this->assertTrue($result, "'css' AJAX command issued with correct selector");
 
     // Tests the 'data' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'data', 'name' => 'testkey', 'value' => 'testvalue'), $commands);
+    $this->assertTrue($result, "'data' AJAX command issued with correct key and value");
 
     // Tests the 'html' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'insert', 'method' => 'html', 'data' => 'replacement text'), $commands);
+    $this->assertTrue($result, "'html' AJAX command issued with correct data");
 
     // Tests the 'insert' command.
     $commands = $this->discardSettings($this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method']."))));
-    $command = $commands[0];
-    $this->assertTrue($command['command'] == 'insert' && $command['method'] == NULL && $command['data'] == 'insert replacement text', "'insert' AJAX command issued with correct data");
+    $result = $this->findJSONKey(array('command' => 'insert', 'data' => 'insert replacement text'), $commands);
+    $this->assertTrue($result, "'insert' AJAX command issued with correct data");
 
     // Tests the 'prepend' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'insert', 'method' => 'prepend', 'data' => 'prepended text'), $commands);
+    $this->assertTrue($result, "'prepend' AJAX command issued with correct data");
 
     // Tests the 'remove' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'remove', 'selector' => '#remove_text'), $commands);
+    $this->assertTrue($result, "'remove' AJAX command issued with correct command and selector");
 
     // Tests the 'restripe' command.
     $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");
+    $result = $this->findJSONKey(array('command' => 'restripe', 'selector' => '#restripe_table'), $commands);
+    $this->assertTrue($result, "'restripe' AJAX command issued with correct selector");
   }
 }
 
@@ -197,8 +255,8 @@ class AJAXFormValuesTestCase extends AJAXTestCase {
         'select' => $item,
       );
       $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select'));
-      $data_command = $commands[1];
-      $this->assertEqual($data_command['value'], $item);
+      $result = $this->findJSONKey(array('value' => $item), $commands);
+      $this->assertTrue($result, "verification of AJAX form values from a selectbox issued with a correct value");
     }
 
     // Verify form values of a checkbox element.
@@ -207,8 +265,8 @@ class AJAXFormValuesTestCase extends AJAXTestCase {
         'checkbox' => $item,
       );
       $commands = $this->discardSettings($this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox'));
-      $data_command = $commands[1];
-      $this->assertEqual((int) $data_command['value'], (int) $item);
+      $result = $this->findJSONKey(array('value' => (int)$item), $commands);
+      $this->assertTrue($result, "verification of AJAX form values from a checkbox issued with a correct value");
     }
   }
 }
@@ -281,12 +339,17 @@ class AJAXMultiFormTestCase extends AJAXTestCase {
     // 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++) {
         $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']);
+        foreach($commands as $key => $command) {
+          if(isset($command['settings']) && $command['command'] == 'settings') {
+              $settings = array_merge_recursive($settings, $command['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');
@@ -327,3 +390,4 @@ class AJAXElementValidation extends AJAXTestCase {
   }
 }
 
+
diff --git modules/simpletest/tests/ajax_test.module modules/simpletest/tests/ajax_test.module
index 46d36b0..09b9ba2 100644
--- modules/simpletest/tests/ajax_test.module
+++ modules/simpletest/tests/ajax_test.module
@@ -31,7 +31,8 @@ function ajax_test_menu() {
  * Menu callback; Returns $_GET['commands'] suitable for use by ajax_deliver().
  *
  * Additionally ensures that ajax_render() incorporates JavaScript settings
- * by invoking drupal_add_js() with a dummy setting.
+ * and files by invoking drupal_add_js() with a dummy setting, which causes
+ * drupal_add_js() to also automatically add "misc/drupal.js".
  */
 function ajax_test_render() {
   // Prepare AJAX commands.
