Bugzilla – Attachment 41254 Details for
Bug 14536
PageObject-pattern base implementation
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 14536 - PageObject-pattern base implementation
Bug-14536---PageObject-pattern-base-implementation.patch (text/plain), 61.38 KB, created by
Olli-Antti Kivilahti
on 2015-07-31 10:34:09 UTC
(
hide
)
Description:
Bug 14536 - PageObject-pattern base implementation
Filename:
MIME Type:
Creator:
Olli-Antti Kivilahti
Created:
2015-07-31 10:34:09 UTC
Size:
61.38 KB
patch
obsolete
>From 177ccd77d110210575de24727f1cfe46b3acebbb Mon Sep 17 00:00:00 2001 >From: Olli-Antti Kivilahti <olli-antti.kivilahti@jns.fi> >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! >--- > misc/devel/interactiveWebDriverShell.pl | 190 +++++++++++++++++++++++++++ > t/db_dependent/Koha/Auth.t | 72 +++++++++++ > t/lib/Page.pm | 222 +++++++++++++++++++++++++++++++ > t/lib/Page/Intra.pm | 195 ++++++++++++++++++++++++++++ > t/lib/Page/Mainpage.pm | 64 +++++++++ > t/lib/Page/Members/MemberFlags.pm | 143 ++++++++++++++++++++ > t/lib/Page/Members/Moremember.pm | 89 +++++++++++++ > t/lib/Page/Opac.pm | 223 ++++++++++++++++++++++++++++++++ > t/lib/Page/Opac/OpacMain.pm | 125 ++++++++++++++++++ > 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 ++++++++++ > 13 files changed, 1719 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/Intra.pm > create mode 100644 t/lib/Page/Mainpage.pm > create mode 100644 t/lib/Page/Members/MemberFlags.pm > create mode 100644 t/lib/Page/Members/Moremember.pm > create mode 100644 t/lib/Page/Opac.pm > create mode 100644 t/lib/Page/Opac/OpacMain.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..fdd5eb5 >--- /dev/null >+++ b/misc/devel/interactiveWebDriverShell.pl >@@ -0,0 +1,190 @@ >+#!/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 <http://www.gnu.org/licenses>. >+ >+=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 = <<HELP; >+ >+interactiveWebDriverShell.pl >+ >+ Prepares a perl debugger session with the requested PageObject loaded. >+ Then you can easily guide the UserAgent through the web session. >+ >+ You should install Term::ReadLine::Gnu for a more pleasant debugging experience. >+ >+ -h --help This help! >+ >+ -p --page Which PageObject matching the given page you want to preload? >+ >+ -P --params List of parameters the PageObject must have. >+ >+ -L --login List of userid and password to automatically login to Koha. Eg: >+ ./interactiveWebDriverShell.pl -L admin 1234 -p members/moremember.pl -P 12 >+ >+ -l --list Lists available PageObjects and their matching --page -parameter >+ values. >+ >+EXAMPLE INVOCATIONS: >+ >+./interactiveWebDriverShell.pl -p mainpage.pl -L admin 1234 >+./interactiveWebDriverShell.pl -p members/moremember.pl -P 1 -L admin 1234 >+ >+USAGE: >+ >+Start the session from your shell >+ ..\$ misc/devel/interactiveWebDriverShell.pl -p mainpage.pl >+or >+Start the session from your shell with parameters >+ ..\$ misc/devel/interactiveWebDriverShell.pl -p members/moremember.pl -P 12 >+ >+Continue to the breakpoint set in this script >+ DB<1> 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"], >+ }, >+################################################################################ >+ ########## 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 <http://www.gnu.org/licenses>. >+ >+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..f46711b >--- /dev/null >+++ b/t/lib/Page.pm >@@ -0,0 +1,222 @@ >+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 <http://www.gnu.org/licenses>. >+ >+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. >+=cut >+ >+sub rebrandFromPageObject { >+ my ($class, $self) = @_; >+ bless($self, $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; >+} >+ >+################################################ >+ ## 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/Intra.pm b/t/lib/Page/Intra.pm >new file mode 100644 >index 0000000..464b45a >--- /dev/null >+++ b/t/lib/Page/Intra.pm >@@ -0,0 +1,195 @@ >+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 <http://www.gnu.org/licenses>. >+ >+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 _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 <span> 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 <http://www.gnu.org/licenses>. >+ >+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/MemberFlags.pm b/t/lib/Page/Members/MemberFlags.pm >new file mode 100644 >index 0000000..60448bd >--- /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 <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use Test::More; >+ >+use t::lib::Page::Members::Moremember; >+ >+use base qw(t::lib::Page::Intra); >+ >+=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/Moremember.pm b/t/lib/Page/Members/Moremember.pm >new file mode 100644 >index 0000000..82afa62 >--- /dev/null >+++ b/t/lib/Page/Members/Moremember.pm >@@ -0,0 +1,89 @@ >+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 <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use Scalar::Util qw(blessed); >+ >+use base qw(t::lib::Page::Intra); >+ >+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 >+################################################################################ >+ >+ >+ >+ >+ >+################################################################################ >+=head PageObject Services >+ >+=cut >+################################################################################ >+ >+ >+ >+ >+ >+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 <http://www.gnu.org/licenses>. >+ >+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/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 <http://www.gnu.org/licenses>. >+ >+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/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 <http://www.gnu.org/licenses>. >+ >+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..70e6383 >--- /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 <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use Test::More; >+ >+use base qw(t::lib::Page::Opac); >+ >+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<scalar(@$histories) ; $i++) { >+ $histories->[$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..975750a >--- /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 <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use base qw(t::lib::Page::Opac); >+ >+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! >\ No newline at end of file >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 <http://www.gnu.org/licenses>. >+ >+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
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 14536
:
41023
|
41052
|
41057
|
41121
|
41156
|
41254
|
41460
|
41524
|
41525
|
41534
|
41678
|
42060
|
42111
|
42118
|
42136
|
42160
|
42433
|
42435
|
42716
|
44410
|
47254
|
63245