Selenium 2.* is running on Linux with Firefox as the browser.
I am using perl with the Selenium::Remote::Driver module to interact with the server.
Is there anything available to check if an alert is present? The perl module provides several functions to click OK on an alert, or to get the text from it, but this will raise an error if there is no alert - how to avoid an error and still get rid of any alert?
Basically, I want to remove all alerts when a page finished loading (if any), but not sure how?
Another option that I tried is disabling all alerts by setting a variable in the firefox profile (which works when you use the browser yourself), but somehow the alert is still there when the browser is used by Selenium because i think Selenium handles the alert itself because of the "handlesAlerts" capability, which is always set to true, and I'm not sure how to disable it. It may be the solution if it's not possible to check for the existence of an alert.
Anyone have an idea?
You could try to dismiss alerts, use eval block to handle exceptions
eval {
$driver->accept_alert;
};
if ($#){
warn "Maybe no alert?":
warn $#;
}
I created a couple of functions that check for an alert and then either cancel or interact with it as needed.
use Try::Tiny qw( try catch );
# checks if there is a javascript alert/confirm/input on the screen
sub alert_is_present
{
my $d = shift;
my $alertPresent = 0;
try{
my $alertTxt = $d->get_alert_text();
logIt( "alert open: $alertTxt", 'DEBUG' ) if $alertTxt;
$alertPresent++;
}catch{
my $err = $_;
if( $err =~ 'modal dialog when one was not open' ){
logIt( 'no alert open', 'DEBUG2' );
}else{
logIt( "ERROR: getting alert_text: $_", 'ERROR' );
}
};
return $alertPresent;
}
# Assumes caller confirmed an alert is present!! Either cancels the alert or
types any passed in data and accepts it.
sub handle_alert
{
my ( $d, $action, $data ) = #_;
logIt( "handle_alert called with: $action, $data", 'DEBUG' );
if( $action eq 'CANCEL' ){
$d->dismiss_alert();
}else{
$d->send_keys_to_alert( $data )
if $data;
$d->accept_alert();
}
$d->pause( 500 );
}
Related
I've currently got a small script running that sends a 401 to the client, upon cancelling and not providing user details the script will return nothing.
I'd like to send a redirect to the page they have come from instead.
The main subroutine looks like this;
#!usr/bin/perl
use strict;
use CGI;
sub checkAuth {
my ($user, $pass) = &getAuthUsers(); # Get the user and pass of already authenticated users.
unless ($user) {
&sendAuthenticationHeader(); # Send 401
}
# Check user against DB and return 1 for success.
if ( &checkUser($user, $pass) eq 'Y') { return 1 };
else { # This is the redirect I'm trying to issue.
my $cgi = CGI->new();
print $cgi->redirect($ENV{HTTP_REFERER}); # Redirect to the referer url
exit;
}
}
Unfortunately whenever I try to send new headers it's just received as plain text.
Any help is appreciated, thanks in advance.
sendAuthenticationHeader() emits a header with a 401 status code.
print $cgi->redirect($ENV{HTTP_REFERER}); emits a header with a 302 status code. Of course, since you've already emitted a header, this gets treated as the body.
There's no point to return a 401 if you want to redirect. Change your code to
sub checkAuth {
my ($user, $pass) = getAuthUsers();
if (!$user || !checkUser($user, $pass)) {
print CGI::redirect($ENV{HTTP_REFERER});
exit;
}
}
Notes:
Removed incorrect &. Don't tell Perl to ignore the prototype of subs. Address the underlying issue instead if required.
The return value of checkUser is boolean, so it should return either a true or a false value (e.g. 0 or 1), not two true values (e.g. N or Y). The above code assumed you fixed this.
I'm writing a software testing framework using perl on Windows platform, which run test cases by calling the software under test. If a test case fails, the framework will capture the screen so we could get more information about the failure.
At first I used an small program called boxcutter-fs.exe. So all I need is to call this program when test case fails:
system("boxcutter-fs.exe screenshot.png");
print "Failed: $?" if ($?);
When the framework handles a normal failure, it works great and give me the right failure screenshot. But I noticed that when the software crashed (an error message box would occur on the active window, and the software under test will be killed after a timeout), boxcutter-fs.exe exited with code 1, and didn't get any screenshot.
Then I turned to other solutions. The first alternative that I tried is Win32::GuiTest:
eval {
SendKeys('{PRTSCR}');
my $screen = Win32::Clipboard::GetBitmap() or die "No image captured: $!\n";
open BITMAP, "> screenshot.bmp" or die "Couldn't open bitmap file: $!\n";
binmode BITMAP;
print BITMAP $screen;
close BITMAP;
};
print "$#" if ($#);
The same result. This works well unless the software crash case occurred. The program reported No image captured so I think Win32::Clipboard::GetBitmap didn't get any thing in the Clipboard.
The last solution is Imager::Screenshot:
eval {
my $img = screenshot(hwnd => 'active');
$img->write(file => 'screenshot.bmp', type => 'bmp' )
or die "Failed: ", $img->{ERRSTR} , "\n";
};
print "$#" if ($#);
This time it gave a black screen screenshot (an all-black image) when the software crash case occurs. Still doesn't work.
Then I found that when the crash and error message box occurs, but the software hasn't been killed so the test framework is still hanging, using a small script with any of the solutions above could capture the screenshot. It seems they just fail at the moment when the software under test is being killed.
Since these 3 methods all use Win32 API to get the screenshot, I wonder they might fail due to the same issue? Any hints?
I studied the source code of Imager::Screenshot, and found the possible cause for the screenshot failure.
First of all, if I use -d option of perl to debug the screenshot script, when the software under test crashed and was killed after a timeout, the screenshot worked. So I suppose the screenshot failure should be a corner case in a specific situation.
Then I read the source code of Imager::Screenshot. Basically, it's a perl module calling XS extensions written with Win32 APIs. The processing flow is basically as following:
Use GetDC according to the window handler hwnd to get display device context dc
Use CreateCompatibleDC to get the device context handler hdc
Use GetDIBits to retrieve the bits of the device context, and write them to the bmp file
My problem is that when the software under test crashed and was killed, the hwnd of its window would be invalid at once, but it was still passed to GetDC to get the display device context, thus the result was invalid too (the bmp file was memset to all 0 at the beginning, so it's a black screenshot)
Now that I noticed the root cause was the invalid hwnd, I come up with a work around: take the screenshot before killing the software under test. I used Proc::Background and Win32::GuiTest. The key point is to ensure the software GUI is set as foreground window:
sub captureWindow {
my ($pid, $screenshot_name) = #_;
for my $hwnd (&findHwnd($pid)) {
if (Win32::GuiTest::SetActiveWindow($hwnd) && Win32::GuiTest::SetForegroundWindow($hwnd)) {
system("boxcutter-fs.exe $screenshot_name");
# send ALT+TAB key so the script was set back to foreground window
Win32::GuiTest::SendKeys("%{TAB}");
last;
}
}
}
sub findHwnd {
my ($target_pid) = #_;
my #target_hwnd;
EnumWindows(
Win32::API::Callback->new(sub {
my ($hwnd, $target_pid) = #_;
my $pid = 0xffffffff;
my $tid = GetWindowThreadProcessId($hwnd, $pid);
$pid = unpack 'L', $pid;
if ($target_pid == $pid) {
push #target_hwnd, $hwnd;
}
return 1;
}, 'NN', 'I'),
$target_pid,
);
return #target_hwnd;
}
sub monitorTestProcess {
my ($cmd, $timeout) = #_;
my $rs;
my $proc = Proc::Background->new($cmd);
my $pid = $proc->pid;
select(undef, undef, undef, 0.5);
&captureWindow($pid, "screenshot_begin.png");
my $timeCount = 0;
while ($proc->alive) {
if ($timeCount >= $timeout) {
&captureWindow($pid, "screenshot_timeout.png");
$proc->die;
last;
}
select(undef, undef, undef, 1);
$timeCount++;
}
return $rs;
}
I'm writing a script to assist people who'll scan a barcode and get a response to keep or dispose the scanned sample. I want to have a message, similar to tk's messagebox or Win32::MsgBox but one that requires no user interaction to go away after three seconds.
My thought was to create the messages in a child process, using alarm to kill the process after a delay. In Tk:
sub tmpMsgBox {
my ($message,$delay) = #_;
if (fork() == 0) {
my $topWin = MainWindow->new;
my $label = $topWin->Label();
my $ok = $topWin->Button();
$label->pack(-side => 'top');
$ok->pack(-side => 'bottom');
$label->configure(-text => $message);
$ok->configure(-text => 'Ok', -command => sub {exit});
$SIG{ALRM} = sub {exit};
alarm $delay || 1;
$topWin->MainLoop;
}
}
for (3..10) {
tmpMsgBox("This window will disappear in $_ seconds", $_);
}
I don't think Tk plays nicely with fork, though, so this idea probably won't work so well if you are also using Tk in your main process.
Desktop::Notify is the standard-compliant interface to the desktop's passive notification pop-ups.
perl -MDesktop::Notify -e'
Desktop::Notify
->new
->create(
body => q{why hello there},
timeout => 3000
)->show'
What you want to do is to send a destroy message to the window after your timeout (remembering to cancel the sending of the message if the user does choose something!) Tk's certainly capable of doing this.
# Make the timeout something like this...
$id = $widget->after(3000, sub {
$widget->destroy;
});
# To cancel, just do...
$id->cancel;
You also need to make sure that you don't block when the widget is forced to go away, of course. This also prevents trouble if someone kills the widget by other means too, so it's a double-bonus.
I am able to open Lotus notes api using Perl, without errors, also I can get list of views that includes Inbox, but when I try to read messages from that view it appears empty? What might I be doing wrong? (in fact it seems like something might of changed on notes side as this code used to work before)
Result of code below:
NAME of View is: ($Inbox) has count of: 0
etc.
CODE:
use Win32::OLE;
my $Notes = Win32::OLE->new('Notes.NotesSession')
or die "Cannot start Lotus Notes Session object.\n";
my $database = $Notes->GetDatabase("",'mail\VIMM.nsf');
$database->OpenMail;
my $array_ref = $database->{Views};
foreach my $view (#$array_ref) {
my $name = $view->{Name};
print "NAME of View is: $name ";
$view = $database->GetView($name);
print "has count of: ", $view->{entryCount}, "\n";
}
Is the mailbox open to all users? You could try setting the -Default- access to Manager and grant it all available roles, just to make sure it's not a security issue preventing the documents from being seen.
I believe it is spelled "EntryCount"?
Also, I recommend "use strict" and "use warnings".
Per runrig's comment, EntryCount is an attribute, so I believe you need:
$view->{entryCount}
Try checking Win32::OLE::LastError() messages. You can do this explicitly with a sub like:
sub w32_ok {
if (my $error = Win32::OLE::LastError()) {
print "Win32::OLE Error! Got: $error";
}
}
Or, have it croak errors, like:
Win32::OLE->Option( Warn => 3 ); # will now croak on errors.
It may be having problems accessing the data you want.
I am trying to make my own Jabber bot but i have run into a little trouble. I have gotten my bot to respond to messages, however, if I try to change the bot's presence then it seems as though all of the messages you send to the bot get delayed.
What I mean is when I run the script I change the presence so I can see that it is online. Then when I send it a message it takes three before the callback subroutine I have set up for messages gets called. After the thirrd message is sent and the chat subroutine is called it still process the first message I sent.
This really doesn't pose too much of a problem except that I have it set up to log out when I send the message "logout" and it has to be followed by two more messages in order to log out. I am not sure what it is that I have to do to fix this but i think it has something to do with iq packets because I have an iq callback set as well and it gets called two times after setting the presence.
Here is my source code:
#!/usr/bin/perl
use strict;
use warnings;
#Libraries
use Net::Jabber;
use DBI;
use DBD::mysql;
#--------------- Config Vars -----------------
# Jabber Client
my $jbrHostname = "DOMAINNAME";
my $jbrUserName = "USERNAME";
my $jbrPassword = "PASSWORD";
my $jbrResource = "RESOURCE";
my $jbrBoss = new Net::Jabber::JID();
$jbrBoss->SetJID(userid=>"USERNAME",server=>$jbrHostname);
# MySQL
my $dbHostname = "DOMAINNAME";
my $dbName = "DATABASENAME";
my $dbUserName = "USERNAME";
my $dbPassword = "PASSWORD";
#--------------- End Config -----------------
# connect to the db
my $dbh = DBI->connect("DBI:mysql:database=$dbName;host=$dbHostname",$dbUserName, $dbPassword, {RaiseError => 1}) or die "Couldn't connect to the database: $!\n";
# create a new jabber client and connect to server
my $jabberBot = Net::Jabber::Client->new();
my $status = $jabberBot->Connect(hostname=>$jbrHostname) or die "Cannot connect ($!)\n";
my #results = $jabberBot->AuthSend(username=>$jbrUserName,password=>$jbrPassword,resource=>$jbrResource);
if($results[0] ne "ok")
{
die "Jabber auth error #results\n";
}
# set jabber bot callbacks
$jabberBot->SetMessageCallBacks(chat=>\&chat);
$jabberBot->SetPresenceCallBacks(available=>\&welcome);
$jabberBot->SetCallBacks(iq=>\&gotIQ);
$jabberBot->PresenceSend(type=>"available");
$jabberBot->Process(1);
sub welcome
{
$jabberBot->MessageSend(to=>$jbrBoss->GetJID(),subject=>"",body=>"Hello There!",type=>"chat",priority=>10);
&keepItGoing;
}
$jabberBot->MessageSend(to=>$jbrBoss->GetJID(),subject=>"",body=>"Hello There! Global...",type=>"chat",priority=>10);
#$jabberBot->Process(5);
&keepItGoing;
sub chat
{
print "Chat Called!\n";
my ($sessionID,$msg) = #_;
$jabberBot->MessageSend(to=>$msg->GetFrom(),subject=>"",body=>"Chatting!",type=>"chat",priority=>10);
if($msg->GetBody() ne 'logout')
{
print $msg->GetBody()."\n";
&keepItGoing;
}
else
{
&killBot($msg);
}
}
sub gotIQ
{
print $_[1]->GetID()."\n";
&chat;
}
sub keepItGoing
{
print "Movin' the chains!\n";
my $proc = $jabberBot->Process(1);
while(defined($proc) && $proc != 1)
{
$proc = $jabberBot->Process(1);
}
}
sub killBot
{
$jabberBot->MessageSend(to=>$_[0]->GetFrom(),subject=>"",body=>"Logging Out!",type=>"chat",priority=>10);
$jabberBot->Process(1);
$jabberBot->Disconnect();
exit;
}
Thanks for your help!
You've got resource starvation because of your keepItGoing routine. In general, trying to use XMPP synchronously like this is not going to work. I suggest getting your callbacks set up, then just calling Process() in one loop.
The docs for Process() say:
Process(integer) - takes the timeout period as an argument. If no
timeout is listed then the function blocks until
a packet is received. Otherwise it waits that
number of seconds and then exits so your program
can continue doing useful things. NOTE: This is
important for GUIs. You need to leave time to
process GUI commands even if you are waiting for
packets. The following are the possible return
values, and what they mean:
1 - Status ok, data received.
0 - Status ok, no data received.
undef - Status not ok, stop processing.
IMPORTANT: You need to check the output of every
Process. If you get an undef then the connection
died and you should behave accordingly.
Each time you call Process(), 0 or more of your callbacks will fire. You never know which, since it depends on server timing. If you want for Process() to return before sending something, you're almost always thinking synchronously, rather than asych, which kills you in XMPP.
In your case, if you remove the call to keepItGoing from chat(), I bet things will work more like you expect.
Replace the line:
$jabberBot->Process(1);
with these:
while (defined($jabberBot->Process(1))) {
# Do stuff here
}