Skip to content

Commit

Permalink
Merge pull request #687 from RotherOSS/issue-#686-subtests_for_selenium
Browse files Browse the repository at this point in the history
Issue #686 subtests for selenium
  • Loading branch information
bschmalhofer committed Dec 29, 2020
2 parents 5d0099b + 12add78 commit a4613df
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 87 deletions.
165 changes: 92 additions & 73 deletions Kernel/System/UnitTest/Selenium.pm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use Time::HiRes qw();

# CPAN modules
use Devel::StackTrace();
use Test2::API qw(context);
use Test2::API qw(context run_subtest);
use Net::DNS::Resolver;

# OTOBO modules
Expand All @@ -47,9 +47,9 @@ our @ObjectDependencies = (
'Kernel::System::UnitTest::Helper',
);

# If a test throws an exception, we'll record it here in a package variable so that we can
# take screenshots of *all* Selenium instances that are currently running on shutdown.
our $TestException;
# If a test throws an exception, we'll record it here in a file scoped variable so that we can
# take screenshots of *all* Selenium instances that are currently running on shutdown.
my $TestException;

=head1 NAME
Expand Down Expand Up @@ -211,7 +211,8 @@ sub new {
}

sub SeleniumErrorHandler {
my ( $Self, $Error ) = @_;
my $Self = shift;
my ( $Error ) = @_;

my $SuppressFrames;

Expand Down Expand Up @@ -297,19 +298,10 @@ sub _execute_command { ## no critic
my $Self = shift;
my ($Res, $Params) = @_;

# an exception is thrown in case of an error
my $Result = $Self->SUPER::_execute_command( $Res, $Params );

# The command 'quit' is called in the destructor on this packages.
# Destruction usually happens after done_testing(), which is bad.
# So don't emit a testing event for 'quit'.
if ( ref $Res eq 'HASH' && $Res->{command} ) {
my %CommandIsSkipped = (
quit => 1,
screenshot => 1,
);

return $Result if $CommandIsSkipped{ $Res->{command} };
}
return $Result if $Self->{SuppressCommandRecording};

my $TestName = 'Selenium command success: ';
$TestName .= $Kernel::OM->Get('Kernel::System::Main')->Dump(
Expand All @@ -321,14 +313,7 @@ sub _execute_command { ## no critic

my $Context = context();

if ( $Self->{SuppressCommandRecording} ) {
$Context->note( $TestName );
}
else {
$Context->pass( $TestName );
}

$Context->release();
$Context->pass_and_release( $TestName );

return $Result;
}
Expand Down Expand Up @@ -391,12 +376,20 @@ Will die() if the verification fails.
sub VerifiedGet {
my ( $Self, $URL ) = @_;

$Self->get($URL);
my $Context = context();

$Self->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after page load.";
my $Code = sub {
$Self->get($URL);

$Self->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after page load.";
};

run_subtest( 'VerifiedGet', $Code, { buffered => 1, inherit_trace => 1 } );

$Context->release;

return;
}
Expand All @@ -411,14 +404,23 @@ Will die() if the verification fails.
=cut

sub VerifiedRefresh {
my ( $Self, $URL ) = @_;
my $Self = shift;
my ( $URL ) = @_;

$Self->refresh();
my $Context = context();

$Self->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after page load.";
my $Code = sub {
$Self->refresh();

$Self->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after page load.";
};

run_subtest( 'VerifiedRefresh', $Code, { buffered => 1, inherit_trace => 1 } );

$Context->release;

return;
}
Expand Down Expand Up @@ -452,55 +454,57 @@ sub Login {

my $Context = context();

$Context->note( 'Initiating login...' );
my $Code = sub {
# we will try several times to log in
my $MaxTries = 5;

# we will try several times to log in
my $MaxTries = 5;
TRY:
for my $Try ( 1 .. $MaxTries ) {

TRY:
for my $Try ( 1 .. $MaxTries ) {
eval {
my $ScriptAlias = $Kernel::OM->Get('Kernel::Config')->Get('ScriptAlias');

eval {
my $ScriptAlias = $Kernel::OM->Get('Kernel::Config')->Get('ScriptAlias');
if ( $Param{Type} eq 'Agent' ) {
$ScriptAlias .= 'index.pl';
}
else {
$ScriptAlias .= 'customer.pl';
}

if ( $Param{Type} eq 'Agent' ) {
$ScriptAlias .= 'index.pl';
}
else {
$ScriptAlias .= 'customer.pl';
}
$Self->get("${ScriptAlias}");

$Self->get("${ScriptAlias}");
$Self->delete_all_cookies();
$Self->VerifiedGet("${ScriptAlias}?Action=Login;User=$Param{User};Password=$Param{Password}");

$Self->delete_all_cookies();
$Self->VerifiedGet("${ScriptAlias}?Action=Login;User=$Param{User};Password=$Param{Password}");
# login successful?
$Self->find_element( 'a#LogoutButton', 'css' ); # dies if not found

# login successful?
$Self->find_element( 'a#LogoutButton', 'css' ); # dies if not found
$Context->pass( 'Login sequence ended...' );
};

$Context->pass( 'Login sequence ended...' );
};
# an error happend
if ($@) {

# an error happend
if ($@) {
$Context->note( "Login attempt $Try of $MaxTries not successful." );

$Context->note( "Login attempt $Try of $MaxTries not successful." );
# try again
next TRY if $Try < $MaxTries;

# try again
next TRY if $Try < $MaxTries;
$Context->release();

$Context->release();
die "Login failed!";
}

die "Login failed!";
# login was sucessful
else {
last TRY;
}
}
};

# login was sucessful
else {
last TRY;
}
}
run_subtest( 'Login', $Code, { buffered => 1, inherit_trace => 1 } );

$Context->release();
$Context->release;

return 1;
}
Expand Down Expand Up @@ -708,7 +712,7 @@ for analysis (in folder /var/otobo-unittest if it exists, in $Home/var/httpd/htd

sub HandleError {
my $Self = shift;
my ( $Error, $CalledInDemolish ) = @_;
my ( $Error, $InGlobalDestruction ) = @_;

# If we really have a selenium error, get the stack trace for it.
if ( $Self->{_SeleniumStackTrace} && $Error eq $Self->{_SeleniumException} ) {
Expand All @@ -717,7 +721,7 @@ sub HandleError {

my $Context = context();

if ( $CalledInDemolish ) {
if ( $InGlobalDestruction ) {
$Context->note( $Error );
}
else {
Expand Down Expand Up @@ -803,13 +807,28 @@ and performs some clean-ups.

sub DEMOLISH {
my $Self = shift;
my ($InGlobalDestruction) = @_;

# Looks like for some reason $InGlobalDestruction is not reliable.
# So, let's assume that the Kernel::System::UnitTest::Selenium is always demolished
# after the done_testing().
$InGlobalDestruction = 1;

if ($TestException) {
$Self->HandleError($TestException, 1);
$Self->HandleError($TestException, $InGlobalDestruction);
}

if ( $Self->{SeleniumTestsActive} ) {
$Self->SUPER::DEMOLISH(@_);

# no testing event from auto-quitting Selenium
if ( $InGlobalDestruction ) {
local $Self->{SuppressCommandRecording} = 1;

$Self->SUPER::DEMOLISH(@_);
}
else {
$Self->SUPER::DEMOLISH(@_);
}

# Cleanup possibly leftover zombie firefox profiles.
my @LeftoverFirefoxProfiles = $Kernel::OM->Get('Kernel::System::Main')->DirectoryRead(
Expand All @@ -833,7 +852,7 @@ sub DEMOLISH {

my %SessionData = $AuthSessionObject->GetSessionIDData( SessionID => $SessionID );

next SESSION if !%SessionData;
next SESSION unless %SessionData;
next SESSION
if $SessionData{UserSessionStart} && $SessionData{UserSessionStart} < $Self->{TestStartSystemTime};

Expand Down
53 changes: 39 additions & 14 deletions Kernel/System/UnitTest/Selenium/WebElement.pm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@

package Kernel::System::UnitTest::Selenium::WebElement;

=head1 NAME
Kernel::System::UnitTest::Selenium::WebElement - Utility functions for Selenium WebElements
=head1 SUBROUTINES
=cut

use strict;
use warnings;
use v5.24;
Expand All @@ -24,11 +32,12 @@ use utf8;

use parent qw(Selenium::Remote::WebElement);

=head1 NAME
# core modules

Kernel::System::UnitTest::Selenium::WebElement - Utility functions for Selenium WebElements
# CPAN modules

=head1 SUBROUTINES
# OTOBO modules
use Test2::API qw/context run_subtest/;

=head2 VerifiedSubmit()
Expand All @@ -42,12 +51,20 @@ sub VerifiedSubmit {
my $Self = shift;
my ($Params) = @_;

$Self->submit();
my $Context = context();

my $Code = sub {
$Self->submit();

$Self->driver()->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after element submit.";
};

$Self->driver()->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after element submit.";
run_subtest( 'VerifiedSubmit', $Code, { buffered => 1, inherit_trace => 1 } );

$Context->release;

return;
}
Expand All @@ -66,14 +83,22 @@ click an element that causes a page get/reload/submit and wait for the page to b
sub VerifiedClick { ## no critic
my $Self = shift;

$Self->driver()->execute_script('window.Core.App.PageLoadComplete = false;');
my $Context = context();

my $Code = sub {
$Self->driver()->execute_script('window.Core.App.PageLoadComplete = false;');

$Self->SUPER::click(@_);

$Self->driver()->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after element click.";
};

$Self->SUPER::click(@_);
run_subtest( 'VerifiedClick', $Code, { buffered => 1, inherit_trace => 1 } );

$Self->driver()->WaitFor(
JavaScript =>
'return typeof(Core) == "object" && typeof(Core.App) == "object" && Core.App.PageLoadComplete'
) || die "OTOBO API verification failed after element click.";
$Context->release;

return;
}
Expand Down

0 comments on commit a4613df

Please sign in to comment.