Bug 21393: Move missing filters code to a module
[koha.git] / t / lib / Selenium.pm
1 package t::lib::Selenium;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
19 use Modern::Perl;
20 use Carp qw( croak );
21 use JSON qw( from_json );
22
23 use C4::Context;
24
25 use base qw(Class::Accessor);
26 __PACKAGE__->mk_accessors(qw(login password base_url opac_base_url selenium_addr selenium_port driver));
27
28 sub capture {
29     my ( $class, $driver ) = @_;
30
31     my $lutim_server = q|https://framapic.org|; # Thanks Framasoft!
32     $driver->capture_screenshot('selenium_failure.png');
33     my $from_json = from_json qx{curl -s -F "format=json" -F "file=\@selenium_failure.png" -F "delete-day=1" $lutim_server};
34     if ( $from_json ) {
35         print STDERR "\nSCREENSHOT: $lutim_server/" . $from_json->{msg}->{short} . "\n";
36     }
37 }
38
39 sub new {
40     my ( $class, $params ) = @_;
41     my $self   = {};
42     my $config = $class->config;
43     $self->{login}    = $params->{login}    || $config->{login};
44     $self->{password} = $params->{password} || $config->{password};
45     $self->{base_url} = $params->{base_url} || $config->{base_url};
46     $self->{opac_base_url} = $params->{opac_base_url} || $config->{opac_base_url};
47     $self->{selenium_addr} = $params->{selenium_addr} || $config->{selenium_addr};
48     $self->{selenium_port} = $params->{selenium_port} || $config->{selenium_port};
49     $self->{driver} = Selenium::Remote::Driver->new(
50         port               => $self->{selenium_port},
51         remote_server_addr => $self->{selenium_addr},
52         error_handler => sub {
53             my ( $driver, $selenium_error ) = @_;
54             print STDERR "\nSTRACE:";
55             my $i = 1;
56             while ( (my @call_details = (caller($i++))) ){
57                 print STDERR "\t" . $call_details[1]. ":" . $call_details[2] . " in " . $call_details[3]."\n";
58             }
59             print STDERR "\n";
60             $class->capture( $driver );
61             croak $selenium_error;
62         }
63     );
64     return bless $self, $class;
65 }
66
67 sub config {
68     return {
69         login    => $ENV{KOHA_USER} || 'koha',
70         password => $ENV{KOHA_PASS} || 'koha',
71         base_url => ( $ENV{KOHA_INTRANET_URL} || C4::Context->preference("staffClientBaseURL") ) . "/cgi-bin/koha/",
72         opac_base_url => ( $ENV{KOHA_OPAC_URL} || C4::Context->preference("OPACBaseURL") ) . "/cgi-bin/koha/",
73         selenium_addr => $ENV{SELENIUM_ADDR} || 'localhost',
74         selenium_port => $ENV{SELENIUM_PORT} || 4444,
75     };
76 }
77
78 sub auth {
79     my ( $self, $login, $password ) = @_;
80
81     $login ||= $self->login;
82     $password ||= $self->password;
83     my $mainpage = $self->base_url . 'mainpage.pl';
84
85     $self->driver->get($mainpage);
86     $self->fill_form( { userid => $login, password => $password } );
87     my $login_button = $self->driver->find_element('//input[@id="submit"]');
88     $login_button->submit();
89 }
90
91 sub opac_auth {
92     my ( $self, $login, $password ) = @_;
93
94     $login ||= $self->login;
95     $password ||= $self->password;
96     my $mainpage = $self->opac_base_url . 'opac-main.pl';
97
98     $self->driver->get($mainpage);
99     $self->fill_form( { userid => $login, password => $password } );
100     $self->submit_form;
101 }
102
103 sub fill_form {
104     my ( $self, $values ) = @_;
105     while ( my ( $id, $value ) = each %$values ) {
106         my $element = $self->driver->find_element('//*[@id="'.$id.'"]');
107         my $tag = $element->get_tag_name();
108         if ( $tag eq 'input' ) {
109             $self->driver->find_element('//input[@id="'.$id.'"]')->send_keys($value);
110         } elsif ( $tag eq 'select' ) {
111             $self->driver->find_element('//select[@id="'.$id.'"]/option[@value="'.$value.'"]')->click;
112         }
113     }
114 }
115
116 sub submit_form {
117     my ( $self ) = @_;
118
119     my $default_submit_selector = '//fieldset[@class="action"]/input[@type="submit"]';
120     $self->click_when_visible( $default_submit_selector );
121 }
122
123 sub click {
124     my ( $self, $params ) = @_;
125     my $xpath_selector;
126     if ( exists $params->{main} ) {
127         $xpath_selector = '//div[@id="'.$params->{main}.'"]';
128     } elsif ( exists $params->{main_class} ) {
129         $xpath_selector = '//div[@class="'.$params->{main_class}.'"]';
130     }
131     if ( exists $params->{href} ) {
132         if ( ref( $params->{href} ) ) {
133             for my $k ( keys %{ $params->{href} } ) {
134                 if ( $k eq 'ends-with' ) {
135                     # ends-with version for xpath version 1
136                     my $ends_with = $params->{href}{"ends-with"};
137                     $xpath_selector .= '//a[substring(@href, string-length(@href) - string-length("'.$ends_with.'") + 1 ) = "'.$ends_with.'"]';
138                     # ends-with version for xpath version 2
139                     #$xpath_selector .= '//a[ends-with(@href, "'.$ends_with.'") ]';
140
141             } else {
142                     die "Only ends-with is supported so far ($k)";
143                 }
144             }
145         } else {
146             $xpath_selector .= '//a[contains(@href, "'.$params->{href}.'")]';
147         }
148     }
149     if ( exists $params->{id} ) {
150         $xpath_selector .= '//*[@id="'.$params->{id}.'"]';
151     }
152     $self->click_when_visible( $xpath_selector );
153 }
154
155 sub click_when_visible {
156     my ( $self, $xpath_selector ) = @_;
157     $self->driver->set_implicit_wait_timeout(20000);
158     my ($visible, $elt);
159     while ( not $visible ) {
160         $elt = $self->driver->find_element($xpath_selector);
161         $visible = $elt->is_displayed;
162         $self->driver->pause(1000) unless $visible;
163     }
164     $elt->click;
165 }
166
167 =head1 NAME
168
169 t::lib::Selenium - Selenium helper module
170
171 =head1 SYNOPSIS
172
173     my $s = t::lib::Selenium->new;
174     my $driver = $s->driver;
175     my $base_url = $s->base_url;
176     $s->auth;
177     $driver->get($s->base_url . 'mainpage.pl');
178     $s->fill_form({ input_id => 'value' });
179
180 =head1 DESCRIPTION
181
182 The goal of this module is to group the different actions we need
183 when we use automation test using Selenium
184
185 =head1 METHODS
186
187 =head2 new
188
189     my $s = t::lib::Selenium->new;
190
191     Constructor - Returns the object Selenium
192     You can pass login, password, base_url, selenium_addr, selenium_port
193     If not passed, the environment variables will be used
194     KOHA_USER, KOHA_PASS, KOHA_INTRANET_URL, SELENIUM_ADDR SELENIUM_PORT
195     Or koha, koha, syspref staffClientBaseURL, localhost, 4444
196
197 =head2 auth
198
199     $s->auth;
200
201     Will login into Koha.
202
203 =head2 fill_form
204
205     $driver->get($url)
206     $s->fill_form({
207         input_id => 'value',
208         element_id => 'other_value',
209     });
210
211     Will fill the different elements of a form.
212     The keys must be element ids (input and select are supported so far)
213     The values must a string.
214
215 =head2 submit_form
216
217     $s->submit_form;
218
219     It will submit the form using the submit button present in in the fieldset with a clas="action".
220     It should be the default way. If it does not work you should certainly fix the Koha interface.
221
222 =head2 click
223
224     $s->click
225
226     This is a bit dirty for now but will evolve depending on the needs
227     3 parameters possible but only the following 2 forms are used:
228     $s->click({ href => '/module/script.pl?foo=bar', main => 'doc3' }); # Sometimes we have doc or doc3. To make sure we are not going to hit a link in the header
229     $s->click({ id => 'element_id });
230
231 =head2 click_when_visible
232
233     $c->click_when_visible
234
235     Should always be called to avoid the "An element could not be located on the page" error
236
237 =head2 capture
238     $c->capture
239
240 Capture a screenshot and upload it using the excellent lut.im service provided by framasoft
241 The url of the image will be printed on STDERR (it should be better to return it instead)
242
243 =head1 AUTHORS
244
245 Jonathan Druart <jonathan.druart@bugs.koha-community.org>
246
247 Alex Buckley <alexbuckley@catalyst.net.nz>
248
249 Koha Development Team
250
251 =head1 COPYRIGHT
252
253 Copyright 2017 - Koha Development Team
254
255 =head1 LICENSE
256
257 This file is part of Koha.
258
259 Koha is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
260 the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
261
262 Koha is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
263
264 You should have received a copy of the GNU General Public License along with Koha; if not, see <http://www.gnu.org/licenses>.
265
266 =cut
267
268 1;