zenstruck / browser

A fluent interface for your Symfony functional tests.
MIT License
185 stars 17 forks source link

How to submit a form field which is dynamically populated? #110

Closed benr77 closed 1 year ago

benr77 commented 1 year ago

Say I have a "customer lookup" field which uses an AJAX request to search for possible matches via a backend endpoint. On page load the <select> contains no options.

How can I populate this field with a value? I know what the ID of the value should be, but obviously the Javascript lookup to the backend does not work.

I have tried ->selectField('Customer', '1234') and also ->fillField('Customer', '1234') but neither are working.

SelectField() gives me an error such as:

Select option with value|text "1234" not found.

FillField() gives me an error such as:

...cannot take "1234" as a value (possible values: "")

Is there a way to just define the name of a form field and the value that should be sent when submitting the form?

Any pointers appreciated. Thanks.

benr77 commented 1 year ago

Update to the above - this actually has uncovered a wider application. I am trying to simulate submitting a form with a form collection, and the JS to add collection items is unavailable.

Using e.g. Codeception I am able to simply specify what the form fields would be if I had added several items to the collection, and then submit these.

This is the same case as above for the select with autocomplete - instead of wanting to fill in form fields directly, we need a general way to manipulate the form data that is send when the form submit click is simulated.

benr77 commented 1 year ago

I have a working solution for the autocomplete field which is not very elegant but seems to work.

->use(function(AbstractBrowser $browser, Crawler $crawler)
    {
        $form = $crawler->selectButton('Next Step')->form();
        $form['customer']->disableValidation()->select('1234');
        $browser->submit($form);
    })

I don't think the callback function(AbstractBrowser $browser, Crawler $crawler) is documented but it works.

kbond commented 1 year ago

Interesting solution. You could probably wrap that up into a dynamic custom browser method.

benr77 commented 1 year ago

I've done exactly that.

// AppBrowser

public function selectAutocompleteField(string $formButtonName, string $selector, string $value): self
{
    return $this->use(function(AbstractBrowser $browser, Crawler $crawler) use ($formButtonName, $selector, $value)
    {
        $form = $crawler->selectButton($formButtonName)->form();
        $form[$selector]->disableValidation()->select($value);
        $browser->submit($form);
    });
}

It would be good if I didn't need to pass the form name...

kbond commented 1 year ago

What about:

public function selectAutocompleteField(string $selector, string $value): self
{
    return $this->use(function(AbstractBrowser $browser, Crawler $crawler) use ($formButtonName, $selector, $value)
    {
        $form = $crawler->filter(\sprintf('[name="%s"]', $selector))->ancestors()->filter('form')->form();
        $form[$selector]->disableValidation()->select($value);
        $browser->submit($form);
    });
}
benr77 commented 1 year ago

Yep - that's a great improvement thanks!

kbond commented 1 year ago

I'm going to close as this seems like a niche feature that can be achieved with a custom browser or component. If there is future interest in this we can revisit.