Framework: Testing select() should have a force option

Created on 8 Jul 2015  路  8Comments  路  Source: laravel/framework

Hi,

It could be very usefull to have an option for forcing select value while testing form with ajax append, or maybe a way to trigger javascript in the crawler.

For exemple :

<!-- Object Form Input -->
<div class="form-group">
    {!! Form::label('object', 'Object :') !!}
    {!! Form::text('object', null, ['class' => 'form-control']) !!}
</div>
$.ajax({
    dataType: "json",
    url : "/objects/liste",
    success : function(data){
        $.each(data,function(index,name){
        $("#object").append('<option value="' + index + '">' + name + '</option>');
    });
   }
})

If I try to test this like

$this->visit('/objects')
       ->select('Foo','object');

I'll get errror InvalidArgumentException: Input "contact" cannot take Foo as a value (possible values: ).

Could be nice to do something like

$this->visit('/objects')
       ->forceSelect('Foo','object');

Thanks

Most helpful comment

Hi,
If anyone needs this functionality it is not that hard... Symfony provides a disableValidation option that you could use by extending the InteractsWithPages Laravel Trait on your TestCase class. It could look something like this :

<?php

class TestCase extends Illuminate\Foundation\Testing\TestCase
{
    /**
     * The base URL to use while testing the application.
     *
     * @var string
     */
    protected $baseUrl = 'http://localhost';

    /**
     * Disabled validation fields array
     *
     * @var array
     */
    protected $disabledValidationFields;

    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

        return $app;
    }

    /**
     * Select an option from a drop-down.
     *
     * @param  string  $option
     * @param  string  $element
     * @return $this
     */
    protected function forceSelect($option, $element)
    {
        return $this->storeDisabledValidationInput($element, $option);
    }

    /**
     * Store a form input in the local array.
     *
     * @param  string  $element
     * @param  string  $text
     * @return $this
     */
    private function storeDisabledValidationInput($element, $text)
    {
        $this->assertFilterProducesResults($element);

        $element = str_replace('#', '', $element);

        $this->inputs[$element] = $text;

        $this->disabledValidationFields[] = $element;

        return $this;
    }

    /**
     * Fill the form with the given data.
     *
     * @param  string  $buttonText
     * @param  array  $inputs
     * @return \Symfony\Component\DomCrawler\Form
     */
    protected function fillForm($buttonText, $inputs = [])
    {
        if (! is_string($buttonText)) {
            $inputs = $buttonText;

            $buttonText = null;
        }

        $form = $this->getForm($buttonText);

        $this->disableValidationOn($form);

        return $form->setValues($inputs);
    }

    /**
     * Adds disableValidation on selected fields
     *
     * @param $form  \Symfony\Component\DomCrawler\Form
     */
    private function disableValidationOn($form)
    {
        if (!is_array($this->disabledValidationFields)) {
            return;
        }

        foreach($this->disabledValidationFields as $field)
        {
            $form->get($field)->disableValidation();
        }

        $this->disabledValidationFields = [];
    }
}

You can then use :

$this->forceSelect('value', 'field'); 

in your tests.

Xavier

All 8 comments

So you've to execute javascript? Probably you can do this with node.js. Probably you can make a PR for that :+1:

I very much doubt such a PR would be accepted.

Probably you can do this with node.js.

I very much doubt that.

Me too. But every one is free to try to achieve things like this :) :+1:

But every one is free to try to achieve things like this

That's fine, but not here.

1_123125_122981_2111757_2123290_050725_sh_bugspray_tn jpg crop original-original

Gebruik maar @ArjanSchouten

@jaketoolson Please delete this. You talk dutch. That isn't fair. Graham does a great job by supporting all those projects. Respect!

I think I did not explain enough my issue my bad, I just hope selenium will be a part of laravel soon.

My problem was on a project I got a form who display company select.
I can test this simply with a $this->select('Acme','company'), but when I select a company a select field is generate with an ajax call.

I know the crawler can't manage the ajax part so I tried to force the test with $this->select('Foo','contact') and indeed I created before an Acme contact "Foo".

The problem is because crawler doesn't support ajax it allways see an empty select and error with errror InvalidArgumentException: Input "contact" cannot take Foo as a value (possible values: ).

Right now I make my test pass with an hidden input who is updated with the contact combo, it let me
force the sumbit value with with input instead of the select. It's ugly but at least it works.

Hi,
If anyone needs this functionality it is not that hard... Symfony provides a disableValidation option that you could use by extending the InteractsWithPages Laravel Trait on your TestCase class. It could look something like this :

<?php

class TestCase extends Illuminate\Foundation\Testing\TestCase
{
    /**
     * The base URL to use while testing the application.
     *
     * @var string
     */
    protected $baseUrl = 'http://localhost';

    /**
     * Disabled validation fields array
     *
     * @var array
     */
    protected $disabledValidationFields;

    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';

        $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

        return $app;
    }

    /**
     * Select an option from a drop-down.
     *
     * @param  string  $option
     * @param  string  $element
     * @return $this
     */
    protected function forceSelect($option, $element)
    {
        return $this->storeDisabledValidationInput($element, $option);
    }

    /**
     * Store a form input in the local array.
     *
     * @param  string  $element
     * @param  string  $text
     * @return $this
     */
    private function storeDisabledValidationInput($element, $text)
    {
        $this->assertFilterProducesResults($element);

        $element = str_replace('#', '', $element);

        $this->inputs[$element] = $text;

        $this->disabledValidationFields[] = $element;

        return $this;
    }

    /**
     * Fill the form with the given data.
     *
     * @param  string  $buttonText
     * @param  array  $inputs
     * @return \Symfony\Component\DomCrawler\Form
     */
    protected function fillForm($buttonText, $inputs = [])
    {
        if (! is_string($buttonText)) {
            $inputs = $buttonText;

            $buttonText = null;
        }

        $form = $this->getForm($buttonText);

        $this->disableValidationOn($form);

        return $form->setValues($inputs);
    }

    /**
     * Adds disableValidation on selected fields
     *
     * @param $form  \Symfony\Component\DomCrawler\Form
     */
    private function disableValidationOn($form)
    {
        if (!is_array($this->disabledValidationFields)) {
            return;
        }

        foreach($this->disabledValidationFields as $field)
        {
            $form->get($field)->disableValidation();
        }

        $this->disabledValidationFields = [];
    }
}

You can then use :

$this->forceSelect('value', 'field'); 

in your tests.

Xavier

Was this page helpful?
0 / 5 - 0 ratings

Related issues

YannPl picture YannPl  路  3Comments

kerbylav picture kerbylav  路  3Comments

klimentLambevski picture klimentLambevski  路  3Comments

gabriellimo picture gabriellimo  路  3Comments

shopblocks picture shopblocks  路  3Comments