[DomCrawler] added the component

This commit is contained in:
Fabien Potencier 2010-04-15 14:41:42 +02:00
parent 13759a7fac
commit 797327110b
17 changed files with 3011 additions and 0 deletions

View File

@ -0,0 +1,640 @@
<?php
namespace Symfony\Components\DomCrawler;
use Symfony\Components\CssSelector\Parser as CssParser;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Crawler eases navigation of a list of \DOMNode objects.
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Crawler extends \SplObjectStorage
{
protected $uri;
protected $host;
protected $path;
/**
* Constructor.
*
* @param mixed $node A Node to use as the base for the crawling
* @param string $uri The base URI to use for absolute links or form actions
*/
public function __construct($node = null, $uri = null)
{
$this->uri = $uri;
list($this->host, $this->path) = $this->parseUri($this->uri);
$this->add($node);
}
/**
* Removes all the nodes.
*/
public function clear()
{
$this->removeAll($this);
}
/**
* Adds a node to the current list of nodes.
*
* This method uses the appropriate specialized add*() method based
* on the type of the argument.
*
* @param null|\DOMNodeList|array|\DOMNode $node A node
*/
public function add($node)
{
if ($node instanceof \DOMNodeList)
{
$this->addNodeList($node);
}
elseif (is_array($node))
{
$this->addNodes($node);
}
elseif (null !== $node)
{
$this->addNode($node);
}
}
/**
* Adds an HTML content to the list of nodes.
*
* @param string $content The HTML content
* @param string $charset The charset
*/
public function addHtmlContent($content, $charset = 'UTF-8')
{
$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;
@$dom->loadHTML($content);
$this->addDocument($dom);
}
/**
* Adds an XML content to the list of nodes.
*
* @param string $content The XML content
* @param string $charset The charset
*/
public function addXmlContent($content, $charset = 'UTF-8')
{
$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;
// remove the default namespace to make XPath expressions simpler
@$dom->loadXML(str_replace('xmlns', 'ns', $content));
$this->addDocument($dom);
}
/**
* Adds a \DOMDocument to the list of nodes.
*
* @param \DOMDocument $dom A \DOMDocument instance
*/
public function addDocument(\DOMDocument $dom)
{
if ($dom->documentElement)
{
$this->addNode($dom->documentElement);
}
}
/**
* Adds a \DOMNodeList to the list of nodes.
*
* @param \DOMNodeList $nodes A \DOMNodeList instance
*/
public function addNodeList(\DOMNodeList $nodes)
{
foreach ($nodes as $node)
{
$this->addNode($node);
}
}
/**
* Adds an array of \DOMNode instances to the list of nodes.
*
* @param array $nodes An array of \DOMNode instances
*/
public function addNodes(array $nodes)
{
foreach ($nodes as $node)
{
$this->add($node);
}
}
/**
* Adds a \DOMNode instance to the list of nodes.
*
* @param \DOMNode $node A \DOMNode instance
*/
public function addNode(\DOMNode $node)
{
if ($node instanceof \DOMDocument)
{
$this->attach($node->documentElement);
}
else
{
$this->attach($node);
}
}
/**
* Returns true if the list of nodes is empty.
*
* @return Boolean true if the list of nodes is empty, false otherwise
*/
public function isEmpty()
{
return $this->count() < 1;
}
/**
* Returns a node given its position in the node list.
*
* @param integer $position The position
*
* @return A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist.
*/
public function eq($position)
{
foreach ($this as $i => $node)
{
if ($i == $position)
{
return new static($node, $this->uri);
}
}
return new static(null, $this->uri);
}
/**
* Calls an anonymous function on each node of the list.
*
* The anonymous function receives the position and the node as arguments.
*
* Example:
*
* $crawler->filter('h1')->each(function ($i, $node)
* {
* return $node->nodeValue;
* });
*
* @param \Closure $closure An anonymous function
*
* @return array An array of values returned by the anonymous function
*/
public function each(\Closure $closure)
{
$data = array();
foreach ($this as $i => $node)
{
$data[] = $closure($node, $i);
}
return $data;
}
/**
* Reduces the list of nodes by calling an anonymous function.
*
* To remove a node from the list, the anonymous function must return false.
*
* @param \Closure $closure An anonymous function
*
* @param Crawler A Crawler instance with the selected nodes.
*/
public function reduce(\Closure $closure)
{
$nodes = array();
foreach ($this as $i => $node)
{
if (false !== $closure($node, $i))
{
$nodes[] = $node;
}
}
return new static($nodes, $this->uri);
}
/**
* Returns the first node of the current selection
*
* @return Crawler A Crawler instance with the first selected node
*/
public function first()
{
return $this->eq(0);
}
/**
* Returns the last node of the current selection
*
* @return Crawler A Crawler instance with the last selected node
*/
public function last()
{
return $this->eq($this->count() - 1);
}
/**
* Returns the siblings nodes of the current selection
*
* @return Crawler A Crawler instance with the sibling nodes
*/
public function siblings()
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
return new static($this->sibling($this->getNode(0)->parentNode->firstChild), $this->uri);
}
/**
* Returns the next siblings nodes of the current selection
*
* @return Crawler A Crawler instance with the next sibling nodes
*/
public function nextAll()
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
return new static($this->sibling($this->getNode(0)), $this->uri);
}
/**
* Returns the previous sibling nodes of the current selection
*
* @return Crawler A Crawler instance with the previous sibling nodes
*/
public function previousAll()
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
return new static($this->sibling($this->getNode(0), 'previousSibling'), $this->uri);
}
/**
* Returns the parents nodes of the current selection
*
* @return Crawler A Crawler instance with the parents nodes of the current selection
*/
public function parents()
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
$node = $this->getNode(0);
$nodes = array();
while ($node = $node->parentNode)
{
if (1 === $node->nodeType && '_root' !== $node->nodeName)
{
$nodes[] = $node;
}
}
return new static($nodes, $this->uri);
}
/**
* Returns the children nodes of the current selection
*
* @return Crawler A Crawler instance with the chidren nodes
*/
public function children()
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
return new static($this->sibling($this->getNode(0)->firstChild), $this->uri);
}
/**
* Returns the attribute value of the first node of the list.
*
* @param string $attribute The attribute name
*
* @return string The attribute value
*/
public function attr($attribute)
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
return $this->getNode(0)->getAttribute($attribute);
}
/**
* Returns the node value of the first node of the list.
*
* @return string The node value
*/
public function text()
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
return $this->getNode(0)->nodeValue;
}
/**
* Extracts information from the list of nodes.
*
* You can extract attributes or/and the node value (_text).
*
* Example:
*
* $crawler->filter('h1 a')->extract(array('_text', 'href'));
*
* @param array $attributes An array of attributes
*
* @param array An array of extracted values
*/
public function extract($attributes)
{
if (!is_array($attributes))
{
$attributes = array($attributes);
}
$data = array();
foreach ($this as $node)
{
$elements = array();
foreach ($attributes as $attribute)
{
if ('_text' === $attribute)
{
$elements[] = $node->nodeValue;
}
else
{
$elements[] = $node->getAttribute($attribute);
}
}
$data[] = count($attributes) > 1 ? $elements : $elements[0];
}
return $data;
}
/**
* Filters the list of nodes with an XPath expression.
*
* @param string $xpath An XPath expression
*
* @return Crawler A new instance of Crawler with the filtered list of nodes
*/
public function filterXPath($xpath)
{
$document = new \DOMDocument('1.0', 'UTF-8');
$root = $document->appendChild($document->createElement('_root'));
foreach ($this as $node)
{
$root->appendChild($document->importNode($node, true));
}
$domxpath = new \DOMXPath($document);
return new static($domxpath->query($xpath), $this->uri);
}
/**
* Filters the list of nodes with a CSS selector.
*
* This method only works if you have installed the CssSelector Symfony Component.
*
* @param string $selector A CSS selector
*
* @return Crawler A new instance of Crawler with the filtered list of nodes
*
* @throws \RuntimeException if the CssSelector Component is not available
*/
public function filter($selector)
{
if (!class_exists('Symfony\\Components\\CssSelector\\Parser'))
{
// @codeCoverageIgnoreStart
throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector is not installed (you can use filterXPath instead).');
// @codeCoverageIgnoreEnd
}
return $this->filterXPath(CssParser::cssToXpath($selector));
}
/**
* Selects links by name or alt value for clickable images.
*
* @param string $value The link text
*
* @return Crawler A new instance of Crawler with the filtered list of nodes
*/
public function selectLink($value)
{
$xpath = sprintf('//a[contains(concat(\' \', normalize-space(string(.)), \' \'), %s)] ', static::xpathLiteral(' '.$value.' ')).
sprintf('| //a/img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)]/ancestor::a', static::xpathLiteral(' '.$value.' '));
return $this->filterXPath($xpath);
}
/**
* Selects a button by name or alt value for images.
*
* @param string $value The button text
*
* @return Crawler A new instance of Crawler with the filtered list of nodes
*/
public function selectButton($value)
{
$xpath = sprintf('//input[((@type="submit" or @type="button") and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', static::xpathLiteral(' '.$value.' ')).
sprintf('or (@type="image" and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id="%s" or @name="%s"] ', static::xpathLiteral(' '.$value.' '), $value, $value).
sprintf('| //button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id="%s" or @name="%s"]', static::xpathLiteral(' '.$value.' '), $value, $value);
return $this->filterXPath($xpath);
}
/**
* Returns a Link object for the first node in the list.
*
* @param string $method The method for the link (get by default)
*
* @return Link A Link instance
*
* @throws \InvalidArgumentException If the current node list is empty
*/
public function link($method = 'get')
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
$node = $this->getNode(0);
return new Link($node, $method, $this->host, $this->path);
}
/**
* Returns an array of Link objects for the nodes in the list.
*
* @return array An array of Link instances
*/
public function links()
{
$links = array();
foreach ($this as $node)
{
$links[] = new Link($node, 'get', $this->host, $this->path);
}
return $links;
}
/**
* Returns a Form object for the first node in the list.
*
* @param array $arguments An array of values for the form fields
* @param string $method The method for the form
*
* @return Form A Form instance
*
* @throws \InvalidArgumentException If the current node list is empty
*/
public function form(array $values = null, $method = null)
{
if (!count($this))
{
throw new \InvalidArgumentException('The current node list is empty.');
}
$form = new Form($this->getNode(0), $method, $this->host, $this->path);
if (null !== $values)
{
$form->setValues($values);
}
return $form;
}
protected function getNode($position)
{
foreach ($this as $i => $node)
{
if ($i == $position)
{
return $node;
}
// @codeCoverageIgnoreStart
}
return null;
// @codeCoverageIgnoreEnd
}
protected function parseUri($uri)
{
if ('http' !== substr($uri, 0, 4))
{
return array(null, '/');
}
$path = parse_url($uri, PHP_URL_PATH);
if ('/' !== substr($path, -1))
{
$path = dirname($path);
}
return array(preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $uri), $path);
}
protected function sibling($node, $siblingDir = 'nextSibling')
{
$nodes = array();
do
{
if ($node !== $this->getNode(0) && $node->nodeType === 1)
{
$nodes[] = $node;
}
}
while($node = $node->$siblingDir);
return $nodes;
}
static public function xpathLiteral($s)
{
if (false === strpos($s, "'"))
{
return sprintf("'%s'", $s);
}
if (false === strpos($s, '"'))
{
return sprintf('"%s"', $s);
}
$string = $s;
$parts = array();
while (true)
{
if (false !== $pos = strpos($string, "'"))
{
$parts[] = sprintf("'%s'", substr($string, 0, $pos));
$parts[] = "\"'\"";
$string = substr($string, $pos + 1);
}
else
{
$parts[] = "'$string'";
break;
}
}
return sprintf("concat(%s)", implode($parts, ', '));
}
}

