From 07b75d88a1c625cce8db384f967ce5717394f5d4 Mon Sep 17 00:00:00 2001 From: Olli-Antti Kivilahti Date: Fri, 10 Jul 2015 11:06:53 +0000 Subject: [PATCH] Bug 14536 - PageObject-pattern base implementation This patch introduces: -PageObject base classes for OPAC and Intra, and example tests. More example tests in Buugg 14540, where setting test context is easier. -a interactive debug script to drive PageObjects more easily. ------------------ PageObjects are used to make robust and reusable integration test components to test various front-end features. PageObjects load a Selenium::Remote::Driver implementation, phantomjs by default and use this to do scripted user actions in the browser, eg. clicking HTML-elements, accepting popup dialogs, entering text to input fields. PageObjects encapsulate those very low-level operations into clear and easily usable actions or services, like doPasswordLogin(). PageObjects also seamlessly deal with navigation from one page to another, eg. my $mainpage = t::lib::Page::Mainpage->new(); $mainpage->doPasswordLogin('admin', '1234')->gotoPatrons()-> searchPatrons({keywordSearch => "Jane Doe"}); PageObjects make doing integration tests so great! You can google for the specifics of PageObject patterns and there are a ton of great tutorials explaining their function. Now PageObjects also have a debugging system where you can get the server response in a title+cookies+html-dump and a screenshot of the browser session. see t::lib::Page for more info! --------------------- Interactive debugger at misc/devel/interactiveWebDriverShell.pl loads the desired PageObject with the needed parameters to the debugger context and makes dynamic UserAgent scripting easy and fun! ----------------------- Bug 14536: (follow-up) Add more functionalty for handling messaging preferences - Fixes memberentry form submit to return Circulation PageObject. - Prevents missing element errors at different steps of memberentry page. Patron modification has multiple different steps (full edit, information edit, sms number edit, library use edit, alternate address edit, alternative contact edit). Some of the input fields required for validation are not always present in each step of these modifications. - Adds functionality to check checkboxes and validate if they are checked or not - Adds navigation to moremember edit links. - Adds navigation to opac-messaging --- misc/devel/interactiveWebDriverShell.pl | 196 +++++++++++++++++++++ t/db_dependent/Koha/Auth.t | 72 ++++++++ t/lib/Page.pm | 294 ++++++++++++++++++++++++++++++++ t/lib/Page/Catalogue/Detail.pm | 150 ++++++++++++++++ t/lib/Page/Catalogue/Search.pm | 107 ++++++++++++ t/lib/Page/Catalogue/Toolbar.pm | 136 +++++++++++++++ t/lib/Page/Circulation/Circulation.pm | 76 +++++++++ t/lib/Page/Intra.pm | 208 ++++++++++++++++++++++ t/lib/Page/Mainpage.pm | 64 +++++++ t/lib/Page/Members/ApiKeys.pm | 200 ++++++++++++++++++++++ t/lib/Page/Members/LeftNavigation.pm | 116 +++++++++++++ t/lib/Page/Members/MemberFlags.pm | 143 ++++++++++++++++ t/lib/Page/Members/Memberentry.pm | 261 ++++++++++++++++++++++++++++ t/lib/Page/Members/Moremember.pm | 207 ++++++++++++++++++++++ t/lib/Page/Members/Toolbar.pm | 144 ++++++++++++++++ t/lib/Page/Opac.pm | 223 ++++++++++++++++++++++++ t/lib/Page/Opac/LeftNavigation.pm | 149 ++++++++++++++++ t/lib/Page/Opac/OpacApiKeys.pm | 170 ++++++++++++++++++ t/lib/Page/Opac/OpacMain.pm | 125 ++++++++++++++ t/lib/Page/Opac/OpacMemberentry.pm | 161 +++++++++++++++++ t/lib/Page/Opac/OpacMessaging.pm | 101 +++++++++++ t/lib/Page/Opac/OpacSearch.pm | 142 +++++++++++++++ t/lib/Page/Opac/OpacSearchHistory.pm | 121 +++++++++++++ t/lib/Page/Opac/OpacUser.pm | 64 +++++++ t/lib/Page/PageUtils.pm | 69 ++++++++ 25 files changed, 3699 insertions(+) create mode 100755 misc/devel/interactiveWebDriverShell.pl create mode 100644 t/db_dependent/Koha/Auth.t create mode 100644 t/lib/Page.pm create mode 100644 t/lib/Page/Catalogue/Detail.pm create mode 100644 t/lib/Page/Catalogue/Search.pm create mode 100644 t/lib/Page/Catalogue/Toolbar.pm create mode 100644 t/lib/Page/Circulation/Circulation.pm create mode 100644 t/lib/Page/Intra.pm create mode 100644 t/lib/Page/Mainpage.pm create mode 100644 t/lib/Page/Members/ApiKeys.pm create mode 100644 t/lib/Page/Members/LeftNavigation.pm create mode 100644 t/lib/Page/Members/MemberFlags.pm create mode 100644 t/lib/Page/Members/Memberentry.pm create mode 100644 t/lib/Page/Members/Moremember.pm create mode 100644 t/lib/Page/Members/Toolbar.pm create mode 100644 t/lib/Page/Opac.pm create mode 100644 t/lib/Page/Opac/LeftNavigation.pm create mode 100644 t/lib/Page/Opac/OpacApiKeys.pm create mode 100644 t/lib/Page/Opac/OpacMain.pm create mode 100644 t/lib/Page/Opac/OpacMemberentry.pm create mode 100644 t/lib/Page/Opac/OpacMessaging.pm create mode 100644 t/lib/Page/Opac/OpacSearch.pm create mode 100644 t/lib/Page/Opac/OpacSearchHistory.pm create mode 100644 t/lib/Page/Opac/OpacUser.pm create mode 100644 t/lib/Page/PageUtils.pm diff --git a/misc/devel/interactiveWebDriverShell.pl b/misc/devel/interactiveWebDriverShell.pl new file mode 100755 index 0000000..72d4df9 --- /dev/null +++ b/misc/devel/interactiveWebDriverShell.pl @@ -0,0 +1,196 @@ +#!/usr/bin/perl -d + +# Copyright 2015 KohaSuomi +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# 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. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, see . + +=head1 NAME + +interactiveWebDriverShell.pl + +=head1 SYNOPSIS + + misc/devel/interactiveWebDriverShell.pl -p mainpage.pl + +Prepares a perl debugger session with the requested PageObject loaded. +Then you can easily guide the UserAgent through the web session. + +=cut + +use Modern::Perl; + +use Getopt::Long qw(:config no_ignore_case); +use Data::Dumper; + +my ($help, $page, $list, @params, @login); + +GetOptions( + "h|help" => \$help, + "p|page=s" => \$page, + "P|params=s{,}" => \@params, + "L|login=s{,}" => \@login, + "l|list" => \$list, +); + +my $help_msg = < c + +The PageObject is bound to variable \$po, +and the Selenium::Remote::Driver-implementation to \$d. +Then all you need to do is start navigating the web! + DB<2> \$po->isPasswordLoginAvailable()->doPasswordLogin('admin','1234'); + + DB<3> \$ele = \$d->find_element('input[value="Save"]'); + +Note! Do not use "my \$ele = 123;" in the debugger session, because that doesn't +work as excepted, simply use "\$ele = 123;". + +HELP + +if ($help) { + print $help_msg; + exit; +} +unless ($page || $list) { + print $help_msg; + exit; +} + +my $supportedPageObjects = { +################################################################################ + ########## STAFF CONFIGURATIONS ########## +################################################################################ + 'mainpage.pl' => + { package => "t::lib::Page::Mainpage", + urlEndpoint => "mainpage.pl", + status => "OK", + params => "none", + }, + "members/moremember.pl" => + { package => "t::lib::Page::Members::Moremember", + urlEndpoint => "members/moremember.pl", + status => "not implemented", + params => ["borrowernumber"], + }, + "members/member-flags.pl" => + { package => "t::lib::Page::Members::MemberFlags", + urlEndpoint => "members/member-flags.pl", + status => "not implemented", + params => ["borrowernumber"], + }, + "catalogue/detail.pl" => + { package => "t::lib::Page::Catalogue::Detail", + urlEndpoint => "catalogue/detail.pl", + status => "OK", + params => ["biblionumber"], + }, +################################################################################ + ########## OPAC CONFIGURATIONS ########## +################################################################################ + "opac/opac-main.pl" => + { package => "t::lib::Page::Opac::OpacMain", + urlEndpoint => "opac/opac-main.pl", + status => "OK", + }, +}; +################################################################################ + ########## END OF PAGE CONFIGURATIONS ########## +################################################################################ + +listSupportedPageObjects ($supportedPageObjects) if $list; +my ($po, $d) = deployPageObject($supportedPageObjects, $page, \@params, \@login) if $page; + + + +print "--Debugging--\n"; +$DB::single = 1; #Breakpoint here +$DB::single = 1; + + + +sub listSupportedPageObjects { + my ($supportedPageObjects) = @_; + print Data::Dumper::Dumper($supportedPageObjects); + exit; +} +sub deployPageObject { + my ($supportedPageObjects, $page, $params, $login) = @_; + + ##Find correct PageObject deployment rules + my $pageObjectMapping = $supportedPageObjects->{$page}; + die "No PageObject mapped to --page '$page'. See --list to list available PageObjects.\n" unless $pageObjectMapping; + + ##Dynamically load package + my $package = $pageObjectMapping->{package}; + eval "require $package"; + + ##Fill required parameters + my $poParams = {}; + if (ref($pageObjectMapping->{params}) eq 'ARRAY') { + foreach my $paramName (@{$pageObjectMapping->{params}}) { + $poParams->{$paramName} = shift(@$params); + die "Insufficient parameters given, parameter '$paramName' unsatisfied.\n" unless $poParams->{$paramName}; + } + } + + ##Check if the status is OK + die "PageObject status for '$page' is not 'OK'. Current status '".$pageObjectMapping->{status}."'.\nPlease implement the missing PageObject.\n" unless $pageObjectMapping->{status} eq 'OK'; + + ##Create PageObject + my $po = $package->new($poParams); + + ##Password login if desired + eval { + $po->isPasswordLoginAvailable->doPasswordLogin($login->[0], $login->[1]) if scalar(@$login); + }; if ($@) { + print "Password login unavailable.\n"; + } + + return ($po, $po->getDriver()); +} diff --git a/t/db_dependent/Koha/Auth.t b/t/db_dependent/Koha/Auth.t new file mode 100644 index 0000000..441070d --- /dev/null +++ b/t/db_dependent/Koha/Auth.t @@ -0,0 +1,72 @@ +#!/usr/bin/env perl + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::More; +use Try::Tiny; #Even Selenium::Remote::Driver uses Try::Tiny :) + +use t::lib::Page::Mainpage; + +use t::lib::TestObjects::BorrowerFactory; + +##Setting up the test context +my $testContext = {}; + +my $password = '1234'; +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + flags => '1', #superlibrarian, not exactly a very good way of doing permission testing? + userid => 'mini_admin', + password => $password, + }, + ], undef, $testContext); + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context + + testPasswordLogin(); + +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +tearDown(); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} + +###################################################### + ### STARTING TEST IMPLEMENTATIONS ### +###################################################### + +sub testPasswordLogin { + my $mainpage = t::lib::Page::Mainpage->new(); + $mainpage->isPasswordLoginAvailable()->doPasswordLogin($borrowers->{'1A01'}->userid(), $password)->quit(); +} \ No newline at end of file diff --git a/t/lib/Page.pm b/t/lib/Page.pm new file mode 100644 index 0000000..595eab0 --- /dev/null +++ b/t/lib/Page.pm @@ -0,0 +1,294 @@ +package t::lib::Page; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use C4::Context; + +use t::lib::WebDriverFactory; + +use Koha::Exception::BadParameter; +use Koha::Exception::SystemCall; + +=head NAME t::lib::Page + +=head SYNOPSIS + +PageObject-pattern parent class. Extend this to implement specific pages shown to our users. + +PageObjects are used to make robust and reusable integration test components to test +various front-end features. PageObjects load a Selenium::Remote::Driver implementation, +phantomjs by default and use this to do scripted user actions in the browser, +eg. clicking HTML-elements, accepting popup dialogs, entering text to input fields. + +PageObjects encapsulate those very low-level operations into clear and easily usable +actions or services, like doPasswordLogin(). +PageObjects also seamlessly deal with navigation from one page to another, eg. + my $mainpage = t::lib::Page::Mainpage->new(); + $mainpage->doPasswordLogin('admin', '1234')->gotoPatrons()-> + searchPatrons({keywordSearch => "Jane Doe"}); + +=head Class variables + +Selenium::Remote::Driver driver, contains the driver implementation used to run these tests +t::Page::Common::Header header, the header page component (not implemented) +t::Page::Common::Footer footer, the footer page component (not implemented) +Scalar userInteractionDelay, How many milliseconds to wait for javascript + to stop processing by default after user actions? + +=head DEBUGGING + +Set Environment value + $ENV{KOHA_PAGEOBJECT_DEBUG} = 1; +Before creating the first PageObject to enable debugging. +Debugging output is written to /tmp/PageObjectDebug/ by default, but you can change it +using the same environment variable + $ENV{KOHA_PAGEOBJECT_DEBUG} = "/tmp/generalDebugging/"; + +=cut + +sub new { + my ($class, $params) = @_; + $params = _mergeDefaultConfig($params); + + my $self = {}; + bless($self, $class); + unless ($params->{driver}) { + my ($driver) = t::lib::WebDriverFactory::getUserAgentDrivers({phantomjs => $params}); + $self->{driver} = $driver; + } + $self->{type} = $params->{type}; #This parameter is mandatory. _mergeDefaultConfig() dies without it. + $self->{resource} = $params->{resource} || '/'; + $self->{resource} .= "?".join('&', @{$params->{getParams}}) if $params->{getParams}; + $self->{header} = $params->{header} || undef; + $self->{footer} = $params->{footer} || undef; + + $self->{userInteractionDelay} = $params->{userInteractionDelay} || 500; + + $self->{driver}->set_window_size(1280, 960); + $self->{driver}->get( $self->{resource} ); + + $self->debugSetEnvironment(); #If debugging is enabled + + return $self; +} + +=head rebrandFromPageObject +When we are getting redirected from one page to another we rebrand the existing PageObject +as another PageObject to have the new page's services available. + +@RETURNS The desired new PageObject Page +=cut + +sub rebrandFromPageObject { + my ($class, $self) = @_; + bless($self, $class); + my $d = $self->getDriver(); + $d->pause(250); #Wait for javascript to load. + $self->debugTakeSessionSnapshot(); + ok(1, "Navigated to $class"); + return $self; +} + +=head _mergeDefaultConfig + +@THROWS Koha::Exception::BadParameter +=cut + +sub _mergeDefaultConfig { + my ($params) = @_; + unless (ref($params) eq 'HASH' && $params->{type}) { + Koha::Exception::BadParameter->throw(error => "t::lib::Page:> When instantiating Page-objects, you must define the 'type'-parameter."); + } + + my $testServerConfigs = C4::Context->config('testservers'); + my $conf = $testServerConfigs->{ $params->{type} }; + Koha::Exception::BadParameter->throw(error => "t::lib::Page:> Unknown 'type'-parameter '".$params->{type}."'. Values 'opac', 'staff' and 'rest' are supported.") + unless $conf; + #Merge given $params-config on top of the $KOHA_CONF's testservers-directives + @$conf{keys %$params} = values %$params; + return $conf; +} + +=head quit +Wrapper for Selenium::Remote::Driver->quit(), +Delete the session & close open browsers. + +When ending this browser session, it is polite to quit, or there is a risk of leaving +floating test browsers floating around. +=cut + +sub quit { + my ($self) = @_; + $self->getDriver()->quit(); +} + +=head pause +Wrapper for Selenium::Remote::Driver->pause(), +=cut + +sub pause { + my ($self, $pauseMillis) = @_; + $self->getDriver()->pause($pauseMillis); + return $self; +} + +=head mockConfirmPopup + +Workaround to a missing feature in PhantomJS v1.9 +Confirm popup's cannot be negotiated with. This preparatory method makes confirm dialogs +always return 'true' or 'false' without showing the actual dialog. + +@PARAM1 Boolean, the confirm popup dialog's return value. + +=cut + +sub mockConfirmPopup { + my ($self, $retVal) = @_; + my $d = $self->getDriver(); + + my $script = q{ + var retVal = (arguments[0] == 1) ? true : false; + var callback = arguments[arguments.length-1]; + window.confirm = function(){return retVal;}; + callback(); + }; + $d->execute_async_script($script, ($retVal ? 1 : 0)); +} + +=head mockPromptPopup + +Workaround to a missing feature in PhantomJS v1.9 +Prompt popup's cannot be negotiated with. This preparatory method makes prompt dialogs +always return 'true' or 'false' without showing the actual dialog. + +@PARAM1 Boolean, the prompt popup dialog's return value. + +=cut + +sub mockPromptPopup { + my ($self, $retVal) = @_; + my $d = $self->getDriver(); + + my $script = q{ + var callback = arguments[arguments.length-1]; + window.prompt = function(){return arguments[0];}; + callback(); + }; + $d->execute_async_script($script, ($retVal ? 1 : 0)); +} + +=head mockAlertPopup + +Workaround to a missing feature in PhantomJS v1.9 +Alert popup's cannot be negotiated with and they freeze PageObject testing. +This preparatory method makes alert dialogs always return NULL without showing +the actual dialog. + +=cut + +sub mockAlertPopup { + my ($self, $retVal) = @_; + my $d = $self->getDriver(); + + my $script = q{ + var callback = arguments[arguments.length-1]; + window.alert = function(){return;}; + callback(); + }; + $d->execute_async_script($script); +} + +################################################ + ## INTRODUCING OBJECT ACCESSORS ## +################################################ +sub setDriver { + my ($self, $driver) = @_; + $self->{driver} = $driver; +} +sub getDriver { + my ($self) = @_; + return $self->{driver}; +} + +################################################ + ## INTRODUCING TESTING HELPERS ## +################################################ +sub debugSetEnvironment { + my ($self) = @_; + if ($ENV{KOHA_PAGEOBJECT_DEBUG}) { + $self->{debugSessionId} = sprintf("%03i",rand(999)); + $self->{debugSessionTmpDirectory} = "/tmp/PageObjectDebug/"; + $self->{debugSessionTmpDirectory} = $ENV{KOHA_PAGEOBJECT_DEBUG} if (not(ref($ENV{KOHA_PAGEOBJECT_DEBUG})) && length($ENV{KOHA_PAGEOBJECT_DEBUG}) > 1); + my $error = system(("mkdir", "-p", $self->{debugSessionTmpDirectory})); + Koha::Exception::SystemCall->throw(error => "Trying to create a temporary directory for PageObject debugging session '".$self->{debugSessionId}."' failed:\n $?") + if $error; + $self->{debugInternalCounter} = 1; + + print "\n\n--Starting PageObject debugging session '".$self->{debugSessionId}."'\n\n"; + } +} + +sub debugTakeSessionSnapshot { + my ($self) = @_; + if ($ENV{KOHA_PAGEOBJECT_DEBUG}) { + my ($actionIdentifier, $actionFile) = $self->_debugGetSessionIdentifier(2); + + $self->_debugWriteHTML($actionIdentifier, $actionFile); + $self->_debugWriteScreenshot($actionIdentifier, $actionFile); + $self->{debugInternalCounter}++; + } +} + +sub _debugGetSessionIdentifier { + my ($self, $callerDepth) = @_; + $callerDepth = $callerDepth || 2; + ##Create a unique and descriptive identifier for this program state. + my ($package, $filename, $line, $subroutine) = caller($callerDepth); #Get where we are called from + $subroutine = $2 if ($subroutine =~ /(::|->)([^:->]+)$/); #Get the last part of the package, the subroutine name. + my $actionIdentifier = "[session '".$self->{debugSessionId}."', counter '".sprintf("%03i",$self->{debugInternalCounter})."', caller '$subroutine']"; + my $actionFile = $self->{debugSessionId}.'_'.sprintf("%03i",$self->{debugInternalCounter}).'_'.$subroutine; + return ($actionIdentifier, $actionFile); +} + +sub _debugWriteHTML { + require Data::Dumper; + my ($self, $actionIdentifier, $actionFile) = @_; + my $d = $self->getDriver(); + + ##Write the current Response data + open(my $fh, ">:encoding(UTF-8)", $self->{debugSessionTmpDirectory}.$actionFile.'.html') + or die "Trying to open a filehandle for PageObject debugging output $actionIdentifier:\n $@"; + print $fh $d->get_title()."\n"; + print $fh "ALL COOKIES DUMP:\n".Data::Dumper::Dumper($d->get_all_cookies()); + print $fh $d->get_page_source()."\n"; + close $fh; +} + +sub _debugWriteScreenshot { + my ($self, $actionIdentifier, $actionFile) = @_; + my $d = $self->getDriver(); + + ##Write a screenshot of the view to file. + my $ok = $d->capture_screenshot($self->{debugSessionTmpDirectory}.$actionFile.'.png'); + Koha::Exception::SystemCall->throw(error => "Cannot capture a screenshot for PageObject $actionIdentifier") + unless $ok; +} +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Catalogue/Detail.pm b/t/lib/Page/Catalogue/Detail.pm new file mode 100644 index 0000000..4372069 --- /dev/null +++ b/t/lib/Page/Catalogue/Detail.pm @@ -0,0 +1,150 @@ +package t::lib::Page::Catalogue::Detail; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::Catalogue::Search; + +use base qw(t::lib::Page::Intra t::lib::Page::Catalogue::Toolbar); + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Catalogue::Detail + +=head SYNOPSIS + +detail.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $detail = t::lib::Page::Catalogue::Detail->new({biblionumber => "1"})->doPasswordLogin('admin', '2134'); + +Instantiates a WebDriver and loads the catalogue/detail.pl to show the given Biblio +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + biblionumber => loads the page to display the Biblio matching the given parameter + +@RETURNS t::lib::Page::Catalogue::Detail, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/catalogue/detail.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{biblionumber}) { + push @{$params->{getParams}}, "biblionumber=".$params->{biblionumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getBiblioMarkers + +@RETURNS HASHRef of Biblio data elements of the displayed Biblio details. + 'title' is guaranteed, others are optional +=cut + +sub _getBiblioMarkers { + my ($self) = @_; + my $d = $self->getDriver(); + + my $e = {}; + $e->{title} = $d->find_element("#catalogue_detail_biblio .title", 'css')->get_text(); #title is mandatory and should always exist to simplify testing. + eval { + $e->{author} = $d->find_element("#catalogue_detail_biblio .author a", 'css')->get_text(); + }; + eval { + $e->{isbn} = $d->find_element("#catalogue_detail_biblio span[property='isbn']", 'css')->get_text(); + }; + return $e; +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head isBiblioMatch + + $detail->isBiblioMatch($record); + +Checks that the loaded Biblio matches the give MARC::Record. +=cut + +sub isBiblioMatch { + my ($self, $record) = @_; + $self->debugTakeSessionSnapshot(); + + my $e = $self->_getBiblioMarkers(); + my $testFail; + if (not($record->title() eq $e->{title})) { + $testFail = 1; + } + if ($record->author() && not($record->author() eq $e->{author})) { + $testFail = 1; + } + + ok(not($testFail), "Biblio '".$record->title()."' matches loaded Biblio"); + return $self; +} + +=head deleteBiblio +Deletes the displayed Biblio + +@RETURNS t::lib::PageObject::Catalogue::Search as the execution moves to that PageObject. +=cut + +sub deleteBiblio { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $e = $self->_getEditDropdownElements(); + $self->mockConfirmPopup('true'); + $e->{deleteRecord}->click(); + + return t::lib::Page::Catalogue::Search->rebrandFromPageObject($self); +} + + +1; #Make the compiler happy! diff --git a/t/lib/Page/Catalogue/Search.pm b/t/lib/Page/Catalogue/Search.pm new file mode 100644 index 0000000..0f25274 --- /dev/null +++ b/t/lib/Page/Catalogue/Search.pm @@ -0,0 +1,107 @@ +package t::lib::Page::Catalogue::Search; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Time::HiRes; +use Test::More; + +use base qw(t::lib::Page::Intra t::lib::Page::Catalogue::Toolbar); + +use Koha::Exception::UnknownObject; + +=head NAME t::lib::Page::Catalogue::Search + +=head SYNOPSIS + +search.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $search = t::lib::Page::Catalogue::Search->new()->doPasswordLogin('admin', '2134'); + +Instantiates a WebDriver and loads the catalogue/search.pl to show the given Biblio +@PARAM1 HASHRef of optional and MANDATORY parameters + +@RETURNS t::lib::Page::Catalogue::Search, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/catalogue/search.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + + my $self = $class->SUPER::new($params); + + return $self; +} + +=head rebrandFromPageObject +@EXTENDS t::lib::Page->rebrandFromPageObject() + +Checks that the rebranding succeeds by making sure that the page we are rebranding to +is the page we have. +=cut + +sub rebrandFromPageObject { + my ($class, $self) = @_; + my $d = $self->getDriver(); + + my $waitCycles = 0; + while ($waitCycles <= 10 && + $d->get_current_url() !~ m!catalogue/search.pl! ) { + Time::HiRes::usleep(250000); + $waitCycles++; + } + + if ($waitCycles > 10) { + my ($package, $filename, $line, $subroutine) = caller(1); + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."->rebrandFromPageObject():> Timeout looking for proper Page markers. This is not a 'catalogue/search.pl'-page!\n Called from $package->$subroutine"); + } + return $class->SUPER::rebrandFromPageObject($self); +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + + + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + + + + + +1; #Make the compiler happy! diff --git a/t/lib/Page/Catalogue/Toolbar.pm b/t/lib/Page/Catalogue/Toolbar.pm new file mode 100644 index 0000000..565869e --- /dev/null +++ b/t/lib/Page/Catalogue/Toolbar.pm @@ -0,0 +1,136 @@ +package t::lib::Page::Catalogue::Toolbar; + +# Copyright 2015 KohaSuomi! +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Catalogue::Toolbar + +=head SYNOPSIS + +PageObject Accessory representing a shared Template between PageObjects. +This encapsulates specific page-related functionality. + +In this case this encapsulates the members-module toolbar's services and provides +a reusable class from all member-module PageObjects. + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getToolbarActionElements +@RETURNS HASHRef of Selenium::Driver::Webelements matching all the clickable elements + in the actions toolbar over the Biblio information. +=cut + +sub _getToolbarActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $e = {}; + eval { + $e->{new} = $d->find_element("#newDropdownContainer button", 'css'); + }; + eval { + $e->{edit} = $d->find_element("#editDropdownContainer button", 'css'); + }; + eval { + $e->{save} = $d->find_element("#saveDropdownContainer button", 'css'); + }; + eval { + $e->{addTo} = $d->find_element("#addtoDropdownContainer button", 'css'); + }; + eval { + $e->{print} = $d->find_element("#printbiblio", 'css'); + }; + eval { + $e->{placeHold} = $d->find_element("#placeholdDropdownContainer button", 'css'); + }; + return $e; +} + +=head _getEditDropdownElements +Clicks the dropdown open if it isnt yet. +@RETURNS HASHRef of all the dropdown elements under the Edit button in the toolbar + over Biblio information. +=cut + +sub _getEditDropdownElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $toolbarElements = $self->_getToolbarActionElements(); + my $editButton = $toolbarElements->{edit}; + my $dropdownElement; + eval { + $dropdownElement = $d->find_child_element($editButton, "#editDropdownContainer ul a:nth-of-type(1)", 'css'); + }; + unless ($dropdownElement && $dropdownElement->is_visible()) { + $editButton->click(); + $self->debugTakeSessionSnapshot(); + } + + my $e = {}; + eval { + $e->{editRecord} = $d->find_element("a[id|='editbiblio']", 'css'); + }; + eval { + $e->{editItems} = $d->find_element("a[id|='edititems']", 'css'); + }; + eval { + $e->{editItemsInBatch} = $d->find_element("a[id|='batchedit']", 'css'); + }; + eval { + $e->{deleteItemsInBatch} = $d->find_element("a[id|='batchdelete']", 'css'); + }; + eval { + $e->{attachItem} = $d->find_element("a[href*='cataloguing/moveitem.pl']", 'css'); + }; + eval { + $e->{editAsNew} = $d->find_element("a[id|='duplicatebiblio']", 'css'); + }; + eval { + $e->{replaceRecord} = $d->find_element("a[id|='z3950copy']", 'css'); + }; + eval { + $e->{deleteRecord} = $d->find_element("a[id|='deletebiblio']", 'css'); + }; + eval { + $e->{deleteAllItems} = $d->find_element("a[id|='deleteallitems']", 'css'); + }; + return $e; +} + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + + + + +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Circulation/Circulation.pm b/t/lib/Page/Circulation/Circulation.pm new file mode 100644 index 0000000..a651a66 --- /dev/null +++ b/t/lib/Page/Circulation/Circulation.pm @@ -0,0 +1,76 @@ +package t::lib::Page::Circulation::Circulation; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar t::lib::Page::Members::LeftNavigation); + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Circulation::Circulation + +=head SYNOPSIS + +circulation.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $circulation = t::lib::Page::Circulation::Circulation->new({borrowernumber => "1"}); + +Instantiates a WebDriver and loads the circ/circulation.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + borrowernumber => loads the page to display Borrower matching the given borrowernumber + +@RETURNS t::lib::Page::Circulation::Circulation, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/circ/circulation.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{borrowernumber}) { + push @{$params->{getParams}}, "borrowernumber=".$params->{borrowernumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +1; \ No newline at end of file diff --git a/t/lib/Page/Intra.pm b/t/lib/Page/Intra.pm new file mode 100644 index 0000000..79636f3 --- /dev/null +++ b/t/lib/Page/Intra.pm @@ -0,0 +1,208 @@ +package t::lib::Page::Intra; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use C4::Context; + +use t::lib::WebDriverFactory; + +use Koha::Exception::BadParameter; +use Koha::Exception::SystemCall; + +use base qw(t::lib::Page); + +=head NAME t::lib::Page::Intra + +=head SYNOPSIS + +PageObject-pattern parent class for Intranet-pages (staff client). Extend this to implement specific pages shown to our users. + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getBreadcrumbLinks + +@RETURNS List of all breadcrumb links +=cut + +sub _getBreadcrumbLinks { + my ($self) = @_; + my $d = $self->getDriver(); + + my $breadcrumbLinks = $d->find_elements("div#breadcrumbs a"); + return ($breadcrumbLinks); +} + +=head _getHeaderElements + +@RETURNS HASHRef of all the Intranet header clickables. +=cut + +sub _getHeaderElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my ($patronsA, $searchA, $cartA, $moreA, $drop3A, $helpA); + #Always visible elements + $patronsA = $d->find_element("#header a[href*='members-home.pl']"); + $searchA = $d->find_element ("#header a[href*='search.pl']"); + $cartA = $d->find_element ("#header a#cartmenulink"); + $moreA = $d->find_element ("#header a[href='#']"); + $drop3A = $d->find_element ("#header a#drop3"); + $helpA = $d->find_element ("#header a#helper"); + + my $e = {}; + $e->{patrons} = $patronsA if $patronsA; + $e->{search} = $searchA if $searchA; + $e->{cart} = $cartA if $cartA; + $e->{more} = $moreA if $moreA; + $e->{drop3} = $drop3A if $drop3A; + $e->{help} = $helpA if $helpA; + return $e; +} + +=head _getPasswordLoginElements + +@RETURNS List of Selenium::Remote::Webelement-objects, + ($submitButton, $useridInput, $passwordInput) +=cut + +sub _getPasswordLoginElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $submitButton = $d->find_element('#submit'); + my $useridInput = $d->find_element('#userid'); + my $passwordInput = $d->find_element('#password'); + return ($submitButton, $useridInput, $passwordInput); +} + +=head _getLoggedInBranchNameElement +@RETURNS Selenium::Remote::WebElement matching the containing the currently logged in users branchname +=cut + +sub _getLoggedInBranchNameElement { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $header = $self->_getHeaderElements(); + my $loggedInBranchNameSpan = $d->find_child_element($header->{drop3}, "#logged-in-branch-name", 'css'); + return $loggedInBranchNameSpan; +} + +=head _getLoggedInBranchCode +@RETURNS String, the logged in branch code +=cut + +sub _getLoggedInBranchCode { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + #Because the branchcode element is hidden, we need to inject some javascript to get its value since Selenium (t$ + my $script = q{ + var elem = document.getElementById('logged-in-branch-code').innerHTML; + var callback = arguments[arguments.length-1]; + callback(elem); + }; + my $loggedInBranchCode = $d->execute_async_script($script); + return $loggedInBranchCode; +} + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head isPasswordLoginAvailable + + $page->isPasswordLoginAvailable(); + +@RETURN t::lib::Page-object +@CROAK if password login is unavailable. +=cut + +sub isPasswordLoginAvailable { + my $self = shift; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + $self->_getPasswordLoginElements(); + ok(($d->get_title() =~ /Log in to Koha/), "Intra PasswordLogin available"); + return $self; +} + +sub doPasswordLogin { + my ($self, $username, $password) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($submitButton, $useridInput, $passwordInput) = $self->_getPasswordLoginElements(); + $useridInput->send_keys($username); + $passwordInput->send_keys($password); + $submitButton->click(); + $self->debugTakeSessionSnapshot(); + + my $cookies = $d->get_all_cookies(); + my @cgisessid = grep {$_->{name} eq 'CGISESSID'} @$cookies; + + ok(($d->get_title() !~ /Log in to Koha/ && #No longer in the login page + $d->get_title() !~ /Access denied/ && + $cgisessid[0]) #Cookie CGISESSID defined! + , "Intra PasswordLogin succeeded"); + + return $self; #After a succesfull password login, we are directed to the same page we tried to access. +} + +sub doPasswordLogout { + my ($self, $username, $password) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + #Click the dropdown menu to make the logout-link visible + my $logged_in_identifierA = $d->find_element('#drop3'); #What a nice and descriptive HTML element name! + $logged_in_identifierA->click(); + + #Logout + my $logoutA = $d->find_element('#logout'); + $logoutA->click(); + $self->debugTakeSessionSnapshot(); + + ok(($d->get_title() =~ /Log in to Koha/), "Intra PasswordLogout succeeded"); + return $self; #After a succesfull password logout, we are still in the same page we did before logout. +} + +sub isLoggedInBranchCode { + my ($self, $expectedBranchCode) = @_; + + my $loggedInBranchCode = $self->_getLoggedInBranchCode(); + is($expectedBranchCode, $loggedInBranchCode, "#logged-in-branch-code '".$loggedInBranchCode."' matches '$expectedBranchCode'"); + return $self; +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Mainpage.pm b/t/lib/Page/Mainpage.pm new file mode 100644 index 0000000..99efafb --- /dev/null +++ b/t/lib/Page/Mainpage.pm @@ -0,0 +1,64 @@ +package t::lib::Page::Mainpage; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use base qw(t::lib::Page::Intra); + +=head NAME t::lib::Page::Mainpage + +=head SYNOPSIS + +Mainpage PageObject providing page functionality as a service! + +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH') { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/mainpage.pl'; + $params->{type} = 'staff'; + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + + + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + + + + + +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Members/ApiKeys.pm b/t/lib/Page/Members/ApiKeys.pm new file mode 100644 index 0000000..070cc08 --- /dev/null +++ b/t/lib/Page/Members/ApiKeys.pm @@ -0,0 +1,200 @@ +package t::lib::Page::Members::ApiKeys; + +# Copyright 2015 KohaSuomi! +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar); + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +=head NAME t::lib::Page::Members::ApiKeys + +=head SYNOPSIS + +apikeys.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $apikeys = t::lib::Page::Members::ApiKeys->new({borrowernumber => "1"}); + +Instantiates a WebDriver and loads the members/apikeys.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + borrowernumber => loads the page to display Borrower matching the given borrowernumber + +@RETURNS t::lib::Page::Members::ApiKeys, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/members/apikeys.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{borrowernumber}) { + push @{$params->{getParams}}, "borrowernumber=".$params->{borrowernumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getActionsAndTableElements +@RETURNS List of + HASHRef of Selenium::Driver::Webelement-objects matching the generic + actions on this page, eg. 'generateNewKey'. + HASHRef of Selenium::Driver::Webelement-objects keyed with the apiKey hash/text. + These are all the apiKey table rows present, and have the + elements prefetched for easy access. + +=cut + +sub _getActionsAndTableElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $generateNewKeySubmit = $d->find_element("#generatenewkey", 'css'); + + my $a = {}; #Collect action elements here + $a->{generateNewKey} = $generateNewKeySubmit; #Bind the global action here for easy reference. + + my $apiKeyRows; + eval { #We might not have ApiKeys yet. + $apiKeyRows = $d->find_elements("#apikeystable tr", 'css'); + shift @$apiKeyRows; #Remove the table header row + }; + my %apiKeys; + for(my $i=0 ; $i[$i]; + $row->{'nth-of-type'} = $i+1; #starts from 1 + $row->{key} = $d->find_child_element($row, "td.apikeykey", 'css')->get_text(); + $row->{active} = $d->find_child_element($row, "td.apikeyactive", 'css')->get_text(); + $row->{lastTransaction} = $d->find_child_element($row, "td.apikeylastransaction", 'css')->get_text(); + $row->{delete} = $d->find_child_element($row, "input.apikeydelete", 'css'); + eval { + $row->{revoke} = $d->find_child_element($row, "input.apikeyrevoke", 'css'); + }; + eval { + $row->{activate} = $d->find_child_element($row, "input.apikeyactivate", 'css'); + }; + $apiKeys{$row->{key}} = $row; + } + + return ($a, \%apiKeys); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub generateNewApiKey { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + $actionElements->{generateNewKey}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre+1, $apiKeyRowsCountPost, "ApiKey generated"); + return $self; +} + +sub revokeApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{revoke}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after revoking it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'No', "ApiKey revoked"); + return $self; +} + +sub activateApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{activate}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after activating it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'Yes', "ApiKey activated"); + return $self; +} + +sub deleteApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{delete}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre-1, $apiKeyRowsCountPost, "ApiKey deleted"); + return $self; +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Members/LeftNavigation.pm b/t/lib/Page/Members/LeftNavigation.pm new file mode 100644 index 0000000..d9f2405 --- /dev/null +++ b/t/lib/Page/Members/LeftNavigation.pm @@ -0,0 +1,116 @@ +package t::lib::Page::Members::LeftNavigation; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +=head NAME t::lib::Page::Members::LeftNavigation + +=head SYNOPSIS + +Provides the services of the members/circulation left navigation column/frame for the implementing PageObject + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getLeftNavigationActionElements +@RETURNS HASHRef of Selenium::Driver::Webelements matching all the clickable elements + in the left navigation frame/column at members and circulation pages. +=cut + +sub _getLeftNavigationActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $e = {}; + eval { + $e->{checkOut} = $d->find_element("div#menu a[href*='circ/circulation.pl']", 'css'); + }; + eval { + $e->{details} = $d->find_element("div#menu a[href*='members/moremember.pl']", 'css'); + }; + eval { + $e->{fines} = $d->find_element("div#menu a[href*='members/pay.pl']", 'css'); + }; + eval { + $e->{routingLists} = $d->find_element("div#menu a[href*='members/routing-lists.pl']", 'css'); + }; + eval { + $e->{circulationHistory} = $d->find_element("div#menu a[href*='members/readingrec.pl']", 'css'); + }; + eval { + $e->{modificationLog} = $d->find_element("div#menu a[href*='tools/viewlog.pl']", 'css'); + }; + eval { + $e->{notices} = $d->find_element("div#menu a[href*='members/notices.pl']", 'css'); + }; + eval { + $e->{statistics} = $d->find_element("div#menu a[href*='members/statistics.pl']", 'css'); + }; + eval { + $e->{purchaseSuggestions} = $d->find_element("div#menu a[href*='members/purchase-suggestions.pl']", 'css'); + }; + return $e; +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub navigateCheckout { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getLeftNavigationActionElements(); + $elements->{checkOut}->click(); + $self->debugTakeSessionSnapshot(); + + ok($d->get_title() =~ m/Checking out to/i, + "Intra Navigate to Check out"); + + return t::lib::Page::Circulation::Circulation->rebrandFromPageObject($self); +} + +sub navigateToDetails { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getLeftNavigationActionElements(); + $elements->{details}->click(); + $self->debugTakeSessionSnapshot(); + + ok($d->get_title() =~ m/Patron details for/i, + "Intra Navigate to Details"); + + return t::lib::Page::Members::Moremember->rebrandFromPageObject($self); +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Members/MemberFlags.pm b/t/lib/Page/Members/MemberFlags.pm new file mode 100644 index 0000000..3fc93a5 --- /dev/null +++ b/t/lib/Page/Members/MemberFlags.pm @@ -0,0 +1,143 @@ +package t::lib::Page::Members::MemberFlags; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use t::lib::Page::Members::Moremember; + +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar); + +=head NAME t::lib::Page::Members::MemberFlags + +=head SYNOPSIS + +member-flags.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $memberflags = t::lib::Page::Members::MemberFlags->new({borrowernumber => "1"}); + +Instantiates a WebDriver and loads the members/member-flags.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + borrowernumber => loads the page to display Borrower matching the given borrowernumber + +@RETURNS t::lib::Page::Members::MemberFlags, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH') { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/members/member-flags.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{borrowernumber}) { + push @{$params->{getParams}}, "member=".$params->{borrowernumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub _getPermissionTreeControlElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $saveButton = $d->find_element('input[value="Save"]'); + my $cancelButton = $d->find_element('a.cancel'); + return ($saveButton, $cancelButton); +} + +=head _getPermissionTreePermissionElements + +@PARAM1 Scalar, Koha::Auth::PermissionModule's module +@PARAM2 Scalar, Koha::Auth::Permission's code +=cut + +sub _getPermissionTreePermissionElements { + my ($self, $module, $code) = @_; + my $d = $self->getDriver(); + + my $moduleTreeExpansionButton = $d->find_element("div.$module-hitarea"); + my $moduleCheckbox = $d->find_element("input#flag-$module"); + my $permissionCheckbox = $d->find_element('input#'.$module.'_'.$code); + return ($moduleTreeExpansionButton, $moduleCheckbox, $permissionCheckbox); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub togglePermission { + my ($self, $permissionModule, $permissionCode) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($moduleTreeExpansionButton, $moduleCheckbox, $permissionCheckbox) = $self->_getPermissionTreePermissionElements($permissionModule, $permissionCode); + if ($moduleTreeExpansionButton->get_attribute("class") =~ /expandable-hitarea/) { #Permission checkboxes are hidden and need to be shown. + $moduleTreeExpansionButton->click(); + $d->pause( $self->{userInteractionDelay} ); + } + + + #$moduleCheckbox->click(); #Clicking this will toggle all module permissions. + my $checked = $permissionCheckbox->get_attribute("checked") || ''; #Returns undef if not checked + $permissionCheckbox->click(); + ok($checked ne ($permissionCheckbox->get_attribute("checked") || ''), + "Module '$permissionModule', permission '$permissionCode', checkbox toggled"); + $self->debugTakeSessionSnapshot(); + + return $self; +} + +sub submitPermissionTree { + my $self = shift; + my $d = $self->getDriver(); + + my ($submitButton, $cancelButton) = $self->_getPermissionTreeControlElements(); + $submitButton->click(); + $self->debugTakeSessionSnapshot(); + + ok(($d->get_title() =~ /Patron details for/), "Permissions set"); + + return t::lib::Page::Members::Moremember->rebrandFromPageObject($self); +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Members/Memberentry.pm b/t/lib/Page/Members/Memberentry.pm new file mode 100644 index 0000000..dfb6161 --- /dev/null +++ b/t/lib/Page/Members/Memberentry.pm @@ -0,0 +1,261 @@ +package t::lib::Page::Members::Memberentry; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar t::lib::Page::Members::LeftNavigation); + +use Koha::Exception::BadParameter; + +use t::lib::Page::Circulation::Circulation; + + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/members/memberentry.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{borrowernumber}) { + push @{$params->{getParams}}, "borrowernumber=".$params->{borrowernumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + push @{$params->{getParams}}, "destination=".$params->{destination} if $params->{destination}; + push @{$params->{getParams}}, "op=".$params->{op} if $params->{op}; + push @{$params->{getParams}}, "categorycode=".$params->{categorycode} if $params->{categorycode}; + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + + +sub _getInputFieldsForValidation { + my ($self) = @_; + my $d = $self->getDriver(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput, $SMSnumber); + eval { + $emailInput = $d->find_element('#email'); + }; + eval { + $emailProInput = $d->find_element('#emailpro'); + }; + eval { + $email_BInput = $d->find_element('#B_email'); + }; + eval { + $phoneInput = $d->find_element('#phone'); + }; + eval { + $phoneProInput = $d->find_element('#phonepro'); + }; + eval { + $phone_BInput = $d->find_element('#B_phone'); + }; + eval { + $SMSnumber = $d->find_element('#SMSnumber'); + }; + + return ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput, $SMSnumber); +} + +sub _getMessagingPreferenceCheckboxes { + my ($self) = @_; + my $d = $self->getDriver(); + + my @email_prefs = $d->find_elements('input[type="checkbox"][id^="email"]'); + my @phone_prefs = $d->find_elements('input[type="checkbox"][id^="phone"]'); + my @sms_prefs = $d->find_elements('input[type="checkbox"][id^="sms"]'); + + return { email => \@email_prefs, phone => \@phone_prefs, sms => \@sms_prefs }; +} + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub checkMessagingPreferencesSet { + my ($self, $valid, @prefs) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + foreach my $type (@prefs){ + my @this_pref = $d->find_elements('input[type="checkbox"][id^="'.$type.'"]'); + + my $ok = 0; + + foreach my $checkbox (@this_pref){ + ok(0, "Intra Memberentry $type messaging checkbox ".$checkbox->get_attribute('id')." not checked") if !$checkbox->is_selected() and $valid; + ok(0, "Intra Memberentry $type messaging checkbox ".$checkbox->get_attribute('id')." checked (not supposed to be)") if $checkbox->is_selected() and !$valid; + $ok = 1; + } + ok($ok, "Intra Memberentry $type messaging checkboxes ok (all " . (($valid) ? 'checked':'unchecked') . ")"); + } + + return $self; +} + +sub clearMessagingContactFields { + my ($self, @fields) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput, $SMSnumber) = $self->_getInputFieldsForValidation(); + + if (@fields) { + for my $field (@fields){ + $emailInput->clear() if $field eq "email"; + $emailProInput->clear() if $field eq "emailpro"; + $email_BInput->clear() if $field eq "B_email"; + $phoneInput->clear() if $field eq "phone"; + $phoneProInput->clear() if $field eq "phonepro"; + $phone_BInput->clear() if $field eq "B_phone"; + $SMSnumber->clear() if $field eq "SMSnumber"; + } + ok(1, "Intra Memberentry contact fields (@fields) cleared"); + } else { + $emailInput->clear(); + $emailProInput->clear(); + $email_BInput->clear(); + $phoneInput->clear(); + $phoneProInput->clear(); + $phone_BInput->clear(); + $SMSnumber->clear(); + ok(1, "Intra Memberentry contact fields (email, emailpro, email_B, phone, phonepro, phone_B, SMSnumber) cleared"); + } + + return $self; +} + +sub checkPreferences { + my ($self, $valid, $type) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $checkboxes = $self->_getMessagingPreferenceCheckboxes(); + + ok (0, "Intra $type checkboxes not defined") if not defined $checkboxes->{$type}; + return 0 if not defined $checkboxes->{$type}; + + foreach my $checkbox (@{ $checkboxes->{$type} }){ + ok(0, "Intra Memberentry $type messaging checkbox ".$checkbox->get_attribute('id')." not available") if !$checkbox->is_enabled() and $valid; + ok(0, "Intra Memberentry $type messaging checkbox ".$checkbox->get_attribute('id')." available (not supposed to be)") if $checkbox->is_enabled() and !$valid; + + $checkbox->click() if not $checkbox->is_selected(); + } + ok (1, "Intra Memberentry $type messaging checkboxes checked") if $valid; + + return $self; +} +sub setEmail { + my ($self, $input) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput, $SMSnumber) = $self->_getInputFieldsForValidation(); + + $emailInput->clear(); + $emailProInput->clear(); + $email_BInput->clear(); + $emailInput->send_keys($input); + $emailProInput->send_keys($input); + $email_BInput->send_keys($input); + ok(1, "Intra Memberentry Wrote \"$input\" to email fields."); + + return $self; +} + +sub setPhone { + my ($self, $input) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput, $SMSnumber) = $self->_getInputFieldsForValidation(); + + $phoneInput->clear(); + $phoneProInput->clear(); + $phone_BInput->clear(); + $phoneInput->send_keys($input); + $phoneProInput->send_keys($input); + $phone_BInput->send_keys($input); + ok(1, "Intra Memberentry Wrote \"$input\" to phone fields."); + + return $self; +} + +sub setSMSNumber { + my ($self, $input) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput, $SMSnumber) = $self->_getInputFieldsForValidation(); + + $SMSnumber->clear(); + $SMSnumber->send_keys($input); + ok(1, "Intra Memberentry Wrote \"$input\" to SMSnumber."); + + return $self; +} + +sub submitForm { + my ($self, $valid) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $submitButton = $d->find_element('form#entryform input[type="submit"]'); + $submitButton->submit(); + $self->debugTakeSessionSnapshot(); + + if ($valid) { + my $submitted = $d->find_element("#editpatron", 'css'); + ok(1, "Intra Memberentry Submit changes success"); + return t::lib::Page::Circulation::Circulation->rebrandFromPageObject($self); + } else { + my @notsubmitted = $d->find_elements('form#entryform label[class="error"]', 'css'); + my $error_ids = ""; + + foreach my $el_id (@notsubmitted){ + my $attr_id = $el_id->get_attribute("for"); + $error_ids .= "'".$attr_id . "' "; + } + + ok(1, "Intra Memberentry Submit changes ". $error_ids .", validation error (as expected)."); + $d->refresh(); + return $self; + } +} + +1; \ No newline at end of file diff --git a/t/lib/Page/Members/Moremember.pm b/t/lib/Page/Members/Moremember.pm new file mode 100644 index 0000000..0a6cd6d --- /dev/null +++ b/t/lib/Page/Members/Moremember.pm @@ -0,0 +1,207 @@ +package t::lib::Page::Members::Moremember; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar t::lib::Page::Members::LeftNavigation); + +use t::lib::Page::Members::ApiKeys; + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Members::Moremember + +=head SYNOPSIS + +moremember.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $moremember = t::lib::Page::Members::Moremember->new({borrowernumber => "1"}); + +Instantiates a WebDriver and loads the members/moremember.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + borrowernumber => loads the page to display Borrower matching the given borrowernumber + +@RETURNS t::lib::Page::Members::Moremember, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/members/moremember.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{borrowernumber}) { + push @{$params->{getParams}}, "borrowernumber=".$params->{borrowernumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub _getEditLinks { + my ($self) = @_; + my $d = $self->getDriver(); + + my $patron_info_edit = $d->find_element("div#patron-information div.action a[href*='memberentry.pl?op=modify'][href*='step=1']", 'css'); + my $sms_number_edit = $d->find_element("div.action a[href*='memberentry.pl?op=modify'][href*='step=5']", 'css'); + my $library_use_edit = $d->find_element("div.action a[href*='memberentry.pl?op=modify'][href*='step=3']", 'css'); + my $alternate_addr_edit = $d->find_element("div.action a[href*='memberentry.pl?op=modify'][href*='step=6']", 'css'); + my $alternative_contact_edit = $d->find_element("div.action a[href*='memberentry.pl?op=modify'][href*='step=2']", 'css'); + + my $e = {}; + $e->{patron_information} = $patron_info_edit if $patron_info_edit; + $e->{smsnumber} = $sms_number_edit if $sms_number_edit; + $e->{library_use} = $library_use_edit if $library_use_edit; + $e->{alternate_address} = $alternate_addr_edit if $alternate_addr_edit; + $e->{alternative_contact} = $alternative_contact_edit if $alternative_contact_edit; + return $e; +} + +sub _getMessagingPreferenceCheckboxes { + my ($self) = @_; + my $d = $self->getDriver(); + + my @email_prefs = $d->find_elements('input[type="checkbox"][id^="email"]'); + my @phone_prefs = $d->find_elements('input[type="checkbox"][id^="phone"]'); + my @sms_prefs = $d->find_elements('input[type="checkbox"][id^="sms"]'); + + return { email => \@email_prefs, phone => \@phone_prefs, sms => \@sms_prefs }; +} + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub checkMessagingPreferencesSet { + my ($self, $valid, @prefs) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + foreach my $type (@prefs){ + my @this_pref = $d->find_elements('input[type="checkbox"][id^="'.$type.'"]'); + + my $ok = 0; + + foreach my $checkbox (@this_pref){ + ok(0, "Intra Moremember $type messaging checkbox ".$checkbox->get_attribute('id')." not checked") if !$checkbox->is_selected() and $valid; + ok(0, "Intra Moremember $type messaging checkbox ".$checkbox->get_attribute('id')." checked (not supposed to be)") if $checkbox->is_selected() and !$valid; + $ok = 1; + } + ok($ok, "Intra Moremember $type messaging checkboxes ok (all " . (($valid) ? 'checked':'unchecked') . ")"); + } + + return $self; +} + +sub navigateToPatronInformationEdit { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getEditLinks(); + $elements->{patron_information}->click(); + ok($d->get_title() =~ m/Modify patron/, "Intra Navigate to Modify patron information"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::Memberentry->rebrandFromPageObject($self); +} + +sub navigateToSMSnumberEdit { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getEditLinks(); + $elements->{smsnumber}->click(); + ok($d->get_title() =~ m/Modify patron/, "Intra Navigate to Modify patron SMS number"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::Memberentry->rebrandFromPageObject($self); +} + +sub navigateToLibraryUseEdit { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getEditLinks(); + $elements->{library_use}->click(); + ok($d->get_title() =~ m/Modify patron/, "Intra Navigate to Modify patron Library use"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::Memberentry->rebrandFromPageObject($self); +} + +sub navigateToAlternateAddressEdit { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getEditLinks(); + $elements->{alternate_address}->click(); + ok($d->get_title() =~ m/Modify patron/, "Intra Navigate to Modify patron Alternate address"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::Memberentry->rebrandFromPageObject($self); +} + +sub navigateToAlternativeContactEdit { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getEditLinks(); + $elements->{alternative_contact}->click(); + ok($d->get_title() =~ m/Modify patron/, "Intra Navigate to Modify patron Alternative contact"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::Memberentry->rebrandFromPageObject($self); +} + + + +1; #Make the compiler happy! diff --git a/t/lib/Page/Members/Toolbar.pm b/t/lib/Page/Members/Toolbar.pm new file mode 100644 index 0000000..132a588 --- /dev/null +++ b/t/lib/Page/Members/Toolbar.pm @@ -0,0 +1,144 @@ +package t::lib::Page::Members::Toolbar; + +# Copyright 2015 KohaSuomi! +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Members::Toolbar + +=head SYNOPSIS + +PageObject Accessory representing a shared Template between PageObjects. +This encapsulates specific page-related functionality. + +In this case this encapsulates the members-module toolbar's services and provides +a reusable class from all member-module PageObjects. + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getToolbarActionElements +Shares the same toolbar with moremember.pl +@RETURNS HASHRef of Selenium::Driver::Webelements matching all the clickable elements + in the actions toolbar over the Borrower information. +=cut + +sub _getToolbarActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $editA = $d->find_element("#editpatron", 'css'); + my $changePasswordA = $d->find_element("#changepassword", 'css'); + my $duplicateA = $d->find_element("#duplicate", 'css'); + my $printButton = $d->find_element("#duplicate + div > button", 'css'); + my $searchToHoldA = $d->find_element("#searchtohold", 'css'); + my $moreButton = $d->find_element("#searchtohold + div > button", 'css'); + + my $e = {}; + $e->{edit} = $editA if $editA; + $e->{changePassword} = $changePasswordA if $changePasswordA; + $e->{duplicate} = $duplicateA if $duplicateA; + $e->{print} = $printButton if $printButton; + $e->{searchToHold} = $searchToHoldA if $searchToHoldA; + $e->{more} = $moreButton if $moreButton; + return $e; +} + +=head _getMoreDropdownElements +Clicks the dropdown open if it isnt yet. +@RETURNS HASHRef of all the dropdown elements under the More button in the toolbar + over Borrower information. +=cut + +sub _getMoreDropdownElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $toolbarElements = $self->_getToolbarActionElements(); + my $moreButton = $toolbarElements->{more}; + my $deleteA; + eval { + $deleteA = $d->find_child_element($moreButton, "#deletepatron", 'css'); + }; + unless ($deleteA && $deleteA->is_visible()) { + $moreButton->click(); + $self->debugTakeSessionSnapshot(); + } + + my $renewPatronA = $d->find_element("#renewpatron", 'css'); + my $setPermissionsA = $d->find_element("#patronflags", 'css'); + my $manageApiKeysA = $d->find_element("#apikeys", 'css'); + $deleteA = $d->find_element("#deletepatron", 'css'); + $self->debugTakeSessionSnapshot(); + my $updateChildToAdultPatronA = $d->find_element("#updatechild", 'css'); + my $exportCheckinBarcodesA = $d->find_element("#exportcheckins", 'css'); + + my $e = {}; + $e->{renewPatron} = $renewPatronA if $renewPatronA; + $e->{setPermissions} = $setPermissionsA if $setPermissionsA; + $e->{manageApiKeys} = $manageApiKeysA if $manageApiKeysA; + $e->{delete} = $deleteA if $deleteA; + $e->{updateChildToAdultPatron} = $updateChildToAdultPatronA if $updateChildToAdultPatronA; + $e->{exportCheckinBarcodes} = $exportCheckinBarcodesA if $exportCheckinBarcodesA; + return $e; +} + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub navigateManageApiKeys { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getMoreDropdownElements(); + $elements->{manageApiKeys}->click(); + ok($d->get_title() =~ m/API Keys/, "Intra Navigate to Manage API Keys"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::ApiKeys->rebrandFromPageObject($self); +} +sub navigateEditPatron { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getToolbarActionElements(); + $elements->{edit}->click(); + ok($d->get_title() =~ m/Modify patron/, "Intra Navigate to Modify patron"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::Memberentry->rebrandFromPageObject($self); +} + + +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Opac.pm b/t/lib/Page/Opac.pm new file mode 100644 index 0000000..b7d805d --- /dev/null +++ b/t/lib/Page/Opac.pm @@ -0,0 +1,223 @@ +package t::lib::Page::Opac; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use C4::Context; + +use t::lib::WebDriverFactory; + +use Koha::Exception::BadParameter; +use Koha::Exception::SystemCall; + +use base qw(t::lib::Page); + +=head NAME t::lib::Page::Opac + +=head SYNOPSIS + +PageObject-pattern parent class for OPAC-pages. Extend this to implement specific pages shown to our users. + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getHeaderRegionActionElements + +Returns each element providing some kind of an action from the topmost header bar in OPAC. +All elements are not always present on each page, so test if the return set contains your +desired element. +@PARAM1 Selenium::Remote::Driver +@RETURNS HASHRef of the found elements: + { cart => $cartA, + lists => $listsA, + loggedinusername => $loggedinusernameA, + searchHistory => $searchHistoryA, + deleteSearchHistory => $deleteSearchHistoryA, + logout => $logoutA, + login => $loginA, + } +=cut + +sub _getHeaderRegionActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my ($cartA, $listsA, $loggedinusernameA, $searchHistoryA, $deleteSearchHistoryA, $logoutA, $loginA); + #Always visible elements + $cartA = $d->find_element("#header-region a#cartmenulink"); + $listsA = $d->find_element("#header-region a#listsmenu"); + #Occasionally visible elements + eval { + $loggedinusernameA = $d->find_element("#header-region a[href*='opac-user.pl']"); + }; + eval { + $searchHistoryA = $d->find_element("#header-region a[href*='opac-search-history.pl']"); + }; + eval { + $deleteSearchHistoryA = $d->find_element("#header-region a[href*='opac-search-history.pl'] + a"); + }; + eval { + $logoutA = $d->find_element("#header-region #logout"); + }; + eval { + $loginA = $d->find_element("#header-region #members a.loginModal-trigger"); + }; + + my $e = {}; + $e->{cart} = $cartA if $cartA; + $e->{lists} = $listsA if $listsA; + $e->{loggedinusername} = $loggedinusernameA if $loggedinusernameA; + $e->{searchHistory} = $searchHistoryA if $searchHistoryA; + $e->{deleteSearchHistory} = $deleteSearchHistoryA if $deleteSearchHistoryA; + $e->{logout} = $logoutA if $logoutA; + $e->{login} = $loginA if $loginA; + return ($e); +} + +sub _getMoresearchesElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $advancedSearchA = $d->find_element("#moresearches a[href*='opac-search.pl']"); + my $authoritySearchA = $d->find_element("#moresearches a[href*='opac-authorities-home.pl']"); + my $tagCloudA = $d->find_element("#moresearches a[href*='opac-tags.pl']"); + return ($advancedSearchA, $authoritySearchA, $tagCloudA); +} + +sub _getBreadcrumbLinks { + my ($self) = @_; + my $d = $self->getDriver(); + + my $breadcrumbLinks = $d->find_elements("ul.breadcrumb a"); + return ($breadcrumbLinks); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head isPasswordLoginAvailable + + $page->isPasswordLoginAvailable(); + +@RETURN t::lib::Page-object +@CROAK if password login is unavailable. +=cut + +sub isPasswordLoginAvailable { + my $self = shift; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + $self->_getPasswordLoginElements(); + ok(1, "PasswordLogin available"); + return $self; +} + +sub doPasswordLogin { + my ($self, $username, $password) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($submitButton, $useridInput, $passwordInput) = $self->_getPasswordLoginElements(); + $useridInput->send_keys($username); + $passwordInput->send_keys($password); + $submitButton->click(); + $self->debugTakeSessionSnapshot(); + + my $cookies = $d->get_all_cookies(); + my @cgisessid = grep {$_->{name} eq 'CGISESSID'} @$cookies; + + my $loggedinusernameSpan = $d->find_element('span.loggedinusername'); + ok(($cgisessid[0]), "PasswordLogin succeeded"); #We have the element && Cookie CGISESSID defined! + + return $self; #After a succesfull password login, we are directed to the same page we tried to access. +} + +sub doPasswordLogout { + my ($self, $username, $password) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + #Logout + my $headerElements = $self->_getHeaderRegionActionElements(); + my $logoutA = $headerElements->{logout}; + $logoutA->click(); + $self->debugTakeSessionSnapshot(); + + $headerElements = $self->_getHeaderRegionActionElements(); #Take the changed header elements + my $txt = $headerElements->{login}->get_text(); + ok(($headerElements->{login}->get_text() =~ /Log in/ || + $d->get_title() =~ /Log in to your account/), "Opac Header PasswordLogout succeeded"); + return t::lib::Page::Opac::OpacMain->rebrandFromPageObject($self); + ok((), "PasswordLogout succeeded"); + return t::lib::Page::Opac::OpacMain->rebrandFromPageObject($self); +} + +sub navigateSearchHistory { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $headerElements = $self->_getHeaderRegionActionElements(); + my $searchHistoryA = $headerElements->{searchHistory}; + $searchHistoryA->click(); + $self->debugTakeSessionSnapshot(); + + ok(($d->get_title() =~ /Your search history/), "Opac Navigation to search history."); + return t::lib::Page::Opac::OpacSearchHistory->rebrandFromPageObject($self); +} + +sub navigateAdvancedSearch { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($advancedSearchA, $authoritySearchA, $tagCloudA) = $self->_getMoresearchesElements(); + $advancedSearchA->click(); + + $self->debugTakeSessionSnapshot(); + ok(($d->get_title() =~ /Advanced search/), "Opac Navigating to advanced search."); + return t::lib::Page::Opac::OpacSearch->rebrandFromPageObject($self); +} + +sub navigateHome { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $breadcrumbLinks = $self->_getBreadcrumbLinks(); + $breadcrumbLinks->[0]->click(); + + $self->debugTakeSessionSnapshot(); + ok(($d->get_current_url() =~ /opac-main\.pl/), "Opac Navigating to OPAC home."); + return t::lib::Page::Opac::OpacMain->rebrandFromPageObject($self); +} + +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Opac/LeftNavigation.pm b/t/lib/Page/Opac/LeftNavigation.pm new file mode 100644 index 0000000..ae10de5 --- /dev/null +++ b/t/lib/Page/Opac/LeftNavigation.pm @@ -0,0 +1,149 @@ +package t::lib::Page::Opac::LeftNavigation; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::Opac::OpacApiKeys; +use t::lib::Page::Opac::OpacMessaging; + +=head NAME t::lib::Page::Opac::LeftNavigation + +=head SYNOPSIS + +Provides the services of the Opac left navigation column/frame for the implementing PageObject + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getLeftNavigationActionElements +@RETURNS HASHRef of Selenium::Driver::Webelements matching all the clickable elements + in the left navigation frame/column at all Opac pages requiring login. +=cut + +sub _getLeftNavigationActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $e = {}; + eval { + $e->{yourSummary} = $d->find_element("a[href*='opac-user.pl']", 'css'); + }; + eval { + $e->{yourFines} = $d->find_element("a[href*='opac-account.pl']", 'css'); + }; + eval { + $e->{yourPersonalDetails} = $d->find_element("a[href*='opac-memberentry.pl']", 'css'); + }; + eval { + $e->{yourTags} = $d->find_element("a[href*='opac-tags.pl']", 'css'); + }; + eval { + $e->{changeYourPassword} = $d->find_element("a[href*='opac-passwd.pl']", 'css'); + }; + eval { + $e->{yourSearchHistory} = $d->find_element("a[href*='opac-search-history.pl']", 'css'); + }; + eval { + $e->{yourReadingHistory} = $d->find_element("a[href*='opac-readingrecord.pl']", 'css'); + }; + eval { + $e->{yourPurchaseSuggestions} = $d->find_element("a[href*='opac-suggestions.pl']", 'css'); + }; + eval { + $e->{yourMessaging} = $d->find_element("a[href*='opac-messaging.pl']", 'css'); + }; + eval { + $e->{yourLists} = $d->find_element("a[href*='opac-shelves.pl']", 'css'); + }; + eval { + $e->{yourAPIKeys} = $d->find_element("a[href*='opac-apikeys.pl']", 'css'); + }; + return $e; +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub navigateYourAPIKeys { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getLeftNavigationActionElements(); + $elements->{yourAPIKeys}->click(); + $self->debugTakeSessionSnapshot(); + + my $breadcrumbs = $self->_getBreadcrumbLinks(); + + ok(ref($breadcrumbs) eq 'ARRAY' && + $breadcrumbs->[scalar(@$breadcrumbs)-1]->get_text() =~ m/API keys/i, + "Opac Navigate to Your API Keys"); + + return t::lib::Page::Opac::OpacApiKeys->rebrandFromPageObject($self); +} + +sub navigateYourMessaging { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getLeftNavigationActionElements(); + $elements->{yourMessaging}->click(); + $self->debugTakeSessionSnapshot(); + + my $breadcrumbs = $self->_getBreadcrumbLinks(); + + ok(ref($breadcrumbs) eq 'ARRAY' && + $breadcrumbs->[scalar(@$breadcrumbs)-1]->get_text() =~ m/Your messaging/i, + "Opac Navigate to Your messaging"); + + return t::lib::Page::Opac::OpacMessaging->rebrandFromPageObject($self); +} + +sub navigateYourPersonalDetails { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getLeftNavigationActionElements(); + $elements->{yourPersonalDetails}->click(); + $self->debugTakeSessionSnapshot(); + + my $breadcrumbs = $self->_getBreadcrumbLinks(); + + ok(ref($breadcrumbs) eq 'ARRAY' && + $breadcrumbs->[scalar(@$breadcrumbs)-1]->get_text() =~ m/Your personal details/i, + "Opac Navigate to Your personal details"); + + return t::lib::Page::Opac::OpacMemberentry->rebrandFromPageObject($self); +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Opac/OpacApiKeys.pm b/t/lib/Page/Opac/OpacApiKeys.pm new file mode 100644 index 0000000..72df3ef --- /dev/null +++ b/t/lib/Page/Opac/OpacApiKeys.pm @@ -0,0 +1,170 @@ +package t::lib::Page::Opac::OpacApiKeys; + +# Copyright 2015 KohaSuomi! +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +=head NAME t::lib::Page::Members::ApiKeys + +=head SYNOPSIS + +apikeys.pl PageObject providing page functionality as a service! + +=cut + +sub new { + Koha::Exception::FeatureUnavailable->throw(error => __PACKAGE__."->new():> You must login first to navigate to this page!"); +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getActionsAndTableElements +@RETURNS List of + HASHRef of Selenium::Driver::Webelement-objects matching the generic + actions on this page, eg. 'generateNewKey'. + HASHRef of Selenium::Driver::Webelement-objects keyed with the apiKey hash/text. + These are all the apiKey table rows present, and have the + elements prefetched for easy access. + +=cut + +sub _getActionsAndTableElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $generateNewKeySubmit = $d->find_element("#generatenewkey", 'css'); + + my $a = {}; #Collect action elements here + $a->{generateNewKey} = $generateNewKeySubmit; #Bind the global action here for easy reference. + + my $apiKeyRows; + eval { #We might not have ApiKeys yet. + $apiKeyRows = $d->find_elements("#apikeystable tr", 'css'); + shift @$apiKeyRows; #Remove the table header row + }; + my %apiKeys; + for(my $i=0 ; $i[$i]; + $row->{'nth-of-type'} = $i+1; #starts from 1 + $row->{key} = $d->find_child_element($row, "td.apikeykey", 'css')->get_text(); + $row->{active} = $d->find_child_element($row, "td.apikeyactive", 'css')->get_text(); + $row->{lastTransaction} = $d->find_child_element($row, "td.apikeylastransaction", 'css')->get_text(); + $row->{delete} = $d->find_child_element($row, "input.apikeydelete", 'css'); + eval { + $row->{revoke} = $d->find_child_element($row, "input.apikeyrevoke", 'css'); + }; + eval { + $row->{activate} = $d->find_child_element($row, "input.apikeyactivate", 'css'); + }; + $apiKeys{$row->{key}} = $row; + } + + return ($a, \%apiKeys); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub generateNewApiKey { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + $actionElements->{generateNewKey}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre+1, $apiKeyRowsCountPost, "ApiKey generated"); + return $self; +} + +sub revokeApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{revoke}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after revoking it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'No', "ApiKey revoked"); + return $self; +} + +sub activateApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{activate}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after activating it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'Yes', "ApiKey activated"); + return $self; +} + +sub deleteApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{delete}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre-1, $apiKeyRowsCountPost, "ApiKey deleted"); + return $self; +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Opac/OpacMain.pm b/t/lib/Page/Opac/OpacMain.pm new file mode 100644 index 0000000..6d1d7dd --- /dev/null +++ b/t/lib/Page/Opac/OpacMain.pm @@ -0,0 +1,125 @@ +package t::lib::Page::Opac::OpacMain; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::Opac::OpacUser; + +use base qw(t::lib::Page::Opac); + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Opac::OpacMain + +=head SYNOPSIS + +PageObject providing page functionality as a service! + +=cut + +=head new + + my $opacmain = t::lib::Page::Opac::OpacMain->new(); + +Instantiates a WebDriver and loads the opac/opac-main.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + none atm. + +@RETURNS t::lib::Page::Opac::OpacMain, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/opac-main.pl'; + $params->{type} = 'opac'; + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub _getPasswordLoginElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $submitButton = $d->find_element('form#auth input[type="submit"]'); + my $useridInput = $d->find_element('#userid'); + my $passwordInput = $d->find_element('#password'); + return ($submitButton, $useridInput, $passwordInput); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head isPasswordLoginAvailable + + $page->isPasswordLoginAvailable(); + +@RETURN t::lib::Page-object +@CROAK if password login is unavailable. +=cut + +sub isPasswordLoginAvailable { + my $self = shift; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + $self->_getPasswordLoginElements(); + ok(1, "OpacMain PasswordLogin available"); + return $self; +} + +sub doPasswordLogin { + my ($self, $username, $password) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($submitButton, $useridInput, $passwordInput) = $self->_getPasswordLoginElements(); + $useridInput->send_keys($username); + $passwordInput->send_keys($password); + $submitButton->click(); + $self->debugTakeSessionSnapshot(); + + my $cookies = $d->get_all_cookies(); + my @cgisessid = grep {$_->{name} eq 'CGISESSID'} @$cookies; + + my $loggedinusernameSpan = $d->find_element('span.loggedinusername'); + ok(($cgisessid[0]), "OpacMain PasswordLogin succeeded"); #We have the element && Cookie CGISESSID defined! + + return t::lib::Page::Opac::OpacUser->rebrandFromPageObject($self); +} + +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Opac/OpacMemberentry.pm b/t/lib/Page/Opac/OpacMemberentry.pm new file mode 100644 index 0000000..9c4e450 --- /dev/null +++ b/t/lib/Page/Opac/OpacMemberentry.pm @@ -0,0 +1,161 @@ +package t::lib::Page::Opac::OpacMemberentry; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public Lice strongnse +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::Opac::OpacUser; + +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Opac::OpacMemberentry + +=head SYNOPSIS + +PageObject providing page functionality as a service! + +=cut + +=head new + + my $opacmemberentry = t::lib::Page::Opac::OpacMemberentry->new(); + +Instantiates a WebDriver and loads the opac/opac-memberentry.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + none atm. + +@RETURNS t::lib::Page::Opac::OpacMemberentry, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/opac-memberentry.pl'; + $params->{type} = 'opac'; + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub _getInputFieldsForValidation { + my ($self) = @_; + my $d = $self->getDriver(); + + my $emailInput = $d->find_element('#borrower_email'); + my $emailProInput = $d->find_element('#borrower_emailpro'); + my $email_BInput = $d->find_element('#borrower_B_email'); + + my $phoneInput = $d->find_element('#borrower_phone'); + my $phoneProInput = $d->find_element('#borrower_phonepro'); + my $phone_BInput = $d->find_element('#borrower_B_phone'); + + return ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput); +} + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head isFieldsAvailable + + $page->isFieldsAvailable(); + +@RETURN t::lib::Page-object +@CROAK if unable to find required fields. +=cut + +sub setEmail { + my ($self, $input) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput) = $self->_getInputFieldsForValidation(); + + $emailInput->send_keys($input); + $emailProInput->send_keys($input); + $email_BInput->send_keys($input); + ok(1, "OpacMemberentry Wrote \"$input\" to email fields."); + + return $self; +} + +sub setPhone { + my ($self, $input) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($emailInput, $emailProInput, $email_BInput, $phoneInput, $phoneProInput, $phone_BInput) = $self->_getInputFieldsForValidation(); + + $phoneInput->send_keys($input); + $phoneProInput->send_keys($input); + $phone_BInput->send_keys($input); + ok(1, "OpacMemberentry Wrote \"$input\" to phone fields."); + + return $self; +} + +sub submitForm { + my ($self, $valid) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $submitButton = $d->find_element('form#memberentry-form input[type="submit"]'); + $submitButton->click(); + + $self->debugTakeSessionSnapshot(); + + if ($valid) { + my $submitted = $d->find_element('#update-submitted'); + ok(1, "OpacMemberentry Submit changes success"); + } else { + my @notsubmitted = $d->find_elements('form#memberentry-form label[id^="borrower_"][id$="-error"]', 'css'); + my $error_ids = ""; + + foreach my $el_id (@notsubmitted){ + my $attr_id = $el_id->get_attribute("id"); + $attr_id =~ s/borrower_//g; + $attr_id =~ s/-error//g; + $error_ids .= "'".$attr_id . "' "; + } + + ok(1, "OpacMemberentry Submit changes ". $error_ids .", validation error (as expected)."); + } + + + + return t::lib::Page::Opac::OpacUser->rebrandFromPageObject($self); +} + +1; \ No newline at end of file diff --git a/t/lib/Page/Opac/OpacMessaging.pm b/t/lib/Page/Opac/OpacMessaging.pm new file mode 100644 index 0000000..acb0aee --- /dev/null +++ b/t/lib/Page/Opac/OpacMessaging.pm @@ -0,0 +1,101 @@ +package t::lib::Page::Opac::OpacMessaging; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public Lice strongnse +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::Opac::OpacUser; + +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Opac::OpacMessaging + +=head SYNOPSIS + +PageObject providing page functionality as a service! + +=cut + +=head new + + my $opacmemberentry = t::lib::Page::Opac::OpacMessaging->new(); + +Instantiates a WebDriver and loads the opac/opac-messaging.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + none atm. + +@RETURNS t::lib::Page::Opac::OpacMessaging, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/opac-messaging.pl'; + $params->{type} = 'opac'; + + my $self = $class->SUPER::new($params); + + return $self; +} + +sub _getMessagingPreferenceCheckboxes { + my ($self) = @_; + my $d = $self->getDriver(); + + my @email_prefs = $d->find_elements('input[type="checkbox"][id^="email"]'); + my @phone_prefs = $d->find_elements('input[type="checkbox"][id^="phone"]'); + my @sms_prefs = $d->find_elements('input[type="checkbox"][id^="sms"]'); + + return { email => \@email_prefs, phone => \@phone_prefs, sms => \@sms_prefs }; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub checkMessagingPreferencesSet { + my ($self, $valid, @prefs) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + foreach my $type (@prefs){ + my @this_pref = $d->find_elements('input[type="checkbox"][id^="'.$type.'"]'); + + my $ok = 0; + + foreach my $checkbox (@this_pref){ + ok(0, "Opac Messaging $type checkbox ".$checkbox->get_attribute('id')." not checked") if !$checkbox->is_selected() and $valid; + ok(0, "Opac Messaging $type checkbox ".$checkbox->get_attribute('id')." checked (not supposed to be)") if $checkbox->is_selected() and !$valid; + $ok = 1; + } + ok($ok, "Opac Messaging $type checkboxes ok (all " . (($valid) ? 'checked':'unchecked') . ")"); + } + + return $self; +} + +1; diff --git a/t/lib/Page/Opac/OpacSearch.pm b/t/lib/Page/Opac/OpacSearch.pm new file mode 100644 index 0000000..d1a8140 --- /dev/null +++ b/t/lib/Page/Opac/OpacSearch.pm @@ -0,0 +1,142 @@ +package t::lib::Page::Opac::OpacSearch; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::PageUtils; +use t::lib::Page::Opac::OpacMain; +use t::lib::Page::Opac::OpacSearchHistory; + +use base qw(t::lib::Page::Opac); + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Opac::OpacSearch + +=head SYNOPSIS + +PageObject providing page functionality as a service! + +=cut + +=head new + + my $opacsearch = t::lib::Page::Opac::OpacSearch->new(); + +Instantiates a WebDriver and loads the opac/opac-search.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + none atm. + +@RETURNS t::lib::Page::Opac::OpacSearch, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/opac-search.pl'; + $params->{type} = 'opac'; + + my $self = $class->SUPER::new($params); + + return $self; +} + + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub _findSearchFieldElements { + my ($self, $searchField) = @_; + my $d = $self->getDriver(); + $searchField = '0' unless $searchField; + + my $indexSelect = $d->find_element("#search-field_$searchField"); + my $termInput = $d->find_element("#search-field_$searchField + input[name='q']"); + my $searchSubmit = $d->find_element("input[type='submit'].btn-success"); #Returns the first instance. + return ($indexSelect, $termInput, $searchSubmit); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head doSetSearchFieldTerm + +Sets the search index and term for one of the (by default) three search fields. +@PARAM1, Integer, which search field to put the parameters into? + Starts from 0 == the topmost search field. +@PARAM2, String, the index to use. Undef if you want to use whatever there is. + Use the english index full name, eg. "Keyword", "Title", "Author". +@PARAM3, String, the search term. This replaces any existing search terms in the search field. +=cut + +sub doSetSearchFieldTerm { + my ($self, $searchField, $selectableIndex, $term) = @_; + $searchField = '0' unless $searchField; #Trouble with Perl interpreting 0 + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($indexSelect, $termInput, $searchSubmit) = $self->_findSearchFieldElements($searchField); + + if ($selectableIndex) { + t::lib::Page::PageUtils::displaySelectsOptions($d, $indexSelect); + my $optionElement = t::lib::Page::PageUtils::getSelectElementsOptionByName($d, $indexSelect, $selectableIndex); + $optionElement->click(); + } + + if ($term) { + $termInput->clear(); + $termInput->send_keys($term); + } + else { + Koha::Exception::BadParameter->throw("doSetSearchFieldTerm():> Parameter \$main is mandatory but is missing? Parameters as follow\n: @_"); + } + + $selectableIndex = '' unless $selectableIndex; + ok(1, "SearchField parameters '$selectableIndex' and '$term' set."); + $self->debugTakeSessionSnapshot(); + return $self; +} + +sub doSearchSubmit { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($indexSelect, $termInput, $searchSubmit) = $self->_findSearchFieldElements(0); #We just want the submit button + $searchSubmit->click(); + $self->debugTakeSessionSnapshot(); + + ok(($d->get_title() =~ /Results of search/), "SearchField search."); + return $self; +} + +1; #Make the compiler happy! diff --git a/t/lib/Page/Opac/OpacSearchHistory.pm b/t/lib/Page/Opac/OpacSearchHistory.pm new file mode 100644 index 0000000..136d889 --- /dev/null +++ b/t/lib/Page/Opac/OpacSearchHistory.pm @@ -0,0 +1,121 @@ +package t::lib::Page::Opac::OpacSearchHistory; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; +use Test::More; + +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); + +use Koha::Exception::FeatureUnavailable; + +=head NAME t::lib::Page::Opac::OpacSearchHistory + +=head SYNOPSIS + +PageObject providing page functionality as a service! + +=cut + +=head new + +YOU CANNOT GET HERE WITHOUT LOGGING IN FIRST! + +=cut + +sub new { + Koha::Exception::FeatureUnavailable->throw(error => __PACKAGE__."->new():> You must login first to navigate to this page!"); +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +sub _getAllSearchHistories { + my ($self) = @_; + my $d = $self->getDriver(); + + $self->pause(500); #Wait for datatables to load the page. + my $histories = $d->find_elements("table.historyt tr"); + #First index has the table header, so skip that. + shift @$histories; + for (my $i=0 ; $i[$i] = $self->_castSearchHistoryRowToHash($histories->[$i]); + } + return $histories; +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +=head testDoSearchHistoriesExist + + $opacsearchhistory->testDoSearchHistoriesExist([ 'maximus', + 'julius', + 'titus', + ]); +@PARAM1 ARRAYRef of search strings shown in the opac-search-history.pl -page. + These search strings need only be contained in the displayed values. +=cut + +sub testDoSearchHistoriesExist { + my ($self, $searchStrings) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $histories = $self->_getAllSearchHistories(); + foreach my $s (@$searchStrings) { + + my $matchFound; + foreach my $h (@$histories) { + if ($h->{searchStringA}->get_text() =~ /$s/) { + $matchFound = $h->{searchStringA}->get_text(); + last(); + } + } + ok($matchFound =~ /$s/, "SearchHistory $s exists."); + } + return $self; +} + +sub _castSearchHistoryRowToHash { + my ($self, $historyRow) = @_; + my $d = $self->getDriver(); + + my $checkbox = $d->find_child_element($historyRow, "input[type='checkbox']","css"); + my $date = $d->find_child_element($historyRow, "span[title]","css"); + $date = $date->get_text(); + my $searchStringA = $d->find_child_element($historyRow, "a + a","css"); + my $resultsCount = $d->find_child_element($historyRow, "td + td + td + td","css"); + + my $sh = { checkbox => $checkbox, + date => $date, + searchStringA => $searchStringA, + resultsCount => $resultsCount, + }; + return $sh; +} + +1; #Make the compiler happy! \ No newline at end of file diff --git a/t/lib/Page/Opac/OpacUser.pm b/t/lib/Page/Opac/OpacUser.pm new file mode 100644 index 0000000..38fda04 --- /dev/null +++ b/t/lib/Page/Opac/OpacUser.pm @@ -0,0 +1,64 @@ +package t::lib::Page::Opac::OpacUser; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); + +use Koha::Exception::FeatureUnavailable; + +=head NAME t::lib::Page::Opac::OpacUser + +=head SYNOPSIS + +PageObject providing page functionality as a service! + +=cut + +=head new + +YOU CANNOT GET HERE WITHOUT LOGGING IN FIRST! +Navigate here from opac-main.pl for example. +=cut + +sub new { + Koha::Exception::FeatureUnavailable->throw(error => __PACKAGE__."->new():> You must login first to navigate to this page!"); +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + + + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + + + + + +1; #Make the compiler happy! diff --git a/t/lib/Page/PageUtils.pm b/t/lib/Page/PageUtils.pm new file mode 100644 index 0000000..77ddae0 --- /dev/null +++ b/t/lib/Page/PageUtils.pm @@ -0,0 +1,69 @@ +package t::lib::Page::PageUtils; + +# Copyright 2015 Open Source Freedom Fighters +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Koha::Exception::UnknownObject; + +=head NAME t::lib::Page::PageUtils + +=head SYNOPSIS + +Contains all kinds of helper functions used all over the PageObject testing framework. + +=cut + +sub getSelectElementsOptionByName { + my ($d, $selectElement, $optionName) = @_; + + my $options = $d->find_child_elements($selectElement, "option", 'css'); + my $correctOption; + foreach my $option (@$options) { + if ($option->get_text() eq $optionName) { + $correctOption = $option; + last(); + } + } + + return $correctOption if $correctOption; + + ##Throw Exception because we didn't find the option element. + my @availableOptions; + foreach my $option (@$options) { + push @availableOptions, $option->get_tag_name() .', value: '. $option->get_value() .', text: '. $option->get_text(); + } + Koha::Exception::UnknownObject->throw(error => + "getSelectElementsOptionByName():> Couldn't find the given option-element using '$optionName'. Available options:\n". + join("\n", @availableOptions)); +} + +sub displaySelectsOptions { + my ($d, $selectElement) = @_; + + my $options = $d->find_child_elements($selectElement, "option", 'css'); + if (scalar(@$options)) { + $selectElement->click() if $options->[0]->is_hidden(); + } + else { + Koha::Exception::UnknownObject->throw(error => + "_displaySelectsOptions():> element: ".$selectElement->get_tag_name()-', class: '.$selectElement->get_attribute("class").", doesn't have any option-elements?"); + } +} + +1; \ No newline at end of file -- 1.9.1