Функциональный тест и sfWidgetFormChoice с несколькими значениями true - PullRequest
0 голосов
/ 04 ноября 2011

Я хочу протестировать пользователя, редактирующего форму, и в этой форме есть <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 голосов
/ 04 ноября 2011

Что ж, после копания в процессе click () я не могу получить ожидаемый результат.

Решения следуют за проблемой:

в теге <select multiple>, я ожидаю, что если я не буду публиковать какие-либо значения, он будет публиковать его как есть, а если я укажу значения, он будет публиковать только те и не будет пытаться контролировать вкл / выкл по значениям.

То, как он реагирует сейчас, заключается в том, что начальные значения и выбранные значения объединяются вместе, и ключи начальных значений, соответствующие ключам выбранных значений, заменяются, что не имеет смысла для меня, потому что эти ключи или их порядок не имеют значения. Меня интересуют только выбранные значения параметров.

Ответ на мой вопрос таков: Вы не можете делать то, что ожидаете с текущей реализацией.

Решение: Переопределите (или отправьте патч, если у вас есть время) функцию doClickElement в классе sfBrowserBase, которая может выглядеть следующим образом: (ищите 3 комментария // исправление для выбора нескольких и последних 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 /3095100/ustanovit-mnogomernyi-massiv-po-klychevomu-puti-iz-znachenii-massiva#3095135
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' => array ('region_list' => array (8,9)) у меня работает.

0 голосов
/ 04 ноября 2011

Я никогда не ставил 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()
;
...