Bug 17600: Remove wrong C4::Context imports
[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
22 use C4::Context;
23
24 use base qw(Class::Accessor);
25 __PACKAGE__->mk_accessors(qw(login password base_url opac_base_url selenium_addr selenium_port driver));
26
27 sub capture {
28     my ( $class, $driver ) = @_;
29
30     $driver->capture_screenshot('selenium_failure.png');
31
32 }
33
34 sub new {
35     my ( $class, $params ) = @_;
36     my $self   = {};
37     my $config = $class->config;
38     $self->{login}    = $params->{login}    || $config->{login};
39     $self->{password} = $params->{password} || $config->{password};
40     $self->{base_url} = $params->{base_url} || $config->{base_url};
41     $self->{opac_base_url} = $params->{opac_base_url} || $config->{opac_base_url};
42     $self->{selenium_addr} = $params->{selenium_addr} || $config->{selenium_addr};
43     $self->{selenium_port} = $params->{selenium_port} || $config->{selenium_port};
44     $self->{driver} = Selenium::Remote::Driver->new(
45         port               => $self->{selenium_port},
46         remote_server_addr => $self->{selenium_addr},
47     );
48     bless $self, $class;
49     $self->add_error_handler;
50     $self->driver->set_implicit_wait_timeout(5000);
51     return $self;
52 }
53
54 sub add_error_handler {
55     my ( $self ) = @_;
56     $self->{driver}->error_handler(
57         sub {
58             my ( $driver, $selenium_error ) = @_;
59             print STDERR "\nSTRACE:";
60             my $i = 1;
61             while ( (my @call_details = (caller($i++))) ){
62                 print STDERR "\t" . $call_details[1]. ":" . $call_details[2] . " in " . $call_details[3]."\n";
63             }
64             print STDERR "\n";
65             $self->capture( $driver );
66             $driver->quit();
67             croak $selenium_error;
68         }
69     );
70 }
71
72 sub remove_error_handler {
73     my ( $self ) = @_;
74     $self->{driver}->error_handler( sub {} );
75 }
76
77 sub config {
78     return {
79         login    => $ENV{KOHA_USER} || 'koha',
80         password => $ENV{KOHA_PASS} || 'koha',
81         base_url => ( $ENV{KOHA_INTRANET_URL} || C4::Context->preference("staffClientBaseURL") ) . "/cgi-bin/koha/",
82         opac_base_url => ( $ENV{KOHA_OPAC_URL} || C4::Context->preference("OPACBaseURL") ) . "/cgi-bin/koha/",
83         selenium_addr => $ENV{SELENIUM_ADDR} || 'localhost',
84         selenium_port => $ENV{SELENIUM_PORT} || 4444,
85     };
86 }
87
88 sub auth {
89     my ( $self, $login, $password ) = @_;
90
91     $login ||= $self->login;
92     $password ||= $self->password;
93     my $mainpage = $self->base_url . 'mainpage.pl';
94
95     $self->driver->get($mainpage);
96     $self->fill_form( { userid => $login, password => $password } );
97     my $login_button = $self->driver->find_element('//input[@id="submit-button"]');
98     $login_button->click();
99 }
100
101 sub opac_auth {
102     my ( $self, $login, $password ) = @_;
103
104     $login ||= $self->login;
105     $password ||= $self->password;
106     my $mainpage = $self->opac_base_url . 'opac-main.pl';
107
108     $self->driver->get($mainpage . q|?logout.x=1|); # Logout before, to make sure we will see the login form
109     $self->driver->get($mainpage);
110     $self->fill_form( { userid => $login, password => $password } );
111     $self->submit_form;
112 }
113
114 sub fill_form {
115     my ( $self, $values ) = @_;
116     while ( my ( $id, $value ) = each %$values ) {
117         my $element = $self->driver->find_element('//*[@id="'.$id.'"]');
118         my $tag = $element->get_tag_name();
119         if ( $tag eq 'input' ) {
120             $self->driver->find_element('//input[@id="'.$id.'"]')->send_keys($value);
121         } elsif ( $tag eq 'select' ) {
122             $self->driver->find_element('//select[@id="'.$id.'"]/option[@value="'.$value.'"]')->click;
123         }
124     }
125 }
126
127 sub submit_form {
128     my ( $self ) = @_;
129
130     my $default_submit_selector = '//fieldset[@class="action"]/input[@type="submit"]';
131     $self->driver->find_element($default_submit_selector)->click
132 }
133
134 sub click {
135     my ( $self, $params ) = @_;
136     my $xpath_selector;
137     if ( exists $params->{main} ) {
138         $xpath_selector = '//div[@id="'.$params->{main}.'"]';
139     } elsif ( exists $params->{main_class} ) {
140         $xpath_selector = '//div[@class="'.$params->{main_class}.'"]';
141     }
142     if ( exists $params->{href} ) {
143         if ( ref( $params->{href} ) ) {
144             for my $k ( keys %{ $params->{href} } ) {
145                 if ( $k eq 'ends-with' ) {
146                     # ends-with version for xpath version 1
147                     my $ends_with = $params->{href}{"ends-with"};
148                     $xpath_selector .= '//a[substring(@href, string-length(@href) - string-length("'.$ends_with.'") + 1 ) = "'.$ends_with.'"]';
149                     # ends-with version for xpath version 2
150                     #$xpath_selector .= '//a[ends-with(@href, "'.$ends_with.'") ]';
151
152             } else {
153                     die "Only ends-with is supported so far ($k)";
154                 }
155             }
156         } else {
157             $xpath_selector .= '//a[contains(@href, "'.$params->{href}.'")]';
158         }
159     }
160     if ( exists $params->{id} ) {
161         $xpath_selector .= '//*[@id="'.$params->{id}.'"]';
162     }
163     $self->driver->find_element($xpath_selector)->click
164 }
165
166 sub wait_for_element_visible {
167     my ( $self, $xpath_selector ) = @_;
168
169     my ($visible, $elt);
170     $self->remove_error_handler;
171     my $max_retries = $self->max_retries;
172     my $i;
173     while ( not $visible ) {
174         $elt = eval {$self->driver->find_element($xpath_selector) };
175         $visible = $elt && $elt->is_displayed;
176         $self->driver->pause(1000) unless $visible;
177
178         die "Cannot wait more for element '$xpath_selector' to be visible"
179             if $max_retries <= ++$i
180     }
181     $self->add_error_handler;
182     return $elt;
183 }
184
185 sub show_all_entries {
186     my ( $self, $xpath_selector ) = @_;
187
188     $self->driver->find_element( $xpath_selector
189           . '//div[@class="dataTables_length"]/label/select/option[@value="-1"]'
190     )->click;
191     my ($all_displayed, $i);
192     my $max_retries = $self->max_retries;
193     while ( not $all_displayed ) {
194         my $dt_infos = $self->driver->get_text(
195             $xpath_selector . '//div[@class="dataTables_info"]' );
196
197         if ( $dt_infos =~ m|Showing 1 to (\d+) of (\d+) entries| ) {
198             $all_displayed = 1 if $1 == $2;
199         }
200
201         $self->driver->pause(1000) unless $all_displayed;
202
203         die "Cannot show all entries from table $xpath_selector"
204             if $max_retries <= ++$i
205     }
206 }
207
208 sub click_when_visible {
209     my ( $self, $xpath_selector ) = @_;
210
211     my $elt = $self->wait_for_element_visible( $xpath_selector );
212
213     my $clicked;
214     $self->remove_error_handler;
215     while ( not $clicked ) {
216         eval { $self->driver->find_element($xpath_selector)->click };
217         $clicked = !$@;
218         $self->driver->pause(1000) unless $clicked;
219     }
220     $self->add_error_handler;
221     $elt->click unless $clicked; # finally Raise the error
222 }
223
224 sub max_retries { 10 }
225
226 =head1 NAME
227
228 t::lib::Selenium - Selenium helper module
229
230 =head1 SYNOPSIS
231
232     my $s = t::lib::Selenium->new;
233     my $driver = $s->driver;
234     my $base_url = $s->base_url;
235     $s->auth;
236     $driver->get($s->base_url . 'mainpage.pl');
237     $s->fill_form({ input_id => 'value' });
238
239 =head1 DESCRIPTION
240
241 The goal of this module is to group the different actions we need
242 when we use automation test using Selenium
243
244 =head1 METHODS
245
246 =head2 new
247
248     my $s = t::lib::Selenium->new;
249
250     Constructor - Returns the object Selenium
251     You can pass login, password, base_url, selenium_addr, selenium_port
252     If not passed, the environment variables will be used
253     KOHA_USER, KOHA_PASS, KOHA_INTRANET_URL, SELENIUM_ADDR SELENIUM_PORT
254     Or koha, koha, syspref staffClientBaseURL, localhost, 4444
255
256 =head2 auth
257
258     $s->auth;
259
260     Will login into Koha.
261
262 =head2 fill_form
263
264     $driver->get($url)
265     $s->fill_form({
266         input_id => 'value',
267         element_id => 'other_value',
268     });
269
270     Will fill the different elements of a form.
271     The keys must be element ids (input and select are supported so far)
272     The values must a string.
273
274 =head2 submit_form
275
276     $s->submit_form;
277
278     It will submit the form using the submit button present in in the fieldset with a clas="action".
279     It should be the default way. If it does not work you should certainly fix the Koha interface.
280
281 =head2 click
282
283     $s->click
284
285     This is a bit dirty for now but will evolve depending on the needs
286     3 parameters possible but only the following 2 forms are used:
287     $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
288     $s->click({ id => 'element_id });
289
290 =head2 click_when_visible
291
292     $c->click_when_visible
293
294     Should always be called to avoid the "An element could not be located on the page" error
295
296 =head2 capture
297     $c->capture
298
299 Capture a screenshot and upload it using the excellent lut.im service provided by framasoft
300 The url of the image will be printed on STDERR (it should be better to return it instead)
301
302 =head2 add_error_handler
303     $c->add_error_handler
304
305 Add our specific error handler to the driver.
306 It will displayed a trace as well as capture a screenshot of the current screen.
307 So only case you should need it is after you called remove_error_handler
308
309 =head2 remove_error_handler
310     $c->remove_error_handler
311
312 Do *not* call this method if you are not aware of what it will do!
313 It will remove any kinds of error raised by the driver.
314 It can be useful in some cases, for instance if you want to make sure something will not happen and that could make the driver exploses otherwise.
315 You certainly should call it for only one statement then must call add_error_handler right after.
316
317 =head1 AUTHORS
318
319 Jonathan Druart <jonathan.druart@bugs.koha-community.org>
320
321 Alex Buckley <alexbuckley@catalyst.net.nz>
322
323 Koha Development Team
324
325 =head1 COPYRIGHT
326
327 Copyright 2017 - Koha Development Team
328
329 =head1 LICENSE
330
331 This file is part of Koha.
332
333 Koha is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
334 the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
335
336 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.
337
338 You should have received a copy of the GNU General Public License along with Koha; if not, see <http://www.gnu.org/licenses>.
339
340 =cut
341
342 1;