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

Resubmit PR 249 #347

Merged
merged 3 commits into from Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions Changes
@@ -1,6 +1,12 @@
Revision history for WWW::Mechanize

{{$NEXT}}
[FIXED]
- File upload fields now correctly handle overwriting the file name and
passing in content without a real file (GH#249) (Gil Magno and Julien
Fiegehenn)
- HTML::Form bumped to 6.08 (GH#347) (Julien Fiegehenn)

[ENHANCEMENTS]
- Add autocheck() to enable or disable autochecking at run time in
addition to setting it at object creation (GH#232) (Julien Fiegehenn)
Expand Down
1 change: 1 addition & 0 deletions dist.ini
Expand Up @@ -24,6 +24,7 @@ dir = script

[Prereqs / RuntimeRequires]
perl = 5.006
HTML::Form = 6.08
Scalar::Util = 1.14

[Prereqs / TestRequires]
Expand Down
64 changes: 59 additions & 5 deletions lib/WWW/Mechanize.pm
Expand Up @@ -1901,12 +1901,33 @@ These methods allow you to set the values of fields in a given form.

=head2 $mech->field( $name, \@values, $number )

=head2 $mech->field( $name, \@file_upload_values, $number )

Given the name of a field, set its value to the value specified.
This applies to the current form (as set by the
C<L<< form_name()|/"$mech->form_name( $name [, \%args ] )" >>> or
C<L<< form_number()|/"$mech->form_number($number)" >>>
method or defaulting to the first form on the page).

If the field is of type "file", its value should be an arrayref. Example:

$mech->field( $file_input, ['/tmp/file.txt'] );

Value examples for "file" inputs, followed by explanation of what each
index mean:

# 0: filepath 1: filename 3: headers
['/tmp/file.txt']
['/tmp/file.txt', 'filename.txt']
['/tmp/file.txt', 'filename.txt', @headers]
['/tmp/file.txt', 'filename.txt', Content => 'some content']
[undef, 'filename.txt', Content => 'content here']

Index 0 is the I<filepath> that will be read from disk. Index 1 is the
filename which will be used in the HTTP request body; if not given,
filepath (index 0) is used instead. If C<<Content => 'content here'>> is
used as shown, then I<filepath> will be ignored.

The optional C<$number> parameter is used to distinguish between two fields
with the same name. The fields are numbered from 1.

Expand All @@ -1922,7 +1943,16 @@ sub field {
}
else {
if ( ref($value) eq 'ARRAY' ) {
$form->param($name, $value);
my $input = $form->find_input($name);

if ( $input->type eq 'file' ) {
$input->file( shift @$value );
$input->filename( shift @$value );
$input->headers( @$value );
}
else {
$form->param($name, $value);
}
}
else {
$form->value($name => $value);
Expand Down Expand Up @@ -2032,6 +2062,8 @@ sub select {

=head2 $mech->set_fields( $name => \$value_instance_number )

=head2 $mech->set_fields( $name => \@file_upload )

This method sets multiple fields of the current form. It takes a list
of field name and value pairs. If there is more than one field with
the same name, the first one found is set. If you want to select which
Expand All @@ -2041,6 +2073,19 @@ which has the field value and its number as the 2 elements.
# set the second $name field to 'foo'
$mech->set_fields( $name => [ 'foo', 2 ] );

The value of a field of type "file" should be an arrayref as described
in C<L<< field()|$mech->field( $name, $value, $number ) >>>. Examples:

$mech->set_fields( $file_field => ['/tmp/file.txt'] );
$mech->set_fields( $file_field => ['/tmp/file.txt', 'filename.txt'] );

The value for a "file" input can also be an arrayref containing an
arrayref and a number, as documented in
C<L<< submit_form()|$mech->submit_form( ... ) >>>.
The number will be used to find the field in the form. Example:

$mech->set_fields( $file_field => [['/tmp/file.txt'], 1] );

The fields are numbered from 1.

For fields that have a predefined set of values, you may also provide a
Expand All @@ -2065,10 +2110,20 @@ sub set_fields {
FIELD:
for my $field ( keys %fields ) {
my $value = $fields{$field};
my $number = 1;

if ( ref $value eq 'ARRAY' ) {
$form->find_input( $field, undef,
$value->[1])->value($value->[0] );
my $input = $form->find_input($field) or next FIELD;

# Honor &submit_form's documentation, that says that a
# "file" input's value can be in the form of
# "[[$filepath, $filename], 1]".
if (
$input->type ne 'file'
|| ( $input->type eq 'file' && ref( $value->[0] ) eq 'ARRAY' )
) {
( $value, $number ) = ( $value->[0], $value->[1] );
}
}
else {
if ( ref $value eq 'SCALAR' ) {
Expand All @@ -2086,9 +2141,8 @@ sub set_fields {
}
$value = $possible_values[ $$value ];
}

$form->value($field => $value);
}
$self->field($field, $value, $number);
}
}

Expand Down
5 changes: 5 additions & 0 deletions t/file_upload.html
@@ -0,0 +1,5 @@
<form method="post" enctype="multipart/form-data">
<input type="file" name="document">
<input type="text" name="another_field">
<input type="submit">
</form>
112 changes: 112 additions & 0 deletions t/file_upload.t
@@ -0,0 +1,112 @@
use strict;
use warnings;
use Test::More;
use Test::Exception;
use WWW::Mechanize;
use URI::file;

my $file = 't/file_upload.html';
my $filename = 'the_file_upload.html';
my $mc = WWW::Mechanize->new;
my $uri = URI::file->new_abs( 't/file_upload.html' )->as_string;
my ($form, $input, $as_string);

# &field
$mc->get( $uri );
$mc->field( 'document', [$file] );
($form) = $mc->forms;
$as_string = $form->make_request->as_string;
like( $as_string, qr! filename="$file" !x,
q/$mc->field( 'document', [$file] )/ );
like(
$as_string, qr!<form method="post" enctype="multipart/form-data"!,
'... and the file was sent'
);

$mc->get( $uri );
$mc->field( 'document', [$file, $filename] );
($form) = $mc->forms;
like( $form->make_request->as_string, qr! filename="$filename" !x,
q/$mc->field( 'document', [$file, $filename] )/ );

$mc->get( $uri );
$mc->field( 'document', [$file, $filename, Content => 'changed content'] );
($form) = $mc->forms;
$as_string = $form->make_request->as_string;
like( $as_string, qr! filename="$filename" !x,
q/$mc->field( 'document', [$file, $filename, Content => 'changed content'] )/ );
like(
$as_string, qr!changed content!,
'... and the Content header was sent instead of the file'
);


# &set_fields

$mc->get( $uri );
$mc->set_fields( 'document' => [$file] );
($form) = $mc->forms;
$as_string = $form->make_request->as_string;
like( $as_string, qr! filename="$file" !x,
q/$mc->set_fields( 'document', [$file] )/ );
like(
$as_string, qr!<form method="post" enctype="multipart/form-data"!,
'... and the file was sent'
);

$mc->get( $uri );
$mc->set_fields( 'document' => [ $file, $filename ] );
($form) = $mc->forms;
like( $form->make_request->as_string, qr! filename="$filename" !x,
q/$mc->set_fields( 'document' => [ $file, $filename ] )/ );

$mc->get( $uri );
$mc->set_fields( 'document' => [ $file, $filename, Content => 'my content' ] );
($form) = $mc->forms;
$as_string = $form->make_request->as_string;
like( $as_string, qr! filename="$filename" !x,
q/$mc->set_fields( 'document' => [ $file, $filename, Content => 'my content' ] )/ );
like(
$as_string, qr!my content!,
'... and the Content header was sent instead of the file'
);

$mc->get( $uri );
$mc->set_fields( 'document' => [[ $file, $filename ], 1] );
($form) = $mc->forms;
like( $form->make_request->as_string, qr! filename="$filename" !x,
q/$mc->set_fields( 'document' => [[ $file, $filename ], 1] )/ );

$mc->get( $uri );
$mc->set_fields
( 'document' => [[ $file, $filename, Content => 'content' ], 1] );
($form) = $mc->forms;
like( $form->make_request->as_string, qr! filename="$filename" !x,
q/$mc->set_fields( 'document' => [[ $file, $filename, Content => 'content' ], 1] )/ );

$mc->get( $uri );
$mc->set_fields
( 'document' => [[ undef, $filename, Content => 'content' ], 1] );
($form) = $mc->forms;
$as_string = $form->make_request->as_string;
like( $as_string, qr! filename="$filename" !x,
q/$mc->set_fields( 'document' => [[ undef, $filename, Content => 'content' ], 1] )/ );

# &set_fields with multiple fields
$mc->get( $uri );
$mc->set_fields( 'another_field' => 'foo', 'document' => [ $file, $filename ] );
($form) = $mc->forms;
like( $form->make_request->as_string, qr! filename="$filename" !x,
q/$mc->set_fields( 'another_field' => 'foo', 'document' => [ $file, $filename ] )/ );


# field does not exist
$mc->get( $uri );
lives_ok { $mc->set_fields( 'does_not_exist' => [ [$file], 1 ] ) }
'setting a field that does not exist lives';
($form) = $mc->forms;
$as_string = $form->make_request->as_string;
unlike( $as_string, qr! filename="$file" !x,
q/$mc->set_fields( 'does_not_exist' => [ [$file], 1 ] )/ );

done_testing;