[DomCrawler] added support for HTML5 'form' attribute
This commit is contained in:
parent
ea79360fba
commit
f8178dd1bb
@ -5,6 +5,7 @@ CHANGELOG
|
|||||||
-----
|
-----
|
||||||
|
|
||||||
* added schema relative URL support to links
|
* added schema relative URL support to links
|
||||||
|
* added support for HTML5 'form' attribute
|
||||||
|
|
||||||
2.2.0
|
2.2.0
|
||||||
-----
|
-----
|
||||||
|
@ -331,7 +331,9 @@ class Form extends Link implements \ArrayAccess
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets current \DOMNode instance.
|
* Sets the node for the form.
|
||||||
|
*
|
||||||
|
* Expects a 'submit' button \DOMNode and finds the corresponding form element.
|
||||||
*
|
*
|
||||||
* @param \DOMNode $node A \DOMNode instance
|
* @param \DOMNode $node A \DOMNode instance
|
||||||
*
|
*
|
||||||
@ -341,8 +343,18 @@ class Form extends Link implements \ArrayAccess
|
|||||||
{
|
{
|
||||||
$this->button = $node;
|
$this->button = $node;
|
||||||
if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
|
if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
|
||||||
|
if ($node->hasAttribute('form')) {
|
||||||
|
// if the node has the HTML5-compliant 'form' attribute, use it
|
||||||
|
$formId = $node->getAttribute('form');
|
||||||
|
$form = $node->ownerDocument->getElementById($formId);
|
||||||
|
if (null === $form) {
|
||||||
|
throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId));
|
||||||
|
}
|
||||||
|
$this->node = $form;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we loop until we find a form ancestor
|
||||||
do {
|
do {
|
||||||
// use the ancestor form element
|
|
||||||
if (null === $node = $node->parentNode) {
|
if (null === $node = $node->parentNode) {
|
||||||
throw new \LogicException('The selected node does not have a form ancestor.');
|
throw new \LogicException('The selected node does not have a form ancestor.');
|
||||||
}
|
}
|
||||||
@ -366,30 +378,47 @@ class Form extends Link implements \ArrayAccess
|
|||||||
$root->appendChild($button);
|
$root->appendChild($button);
|
||||||
$xpath = new \DOMXPath($document);
|
$xpath = new \DOMXPath($document);
|
||||||
|
|
||||||
foreach ($xpath->query('descendant::input | descendant::button | descendant::textarea | descendant::select', $root) as $node) {
|
// add descendant elements to the form
|
||||||
if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
|
$fieldNodes = $xpath->query('descendant::input | descendant::button | descendant::textarea | descendant::select', $root);
|
||||||
continue;
|
foreach ($fieldNodes as $node) {
|
||||||
}
|
$this->addField($node, $button);
|
||||||
|
}
|
||||||
|
|
||||||
$nodeName = $node->nodeName;
|
// find form elements corresponding to the current form by the HTML5 form attribute
|
||||||
|
if ($this->node->hasAttribute('id')) {
|
||||||
if ($node === $button) {
|
$formId = Crawler::xpathLiteral($this->node->getAttribute('id'));
|
||||||
$this->set(new Field\InputFormField($node));
|
$xpath = new \DOMXPath($this->node->ownerDocument);
|
||||||
} elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) {
|
$fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s]', $formId, $formId, $formId, $formId));
|
||||||
$this->set(new Field\ChoiceFormField($node));
|
foreach ($fieldNodes as $node) {
|
||||||
} elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) {
|
$this->addField($node, $button);
|
||||||
if ($this->has($node->getAttribute('name'))) {
|
|
||||||
$this->get($node->getAttribute('name'))->addChoice($node);
|
|
||||||
} else {
|
|
||||||
$this->set(new Field\ChoiceFormField($node));
|
|
||||||
}
|
|
||||||
} elseif ('input' == $nodeName && 'file' == $node->getAttribute('type')) {
|
|
||||||
$this->set(new Field\FileFormField($node));
|
|
||||||
} elseif ('input' == $nodeName && !in_array($node->getAttribute('type'), array('submit', 'button', 'image'))) {
|
|
||||||
$this->set(new Field\InputFormField($node));
|
|
||||||
} elseif ('textarea' == $nodeName) {
|
|
||||||
$this->set(new Field\TextareaFormField($node));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addField(\DOMNode $node, \DOMNode $button)
|
||||||
|
{
|
||||||
|
if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nodeName = $node->nodeName;
|
||||||
|
|
||||||
|
if ($node === $button) {
|
||||||
|
$this->set(new Field\InputFormField($node));
|
||||||
|
} elseif ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == $node->getAttribute('type')) {
|
||||||
|
$this->set(new Field\ChoiceFormField($node));
|
||||||
|
} elseif ('input' == $nodeName && 'radio' == $node->getAttribute('type')) {
|
||||||
|
if ($this->has($node->getAttribute('name'))) {
|
||||||
|
$this->get($node->getAttribute('name'))->addChoice($node);
|
||||||
|
} else {
|
||||||
|
$this->set(new Field\ChoiceFormField($node));
|
||||||
|
}
|
||||||
|
} elseif ('input' == $nodeName && 'file' == $node->getAttribute('type')) {
|
||||||
|
$this->set(new Field\FileFormField($node));
|
||||||
|
} elseif ('input' == $nodeName && !in_array($node->getAttribute('type'), array('submit', 'button', 'image'))) {
|
||||||
|
$this->set(new Field\InputFormField($node));
|
||||||
|
} elseif ('textarea' == $nodeName) {
|
||||||
|
$this->set(new Field\TextareaFormField($node));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,6 +393,9 @@ EOF
|
|||||||
$this->assertEquals(1, $crawler->selectButton('BarValue')->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('BarName')->count(), '->selectButton() selects buttons');
|
||||||
$this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons');
|
$this->assertEquals(1, $crawler->selectButton('BarId')->count(), '->selectButton() selects buttons');
|
||||||
|
|
||||||
|
$this->assertEquals(1, $crawler->selectButton('FooBarValue')->count(), '->selectButton() selects buttons with form attribute too');
|
||||||
|
$this->assertEquals(1, $crawler->selectButton('FooBarName')->count(), '->selectButton() selects buttons with form attribute too');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLink()
|
public function testLink()
|
||||||
@ -427,10 +430,17 @@ EOF
|
|||||||
|
|
||||||
public function testForm()
|
public function testForm()
|
||||||
{
|
{
|
||||||
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectButton('FooValue');
|
$testCrawler = $this->createTestCrawler('http://example.com/bar/');
|
||||||
|
$crawler = $testCrawler->selectButton('FooValue');
|
||||||
|
$crawler2 = $testCrawler->selectButton('FooBarValue');
|
||||||
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance');
|
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance');
|
||||||
|
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler2->form(), '->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');
|
$this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute');
|
||||||
|
|
||||||
|
$this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument');
|
||||||
|
$this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->form() takes an array of values to submit as its first argument');
|
||||||
|
$this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->form() takes an array of values to submit as its first argument');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->createTestCrawler()->filterXPath('//ol')->form();
|
$this->createTestCrawler()->filterXPath('//ol')->form();
|
||||||
@ -576,12 +586,16 @@ EOF
|
|||||||
|
|
||||||
<a href="?get=param">GetLink</a>
|
<a href="?get=param">GetLink</a>
|
||||||
|
|
||||||
<form action="foo">
|
<form action="foo" id="FooFormId">
|
||||||
|
<input type="text" value="TextValue" name="TextName" />
|
||||||
<input type="submit" value="FooValue" name="FooName" id="FooId" />
|
<input type="submit" value="FooValue" name="FooName" id="FooId" />
|
||||||
<input type="button" value="BarValue" name="BarName" id="BarId" />
|
<input type="button" value="BarValue" name="BarName" id="BarId" />
|
||||||
<button value="ButtonValue" name="ButtonName" id="ButtonId" />
|
<button value="ButtonValue" name="ButtonName" id="ButtonId" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<input type="submit" value="FooBarValue" name="FooBarName" form="FooFormId" />
|
||||||
|
<input type="text" value="FooTextValue" name="FooTextName" form="FooFormId" />
|
||||||
|
|
||||||
<ul class="first">
|
<ul class="first">
|
||||||
<li class="first">One</li>
|
<li class="first">One</li>
|
||||||
<li>Two</li>
|
<li>Two</li>
|
||||||
|
@ -62,6 +62,51 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __construct() should throw \\LogicException if the form attribute is invalid
|
||||||
|
* @expectedException \LogicException
|
||||||
|
*/
|
||||||
|
public function testConstructorThrowsExceptionIfNoRelatedForm()
|
||||||
|
{
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadHTML('
|
||||||
|
<html>
|
||||||
|
<form id="bar">
|
||||||
|
<input type="submit" form="nonexistent" />
|
||||||
|
</form>
|
||||||
|
<input type="text" form="nonexistent" />
|
||||||
|
<button />
|
||||||
|
</html>
|
||||||
|
');
|
||||||
|
|
||||||
|
$nodes = $dom->getElementsByTagName('input');
|
||||||
|
|
||||||
|
$form = new Form($nodes->item(0), 'http://example.com');
|
||||||
|
$form = new Form($nodes->item(1), 'http://example.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructorHandlesFormAttribute()
|
||||||
|
{
|
||||||
|
$dom = new \DOMDocument();
|
||||||
|
$dom->loadHTML('
|
||||||
|
<html>
|
||||||
|
<form id="bar">
|
||||||
|
<input type="submit" form="bar" />
|
||||||
|
</form>
|
||||||
|
<input type="submit" form="bar" />
|
||||||
|
<button />
|
||||||
|
</html>
|
||||||
|
');
|
||||||
|
|
||||||
|
$nodes = $dom->getElementsByTagName('input');
|
||||||
|
|
||||||
|
$form = new Form($nodes->item(0), 'http://example.com');
|
||||||
|
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), 'HTML5-compliant form attribute handled incorrectly');
|
||||||
|
|
||||||
|
$form = new Form($nodes->item(1), 'http://example.com');
|
||||||
|
$this->assertSame($dom->getElementsByTagName('form')->item(0), $form->getFormNode(), 'HTML5-compliant form attribute handled incorrectly');
|
||||||
|
}
|
||||||
|
|
||||||
public function testMultiValuedFields()
|
public function testMultiValuedFields()
|
||||||
{
|
{
|
||||||
$form = $this->createForm('<form>
|
$form = $this->createForm('<form>
|
||||||
|
Reference in New Issue
Block a user