Can't set form value on a functional test - symfony

I'm currently doing this tutorial: http://tutorial.symblog.co.uk/docs/testing-unit-and-functional-phpunit.html I'm at the "Test contact page" part.
There we have defined a test called testContact() http://pastebin.com/PtzwY7PJ (edited by me, the outcommented stuff results in the same error)
If I run the tests I get the error. InvalidArgumentException: Unreachable field "name"
If I send the form on the page I can catch if via the symfony toolbar, so I assume, the form works correctly.
Heres the dumped object of $form: http://pastebin.com/n8MyHEfy
Thanks!

the form is named 'contact' now, so you need:
// Select based on button value, or id or name for buttons
$form = $crawler->selectButton('Submit')->form();
$form['contact[name]'] = 'name';
$form['contact[email]'] = 'email#email.com';
$form['contact[subject]'] = 'Subject';
$form['contact[body]'] = 'The comment body must be at least 50 characters long as there is a validation constrain on the Enquiry entity';
$crawler = $client->submit($form);

shouldn't it be like this?
$form = $crawler->selectButton('Submit')->form(array(
'blogger_blogbundle_enquirytype[name]' =>'name'
));

Related

How to Test a Form with Multiple Submit

I have a form with 2 submit
// src/Form/FooType.php
$builder
->add('mainsubmit', SubmitType::class, [])
->add('extrasubmit', SubmitType::class, [])
In my controller, I do some different treatment depending of the submit pressed
// src/Controller/FooController.php
if ($form->isSubmitted() && $form->isValid()) {
if ($form->get('extrasubmit')->isClicked()) {
// do some extra stuff
}
When I click on the extra button, I can see it the symfony Profiler in the request POST parameters "extrasubmit" => "".
Everything works fine.
I'm doing functional tests with the crawler.
Without trying to submit with the extra submit, it works fine, so we can assume my test doesn't have a typo.
How can I simulate the click on the extra submit ?
First Try:
$form = $crawler->filter('form')->form();
// [...]
$form['my_form_name[extrasubmit]'] = true;
$httpClient->submit($form);
// => InvalidArgumentException: Unreachable field "extrasubmit"
Second Try:
$form->get('my_form_name[extrasubmit]')->setValue("");
// => InvalidArgumentException: Unreachable field "extrasubmit"
Like often when have trouble with the DomCrawler, it is much better to fo throught $form->getPhpValues() which returns an array of values.
Then you just have to mimic the values given in the request
$values = $form->getPhpValues();
$values['my_form_name']['extrasubmit'] = '';
$httpClient->request(
$form->getMethod(),
$form->getUri(),
$values
);
According to code you can set the button via the setNode($domnode) method on the form. Sadly there is no function to find the nodes on the form object itself (since the node doesn't have to be a descendant of the form, that might not even help much).
So I guess something similar to
$form->setNode($crawler->filter('#extrasubmitbutton-id')[0]);
would set the submit button...

Convert old silverstripe form to SS 3

I have an old SS2.4 site which I have updated to SS3.1.13. The only part of the old site I can't get working is a search form that filters DataObjects. The old code is:
public function doCollectionSearch($data, $form)
{
$filters = array();
...
//setup some filters based on user selections
...
$where = implode(" AND ", $filters);
if(!isset($_REQUEST['start'])) $_REQUEST['start'] = 0;
$limit = $_REQUEST['start'].",50";
return $this->customise(array(
'Collections' => DataObject::get("Collection", $where, "Genus, Species ASC", null, $limit)
))->renderWith(array('Collection_results','Page'));
}
I have updated the last part to:
return $this->customise(array(
'Collections' => Collection::get()->where($where)->sort("Genus, Species ASC")->limit($limit)
))->renderWith(array('Collection_results','Page'));
But I get a "the method 'fortemplate' does not exist on 'CollectionPage_Controller'".
I know the $where is not right yet, but if I strip that out I still get an error..
I know I am missing something obvious...can anyone suggest a fix?
You'll get that error if your template accesses a method or database field that returns an object that can't be reduced to a string. This often happens when you have a related object (say a parent page) and you do something like the following:
<p>My parent is: $Parent</p>
Instead of
<p>My parent is: $Parent.Title</p>
The same thing existed in 2.4 so this doesn't totally explain your problem, but I'd look for something like the above scenario in your templates.

How to redirect with custom parameters?

I'm building a form. When the user submit the form, data are saved into the db, then the user is redirected to the same form with:
return $app->redirect('/admin/edit/user/' . $message);
The message variable is a message about the success or failure of the DB job.
But this solution doesn't suit me best... Is it possible to do the same with passing the $message variable as a POST parameter ? I don't want it appears into the URL...
You can do this by:
return $this->redirect($this->generateUrl('route_name', array('param1' => 'foo', 'param2' => 'bar')));
But... look at this:
http://symfony.com/doc/current/book/controller.html#flash-messages

Symfony2 functional testing InvalidArgumentException: The current node list is empty

I get "InvalidArgumentException: The current node list is empty." running functional tests through PHPUnit. Here is test i wrote:
public function testAdd()
{
$client = static::createClientWithAuthentication('main');
$crawler = $client->request('GET', 'en/manage');
$send_button = $crawler->selectButton('submit');
$form = $send_button->form(array(
'PrCompany[email]' => 'test#example.ua',
'PrCompany[first_name]' => 'Anton',
'PrCompany[last_name]' => 'Tverdiuh',
'PrCompany[timezone]' => 'Europe/Amsterdam'
));
$form['PrCompany[companies][1]']->tick();
$client->submit($form);
$this->assertTrue($crawler->filter('html:contains("User is invited")')->count() > 0);
}
You can debug the problem by using var_dump($client->getResponse()->getContent());
Additionally, I think you should write this:
$crawler = $client->submit($form);
Otherwise you'll be testing the response of the first url, before form submission.
I was also struggling with this, and It appeared that the selectButton method triggered this error.
After reading up on the DOM Crawler docs I found that the selectButton method takes the actual button text as a string argument. So if your button is 'submit my form please', that will be your text.
It does take different parameters too, as shown below (taken from the docs)
A selectButton() method is available on the Crawler which returns another
Crawler that matches a button (input[type=submit], input[type=image],
or a button) with the given text.
EDIT
After finally successfully completing the test I would also recommend you follow this example for testing forms:
use Goutte\Client;
$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';
$crawler = $client->submit($form);
$this->assertTrue($crawler->filter('html:contains("Welcome Back")')->count() > 0);
The main difference being, I have used the Goutte bundle, which I installed with composer from the packagist (in my case I added "fabpot/goutte": "1.0.*#dev")
As a follow up to what #greg0ire wrote, check to see if
var_dump($client->getResponse()->getContent());
Returns a redirect page instead of the actual content. If so, you can add this:
$client->followRedirects(true);
I had the same problem with Silex application. I was looking for
$buttonCrawler = $crawler->selectButton('input[type="submit"]');
Instead, the correct way to do it is give the value of the button
$buttonCrawler = $crawler->selectButton('value_of_the_button');
For example, in your page:
<form>
...
<input type="submit" value="Click Me">
</form>
And in your tests:
$buttonCrawler = $crawler->selectButton('Click Me');
$form = $buttonCrawler->form();
...
I see question still dont have answer. I had the same problem.
In my case goutte was not able to do this request because input name is changed by javascript on the fly.
When goutte received html it saw one form. And when submitting with pre filled params, form input elements could not be matched by $form->setValues($params) so \InvalidArgumentException was thrown.
Solved by doing request by hand.
// $form->setValues($data);
// $this->getGoutte()->submit($form);
$data = array(
'input_name[key]' => 'value'
);
$this->getGoutte()->request($form->getMethod(), $form->getUri(), $params);
You can try to use Codeception with Symfony2 module. It provides flexible interface to Symfony2 functional tests and has better debugging features.
This error would occur when crawler can't find form element requested; Quite tricky when you are using, for instance, form builder as when run, it will create different input name:
$form = $this-> createFormBuilder($store)
->add('storeNumber','text')
->add('storeName','text')
->add('save', 'submit')
->getForm();
will output field name like:
form_storeNumber
which should be used in test class:
$form=$crawler->selectButton('save')->form();
$form['form_storeNumber']='10';

Loading the previous revision of a node

When you get a node, how do you load the previous version (revision)?
I know how loading a revision but not how getting the previous revision number ($node->vid is the current revision).
thanks
Supposing that you have a node object $node, you can use the following code to get the previous revision.
$previous_vid = db_result(
db_query('SELECT MAX(vid) AS vid FROM {node_revisions} WHERE vid < %d AND nid = %d', $node->vid, $node->nid)
);
Once you have the previous revision, you can load the new node object with node_load(array('nid' => $node-nid, 'vid' => $previous_vid)).
The code should check if db_result() returns FALSE, in the case there isn't a previous revision.
To note that the field vid is global for each node; it doesn't contain the same value for different nodes.
Thanks all.
I found also an other solution:
$revisions = node_revision_list($node);
next($revisions);
if ($preview_key = key($revisions)) {
$preview_revision = $revisions[$preview_key];
$old_node = node_load($node->nid, $preview_revision->vid);
}
But if you have a lot of revision you get a big array.
If I understand what you're trying to do; you want to get the preview of the node after someone submits changes?
The preview button has its own submit handler, node_form_build_preview(). There, it creates a new node object using the data in $form_state and runs node_preview(), which returns the markup for the preview.
If you want to capture that preview when the user clicks the preview button, you'll need to use hook_form_alter to add another submit handler to the preview button:
$['form']['buttons']['preview']['#submit'][] = 'mymodule_custom_preview';
where mymodule_custom_preview is the name of your custom submit function. Take a look at node_form_build_preview() for guidance, but your submit function is going to look something like this:
function mymodule_custom_preview($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state);
$preview = node_preview($node);
}
Also take a look at node_form(), which gives you an idea of how the node form is structured. When you're all done, you're going to have code in your module that looks something like this:
function mymodule_form_alter(&$form, $form_state, $form_id) {
if (strstr($form_id, '_node_form') !== FALSE) {
$['form']['buttons']['preview']['#submit'][] = 'mymodule_custom_preview';
}
}
function mymodule_custom_preview($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state);
$preview = node_preview($node);
// Do what you will with $preview.
}

Resources