
########
Selenium
########


****
NAME
****


Kernel::System::UnitTest::Selenium - run front end tests

This class inherits from Selenium::Remote::Driver. You can use
its full API (see
`https://metacpan.org/release/AIVATURI/Selenium-Remote-Driver-0.15/view/lib/Selenium/Remote/Driver.pm`).

Every successful Selenium command will be logged as a successful unit test.
In case of an error, an exception will be thrown that you can catch in your
unit test file and handle with \ ``HandleError()``\  in this class. It will output
a failing test result and generate a screen shot for analysis.

new()
=====


create a selenium object to run front end tests.

To do this, you need a running \ ``selenium``\  or \ ``phantomjs``\  server.

Specify the connection details in \ ``Config.pm``\ , like this:


.. code-block:: perl

     # For testing with Firefox until v. 47 (testing with recent FF and marionette is currently not supported):
     $Self->{'SeleniumTestsConfig'} = {
         remote_server_addr  => 'localhost',
         port                => '4444',
         platform            => 'ANY',
         browser_name        => 'firefox',
         extra_capabilities => {
             marionette     => \0,   # Required to run FF 47 or older on Selenium 3+.
         },
     };
 
     # For testing with Chrome/Chromium (requires installed geckodriver):
     $Self->{'SeleniumTestsConfig'} = {
         remote_server_addr  => 'localhost',
         port                => '4444',
         platform            => 'ANY',
         browser_name        => 'chrome',
         extra_capabilities => {
             chromeOptions => {
                 # disable-infobars makes sure window size calculations are ok
                 args => [ "disable-infobars" ],
             },
         },
     };


Then you can use the full API of `Selenium::Remote::Driver` on this object.


.. code-block:: perl

     # For testing with other selenium configurations, change the SeleniumTestsConfig directly without changing the Config.
 
     $Kernel::OM->ObjectParamAdd(
         'Kernel::System::UnitTest::Selenium' => {
             SeleniumTestsConfig  => {
                 remote_server_addr => 'selenium',
                 port               => '4444',
                 browser_name       => 'chrome',
                 extra_capabilities => {
                     chromeOptions => {
                         args => [ "disable-gpu", "disable-infobars" ],
                     },
                     marionette => '',
                 },
             }
         },
     );
 
     my $SeleniumObject = $Kernel::OM->Get('Kernel::System::UnitTest::Selenium');



RunTest()
=========


runs a selenium test if Selenium testing is configured.


.. code-block:: perl

     $SeleniumObject->RunTest( sub { ... } );



_execute_command()
==================


Override internal command of base class.

We use it to output successful command runs to the UnitTest object.
Errors will cause an exception and be caught elsewhere.


get()
=====


Override get method of base class to prepend the correct base URL.


.. code-block:: perl

     $SeleniumObject->get(
         $URL,
     );



get_alert_text()
================


Override get_alert_text() method of base class to return alert text as string.


.. code-block:: perl

     my $AlertText = $SeleniumObject->get_alert_text();


returns


.. code-block:: perl

     my $AlertText = 'Some alert text!'



VerifiedGet()
=============


perform a get() call, but wait for the page to be fully loaded (works only within Znuny).
Will die() if the verification fails.


.. code-block:: perl

     $SeleniumObject->VerifiedGet(
         $URL,
     );



VerifiedRefresh()
=================


perform a refresh() call, but wait for the page to be fully loaded (works only within Znuny).
Will die() if the verification fails.


.. code-block:: perl

     $SeleniumObject->VerifiedRefresh();



Login()
=======


login to agent or customer interface


.. code-block:: perl

     $SeleniumObject->Login(
         Type     => 'Agent', # Agent|Customer
         User     => 'someuser',
         Password => 'somepassword',
     );



WaitFor()
=========


wait with increasing sleep intervals until the given condition is true or the wait time is over.
Exactly one condition (JavaScript or WindowCount) must be specified.


