/**
  * Extends WebTestBase::drupalPostAjaxForm() to replace additional content
  * on the page after an ajax submission.
  *
  * WebTestBase::drupalPostAjaxForm() will only process ajax insertions which
  * don't have a 'selector' attribute, because it's not easy to convert from a
  * jQuery selector to an XPath.  However, Ubercart uses many simple, id-based
  * selectors, and these can be converted easily
  * (eg: '#my-identifier' => '//*[@id="my-identifier"]').
  *
  * This helper method post-processes the command array returned by
  * drupalPostAjaxForm() to perform these insertions.
  *
  * @see WebTestBase::drupalPostAjaxForm()
  */
 protected function ucPostAjax($path, $edit, $triggering_element, $ajax_path = NULL, array $options = [], array $headers = [], $form_html_id = NULL, $ajax_settings = NULL)
 {
     $commands = parent::drupalPostAjaxForm($path, $edit, $triggering_element, $ajax_path, $options, $headers, $form_html_id, $ajax_settings);
     $dom = new \DOMDocument();
     @$dom->loadHTML($this->getRawContent());
     foreach ($commands as $command) {
         if ($command['command'] == 'insert' && isset($command['selector']) && preg_match('/^\\#-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/', $command['selector'])) {
             $xpath = new \DOMXPath($dom);
             $wrapperNode = $xpath->query('//*[@id="' . substr($command['selector'], 1) . '"]')->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;
                 }
             }
         }
     }
     $content = $dom->saveHTML();
     $this->setRawContent($content);
     $this->verbose('Page content after ajax submission:<hr />' . $this->content);
     return $commands;
 }