功能测试和sfWidgetFormChoice有多个true

时间:2011-11-04 05:20:18

标签: symfony1 symfony-1.4 symfony-forms

我想测试用户编辑表单,并且在该表单中有<select multiple="multiple">。如何在功能测试中从该表单窗口小部件中选择或取消选择<option>值?

表单设置:

'regions_list' => new sfWidgetFormDoctrineChoice(array(
  'multiple' => true, 
  'model' => 'Region'
)),

功能测试:

$browser->
  getRoute('profile')->

  //setField('profile[regions_list]', '[9][8]')->  // tried syntaxmany combinations
  click('Save Profile', array(
    'profile' => array(
      // 'regions_list' => ???,
      // I've tried many combinations even with setField and none made sense and where testable so far
    )
  ))->

  with('form')->begin()->
    hasErrors(0)->
    hasGlobalError(0)->
  end()
;

2 个答案:

答案 0 :(得分:0)

我从来没有将with('form')放在click()附近,这对我的测试有效:

$browser->
  getRoute('profile')->

  click('Save Profile', array(
    'profile' => array('regions_list' => array(8,9)
        // with: <option value="8">something</option><option value="9">else</option>
        // ... and other fields
    )
  ))->
  end()->

  with('form')->begin()->
    debug() -> // added this line so you can see what is happening
    hasErrors(0)->
    hasGlobalError(0)->
  end()
;

答案 1 :(得分:0)

好吧,在挖掘click()过程之后,我无法得到我期望的结果。

解决方案遵循以下问题:

<select multiple>标记中,我希望如果我不发布任何值,它会按原样重新发布,如果我指定值,它将只发布那些而不是尝试控制/关闭值。

现在它的反应方式是将初始值和所选值合并在一起,并且替换匹配所选值键的初始值键,这对我来说没有意义,因为这些键或它们的顺序是无关紧要的。我只关心所选的选项值。

我的问题的答案是:你不能按照当前的实施做你期望的事。