View File

@ -0,0 +1,208 @@
<?php
namespace Symfony\Components\DomCrawler\Field;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* ChoiceFormField represents a choice form field.
*
* It is constructed from a HTML select tag, or a HTML checkbox, or radio inputs.
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ChoiceFormField extends FormField
{
protected $type;
protected $multiple;
protected $options;
/**
* Returns true if the field should be included in the submitted values.
*
* @return Boolean true if the field should be included in the submitted values, false otherwise
*/
public function hasValue()
{
// don't send a value for unchecked checkboxes
if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value)
{
return false;
}
return true;
}
/**
* Sets the value of the field.
*
* @param string $value The value of the field
*/
public function setValue($value)
{
if ('checkbox' == $this->type && false === $value)
{
// uncheck
$this->value = null;
}
elseif ('checkbox' == $this->type && true === $value)
{
// check
$this->value = $this->options[0];
}
else
{
if (is_array($value))
{
if (!$this->multiple)
{
throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
}
foreach ($value as $v)
{
if (!in_array($v, $this->options))
{
throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->options)));
}
}
}
elseif (!in_array($value, $this->options))
{
throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->options)));
}
if ($this->multiple && !is_array($value))
{
$value = array($value);
}
if (is_array($value))
{
$this->value = $value;
}
else
{
parent::setValue($value);
}
}
}
/**
* Adds a choice to the current ones.
*
* This method should only be used internally.
*
* @param \DOMNode $node A \DOMNode
*/
public function addChoice(\DOMNode $node)
{
if (!$this->multiple && 'radio' != $this->type)
{
throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
}
$this->options[] = $value = $node->hasAttribute('value') ? $node->getAttribute('value') : '1';
if ($node->getAttribute('checked'))
{
$this->value = $value;
}
}
/**
* Returns the type of the choice field (radio, select, or checkbox).
*
* @return string The type
*/
public function getType()
{
return $this->type;
}
/**
* Returns true if the field accepts multiple values.
*
* @return Boolean true if the field accepts multiple values, false otherwise
*/
public function isMultiple()
{
return $this->multiple;
}
/**
* Initializes the form field.
*/
protected function initialize()
{
if ('input' != $this->node->nodeName && 'select' != $this->node->nodeName)
{
throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName));
}
if ('input' == $this->node->nodeName && 'checkbox' != $this->node->getAttribute('type') && 'radio' != $this->node->getAttribute('type'))
{
throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type')));
}
$this->value = null;
$this->options = array();
$this->multiple = false;
if ('input' == $this->node->nodeName)
{
$this->type = $this->node->getAttribute('type');
$this->options[] = $value = $this->node->hasAttribute('value') ? $this->node->getAttribute('value') : '1';
if ($this->node->getAttribute('checked'))
{
$this->value = $value;
}
}
else
{
$this->type = 'select';
if ($this->node->hasAttribute('multiple'))
{
$this->multiple = true;
$this->value = array();
$this->name = str_replace('[]', '', $this->name);
}
$found = false;
foreach ($this->xpath->query('descendant::option', $this->node) as $option)
{
$this->options[] = $option->getAttribute('value');
if ($option->getAttribute('selected'))
{
$found = true;
if ($this->multiple)
{
$this->value[] = $option->getAttribute('value');
}
else
{
$this->value = $option->getAttribute('value');
}
}
}
// if no option is selected and if it is a simple select box, take the first option as the value
$option = $this->xpath->query('descendant::option', $this->node)->item(0);
if (!$found && !$this->multiple && $option instanceof \DOMElement)
{
$this->value = $option->getAttribute('value');
}
}
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace Symfony\Components\DomCrawler\Field;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* FileFormField represents a file form field (an HTML file input tag).
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FileFormField extends FormField
{
/**
* Sets the PHP error code associated with the field.
*
* @param integer $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION)
*/
public function setErrorCode($error)
{
$codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION);
if (!in_array($error, $codes))
{
throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error));
}
$this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0);
}
/**
* Sets the value of the field.
*
* @param string $value The value of the field
*/
public function setValue($value)
{
if (null !== $value && is_readable($value))
{
$error = UPLOAD_ERR_OK;
$size = filesize($value);
}
else
{
$error = UPLOAD_ERR_NO_FILE;
$size = 0;
$value = '';
}
$this->value = array('name' => basename($value), 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size);
}
/**
* Initializes the form field.
*/
protected function initialize()
{
if ('input' != $this->node->nodeName)
{
throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName));
}
if ('file' != $this->node->getAttribute('type'))
{
throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type')));
}
$this->setValue(null);
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace Symfony\Components\DomCrawler\Field;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* FormField is the abstract class for all form fields.
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class FormField
{
protected $node;
protected $name;
protected $value;
protected $document;
protected $xpath;
/**
* Constructor.
*
* @param \DOMNode $node The node associated with this field
*/
public function __construct(\DOMNode $node)
{
$this->node = $node;
$this->name = $node->getAttribute('name');
$this->document = new \DOMDocument('1.0', 'UTF-8');
$this->node = $this->document->importNode($this->node, true);
$root = $this->document->appendChild($this->document->createElement('_root'));
$root->appendChild($this->node);
$this->xpath = new \DOMXPath($this->document);
$this->initialize();
}
/**
* Returns the name of the field.
*
* @return string The name of the field
*/
public function getName()
{
return $this->name;
}
/**
* Gets the value of the field.
*
* @return string|array The value of the field
*/
public function getValue()
{
return $this->value;
}
/**
* Sets the value of the field.
*
* @param string $value The value of the field
*/
public function setValue($value)
{
$this->value = (string) $value;
}
/**
* Returns true if the field should be included in the submitted values.
*
* @return Boolean true if the field should be included in the submitted values, false otherwise
*/
public function hasValue()
{
return true;
}
/**
* Initializes the form field.
*/
abstract protected function initialize();
}

