Selenium#

NAME#

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

This class inherits from Selenium::Remote::Driver. You can use its full API (see http://search.cpan.org/~aivaturi/Selenium-Remote-Driver-0.15/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:

# 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.

# 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.

$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.

$SeleniumObject->get(
    $URL,
);

get_alert_text()#

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

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

returns

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.

$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.

$SeleniumObject->VerifiedRefresh();

Login()#

login to agent or customer interface

$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.

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.

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.

$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.

$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.

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

InputFieldValueSet()#

sets modernized input field value.

$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.

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

Returns:

my $Success = 1;

SelectOption()#

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

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

Returns:

my $Success = 1;

InputGet()#

Wrapper for the Znuny.Form.Input JavaScript namespace ‘Get’ function.

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.

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:

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:

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.

# 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.

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)

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

$Result = 'select';

InputHide()#

Wrapper for the Znuny.Form.Input JavaScript namespace ‘Hide’ function.

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

my $Result = 1;

InputExists()#

Wrapper for the Znuny.Form.Input JavaScript namespace ‘Exists’ function.

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

$Result = 1;

InputShow()#

Wrapper for the Znuny.Form.Input JavaScript namespace ‘Show’ function.

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

$Result = 1;

InputModule()#

Wrapper for the Znuny.Form.Input JavaScript namespace ‘Module’ function.

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.

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.

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.

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

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

SwitchToMainWindow()#

Switches the Selenium context to the main window

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

PageContains()#

Checks if the currently opened page contains the given String

$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

$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.

$SeleniumObject->AJAXCompleted();

AgentInterface()#

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

$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.

$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.

$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.

$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.

$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.

$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.

$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.

$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

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

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.

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.

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

ElementExistsNot()#

This function checks if a given element does not exist.

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

CreateScreenshot()#

CreateScreenshot.

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

Returns:

my $Success = 1;

GetScreenshotFileName()#

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

Returns:

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

GetScreenshotDirectory()#

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

Returns:

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

GetScreenshotURL()#

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

Returns:

my $ScreenshotURL = 'URL';

GetSeleniumHome()#

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

Returns:

my $SeleniumHome = '/opt/znuny';