.. code-block:: perl

     my $Success = $SeleniumObject->WaitFor(
         AlertPresent   => 1,                                 # Wait until an alert, confirm or prompt dialog is present
         Callback       => sub { ... }                        # Wait until function returns true
         ElementExists  => 'xpath-selector',                  # Wait until an element is present
         ElementExists  => ['css-selector', 'css'],
         ElementMissing => 'xpath-selector',                  # Wait until an element is not present
         ElementMissing => ['css-selector', 'css'],
         JavaScript     => 'return $(".someclass").length',   # JavaScript code that checks condition
         WindowCount    => 2,                                 # Wait until this many windows are open
         Time           => 20,                                # optional, wait time in seconds (default 20)
         SkipDie        => 1,                                 # Instead of a dying process do return the result of the wait for
     );



SwitchToFrame()
===============


Change focus to another frame on the page. If \ ``WaitForLoad``\  is passed, it will wait until the frame has loaded the
page completely.


.. code-block:: perl

     my $Success = $SeleniumObject->SwitchToFrame(
         FrameSelector => '.Iframe',     # (required) CSS selector of the frame element
         WaitForLoad   => 1,             # (optional) Wait until the frame has loaded, if necessary
         Time          => 20,            # (optional) Wait time in seconds (default 20)
     );



DragAndDrop()
=============


Drag and drop an element.


.. code-block:: perl

     $SeleniumObject->DragAndDrop(
         Element         => '.Element', # (required) css selector of element which should be dragged
         Target          => '.Target',  # (required) css selector of element on which the dragged element should be dropped
         TargetOffset    => {           # (optional) Offset for target. If not specified, the mouse will move to the middle of the element.
             X   => 150,
             Y   => 100,
         }
     );



HandleError()
=============


use this method to handle any Selenium exceptions.


.. code-block:: perl

     $SeleniumObject->HandleError($@);


It will create a failing test result and store a screen shot of the page
for analysis (in folder /var/otrs-unittest if it exists, in $Home/var/httpd/htdocs otherwise).


DEMOLISH()
==========


override DEMOLISH from `Selenium::Remote::Driver` (required because this class is managed by Moo).
Performs proper error handling (calls \ ``HandleError()``\  if needed). Adds a unit test result to indicate the shutdown,
and performs some clean-ups.



********************
DEPRECATED FUNCTIONS
********************


WaitForjQueryEventBound()
=========================


waits until event handler is bound to the selected \ ``jQuery``\  element. Deprecated - it will be removed in the future releases.


.. code-block:: perl

     $SeleniumObject->WaitForjQueryEventBound(
         CSSSelector => 'li > a#Test',       # (required) css selector
         Event       => 'click',             # (optional) Specify event name. Default 'click'.
     );



InputFieldValueSet()
====================


sets modernized input field value.


.. code-block:: perl

     $SeleniumObject->InputFieldValueSet(
         Element => 'css-selector',              # (required) css selector
         Value   => 3,                           # (optional) Value
     );



SendKeys()
==========


Wrapper for the selenium function 'send_keys'.
Send the content as single key presses/pushes to form/input.


.. code-block:: perl

     my $Success = $SeleniumObject->SendKeys(
         Selector     => '#DynamicField_Test',
         SelectorType => 'css',                  # optional
         Content      => 'ABCDEFG',
     );


Returns:


.. code-block:: perl

     my $Success = 1;



SelectOption()
==============


Select a option value of selection field.
Can also be used to select autocomplete fields.


.. code-block:: perl

     my $Success = $SeleniumObject->SelectOption(
         Selector => 'li.ui-menu-item',
         Content  => 'ABCDEFG',
     );


Returns:


.. code-block:: perl

     my $Success = 1;



InputGet()
==========


Wrapper for the Znuny.Form.Input JavaScript namespace 'Get' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputGet(
         Attribute => 'QueueID',
         Options   => {                          # optional
             KeyOrValue => 'Value',              # default is 'Key'
         }
     );
 
     $Result = 'Postmaster';



InputSet()
==========


Wrapper for the Znuny.Form.Input JavaScript namespace 'Set' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputSet(
         Attribute   => 'QueueID',
         WaitForAJAX => 0,                       # optional, default 1
         Content     => 'Misc',                  # optional, none removes content
         Options     => {                        # optional
             KeyOrValue    => 'Value',           # default is 'Key'
             TriggerChange => 0,                 # default is 1
         }
     );


!!!! ATTENTION !!!!
For setting DynamicField Date or DateTime Fields the call should look like:


.. code-block:: perl

     my $Result = $SeleniumObject->InputSet(
         Attribute => 'DynamicField_NameOfYourDateOrDateTimeField',
         Content   => {
             Year   => '2016',
             Month  => '08',
             Day    => '30',
             Hour   => '00',
             Minute => '00',
             Second => '00',
             Used   => 1, # THIS ONE IS IMPORTANT -
                        # if not set to 1 field will not get activated and though not transmitted
         },
         WaitForAJAX => 1,
         Options     => {
             TriggerChange => 1,
         }
     );


For Checkboxes the call has to be done with undef,
everything else like '0', 0,... will fail. Example:


.. code-block:: perl

     my $Result = $SeleniumObject->InputSet(
         Attribute   => 'DynamicField_ExampleCheckbox',
         WaitForAJAX => 0,                       # optional, default 1
         Content     => undef,                   # optional, none removes content
         Options     => {                        # optional
             TriggerChange => 1,                 # default is 1
         }
     );
 
     $Result = 1;



InputMandatory()
================


Wrapper for the Znuny.Form.Input JavaScript namespace 'Mandatory' function.
Sets OR returns the Mandatory state of an input field.


.. code-block:: perl

     # Set mandatory state:
 
     my $Result = $SeleniumObject->InputMandatory(
         Attribute => 'QueueID',
         Mandatory => 1,         # 1 or 0
     );
 
     $Result = 1;
 
     # OR return mandatory state:
 
     my $Result = $SeleniumObject->InputMandatory(
         Attribute => 'QueueID',
     );
 
     $Result = 0;



InputFieldID()
==============


Wrapper for the Znuny.Form.Input JavaScript namespace 'FieldID' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputFieldID(
         Attribute => 'QueueID',
     );
 
     $Result = 'Dest';



InputType()
===========


Wrapper for the Znuny.Form.Input JavaScript namespace 'Type' function.
Attention: Requires the FieldID - not the Attribute! (See InputFieldID)


.. code-block:: perl

     my $Result = $SeleniumObject->InputType(
         FieldID => 'Dest',
     );
 
     $Result = 'select';



InputHide()
===========


Wrapper for the Znuny.Form.Input JavaScript namespace 'Hide' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputHide(
         Attribute => 'QueueID',
     );
 
     my $Result = 1;



InputExists()
=============


Wrapper for the Znuny.Form.Input JavaScript namespace 'Exists' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputExists(
         Attribute => 'QueueID',
     );
 
     $Result = 1;



InputShow()
===========


Wrapper for the Znuny.Form.Input JavaScript namespace 'Show' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputShow(
         Attribute => 'QueueID',
     );
 
     $Result = 1;



InputModule()
=============


Wrapper for the Znuny.Form.Input JavaScript namespace 'Module' function.


.. code-block:: perl

     my $Result = $SeleniumObject->InputModule(
         Action => 'QueueID',
     );
 
     $Result = 1;



InputFieldIDMapping()
=====================


Wrapper for the Znuny.Form.Input JavaScript namespace 'FieldIDMapping' function.
Sets OR returns the mapping structure of the given Action.


.. code-block:: perl

     my $Result = $SeleniumObject->InputFieldIDMapping(
         Action  => 'AgentTicketZoom',
         Mapping => {
             # ...
             QueueID => 'DestQueueID',
             # ...
         },
     );
 
     $Result = 1;
 
     # OR
 
     my $Result = $SeleniumObject->InputFieldIDMapping(
         Action  => 'AgentTicketZoom',
     );
 
     $Result = {
         DestQueueID => 'DestQueueID',
         QueueID     => 'DestQueueID'
     };



AgentLogin()
============


Creates and logs in an Agent. Calls TestUserDataGet and Login on the ZnunyHelper object.


.. code-block:: perl

     my %UserData = $SeleniumObject->AgentLogin(
         Groups => ['admin', 'users'],           # optional, list of groups to add this user to (rw rights)
         Language => 'de'                        # optional, defaults to 'en' if not set
     );
 
     %UserData = {
         UserID        => 2,
         UserFirstname => $TestUserLogin,
         UserLastname  => $TestUserLogin,
         UserLogin     => $TestUserLogin,
         UserPw        => $TestUserLogin,
         UserEmail     => $TestUserLogin . '@localunittest.com',
     };