View File

@ -0,0 +1,48 @@
<?php
namespace Symfony\Components\DomCrawler\Field;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* InputFormField represents an input form field (an HTML input tag).
*
* For inputs with type of file, checkbox, or radio, there are other more
* specialized classes (cf. FileFormField and ChoiceFormField).
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class InputFormField extends FormField
{
/**
* Initializes the form field.
*/
protected function initialize()
{
if ('input' != $this->node->nodeName)
{
throw new \LogicException(sprintf('An InputFormField can only be created from an input tag (%s given).', $this->node->nodeName));
}
if ('checkbox' == $this->node->getAttribute('type'))
{
throw new \LogicException('Checkboxes should be instances of ChoiceFormField.');
}
if ('file' == $this->node->getAttribute('type'))
{
throw new \LogicException('File inputs should be instances of FileFormField.');
}
$this->value = $this->node->getAttribute('value');
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Symfony\Components\DomCrawler\Field;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* TextareaFormField represents a textarea form field (an HTML textarea tag).
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TextareaFormField extends FormField
{
/**
* Initializes the form field.
*/
protected function initialize()
{
if ('textarea' != $this->node->nodeName)
{
throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName));
}
$this->value = null;
foreach ($this->node->childNodes as $node)
{
$this->value .= $this->document->saveXML($node);
}
}
}

View File

@ -0,0 +1,363 @@
<?php
namespace Symfony\Components\DomCrawler;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Form represents an HTML form.
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Form
{
protected $document;
protected $button;
protected $node;
protected $fields;
protected $method;
protected $host;
protected $path;
/**
* Constructor.
*
* @param \DOMNode $node A \DOMNode instance
* @param string $method The method to use for the link (if null, it defaults to the method defined by the form)
* @param string $host The base URI to use for absolute links (like http://localhost)
* @param string $path The base path for relative links (/ by default)
*
* @throws \LogicException if the node is not a button inside a form tag
*/
public function __construct(\DOMNode $node, $method = null, $host = null, $path = '/')
{
$this->button = $node;
if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image'))))
{
do
{
// use the ancestor form element
if (null === $node = $node->parentNode)
{
throw new \LogicException('The selected node does not have a form ancestor.');
}
}
while ('form' != $node->nodeName);
}
else
{
throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
}
$this->node = $node;
$this->method = $method;
$this->host = $host;
$this->path = empty($path) ? '/' : $path;
$this->initialize();
}
/**
* Gets the form node associated with this form.
*
* @return \DOMNode $node A \DOMNode instance
*/
public function getFormNode()
{
return $this->node;
}
/**
* Gets the value of a field.
*
* @param string $name The field name
*
* @throws \InvalidArgumentException if the field does not exist
*/
public function getValue($name)
{
if (!$this->hasField($name))
{
throw new \InvalidArgumentException(sprintf('The form field "%s" does not exist', $name));
}
return $this->fields[$name]->getValue();
}
/**
* Sets the value of a field.
*
* @param string $name The field name
* @param string|array $value The value of the field
*
* @throws \InvalidArgumentException if the field does not exist
*/
public function setValue($name, $value)
{
if (!$this->hasField($name))
{
throw new \InvalidArgumentException(sprintf('The form field "%s" does not exist', $name));
}
$this->fields[$name]->setValue($value);
return $this;
}
/**
* Sets the value of the fields.
*
* @param array $values An array of field values
*/
public function setValues(array $values)
{
foreach ($values as $name => $value)
{
$this->setValue($name, $value);
}
return $this;
}
/**
* Gets the field values.
*
* The returned array does not include file fields (@see getFiles).
*
* @return array An array of field values.
*/
public function getValues()
{
$values = array();
foreach ($this->fields as $name => $field)
{
if (!$field instanceof Field\FileFormField && $field->hasValue())
{
$values[$name] = $field->getValue();
}
}
return $values;
}
/**
* Gets the file field values.
*
* @return array An array of file field values.
*/
public function getFiles()
{
if (!in_array($this->getMethod(), array('post', 'put', 'delete')))
{
return array();
}
$files = array();
foreach ($this->fields as $name => $field)
{
if ($field instanceof Field\FileFormField)
{
$files[$name] = $field->getValue();
}
}
return $files;
}
/**
* Gets the field values as PHP.
*
* This method converts fields with th array notation
* (like foo[bar] to arrays) like PHP does.
*
* @return array An array of field values.
*/
public function getPhpValues()
{
$qs = http_build_query($this->getValues());
parse_str($qs, $values);
return $values;
}
/**
* Gets the file field values as PHP.
*
* This method converts fields with th array notation
* (like foo[bar] to arrays) like PHP does.
*
* @return array An array of field values.
*/
public function getPhpFiles()
{
$qs = http_build_query($this->getFiles());
parse_str($qs, $values);
return $values;
}
/**
* Gets the URI of the form.
*
* The returned URI is not the same as the form "action" attribute.
* This method merges the value if the method is GET to mimics
* browser behavior.
*
* @param Boolean $absolute Wheter to return an absolute URI or not (this only works if a base URI has been provided)
*
* @return string The URI
*/
public function getUri($absolute = true)
{
$uri = $this->node->getAttribute('action');
if (!in_array($this->getMethod(), array('post', 'put', 'delete')) && $queryString = http_build_query($this->getValues(), null, '&'))
{
$sep = false === strpos($uri, '?') ? '?' : '&';
$uri .= $sep.$queryString;
}
if ($uri && '/' !== $uri[0])
{
$uri = $this->path.$uri;
}
if ($absolute && null !== $this->host)
{
return $this->host.$uri;
}
return $uri;
}
/**
* Gets the form method.
*
* If no method is defined in the form, GET is returned.
*
* @return string The method
*/
public function getMethod()
{
if (null !== $this->method)
{
return $this->method;
}
return $this->node->getAttribute('method') ? strtolower($this->node->getAttribute('method')) : 'get';
}
/**
* Returns true if the named field exists.
*
* @param string $name The field name
*
* @param Boolean true if the field exists, false otherwise
*/
public function hasField($name)
{
return isset($this->fields[$name]);
}
/**
* Gets a named field.
*
* @param string $name The field name
*
* @return Field\FormField The field instance
*/
public function getField($name)
{
if (!$this->hasField($name))
{
throw new \InvalidArgumentException(sprintf('The form has no "%s" field', $name));
}
return $this->fields[$name];
}
/**
* Sets a named field.
*
* @param string $name The field name
*
* @return Field\FormField The field instance
*/
public function setField(Field\FormField $field)
{
$this->fields[$field->getName()] = $field;
}
/**
* Gets all fields.
*
* @return array An array of fields
*/
public function getFields()
{
return $this->fields;
}
protected function initialize()
{
$this->fields = array();
$document = new \DOMDocument('1.0', 'UTF-8');
$node = $document->importNode($this->node, true);
$button = $document->importNode($this->button, true);
$root = $document->appendChild($document->createElement('_root'));
$root->appendChild($node);
$root->appendChild($button);
$xpath = new \DOMXPath($document);
foreach ($xpath->query('descendant::input | descendant::textarea | descendant::select', $root) as $node)
{
if ($node->hasAttribute('disabled') || !$node->hasAttribute('name'))
{
continue;
}
$nodeName = $node->nodeName;
if ($node === $button)
{
$this->setField(new Field\InputFormField($node));
}
elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type'))
{
$this->setField(new Field\ChoiceFormField($node));
}
elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type'))
{
if ($this->hasField($node->getAttribute('name')))
{
$this->getField($node->getAttribute('name'))->addChoice($node);
}
else
{
$this->setField(new Field\ChoiceFormField($node));
}
}
elseif ('input' == $nodeName && 'file' == $node->getAttribute('type'))
{
$this->setField(new Field\FileFormField($node));
}
elseif ('input' == $nodeName && !in_array($node->getAttribute('type'), array('submit', 'button', 'image')))
{
$this->setField(new Field\InputFormField($node));
}
elseif ('textarea' == $nodeName)
{
$this->setField(new Field\TextareaFormField($node));
}
}
}
}

