Skip to content

[DomCrawler] ChoiceFormField values for multiple and expanded options #40826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jvancoillie opened this issue Apr 15, 2021 · 5 comments
Closed

Comments

@jvancoillie
Copy link

jvancoillie commented Apr 15, 2021

Symfony version(s) affected: 5.2.4 (symfony/dom-crawler)

Description

DomCrawler ChoiceFormField does not accept multiple values with multiple and expanded field options enabled

How to reproduce

WebTestCase submitForm with multiple values

    public function testNewDocument()
    {
        // ... client
        
        $client->submitForm('Save changes', [
            'document[states]' => [Document::REVIEWED, Document::PUBLISHED],
        ]);
        
        // ... asserts
    }

FormType used in my test

DocumentFormType extends AbstractType 
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('states', ChoiceType::class, [
            'multiple' => true,
            'expanded' => true,
            'choices' => [
                Document::DRAFT => Document::DRAFT , 
                Document::REVIEWED => Document::REVIEWED, 
                Document::PUBLISHED => Document::PUBLISHED
            ],
        ]);
    }
}

phpunit throws me the following exception

InvalidArgumentException: Input "document[states][]" cannot take "reviewed" as a value (possible values: "draft").

Possible Solution

Detect if the inputs are multiple when extended is enabled during the initialization of ChoiceFormField#L215

@stof
Copy link
Member

stof commented Apr 15, 2021

that's not how expanded choice fields are working. Each checkbox has its own index, submitting document[states][0] and document[states][3]. so you need to submit the right data.

And the Form system of DomCrawler handles each checkbox on its own, not as a group named document[states] (which actually matches how browsers behave: there is strictly no relation between the various checkboxes)

@jvancoillie
Copy link
Author

jvancoillie commented Apr 15, 2021

@stof thank you for your quick reply

For testing the expanded multiple choice, I need to know the value position and submit it like this :

    public function testNewDocument()
    {
        // ... client
        
        $client->submitForm('Save changes', [
            'document[states][1]' => Document::REVIEWED, 
            'document[states][2]' => Document::PUBLISHED,
        ]);
        
        // ... asserts
    }

But for non-extended multiple ChoiceType, I can still use it like this ?

    public function testNewDocument()
    {
        // ... client
        
        $client->submitForm('Save changes', [
          'document[states]' => [Document::REVIEWED, Document::PUBLISHED]
        ]);
        
        // ... asserts
    }

Thank you for this explanation

@stof
Copy link
Member

stof commented Apr 15, 2021

@jvancoillie yes, because a <select multiple> element submits its values using document[states][] as name (we cannot provide separate names for each <option> there)

@jvancoillie
Copy link
Author

@stof thank you for all these details, I had not found my happiness in the document. :(
Sorry for the inconvenience, hope this conversation can help some people

@boedah
Copy link

boedah commented Jun 9, 2022

I know this issue was closed long ago, but I modified #7337 (comment) to find and tick multiple boxes by multiple values, maybe someone finds it useful (FYI @jvancoillie):

    /**
     * Find checkboxes which have any of the given value(s).
     * Useful for multiple, expanded choice fields.
     *
     * Supply the field without a trailing '[]', e.g.
     * `getMultipleCheckboxesByValues($form['my_form[someMultipleExpandedChoiceField]'], [25, 75]);`
     *
     * @param ChoiceFormField[]|FormField[] $field
     *
     * @return ChoiceFormField[]
     */
    protected function getMultipleCheckboxesByValues(
        array        $field,
        array|string $values,
        bool         $force = true
    ): array {
        $values = array_map('strval', (array)$values);
        $matchedFields = array_filter($field, function (ChoiceFormField $choice) use ($values) {
            if ('checkbox' !== $choice->getType()) {
                throw new \LogicException(sprintf('Field "%s" is not a checkbox (%s).', $choice->getName(), $choice->getType()));
            }

            return in_array($choice->availableOptionValues()[0], $values);
        });

        if ($force && empty($matchedFields)) {
            throw new \InvalidArgumentException(sprintf('Field "%s" does not have any option with value "%s".', $field[0]->getName(), implode('", "', $values)));
        }

        return $matchedFields;
    }

    /**
     * Ticks checkboxes which have any of the given value(s).
     * Useful for multiple, expanded choice fields.
     *
     * Supply the field without a trailing '[]', e.g.
     * `tickMultipleCheckboxesByValues($form['my_form[someMultipleExpandedChoiceField]'], [25, 75]);`
     *
     * @param ChoiceFormField[]|FormField[] $field
     */
    protected function tickMultipleCheckboxesByValues(array $field, array|string $values, bool $force = true): void
    {
        foreach ($this->getMultipleCheckboxesByValues($field, $values, $force) as $checkbox) {
            $checkbox->tick();
        }
    }

I think it should be possible to do this out of the box, are you interested in a PR, @stof?
If so, in which repo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants