The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Jifty::Test::WWW::Selenium;
use strict;
use warnings;

use base 'Test::WWW::Selenium';

=head1 NAME

Jifty::Test::WWW::Selenium - Subclass of L<Test::WWW::Selenium> with
extra Jifty integration

=head1 SYNOPSIS

  use Jifty::Test::WWW::Selenium;
  my $server  = Jifty::Test->make_server;
  my $sel = Jifty::Test::WWW::Selenium->rc_ok( $server, lang => 'en_US.UTF-8' );
  my $URL = $server->started_ok;

  $sel->open_ok('/');

=head1 DESCRIPTION

L<Jifty::Test::WWW::Selenium> creates a L<Test::WWW::Selenium> object
associated with your jifty application to test.  In addition, it
starts selenium remote control for you, unless C<SELENIUM_RC_SERVER>
is specified when the test is run.  You might also want to set
C<SELENIUM_RC_TEST_AGAINST> to your local IP address so
C<SELENIUM_RC_SERVER> can test against you.  C<SELENIUM_RC_BROWSER>
tells the rc server what browser to run the tests with.

=head2 rc_ok

When the selenium rc server is started by
L<Jifty::Test::WWW::Selenium>, the browser's language is default to
en_US, unless you pass C<lang> param to rc_ok.

=cut

sub rc_ok {
    my $class = shift;
    my $server = shift;
    my %args = @_;

    $class->_skip_rest("live test doesn't work on Win32 at the moment")
                if $^O eq 'MSWin32';

    $ENV{JIFTY_OPENID_WHITELIST_HOST} = $ENV{SELENIUM_RC_TEST_AGAINST} || 'localhost';

    if ( $args{selenium_rc} ||= $ENV{SELENIUM_RC_SERVER} ) {
        @args{'host','port'} = split /:/, $args{selenium_rc}, 2;
    }
    else {
        @args{'host','port'} = eval { $class->_start_src(%args) };
        if ($@) {
            $class->_skip_rest("No selenium: $@");
        }
    }

    $args{browser_url} ||= 'http://'.($ENV{SELENIUM_RC_TEST_AGAINST} || $args{test_server} || 'localhost').':'.$server->port;

    $args{browser} ||= $ENV{SELENIUM_RC_BROWSER} || $class->_get_default_browser;

    $SIG{CHLD} = \&_REAPER;

    my $try = 5;
    my $sel;
    while ($try--) {
        $sel = eval { Test::WWW::Selenium->new( %args, auto_stop => 0 ) };
        last if $sel;
        Test::More::diag "waiting for selenium rc...";
        sleep 3;
    }

    if (!$sel) {
        $class->_skip_rest("No selenium");
    }

    Test::More::isa_ok($sel, 'Test::WWW::Selenium');
    return $sel;
}

sub _REAPER {
    my $waitedpid = wait;
    # loathe sysV: it makes us not only reinstate
    # the handler, but place it after the wait
    $SIG{CHLD} = \&_REAPER;
}

sub _get_default_browser {
    my $class = shift;

    return '*firefox /usr/lib/mozilla-firefox/firefox';
}

my @cleanup;

sub _start_src {
    my ($self, %args) = @_;
    eval 'require Alien::SeleniumRC; 1'
        or die 'requires Alien::SeleniumRC to start selenium-rc.';

    my $pid = fork();
    die if $pid == -1;
    if ($pid) {
        push @cleanup, $pid;
        return ('localhost', 4444);
    }
    else {
        unless ($^O eq 'MSWin32') {
            require POSIX;
            POSIX::setsid();  # Win32 doesn't have this.
        }
        unless ($ENV{TEST_VERBOSE}) {
            close *STDERR;
            close *STDOUT;
        }
        $ENV{LANG} = $args{lang} || 'en_US.UTF-8';
        $ENV{PATH} = "$ENV{PATH}:/usr/lib/firefox:/usr/lib/mozilla-firefox";
        Test::More::diag "start selenium rc [$$]";
        local $SIG{CHLD} = \&_REAPER;
        local $SIG{TERM} = sub { exit 0 };
        Alien::SeleniumRC::start(@{ $args{args} || [] });
        Test::More::diag "selenium rc [$$] finished.";
        exit;
    }
}

END {
    kill(15, -$_) for @cleanup;
}

sub _skip_rest {
    my $self = shift;
    my $why  = shift;

    # Schwern: i want skip_rest
    my $Tester = Test::Builder->new;
    $Tester->skip($why);

    unless ($Tester->{No_Plan}) {
        for my $ct ($Tester->{Curr_Test} + 1 .. $Tester->{Expected_Tests}) {
            $Tester->skip($why); # skip rest of the test
        }
    }
    exit(0);
}

1;