View File

@ -0,0 +1,94 @@
<?php
namespace Symfony\Components\DomCrawler;
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Link represents an HTML link (an HTML a tag).
*
* @package Symfony
* @subpackage Components_DomCrawler
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Link
{
protected $node;
protected $method;
protected $host;
protected $path;
/**
* Constructor.
*
* @param \DOMNode $node A \DOMNode instance
* @param string $method The method to use for the link (get by default)
* @param string $host The base URI to use for absolute links (like http://localhost)
* @param string $path The base path for relative links (/ by default)
*
* @throws \LogicException if the node is not a link
*/
public function __construct(\DOMNode $node, $method = 'get', $host = null, $path = '/')
{
if ('a' != $node->nodeName)
{
throw new \LogicException(sprintf('Unable to click on a "%s" tag.', $node->nodeName));
}
$this->node = $node;
$this->method = $method;
$this->host = $host;
$this->path = empty($path) ? '/' : $path;
}
/**
* Gets the node associated with this link.
*
* @return \DOMNode A \DOMNode instance
*/
public function getNode()
{
return $this->node;
}
/**
* Gets the URI associated with this link.
*
* @param Boolean $absolute Wheter to return an absolute URI or not (this only works if a base URI has been provided)
*
* @return string The URI
*/
public function getUri($absolute = true)
{
$uri = $this->node->getAttribute('href');
if ($uri && '/' !== $uri[0])
{
$uri = $this->path.$uri;
}
if ($absolute && null !== $this->host)
{
return $this->host.$uri;
}
return $uri;
}
/**
* Gets the method associated with this link.
*
* @return string The method
*/
public function getMethod()
{
return $this->method;
}
}

View File