CustomerUserLogin()
===================


Creates and logs in an CustomerUser. Calls TestCustomerUserDataGet and Login on the ZnunyHelper object.


.. code-block:: perl

     my %CustomerUserData = $SeleniumObject->CustomerUserLogin(
         Language => 'de' # optional, defaults to 'en' if not set
     );
 
     %CustomerUserData = {
         CustomerUserID => 1,
         Source         => 'CustomerUser',
         UserFirstname  => $TestUserLogin,
         UserLastname   => $TestUserLogin,
         UserCustomerID => $TestUserLogin,
         UserLogin      => $TestUserLogin,
         UserPassword   => $TestUserLogin,
         UserEmail      => $TestUserLogin . '@localunittest.com',
         ValidID        => 1,
     };



SwitchToPopUp()
===============


Switches the Selenium context to the PopUp


.. code-block:: perl

     $SeleniumObject->SwitchToPopUp(
         WaitForAJAX => 0, # optional, default 1
     );



SwitchToMainWindow()
====================


Switches the Selenium context to the main window


.. code-block:: perl

     $SeleniumObject->SwitchToMainWindow(
         WaitForAJAX => 0, # optional, default 1
     );



PageContains()
==============


Checks if the currently opened page contains the given String


.. code-block:: perl

     $SeleniumObject->PageContains(
         String  => 'Ticked locked.',
         Message => "Page contains 'Ticket locked.'" # optional - default
     );



PageContainsNot()
=================


Checks if the currently opened page does not contain the given String


.. code-block:: perl

     $SeleniumObject->PageContainsNot(
         String  => 'Ticked locked.',
         Message => "Page does not contain 'Ticket locked.'" # optional - default
     );



AJAXCompleted()
===============


Waits for AJAX requests to be completed by checking the jQuery 'active' attribute.


.. code-block:: perl

     $SeleniumObject->AJAXCompleted();



AgentInterface()
================


Performs a GET request to the AgentInterface with the given parameters. Interally _GETInterface is called.


.. code-block:: perl

     $SeleniumObject->AgentInterface(
         Action      => 'AgentTicketZoom',
         WaitForAJAX => 0,                     # optional, default 1
     );



AgentRequest()
==============


Performs a GET request to a non-JavaScript controller in the AgentInterface with the given parameters. Interally _GETRequest is called.


.. code-block:: perl

     $SeleniumObject->AgentRequest(
         Action      => 'CustomerUserSearch',
         Param       => {
             Term => 'test-customer-user'
         }
     );



CustomerInterface()
===================


Performs a GET request to the CustomerInterface with the given parameters. Interally _GETInterface is called.


.. code-block:: perl

     $SeleniumObject->CustomerInterface(
         Action      => 'CustomerTicketMessage',
         WaitForAJAX => 0,                      # optional, default 1
     );



CustomerRequest()
=================


Performs a GET request to a non-JavaScript controller in the CustomerInterface with the given parameters. Interally _GETRequest is called.


.. code-block:: perl

     $SeleniumObject->CustomerRequest(
         Action      => 'CustomerUserSearch',
         Param       => {
             Term => 'test-customer-user'
         }
     );



PublicInterface()
=================


Performs a GET request to the PublicInterface with the given parameters. Interally _GETInterface is called.


.. code-block:: perl

     $SeleniumObject->PublicInterface(
         Action      => 'PublicFAQ',
         WaitForAJAX => 0,             # optional, default 1
     );



PublicRequest()
===============


Performs a GET request to a non-JavaScript controller in the PublicInterface with the given parameters. Interally _GETRequest is called.


.. code-block:: perl

     $SeleniumObject->PublicRequest(
         Action      => 'PublicUserSearch',
         Param       => {
             Term => 'test-customer-user'
         }
     );



_GETInterface()
===============


Performs a GET request to the given Interface with the given parameters. Interally VerifiedGet is called.
Request waits till page has finished loading via checking if the jQuery Object has been initialized and
all AJAX requests are completed via function AJAXCompleted.


