Skip to content
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

Setting a radio button's value to an empty string results in the first item's value #26

Open
randyl opened this issue Aug 21, 2020 · 3 comments

Comments

@randyl
Copy link

randyl commented Aug 21, 2020

If a radio button's value is set to an empty string, HTML::Form uses the value of the first item in the radio's menu. Here's a sample program demonstrating the behavior:

#!/usr/bin/env perl

use 5.010;
use strict;
use warnings;
use HTML::Form;

my $form = HTML::Form->parse( <<'HTML', 'https://www.example.com' );
<form>
    <input type="radio" name="foo" value="1">
    <input type="radio" name="foo" value="2">
    <input type="radio" name="foo" value="3">
</form>
HTML

my $input = $form->find_input('foo');
say "initial value='" . ($input->value() // 'undef') . "'";

$input->value('');
say "empty string value='" . ($input->value() // 'undef') . "'";

$input->value(undef);
say "undef value='" . ($input->value() // 'undef') . "'";

# Output:
# initial value='undef'
# empty string value='1'
# undef value='undef'

No matter what the initial value of the radio button is, setting it to an empty string always changes its value to the first item. It's not clear to me if this behavior is a bug, but I did find it surprising.

@petdance
Copy link

Having one of the buttons checked initially doesn't seem to change things:

use 5.010;
use HTML::Form;

my $form = HTML::Form->parse( <<'HTML', 'https://www.example.com' );
<form>
    <input type="radio" name="foo" value="1">
    <input type="radio" name="foo" value="2" checked='checked'>
    <input type="radio" name="foo" value="3">
</form>
HTML

my $input = $form->find_input('foo');
say "initial value='" . $input->value() . "'";

$input->value('');
say "empty string value='" . $input->value() . "'";

$input->value(' ');
say "blank string value='" . $input->value() . "'";

$input->value(undef);
say "undef value='" . $input->value() . "'";

# Output:
# initial value='2'
# empty string value='1'
# blank string value=' '
# undef value=''

@oalders
Copy link
Member

oalders commented Aug 21, 2020

@randyl, thanks for reporting this. We should get this fixed. If you have the capacity right now to fix this, we'd be happy to accept a pull request. If not, that's ok too. :)

@simbabque
Copy link
Contributor

I've investigated this. Internally, each possible value for a HTML::Form::ListInput has a name and a value. We've got the following comment at the top of that class.

#select/option   (val1, val2, ....)
#input/radio     (undef, val1, val2,...)
#input/checkbox  (undef, value)
#select-multiple/option (undef, value)

In <select>s the name refers to the label, i.e. <option value="value">name</option>. But for radios, the name property gets set to the empty string q{} when the form is parsed.

When we set value, it uses the name to compare what we pass into value for each option, so when we pass in an empty string, the first one is a match.

I've turned this into a test.

#!/usr/bin/env perl

use strict;
use warnings;
use HTML::Form;
use Test::More;

subtest 'radiobutons, no initial value' => sub {
    my $form = HTML::Form->parse( <<'HTML', 'https://www.example.com' );
<form>
    <input type="radio" name="foo" value="1">
    <input type="radio" name="foo" value="2">
    <input type="radio" name="foo" value="3">
</form>
HTML

    my $input = $form->find_input('foo');
    is $input->value, undef, 'start with no value';

    $input->value('');
    is $input->value, undef, 'no value when value set to empty string'; # FAIL

    $input->value(undef);
    is $input->value, undef, 'no value when value set to undef';
};

subtest 'radiobuttons, initial value' => sub {
    my $form = HTML::Form->parse( <<'HTML', 'https://www.example.com' );
<form>
    <input type="radio" name="foo" value="1">
    <input type="radio" name="foo" value="2" checked="checked">
    <input type="radio" name="foo" value="3">    
</form>
HTML

    my $input = $form->find_input('foo');
    is $input->value, '2', 'initial value';

    $input->value('');
    is $input->value, undef, 'no value when value set to empty string'; # FAIL

    $input->value(undef);
    is $input->value, undef, 'no value when value set to undef';
};

subtest 'select, no initial value' => sub {
    my $form = HTML::Form->parse( <<'HTML', 'https://www.example.com' );
<form>
    <select name="foo">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="empty string"></option>
</form>
HTML

    my $input = $form->find_input('foo');
    is $input->value, 1, 'start with first';

    $input->value('');
    is $input->value, 'empty string', 'value set to empty string';

    $input->value(undef);
    is $input->value, undef, 'no value when value set to undef';
};

done_testing;

The radio ones fail, like in the two example scripts. There's a third set of tests for the select, which passes. In this case, setting the value to the one that has no label, and therefore an empty string as its name makes sense.

$ prove -lv t/gh-26.t
t/gh-26.t .. 
# Subtest: radiobutons, no initial value
    ok 1 - start with no value
    not ok 2 - no value when value set to empty string

    #   Failed test 'no value when value set to empty string'
    #   at t/gh-26.t line 21.
    #          got: '1'
    #     expected: undef
    ok 3 - no value when value set to undef
    1..3
    # Looks like you failed 1 test of 3.
not ok 1 - radiobutons, no initial value

#   Failed test 'radiobutons, no initial value'
#   at t/gh-26.t line 25.
# Subtest: radiobuttons, initial value
    ok 1 - initial value
    not ok 2 - no value when value set to empty string
    ok 3 - no value when value set to undef

    #   Failed test 'no value when value set to empty string'
    #   at t/gh-26.t line 40.
    #          got: '1'
    #     expected: undef
    1..3
    # Looks like you failed 1 test of 3.
not ok 2 - radiobuttons, initial value

#   Failed test 'radiobuttons, initial value'
#   at t/gh-26.t line 44.
# Subtest: select, no initial value
    ok 1 - start with first
    ok 2 - value set to empty string
    ok 3 - no value when value set to undef
    1..3
ok 3 - select, no initial value
1..3
# Looks like you failed 2 tests of 3.
Dubious, test returned 2 (wstat 512, 0x200)
Failed 2/3 subtests 

Test Summary Report
-------------------
t/gh-26.t (Wstat: 512 Tests: 3 Failed: 2)
  Failed tests:  1-2
  Non-zero exit status: 2
Files=1, Tests=3,  0 wallclock secs ( 0.02 usr  0.00 sys +  0.06 cusr  0.01 csys =  0.09 CPU)
Result: FAIL

I am not sure what the expected behaviour should be here, and if we should really fix it. It's been like this for probably over 20 years. What are we going to break by making it behave like when we pass undef, for checkboxes only?

Maybe we should just document this behaviour.

(Please note I've not tried it for checkboxes.)

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

No branches or pull requests

4 participants