@ -0,0 +1,524 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler;
use Symfony\Components\DomCrawler\Crawler;
class CrawlerTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$crawler = new Crawler();
$this->assertEquals(0, count($crawler), '__construct() returns an empty crawler');
$crawler = new Crawler(new \DOMNode());
$this->assertEquals(1, count($crawler), '__construct() takes a node as a first argument');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::add
*/
public function testAdd()
{
$crawler = new Crawler();
$crawler->add($this->createDomDocument());
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->add() adds nodes from a \DOMDocument');
$crawler = new Crawler();
$crawler->add($this->createNodeList());
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->add() adds nodes from a \DOMNodeList');
foreach ($this->createNodeList() as $node)
{
$list[] = $node;
}
$crawler = new Crawler();
$crawler->add($list);
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->add() adds nodes from an array of nodes');
$crawler = new Crawler();
$crawler->add($this->createNodeList()->item(0));
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->add() adds nodes from an \DOMNode');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::addHtmlContent
*/
public function testAddHtmlContent()
{
$crawler = new Crawler();
$crawler->addHtmlContent('<html><div class="foo"></html>', 'UTF-8');
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->addHtmlContent() adds nodes from an HTML string');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::addXmlContent
*/
public function testAddXmlContent()
{
$crawler = new Crawler();
$crawler->addXmlContent('<html><div class="foo"></div></html>', 'UTF-8');
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->addXmlContent() adds nodes from an XML string');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::addDocument
*/
public function testAddDocument()
{
$crawler = new Crawler();
$crawler->addDocument($this->createDomDocument());
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->addDocument() adds nodes from a \DOMDocument');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::addNodeList
*/
public function testAddNodeList()
{
$crawler = new Crawler();
$crawler->addNodeList($this->createNodeList());
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->addNodeList() adds nodes from a \DOMNodeList');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::addNodes
*/
public function testAddNodes()
{
foreach ($this->createNodeList() as $node)
{
$list[] = $node;
}
$crawler = new Crawler();
$crawler->addNodes($list);
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->addNodes() adds nodes from an array of nodes');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::addNode
*/
public function testAddNode()
{
$crawler = new Crawler();
$crawler->addNode($this->createNodeList()->item(0));
$this->assertEquals('foo', $crawler->filter('div')->attr('class'), '->addNode() adds nodes from an \DOMNode');
}
public function testClear()
{
$crawler = new Crawler(new \DOMNode());
$crawler->clear();
$this->assertEquals(0, count($crawler), '->clear() removes all the nodes from the crawler');
}
public function testIsEmpty()
{
$crawler = new Crawler(new \DOMNode());
$this->assertFalse($crawler->isEmpty(), '->isEmpty() returns false if the crawler node list is not empty');
$crawler->clear();
$this->assertTrue($crawler->isEmpty(), '->isEmpty() returns true if the crawler node list is empty');
}
public function testEq()
{
$crawler = $this->createTestCrawler()->filter('li');
$this->assertNotSame($crawler, $crawler->eq(0), '->eq() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->eq() returns a new instance of a crawler');
$this->assertEquals('Two', $crawler->eq(1)->text(), '->eq() returns the nth node of the list');
$this->assertTrue($crawler->eq(100)->isEmpty(), '->eq() returns an empty crawler if the nth node does not exist');
}
public function testEach()
{
$data = $this->createTestCrawler()->filter('ul.first li')->each(function ($node, $i)
{
return $i.'-'.$node->nodeValue;
});
$this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list');
}
public function testReduce()
{
$crawler = $this->createTestCrawler()->filter('ul.first li');
$nodes = $crawler->reduce(function ($node, $i)
{
return $i == 1 ? false : true;
});
$this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler');
$this->assertEquals(2, count($nodes), '->reduce() filters the nodes in the list');
}
public function testAttr()
{
$this->assertEquals('first', $this->createTestCrawler()->filter('li')->attr('class'), '->attr() returns the attribute of the first element of the node list');
try
{
$this->createTestCrawler()->filter('ol')->attr('class');
$this->fail('->attr() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->attr() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testText()
{
$this->assertEquals('One', $this->createTestCrawler()->filter('li')->text(), '->text() returns the node value of the first element of the node list');
try
{
$this->createTestCrawler()->filter('ol')->text();
$this->fail('->text() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->text() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testExtract()
{
$crawler = $this->createTestCrawler()->filter('ul.first li');
$this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list');
$this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list');
$this->assertEquals(array(), $this->createTestCrawler()->filter('lo')->extract('_text'), '->extract() returns an empty array if the node list is empty');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::filterXPath
*/
public function testFilterXPath()
{
$crawler = $this->createTestCrawler();
$this->assertNotSame($crawler, $crawler->filterXPath('//li'), '->filterXPath() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->filterXPath() returns a new instance of a crawler');
$crawler = $this->createTestCrawler()->filter('ul');
$this->assertEquals(6, $crawler->filterXPath('//li')->count(), '->filterXPath() filters the node list with the XPath expression');
}
/**
* @covers Symfony\Components\DomCrawler\Crawler::filter
*/
public function testFilter()
{
$crawler = $this->createTestCrawler();
$this->assertNotSame($crawler, $crawler->filter('li'), '->filter() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->filter() returns a new instance of a crawler');
$crawler = $this->createTestCrawler()->filter('ul');
$this->assertEquals(6, $crawler->filter('li')->count(), '->filter() filters the node list with the CSS selector');
}
public function testSelectLink()
{
$crawler = $this->createTestCrawler();
$this->assertNotSame($crawler, $crawler->selectLink('Foo'), '->selectLink() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->selectLink() returns a new instance of a crawler');
$this->assertEquals(1, $crawler->selectLink('Fabien\'s Foo')->count(), '->selectLink() selects links by the node values');
$this->assertEquals(1, $crawler->selectLink('Fabien\'s Bar')->count(), '->selectLink() selects links by the alt attribute of a clickable image');
$this->assertEquals(2, $crawler->selectLink('Fabien"s Foo')->count(), '->selectLink() selects links by the node values');
$this->assertEquals(2, $crawler->selectLink('Fabien"s Bar')->count(), '->selectLink() selects links by the alt attribute of a clickable image');
$this->assertEquals(1, $crawler->selectLink('\' Fabien"s Foo')->count(), '->selectLink() selects links by the node values');
$this->assertEquals(1, $crawler->selectLink('\' Fabien"s Bar')->count(), '->selectLink() selects links by the alt attribute of a clickable image');
$this->assertEquals(4, $crawler->selectLink('Foo')->count(), '->selectLink() selects links by the node values');
$this->assertEquals(4, $crawler->selectLink('Bar')->count(), '->selectLink() selects links by the node values');
}
public function testSelectButton()
{
$crawler = $this->createTestCrawler();
$this->assertNotSame($crawler, $crawler->selectButton('FooValue'), '->selectButton() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->selectButton() returns a new instance of a crawler');
$this->assertEquals(1, $crawler->selectButton('FooValue')->count(), '->selectButton() selects buttons');
$this->assertEquals(1, $crawler->selectButton('FooName')->count(), '->selectButton() selects buttons');
$this->assertEquals(1, $crawler->selectButton('FooId')->count(), '->selectButton() selects buttons');
$this->assertEquals(1, $crawler->selectButton('BarValue')->count(), '->selectButton() selects buttons');
$this->assertEquals(1, $crawler->selectButton('BarName')->count(), '->selectButton() selects buttons');
$this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons');
}
public function testLink()
{
$crawler = $this->createTestCrawler()->selectLink('Foo');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance');
$this->assertEquals('/foo', $crawler->link()->getUri(), '->link() returns a Link instance');
$this->assertEquals('post', $crawler->link('post')->getMethod(), '->link() takes a method as its argument');
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
$this->assertEquals('http://example.com/bar/foo', $crawler->link()->getUri(), '->link() returns a Link instance');
$crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('Foo');
$this->assertEquals('http://example.com/foo', $crawler->link()->getUri(), '->form() linketurns a Link instance');
try
{
$this->createTestCrawler()->filter('ol')->link();
$this->fail('->link() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->link() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testLinks()
{
$crawler = $this->createTestCrawler()->selectLink('Foo');
$this->assertType('array', $crawler->links(), '->links() returns an array');
$this->assertEquals(4, count($crawler->links()), '->links() returns an array');
$links = $crawler->links();
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances');
$this->assertEquals(array(), $this->createTestCrawler()->filter('ol')->links(), '->links() returns an empty array if the node selection is empty');
}
public function testForm()
{
$crawler = $this->createTestCrawler()->selectButton('FooValue');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance');
$this->assertEquals('/foo?FooName=FooValue', $crawler->form()->getUri(), '->form() returns a Form instance');
$this->assertEquals(array('FooName' => 'FooBar'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument');
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectButton('FooValue');
$this->assertEquals('http://example.com/bar/foo?FooName=FooValue', $crawler->form()->getUri(), '->form() returns a Form instance');
$crawler = $this->createTestCrawler('http://example.com/bar')->selectButton('FooValue');
$this->assertEquals('http://example.com/foo?FooName=FooValue', $crawler->form()->getUri(), '->form() returns a Form instance');
try
{
$this->createTestCrawler()->filter('ol')->form();
$this->fail('->form() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->form() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testLast()
{
$crawler = $this->createTestCrawler()->filter('ul.first li');
$this->assertNotSame($crawler, $crawler->last(), '->last() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->last() returns a new instance of a crawler');
$this->assertEquals('Three', $crawler->last()->text());
}
public function testFirst()
{
$crawler = $this->createTestCrawler()->filter('li');
$this->assertNotSame($crawler, $crawler->first(), '->first() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->first() returns a new instance of a crawler');
$this->assertEquals('One', $crawler->first()->text());
}
public function testSiblings()
{
$crawler = $this->createTestCrawler()->filter('li')->eq(1);
$this->assertNotSame($crawler, $crawler->siblings(), '->siblings() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->siblings() returns a new instance of a crawler');
$nodes = $crawler->siblings();
$this->assertEquals(2, $nodes->count());
$this->assertEquals('One', $nodes->eq(0)->text());
$this->assertEquals('Three', $nodes->eq(1)->text());
$nodes = $this->createTestCrawler()->filter('li')->eq(0)->siblings();
$this->assertEquals(2, $nodes->count());
$this->assertEquals('Two', $nodes->eq(0)->text());
$this->assertEquals('Three', $nodes->eq(1)->text());
try
{
$this->createTestCrawler()->filter('ol')->siblings();
$this->fail('->siblings() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->siblings() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testNextAll()
{
$crawler = $this->createTestCrawler()->filter('li')->eq(1);
$this->assertNotSame($crawler, $crawler->nextAll(), '->nextAll() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->nextAll() returns a new instance of a crawler');
$nodes = $crawler->nextAll();
$this->assertEquals(1, $nodes->count());
$this->assertEquals('Three', $nodes->eq(0)->text());
try
{
$this->createTestCrawler()->filter('ol')->nextAll();
$this->fail('->nextAll() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->nextAll() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testPreviousAll()
{
$crawler = $this->createTestCrawler()->filter('li')->eq(2);
$this->assertNotSame($crawler, $crawler->previousAll(), '->previousAll() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->previousAll() returns a new instance of a crawler');
$nodes = $crawler->previousAll();
$this->assertEquals(2, $nodes->count());
$this->assertEquals('Two', $nodes->eq(0)->text());
try
{
$this->createTestCrawler()->filter('ol')->previousAll();
$this->fail('->previousAll() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->previousAll() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testChildren()
{
$crawler = $this->createTestCrawler()->filter('ul');
$this->assertNotSame($crawler, $crawler->children(), '->children() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->children() returns a new instance of a crawler');
$nodes = $crawler->children();
$this->assertEquals(3, $nodes->count());
$this->assertEquals('One', $nodes->eq(0)->text());
$this->assertEquals('Two', $nodes->eq(1)->text());
$this->assertEquals('Three', $nodes->eq(2)->text());
try
{
$this->createTestCrawler()->filter('ol')->children();
$this->fail('->children() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->children() throws an \InvalidArgumentException if the node list is empty');
}
}
public function testParents()
{
$crawler = $this->createTestCrawler()->filter('li:first-child');
$this->assertNotSame($crawler, $crawler->parents(), '->parents() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Components\\DomCrawler\\Crawler', $crawler, '->parents() returns a new instance of a crawler');
$nodes = $crawler->parents();
$this->assertEquals(3, $nodes->count());
$nodes = $this->createTestCrawler()->filter('html')->parents();
$this->assertEquals(0, $nodes->count());
try
{
$this->createTestCrawler()->filter('ol')->parents();
$this->fail('->parents() throws an \InvalidArgumentException if the node list is empty');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->parents() throws an \InvalidArgumentException if the node list is empty');
}
}
public function createTestCrawler($uri = null)
{
$dom = new \DOMDocument();
$dom->loadHTML('
<html>
<body>
<a href="foo">Foo</a>
<a href="/foo"> Fabien\'s Foo </a>
<a href="/foo">Fabien"s Foo</a>
<a href="/foo">\' Fabien"s Foo</a>
<a href="/bar"><img alt="Bar"/></a>
<a href="/bar"><img alt=" Fabien\'s Bar "/></a>
<a href="/bar"><img alt="Fabien&quot;s Bar"/></a>
<a href="/bar"><img alt="\' Fabien&quot;s Bar"/></a>
<form action="foo">
<input type="submit" value="FooValue" name="FooName" id="FooId" />
<input type="button" value="BarValue" name="BarName" id="BarId" />
<button value="ButtonValue" name="ButtonName" id="ButtonId" />
</form>
<ul class="first">
<li class="first">One</li>
<li>Two</li>
<li>Three</li>
</ul>
<ul>
<li>One Bis</li>
<li>Two Bis</li>
<li>Three Bis</li>
</ul>
</body>
</html>
');
return new Crawler($dom, $uri);
}
protected function createDomDocument()
{
$dom = new \DOMDocument();
$dom->loadXML('<html><div class="foo"></div></html>');
return $dom;
}
protected function createNodeList()
{
$dom = new \DOMDocument();
$dom->loadXML('<html><div class="foo"></div></html>');
$domxpath = new \DOMXPath($dom);
return $domxpath->query('//div');
}
}

View File

@ -0,0 +1,262 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler\Field;
require_once __DIR__.'/FormFieldTestCase.php';
use Symfony\Components\DomCrawler\Field\ChoiceFormField;
class ChoiceFormFieldTest extends FormFieldTestCase
{
public function testInitialize()
{
$node = $this->createNode('textarea', '');
try
{
$field = new ChoiceFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not an input or a select');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input or a select');
}
$node = $this->createNode('input', '', array('type' => 'text'));
try
{
$field = new ChoiceFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio');
}
}
public function testGetType()
{
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$this->assertEquals('radio', $field->getType(), '->getType() returns radio for radio buttons');
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$this->assertEquals('checkbox', $field->getType(), '->getType() returns radio for a checkbox');
$node = $this->createNode('select', '');
$field = new ChoiceFormField($node);
$this->assertEquals('select', $field->getType(), '->getType() returns radio for a select');
}
public function testIsMultiple()
{
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$this->assertEquals(false, $field->isMultiple(), '->isMultiple() returns false for radio buttons');
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$this->assertEquals(false, $field->isMultiple(), '->isMultiple() returns false for checkboxes');
$node = $this->createNode('select', '');
$field = new ChoiceFormField($node);
$this->assertEquals(false, $field->isMultiple(), '->isMultiple() returns false for selects without the multiple attribute');
$node = $this->createNode('select', '', array('multiple' => 'multiple'));
$field = new ChoiceFormField($node);
$this->assertEquals(true, $field->isMultiple(), '->isMultiple() returns true for selects with the multiple attribute');
}
public function testSelects()
{
$node = $this->createSelectNode(array('foo' => false, 'bar' => false));
$field = new ChoiceFormField($node);
$this->assertTrue($field->hasValue(), '->hasValue() returns true for selects');
$this->assertEquals('foo', $field->getValue(), '->getValue() returns the first option if none are selected');
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false when no multiple attribute is defined');
$node = $this->createSelectNode(array('foo' => false, 'bar' => true));
$field = new ChoiceFormField($node);
$this->assertEquals('bar', $field->getValue(), '->getValue() returns the selected option');
$field->setValue('foo');
$this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected option');
try
{
$field->setValue('foobar');
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the selected options');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the selected options');
}
try
{
$field->setValue(array('foobar'));
$this->fail('->setValue() throws an \InvalidArgumentException if the value is an array');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is an array');
}
}
public function testMultipleSelects()
{
$node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple'));
$field = new ChoiceFormField($node);
$this->assertEquals(array(), $field->getValue(), '->setValue() returns an empty array if multiple is true and no option is selected');
$field->setValue('foo');
$this->assertEquals(array('foo'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
$field->setValue('bar');
$this->assertEquals(array('bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
$field->setValue(array('foo', 'bar'));
$this->assertEquals(array('foo', 'bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true');
$node = $this->createSelectNode(array('foo' => true, 'bar' => true), array('multiple' => 'multiple'));
$field = new ChoiceFormField($node);
$this->assertEquals(array('foo', 'bar'), $field->getValue(), '->getValue() returns the selected options');
try
{
$field->setValue(array('foobar'));
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the options');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the options');
}
}
public function testRadioButtons()
{
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar'));
$field->addChoice($node);
$this->assertFalse($field->hasValue(), '->hasValue() returns false when no radio button is selected');
$this->assertEquals(null, $field->getValue(), '->getValue() returns null if no radio button is selected');
$this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons');
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => 'checked'));
$field->addChoice($node);
$this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected');
$this->assertEquals('bar', $field->getValue(), '->getValue() returns the value attribute of the selected radio button');
$field->setValue('foo');
$this->assertEquals('foo', $field->getValue(), '->setValue() changes the selected radio button');
try
{
$field->setValue('foobar');
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the radio button values');
}
}
public function testCheckboxes()
{
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name'));
$field = new ChoiceFormField($node);
$this->assertFalse($field->hasValue(), '->hasValue() returns false when the checkbox is not checked');
$this->assertEquals(null, $field->getValue(), '->getValue() returns null if the checkbox is not checked');
$this->assertFalse($field->isMultiple(), '->hasValue() returns false for checkboxes');
try
{
$field->addChoice(new \DOMNode());
$this->fail('->addChoice() throws a \LogicException for checkboxes');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException for checkboxes');
}
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked'));
$field = new ChoiceFormField($node);
$this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked');
$this->assertEquals('1', $field->getValue(), '->getValue() returns 1 if the checkbox is checked and has no value attribute');
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute if the checkbox is checked');
$node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo'));
$field = new ChoiceFormField($node);
$field->setValue(false);
$this->assertEquals(null, $field->getValue(), '->setValue() unchecks the checkbox is value is false');
$field->setValue(true);
$this->assertEquals('foo', $field->getValue(), '->setValue() checks the checkbox is value is true');
try
{
$field->setValue('bar');
$this->fail('->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one from the value attribute');
}
}
protected function createSelectNode($options, $attributes = array())
{
$document = new \DOMDocument();
$node = $document->createElement('select');
foreach ($attributes as $name => $value)
{
$node->setAttribute($name, $value);
}
$node->setAttribute('name', 'name');
foreach ($options as $value => $selected)
{
$option = $document->createElement('option', $value);
$option->setAttribute('value', $value);
if ($selected)
{
$option->setAttribute('selected', 'selected');
}
$node->appendChild($option);
}
return $node;
}
}

View File

@ -0,0 +1,81 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler\Field;
require_once __DIR__.'/FormFieldTestCase.php';
use Symfony\Components\DomCrawler\Field\FileFormField;
class FileFormFieldTest extends FormFieldTestCase
{
public function testInitialize()
{
$node = $this->createNode('input', '', array('type' => 'file'));
$field = new FileFormField($node);
$this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), '->initialize() sets the value of the field to no file uploaded');
$node = $this->createNode('textarea', '');
try
{
$field = new FileFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not an input field');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input field');
}
$node = $this->createNode('input', '', array('type' => 'text'));
try
{
$field = new FileFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not a file input field');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a file input field');
}
}
public function testSetValue()
{
$node = $this->createNode('input', '', array('type' => 'file'));
$field = new FileFormField($node);
$field->setValue(null);
$this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), '->setValue() clears the uploaded file if the value is null');
$field->setValue(__FILE__);
$this->assertEquals(array('name' => 'FileFormFieldTest.php', 'type' => '', 'tmp_name' => __FILE__, 'error' => 0, 'size' => filesize(__FILE__)), $field->getValue(), '->setValue() sets the value to the given file');
}
public function testSetErrorCode()
{
$node = $this->createNode('input', '', array('type' => 'file'));
$field = new FileFormField($node);
$field->setErrorCode(UPLOAD_ERR_FORM_SIZE);
$value = $field->getValue();
$this->assertEquals(UPLOAD_ERR_FORM_SIZE, $value['error'], '->setErrorCode() sets the file input field error code');
try
{
$field->setErrorCode('foobar');
$this->fail('->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setErrorCode() throws a \InvalidArgumentException if the error code is not valid');
}
}
}

View File

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler\Field;
require_once __DIR__.'/FormFieldTestCase.php';
use Symfony\Components\DomCrawler\Field\InputFormField;
class FormFieldTest extends FormFieldTestCase
{
public function testGetName()
{
$node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
$field = new InputFormField($node);
$this->assertEquals('name', $field->getName(), '->getName() returns the name of the field');
}
public function testGetSetHasValue()
{
$node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
$field = new InputFormField($node);
$this->assertEquals('value', $field->getValue(), '->getValue() returns the value of the field');
$field->setValue('foo');
$this->assertEquals('foo', $field->getValue(), '->setValue() sets the value of the field');
$this->assertTrue($field->hasValue(), '->hasValue() always returns true');
}
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler\Field;
class FormFieldTestCase extends \PHPUnit_Framework_TestCase
{
protected function createNode($tag, $value, $attributes = array())
{
$document = new \DOMDocument();
$node = $document->createElement($tag, $value);
foreach ($attributes as $name => $value)
{
$node->setAttribute($name, $value);
}
return $node;
}
}

View File

@ -0,0 +1,60 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler\Field;
require_once __DIR__.'/FormFieldTestCase.php';
use Symfony\Components\DomCrawler\Field\InputFormField;
class InputFormFieldTest extends FormFieldTestCase
{
public function testInitialize()
{
$node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
$field = new InputFormField($node);
$this->assertEquals('value', $field->getValue(), '->initialize() sets the value of the field to the value attribute value');
$node = $this->createNode('textarea', '');
try
{
$field = new InputFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not an input');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input');
}
$node = $this->createNode('input', '', array('type' => 'checkbox'));
try
{
$field = new InputFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is a checkbox');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is a checkbox');
}
$node = $this->createNode('input', '', array('type' => 'file'));
try
{
$field = new InputFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is a file');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is a file');
}
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler\Field;
require_once __DIR__.'/FormFieldTestCase.php';
use Symfony\Components\DomCrawler\Field\TextareaFormField;
class TextareaFormFieldTest extends FormFieldTestCase
{
public function testInitialize()
{
$node = $this->createNode('textarea', 'foo bar');
$field = new TextareaFormField($node);
$this->assertEquals('foo bar', $field->getValue(), '->initialize() sets the value of the field to the textare node value');
$node = $this->createNode('input', '');
try
{
$field = new TextareaFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not a textarea');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a textarea');
}
}
}

View File

@ -0,0 +1,352 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler;
use Symfony\Components\DomCrawler\Form;
class FormTest extends \PHPUnit_Framework_TestCase
{
public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
{
$dom = new \DOMDocument();
$dom->loadHTML('
<html>
<input type="submit" />
<form>
<input type="foo" />
</form>
<button />
</html>
');
$nodes = $dom->getElementsByTagName('input');
try
{
$form = new Form($nodes->item(0));
$this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
}
try
{
$form = new Form($nodes->item(1));
$this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image');
}
$nodes = $dom->getElementsByTagName('button');
try
{
$form = new Form($nodes->item(0));
$this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image');
}
catch (\LogicException $e)
{
$this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image');
}
}
/**
* @dataProvider provideInitializeValues
*/
public function testConstructor($message, $form, $values)
{
$form = $this->createForm('<form>'.$form.'</form>');
$this->assertEquals($values, array_map(function ($field) { return array(get_class($field), $field->getValue()); }, $form->getFields()), '->getDefaultValues() '.$message);
}
public function provideInitializeValues()
{
return array(
array(
'does not take into account input fields without a name attribute',
'<input type="text" value="foo" />
<input type="submit" />',
array(),
),
array(
'does not take into account disabled input fields',
'<input type="text" name="foo" value="foo" disabled="disabled" />
<input type="submit" />',
array(),
),
array(
'appends the submitted button value',
'<input type="submit" name="bar" value="bar" />',
array('bar' => array('Symfony\\Components\\DomCrawler\\Field\\InputFormField', 'bar')),
),
array(
'appends the submitted button value but not other submit buttons',
'<input type="submit" name="bar" value="bar" />
<input type="submit" name="foobar" value="foobar" />',
array('foobar' => array('Symfony\\Components\\DomCrawler\\Field\\InputFormField', 'foobar')),
),
array(
'returns textareas',
'<textarea name="foo">foo</textarea>
<input type="submit" />',
array('foo' => array('Symfony\\Components\\DomCrawler\\Field\\TextareaFormField', 'foo')),
),
array(
'returns inputs',
'<input type="text" name="foo" value="foo" />
<input type="submit" />',
array('foo' => array('Symfony\\Components\\DomCrawler\\Field\\InputFormField', 'foo')),
),
array(
'returns checkboxes',
'<input type="checkbox" name="foo" value="foo" checked="checked" />
<input type="submit" />',
array('foo' => array('Symfony\\Components\\DomCrawler\\Field\\ChoiceFormField', 'foo')),
),
array(
'returns not-checked checkboxes',
'<input type="checkbox" name="foo" value="foo" />
<input type="submit" />',
array('foo' => array('Symfony\\Components\\DomCrawler\\Field\\ChoiceFormField', false)),
),
array(
'returns radio buttons',
'<input type="radio" name="foo" value="foo" />
<input type="radio" name="foo" value="bar" checked="bar" />
<input type="submit" />',
array('foo' => array('Symfony\\Components\\DomCrawler\\Field\\ChoiceFormField', 'bar')),
),
array(
'returns file inputs',
'<input type="file" name="foo" />
<input type="submit" />',
array('foo' => array('Symfony\\Components\\DomCrawler\\Field\\FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))),
),
);
}
public function testGetFormNode()
{
$dom = new \DOMDocument();
$dom->loadHTML('<html><form><input type="submit" /></form></html>');
$form = new Form($dom->getElementsByTagName('input')->item(0));
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), '->getFormNode() returns the form node associated with this form');
}
public function testGetMethod()
{
$form = $this->createForm('<form><input type="submit" /></form>');
$this->assertEquals('get', $form->getMethod(), '->getMethod() returns get if no method is defined');
$form = $this->createForm('<form method="post"><input type="submit" /></form>');
$this->assertEquals('post', $form->getMethod(), '->getMethod() returns the method attribute value of the form');
$form = $this->createForm('<form method="post"><input type="submit" /></form>', 'put');
$this->assertEquals('put', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
}
public function testGetSetValue()
{
$form = $this->createForm('<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>');
$this->assertEquals('foo', $form->getValue('foo'), '->getValue() returns the value of a form field');
$ret = $form->setValue('foo', 'bar');
$this->assertEquals($form, $ret, '->setValue() implements a fluent interface');
$this->assertEquals('bar', $form->getValue('foo'), '->setValue() changes the value of a form field');
try
{
$form->setValue('foobar', 'bar');
$this->pass('->setValue() throws an \InvalidArgumentException exception if the field does not exist');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->setValue() throws an \InvalidArgumentException exception if the field does not exist');
}
try
{
$form->getValue('foobar');
$this->pass('->getValue() throws an \InvalidArgumentException exception if the field does not exist');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->getValue() throws an \InvalidArgumentException exception if the field does not exist');
}
}
public function testGetValues()
{
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar'), $form->getValues(), '->getValues() returns all form field values');
$form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes');
$form = $this->createForm('<form><input type="file" name="foo" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields');
}
public function testSetValues()
{
$form = $this->createForm('<form><input type="checkbox" name="foo" value="foo" checked="checked" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$form->setValues(array('foo' => false, 'bar' => 'foo'));
$this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields');
}
public function testGetPhpValues()
{
$form = $this->createForm('<form><input type="text" name="foo[bar]" value="foo" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays');
}
public function testGetFiles()
{
$form = $this->createForm('<form><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get');
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields');
}
public function testGetPhpFiles()
{
$form = $this->createForm('<form method="post"><input type="file" name="foo[bar]" /><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays');
}
/**
* @dataProvider provideGetUriValues
*/
public function testGetUri($message, $form, $values, $uri)
{
$form = $this->createForm($form);
$form->setValues($values);
$this->assertEquals($uri, $form->getUri(), '->getUri() '.$message);
}
public function testGetUriAbsolute()
{
$form = $this->createForm('<form action="foo"><input type="submit" /></form>', null, 'http://localhost', '/foo/');
$this->assertEquals('http://localhost/foo/foo', $form->getUri(true), '->getUri() returns absolute URIs');
$form = $this->createForm('<form action="/foo"><input type="submit" /></form>', null, 'http://localhost', '/foo/');
$this->assertEquals('http://localhost/foo', $form->getUri(true), '->getUri() returns absolute URIs');
$form = $this->createForm('<form action="/foo"><input type="submit" /></form>');
$this->assertEquals('/foo', $form->getUri(true), '->getUri() returns absolute URIs only if the host has been defined in the constructor');
$form = $this->createForm('<form action="foo"><input type="submit" /></form>');
$this->assertEquals('/foo', $form->getUri(true), '->getUri() returns absolute URIs only if the host has been defined in the constructor');
}
public function provideGetUriValues()
{
return array(
array(
'returns the URI of the form',
'<form action="/foo"><input type="submit" /></form>',
array(),
'/foo'
),
array(
'appends the form values if the method is get',
'<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
array(),
'/foo?foo=foo'
),
array(
'appends the form values and merges the submitted values',
'<form action="/foo"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
array('foo' => 'bar'),
'/foo?foo=bar'
),
array(
'does not append values if the method is post',
'<form action="/foo" method="post"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
array(),
'/foo'
),
array(
'appends the form values to an existing query string',
'<form action="/foo?bar=bar"><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
array(),
'/foo?bar=bar&foo=foo'
),
array(
'returns an empty URI if the action is empty',
'<form><input type="submit" /></form>',
array(),
'',
),
array(
'appends the form values even if the action is empty',
'<form><input type="text" name="foo" value="foo" /><input type="submit" /></form>',
array(),
'/?foo=foo',
),
);
}
public function testHasField()
{
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertFalse($form->hasField('foo'), '->hasField() returns false if a field is not in the form');
$this->assertTrue($form->hasField('bar'), '->hasField() returns true if a field is in the form');
}
public function testGetField()
{
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$this->assertEquals('Symfony\\Components\\DomCrawler\\Field\\InputFormField', get_class($form->getField('bar')), '->getField() returns the field object associated with the given name');
try
{
$form->getField('foo');
$this->fail('->getField() throws an \InvalidArgumentException if the field does not exist');
}
catch (\InvalidArgumentException $e)
{
$this->assertTrue(true, '->getField() throws an \InvalidArgumentException if the field does not exist');
}
}
public function testGetFields()
{
$form = $this->createForm('<form method="post"><input type="text" name="bar" value="bar" /><input type="submit" /></form>');
$fields = $form->getFields();
$this->assertEquals(1, count($fields), '->getFields() return an array of form field objects');
$this->assertEquals('Symfony\\Components\\DomCrawler\\Field\\InputFormField', get_class($fields['bar']), '->getFields() return an array of form field objects');
}
protected function createForm($form, $method = null, $host = null, $path = '/')
{
$dom = new \DOMDocument();
$dom->loadHTML('<html>'.$form.'</html>');
$nodes = $dom->getElementsByTagName('input');
return new Form($nodes->item($nodes->length - 1), $method, $host, $path);
}
}

View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Tests\Components\DomCrawler;
use Symfony\Components\DomCrawler\Link;
class LinkTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$dom = new \DOMDocument();
$dom->loadHTML('<html><div><div></html>');
$node = $dom->getElementsByTagName('div')->item(0);
try
{
new Link($node);
$this->fail('__construct() throws a \LogicException if the node is not an "a" tag');
}
catch (\Exception $e)
{
$this->assertType('\LogicException', $e, '__construct() throws a \LogicException if the node is not an "a" tag');
}
}
public function testGetters()
{
$dom = new \DOMDocument();
$dom->loadHTML('<html><a href="/foo">foo</a></html>');
$node = $dom->getElementsByTagName('a')->item(0);
$link = new Link($node);
$this->assertEquals('/foo', $link->getUri(), '->getUri() returns the URI of the link');
$this->assertEquals($node, $link->getNode(), '->getNode() returns the node associated with the link');
$this->assertEquals('get', $link->getMethod(), '->getMethod() returns the method of the link');
$link = new Link($node, 'post');
$this->assertEquals('post', $link->getMethod(), '->getMethod() returns the method of the link');
$link = new Link($node, 'get', 'http://localhost', '/bar/');
$this->assertEquals('http://localhost/foo', $link->getUri(), '->getUri() returns the absolute URI of the link');
$this->assertEquals('/foo', $link->getUri(false), '->getUri() returns the relative URI of the link if false is the first argument');
$dom = new \DOMDocument();
$dom->loadHTML('<html><a href="foo">foo</a></html>');
$node = $dom->getElementsByTagName('a')->item(0);
$link = new Link($node, 'get', 'http://localhost', '/bar/');
$this->assertEquals('http://localhost/bar/foo', $link->getUri(), '->getUri() returns the absolute URI of the link for relative hrefs');
$this->assertEquals('/bar/foo', $link->getUri(false), '->getUri() returns the relative URI of the link if false is the first argument');
}
}