desperado desperado - 6 months ago 73
PHP Question

LogicException: The selected node does not have a form ancestor. error with symfony2 phpunit testing

I always get the following error when I try to fill out a form with PHPUnit Functional Testing:

LogicException: The selected node does not have a form ancestor. error with symfony2 phpunit testing


Here is the code, the error shows up at the last line of code:

$editusercrawler = $client->request('GET', '/profile');
$this->assertTrue($editusercrawler->filter('html:contains("Edit Profile")')->count() > 0);


// Go To Edit
$link = $editusercrawler->filter('a:contains("Edit Profile")')->eq(0)->link();
$editusercrawler = $client->click($link);
//var_dump($client->getResponse()->getContent());

// Check if User Edit was called
$this->assertTrue($editusercrawler->filter('html:contains("Edit User")')->count() > 0);
//var_dump($client->getResponse()->getContent());
// Fill out form

//var_dump($client->getResponse()->getContent());
$editusercrawler = $client->click($link);
$editusercrawler = $client->request('GET', '/profile/edit');
var_dump($client->getResponse()->getContent());

$editUserForm = $editusercrawler->selectButton('update')->form();

Answer

This problem appears, when HTML isn't structured properly. The setNode method has the following structure (I am using Laravel )

protected function setNode(\DOMElement $node)
{
    $this->button = $node;
    if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($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 {
        if (null === $node = $node->parentNode) {
            throw new \LogicException('The selected node does not have a form ancestor.');
        }
    } while ('form' !== $node->nodeName);
} elseif ('form' !== $node->nodeName) {
    throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
}

$this->node = $node;

}

  • As you see, the method first checks if node name matches button/input, while going further for input tag by verifying the 'type' attribute.
  • Next it checks for the form attribute. Otherwise it throws exception. Now $this->node is set.
  • Here comes the final explanation Currently you have $node (you can use dd() or var_dump() to see output) set, as next parentNode will be checked. With a do while loop it searches so long, until the nodeName wont be named "form".

As said in the beginning, your HTML isn't structured properly.

INVALID EXAMPLE

<form>
<div id="first">
<input type="text" value="Wont check this">
<div/>
</form>
<div id="second">
<input type="submit" value="Working">
</div>

VALID EXAMPLE

<form>
<div id="first">
<input type="text" value="Wont check this">
<div/>
<div id="second">
<input type="submit" value="Working">
</div>
</form>

Thats a simple explanation why it wont work. In my case, I was starting form inside the bootstrap panel-body div, but had submit button in panel-footer div.