The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/bin/perl -w

use strict;
use warnings;

use Test::More ( tests => 38 );
use POE::XUL::Event;
use POE::XUL::Request;

use HTTP::Response;

#####
my $CM = My::CM->new( honk => "HONK-HONK", 
                      bonk => "BONK-BONK" 
                    );
my $resp = HTTP::Response->new( 500 );
my $req  = HTTP::Request->new( "POST", "/xul" );
$req = bless $req, 'POE::XUL::Request';
$req->param( 'source_id', 'bonk' );
$req->param( 'window', 'honk' );
$req->param( 'value', 'zip' );

##### constructor
my $event = POE::XUL::Event->new( 'Test', $CM, $resp );
ok( $event, "Built an event" );

##### accessors
is( $event->name, 'Test', "Right type" );
$event->event( 'Honking' );
is( $event->name, 'Honking', "Set event type" );
$event->set( SID => 123456 );
is( $event->get( 'SID' ), 123456, "->set / ->get" );
$event->SID( 654321 );
is( $event->SID, 654321, "->field(v) / ->field" );

##### __init
$event->__init( $req );
pass( "__init didn't blow up" );
is( $event->value, 'zip', "->value" );
is( $event->source, 'BONK-BONK', "->source" );
is( $event->source_id, 'bonk', "->source_id" );
is( $event->target, 'BONK-BONK', "->target" );
is( $event->window, 'HONK-HONK', "->window" );
is( $event->window_id, 'honk', "->window_id" );

##### window defaults to the main window
$req->param( window => '' );
$event->__init( $req );
is( $event->window, 'MAIN WINDOW', "->window defaults to main window" );
is( $event->window_id, '', "->window_id also" );

##### __init special cases
$event->source( '' );
$event->source_id( '' );
$event->window( '' );
$event->window_id( '' );
$req->param( window => 'honk' );

foreach my $E ( qw( boot connect ) ) {
    $event->event( $E );
    $event->__init( $req );
    is( $event->source, '', "$E doesn't have a source" );
    is( $event->window, 'honk', "$E doesn't have a window" );
}

$event->event( 'disconnect' );
$event->__init( $req );
is( $event->source, '', "disconnect doesn't have a source" );
is( $event->window, 'HONK-HONK', "disconnect does have a window" );

##### defer + handled
$event->done( 1 );
$event->defer;
ok( !$event->done, "->defer prevented means response isn't done" );
$event->handled;
is( $CM->{__respond_with}, $resp, "event told CM to send a response" );
ok( $event->flushed, "event marked as flushed" );

eval {
    $event->flush;
};
ok( ($@ =~ /already/), "May only flush an event once" );

$event->{flushed} = 0;

##### coderef + run + wrap + side-effects 
$event->event( 'Honking' );
$event->coderef( sub { is( $_[0], $event, "Coderef gets an event" ) } );
$event->run();
ok( !$event->coderef, "->run clears coderef" );
is( $CM->{did}{Honking}, $event, "called side-effects in CM" );

##### coderef w/o side-effects
$event->coderef( sub { is( $_[0], $event, "Coderef gets an event" ) } );
$event->event( 'Click' );
$event->run();
ok( !$CM->{did}{Click}, "Didn't call unknonwn side-effects in CM" );

##### listener coderefs
my $dest = '';
$CM->{N1} = My::Node->new( name=>'N1', dest=>\$dest );
$CM->{N1}->attach( 'Click' );

$req->param( 'source_id' => 'N1' );
$event->__init( $req );
is( $event->target, $CM->{N1}, "New target" );
$event->run;
is( $dest, 'N1.Click', "N1's handler was called" );
$dest = '';

##### listener error handling
$CM->{N2} = My::Node->new( name=>'N2', dest=>\$dest );
$CM->{N2}->attach( Click => sub { die "KABOOM\n" } );

$req->param( 'source_id' => 'N2' );
$event->__init( $req );
is( $event->target, $CM->{N2}, "New target" );
$event->run;
is( $CM->{__error}, "APPLICATION ERROR: KABOOM\n", "And it blew up" );

##### bubble_to
$CM->{N3} = My::Node->new( name=>'N1', dest=>\$dest );

$req->param( 'source_id' => 'N3' );
$event->__init( $req );
is( $event->target, $CM->{N3}, "New target" );
$event->bubble_to( $CM->{N1} );

is( $dest, '', "Nothing happened" );
$event->run;
is( $dest, 'N1.Click', "Bubbled to N1's handler" );

##### We leave the POE related stuff to be tested by the full POE tests

#####
$event->dispose;
is( $event->resp, undef(), "No more response" );
is( $event->CM, undef(), "No more CM" );



############################################################################
package My::Node;

use strict;
use warnings;

sub new
{
    my $package = shift;
    return bless { @_ }, $package;
}

sub attach
{
    my( $self, $name, $subref ) = @_;
    $subref ||= sub { ${ $self->{dest} } = join '.', $self->{name}, $name };
    $self->{events}{$name} = $subref;
}

sub event
{
    my( $self, $name ) = @_;
    return $self->{events}{$name};
}

############################################################################
package My::CM;

use strict;
use warnings;

sub new
{
    my $package = shift;
    return bless { @_ }, $package;
}


sub getElementById
{
    my( $self, $name ) = @_;
    return $self->{$name};
}

sub request_start
{
    my( $self, $event ) = @_;
    $self->{current_event} = $event;
}

sub window
{
    return "MAIN WINDOW";
}

sub response
{
    my( $self, $resp ) = @_;
    $self->{__respond_with} = $resp;
}

sub responded
{
    my( $self ) = @_;
    return exists $self->{__respond_with};
}

sub error_response
{
    my( $self, $resp, $msg ) = @_;
    $self->{__respond_with} = $resp;
    $self->{__error} = $msg;
}

sub _mk_sideeffect
{
    my( $name ) = @_;
    return sub {
        my( $self, $event ) = @_;
        $self->{did}{$name} = $event;
    }
}

BEGIN {
    *handle_Honking = _mk_sideeffect( 'Honking' );
}