解决方案: 覆盖(或提交补丁,如果你有时间)sfBrowserBase类中的doClickElement函数可能如下所示:(查找3条注释//修复select select和最后2种方法添加)

public function doClickElement(DOMElement $item, $arguments = array(), $options = array())
{
  $method = strtolower(isset($options['method']) ? $options['method'] : 'get');

  if ('a' == $item->nodeName)
  {
    if (in_array($method, array('post', 'put', 'delete')))
    {
      if (isset($options['_with_csrf']) && $options['_with_csrf'])
      {
        $arguments['_with_csrf'] = true;
      }

      return array($item->getAttribute('href'), $method, $arguments);
    }
    else
    {
      return array($item->getAttribute('href'), 'get', $arguments);
    }
  }
  else if ('button' == $item->nodeName || ('input' == $item->nodeName && in_array($item->getAttribute('type'), array('submit', 'button', 'image'))))
  {
    // add the item's value to the arguments
    $this->parseArgumentAsArray($item->getAttribute('name'), $item->getAttribute('value'), $arguments);

    // use the ancestor form element
    do
    {
      if (null === $item = $item->parentNode)
      {
        throw new Exception('The clicked form element does not have a form ancestor.');
      }
    }
    while ('form' != $item->nodeName);
  }

  // form attributes
  $url = $item->getAttribute('action');
  if (!$url || '#' == $url)
  {
    $url = $this->stack[$this->stackPosition]['uri'];
  }
  $method = strtolower(isset($options['method']) ? $options['method'] : ($item->getAttribute('method') ? $item->getAttribute('method') : 'get'));

  // merge form default values and arguments
  $defaults = array();
  $arguments = sfToolkit::arrayDeepMerge($this->fields, $arguments);

  // fix for select multiple
  $select_multiple_to_check = array();

  $xpath = $this->getResponseDomXpath();
  foreach ($xpath->query('descendant::input | descendant::textarea | descendant::select', $item) as $element)
  {
    if ($element->hasAttribute('disabled'))
    {
      continue;
    }

    $elementName = $element->getAttribute('name');
    $nodeName    = $element->nodeName;
    $value       = null;

    if ($nodeName == 'input' && ($element->getAttribute('type') == 'checkbox' || $element->getAttribute('type') == 'radio'))
    {
      // fix for select multiple
      if (substr($elementName, -2) == '[]') 
      {
        $select_multiple_to_check[$elementName] = true;
      }

      if ($element->getAttribute('checked'))
      {
        $value = $element->hasAttribute('value') ? $element->getAttribute('value') : '1';
      }
    }
    else if ($nodeName == 'input' && $element->getAttribute('type') == 'file')
    {
      $filename = array_key_exists($elementName, $arguments) ? $arguments[$elementName] : sfToolkit::getArrayValueForPath($arguments, $elementName, '');

      if (is_readable($filename))
      {
        $fileError = UPLOAD_ERR_OK;
        $fileSize = filesize($filename);
      }
      else
      {
        $fileError = UPLOAD_ERR_NO_FILE;
        $fileSize = 0;
      }

      unset($arguments[$elementName]);

      $this->parseArgumentAsArray($elementName, array('name' => basename($filename), 'type' => '', 'tmp_name' => $filename, 'error' => $fileError, 'size' => $fileSize), $this->files);
    }
    else if ('input' == $nodeName && !in_array($element->getAttribute('type'), array('submit', 'button', 'image')))
    {
      $value = $element->getAttribute('value');
    }
    else if ($nodeName == 'textarea')
    {
      $value = '';
      foreach ($element->childNodes as $el)
      {
        $value .= $this->getResponseDom()->saveXML($el);
      }
    }
    else if ($nodeName == 'select')
    {
      if ($multiple = $element->hasAttribute('multiple'))
      {
        // fix for select multiple
        $select_multiple_to_check[$elementName] = true;

        $elementName = str_replace('[]', '', $elementName);
        $value = array();
      }
      else
      {
        $value = null;
      }

      $found = false;
      foreach ($xpath->query('descendant::option', $element) as $option)
      {
        if ($option->getAttribute('selected'))
        {
          $found = true;
          if ($multiple)
          {
            $value[] = $option->getAttribute('value');
          }
          else
          {
            $value = $option->getAttribute('value');
          }
        }
      }
      $option = $xpath->query('descendant::option', $element)->item(0);
      if (!$found && !$multiple && $option instanceof DOMElement)
      {
        $value = $option->getAttribute('value');
      }


    }

    if (null !== $value)
    {
      $this->parseArgumentAsArray($elementName, $value, $defaults);
    }
  }

  // fix for select multiple
  foreach($select_multiple_to_check as $elementName => $uselessbool)
  {
    $path = array_filter(preg_split('/(\[ | \[\] | \])/x', $elementName), create_function('$s', 'return $s !== "";'));
    if ($this->findInArrayByArrayPath($arguments, $path) !== false)
    {
      $this->unsetInArrayByArrayPath($defaults, $path);
    }
  }

  $arguments = sfToolkit::arrayDeepMerge($defaults, $arguments);

  if (in_array($method, array('post', 'put', 'delete')))
  {
    return array($url, $method, $arguments);
  }
  else
  {
    $queryString = http_build_query($arguments, null, '&');
    $sep = false === strpos($url, '?') ? '?' : '&';

    return array($url.($queryString ? $sep.$queryString : ''), 'get', array());
  }
}

// fix for select multiple
// taken from http://stackoverflow.com/questions/3145068/set-multi-dimensional-array-by-key-path-from-array-values/3145199#3145199
public function findInArrayByArrayPath(&$array, &$path, $_i=0) {
  // sanity check
  if ( !(is_array($array) && is_array($path)) ) return false;
  $c = count($path); if ($_i >= $c) return false;

  if ($_i==0) {$path = array_values($path);} // to make sure we don't get skipped numeric keys which does happens in the preg_split above

  $k = $path[$_i];
  if (array_key_exists($k, $array))
    return ($_i == $c-1) ? $array[$k] : $this->findInArrayByArrayPath($array[$k], $path, $_i+1);
  else
    return false;
}

// fix for select multiple
public function unsetInArrayByArrayPath(&$array, &$path, $_i=0) {
  // sanity check
  if ( !(is_array($array) && is_array($path)) ) return false;
  $c = count($path); if ($_i >= $c) return false;

  if ($_i==0) {$path = array_values($path);} // to make sure we don't get skipped numeric keys which does happens in the preg_split above

  $k = $path[$_i];
  if (array_key_exists($k, $array))
    if ($_i == $c-1) {
      unset($array[$k]);
      return true;
    } else {
      return $this->unsetInArrayByArrayPath($array[$k], $path, $_i+1);
    }
  else
    return false;
}

现在:     'profile'=&gt; array('regions_list'=&gt; array(8,9))适合我。