Search code examples
symfonyphpunitfunctional-testingdomcrawler

Form tests: How to submit a collection to an existing form?


I use two ways to test my forms:

By using $form = …->form();

Then setting the values of the $form array (more precisely this is a \Symfony\Component\DomCrawler\Form object):

Full example from the documentation:

$form = $crawler->selectButton('submit')->form();

// set some values
$form['name'] = 'Lucas';
$form['form_name[subject]'] = 'Hey there!';

// submit the form
$crawler = $client->submit($form);

By sending the POST data directly:

The previous code doesn't work with forms which manage collections (relying on fields created by Javascript) because it throws an error if the field doesn't exist. That's why I also use this other way.

Full example from the documentation:

// Directly submit a form (but using the Crawler is easier!)
$client->request('POST', '/submit', array('name' => 'Fabien'));

This solution is the only way I know to test forms which manage collections with fields added by Javascript (see link to documentation above). But this second solution is harder to use because:

  • it doesn't check which fields exist, this is impractical when I have to submit a form with existing fields and a collection which relies on fields created dynamically with Javascript
  • it requires to add the form _token manually

My question

Is it possible to use the syntax from the first way to define the existing fields then add new dynamically created fields with the second syntax?

In other words, I would like to have something like this:

$form = $crawler->selectButton('submit')->form();

// set some values for the existing fields
$form['name'] = 'Lucas';
$form['form_name[subject]'] = 'Hey there!';

// submit the form with additional data
$crawler = $client->submit($form, array('name' => 'Fabien'));

But I get this error:

Unreachable field "name"

And $form->get('name')->setData('Fabien'); triggers the same error.

This example is not perfect because the form has no collection, but it's enough to show you my problem.

I'm looking for a way to avoid this validation when I add some fields to the existing form.


Solution

  • This can be done by calling slightly modified code from the submit() method:

    // Get the form.
    $form = $crawler->filter('button')->form();
    
    // Merge existing values with new values.
    $values = array_merge_recursive(
        $form->getPhpValues(),
        array(
            // New values.
            'FORM_NAME' => array(
                'COLLECTION_NAME' => array(
                    array(
                        'FIELD_NAME_1' => 'a',
                        'FIELD_NAME_2' => '1',
                    )
                )
            )
        )
    );
    
    // Submit the form with the existing and new values.
    $crawler = $this->client->request($form->getMethod(), $form->getUri(), $values,
        $form->getPhpFiles());
    

    The array with the news values in this example correspond to a form where you have a fields with these names:

    <input type="…" name="FORM_NAME[COLLECTION_NAME][A_NUMBER][FIELD_NAME_1]" />
    <input type="…" name="FORM_NAME[COLLECTION_NAME][A_NUMBER][FIELD_NAME_2]" />
    

    The number (index) of the fields is irrelevant, PHP will merge the arrays and submit the data, Symfony will transform this data in the corresponding fields.