.. code-block:: perl

     $SeleniumObject->_GETInterface(
         Interface   => 'Agent',           # or Customer or Public
         WaitForAJAX => 0,                 # optional, default 1
         Param       => {                  # optional
             Action => AgentTicketZoom,
         }
     );



_GETRequest()
=============


Performs a GET request to a Request endpoint in the given Interface with the given parameters. Interally Seleniums get is called.


.. code-block:: perl

     $SeleniumObject->_GETRequest(
         Interface   => 'Agent',           # or Customer or Public
         Param       => {                  # optional
             Action => AgentTicketZoom,
         }
     );



RequestURLBuild()
=================


This function builds a requestable HTTP GET URL to the given OTRS interface with the given parameters


.. code-block:: perl

     my $RequestURL = $SeleniumObject->RequestURLBuild(
         Interface   => 'Agent',           # or Customer or Public
         Param       => {                  # optional
             Action => AgentTicketZoom,
         }
     );
 
     $RequestURL = 'http://localhost/znuny/index.pl?Action=AgentTicketZoom';



_Hash2GETParamString()
======================


Converts a Hash into a GET Parameter String, without the leading ?. Inspired by http://stackoverflow.com/a/449204


.. code-block:: perl

     my $Result = $SeleniumObject->_Hash2GETParamString(
         Action   => 'AgentTicketZoom',
         TicketID => 1,
     );
 
     my $Result = $SeleniumObject->_Hash2GETParamString(
         Action   => 'AgentTicketZoom',
         TicketID => \@TicketIDs,
     );
 
     $Result = 'Action=AgentTicketZoom;TicketID=1';



FindElementSave()
=================


This function is a wrapper around the 'find_element' function which can be used to check if elements are even present.


.. code-block:: perl

     my $Element = $SeleniumObject->FindElementSave(
         Selector     => '#GroupID',
         SelectorType => 'css',        # optional
     );
 
     is equivalent to:
 
     $Element = $Self->find_element('#GroupID', 'css');



ElementExists()
===============


This function checks if a given element exists.


.. code-block:: perl

     $SeleniumObject->ElementExists(
         Selector     => '#GroupID',
         SelectorType => 'css',        # optional
     );



ElementExistsNot()
==================


This function checks if a given element does not exist.


.. code-block:: perl

     $SeleniumObject->ElementExistsNot(
         Selector     => '#GroupID',
         SelectorType => 'css',        # optional
     );



CreateScreenshot()
==================


CreateScreenshot.


.. code-block:: perl

     my $Success = $SeleniumObject->CreateScreenshot();


Returns:


.. code-block:: perl

     my $Success = 1;



GetScreenshotFileName()
=======================



.. code-block:: perl

     my $ScreenshotFileName = $SeleniumObject->GetScreenshotFileName(
         Filename => 'ZnunyRocks',
         # or
         Line     => '359',
         Function => 'InputFieldID',
         Hook     => 'BEFORE',
     );


Returns:


.. code-block:: perl

     #                         TIME      - Path to Test       - Line   - Function   - BEFORE or AFTER function
     my $ScreenshotFileName = '1497085163-Znuny_Selenium_Input-Line=359-InputFieldID-BEFORE.png';



GetScreenshotDirectory()
========================



.. code-block:: perl

     my %ScreenshotDirectory = $SeleniumObject->GetScreenshotDirectory(
         Directory => 'Captured',
     );


Returns:


.. code-block:: perl

     my %ScreenshotDirectory = (
         WebPath  => 'SeleniumScreenshots/Captured',
         FullPath => '/opt/znuny/var/httpd/htdocs/SeleniumScreenshots/Captured',
     );



GetScreenshotURL()
==================



.. code-block:: perl

     my $ScreenshotURL = $SeleniumObject->GetScreenshotURL(
         WebPath  = '/otrs-web/SeleniumScreenshots/ZnunyRocks/',
         Filename = 'AgentTicketZoom',
     );


Returns:


.. code-block:: perl

     my $ScreenshotURL = 'URL';



GetSeleniumHome()
=================



.. code-block:: perl

     my $SeleniumHome = $SeleniumObject->GetSeleniumHome(
         Directory => '/opt/znuny',
     );


Returns:


.. code-block:: perl

     my $SeleniumHome = '/opt/znuny';





