diff options
author | Ivan Kohler <ivan@freeside.biz> | 2015-07-09 22:18:55 -0700 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2015-07-09 22:18:55 -0700 |
commit | 1c538bfabc2cd31f27067505f0c3d1a46cba6ef0 (patch) | |
tree | 96922ad4459eda1e649327fd391d60c58d454c53 /rt/t/web | |
parent | 4f5619288413a185e9933088d9dd8c5afbc55dfa (diff) |
RT 4.2.11, ticket#13852
Diffstat (limited to 'rt/t/web')
89 files changed, 3965 insertions, 676 deletions
diff --git a/rt/t/web/admin_queue_lifecycle.t b/rt/t/web/admin_queue_lifecycle.t index 295e9ea57..6b8401283 100644 --- a/rt/t/web/admin_queue_lifecycle.t +++ b/rt/t/web/admin_queue_lifecycle.t @@ -24,7 +24,7 @@ my $lifecycle_input = $form->find_input('Lifecycle'); is( $lifecycle_input->value, 'default', 'default lifecycle' ); my @lifecycles = sort $lifecycle_input->possible_values; -is_deeply( \@lifecycles, [qw/approvals default foo/], 'found all lifecycles' ); +is_deeply( \@lifecycles, [qw/default foo/], 'found all lifecycles' ); $m->submit_form(); $m->content_lacks( 'Lifecycle changed from', diff --git a/rt/t/web/admin_user.t b/rt/t/web/admin_user.t index 36b9af1b4..dc984eb75 100644 --- a/rt/t/web/admin_user.t +++ b/rt/t/web/admin_user.t @@ -27,9 +27,9 @@ diag "test the history page" if $ENV{TEST_VERBOSE}; $m->get_ok( $url . '/Admin/Users/History.html?id=' . $root->id ); $m->content_contains('User created', 'has User created entry'); -diag "test gnupg page" if $ENV{TEST_VERBOSE}; -$m->follow_link_ok( { text => 'GnuPG' } ); -$m->content_contains('GnuPG public key'); +diag "test keys page" if $ENV{TEST_VERBOSE}; +$m->follow_link_ok( { text => 'Private keys' } ); +$m->content_contains('Public key(s) for rt-test@example.com'); $m->content_contains('The key is ultimately trusted'); $m->content_contains('F0CB3B482CFA485680A4A0BDD328035D84881F1B'); $m->content_contains('Tue Aug 07 2007'); diff --git a/rt/t/web/articles-links.t b/rt/t/web/articles-links.t index eb6de51b3..4aa8f9133 100644 --- a/rt/t/web/articles-links.t +++ b/rt/t/web/articles-links.t @@ -44,7 +44,7 @@ $m->click('SubmitTicket'); $m->follow_link_ok({text => 'Links'}); -$m->text_contains('Article ' . $article->id . ': instance of ticket #17421', 'Article appears with its name in the links table'); +$m->text_contains('Article #' . $article->id . ': instance of ticket #17421', 'Article appears with its name in the links table'); my $refers_to = $ticket->RefersTo; is($refers_to->Count, 1, 'the ticket has a refers-to link'); diff --git a/rt/t/web/attachment_dropping.t b/rt/t/web/attachment_dropping.t new file mode 100644 index 000000000..466f7a0f0 --- /dev/null +++ b/rt/t/web/attachment_dropping.t @@ -0,0 +1,52 @@ +use warnings; +use strict; + +use RT::Test tests => undef; +use File::Temp 'tempfile'; + +my $content = 'a' x 1000 . 'b' x 10; +my ( $fh, $path ) = tempfile( UNLINK => 1, SUFFIX => '.txt' ); +print $fh $content; +close $fh; + +my $name = ( File::Spec->splitpath($path) )[2]; + +RT->Config->Set( 'WebSessionClass', "Apache::Session::File"); +RT->Config->Set( 'MaxAttachmentSize', 1000 ); +RT->Config->Set( 'TruncateLongAttachments', '0' ); +RT->Config->Set( 'DropLongAttachments', '1' ); + +my $cf = RT::CustomField->new( RT->SystemUser ); +ok( + $cf->Create( + Name => 'test truncation', + Queue => '0', + Type => 'FreeformSingle', + ), +); +my $cfid = $cf->id; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok( $queue->id, "Loaded General queue" ); +$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=' . $queue->id ); +$m->content_contains( "Create a new ticket", 'ticket create page' ); + +$m->form_name('TicketCreate'); +$m->field( 'Subject', 'Attachments dropping test' ); +$m->field( 'Attach', $path ); +$m->field( 'Content', 'Some content' ); +my $cf_content = 'cf' . 'a' x 998 . 'cfb'; +$m->field( "Object-RT::Ticket--CustomField-$cfid-Value", $cf_content ); +$m->submit; +is( $m->status, 200, "request successful" ); + +$m->content_contains( "File '$name' dropped because its size (1010 bytes) exceeded configured maximum size setting (1000 bytes).", 'dropped message' ); +$m->content_lacks( 'cfaaaa', 'cf value was dropped' ); +$m->follow_link_ok( { text => "Download $name" } ); +is( $m->content, 'Large attachment dropped', 'dropped $name' ); + +undef $m; +done_testing; diff --git a/rt/t/web/attachment_encoding.t b/rt/t/web/attachment_encoding.t index f49720e0f..3f7d6d1cf 100644 --- a/rt/t/web/attachment_encoding.t +++ b/rt/t/web/attachment_encoding.t @@ -86,7 +86,7 @@ diag 'test with attachemnts' if $ENV{TEST_VERBOSE}; '-> /Ticket/Attachment/...' ); $m->content_contains( $filename, "has file content $filename" ); - ( $id ) = $m->uri =~ /(\d+)\D+$/; + ( $id ) = $m->uri =~ m{/(\d+)/[^/]+$}; ok( $id, 'found attachment id' ); $attachment = RT::Attachment->new( $RT::SystemUser ); ok($attachment->Load($id), "load att $id"); diff --git a/rt/t/web/attachment_truncation.t b/rt/t/web/attachment_truncation.t new file mode 100644 index 000000000..b60f29e90 --- /dev/null +++ b/rt/t/web/attachment_truncation.t @@ -0,0 +1,53 @@ +use warnings; +use strict; + +use RT::Test tests => undef; +use File::Temp 'tempfile'; + +my $content = 'a' x 1000 . 'b' x 10; +my ( $fh, $path ) = tempfile( UNLINK => 1, SUFFIX => '.txt' ); +print $fh $content; +close $fh; +my $name = ( File::Spec->splitpath($path) )[2]; + +RT->Config->Set( 'WebSessionClass', "Apache::Session::File"); +RT->Config->Set( 'MaxAttachmentSize', 1000 ); +RT->Config->Set( 'TruncateLongAttachments', '1' ); + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok( $queue->id, "Loaded General queue" ); + +my $cf = RT::CustomField->new( RT->SystemUser ); +ok( + $cf->Create( + Name => 'test truncation', + Queue => '0', + Type => 'FreeformSingle', + ), +); +my $cfid = $cf->id; + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +$m->get_ok( $baseurl . '/Ticket/Create.html?Queue=' . $queue->id ); +$m->content_contains( "Create a new ticket", 'ticket create page' ); + +$m->form_name('TicketCreate'); +$m->field( 'Subject', 'Attachments test' ); +$m->field( 'Attach', $path ); +$m->field( 'Content', 'Some content' ); +my $cf_content = 'cf' . 'a' x 998 . 'cfb'; +$m->field( "Object-RT::Ticket--CustomField-$cfid-Value", $cf_content ); +$m->submit; +is( $m->status, 200, "request successful" ); + +$m->content_contains( "File '$name' truncated because its size (1010 bytes) exceeded configured maximum size setting (1000 bytes).", 'truncated message' ); +$m->content_contains( 'cf' . 'a' x 998, 'has the first 1000 cf chars' ); +$m->content_lacks( 'aaacfb', 'lacks cf chars after that' ); +$m->follow_link_ok( { text => "Download $name" } ); +$m->content_contains( 'a' x 1000, 'has the first 1000 chars' ); +$m->content_lacks( 'b', 'lacks chars after that' ); + +undef $m; +done_testing; diff --git a/rt/t/web/attachments.t b/rt/t/web/attachments.t index b518ec176..0ae407d7f 100644 --- a/rt/t/web/attachments.t +++ b/rt/t/web/attachments.t @@ -1,94 +1,506 @@ use strict; use warnings; -use RT::Test tests => 33; +use RT::Test tests => 159; -use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; -use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png'; -use constant TextFile => $RT::MasonComponentRoot .'/NoAuth/css/print.css'; +use constant LogoFile => $RT::StaticPath .'/images/bpslogo.png'; +use constant FaviconFile => $RT::StaticPath .'/images/favicon.png'; +use constant TextFile => $RT::StaticPath .'/css/mobile.css'; -my ($baseurl, $m) = RT::Test->started_ok; +my ($url, $m) = RT::Test->started_ok; ok $m->login, 'logged in'; -my $queue = RT::Queue->new(RT->Nobody); -my $qid = $queue->Load('General'); -ok( $qid, "Loaded General queue" ); - -$m->form_name('CreateTicketInQueue'); -$m->field('Queue', $qid); -$m->submit; -is($m->status, 200, "request successful"); -$m->content_contains("Create a new ticket", 'ticket create page'); - -$m->form_name('TicketCreate'); -$m->field('Subject', 'Attachments test'); -$m->field('Attach', LogoFile); -$m->field('Content', 'Some content'); -$m->submit; -is($m->status, 200, "request successful"); - -$m->content_contains('Attachments test', 'we have subject on the page'); -$m->content_contains('Some content', 'and content'); -$m->content_contains('Download bpslogo.png', 'page has file name'); - -open LOGO, "<", LogoFile or die "Can't open logo file: $!"; -binmode LOGO; -my $logo_contents = do {local $/; <LOGO>}; -close LOGO; -$m->follow_link_ok({text => "Download bpslogo.png"}); -is($m->content_type, "image/png"); -is($m->content, $logo_contents, "Binary content matches"); - -$m->back; -$m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); -$m->form_name('TicketUpdate'); -$m->field('Attach', TextFile); -$m->click('AddMoreAttach'); -is($m->status, 200, "request successful"); - -$m->form_name('TicketUpdate'); -$m->field('Attach', FaviconFile); -$m->field('UpdateContent', 'Message'); -$m->click('SubmitTicket'); -is($m->status, 200, "request successful"); - -$m->content_contains('Download bpslogo.png', 'page has file name'); -$m->content_contains('Download favicon.png', 'page has file name'); -$m->content_contains('Download print.css', 'page has file name'); - -$m->follow_link_ok( { text => 'Download bpslogo.png' } ); -is( $m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' ); - -$m->back; - -$m->follow_link_ok( { text => 'Download print.css' } ); -is( $m->response->header('Content-Type'), - 'text/css;charset=UTF-8', 'Content-Type of text has charset' ); - -diag "test mobile ui"; -$m->get_ok( $baseurl . '/m/ticket/create?Queue=' . $qid ); - -$m->form_name('TicketCreate'); -$m->field('Subject', 'Attachments test'); -$m->field('Attach', LogoFile); -$m->field('Content', 'Some content'); -$m->submit; -is($m->status, 200, "request successful"); - -$m->content_contains('Attachments test', 'we have subject on the page'); -$m->content_contains('bpslogo.png', 'page has file name'); - -$m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); -$m->form_name('TicketUpdate'); -$m->field('Attach', LogoFile); -$m->click('AddMoreAttach'); -is($m->status, 200, "request successful"); - -$m->form_name('TicketUpdate'); -$m->field('Attach', FaviconFile); -$m->field('UpdateContent', 'Message'); -$m->click('SubmitTicket'); -is($m->status, 200, "request successful"); - -$m->content_contains('bpslogo.png', 'page has file name'); -$m->content_contains('favicon.png', 'page has file name'); +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); +ok( $queue && $queue->id, "Loaded General queue" ); + +diag "create a ticket in full interface"; +diag "w/o attachments"; +{ + $m->goto_create_ticket( $queue ); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->content_contains("Create a new ticket", 'ticket create page'); + $m->submit; + is($m->status, 200, "request successful"); +} + +diag "with one attachment"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Subject', 'Attachments test'); + $m->field('Attach', LogoFile); + $m->field('Content', 'Some content'); + + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Attachments test', 'we have subject on the page'); + $m->content_contains('Some content', 'and content'); + $m->content_contains('Download bpslogo.png', 'page has file name'); +} + +diag "with two attachments"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->field('Subject', 'Attachments test'); + $m->field('Content', 'Some content'); + + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Attachments test', 'we have subject on the page'); + $m->content_contains('Some content', 'and content'); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, but delete one along the way"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->tick( 'DeleteAttach', LogoFile ); + $m->field('Subject', 'Attachments test'); + $m->field('Content', 'Some content'); + + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Attachments test', 'we have subject on the page'); + $m->content_contains('Some content', 'and content'); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, but delete one along the way"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->tick( 'DeleteAttach', LogoFile ); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Subject', 'Attachments test'); + $m->field('Content', 'Some content'); + + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Attachments test', 'we have subject on the page'); + $m->content_contains('Some content', 'and content'); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "reply to a ticket in full interface"; +diag "with one attachment"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m->form_name('TicketUpdate'); + $m->field('Attach', LogoFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_contains('Download bpslogo.png', 'page has file name'); +} + +diag "with two attachments"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m->form_name('TicketUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, delete one along the way"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m->form_name('TicketUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketUpdate'); + $m->tick('DeleteAttach', LogoFile); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "jumbo interface"; +diag "with one attachment"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket"); + $m->form_name('TicketModifyAll'); + $m->field('Attach', LogoFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->goto_ticket( $ticket->id ); + $m->content_contains('Download bpslogo.png', 'page has file name'); +} + +diag "with two attachments"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket"); + $m->form_name('TicketModifyAll'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketModifyAll'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->goto_ticket( $ticket->id ); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "with one attachment, delete one along the way"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->goto_ticket( $ticket->id ); + $m->follow_link_ok({text => 'Jumbo'}, "jumbo the ticket"); + $m->form_name('TicketModifyAll'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketModifyAll'); + $m->tick('DeleteAttach', LogoFile); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->goto_ticket( $ticket->id ); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "bulk update"; +diag "one attachment"; +{ + my @tickets = RT::Test->create_tickets( + { + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + }, + {}, + {}, + ); + my $query = join ' OR ', map "id=$_", map $_->id, @tickets; + $query =~ s/ /%20/g; + $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); + + $m->form_name('BulkUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->submit; + is($m->status, 200, "request successful"); + + foreach my $ticket ( @tickets ) { + $m->goto_ticket( $ticket->id ); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + } +} + +diag "two attachments"; +{ + my @tickets = RT::Test->create_tickets( + { + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + }, + {}, + {}, + ); + my $query = join ' OR ', map "id=$_", map $_->id, @tickets; + $query =~ s/ /%20/g; + $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); + + $m->form_name('BulkUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('BulkUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->submit; + is($m->status, 200, "request successful"); + + foreach my $ticket ( @tickets ) { + $m->goto_ticket( $ticket->id ); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + } +} + +diag "one attachment, delete one along the way"; +{ + my @tickets = RT::Test->create_tickets( + { + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + }, + {}, + {}, + ); + my $query = join ' OR ', map "id=$_", map $_->id, @tickets; + $query =~ s/ /%20/g; + $m->get_ok( $url . "/Search/Bulk.html?Query=$query&Rows=10" ); + + $m->form_name('BulkUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('BulkUpdate'); + $m->tick('DeleteAttach', LogoFile); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->submit; + is($m->status, 200, "request successful"); + + foreach my $ticket ( @tickets ) { + $m->goto_ticket( $ticket->id ); + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + } +} + +diag "self service"; +diag "create with attachment"; +{ + $m->get_ok( $url . "/SelfService/Create.html?Queue=". $queue->id ); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->field('Subject', 'Subject'); + $m->field('Content', 'Message'); + ok($m->current_form->find_input('AddMoreAttach'), "more than one attachment"); + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "update with attachment"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m->get_ok( $url . "/SelfService/Update.html?id=". $ticket->id ); + $m->form_name('TicketUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + ok($m->current_form->find_input('AddMoreAttach'), "more than one attachment"); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_contains('Download favicon.png', 'page has file name'); +} + +diag "mobile ui"; + +diag "simple create + reply"; +{ + $m->get_ok( $url . '/m/ticket/create?Queue=' . $queue->id ); + + $m->form_name('TicketCreate'); + $m->field('Subject', 'Attachments test'); + $m->field('Attach', LogoFile); + $m->field('Content', 'Some content'); + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Attachments test', 'we have subject on the page'); + $m->content_contains('bpslogo.png', 'page has file name'); + + $m->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m->form_name('TicketUpdate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketUpdate'); + $m->field('Attach', FaviconFile); + $m->field('UpdateContent', 'Message'); + $m->click('SubmitTicket'); + is($m->status, 200, "request successful"); + + $m->content_contains('bpslogo.png', 'page has file name'); + $m->content_contains('favicon.png', 'page has file name'); +} + + +diag "check content type and content"; +{ + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', LogoFile); + $m->click('AddMoreAttach'); + is($m->status, 200, "request successful"); + + $m->form_name('TicketCreate'); + $m->field('Attach', TextFile); + $m->field('Subject', 'Attachments test'); + $m->field('Content', 'Some content'); + + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_contains('Attachments test', 'we have subject on the page'); + $m->content_contains('Some content', 'and content'); + $m->content_contains('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download mobile.css', 'page has file name'); + + $m->follow_link_ok({text => "Download bpslogo.png"}); + is($m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' ); + is($m->content_type, "image/png"); + is($m->content, RT::Test->file_content(LogoFile), "Binary content matches"); + $m->back; + + $m->follow_link_ok( { text => 'Download mobile.css' } ); + is( $m->response->header('Content-Type'), + 'text/css;charset=UTF-8', + 'Content-Type of text has charset', + ); + is($m->content_type, "text/css"); + is($m->content, RT::Test->file_content(TextFile), "Text content matches"); +} + +diag "concurent actions"; +my $m2 = RT::Test::Web->new; +ok $m2->login, 'second login'; + +diag "update and create"; +{ + my $ticket = RT::Test->create_ticket( + Queue => $queue, + Subject => 'Attachments test', + Content => 'Some content', + ); + + $m2->goto_ticket( $ticket->id ); + $m2->follow_link_ok({text => 'Reply'}, "reply to the ticket"); + $m2->form_name('TicketUpdate'); + $m2->field('Attach', LogoFile); + $m2->click('AddMoreAttach'); + is($m2->status, 200, "request successful"); + + $m->goto_create_ticket( $queue ); + + $m->form_name('TicketCreate'); + $m->field('Attach', FaviconFile); + $m->field('Subject', 'Attachments test'); + $m->field('Content', 'Some content'); + $m->submit; + is($m->status, 200, "request successful"); + + $m->content_lacks('Download bpslogo.png', 'page has file name'); + $m->content_contains('Download favicon.png', 'page has file name'); + + $m2->form_name('TicketUpdate'); + $m2->click('SubmitTicket'); + $m2->content_contains('Download bpslogo.png', 'page has file name'); + $m2->content_lacks('Download favicon.png', 'page has no file name'); +} + diff --git a/rt/t/web/basic.t b/rt/t/web/basic.t index 02483b208..79c247d24 100644 --- a/rt/t/web/basic.t +++ b/rt/t/web/basic.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 23; +use RT::Test tests => 24; my ($baseurl, $agent) = RT::Test->started_ok; @@ -71,16 +71,15 @@ my $url = $agent->rt_base_url; fields => { TimeWorked => 5, 'TimeWorked-TimeUnits' => "hours" } ); - $agent->content_contains("to '300'", "5 hours is 300 minutes"); + $agent->content_contains("5 hours", "5 hours is displayed"); + $agent->content_contains("300 min", "but minutes is also"); } -TODO: { - todo_skip("Need to handle mason trying to compile images",1); -$agent->get( $url."NoAuth/images/test.png" ); +$agent->get( $url."static/images/test.png" ); my $file = RT::Test::get_relocatable_file( File::Spec->catfile( - qw(.. .. share html NoAuth images test.png) + qw(.. .. share static images test.png) ) ); is( @@ -88,7 +87,6 @@ is( -s $file, "got a file of the correct size ($file)", ); -} # # XXX: hey-ho, we have these tests in t/web/query-builder diff --git a/rt/t/web/basic_auth.t b/rt/t/web/basic_auth.t new file mode 100644 index 000000000..ff77f29f2 --- /dev/null +++ b/rt/t/web/basic_auth.t @@ -0,0 +1,34 @@ +use strict; +use warnings; +use RT; +use RT::Test tests => 9; + +RT->Config->Set( DevelMode => 0 ); +RT->Config->Set( WebRemoteUserAuth => 1 ); + +my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 ); + +# This tests the plack middleware, not RT +$m->get($url); +is($m->status, 401, "Initial request with no creds gets 401"); + +# This tests the plack middleware, not RT +$m->get($url, $m->auth_header( root => "wrong" )); +is($m->status, 401, "Request with wrong creds gets 401"); + +$m->get($url, $m->auth_header( root => "password" )); +is($m->status, 200, "Request with right creds gets 200"); + +$m->content_like( + qr{<span class="current-user">\Qroot\E</span>}i, + "Has user on the page" +); +$m->content_unlike(qr/Logout/i, "Has no logout button, no WebFallbackToRTLogin"); + +# Again, testing the plack middleware +$m->get($url); +is($m->status, 401, "Subsequent requests without credentials aren't still logged in"); + + +# Put the credentials back for the warnings check at the end +$m->auth( root => "password" ); diff --git a/rt/t/web/case-sensitivity.t b/rt/t/web/case-sensitivity.t index 5f40ef690..759937192 100644 --- a/rt/t/web/case-sensitivity.t +++ b/rt/t/web/case-sensitivity.t @@ -22,7 +22,7 @@ $m->login; require JSON; is_deeply( JSON::from_json( $m->content ), - [{"value" => "root\@localhost","label" => "Enoch Root", id=>$root_id}] + [{id => 12, "value" => "root\@localhost","label" => "root (Enoch Root)"}] ); } @@ -73,7 +73,7 @@ my $cf; # test custom field values auto completer { - $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object---CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue'); + $m->get_ok('/Helpers/Autocomplete/CustomFieldValues?term=eNo&Object-RT::Ticket--CustomField-'. $cf->id .'-Value&ContextId=1&ContextType=RT::Queue'); require JSON; is_deeply( JSON::from_json( $m->content ), diff --git a/rt/t/web/cf_access.t b/rt/t/web/cf_access.t index 675fa2177..48ab5a21b 100644 --- a/rt/t/web/cf_access.t +++ b/rt/t/web/cf_access.t @@ -5,14 +5,14 @@ use RT::Test tests => 32; my ($baseurl, $m) = RT::Test->started_ok; -use constant ImageFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png'; +use constant ImageFile => $RT::StaticPath .'/images/bpslogo.png'; use constant ImageFileContent => RT::Test->file_content(ImageFile); ok $m->login, 'logged in'; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); # Test form validation $m->submit_form( @@ -94,10 +94,10 @@ diag "apply the CF to General queue"; my ( $cf, $cfid, $tid ); { $m->title_is(q/Editing CustomField img/, 'admin-cf created'); - $m->follow_link( id => 'tools-config-queues'); + $m->follow_link( id => 'admin-queues'); $m->follow_link( text => 'General' ); $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( id => 'page-ticket-custom-fields'); + $m->follow_link( id => 'page-custom-fields-tickets'); $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); @@ -215,7 +215,7 @@ diag "create a ticket with an image"; } $m->get( $m->rt_base_url ); -$m->follow_link( id => 'search-new'); +$m->follow_link( id => 'search-tickets-new'); $m->title_is(q/Query Builder/, 'Query building'); $m->submit_form( form_name => "BuildQuery", diff --git a/rt/t/web/cf_date.t b/rt/t/web/cf_date.t index 2180e140f..a38388972 100644 --- a/rt/t/web/cf_date.t +++ b/rt/t/web/cf_date.t @@ -14,7 +14,7 @@ my $cf_name = 'test cf date'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -33,12 +33,12 @@ my $queue = RT::Test->load_or_create_queue( Name => 'General' ); ok $queue && $queue->id, 'loaded or created queue'; { - $m->follow_link( id => 'tools-config-queues-select'); + $m->follow_link( id => 'admin-queues-select'); $m->title_is( q/Admin queues/, 'admin-queues screen' ); $m->follow_link( text => 'General' ); $m->title_is( q/Configuration for queue General/, 'admin-queue: general' ); - $m->follow_link( text => 'Ticket Custom Fields' ); + $m->follow_link( id => 'page-custom-fields-tickets' ); $m->title_is( q/Custom Fields for queue General/, 'admin-queue: general cfid' ); @@ -186,7 +186,7 @@ diag 'check invalid inputs'; my @warnings = $m->get_warnings; chomp @warnings; - is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} ); + is_deeply( [@warnings], [(q{Couldn't parse date 'foodate' by Time::ParseDate})x2] ); } diag 'retain values when adding attachments'; diff --git a/rt/t/web/cf_datetime.t b/rt/t/web/cf_datetime.t index 72a8b3f7e..da938ab85 100644 --- a/rt/t/web/cf_datetime.t +++ b/rt/t/web/cf_datetime.t @@ -24,7 +24,7 @@ if ( ( $ENV{RT_TEST_WEB_HANDLER} || '' ) =~ /^apache(\+mod_perl)?$/ my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -47,7 +47,7 @@ ok $queue && $queue->id, 'loaded or created queue'; $m->title_is(q/Admin queues/, 'admin-queues screen'); $m->follow_link( text => 'General' ); $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( text => 'Ticket Custom Fields' ); + $m->follow_link( id => 'page-custom-fields-tickets' ); $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); @@ -212,7 +212,7 @@ diag 'check invalid inputs'; my @warnings = $m->get_warnings; chomp @warnings; - is_deeply( @warnings, q{Couldn't parse date 'foodate' by Time::ParseDate} ); + is_deeply( [@warnings], [(q{Couldn't parse date 'foodate' by Time::ParseDate})x2] ); } diag 'retain values when adding attachments'; diff --git a/rt/t/web/cf_groupings.t b/rt/t/web/cf_groupings.t new file mode 100644 index 000000000..0a40f71af --- /dev/null +++ b/rt/t/web/cf_groupings.t @@ -0,0 +1,277 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my @groupings = qw/Basics Dates People Links More/; +RT->Config->Set( 'CustomFieldGroupings', + 'RT::Ticket' => { + map { +($_ => ["Test$_"]) } @groupings, + }, +); + +my %CF; +for my $grouping (@groupings) { + my $name = "Test$grouping"; + my $cf = RT::CustomField->new( RT->SystemUser ); + my ($id, $msg) = $cf->Create( + Name => $name, + Queue => '0', + Description => 'A Testing custom field', + Type => 'FreeformSingle', + Pattern => '^(?!bad value).*$', + ); + ok $id, "custom field '$name' correctly created"; + $CF{$grouping} = $id; +} + +my $queue = RT::Test->load_or_create_queue( Name => 'General' ); + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my %location = ( + Basics => ".ticket-info-basics", + Dates => ".ticket-info-dates", + People => "#ticket-create-message", + Links => ".ticket-info-links", + More => ".ticket-info-cfs", +); +{ + note "testing Create"; + $m->goto_create_ticket($queue); + + my $prefix = 'Object-RT::Ticket--CustomField:'; + my $dom = $m->dom; + $m->form_name('TicketCreate'); + $m->field("Subject", "CF grouping test"); + + for my $grouping (@groupings) { + my $input_name = $prefix . "$grouping-$CF{$grouping}-Value"; + is $dom->find(qq{input[name="$input_name"]})->size, 1, "only one CF input on the page"; + ok $dom->at(qq{$location{$grouping} input[name="$input_name"]}), "CF is in the right place"; + $m->field( $input_name, "Test" . $grouping . "Value" ); + } + $m->submit; +} + +my $id = $m->get_ticket_id; +{ + note "testing Display"; + ok $id, "created a ticket"; + my $dom = $m->dom; + + $location{People} = ".ticket-info-people"; + foreach my $grouping (@groupings) { + my $row_id = "CF-$CF{$grouping}-ShowRow"; + is $dom->find(qq{#$row_id})->size, 1, "CF on the page"; + is $dom->at(qq{#$row_id})->all_text, "Test$grouping: Test${grouping}Value", "value is set"; + ok $dom->at(qq{$location{$grouping} #$row_id}), "CF is in the right place"; + } +} + +{ + note "testing Basics/People/Dates/Links pages"; + my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:'; + { # Basics and More both show up on "Basics" + for my $name (qw/Basics More/) { + $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics'); + is $m->dom->find(qq{input[name^="$prefix"][name\$="-Value"]})->size, 2, + "two CF inputs on the page"; + + my $input_name = "$prefix$name-$CF{$name}-Value"; + ok $m->dom->at(qq{$location{$name} input[name="$input_name"]}), + "CF is in the right place"; + $m->submit_form_ok({ + with_fields => { $input_name => "Test${name}Changed" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{to Test${name}Changed}); + + $m->submit_form_ok({ + with_fields => { $input_name => "bad value" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{Test\Q$name\E: Input must match}); + } + } + + # Everything else gets its own page + foreach my $name ( qw(People Dates Links) ) { + $m->follow_link_ok({id => "page-\L$name"}, "Ticket's $name page"); + is $m->dom->find(qq{input[name^="$prefix"][name\$="-Value"]})->size, 1, + "only one CF input on the page"; + my $input_name = "$prefix$name-$CF{$name}-Value"; + $m->submit_form_ok({ + with_fields => { $input_name => "Test${name}Changed" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{to Test${name}Changed}); + + $m->submit_form_ok({ + with_fields => { $input_name => "bad value" }, + button => 'SubmitTicket', + }); + $m->content_like(qr{Could not add new custom field value: Input must match}); + } +} + +{ + note "testing Jumbo"; + my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:'; + $m->follow_link_ok({id => "page-jumbo"}, "Ticket's Jumbo page"); + my $dom = $m->dom; + $m->form_name("TicketModifyAll"); + + foreach my $name ( qw(Basics People Dates Links More) ) { + my $input_name = "$prefix$name-$CF{$name}-Value"; + is $dom->find(qq{input[name="$input_name"]})->size, 1, + "only one CF input on the page"; + $m->field( $input_name, "Test${name}Again" ); + } + $m->click('SubmitTicket'); + foreach my $name ( qw(Basics People Dates Links More) ) { + $m->content_like(qr{to Test${name}Again}); + } +} + +{ + note "Reconfigure to place one CF in multiple boxes"; + $m->no_warnings_ok; + RT::Test->stop_server; + + RT->Config->Set( 'CustomFieldGroupings', + 'RT::Ticket' => { + Basics => [ 'TestMore' ], + More => [ 'TestMore' ], + }, + ); + + ( $baseurl, $m ) = RT::Test->started_ok; + ok $m->login, 'logged in as root'; +} + +{ + note "Testing one CF in multiple boxes"; + $m->goto_create_ticket($queue); + + my $prefix = 'Object-RT::Ticket--CustomField:'; + my $dom = $m->dom; + $m->form_name('TicketCreate'); + + my $cf = $CF{More}; + is $m->dom->find(qq{input[name^="$prefix"][name\$="-$cf-Value"]})->size, 2, + "Two 'More' CF inputs on the page"; + for my $grouping (qw/Basics More/) { + my $input_name = $prefix . "$grouping-$cf-Value"; + is $dom->find(qq{input[name="$input_name"]})->size, 1, "Found the $grouping grouping"; + ok $dom->at(qq{$location{$grouping} input[name="$input_name"]}), "CF is in the right place"; + $m->field( $input_name, "TestMoreValue" ); + } + $m->submit; + $m->no_warnings_ok( "Submitting CF with two (identical) values had no warnings" ); +} + +$id = $m->get_ticket_id; +my $ticket = RT::Ticket->new ( RT->SystemUser ); +$ticket->Load( $id ); +is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "TestMoreValue", + "Value submitted twice is set correctly (and only once)"; + +my $cf = $CF{More}; +my $prefix = 'Object-RT::Ticket-'. $id .'-CustomField:'; +{ + note "Updating with multiple appearances of a CF"; + $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics'); + + is $m->dom->find(qq{input[name^="$prefix"][name\$="-$cf-Value"]})->size, 2, + "Two 'More' CF inputs on the page"; + my @inputs; + for my $grouping (qw/Basics More/) { + my $input_name = "$prefix$grouping-$cf-Value"; + push @inputs, $input_name; + ok $m->dom->at(qq{$location{$grouping} input[name="$input_name"]}), + "CF is in the right place"; + } + $m->submit_form_ok({ + with_fields => { + map {+($_ => "TestMoreChanged")} @inputs, + }, + button => 'SubmitTicket', + }); + $m->no_warnings_ok; + $m->content_like(qr{to TestMoreChanged}); + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "TestMoreChanged", + "Updated value submitted twice is set correctly (and only once)"; +} + +{ + note "Updating with _differing_ values in multiple appearances of a CF"; + + my %inputs = map {+($_ => "$prefix$_-$cf-Value")} qw/Basics More/; + $m->submit_form_ok({ + with_fields => { + $inputs{Basics} => "BasicsValue", + $inputs{More} => "MoreValue", + }, + button => 'SubmitTicket', + }); + $m->warning_like(qr{CF $cf submitted with multiple differing values}); + $m->content_like(qr{to BasicsValue}, "Arbitrarily chose first value"); + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "BasicsValue", + "Conflicting value submitted twice is set correctly (and only once)"; +} + +{ + note "Configuring CF to be a select-multiple"; + my $custom_field = RT::CustomField->new( RT->SystemUser ); + $custom_field->Load( $cf ); + $custom_field->SetType( "Select" ); + $custom_field->SetMaxValues( 0 ); + $custom_field->AddValue( Name => $_ ) for 1..9; +} + +{ + note "Select multiples do not interfere with each other when appearing multiple times"; + $m->follow_link_ok({id => 'page-basics'}, 'Ticket -> Basics'); + + $m->form_name('TicketModify'); + my %inputs = map {+($_ => "$prefix$_-$cf-Values")} qw/Basics More/; + ok $m->dom->at(qq{select[name="$inputs{Basics}"]}), "Found 'More' CF in Basics box"; + ok $m->dom->at(qq{select[name="$inputs{More}"]}), "Found 'More' CF in More box"; + + $m->select( $inputs{Basics} => [1, 3, 9] ); + $m->select( $inputs{More} => [1, 3, 9] ); + $m->click( 'SubmitTicket' ); + $m->no_warnings_ok; + $m->content_like(qr{$_ added as a value for TestMore}) for 1, 3, 9; + $m->content_like(qr{BasicsValue is no longer a value for custom field TestMore}); + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "1|3|9", + "Multi-select values submitted correctly"; +} + +{ + note "Submit multiples correctly choose one set of values when conflicting information is submitted"; + $m->form_name('TicketModify'); + my %inputs = map {+($_ => "$prefix$_-$cf-Values")} qw/Basics More/; + $m->select( $inputs{Basics} => [2, 3, 4] ); + $m->select( $inputs{More} => [8, 9] ); + $m->click( 'SubmitTicket' ); + $m->warning_like(qr{CF $cf submitted with multiple differing values}); + $m->content_like(qr{$_ added as a value for TestMore}) for 2, 4; + $m->content_unlike(qr{$_ added as a value for TestMore}) for 8; + $m->content_like(qr{$_ is no longer a value for custom field TestMore}) for 1, 9; + + $ticket->Load( $id ); + is $ticket->CustomFieldValuesAsString( "TestMore", Separator => "|" ), "3|2|4", + "Multi-select values submitted correctly"; +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_groupings_user.t b/rt/t/web/cf_groupings_user.t new file mode 100644 index 000000000..fe79ae5ad --- /dev/null +++ b/rt/t/web/cf_groupings_user.t @@ -0,0 +1,110 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +RT->Config->Set( 'CustomFieldGroupings', + 'RT::User' => { + Identity => ['TestIdentity'], + 'Access control' => ['TestAccessControl'], + Location => ['TestLocation'], + Phones => ['TestPhones'], + More => ['TestMore'], + }, +); + +my %CF; + +while (my ($group,$cfs) = each %{ RT->Config->Get('CustomFieldGroupings')->{'RT::User'} } ) { + my $name = $cfs->[0]; + my $cf = RT::CustomField->new( RT->SystemUser ); + my ($id, $msg) = $cf->Create( + Name => $name, + Description => 'A custom field', + LookupType => RT::User->new( $RT::SystemUser )->CustomFieldLookupType, + Type => 'FreeformSingle', + Pattern => '^(?!bad value).*$', + ); + ok $id, "custom field '$name' correctly created"; + + ($id, $msg) = $cf->AddToObject( RT::User->new( $cf->CurrentUser ) ); + ok $id, "applied custom field" or diag "error: $msg"; + + $group =~ s/\W//g; + $CF{$name} = "$group-" . $cf->Id; +} + +my ( $baseurl, $m ) = RT::Test->started_ok; +ok $m->login, 'logged in as root'; + +my %location = ( + Identity => ".user-info-identity", + AccessControl => ".user-info-access-control", + Location => ".user-info-location", + Phones => ".user-info-phones", + More => ".user-info-cfs", +); +{ + note "testing Create"; + $m->follow_link_ok({id => 'admin-users-create'}, 'Create '); + + my $dom = $m->dom; + $m->form_name('UserCreate'); + + $m->field( 'Name', 'user1' ); + + my $prefix = 'Object-RT::User--CustomField:'; + for my $name (keys %location) { + my $input_name = $prefix . $CF{"Test$name"} .'-Value'; + is $dom->find(qq{input[name="$input_name"]})->size, 1, "only one CF input on the page"; + ok $dom->at(qq{$location{$name} input[name="$input_name"]}), "CF is in the right place"; + $m->field( $input_name, "Test${name}Value" ); + } + + $m->submit; + $m->content_like(qr{User created}); +} + +my ($id) = ($m->uri =~ /id=(\d+)/); +ok $id, "found user's id #$id"; + +{ + note "testing values on Modify page and on the object"; + my $user = RT::User->new( RT->SystemUser ); + $user->Load( $id ); + ok $user->id, "loaded user"; + + my $dom = $m->dom; + $m->form_name('UserModify'); + my $prefix = "Object-RT::User-$id-CustomField:"; + foreach my $name ( keys %location ) { + is $user->FirstCustomFieldValue("Test$name"), "Test${name}Value", + "correct value of Test$name CF"; + my $input_name = $prefix . $CF{"Test$name"} .'-Value'; + is $m->value($input_name), "Test${name}Value", + "correct value in UI"; + $m->field( $input_name, "Test${name}Changed" ); + ok $dom->at(qq{$location{$name} input[name="$input_name"]}), "CF is in the right place"; + } + $m->submit; +} + +{ + note "testing that update works"; + my $user = RT::User->new( RT->SystemUser ); + $user->Load( $id ); + ok $user->id, "loaded user"; + + $m->form_name('UserModify'); + my $prefix = "Object-RT::User-$id-CustomField:"; + foreach my $name ( keys %location ) { + is $user->FirstCustomFieldValue("Test$name"), "Test${name}Changed", + "correct value of Test$name CF"; + my $input = $prefix . $CF{"Test$name"} .'-Value'; + is $m->value($input), "Test${name}Changed", + "correct value in UI"; + } +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_image.t b/rt/t/web/cf_image.t new file mode 100644 index 000000000..355f25968 --- /dev/null +++ b/rt/t/web/cf_image.t @@ -0,0 +1,61 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my (undef, $m) = RT::Test->started_ok; +$m->login; +$m->follow_link( id => 'admin-custom-fields-create' ); +$m->submit_form_ok({ + form_name => "ModifyCustomField", + fields => { + Name => 'Images', + TypeComposite => 'Image-1', + LookupType => 'RT::Queue-RT::Ticket', + }, +}); +$m->content_contains("Object created"); +my $cfid = $m->form_name('ModifyCustomField')->value('id'); +ok $cfid, "Created CF correctly"; + +$m->follow_link_ok( {id => "page-applies-to"} ); +$m->form_with_fields( "AddCustomField-1" ); +$m->tick( "AddCustomField-1", 0 ); +$m->click_ok( "UpdateObjs" ); +$m->content_contains("Object created"); + + +$m->submit_form_ok({ + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, +}); +$m->content_contains("Upload one image"); +$m->submit_form_ok({ + form_name => "TicketCreate", + fields => { + Subject => 'Test ticket', + Content => 'test', + }, +}); +$m->content_like( qr/Ticket \d+ created/, + "a ticket is created succesfully" ); + +$m->follow_link_ok( {id => "page-basics"} ); +$m->content_contains("Upload one image"); +$m->submit_form_ok({ + form_name => "TicketModify", + fields => { + "Object-RT::Ticket-1-CustomField-1-Upload" => + RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), + }, +}); +$m->content_contains("bpslogo.png added"); +$m->content_contains("/Download/CustomFieldValue/1/bpslogo.png"); + +$m->form_name("TicketModify"); +$m->tick("Object-RT::Ticket-1-CustomField-1-DeleteValueIds", 1); +$m->click_ok("SubmitTicket"); +$m->content_lacks("/Download/CustomFieldValue/1/bpslogo.png"); + +undef $m; +done_testing; diff --git a/rt/t/web/cf_onqueue.t b/rt/t/web/cf_onqueue.t index bd6ae66aa..dd3320a25 100644 --- a/rt/t/web/cf_onqueue.t +++ b/rt/t/web/cf_onqueue.t @@ -8,7 +8,7 @@ ok $m->login, 'logged in'; diag "Create a queue CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { diff --git a/rt/t/web/cf_pattern.t b/rt/t/web/cf_pattern.t new file mode 100644 index 000000000..ff85ec6c7 --- /dev/null +++ b/rt/t/web/cf_pattern.t @@ -0,0 +1,80 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my ($base, $m) = RT::Test->started_ok; + +my $cf = RT::Test->load_or_create_custom_field( + Name => 'Yaks', + Type => 'FreeformSingle', + Pattern => '(?#Digits)^\d+$', + Queue => 0, + LookupType => 'RT::Queue-RT::Ticket', +); +ok $cf && $cf->id, "Created CF with Pattern"; + +my $ticket = RT::Test->create_ticket( + Queue => 1, + Subject => 'a test ticket', +); +ok $ticket && $ticket->id, "Created ticket"; + +$m->login; + +for my $page ("/Ticket/Create.html?Queue=1", "/Ticket/Modify.html?id=".$ticket->id) { + diag $page; + $m->get_ok($page, "Fetched $page"); + $m->content_contains("Yaks"); + $m->content_contains("Input must match [Digits]"); + $m->content_lacks("cfinvalidfield"); + + my $cfinput = RT::Interface::Web::GetCustomFieldInputName( + Object => ( $page =~ /Create/ ? RT::Ticket->new( RT->SystemUser ) : $ticket ), + CustomField => $cf, + ); + $m->submit_form_ok({ + with_fields => { + $cfinput => "too many", + "${cfinput}-Magic" => "1", + }, + }); + $m->content_contains("Input must match [Digits]"); + $m->content_contains("cfinvalidfield"); + + $m->submit_form_ok({ + with_fields => { + $cfinput => "42", + "${cfinput}-Magic" => "1", + }, + }); + + if ($page =~ /Create/) { + $m->content_like(qr/Ticket \d+ created/, "Created ticket"); + } else { + $m->content_contains("Yaks 42 added", "Updated ticket"); + $m->content_contains("Input must match [Digits]"); + $m->content_lacks("cfinvalidfield"); + } +} + +diag "Quick ticket creation"; +{ + $m->get_ok("/"); + $m->submit_form_ok({ + with_fields => { + Subject => "test quick create", + QuickCreate => 1, + }, + }); + my $tickets = RT::Tickets->new(RT->SystemUser); + $tickets->FromSQL("Subject = 'test quick create'"); + is $tickets->Count, 0, "No ticket created"; + + like $m->uri, qr/Ticket\/Create\.html/, "Redirected to the ticket create page"; + $m->content_contains("Yaks: Input must match", "Found CF validation error"); + $m->content_contains("test quick create", "Found prefilled Subject"); +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_render_type.t b/rt/t/web/cf_render_type.t index 8d8efa897..42efb32bc 100644 --- a/rt/t/web/cf_render_type.t +++ b/rt/t/web/cf_render_type.t @@ -11,7 +11,7 @@ my $cf_name = 'test render type'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { diff --git a/rt/t/web/cf_select_one.t b/rt/t/web/cf_select_one.t index 92fcf53f3..4f81e2a1a 100644 --- a/rt/t/web/cf_select_one.t +++ b/rt/t/web/cf_select_one.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test tests => 45; +use RT::Test tests => undef; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, 'logged in as root'; @@ -12,7 +12,7 @@ my $cf_name = 'test select one value'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { @@ -49,10 +49,10 @@ ok $queue && $queue->id, 'loaded or created queue'; diag "apply the CF to General queue"; { - $m->follow_link( id => 'tools-config-queues'); + $m->follow_link( id => 'admin-queues'); $m->follow_link( text => 'General' ); $m->title_is(q/Configuration for queue General/, 'admin-queue: general'); - $m->follow_link( id => 'page-ticket-custom-fields'); + $m->follow_link( id => 'page-custom-fields-tickets'); $m->title_is(q/Custom Fields for queue General/, 'admin-queue: general cfid'); $m->form_name('EditCustomFields'); @@ -151,3 +151,32 @@ diag "check that we can set empty value when the current is 0"; undef, 'API returns correct value'; } +diag 'retain selected cf values when adding attachments'; +{ + my ( $ticket, $id ); + $m->submit_form( + form_name => "CreateTicketInQueue", + fields => { Queue => 'General' }, + ); + $m->content_contains($cf_name, 'Found cf field' ); + + $m->submit_form_ok( + { form_name => "TicketCreate", + fields => { + Subject => 'test defaults', + Content => 'test', + "Object-RT::Ticket--CustomField-$cfid-Values" => 'qwe', + }, + button => 'AddMoreAttach', + }, + 'Add an attachment on create' + ); + + $m->form_name("TicketCreate"); + is($m->value("Object-RT::Ticket--CustomField-$cfid-Values"), + "qwe", + "Selected value still on form" ); +} + +undef $m; +done_testing; diff --git a/rt/t/web/cf_textarea.t b/rt/t/web/cf_textarea.t new file mode 100644 index 000000000..d11bda4d5 --- /dev/null +++ b/rt/t/web/cf_textarea.t @@ -0,0 +1,75 @@ +use strict; +use warnings; + +use RT::Test tests => 'no_declare'; + +my $content = join ' ', ('The quick brown fox jumps over the lazy dog.') x 5; +$content = join "\n\n", $content, $content, $content; + +my ($base, $m) = RT::Test->started_ok; + +$m->login; + +my $ticket = RT::Test->create_ticket( + Queue => 1, + Subject => 'a test ticket', +); +ok $ticket && $ticket->id, "Created ticket"; + +my $EditUrl = "/Ticket/Modify.html?id=" . $ticket->id; + +my $cfs = { + area => { + type => 'Text', + name => 'TheTextarea', + }, + text => { + type => 'FreeformSingle', + name => 'TheControlField', + }, +}; + +while ( my( $label, $data ) = each %$cfs ) { + my $cf = $data->{obj} = RT::Test->load_or_create_custom_field( + Name => $data->{name}, + Type => $data->{type}, + Queue => 0, + LookupType => 'RT::Queue-RT::Ticket', + ); + ok $cf && $cf->id, "Created $data->{type} CF"; + + # get cf input field name + $data->{input} = RT::Interface::Web::GetCustomFieldInputName( + Object => $ticket, + CustomField => $cf, + ); +} + +# open ticket "Basics" page +$m->get_ok($EditUrl, "Fetched $EditUrl"); +$m->content_contains($_->{name} . ':') for ( values %$cfs ); + +$m->submit_form_ok({ + with_fields => { + $cfs->{area}{input} => $content, + $cfs->{area}{input} . '-Magic' => "1", + $cfs->{text}{input} => 'value a', + $cfs->{text}{input} . '-Magic' => "1", + }, +}, 'submitted form to initially set CFs'); +$m->content_contains('<li>TheControlField value a added</li>'); +$m->content_contains("<li>TheTextarea $content added</li>", 'content found'); + +# http://issues.bestpractical.com/Ticket/Display.html?id=30378 +# #30378: RT 4.2.6 - Very long text fields get updated even when they haven't changed +$m->submit_form_ok({ + with_fields => { + $cfs->{text}{input} => 'value b', + $cfs->{text}{input} . '-Magic' => "1", + }, +}, 'submitted form to initially set CFs'); +$m->content_contains('<li>TheControlField value a changed to value b</li>'); +$m->content_lacks("<li>TheTextarea $content changed to $content</li>", 'textarea wasnt updated'); + +undef $m; +done_testing; diff --git a/rt/t/web/cf_values_class.t b/rt/t/web/cf_values_class.t index 646642781..6535c505b 100644 --- a/rt/t/web/cf_values_class.t +++ b/rt/t/web/cf_values_class.t @@ -14,7 +14,7 @@ my $cf_name = 'test values class'; my $cfid; diag "Create a CF"; { - $m->follow_link( id => 'tools-config-custom-fields-create'); + $m->follow_link( id => 'admin-custom-fields-create'); $m->submit_form( form_name => "ModifyCustomField", fields => { diff --git a/rt/t/web/charting.t b/rt/t/web/charting.t index e19ec41ae..5131f9cef 100644 --- a/rt/t/web/charting.t +++ b/rt/t/web/charting.t @@ -1,16 +1,10 @@ use strict; use warnings; -BEGIN { - require RT::Test; - - if (eval { require GD; 1 }) { - RT::Test->import(plan => 'no_plan'); - } - else { - RT::Test->import(skip_all => 'GD required.'); - } -} +use RT::Test tests => undef; + +plan skip_all => 'GD required' + unless GD->require; for my $n (1..7) { my $ticket = RT::Ticket->new( RT->SystemUser ); @@ -35,8 +29,8 @@ ok( $m->login, "Logged in" ); # Test that defaults work $m->get_ok( "/Search/Chart.html?Query=id>0" ); -$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by queue"); -$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table"); +$m->content_like(qr{<th[^>]*>Status\s*</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by status"); +$m->content_like(qr{new\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); $m->get_ok( "/Search/Chart?Query=id>0" ); @@ -45,35 +39,41 @@ ok( length($m->content), "Has content" ); # Group by Queue -$m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Queue" ); -$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Tickets\s*</th>}, "Grouped by queue"); -$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table"); +$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Queue" ); +$m->content_like(qr{<th[^>]*>Queue\s*</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by queue"); +$m->content_like(qr{General\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found results in table"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); -$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Queue" ); +$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Queue" ); is( $m->content_type, "image/png" ); ok( length($m->content), "Has content" ); # Group by Requestor email -$m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Requestor.EmailAddress" ); -$m->content_like(qr{<th[^>]*>Requestor\.EmailAddress\s*</th>\s*<th[^>]*>Tickets\s*</th>}, +$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Requestor.EmailAddress" ); +$m->content_like(qr{<th[^>]*>Requestor\s+EmailAddress</th>\s*<th[^>]*>Ticket count\s*</th>}, "Grouped by requestor"); -$m->content_like(qr{root0\@localhost</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>3</a>}, "Found results in table"); +$m->content_like(qr{root0\@localhost\s*</th>\s*<td[^>]*>\s*<a[^>]*>3</a>}, "Found results in table"); $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); -$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Email" ); +$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Requestor.EmailAddress" ); is( $m->content_type, "image/png" ); ok( length($m->content), "Has content" ); - # Group by Requestor phone -- which is bogus, and falls back to queue -$m->get_ok( "/Search/Chart.html?Query=id>0&PrimaryGroupBy=Requestor.Phone" ); -$m->content_like(qr{General</a>\s*</td>\s*<td[^>]*>\s*<a[^>]*>7</a>}, + +$m->get_ok( "/Search/Chart.html?Query=id>0&GroupBy=Requestor.Phone" ); +$m->warning_like( qr{'Requestor\.Phone' is not a valid grouping for reports} ); + +TODO: { + local $TODO = "UI should show that it's group by status"; + $m->content_like(qr{new\s*</th>\s*<td[^>]*>\s*<a[^>]*>7</a>}, "Found queue results in table, as a default"); +} $m->content_like(qr{<img src="/Search/Chart\?}, "Found image"); -$m->get_ok( "/Search/Chart?Query=id>0&PrimaryGroupBy=Requestor.Phone" ); +$m->get_ok( "/Search/Chart?Query=id>0&GroupBy=Requestor.Phone" ); +$m->warning_like( qr{'Requestor\.Phone' is not a valid grouping for reports} ); is( $m->content_type, "image/png" ); ok( length($m->content), "Has content" ); @@ -93,3 +93,6 @@ $advanced = $m->find_link( text => 'Advanced' )->URI->equery; like( $advanced, qr{Query=id%3E0}, 'Advanced link still has Query param with id search' ); + +undef $m; +done_testing; diff --git a/rt/t/web/class_create.t b/rt/t/web/class_create.t index 2d9ad035d..7723d7aa9 100644 --- a/rt/t/web/class_create.t +++ b/rt/t/web/class_create.t @@ -13,7 +13,7 @@ my $class_name = 'test class'; my $class_id; diag "Create a class"; { - $m->follow_link( id => 'tools-config-articles-classes-create'); + $m->follow_link( id => 'admin-articles-classes-create'); # Test class form validation $m->submit_form( diff --git a/rt/t/web/command_line.t b/rt/t/web/command_line.t index a5c52d261..47f672856 100644 --- a/rt/t/web/command_line.t +++ b/rt/t/web/command_line.t @@ -111,11 +111,11 @@ ok($val,$msg); # add a comment to ticket expect_send("comment -m 'comment-$$' $ticket_id", "Adding a comment..."); - expect_like(qr/Message recorded/, "Added the comment"); + expect_like(qr/Comments added/, "Added the comment"); ### should test to make sure it actually got added # add correspondance to ticket (?) expect_send("correspond -m 'correspond-$$' $ticket_id", "Adding correspondence..."); - expect_like(qr/Message recorded/, "Added the correspondence"); + expect_like(qr/Correspondence added/, "Added the correspondence"); ### should test to make sure it actually got added my $test_email = RT::Test::get_relocatable_file('lorem-ipsum', @@ -124,7 +124,7 @@ ok($val,$msg); # text attachment check_attachment($test_email); # binary attachment - check_attachment($RT::MasonComponentRoot.'/NoAuth/images/bpslogo.png'); + check_attachment($RT::StaticPath . '/images/bpslogo.png'); # change a ticket's Owner expect_send("edit ticket/$ticket_id set owner=root", 'Changing owner...'); @@ -158,7 +158,7 @@ expect_send("show ticket/$ticket_id -f queue", 'Verifying change...'); expect_like(qr/Queue: EditedQueue$$/, 'Verified change'); # cannot move ticket to a nonexistent queue expect_send("edit ticket/$ticket_id set queue=nonexistent-$$", 'Changing to nonexistent queue...'); -expect_like(qr/queue does not exist/i, 'Errored out'); +expect_like(qr/Queue nonexistent-$$ does not exist/i, 'Errored out'); expect_send("show ticket/$ticket_id -f queue", 'Verifying lack of change...'); expect_like(qr/Queue: EditedQueue$$/, 'Verified lack of change'); @@ -213,7 +213,7 @@ expect_send("edit ticket/$ticket_id set CF-myCF$$=1,2,3", 'Changing CF...'); expect_like(qr/Ticket $ticket_id updated/, 'Changed cf'); expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value'); expect_like(qr/\QCF.{myCF$$}\E: 1,2,3/i, 'Verified change'); -expect_send("edit ticket/$ticket_id set CF-myCF$$=\"1's,2,3\"", 'Changing CF...'); +expect_send(qq{edit ticket/$ticket_id set CF-myCF$$="1's,2,3"}, 'Changing CF...'); expect_like(qr/Ticket $ticket_id updated/, 'Changed cf'); expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value'); expect_like(qr/\QCF.{myCF$$}\E: 1's,2,3/i, 'Verified change'); @@ -238,32 +238,85 @@ expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c,\s*o/i, 'Verified multiple cf change'); -expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'a,b,c'\" ", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); -expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); +sub multi_round_trip { + my ($op, $value, $regex) = @_; + $Test::Builder::Level++; + # The outer double quotes are for the argument parsing that the + # command-line does; the extra layer of escaping is to for them, as + # well. It is equivilent to the quoting that the shell would + # require. + my $quoted = $value; + $quoted =~ s/(["\\])/\\$1/g; + expect_send(qq{edit ticket/$ticket_id $op CF.{MultipleCF$$}="$quoted"}, qq{CF $op $value}); + expect_like(qr/Ticket $ticket_id updated/, qq{Got expected "updated" answer}); + expect_send(qq{show ticket/$ticket_id -f CF.{MultipleCF$$}}, qq{Sent "show"}); + expect_like(qr/\QCF.{MultipleCF$$}\E: $regex$/i, qq{Answer matches $regex}); +} -expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=q{a,b,c}", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); -expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: 'a,b,c'/i, 'Verified change'); -expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=\"'a,b,c'\"", 'Changing CF...'); +# Test simple quoting +my $ticket = RT::Ticket->new($RT::SystemUser); +$ticket->Load($ticket_id); +multi_round_trip(set => q|'a,b,c'|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(del => q|a|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(set => q|q{a,b,c}|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(del => q|a|, qr/'a,b,c'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected"); + +multi_round_trip(del => q|'a,b,c'|, qr/\s*/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values"); + +multi_round_trip(set => q|q{1,2's,3}|, qr/'1,2\\'s,3'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value"); +is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{1,2's,3}, "And that CF value is as expected"); + +multi_round_trip(del => q|q{1,2's,3}|, qr/\s*/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values"); + +# Test escaping of quotes - generate (foo)(bar') with no escapes +multi_round_trip(set => q|'foo',bar'|, qr/foo,bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo|, "Direct value checks out"); +is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|bar'|, "Direct value checks out"); +multi_round_trip(del => q|bar'|, qr/foo/); + +# With one \, generate (foo',bar) + +# We obviously need two \s in the following q|| string in order to +# generate a string with one actual \ in it; this causes the string to, +# in general, have twice as many \s in it as we wish to test. +multi_round_trip(set => q|'foo\\',bar'|, qr/'foo\\',bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo',bar|, "Direct value checks out"); + +# With two \, generate (foo\)(bar') +multi_round_trip(set => q|'foo\\\\',bar'|, qr/foo\\,bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\|, "Direct value checks out"); +is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|bar'|, "Direct value checks out"); +multi_round_trip(del => q|bar'|, qr/foo\\/); + +# With three \, generate (foo\',bar) +multi_round_trip(set => q|'foo\\\\\\',bar'|, qr/'foo\\\\\\',bar'/); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\',bar|, "Direct value checks out"); + +# Check that we don't infinite-loop on 'foo'bar,baz; this should be ('foo'bar)(baz) +expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'foo'bar,baz\"", 'Changing CF to have quotes not at commas'); expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: \s*$/i, 'Verified change'); +is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two value"); +is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|'foo'bar|, "Direct value checks out"); +is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|baz|, "Direct value checks out"); -expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"q{1,2's,3}\"", 'Changing CF...'); -expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf'); -expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value'); -expect_like(qr/\QCF.{MultipleCF$$}\E: '1,2\\'s,3'/i, 'Verified change'); # ... # change a ticket's ...[other properties]... @@ -401,9 +454,9 @@ expect_send("merge $merge_ticket_B $merge_ticket_A", 'Merging the tickets...'); expect_like(qr/Merge completed/, 'Merged the tickets'); expect_send("show ticket/$merge_ticket_A/history", 'Checking merge on first ticket'); -expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in first ticket'); +expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in first ticket'); expect_send("show ticket/$merge_ticket_B/history", 'Checking merge on second ticket'); -expect_like(qr/Merged into ticket #$merge_ticket_A by root/, 'Merge recorded in second ticket'); +expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in second ticket'); { # create a user; give them privileges to take and steal @@ -522,7 +575,7 @@ sub check_attachment { my $attachment_path = shift; (my $filename = $attachment_path) =~ s/.*\/(.*)$/$1/; expect_send("comment -m 'attach file' -a $attachment_path $ticket_id", "Adding an attachment ($filename)"); - expect_like(qr/Message recorded/, "Added the attachment"); + expect_like(qr/Comments added/, "Added the attachment"); expect_send("show ticket/$ticket_id/attachments","Finding Attachment"); my $attachment_regex = qr/(\d+):\s+$filename/; expect_like($attachment_regex,"Attachment Uploaded"); @@ -536,7 +589,11 @@ sub check_attachment { TODO: { local $TODO = "Binary PNG content is getting mangled somewhere along the way" if $attachment_path =~ /\.png$/; - expect_is($attachment_content,"Attachment contains original text"); + is( + MIME::Base64::encode_base64(Test::Expect::before()), + MIME::Base64::encode_base64($attachment_content), + "Attachment contains original text" + ); } } diff --git a/rt/t/web/compilation_errors.t b/rt/t/web/compilation_errors.t index 126d33691..e4845e0de 100644 --- a/rt/t/web/compilation_errors.t +++ b/rt/t/web/compilation_errors.t @@ -6,7 +6,7 @@ BEGIN { sub wanted { -f && /\.html$/ && $_ !~ /Logout.html$/ && $File::Find::dir !~ /RichText/; } - my $tests = 8; + my $tests = 7; find( sub { wanted() and $tests += 4 }, 'share/html/' ); plan tests => $tests + 1; # plus one for warnings check } @@ -36,12 +36,10 @@ is($agent->status, 200, "Fetched the page ok"); $agent->content_contains('Logout', "Found a logout link"); -find ( sub { wanted() and test_get($agent, $File::Find::name) } , 'share/html/'); +find ( { wanted => sub { wanted() and test_get($agent, $File::Find::name) }, no_chdir => 1 } , 'share/html/'); -TODO: { - local $TODO = "we spew *lots* of undef warnings"; - $agent->no_warnings_ok; -}; +# We expect to spew a lot of warnings; toss them away +$agent->get_warnings; sub test_get { my $agent = shift; diff --git a/rt/t/web/config_tab_right.t b/rt/t/web/config_tab_right.t index 69bf80c69..df146903a 100644 --- a/rt/t/web/config_tab_right.t +++ b/rt/t/web/config_tab_right.t @@ -19,7 +19,7 @@ my ($baseurl, $m) = RT::Test->started_ok; ok $m->login($uname, $upass), "logged in"; { - $m->content_lacks('Configuration', 'no configuration tab'); + $m->content_lacks('li-admin', 'no Admin tab'); $m->get('/Admin/'); is $m->status, 403, 'no access to /Admin/'; } @@ -32,9 +32,9 @@ RT::Test->set_rights( { $m->get('/'); - $m->content_contains('Configuration', 'configuration tab is there'); + $m->content_contains('li-admin', 'admin tab is there'); - $m->follow_link_ok({text => 'Configuration'}); + $m->follow_link_ok({text => 'Admin'}); is $m->status, 200, 'user has access to /Admin/'; } diff --git a/rt/t/web/crypt-gnupg.t b/rt/t/web/crypt-gnupg.t index 85e090cbc..995b45d99 100644 --- a/rt/t/web/crypt-gnupg.t +++ b/rt/t/web/crypt-gnupg.t @@ -2,7 +2,7 @@ use strict; use warnings; use RT::Test::GnuPG - tests => 104, + tests => undef, gnupg_options => { passphrase => 'recipient', 'trust-model' => 'always', @@ -17,9 +17,9 @@ RT->Config->Set( CorrespondAddress => 'general@example.com'); RT->Config->Set( DefaultSearchResultFormat => qq{ '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#', '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject', - 'OO-__OwnerName__-O', + 'OO-__Owner__-O', 'OR-__Requestors__-O', - 'KO-__KeyOwnerName__-K', + 'KO-__KeyOwner__-K', 'KR-__KeyRequestors__-K', Status}); @@ -101,7 +101,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -169,7 +169,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -241,7 +241,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -307,7 +307,7 @@ MAIL my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; is( $msg->GetHeader('X-RT-Privacy'), - 'PGP', + 'GnuPG', "RT's outgoing mail has crypto" ); is( $msg->GetHeader('X-RT-Incoming-Encryption'), @@ -351,8 +351,13 @@ $nokey->PrincipalObj->GrantRight(Right => 'CreateTicket'); $nokey->PrincipalObj->GrantRight(Right => 'OwnTicket'); my $tick = RT::Ticket->new( RT->SystemUser ); -$tick->Create(Subject => 'owner lacks pubkey', Queue => 'general', - Owner => $nokey); +warning_like { + $tick->Create(Subject => 'owner lacks pubkey', Queue => 'general', + Owner => $nokey); +} [ + qr/nokey\@example.com: skipped: public key not found/, + qr/Recipient 'nokey\@example.com' is unusable/, +]; ok(my $id = $tick->id, 'created ticket for owner-without-pubkey'); $tick = RT::Ticket->new( RT->SystemUser ); @@ -426,25 +431,36 @@ $m->get("$baseurl/Search/Simple.html?q=General"); my $content = $m->content; $content =~ s/(/(/g; $content =~ s/)/)/g; - -like($content, qr/OO-Nobody-O/, "original OwnerName untouched"); -like($content, qr/OO-nokey-O/, "original OwnerName untouched"); -like($content, qr/OO-root-O/, "original OwnerName untouched"); - -like($content, qr/OR-recipient\@example.com-O/, "original Requestors untouched"); -like($content, qr/OR-nokey\@example.com-O/, "original Requestors untouched"); - -like($content, qr/KO-root-K/, "KeyOwnerName does not issue no-pubkey warning for recipient"); -like($content, qr/KO-nokey \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey warning for root"); -like($content, qr/KO-Nobody \(no pubkey!\)-K/, "KeyOwnerName issues no-pubkey warning for nobody"); - -like($content, qr/KR-recipient\@example.com-K/, "KeyRequestors does not issue no-pubkey warning for recipient\@example.com"); - -like($content, qr/KR-general\@example.com-K/, "KeyRequestors does not issue no-pubkey warning for general\@example.com"); -like($content, qr/KR-nokey\@example.com \(no pubkey!\)-K/, "KeyRequestors DOES issue no-pubkey warning for nokey\@example.com"); +$content =~ s/<(a|span)\b[^>]+>//g; +$content =~ s/<\/(a|span)>//g; +$content =~ s/</</g; +$content =~ s/>/>/g; + +like($content, qr/OO-Nobody in particular-O/, + "original Owner untouched"); +like($content, qr/OO-nokey-O/, + "original Owner untouched"); +like($content, qr/OO-root \(Enoch Root\)-O/, + "original Owner untouched"); +like($content, qr/OR-<recipient\@example\.com>-O/, + "original Requestors untouched"); +like($content, qr/OR-nokey-O/, + "original Requestors untouched"); + +like($content, qr/KO-Nobody in particular \(no pubkey!\)-K/, + "KeyOwner issues no-pubkey warning for nobody"); +like($content, qr/KO-nokey \(no pubkey!\)-K/, + "KeyOwner issues no-pubkey warning for root"); +like($content, qr/KO-root \(Enoch Root\)-K/, + "KeyOwner does not issue no-pubkey warning for recipient"); +like($content, qr/KR-<recipient\@example\.com>-K/, + "KeyRequestors does not issue no-pubkey warning for recipient\@example.com"); +like($content, qr/KR-nokey \(no pubkey!\)-K/, + "KeyRequestors DOES issue no-pubkey warning for nokey\@example.com"); $m->next_warning_like(qr/public key not found/); -$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); $m->next_warning_like(qr/public key not found/); -$m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); $m->no_leftover_warnings_ok; + +undef $m; +done_testing; diff --git a/rt/t/web/csrf.t b/rt/t/web/csrf.t index 24aae40a1..9d95d0685 100644 --- a/rt/t/web/csrf.t +++ b/rt/t/web/csrf.t @@ -99,9 +99,9 @@ $m->title_is('Possible cross-site request forgery'); my $link = $m->find_link(text_regex => qr{resume your request}); (my $broken_url = $link->url) =~ s/(CSRF_Token)=\w+/$1=crud/; $m->get_ok($broken_url); -$m->content_contains("Queue could not be loaded"); +$m->content_like(qr/Queue\s+could not be loaded/); $m->title_is('RT Error'); -$m->warning_like(qr/Queue could not be loaded/); +$m->warning_like(qr/Queue\s+could not be loaded/); # The token doesn't work for other pages, or other arguments to the same page. $m->add_header(Referer => undef); @@ -134,7 +134,7 @@ $m->content_contains("Create a new ticket", 'ticket create page'); $m->form_name('TicketCreate'); $m->field('Subject', 'Attachments test'); -my $logofile = "$RT::MasonComponentRoot/NoAuth/images/bpslogo.png"; +my $logofile = "$RT::StaticPath/images/bpslogo.png"; open LOGO, "<", $logofile or die "Can't open logo file: $!"; binmode LOGO; my $logo_contents = do {local $/; <LOGO>}; diff --git a/rt/t/web/custom_search.t b/rt/t/web/custom_search.t index bf7d659cd..75f832d30 100644 --- a/rt/t/web/custom_search.t +++ b/rt/t/web/custom_search.t @@ -11,7 +11,7 @@ my $url = $m->rt_base_url; my $t = RT::Ticket->new(RT->SystemUser); $t->Create(Subject => 'for custom search'.$$, Queue => 'general', - Owner => 'root', Requestor => 'customsearch@localhost'); + Owner => 'root', Requestor => 'customsearch@localhost'); ok(my $id = $t->id, 'created ticket for custom search'); ok $m->login, 'logged in'; diff --git a/rt/t/web/dashboards-basics.t b/rt/t/web/dashboards-basics.t index edb706810..c3533a3f1 100644 --- a/rt/t/web/dashboards-basics.t +++ b/rt/t/web/dashboards-basics.t @@ -41,24 +41,24 @@ $m->content_lacks('<a href="/Dashboards/Modify.html?Create=1">New</a>', $m->no_warnings_ok; $m->get_ok($url."Dashboards/Modify.html?Create=1"); -$m->content_contains("Permission denied"); +$m->content_contains("Permission Denied"); $m->content_lacks("Save Changes"); -$m->warning_like(qr/Permission denied/, "got a permission denied warning"); +$m->warning_like(qr/Permission Denied/, "got a permission denied warning"); $user_obj->PrincipalObj->GrantRight(Right => 'ModifyOwnDashboard', Object => $RT::System); # Modify itself is no longer good enough, you need Create $m->get_ok($url."Dashboards/Modify.html?Create=1"); -$m->content_contains("Permission denied"); +$m->content_contains("Permission Denied"); $m->content_lacks("Save Changes"); -$m->warning_like(qr/Permission denied/, "got a permission denied warning"); +$m->warning_like(qr/Permission Denied/, "got a permission denied warning"); $user_obj->PrincipalObj->GrantRight(Right => 'CreateOwnDashboard', Object => $RT::System); $m->get_ok($url."Dashboards/Modify.html?Create=1"); -$m->content_lacks("Permission denied"); +$m->content_lacks("Permission Denied"); $m->content_contains("Create"); $m->get_ok($url."Dashboards/index.html"); @@ -72,12 +72,12 @@ $m->content_contains("Saved dashboard different dashboard"); $user_obj->PrincipalObj->GrantRight(Right => 'SeeOwnDashboard', Object => $RT::System); $m->get($url."Dashboards/index.html"); $m->follow_link_ok({ text => 'different dashboard'}); -$m->content_lacks("Permission denied", "we now have SeeOwnDashboard"); +$m->content_lacks("Permission Denied", "we now have SeeOwnDashboard"); $m->content_lacks('Delete', "Delete button hidden because we lack DeleteOwnDashboard"); $m->get_ok($url."Dashboards/index.html"); $m->content_contains("different dashboard", "we now have SeeOwnDashboard"); -$m->content_lacks("Permission denied"); +$m->content_lacks("Permission Denied"); $m->follow_link_ok({text => "different dashboard"}); $m->content_contains("Basics"); @@ -132,9 +132,9 @@ like($searches[1]->Name, qr/highest priority tickets I own/, "correct new search my $ticket = RT::Ticket->new(RT->SystemUser); $ticket->Create( Queue => $queue->Id, - Requestor => [ $user_obj->Name ], - Owner => $user_obj, - Subject => 'dashboard test', + Requestor => [ $user_obj->Name ], + Owner => $user_obj, + Subject => 'dashboard test', ); $m->follow_link_ok({id => 'page-show'}); @@ -154,8 +154,8 @@ $m->content_contains("dashboard test", "ticket subject"); $m->get_ok("/Dashboards/Subscription.html?id=$id"); $m->form_name('SubscribeDashboard'); $m->click_button(name => 'Save'); -$m->content_contains("Permission denied"); -$m->warning_like(qr/Unable to subscribe to dashboard.*Permission denied/, "got a permission denied warning when trying to subscribe to a dashboard"); +$m->content_contains("Permission Denied"); +$m->warning_like(qr/Unable to subscribe to dashboard.*Permission Denied/, "got a permission denied warning when trying to subscribe to a dashboard"); $user_obj->Attributes->RedoSearch; is($user_obj->Attributes->Named('Subscription'), 0, "no subscriptions"); @@ -172,7 +172,7 @@ $m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s, $m->form_name('SubscribeDashboard'); $m->click_button(name => 'Save'); -$m->content_lacks("Permission denied"); +$m->content_lacks("Permission Denied"); $m->content_contains("Subscribed to dashboard different dashboard"); $user_obj->Attributes->RedoSearch; @@ -183,9 +183,9 @@ $m->follow_link_ok({text => "Subscription"}); $m->content_contains("Modify the subscription to dashboard different dashboard"); $m->get_ok("/Dashboards/Modify.html?id=$id&Delete=1"); -$m->content_contains("Permission denied", "unable to delete dashboard because we lack DeleteOwnDashboard"); +$m->content_contains("Permission Denied", "unable to delete dashboard because we lack DeleteOwnDashboard"); -$m->warning_like(qr/Couldn't delete dashboard.*Permission denied/, "got a permission denied warning when trying to delete the dashboard"); +$m->warning_like(qr/Couldn't delete dashboard.*Permission Denied/, "got a permission denied warning when trying to delete the dashboard"); $user_obj->PrincipalObj->GrantRight(Right => 'DeleteOwnDashboard', Object => $RT::System); diff --git a/rt/t/web/dashboards-groups.t b/rt/t/web/dashboards-groups.t index db2fccf1c..9f1c37deb 100644 --- a/rt/t/web/dashboards-groups.t +++ b/rt/t/web/dashboards-groups.t @@ -79,7 +79,7 @@ $m->form_name('ModifyDashboard'); $m->field("Name" => 'inner dashboard'); $m->field("Privacy" => "RT::Group-" . $inner_group->Id); $m->click_button(value => 'Create'); -$m->content_lacks("Permission denied", "we now have SeeGroupDashboard"); +$m->content_lacks("Permission Denied", "we now have SeeGroupDashboard"); $m->content_contains("Saved dashboard inner dashboard"); $m->content_lacks('Delete', "Delete button hidden because we lack DeleteDashboard"); @@ -95,15 +95,15 @@ is($dashboard->PossibleHiddenSearches, 0, "all searches are visible"); $m->get_ok("/Dashboards/Modify.html?id=$id"); $m->content_contains("inner dashboard", "we now have SeeGroupDashboard right"); -$m->content_lacks("Permission denied"); +$m->content_lacks("Permission Denied"); $m->content_contains('Subscription', "Subscription link not hidden because we have SubscribeDashboard"); $m->get_ok("/Dashboards/index.html"); $m->content_contains("inner dashboard", "We can see the inner dashboard from the UI"); -$m->get_ok("/index.html"); -$m->content_contains("inner dashboard", "We can see the inner dashboard from the menu drop-down"); +$m->get_ok("/Prefs/DashboardsInMenu.html"); +$m->content_contains("inner dashboard", "Can also see it in the menu options"); my ($group) = grep {$_->isa("RT::Group") and $_->Id == $inner_group->Id} RT::Dashboard->new($currentuser)->_PrivacyObjects; @@ -191,5 +191,5 @@ is_deeply( $m->get_ok("/Dashboards/index.html"); $m->content_contains("inner dashboard", "The dashboards list includes superuser rights"); -$m->get_ok("/index.html"); +$m->get_ok("/Prefs/DashboardsInMenu.html"); $m->content_lacks("inner dashboard", "But the menu skips them"); diff --git a/rt/t/web/dashboards-in-menu.t b/rt/t/web/dashboards-in-menu.t new file mode 100644 index 000000000..3126d55c3 --- /dev/null +++ b/rt/t/web/dashboards-in-menu.t @@ -0,0 +1,85 @@ +use strict; +use warnings; + +use RT::Test tests => 31; +my ($baseurl, $m) = RT::Test->started_ok; + +my $system_foo = RT::Dashboard->new($RT::SystemUser); +$system_foo->Save( + Name => 'system foo', + Privacy => 'RT::System-' . $RT::System->id, +); + +my $system_bar = RT::Dashboard->new($RT::SystemUser); +$system_bar->Save( + Name => 'system bar', + Privacy => 'RT::System-' . $RT::System->id, +); + +ok( $m->login(), "logged in" ); + +diag "global setting"; +# in case "RT at a glance" contains dashboards stuff. +$m->get_ok( $baseurl . "/Search/Simple.html" ); +ok( !$m->find_link( text => 'system foo' ), 'no system foo link' ); +$m->get_ok( $baseurl."/Admin/Global/DashboardsInMenu.html"); + +my $form_name = 'SelectionBox-dashboards_in_menu'; +$m->form_name($form_name); + +$m->field('dashboards_in_menu-Available' => [$system_foo->id],); +$m->click_button(name => 'add'); +$m->content_contains('Global dashboards in menu saved.', 'saved'); + +$m->logout; +ok( $m->login(), "relogged in" ); + +$m->get_ok( $baseurl . "/Search/Simple.html" ); +$m->follow_link_ok( { text => 'system foo' }, 'follow system foo link' ); +$m->title_is( 'system foo Dashboard', 'got system foo dashboard page' ); + +diag "setting in admin users"; +my $root = RT::CurrentUser->new( $RT::SystemUser ); +ok( $root->Load('root') ); +my $self_foo = RT::Dashboard->new($root); +$self_foo->Save( Name => 'self foo', Privacy => 'RT::User-' . $root->id ); +my $self_bar = RT::Dashboard->new($root); +$self_bar->Save( Name => 'self bar', Privacy => 'RT::User-' . $root->id ); + +ok( !$m->find_link( text => 'self foo' ), 'no self foo link' ); +$m->get_ok( $baseurl."/Admin/Users/DashboardsInMenu.html?id=" . $root->id); +$m->form_name($form_name); +$m->field('dashboards_in_menu-Available' => [$self_foo->id]); +$m->click_button(name => 'add'); +$m->content_contains( 'Preferences saved for dashboards in menu.', + 'prefs saved' ); +$m->form_name($form_name); +$m->field('dashboards_in_menu-Selected' => [$system_foo->id]); +$m->content_contains( 'Preferences saved for dashboards in menu.', + 'prefs saved' ); +$m->click_button(name => 'remove'); + +$m->logout; +ok( $m->login(), "relogged in" ); +$m->get_ok( $baseurl . "/Search/Simple.html" ); +ok( !$m->find_link( text => 'system foo' ), 'no system foo link' ); +$m->follow_link_ok( { text => 'self foo' }, 'follow self foo link' ); +$m->title_is( 'self foo Dashboard', 'got self foo dashboard page' ); + +diag "setting in prefs"; +$m->get_ok( $baseurl."/Prefs/DashboardsInMenu.html"); +$m->form_name($form_name); +$m->field('dashboards_in_menu-Available' => [$self_bar->id]); +$m->click_button(name => 'add'); +$m->content_contains( 'Preferences saved for dashboards in menu.', + 'prefs saved' ); +$m->follow_link_ok( { text => 'self bar' }, 'follow self bar link' ); +$m->title_is( 'self bar Dashboard', 'got self bar dashboard page' ); +$m->get_ok( $baseurl."/Prefs/DashboardsInMenu.html"); +$m->form_with_fields('Reset'); +$m->click; +$m->content_contains( 'Preferences saved', 'prefs saved' ); +ok( $m->find_link( text => 'system foo' ), 'got system foo link' ); +ok( !$m->find_link( text => 'self foo' ), 'no self foo link' ); +ok( !$m->find_link( text => 'self bar' ), 'no self bar link' ); + diff --git a/rt/t/web/dashboards-search-cache.t b/rt/t/web/dashboards-search-cache.t index 517e26ee6..18989d54e 100644 --- a/rt/t/web/dashboards-search-cache.t +++ b/rt/t/web/dashboards-search-cache.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 20; +use RT::Test tests => 33; my ($baseurl, $m) = RT::Test->started_ok; my $url = $m->rt_base_url; @@ -20,6 +20,16 @@ $m->form_name('BuildQuery'); $m->field(SavedSearchDescription => 'Original Name'); $m->click('SavedSearchSave'); +# create the inner dashboard +$m->get_ok("$url/Dashboards/Modify.html?Create=1"); +$m->form_name('ModifyDashboard'); +$m->field('Name' => 'inner dashboard'); +$m->click_button(value => 'Create'); +$m->text_contains('Saved dashboard inner dashboard'); + +my ($inner_id) = $m->content =~ /name="id" value="(\d+)"/; +ok($inner_id, "got an ID, $inner_id"); + # create a dashboard $m->get_ok("$url/Dashboards/Modify.html?Create=1"); $m->form_name('ModifyDashboard'); @@ -34,16 +44,28 @@ ok($dashboard_id, "got an ID, $dashboard_id"); $m->follow_link_ok({text => 'Content'}); my $form = $m->form_name('Dashboard-Searches-body'); my @input = $form->find_input('Searches-body-Available'); -my ($search) = +my ($search_value) = map { ( $_->possible_values )[1] } grep { ( $_->value_names )[1] =~ /Saved Search: Original Name/ } @input; -$form->value('Searches-body-Available' => $search ); +$form->value('Searches-body-Available' => $search_value ); +$m->click_button(name => 'add'); +$m->text_contains('Dashboard updated'); + +# add the dashboard to the dashboard +$m->follow_link_ok({text => 'Content'}); +$form = $m->form_name('Dashboard-Searches-body'); +@input = $form->find_input('Searches-body-Available'); +my ($dashboard_value) = + map { ( $_->possible_values )[1] } + grep { ( $_->value_names )[1] =~ /Dashboard: inner dashboard/ } @input; +$form->value('Searches-body-Available' => $dashboard_value ); $m->click_button(name => 'add'); $m->text_contains('Dashboard updated'); # subscribe to the dashboard $m->follow_link_ok({text => 'Subscription'}); $m->text_contains('Saved Search: Original Name'); +$m->text_contains('Dashboard: inner dashboard'); $m->form_name('SubscribeDashboard'); $m->click_button(name => 'Save'); $m->text_contains('Subscribed to dashboard cachey dashboard'); @@ -52,10 +74,10 @@ $m->text_contains('Subscribed to dashboard cachey dashboard'); $m->follow_link_ok({text => 'Tickets'}, 'to query builder'); $form = $m->form_name('BuildQuery'); @input = $form->find_input('SavedSearchLoad'); -($search) = +($search_value) = map { ( $_->possible_values )[1] } grep { ( $_->value_names )[1] =~ /Original Name/ } @input; -$form->value('SavedSearchLoad' => $search ); +$form->value('SavedSearchLoad' => $search_value ); $m->click_button(value => 'Load'); $m->text_contains('Loaded saved search "Original Name"'); @@ -64,10 +86,24 @@ $m->field('SavedSearchDescription' => 'New Name'); $m->click_button(value => 'Update'); $m->text_contains('Updated saved search "New Name"'); +# rename the dashboard +$m->get_ok("/Dashboards/Modify.html?id=$inner_id"); +$m->form_name('ModifyDashboard'); +$m->field('Name' => 'recursive dashboard'); +$m->click_button(value => 'Save Changes'); +$m->text_contains('Dashboard recursive dashboard updated'); + # check subscription page again $m->get_ok("/Dashboards/Subscription.html?id=$dashboard_id"); TODO: { local $TODO = 'we cache search names too aggressively'; $m->text_contains('Saved Search: New Name'); $m->text_unlike(qr/Saved Search: Original Name/); # t-w-m lacks text_lacks + + $m->text_contains('Dashboard: recursive dashboard'); + $m->text_unlike(qr/Dashboard: inner dashboard/); # t-w-m lacks text_lacks } + +$m->get_ok("/Dashboards/Render.html?id=$dashboard_id"); +$m->text_contains('New Name'); +$m->text_unlike(qr/Original Name/); # t-w-m lacks text_lacks diff --git a/rt/t/web/gnupg-select-keys-on-create.t b/rt/t/web/gnupg-select-keys-on-create.t index 8c1ae448c..e30b264d9 100644 --- a/rt/t/web/gnupg-select-keys-on-create.t +++ b/rt/t/web/gnupg-select-keys-on-create.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test::GnuPG tests => 83, gnupg_options => { passphrase => 'rt-test' }; +use RT::Test::GnuPG tests => 79, gnupg_options => { passphrase => 'rt-test' }; use RT::Action::SendEmail; my $queue = RT::Test->load_or_create_queue( @@ -37,7 +37,7 @@ diag "check that signing doesn't work if there is no key"; { RT::Test->import_gnupg_key('rt-recipient@example.com'); RT::Test->trust_gnupg_key('rt-recipient@example.com'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-recipient@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-recipient@example.com' ); is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key'; } @@ -66,11 +66,7 @@ diag "check that things don't work if there is no key"; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; - for (1 .. 4) { - $m->next_warning_like(qr/public key not found/) ; - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); - } - + $m->next_warning_like(qr/public key not found/) for 1 .. 4; $m->no_leftover_warnings_ok; } @@ -78,7 +74,7 @@ diag "import first key of rt-test\@example.com"; my $fpr1 = ''; { RT::Test->import_gnupg_key('rt-test@example.com', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[0]{'TrustLevel'}, 0, 'is not trusted key'; $fpr1 = $res{'info'}[0]{'Fingerprint'}; } @@ -127,7 +123,7 @@ diag "import a second key of rt-test\@example.com"; my $fpr2 = ''; { RT::Test->import_gnupg_key('rt-test@example.com.2', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; $fpr2 = $res{'info'}[2]{'Fingerprint'}; } @@ -174,7 +170,7 @@ diag "check that things still doesn't work if two keys are not trusted"; { RT::Test->lsign_gnupg_key( $fpr1 ); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); ok $res{'info'}[0]{'TrustLevel'} > 0, 'trusted key'; is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; } diff --git a/rt/t/web/gnupg-select-keys-on-update.t b/rt/t/web/gnupg-select-keys-on-update.t index a5b01d3ae..a666851db 100644 --- a/rt/t/web/gnupg-select-keys-on-update.t +++ b/rt/t/web/gnupg-select-keys-on-update.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test::GnuPG tests => 88, gnupg_options => { passphrase => 'rt-test' }; +use RT::Test::GnuPG tests => 86, gnupg_options => { passphrase => 'rt-test' }; use RT::Action::SendEmail; @@ -52,7 +52,7 @@ diag "check that signing doesn't work if there is no key"; { RT::Test->import_gnupg_key('rt-recipient@example.com'); RT::Test->trust_gnupg_key('rt-recipient@example.com'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-recipient@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-recipient@example.com' ); is $res{'info'}[0]{'TrustTerse'}, 'ultimate', 'ultimately trusted key'; } @@ -82,10 +82,7 @@ diag "check that things don't work if there is no key"; my @mail = RT::Test->fetch_caught_mails; ok !@mail, 'there are no outgoing emails'; - for (1 .. 2) { - $m->next_warning_like(qr/public key not found/); - $m->next_warning_like(qr/above error may result from an unconfigured RT\/GPG/); - } + $m->next_warning_like(qr/public key not found/) for 1 .. 2; $m->no_leftover_warnings_ok; } @@ -94,7 +91,7 @@ diag "import first key of rt-test\@example.com"; my $fpr1 = ''; { RT::Test->import_gnupg_key('rt-test@example.com', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[0]{'TrustLevel'}, 0, 'is not trusted key'; $fpr1 = $res{'info'}[0]{'Fingerprint'}; } @@ -144,7 +141,7 @@ diag "import a second key of rt-test\@example.com"; my $fpr2 = ''; { RT::Test->import_gnupg_key('rt-test@example.com.2', 'public'); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; $fpr2 = $res{'info'}[2]{'Fingerprint'}; } @@ -192,7 +189,7 @@ diag "check that things still doesn't work if two keys are not trusted"; { RT::Test->lsign_gnupg_key( $fpr1 ); - my %res = RT::Crypt::GnuPG::GetKeysInfo('rt-test@example.com'); + my %res = RT::Crypt->GetKeysInfo( Key => 'rt-test@example.com' ); ok $res{'info'}[0]{'TrustLevel'} > 0, 'trusted key'; is $res{'info'}[1]{'TrustLevel'}, 0, 'is not trusted key'; } @@ -253,7 +250,7 @@ diag "check that key selector works and we can select trusted key"; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_contains('Message recorded', 'Message recorded' ); + $m->content_contains('Correspondence added', 'Correspondence added' ); my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; @@ -289,7 +286,7 @@ diag "check encrypting of attachments"; $m->select( 'UseKey-rt-test@example.com' => $fpr1 ); $m->click('SubmitTicket'); - $m->content_contains('Message recorded', 'Message recorded' ); + $m->content_contains('Correspondence added', 'Correspondence added' ); my @mail = RT::Test->fetch_caught_mails; ok @mail, 'there are some emails'; diff --git a/rt/t/web/group_create.t b/rt/t/web/group_create.t index 548970d2d..f62e56595 100644 --- a/rt/t/web/group_create.t +++ b/rt/t/web/group_create.t @@ -13,7 +13,7 @@ my $group_name = 'test group'; my $group_id; diag "Create a group"; { - $m->follow_link( id => 'tools-config-groups-create'); + $m->follow_link( id => 'admin-groups-create'); # Test group form validation $m->submit_form( diff --git a/rt/t/web/helpers-http-cache-headers.t b/rt/t/web/helpers-http-cache-headers.t index 1731e9d17..1020832ca 100644 --- a/rt/t/web/helpers-http-cache-headers.t +++ b/rt/t/web/helpers-http-cache-headers.t @@ -23,10 +23,14 @@ ok $m->login, 'logged in'; my $docroot = join '/', qw(share html); # find endpoints to loop over -my @endpoints = ('/NoAuth/css/print.css'); +my @endpoints = ( + "/NoAuth/css/aileron/squished-".("0"x32).".css", + '/static/images/bpslogo.png', +); find({ wanted => sub { if ( -f $_ && $_ !~ m|autohandler$| ) { + return if m{/\.[^/]+\.sw[op]$}; # vim swap files ( my $endpoint = $_ ) =~ s|^$docroot||; push @endpoints, $endpoint; } @@ -76,7 +80,7 @@ foreach my $endpoint ( @endpoints ) { my $header_key = 'default'; if ( $endpoint =~ m|Autocomplete| ) { $header_key = 'Autocomplete'; - } elsif ( $endpoint =~ m|NoAuth| ) { + } elsif ( $endpoint =~ m/NoAuth|static/ ) { $header_key = 'NoAuth'; } my $headers = $expected->{$header_key}; diff --git a/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/ModifyLoginRedirect index 90278ae49..90278ae49 100644 --- a/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default +++ b/rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/ModifyLoginRedirect diff --git a/rt/t/web/html_template.t b/rt/t/web/html_template.t index a2764556f..108d83fa1 100644 --- a/rt/t/web/html_template.t +++ b/rt/t/web/html_template.t @@ -13,8 +13,8 @@ my $template = Encode::decode("UTF-8", "你好 éèà€"); my $subject = Encode::decode("UTF-8", "标题"); my $content = Encode::decode("UTF-8", "测试"); { - $m->follow_link_ok( { id => 'tools-config-global-templates' }, '-> Templates' ); - $m->follow_link_ok( { text => 'Autoreply' }, '-> Autoreply' ); + $m->follow_link_ok( { id => 'admin-global-templates' }, '-> Templates' ); + $m->follow_link_ok( { text => 'Autoreply in HTML' }, '-> Autoreply in HTML' ); $m->submit_form( form_name => 'ModifyTemplate', diff --git a/rt/t/web/install.t b/rt/t/web/install.t new file mode 100644 index 000000000..e33e58b16 --- /dev/null +++ b/rt/t/web/install.t @@ -0,0 +1,173 @@ +use strict; +use warnings; +use File::Spec; + +$ENV{RT_TEST_WEB_HANDLER} = 'plack+rt-server'; +use RT::Test + tests => undef, + nodb => 1, + server_ok => 1; + +my $dbname = 'rt4test_install_xxx'; +my $rtname = 'rttestname'; +my $domain = 'rttes.com'; +my $password = 'newpass'; +my $correspond = 'reply@example.com'; +my $comment = 'comment@example.com'; + +# use bin/rt to fake sendmail to make sure the file exists +my $sendmail = File::Spec->catfile( $RT::BinPath, 'rt' ); +my $owner = 'root@localhost'; + +unlink File::Spec->catfile( $RT::VarPath, $dbname ); + +my ( $url, $m ) = RT::Test->started_ok; +$m->warning_like(qr/If this is a new installation of RT/, + "Got startup warning"); + +my ($port) = $url =~ /:(\d+)/; +$m->get_ok($url); + +is( $m->uri, $url . '/Install/index.html', 'install page' ); +$m->select( 'Lang', 'zh-cn' ); +$m->click('ChangeLang'); +$m->content_contains( Encode::decode("UTF-8",'语言'), 'select chinese' ); + +$m->click('Run'); +$m->content_contains( Encode::decode("UTF-8",'数据库'), 'select db type in chinese' ); + +$m->back; +$m->select( 'Lang', 'en' ); +$m->click('ChangeLang'); +$m->content_contains( 'Select another language', 'back to english' ); + +$m->click('Run'); + +is( $m->uri, $url . '/Install/DatabaseType.html', 'db type page' ); +my $select_type = $m->current_form->find_input('DatabaseType'); +my @possible_types = $select_type->possible_values; +ok( @possible_types, 'we have at least 1 db type' ); + +SKIP: { + skip 'no mysql found', 7 unless grep { /mysql/ } @possible_types; + $m->select( 'DatabaseType', 'mysql' ); + $m->click; + for my $field (qw/Name Host Port Admin AdminPassword User Password/) { + ok( $m->current_form->find_input("Database$field"), + "db mysql has field Database$field" ); + } + $m->back; +} + +SKIP: { + skip 'no pg found', 8 unless grep { /Pg/ } @possible_types; + $m->select( 'DatabaseType', 'Pg' ); + $m->click; + for my $field ( + qw/Name Host Port Admin AdminPassword User Password/) + { + ok( $m->current_form->find_input("Database$field"), + "db Pg has field Database$field" ); + } + $m->back; +} + +$m->select( 'DatabaseType', 'SQLite' ); +$m->click; + +is( $m->uri, $url . '/Install/DatabaseDetails.html', 'db details page' ); +$m->field( 'DatabaseName' => $dbname ); +$m->submit_form( fields => { DatabaseName => $dbname } ); +$m->content_contains( 'Connection succeeded', 'succeed msg' ); +$m->content_contains( +qq{$dbname already exists, but does not contain RT's tables or metadata. The 'Initialize Database' step later on can insert tables and metadata into this existing database. if this is acceptable, click 'Customize Basic' below to continue customizing RT.}, + 'more db state msg' +); +$m->click; + +is( $m->uri, $url . '/Install/Basics.html', 'basics page' ); +$m->click; +$m->content_contains( + 'You must enter an Administrative password', + "got password can't be empty error" +); + +for my $field (qw/rtname WebDomain WebPort Password/) { + ok( $m->current_form->find_input($field), "has field $field" ); +} +is( $m->value('WebPort'), $port, 'default port' ); +$m->field( 'rtname' => $rtname ); +$m->field( 'WebDomain' => $domain ); +$m->field( 'Password' => $password ); +$m->click; + +is( $m->uri, $url . '/Install/Sendmail.html', 'mail page' ); +for my $field (qw/SendmailPath OwnerEmail/) { + ok( $m->current_form->find_input($field), "has field $field" ); +} + +$m->field( 'OwnerEmail' => '' ); +$m->click; +$m->content_contains( "doesn't look like an email address", + 'got email error' ); + +$m->field( 'SendmailPath' => '/fake/path/sendmail' ); +$m->click; +$m->content_contains( "/fake/path/sendmail doesn't exist", + 'got sendmail error' ); + +$m->field( 'SendmailPath' => $sendmail ); +$m->field( 'OwnerEmail' => $owner ); +$m->click; + +is( $m->uri, $url . '/Install/Global.html', 'global page' ); +for my $field (qw/CommentAddress CorrespondAddress/) { + ok( $m->current_form->find_input($field), "has field $field" ); +} + +$m->click; +is( $m->uri, $url . '/Install/Initialize.html', 'init db page' ); +$m->back; + +is( $m->uri, $url . '/Install/Global.html', 'global page' ); +$m->field( 'CorrespondAddress' => 'reply' ); +$m->click; +$m->content_contains( "doesn't look like an email address", + 'got email error' ); +$m->field( 'CommentAddress' => 'comment' ); +$m->click; +$m->content_contains( "doesn't look like an email address", + 'got email error' ); + +$m->field( 'CorrespondAddress' => 'reply@example.com' ); +$m->field( 'CommentAddress' => 'comment@example.com' ); +$m->click; + +is( $m->uri, $url . '/Install/Initialize.html', 'init db page' ); +$m->click; + +is( $m->uri, $url . '/Install/Finish.html', 'finish page' ); +$m->click; + +is( $m->uri, $url . '/', 'home page' ); +$m->login( 'root', $password ); +$m->content_contains( 'RT at a glance', 'logged in with newpass' ); + +RT->LoadConfig; +my $config = RT->Config; + +is( $config->Get('DatabaseType'), 'SQLite', 'DatabaseType in config' ); +is( $config->Get('DatabaseName'), $dbname, 'DatabaseName in config' ); +is( $config->Get('rtname'), $rtname, 'rtname in config' ); +is( $config->Get('WebDomain'), $domain, 'WebDomain email in config' ); +is( $config->Get('WebPort'), $port, 'WebPort email in config' ); +is( $config->Get('SendmailPath'), $sendmail, 'SendmailPath in config' ); +is( $config->Get('OwnerEmail'), $owner, 'OwnerEmail in config' ); +is( $config->Get('CorrespondAddress'), + $correspond, 'correspond address in config' ); +is( $config->Get('CommentAddress'), $comment, 'comment address in config' ); + +unlink File::Spec->catfile( $RT::VarPath, $dbname ); + +undef $m; +done_testing; diff --git a/rt/t/web/language_update.t b/rt/t/web/language_update.t new file mode 100644 index 000000000..35082f886 --- /dev/null +++ b/rt/t/web/language_update.t @@ -0,0 +1,22 @@ +use strict; +use warnings; +use RT::Test tests => 9; + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login(), 'logged in' ); + +$m->follow_link_ok({text => 'About me'}); +$m->form_with_fields('Lang'); +$m->field(Lang => 'zh_TW'); +$m->submit; + +$m->text_contains(Encode::decode("UTF-8","並讓現存的 iCal feeds不再能用"), "successfully updated to zh_TW"); +$m->text_contains(Encode::decode("UTF-8","使用語言 的值從 (無) 改為 'zh_TW'"), "when updating to language zh_TW, results are in zh_TW"); + +$m->form_with_fields('Lang'); +$m->field(Lang => 'en_us'); +$m->submit; + +$m->text_contains("breaking all existing iCal feeds", "successfully updated to en_us"); +$m->text_contains("Lang changed from 'zh_TW' to 'en_us'", "when updating to language en_us, results are in en_us"); + diff --git a/rt/t/web/login.t b/rt/t/web/login.t index d0213c373..4b3620d41 100644 --- a/rt/t/web/login.t +++ b/rt/t/web/login.t @@ -1,7 +1,9 @@ use strict; use warnings; -use RT::Test tests => 34; +use RT::Test; + +RT::Config->Set(AllowLoginPasswordAutoComplete => 1); my ( $baseurl, $m ) = RT::Test->started_ok; @@ -17,6 +19,7 @@ diag "normal login"; $m->get($baseurl); $m->title_is('Login'); is( $m->uri, $baseurl, "right url" ); + $m->content_lacks('autocomplete="off"'); $m->submit_form( form_id => 'login', diff --git a/rt/t/web/mobile.t b/rt/t/web/mobile.t new file mode 100644 index 000000000..3f32e49e6 --- /dev/null +++ b/rt/t/web/mobile.t @@ -0,0 +1,210 @@ +use strict; +use warnings; +use RT::Test tests => 170; + +my ( $url, $m ) = RT::Test->started_ok; +my $root = RT::Test->load_or_create_user( Name => 'root' ); + +diag "create another queue"; +my $test_queue = RT::Queue->new( $RT::SystemUser ); +ok( $test_queue->Create( Name => 'foo' ) ); + +diag "create cf cfbar"; +my $cfbar = RT::CustomField->new( $RT::SystemUser ); +ok( + $cfbar->Create( + Name => 'cfbar', + Type => 'Freeform', + LookupType => 'RT::Queue-RT::Ticket' + ) +); + +$cfbar->AddToObject( $test_queue ); + +diag "create some tickets to link"; +# yep, create 3 tickets for DependsOn +my @tickets = map { { Subject => "link of $_" } } + qw/DependsOn DependsOn DependsOn DependedOnBy HasMember HasMember + MemberOf RefersTo RefersTo ReferredToBy/; +RT::Test->create_tickets( { Status => 'resolved' }, @tickets ); + +diag "test different mobile agents"; +my @agents = ( + 'hiptop', 'Blazer', 'Novarra', 'Vagabond', + 'SonyEricsson', 'Symbian', 'NetFront', 'UP.Browser', + 'UP.Link', 'Windows CE', 'MIDP', 'J2ME', + 'DoCoMo', 'J-PHONE', 'PalmOS', 'PalmSource', + 'iPhone', 'iPod', 'AvantGo', 'Nokia', + 'Android', 'WebOS', 'S60' +); + +for my $agent (@agents) { + $m->agent($agent); + $m->get_ok($url); + $m->content_contains( 'Not using a mobile browser', + "mobile login page for agent $agent" ); +} + +$m->submit_form( fields => { user => 'root', pass => 'password' } ); +is( $m->uri, $url . '/m/', 'logged in via mobile ui' ); +ok( $m->find_link( text => 'Home' ), 'has homepage link, so really logged in' ); + +diag "create some tickets"; +$m->follow_link_ok( { text => 'New ticket' } ); +like( $m->uri, qr'/m/ticket/select_create_queue', 'queue select page' ); +$m->follow_link_ok( { text => 'General' } ); +like( $m->uri, qr'/m/ticket/create', 'ticket create page' ); +$m->submit_form( + fields => { + Subject => 'ticket1', + Content => 'content 1', + Status => 'open', + Cc => 'cc@example.com', + AdminCc => 'admincc@example.com', + InitialPriority => 13, + FinalPriority => 93, + TimeEstimated => 2, + 'TimeEstimated-TimeUnits' => 'hours', + TimeWorked => 30, + TimeLeft => 60, + Starts => '2011-01-11 11:11:11', + Due => '2011-02-12 12:12:12', + 'new-DependsOn' => '1 2 3', + 'DependsOn-new' => '4', + 'new-MemberOf' => '5 6', + 'MemberOf-new' => '7', + 'new-RefersTo' => '8 9', + 'RefersTo-new' => '10', + } +); +like( $m->uri, qr'/m/ticket/show', 'ticket show page' ); +$m->content_contains( 'ticket1', 'subject' ); +$m->content_contains( 'open', 'status' ); +$m->content_contains( 'cc@example.com', 'cc' ); +$m->content_contains( 'admincc@example.com', 'admincc' ); +$m->content_contains( '13/93', 'priority' ); +$m->content_contains( '2 hour', 'time estimates' ); +$m->content_contains( '30 min', 'time worked' ); +$m->content_contains( '60 min', 'time left' ); +$m->content_contains( 'Tue Jan 11 11:11:11', 'starts' ); +$m->content_contains( 'Sat Feb 12 12:12:12', 'due' ); +$m->content_like( qr/(link of DependsOn).*\1.*\1/s, 'depends on' ); +$m->content_contains( 'link of DependedOnBy', 'depended on by' ); +$m->content_like( qr/(link of HasMember).*\1/s, 'has member' ); +$m->content_contains( 'link of MemberOf', 'member of' ); +$m->content_like( qr/(link of RefersTo).*\1/s, 'refers to' ); +$m->content_contains( 'link of ReferredToBy', 'referred to by' ); + +diag "test ticket reply"; +$m->follow_link_ok( { text => 'Reply' } ); +like( $m->uri, qr'/m/ticket/reply', 'ticket reply page' ); +$m->submit_form( + fields => { + UpdateContent => 'reply 1', + UpdateTimeWorked => '30', + UpdateStatus => 'resolved', + UpdateType => 'response', + }, + button => 'SubmitTicket', +); +like( $m->uri, qr'/m/ticket/show', 'back to ticket show page' ); +$m->content_contains( '1 hour', 'time worked' ); +$m->content_contains( 'resolved', 'status' ); +$m->follow_link_ok( { text => 'Reply' } ); +like( $m->uri, qr'/m/ticket/reply', 'ticket reply page' ); +$m->submit_form( + fields => { + UpdateContent => 'reply 2', + UpdateSubject => 'ticket1', + UpdateStatus => 'open', + UpdateType => 'private', + }, + button => 'SubmitTicket', +); +$m->no_warnings_ok; +$m->content_contains( 'ticket1', 'subject' ); +$m->content_contains( 'open', 'status' ); + +like( $m->uri, qr'/m/ticket/show', 'back to ticket show page' ); + +diag "test ticket history"; +$m->follow_link_ok( { text => 'History' } ); +like( $m->uri, qr'/m/ticket/history', 'ticket history page' ); +$m->content_contains( 'content 1', 'has main content' ); +$m->content_contains( 'reply 1', 'has replied content' ); +$m->content_contains( 'reply 2', 'has replied content' ); + +diag "create another ticket in queue foo"; +$m->follow_link_ok( { text => 'Home' } ); +is( $m->uri, "$url/m/", 'main mobile page' ); +$m->follow_link_ok( { text => 'New ticket' } ); +like( $m->uri, qr'/m/ticket/select_create_queue', 'queue select page' ); +$m->follow_link_ok( { text => 'foo' } ); +like( $m->uri, qr'/m/ticket/create', 'ticket create page' ); +$m->content_contains( 'cfbar', 'has cf name' ); +$m->content_contains( 'Object-RT::Ticket--CustomField-' . $cfbar->id . '-Value', 'has cf input name' ); +$m->submit_form( + fields => { + Subject => 'ticket2', + Content => 'content 2', + Owner => $root->id, + 'Object-RT::Ticket--CustomField-' . $cfbar->id . '-Value' => 'cfvalue', + } +); +$m->no_warnings_ok; +like( $m->uri, qr'/m/ticket/show', 'ticket show page' ); +$m->content_contains( 'cfbar', 'has cf name' ); +$m->content_contains( 'cfvalue', 'has cf value' ); + +$m->follow_link_ok( { text => 'Home' } ); +is( $m->uri, "$url/m/", 'main mobile page' ); + +diag "test unowned tickets link"; +$m->follow_link_ok( { text => 'Unowned tickets' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +$m->content_contains( 'ticket1', 'has ticket1' ); +$m->content_lacks( 'ticket2', 'no ticket2' ); +$m->back; + +diag "test tickets I own link"; +$m->follow_link_ok( { text => 'Tickets I own' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +$m->content_lacks( 'ticket1', 'no ticket1' ); +ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' ); +$m->back; + +diag "test all tickets link"; +$m->follow_link_ok( { text => 'All tickets' } ); +$m->content_contains( 'Found 12 tickets', 'found 12 tickets' ); +ok( $m->find_link( text_regex => qr/ticket1/ ), 'has ticket1 link' ); +ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' ); +$m->back; + +diag "test bookmarked tickets link"; +my $ticket = RT::Ticket->new(RT::CurrentUser->new('root')); +$ticket->Load(11); +$root->ToggleBookmark($ticket); + +$m->follow_link_ok( { text => 'Bookmarked tickets' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +ok( $m->find_link( text_regex => qr/ticket1/ ), 'has ticket1 link' ); +$m->content_lacks( 'ticket2', 'no ticket2' ); +$m->back; + +diag "test tickets search"; +$m->submit_form( fields => { q => 'ticket2' } ); +$m->content_contains( 'Found 1 ticket', 'found 1 ticket' ); +$m->content_lacks( 'ticket1', 'no ticket1' ); +ok( $m->find_link( text_regex => qr/ticket2/ ), 'has ticket2 link' ); +$m->back; + +diag "test logout link"; +$m->follow_link_ok( { text => 'Logout' } ); +is( $m->uri, "$url/m/", 'still in mobile' ); +$m->submit_form( fields => { user => 'root', pass => 'password' } ); + +diag "test notmobile link"; +$m->follow_link_ok( { text => 'Home' } ); +$m->follow_link_ok( { text => 'Not using a mobile browser?' } ); +is( $m->uri, $url . '/', 'got full ui' ); + diff --git a/rt/t/web/offline.t b/rt/t/web/offline.t deleted file mode 100644 index 06d51913a..000000000 --- a/rt/t/web/offline.t +++ /dev/null @@ -1,77 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 20; - -my ( $url, $m ) = RT::Test->started_ok; -ok( $m->login, 'logged in' ); - -{ - my $template = <<EOF; -===Create-Ticket: ticket1 -Queue: General -Subject: test -Status: new -EOF - my $ticket = create_ticket_offline( $m, $template ); - ok $ticket->id, 'created a ticket with offline tool'; - is $ticket->QueueObj->Name, 'General', 'correct value'; - is $ticket->Subject, 'test', 'correct value'; - is $ticket->Status, 'new', 'correct value'; -} - -{ - my $template = <<'EOF'; -===Create-Ticket: ticket1 -Queue: General -Subject: test -Status: new -Requestor: test@example.com -EOF - my $ticket = create_ticket_offline( $m, $template ); - ok $ticket->id, 'created a ticket with offline tool'; - is $ticket->RequestorAddresses, 'test@example.com', 'correct value'; -} - -{ - my $group = RT::Group->new(RT->SystemUser); - my ($id, $msg) = $group->CreateUserDefinedGroup( Name => 'test' ); - ok $id, "created a user defined group"; - - my $template = <<'EOF'; -===Create-Ticket: ticket1 -Queue: General -Subject: test -Status: new -Requestor: test@example.com -RequestorGroup: test -EOF - my $ticket = create_ticket_offline( $m, $template ); - ok $ticket->id, 'created a ticket with offline tool'; - ok grep( - { $_->MemberId eq $group->id } - @{ $ticket->Requestors->MembersObj->ItemsArrayRef } - ), 'correct value' ; - is $ticket->RequestorAddresses, 'test@example.com', 'correct value'; -} - -sub create_ticket_offline { - my ($m, $template) = @_; - - $m->get_ok( $url . '/Tools/Offline.html' ); - - $m->submit_form( - form_name => 'TicketUpdate', - fields => { string => $template }, - button => 'UpdateTickets', - ); - - my $ticket = RT::Ticket->new( RT->SystemUser ); - $m->content_like( qr/Ticket \d+ created/, 'found ticket created message' ) - or return $ticket; - - $ticket->Load( $m->content =~ /Ticket (\d+) created/ ); - return $ticket; -} - - diff --git a/rt/t/web/offline_messages_utf8.t b/rt/t/web/offline_messages_utf8.t deleted file mode 100644 index 4cf6954bd..000000000 --- a/rt/t/web/offline_messages_utf8.t +++ /dev/null @@ -1,64 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 8; -use RT::Ticket; - -my ( $url, $m ) = RT::Test->started_ok; -$m->default_header( 'Accept-Language' => "zh-tw" ); -ok( $m->login, 'logged in' ); - -my $ticket_id; -my $template; - -{ - - # test create message - $template = <<EOF; -===Create-Ticket: ticket1 -Queue: General -Subject: test message -Status: new -Content: -ENDOFCONTENT -Due: -TimeEstimated: 100 -TimeLeft: 100 -FinalPriority: 90 -EOF - - $m->get_ok( $url . '/Tools/Offline.html' ); - - $m->submit_form( - form_name => 'TicketUpdate', - fields => { string => $template, }, - button => 'UpdateTickets', - ); - my $content = Encode::encode("UTF-8", $m->content); - ok( $content =~ m/申請單 #(\d+) 成功新增於 'General' 表單/, 'message is shown right' ); - $ticket_id = $1; -} - -{ - - # test update message - $template = <<EOF; -===Update-Ticket: 1 -Subject: test message update -EOF - - $m->get_ok( $url . '/Tools/Offline.html' ); - $m->submit_form( - form_name => 'TicketUpdate', - fields => { string => $template, }, - button => 'UpdateTickets', - ); - - my $content = Encode::encode("UTF-8", $m->content); - ok( - $content =~ -qr/主題\s*的值從\s*'test message'\s*改為\s*'test message update'/, - 'subject is updated' - ); -} - diff --git a/rt/t/web/offline_utf8.t b/rt/t/web/offline_utf8.t deleted file mode 100644 index aab3049a3..000000000 --- a/rt/t/web/offline_utf8.t +++ /dev/null @@ -1,53 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 9; - -use RT::Ticket; -my $file = File::Spec->catfile( RT::Test->temp_directory, 'template' ); -open my $fh, '>', $file or die $!; -my $template = Encode::decode("UTF-8",<<EOF); -===Create-Ticket: ticket1 -Queue: General -Subject: 标题 -Status: new -Content: -这是正文 -ENDOFCONTENT -EOF - -print $fh Encode::encode("UTF-8",$template); -close $fh; - -my ( $url, $m ) = RT::Test->started_ok; -ok( $m->login, 'logged in' ); - -$m->get_ok( $url . '/Tools/Offline.html' ); - -$m->submit_form( - form_name => 'TicketUpdate', - fields => { Template => $file, }, - button => 'Parse', -); - -$m->content_contains( Encode::decode("UTF-8",'这是正文'), 'content is parsed right' ); - -$m->submit_form( - form_name => 'TicketUpdate', - button => 'UpdateTickets', - - # mimic what browsers do: they seems decoded $template - fields => { string => $template }, -); - -$m->content_like( qr/Ticket \d+ created/, 'found ticket created message' ); -my ( $ticket_id ) = $m->content =~ /Ticket (\d+) created/; - -my $ticket = RT::Ticket->new( RT->SystemUser ); -$ticket->Load( $ticket_id ); -is( $ticket->Subject, Encode::decode("UTF-8",'标题'), 'subject in $ticket is right' ); - -$m->goto_ticket($ticket_id); -$m->content_contains( Encode::decode("UTF-8",'这是正文'), - 'content is right in ticket display page' ); - diff --git a/rt/t/web/owner_disabled_group_19221.t b/rt/t/web/owner_disabled_group_19221.t index d41decfd2..b71fc5b3e 100644 --- a/rt/t/web/owner_disabled_group_19221.t +++ b/rt/t/web/owner_disabled_group_19221.t @@ -122,7 +122,7 @@ diag "Check WithMember and WithoutMember recursively"; { my $with = RT::Groups->new( RT->SystemUser ); $with->WithMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 ); - $with->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' ); + $with->LimitToUserDefinedGroups; is_deeply( [map {$_->Name} @{$with->ItemsArrayRef}], ['Disabled Group','Supergroup'], @@ -131,7 +131,7 @@ diag "Check WithMember and WithoutMember recursively"; my $without = RT::Groups->new( RT->SystemUser ); $without->WithoutMember( PrincipalId => $user->PrincipalObj->Id, Recursively => 1 ); - $without->Limit( FIELD => 'domain', OPERATOR => '=', VALUE => 'UserDefined' ); + $without->LimitToUserDefinedGroups; is_deeply( [map {$_->Name} @{$without->ItemsArrayRef}], [], diff --git a/rt/t/web/path-traversal.t b/rt/t/web/path-traversal.t index 01302e672..8207265ef 100644 --- a/rt/t/web/path-traversal.t +++ b/rt/t/web/path-traversal.t @@ -8,35 +8,30 @@ ok($agent->login); $agent->get("$baseurl/NoAuth/../Elements/HeaderJavascript"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +$agent->warning_like(qr/Invalid request.*aborting/); $agent->get("$baseurl/NoAuth/../%45lements/HeaderJavascript"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +$agent->warning_like(qr/Invalid request.*aborting/); $agent->get("$baseurl/NoAuth/%2E%2E/Elements/HeaderJavascript"); is($agent->status, 400); -$agent->warning_like(qr/Invalid request.*aborting/,); +$agent->warning_like(qr/Invalid request.*aborting/); $agent->get("$baseurl/NoAuth/../../../etc/RT_Config.pm"); is($agent->status, 400); -SKIP: { - skip "Apache rejects busting up above / for us", 2 if $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/; - $agent->warning_like(qr/Invalid request.*aborting/,); -}; +$agent->warning_like(qr/Invalid request.*aborting/) unless $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/; -$agent->get("$baseurl/NoAuth/css/web2/images/../../../../../../etc/RT_Config.pm"); -is($agent->status, 400); -SKIP: { - skip "Apache rejects busting up above / for us", 2 if $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/; - $agent->warning_like(qr/Invalid request.*aborting/,); -}; +$agent->get("$baseurl/static/css/web2/images/../../../../../../etc/RT_Config.pm"); +# Apache hardcodes a 400m but the static handler returns a 403 for traversal too high +is($agent->status, $ENV{RT_TEST_WEB_HANDLER} =~ /^apache/ ? 400 : 403); # Do not reject a simple /. in the URL, for downloading uploaded # dotfiles, for example. $agent->get("$baseurl/Ticket/Attachment/28/9/.bashrc"); is($agent->status, 200); # Even for a file not found, we return 200 -$agent->content_contains("Bad attachment id"); +$agent->next_warning_like(qr/could not be loaded/, "couldn't loaded warning"); +$agent->content_like(qr/Attachment \S+ could not be loaded/); # do not reject these URLs, even though they contain /. outside the path $agent->get("$baseurl/index.html?ignored=%2F%2E"); diff --git a/rt/t/web/psgi-wrap.t b/rt/t/web/psgi-wrap.t new file mode 100644 index 000000000..0e4b0532b --- /dev/null +++ b/rt/t/web/psgi-wrap.t @@ -0,0 +1,15 @@ +use strict; +use warnings; + +use RT::Test + tests => undef, + plugins => [qw(RT::Extension::PSGIWrap)]; + +my ($base, $m) = RT::Test->started_ok; +$m->login; +ok(my $res = $m->get("/")); +is($res->code, 200, 'Successful request to /'); +ok($res->header('X-RT-PSGIWrap'), 'X-RT-PSGIWrap header set from the plugin'); + +undef $m; +done_testing(); diff --git a/rt/t/web/query_builder.t b/rt/t/web/query_builder.t index 3589c381a..dbe909939 100644 --- a/rt/t/web/query_builder.t +++ b/rt/t/web/query_builder.t @@ -196,7 +196,7 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta # create a custom field with nonascii name and try to add a condition { my $cf = RT::CustomField->new( RT->SystemUser ); - $cf->LoadByName( Name => "\x{442}", Queue => 0 ); + $cf->LoadByName( Name => "\x{442}", LookupType => RT::Ticket->CustomFieldLookupType, ObjectId => 0 ); if ( $cf->id ) { is($cf->Type, 'Freeform', 'loaded and type is correct'); } else { @@ -212,10 +212,10 @@ diag "click advanced, enter 'C1 OR ( C2 AND C3 )', apply, aggregators should sta ok( $response->is_success, "Fetched " . $url."Search/Build.html" ); ok($agent->form_name('BuildQuery'), "found the form once"); - $agent->field("ValueOf'CF.{\x{442}}'", "\x{441}"); + $agent->field("ValueOfCF.{\x{442}}", "\x{441}"); $agent->submit(); is( getQueryFromForm($agent), - "'CF.{\x{442}}' LIKE '\x{441}'", + "CF.{\x{442}} LIKE '\x{441}'", "no changes, no duplicate condition with badly encoded text" ); diff --git a/rt/t/web/query_builder_queue_limits.t b/rt/t/web/query_builder_queue_limits.t index 332cc939c..6bbf33386 100644 --- a/rt/t/web/query_builder_queue_limits.t +++ b/rt/t/web/query_builder_queue_limits.t @@ -71,9 +71,9 @@ $m->get_ok( $url . '/Search/Build.html' ); diag "check default statuses, cf and owners"; my $form = $m->form_name('BuildQuery'); ok( $form, 'found BuildQuery form' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf by default' ); -ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf by default' ); -ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf by default' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf by default' ); +ok( !$form->find_input("ValueOfCF.{general_cf}"), 'no general_cf by default' ); +ok( !$form->find_input("ValueOfCF.{foo_cf}"), 'no foo_cf by default' ); my $status_input = $form->find_input('ValueOfStatus'); my @statuses = sort $status_input->possible_values; @@ -94,9 +94,9 @@ $m->submit_form( ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); -ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'still no general_cf' ); +ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); +ok( !$form->find_input("ValueOfCF.{general_cf}"), 'still no general_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( @@ -119,9 +119,9 @@ $m->submit_form( ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' ); -ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +ok( $form->find_input("ValueOfCF.{general_cf}"), 'found general_cf' ); +ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( @@ -144,9 +144,9 @@ $m->submit_form( ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); -ok( !$form->find_input("ValueOf'CF.{foo_cf}'"), 'no foo_cf' ); -ok( !$form->find_input("ValueOf'CF.{general_cf}'"), 'no general_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); +ok( !$form->find_input("ValueOfCF.{foo_cf}"), 'no foo_cf' ); +ok( !$form->find_input("ValueOfCF.{general_cf}"), 'no general_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( @@ -166,9 +166,9 @@ $m->submit_form( fields => { Query => q{Queue = 'General' OR Queue = 'foo'} }, ); $form = $m->form_name('BuildQuery'); -ok( $form->find_input("ValueOf'CF.{general_cf}'"), 'found general_cf' ); -ok( $form->find_input("ValueOf'CF.{foo_cf}'"), 'found foo_cf' ); -ok( $form->find_input("ValueOf'CF.{global_cf}'"), 'found global_cf' ); +ok( $form->find_input("ValueOfCF.{general_cf}"), 'found general_cf' ); +ok( $form->find_input("ValueOfCF.{foo_cf}"), 'found foo_cf' ); +ok( $form->find_input("ValueOfCF.{global_cf}"), 'found global_cf' ); $status_input = $form->find_input('ValueOfStatus'); @statuses = sort $status_input->possible_values; is_deeply( diff --git a/rt/t/web/query_log.t b/rt/t/web/query_log.t index 89cca2d7b..cfb4d81d7 100644 --- a/rt/t/web/query_log.t +++ b/rt/t/web/query_log.t @@ -14,6 +14,5 @@ $root->LoadByEmail('root@localhost'); $m->get_ok("/Admin/Tools/Queries.html"); $m->text_contains("/index.html", "we include info about a page we hit while logging in"); $m->text_contains("Stack:", "stack traces"); -$m->text_like(qr{share/html/autohandler:\d+}, "stack trace includes mason components"); +$m->text_like(qr{/autohandler:\d+}, "stack trace includes mason components"); $m->text_contains("SELECT * FROM Principals WHERE id = '".$root->id."'", "we interpolate bind params"); - diff --git a/rt/t/web/queue_create.t b/rt/t/web/queue_create.t index 566876231..40f7b3b8b 100644 --- a/rt/t/web/queue_create.t +++ b/rt/t/web/queue_create.t @@ -13,7 +13,7 @@ my $queue_name = 'test queue'; my $queue_id; diag "Create a queue"; { - $m->follow_link( id => 'tools-config-queues-create'); + $m->follow_link( id => 'admin-queues-create'); # Test queue form validation $m->submit_form( diff --git a/rt/t/web/redirect-after-login.t b/rt/t/web/redirect-after-login.t index 35025a1e1..eb2718cf3 100644 --- a/rt/t/web/redirect-after-login.t +++ b/rt/t/web/redirect-after-login.t @@ -4,8 +4,6 @@ use warnings; use RT::Test tests => 122; -RT->Config->Set( GnuPG => Enable => 0 ); - my ($baseurl, $agent) = RT::Test->started_ok; my $url = $agent->rt_base_url; @@ -226,7 +224,7 @@ for my $path (qw(Prefs/Other.html /Prefs/Other.html)) { unlike($agent->content, qr/Your username or password is incorrect/, "didn't get any error message"); } -# XXX TODO: we should also be testing WebExternalAuth here, but we don't have +# XXX TODO: we should also be testing WebRemoteUserAuth here, but we don't have # the framework for dealing with that 1; diff --git a/rt/t/web/reminder-permissions.t b/rt/t/web/reminder-permissions.t new file mode 100644 index 000000000..dd859cd33 --- /dev/null +++ b/rt/t/web/reminder-permissions.t @@ -0,0 +1,178 @@ +use strict; +use warnings; +use RT::Test tests => 40; + +my $user_a = RT::Test->load_or_create_user( + Name => 'user_a', + Password => 'password', +); + +ok( $user_a && $user_a->id, 'created user_a' ); +ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [qw/SeeQueue CreateTicket ShowTicket OwnTicket/] + }, + ), + 'add basic rights for user_a' +); + +ok( + RT::Test->add_rights( + { + Principal => 'Owner', + Right => [qw/ModifyTicket/], + }, + ), + 'add basic rights for owner' +); + +my $ticket = RT::Test->create_ticket( + Subject => 'test reminder permission', + Queue => 'General', +); + +ok( $ticket->id, 'created a ticket' ); + +my ( $baseurl, $m ) = RT::Test->started_ok; +$m->login; + +my ( $root_reminder_id, $user_a_reminder_id ); +diag "create two reminders, with owner root and user_a, respectively"; +{ + $m->goto_ticket( $ticket->id ); + $m->text_contains( 'New reminder:', 'can create a new reminder' ); + $m->form_name('UpdateReminders'); + $m->field( 'NewReminder-Subject' => "root reminder" ); + $m->submit; + $m->text_contains( "Reminder 'root reminder': Created", + 'created root reminder' ); + + $m->form_name('UpdateReminders'); + $m->field( 'NewReminder-Subject' => "user_a reminder", ); + $m->field( 'NewReminder-Owner' => $user_a->id, ); + $m->submit; + $m->text_contains( "Reminder 'user_a reminder': Created", + 'created user_a reminder' ); + + my $reminders = RT::Reminders->new($user_a); + $reminders->Ticket( $ticket->id ); + my $col = $reminders->Collection; + while ( my $c = $col->Next ) { + if ( $c->Subject eq 'root reminder' ) { + $root_reminder_id = $c->id; + } + elsif ( $c->Subject eq 'user_a reminder' ) { + $user_a_reminder_id = $c->id; + } + } +} + +diag "check root_a can update user_a reminder but not root reminder"; +my $m_a = RT::Test::Web->new; +{ + ok( $m_a->login( user_a => 'password' ), 'logged in as user_a' ); + $m_a->goto_ticket( $ticket->id ); + $m_a->content_lacks( 'New reminder:', 'can not create a new reminder' ); + $m_a->content_contains( 'root reminder', 'can see root reminder' ); + $m_a->content_contains( 'user_a reminder', 'can see user_a reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is disabled" + ); + + $m_a->form_name('UpdateReminders'); + $m_a->tick( "Complete-Reminder-$user_a_reminder_id" => 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'user_a reminder': Status changed from 'open' to 'resolved'", + 'complete user_a reminder' ); + + $m_a->follow_link_ok( { id => 'page-reminders' } ); + $m_a->title_is( "Reminders for ticket #" . $ticket->id ); + $m_a->content_contains( 'root reminder', 'can see root reminder' ); + $m_a->content_contains( 'user_a reminder', 'can see user_a reminder' ); + $m_a->content_lacks( 'New reminder:', 'can not create a new reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is disabled" + ); + + $m_a->form_name('UpdateReminders'); + $m_a->untick( "Complete-Reminder-$user_a_reminder_id", 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'user_a reminder': Status changed from 'resolved' to 'open'", + 'reopen user_a reminder' + ); + +} + +diag "set ticket owner to user_a to let user_a grant modify ticket right"; +{ + $ticket->SetOwner( $user_a->id ); + + $m_a->goto_ticket( $ticket->id ); + $m_a->content_contains( 'New reminder:', 'can create a new reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is still disabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->field( 'NewReminder-Subject' => "user_a from display reminder" ); + $m_a->submit; + $m_a->text_contains( "Reminder 'user_a from display reminder': Created", + 'created user_a from display reminder' ); + + $m_a->follow_link_ok( { id => 'page-reminders' } ); + $m_a->title_is( "Reminders for ticket #" . $ticket->id ); + $m_a->content_contains( 'New reminder:', 'can create a new reminder' ); + $m_a->content_like( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is still disabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->field( 'NewReminder-Subject' => "user_a from reminders reminder" ); + $m_a->submit; + $m_a->text_contains( "Reminder 'user_a from reminders reminder': Created", + 'created user_a from reminders reminder' ); +} + +diag "grant user_a with ModifyTicket globally"; +{ + ok( + RT::Test->add_rights( + { + Principal => $user_a, + Right => [qw/ModifyTicket/], + }, + ), + 'add ModifyTicket rights to user_a' + ); + + $m_a->goto_ticket( $ticket->id ); + $m_a->content_unlike( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is enabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->tick( "Complete-Reminder-$root_reminder_id" => 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'root reminder': Status changed from 'open' to 'resolved'", + 'complete root reminder' ); + + $m_a->follow_link_ok( { id => 'page-reminders' } ); + $m_a->content_unlike( +qr!<input[^/]+name="Complete-Reminder-$root_reminder_id"[^/]+disabled="disabled"!, + "root reminder checkbox is enabled" + ); + $m_a->form_name('UpdateReminders'); + $m_a->untick( "Complete-Reminder-$root_reminder_id" => 1 ); + $m_a->submit; + $m_a->text_contains( + "Reminder 'root reminder': Status changed from 'resolved' to 'open'", + 'reopen root reminder' ); +} + diff --git a/rt/t/web/reminders.t b/rt/t/web/reminders.t index 510235156..98a8d6954 100644 --- a/rt/t/web/reminders.t +++ b/rt/t/web/reminders.t @@ -26,7 +26,7 @@ $m->goto_ticket($ticket->id); $m->form_name('UpdateReminders'); $m->field( 'NewReminder-Subject' => "baby's first reminder" ); $m->submit; -$m->content_contains("Reminder 'baby's first reminder' added"); +$m->content_contains("Reminder 'baby's first reminder': Created"); $ticket->SetStatus('deleted'); is( $ticket->Status, 'deleted', 'deleted ticket' ); diff --git a/rt/t/web/remote_user.t b/rt/t/web/remote_user.t index edad6ef95..c17a93379 100644 --- a/rt/t/web/remote_user.t +++ b/rt/t/web/remote_user.t @@ -1,36 +1,197 @@ use strict; use warnings; use RT; -use RT::Test tests => 9; -use MIME::Base64 qw//; +use RT::Test plan => 'no_plan'; -RT->Config->Set( DevelMode => 0 ); -RT->Config->Set( WebExternalAuth => 1 ); +sub stop_server { + my $mech = shift; -sub auth { - return Authorization => "Basic " . - MIME::Base64::encode( join(":", @_) ); + # Ensure we're logged in for the final warnings check + $$mech->auth("root"); + + # Force the warnings check before we stop the server + undef $$mech; + + RT::Test->stop_server; +} + +diag "Continuous + Fallback"; +{ + RT->Config->Set( DevelMode => 0 ); + RT->Config->Set( WebRemoteUserAuth => 1 ); + RT->Config->Set( WebRemoteUserAuthContinuous => 1 ); + RT->Config->Set( WebFallbackToRTLogin => 1 ); + RT->Config->Set( WebRemoteUserAutocreate => 0 ); + + my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); + + diag "Internal auth"; + { + # Empty REMOTE_USER + $m->auth(""); + + # First request gets the login form + $m->get_ok($url, "No basic auth is OK"); + $m->content_like(qr/Login/, "Login form"); + + # Log in using RT's form + $m->submit_form_ok({ + with_fields => { + user => 'root', + pass => 'password', + }, + }, "Submitted login form"); + ok $m->logged_in_as("root"), "Logged in as root"; + + # Still logged in on another request without REMOTE_USER + $m->follow_link_ok({ text => 'My Tickets' }); + ok $m->logged_in_as("root"), "Logged in as root"; + + ok $m->logout, "Logged out"; + + # We're definitely logged out? + $m->get_ok($url); + $m->content_like(qr/Login/, "Login form"); + } + + diag "External auth"; + { + # REMOTE_USER of root + $m->auth("root"); + + # Automatically logged in as root without Login page + $m->get_ok($url); + ok $m->logged_in_as("root"), "Logged in as root"; + + # Still logged in on another request + $m->follow_link_ok({ text => 'My Tickets' }); + ok $m->logged_in_as("root"), "Still logged in as root"; + + # Drop credentials and... + $m->auth(""); + + # ...see if RT notices + $m->get($url); + is $m->status, 403, "403 Forbidden from RT"; + + # Next request gets us the login form + $m->get_ok($url); + $m->content_like(qr/Login/, "Login form"); + } + + diag "External auth with invalid user, login internally"; + { + # REMOTE_USER of invalid + $m->auth("invalid"); + + # Login internally via the login link + $m->get("$url/Search/Build.html"); + is $m->status, 403, "403 Forbidden"; + $m->follow_link_ok({ url_regex => qr'NoAuth/Login\.html' }, "follow logout link"); + $m->content_like(qr/Login/, "Login form"); + + # Log in using RT's form + $m->submit_form_ok({ + with_fields => { + user => 'root', + pass => 'password', + }, + }, "Submitted login form"); + ok $m->logged_in_as("root"), "Logged in as root"; + like $m->uri, qr'Search/Build\.html', "at our originally requested page"; + + # Still logged in on another request + $m->follow_link_ok({ text => 'Tools' }); + ok $m->logged_in_as("root"), "Logged in as root"; + + ok $m->logout, "Logged out"; + + $m->next_warning_like(qr/Couldn't find internal user for 'invalid'/, "found warning for first request"); + $m->next_warning_like(qr/Couldn't find internal user for 'invalid'/, "found warning for second request"); + } + + stop_server(\$m); } -my ( $url, $m ) = RT::Test->started_ok( basic_auth => 1 ); -$m->get($url); -is($m->status, 401, "Initial request with no creds gets 401"); +diag "Fallback OFF"; +{ + RT->Config->Set( DevelMode => 0 ); + RT->Config->Set( WebRemoteUserAuth => 1 ); + RT->Config->Set( WebRemoteUserContinuous => 0 ); + RT->Config->Set( WebFallbackToRTLogin => 0 ); + RT->Config->Set( WebRemoteUserAutocreate => 0 ); -$m->get($url, auth( root => "wrong" )); -is($m->status, 401, "Request with wrong creds gets 401"); + my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); -$m->get($url, auth( root => "password" )); -is($m->status, 200, "Request with right creds gets 200"); + diag "No remote user"; + { + $m->auth(""); + $m->get($url); + is $m->status, 403, "Forbidden"; + } + + stop_server(\$m); +} -$m->content_like( - qr{<span class="current-user">\Qroot\E</span>}i, - "Has user on the page" -); -$m->content_unlike(qr/Logout/i, "Has no logout button, no WebFallbackToInternalAuth"); +diag "WebRemoteUserAutocreate"; +{ + RT->Config->Set( DevelMode => 0 ); + RT->Config->Set( WebRemoteUserAuth => 1 ); + RT->Config->Set( WebRemoteUserContinuous => 1 ); + RT->Config->Set( WebFallbackToRTLogin => 0 ); + RT->Config->Set( WebRemoteUserAutocreate => 1 ); + RT->Config->Set( UserAutocreateDefaultsOnLogin => { Organization => "BPS" } ); -$m->get($url); -is($m->status, 401, "Subsequent requests without credentials aren't still logged in"); + my ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); + diag "New user"; + { + $m->auth("anewuser"); + $m->get_ok($url); + ok $m->logged_in_as("anewuser"), "Logged in as anewuser"; + + my $user = RT::User->new( RT->SystemUser ); + $user->Load("anewuser"); + ok $user->id, "Found newly created user"; + is $user->Organization, "BPS", "Found Organization from UserAutocreateDefaultsOnLogin hash"; + ok $user->Privileged, "Privileged by default"; + } + + stop_server(\$m); + RT->Config->Set( + UserAutocreateDefaultsOnLogin => { + Privileged => 0, + EmailAddress => 'foo@example.com', + }, + ); + ( $url, $m ) = RT::Test->started_ok( basic_auth => 'anon' ); + + diag "Create unprivileged users"; + { + $m->auth("unpriv"); + $m->get_ok($url); + ok $m->logged_in_as("unpriv"), "Logged in as an unpriv user"; + like $m->uri->path, RT->Config->Get('SelfServiceRegex'), "SelfService URL"; + + my $user = RT::User->new( RT->SystemUser ); + $user->Load("unpriv"); + ok $user->id, "Found newly created user"; + ok !$user->Privileged, "Unprivileged per config"; + is $user->EmailAddress, 'foo@example.com', "Email address per config"; + } + + diag "User creation failure"; + { + $m->auth("conflicting"); + $m->get($url); + is $m->status, 403, "Forbidden"; + $m->next_warning_like(qr/Couldn't auto-create user 'conflicting' when attempting WebRemoteUser: Email address in use/, 'found failed auth warning'); + + my $user = RT::User->new( RT->SystemUser ); + $user->Load("conflicting"); + ok !$user->id, "Couldn't find conflicting user"; + } + + stop_server(\$m); +} -# Put the credentials back for the warnings check at the end -$m->default_header( auth( root => "password" )); diff --git a/rt/t/web/rest-search-group.t b/rt/t/web/rest-search-group.t new file mode 100644 index 000000000..b62aa09e4 --- /dev/null +++ b/rt/t/web/rest-search-group.t @@ -0,0 +1,102 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my $group_foo = RT::Group->new($RT::SystemUser); +$group_foo->CreateUserDefinedGroup( Name => 'foo' ); + +my $group_bar = RT::Group->new($RT::SystemUser); +$group_bar->CreateUserDefinedGroup( Name => 'bar' ); + +my $group_baz = RT::Group->new($RT::SystemUser); +$group_baz->CreateUserDefinedGroup( Name => 'baz' ); +$group_baz->SetDisabled(1); + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login, 'logged in' ); + +search_groups_ok( + { query => 'id = ' . $group_foo->id }, + [ $group_foo->id . ': foo' ], + 'search by id' +); + +search_groups_ok( + { + query => 'Name = ' . $group_foo->Name, + format => 's', + fields => 'id,name', + }, + [ "id\tName", $group_foo->id . "\tfoo" ], + 'search by name with customized fields' +); + +search_groups_ok( + { query => 'foo = 3' }, + ['Invalid field specification: foo'], + 'invalid field' +); + +search_groups_ok( + { query => 'id foo 3' }, + ['Invalid operator specification: foo'], + 'invalid op' +); + +search_groups_ok( + { query => '', orderby => 'id' }, + [ $group_foo->id . ': foo', $group_bar->id . ': bar', ], + 'order by id' +); + +search_groups_ok( + { query => '', orderby => 'name' }, + [ $group_bar->id . ': bar', $group_foo->id . ': foo' ], + 'order by name' +); + +search_groups_ok( + { query => '', orderby => '+name' }, + [ $group_bar->id . ': bar', $group_foo->id . ': foo' ], + 'order by +name' +); + +search_groups_ok( + { query => '', orderby => '-name' }, + [ $group_foo->id . ': foo', $group_bar->id . ': bar' ], + 'order by -name' +); + +search_groups_ok( + { query => 'Disabled = 0', orderby => 'id' }, + [ $group_foo->id . ': foo', $group_bar->id . ': bar' ], + 'enabled groups' +); + +search_groups_ok( + { query => 'Disabled = 1', orderby => 'id' }, + [ $group_baz->id . ': baz' ], + 'disabled groups' +); + +sub search_groups_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $query = shift; + my $expected = shift; + my $name = shift || 'search groups'; + + my $uri = URI->new("$baseurl/REST/1.0/search/group"); + $uri->query_form(%$query); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply( \@lines, $expected, $name ); + +} + +undef $m; +done_testing(); diff --git a/rt/t/web/rest-search-queue.t b/rt/t/web/rest-search-queue.t new file mode 100644 index 000000000..a827d8643 --- /dev/null +++ b/rt/t/web/rest-search-queue.t @@ -0,0 +1,104 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my $queue_foo = RT::Test->load_or_create_queue( Name => 'Foo' ); +my $queue_bar = RT::Test->load_or_create_queue( Name => 'Bar' ); +my $queue_baz = RT::Test->load_or_create_queue( Name => 'Baz' ); +$queue_baz->SetDisabled(1); + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login, 'logged in' ); + +search_queues_ok( { query => 'id = 1' }, ['1: General'], 'search id = 1' ); +search_queues_ok( + { + query => 'Name = General', + format => 's', + fields => 'id,name,description' + }, + [ "id\tName\tDescription", "1\tGeneral\tThe default queue" ], + 'search by name with customized fields' +); + +search_queues_ok( + { query => 'id > 10' }, + ['No matching results.'], + 'no matching results' +); + +search_queues_ok( + { query => 'foo = 3' }, + ['Invalid field specification: foo'], + 'invalid field' +); + +search_queues_ok( + { query => 'id foo 3' }, + ['Invalid operator specification: foo'], + 'invalid op' +); + +search_queues_ok( + { query => '', orderby => 'id' }, + [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ], + 'order by id' +); + +search_queues_ok( + { query => '', orderby => 'name' }, + [ $queue_bar->id . ': Bar', $queue_foo->id . ': Foo', '1: General', ], + 'order by name' +); + +search_queues_ok( + { query => '', orderby => '+name' }, + [ $queue_bar->id . ': Bar', $queue_foo->id . ': Foo', '1: General', ], + 'order by +name' +); + +search_queues_ok( + { query => '', orderby => '-name' }, + [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ], + 'order by -name' +); + +search_queues_ok( + { query => 'Disabled = 0', orderby => 'id' }, + [ '1: General', $queue_foo->id . ': Foo', $queue_bar->id . ': Bar', ], + 'enabled queues' +); + +search_queues_ok( + { query => 'Disabled = 1', orderby => 'id' }, + [ $queue_baz->id . ': Baz', ], + 'disabled queues' +); + +search_queues_ok( + { query => 'Disabled = 2', orderby => 'id' }, + [ '2: ___Approvals', ], + 'special Approvals queue' +); + +sub search_queues_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $query = shift; + my $expected = shift; + my $name = shift || 'search queues'; + + my $uri = URI->new("$baseurl/REST/1.0/search/queue"); + $uri->query_form(%$query); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply( \@lines, $expected, $name ); + +} + +undef $m; +done_testing(); diff --git a/rt/t/web/rest-search-user.t b/rt/t/web/rest-search-user.t new file mode 100644 index 000000000..84a967377 --- /dev/null +++ b/rt/t/web/rest-search-user.t @@ -0,0 +1,115 @@ +use strict; +use warnings; +use RT::Test tests => undef; + +my $root = RT::Test->load_or_create_user( Name => 'root', ); +my $user_foo = RT::Test->load_or_create_user( + Name => 'foo', + Password => 'password', +); +my $user_bar = RT::Test->load_or_create_user( Name => 'bar' ); +my $user_baz = RT::Test->load_or_create_user( Name => 'baz' ); +$user_baz->SetDisabled(1); + +my ( $baseurl, $m ) = RT::Test->started_ok; + +ok( $m->login, 'logged in' ); + +search_users_ok( + { query => 'id = ' . $user_foo->id }, + [ $user_foo->id . ': foo' ], + 'search by id' +); + +search_users_ok( + { + query => 'Name = ' . $user_foo->Name, + format => 's', + fields => 'id,name' + }, + [ "id\tName", $user_foo->id . "\tfoo" ], + 'search by name with customized fields' +); + + +search_users_ok( + { query => 'foo = 3' }, + ['Invalid field specification: foo'], + 'invalid field' +); + +search_users_ok( + { query => 'id foo 3' }, + ['Invalid operator specification: foo'], + 'invalid op' +); + +search_users_ok( + { query => 'password = foo' }, + ['Invalid field specification: password'], + "can't search password" +); + +search_users_ok( + { query => '', orderby => 'id' }, + [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar', ], + 'order by id' +); + +search_users_ok( + { query => '', orderby => 'name' }, + [ $user_bar->id . ': bar', $user_foo->id . ': foo', $root->id . ': root' ], + 'order by name' +); + +search_users_ok( + { query => '', orderby => '+name' }, + [ $user_bar->id . ': bar', $user_foo->id . ': foo', $root->id . ': root' ], + 'order by +name' +); + +search_users_ok( + { query => '', orderby => '-name' }, + [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar' ], + 'order by -name' +); + +search_users_ok( + { query => 'Disabled = 0', orderby => 'id' }, + [ $root->id . ': root', $user_foo->id . ': foo', $user_bar->id . ': bar', ], + 'enabled users' +); + +search_users_ok( + { query => 'Disabled = 1', orderby => 'id' }, + [ $user_baz->id . ': baz', ], + 'disabled users' +); + +ok( $m->login( 'foo', 'password', logout => 1 ), 'logged in as foo' ); +search_users_ok( + { query => 'id = ' . $user_foo->id }, + [ 'Permission denied' ], + "can't search without permission" +); + +sub search_users_ok { + local $Test::Builder::Level = $Test::Builder::Level + 1; + my $query = shift; + my $expected = shift; + my $name = shift || 'search users'; + + my $uri = URI->new("$baseurl/REST/1.0/search/user"); + $uri->query_form(%$query); + $m->get_ok($uri); + + my @lines = split /\n/, $m->content; + shift @lines; # header + shift @lines; # empty line + + is_deeply( \@lines, $expected, $name ); + +} + +undef $m; +done_testing(); diff --git a/rt/t/web/rest.t b/rt/t/web/rest.t index 3a84b2a01..8b8cbcb86 100644 --- a/rt/t/web/rest.t +++ b/rt/t/web/rest.t @@ -204,7 +204,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content) $text = $m->content; $text =~ s/.*?\n\n//; $text =~ s/\n\n/\n/; - $text =~ s{CF\.{severity}:.*\n}{}img; + $text =~ s{CF\.\{severity\}:.*\n}{}img; $text .= "CF.{severity}: explosive, a bit\n"; $m->post( "$baseurl/REST/1.0/ticket/edit", @@ -234,7 +234,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content) ] ); $text = $m->content; - $text =~ s{CF\.{severity}:.*\n}{}img; + $text =~ s{CF\.\{severity\}:.*\n}{}img; $text .= "CF.{severity}:\n"; $m->post( "$baseurl/REST/1.0/ticket/edit", @@ -301,7 +301,7 @@ is($link, 1, "Check ticket link.") or diag("'content' obtained:\n", $m->content) ] ); $text = $m->content; - $text =~ s{CF\.{single}:.*\n}{}img; + $text =~ s{CF\.\{single\}:.*\n}{}img; $text .= "CF.{single}: that\n"; $m->post( "$baseurl/REST/1.0/ticket/edit", diff --git a/rt/t/web/rest_user_cf.t b/rt/t/web/rest_user_cf.t new file mode 100644 index 000000000..d9f4ea3ba --- /dev/null +++ b/rt/t/web/rest_user_cf.t @@ -0,0 +1,26 @@ +use strict; +use warnings; +use RT::Interface::REST; + +use RT::Test tests => undef; + +my ( $baseurl, $m ) = RT::Test->started_ok; + +my $cf = RT::Test->load_or_create_custom_field( + Name => 'foo', + Type => 'Freeform', + LookupType => 'RT::User', +); +$cf->AddToObject(RT::User->new(RT->SystemUser)); + +my $root = RT::User->new( RT->SystemUser ); +$root->Load('root'); +$root->AddCustomFieldValue( Field => 'foo', Value => 'blabla' ); +is( $root->FirstCustomFieldValue('foo'), 'blabla', 'cf is set' ); + +ok( $m->login, 'logged in' ); +$m->post( "$baseurl/REST/1.0/show", [ id => 'user/12', ] ); +like( $m->content, qr/CF-foo: blabla/, 'found the cf' ); + +undef $m; +done_testing; diff --git a/rt/t/web/richtext-autohandler.t b/rt/t/web/richtext-autohandler.t deleted file mode 100644 index 724a7b34c..000000000 --- a/rt/t/web/richtext-autohandler.t +++ /dev/null @@ -1,14 +0,0 @@ -use strict; -use warnings; - -use RT::Test tests => 9; -my ($baseurl, $agent) = RT::Test->started_ok; - -$agent->get("$baseurl/NoAuth/RichText/ckeditor/config.js"); -is($agent->status, 403); -$agent->content_lacks("config.disableNativeSpellChecker"); - -$agent->get_ok("/NoAuth/RichText/config.js"); -$agent->content_contains("config.disableNativeSpellChecker"); - -$agent->warning_like(qr/Invalid request directly to the rich text editor/,); diff --git a/rt/t/web/rights.t b/rt/t/web/rights.t index 23b357f79..c7e8aac00 100644 --- a/rt/t/web/rights.t +++ b/rt/t/web/rights.t @@ -6,7 +6,7 @@ use RT::Test tests => 14; my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, "logged in"; -$m->follow_link_ok({ id => 'tools-config-global-group-rights'}); +$m->follow_link_ok({ id => 'admin-global-group-rights'}); sub get_rights { diff --git a/rt/t/web/rights1.t b/rt/t/web/rights1.t index 63ddb38c4..2cc7689c6 100644 --- a/rt/t/web/rights1.t +++ b/rt/t/web/rights1.t @@ -29,9 +29,9 @@ $agent->login( $user_obj->Name, 'customer'); # Test for absence of Configure and Preferences tabs. ok(!$agent->find_link( url => "$RT::WebPath/Admin/", - text => 'Configuration'), "No config tab" ); + text => 'Admin'), "No admin tab" ); ok(!$agent->find_link( url => "$RT::WebPath/User/Prefs.html", - text => 'Preferences'), "No prefs pane" ); + text => 'Preferences'), "No prefs pane" ); # Now test for their presence, one at a time. Sleep for a bit after # ACL changes, thanks to the 10s ACL cache. @@ -43,20 +43,20 @@ $agent->reload; $agent->content_contains('Logout', "Reloaded page successfully"); ok($agent->find_link( url => "$RT::WebPath/Admin/", - text => 'Configuration'), "Found config tab" ); + text => 'Admin'), "Found admin tab" ); my ($revokeid,$revokemsg) =$user_obj->PrincipalObj->RevokeRight(Right => 'ShowConfigTab'); ok ($revokeid,$revokemsg); ($grantid,$grantmsg) =$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf'); ok ($grantid,$grantmsg); $agent->reload(); $agent->content_contains('Logout', "Reloaded page successfully"); -ok($agent->find_link( - id => 'preferences-settings' ), "Found prefs pane" ); +ok($agent->find_link( + id => 'preferences-settings' ), "Found prefs pane" ); ($revokeid,$revokemsg) = $user_obj->PrincipalObj->RevokeRight(Right => 'ModifySelf'); ok ($revokeid,$revokemsg); # Good. Now load the search page and test Load/Save Search. $agent->follow_link( url => "$RT::WebPath/Search/Build.html", - text => 'Tickets'); + text => 'Tickets'); is($agent->status, 200, "Fetched search builder page"); $agent->content_lacks("Load saved search", "No search loading box"); $agent->content_lacks("Saved searches", "No saved searches box"); @@ -79,23 +79,23 @@ $agent->content_like(qr/input\s+type=['"]submit['"][^>]+name=['"]SavedSearchSave # via SelectOwner. my $queue_obj = RT::Queue->new(RT->SystemUser); -($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$, - Description => 'queue for SelectOwner testing'); +($ret, $msg) = $queue_obj->Create(Name => 'CustomerQueue-'.$$, + Description => 'queue for SelectOwner testing'); ok($ret, "SelectOwner test queue creation. $msg"); my $group_obj = RT::Group->new(RT->SystemUser); ($ret, $msg) = $group_obj->CreateUserDefinedGroup(Name => 'CustomerGroup-'.$$, - Description => 'group for SelectOwner testing'); + Description => 'group for SelectOwner testing'); ok($ret, "SelectOwner test group creation. $msg"); # Add our customer to the customer group, and give it queue rights. ($ret, $msg) = $group_obj->AddMember($user_obj->PrincipalObj->Id()); ok($ret, "Added customer to its group. $msg"); ($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'OwnTicket', - Object => $queue_obj); - + Object => $queue_obj); + ok($grantid,$grantmsg); ($grantid,$grantmsg) =$group_obj->PrincipalObj->GrantRight(Right => 'SeeQueue', - Object => $queue_obj); + Object => $queue_obj); ok ($grantid,$grantmsg); # Now. When we look at the search page we should be able to see # ourself in the list of possible owners. diff --git a/rt/t/web/saved_search_chart.t b/rt/t/web/saved_search_chart.t index 70111b97c..3737b512e 100644 --- a/rt/t/web/saved_search_chart.t +++ b/rt/t/web/saved_search_chart.t @@ -58,7 +58,7 @@ $m->submit_form( form_name => 'SaveSearch', fields => { Query => 'id=2', - PrimaryGroupBy => 'Status', + GroupBy => 'Status', ChartStyle => 'pie', }, button => 'SavedSearchSave', @@ -67,13 +67,13 @@ $m->submit_form( $m->content_contains("Chart first chart updated", 'found updated message' ); $m->content_contains("id=2", 'Query is updated' ); $m->content_like( qr/value="Status"\s+selected="selected"/, - 'PrimaryGroupBy is updated' ); + 'GroupBy is updated' ); $m->content_like( qr/value="pie"\s+selected="selected"/, 'ChartType is updated' ); ok( $search->Load($id) ); is( $search->SubValue('Query'), 'id=2', 'Query is indeed updated' ); -is( $search->SubValue('PrimaryGroupBy'), - 'Status', 'PrimaryGroupBy is indeed updated' ); +is( $search->SubValue('GroupBy'), + 'Status', 'GroupBy is indeed updated' ); is( $search->SubValue('ChartStyle'), 'pie', 'ChartStyle is indeed updated' ); # finally, let's test delete diff --git a/rt/t/web/saved_search_permissions.t b/rt/t/web/saved_search_permissions.t index f61c931a0..e24ae6146 100644 --- a/rt/t/web/saved_search_permissions.t +++ b/rt/t/web/saved_search_permissions.t @@ -26,7 +26,7 @@ ok( $m->login( 'foo', 'foobar', logout => 1 ), 'logged in' ); $m->get_ok( $url . "/Search/Build.html?SavedSearchLoad=$id" ); my $message = qq{Can not load saved search "$id"}; -RT::Interface::Web::EscapeUTF8( \$message ); +RT::Interface::Web::EscapeHTML( \$message ); $m->content_contains( $message, 'user foo can not load saved search of root' ); $m->warning_like( qr/User #\d+ tried to load container user #\d+/, diff --git a/rt/t/web/scrips.t b/rt/t/web/scrips.t index 0ff46bf26..d669f4c4e 100644 --- a/rt/t/web/scrips.t +++ b/rt/t/web/scrips.t @@ -1,7 +1,9 @@ use strict; use warnings; -use RT::Test tests => 14; +use RT::Test tests => undef; + +RT->Config->Set( UseTransactionBatch => 1 ); # TODO: # Test the rest of the conditions. @@ -9,10 +11,16 @@ use RT::Test tests => 14; # Test templates? # Test cleanup scripts. +my $queue_g = RT::Test->load_or_create_queue( Name => 'General' ); +ok $queue_g && $queue_g->id, 'loaded or created queue'; + +my $queue_r = RT::Test->load_or_create_queue( Name => 'Regression' ); +ok $queue_r && $queue_r->id, 'loaded or created queue'; + my ($baseurl, $m) = RT::Test->started_ok; ok $m->login, "logged in"; -$m->follow_link_ok({id => 'tools-config-global-scrips-create'}); +$m->follow_link_ok({id => 'admin-global-scrips-create'}); sub prepare_code_with_value { my $value = shift; @@ -47,16 +55,17 @@ sub prepare_code_with_value { foreach my $data (@values_for_actions) { my ($condition, $prepare_code_value) = @$data; diag "Create Scrip (Cond #$condition)" if $ENV{TEST_VERBOSE}; - $m->follow_link_ok({id => 'tools-config-global-scrips-create'}); + $m->follow_link_ok({id => 'admin-global-scrips-create'}); my $prepare_code = prepare_code_with_value($prepare_code_value); - $m->form_name('ModifyScrip'); + $m->form_name('CreateScrip'); $m->set_fields( - 'Scrip-new-ScripCondition' => $condition, - 'Scrip-new-ScripAction' => 15, # User Defined - 'Scrip-new-Template' => 1, # Blank - 'Scrip-new-CustomPrepareCode' => $prepare_code, + 'ScripCondition' => $condition, + 'ScripAction' => 'User Defined', + 'Template' => 'Blank', + 'CustomPrepareCode' => $prepare_code, ); - $m->submit; + $m->click('Create'); + $m->content_like(qr{Scrip Created}); } my $ticket_obj = RT::Test->create_ticket( @@ -76,7 +85,7 @@ sub prepare_code_with_value { $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', + To => 'rt-test@example.com, rt-to@example.com', }, button => 'ForwardAndReturn' ); @@ -92,7 +101,7 @@ sub prepare_code_with_value { $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', + To => 'rt-test@example.com, rt-to@example.com', }, button => 'ForwardAndReturn' ); @@ -101,3 +110,191 @@ sub prepare_code_with_value { RT::Test->clean_caught_mails; } + +note "check basics in scrip's admin interface"; +{ + $m->follow_link_ok( { id => 'admin-global-scrips-create' } ); + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), '', 'empty value'; + is $m->value_name('ScripAction'), '-', 'empty value'; + is $m->value_name('ScripCondition'), '-', 'empty value'; + is $m->value_name('Template'), '-', 'empty value'; + $m->field('Description' => 'test'); + $m->click('Create'); + $m->content_contains("Action is mandatory argument"); + + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), 'test', 'value stays on the page'; + $m->select('ScripAction' => 'Notify Ccs'); + $m->click('Create'); + $m->content_contains("Template is mandatory argument"); + + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), 'test', 'value stays on the page'; + is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page'; + $m->select('Template' => 'Blank'); + $m->click('Create'); + $m->content_contains("Condition is mandatory argument"); + + ok $m->form_name('CreateScrip'); + is $m->value_name('Description'), 'test', 'value stays on the page'; + is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page'; + $m->select('ScripCondition' => 'On Close'); + $m->click('Create'); + $m->content_contains("Scrip Created"); + + ok $m->form_name('ModifyScrip'); + is $m->value_name('Description'), 'test', 'correct value'; + is $m->value_name('ScripCondition'), 'On Close', 'correct value'; + is $m->value_name('ScripAction'), 'Notify Ccs', 'correct value'; + is $m->value_name('Template'), 'Blank', 'correct value'; + $m->field('Description' => 'test test'); + $m->click('Update'); + # regression + $m->content_lacks("Template is mandatory argument"); + + ok $m->form_name('ModifyScrip'); + is $m->value_name('Description'), 'test test', 'correct value'; + $m->content_contains("Description changed from", "found action result message"); +} + +note "check application in admin interface"; +{ + $m->follow_link_ok({ id => 'admin-global-scrips-create' }); + $m->submit_form_ok({ + with_fields => { + Description => "testing application", + ScripCondition => "On Create", + ScripAction => "Open Tickets", + Template => "Blank", + }, + button => 'Create', + }, "created scrip"); + $m->content_contains("Scrip Created", "found result message"); + + my ($sid) = ($m->content =~ /Modify scrip #(\d+)/); + ok $sid, "found scrip id on the page"; + RT::Test->object_scrips_are($sid, [0]); + + $m->follow_link_ok({ id => 'page-applies-to' }); + ok $m->form_name("AddRemoveScrip"), "found form"; + $m->tick("RemoveScrip-$sid", 0); + $m->click_ok("Update", "update scrip application"); + RT::Test->object_scrips_are($sid, []); + + ok $m->form_name("AddRemoveScrip"), "found form"; + $m->tick("AddScrip-$sid", 0); + $m->tick("AddScrip-$sid", $queue_g->id); + $m->click_ok("Update", "update scrip application"); + RT::Test->object_scrips_are($sid, [0], [$queue_g->id, $queue_r->id]); +} + +note "check templates in scrip's admin interface"; +{ + my $template = RT::Template->new( RT->SystemUser ); + my ($status, $msg) = $template->Create( Queue => $queue_g->id, Name => 'foo' ); + ok $status, 'created a template'; + + my $templates = RT::Templates->new( RT->SystemUser ); + $templates->LimitToGlobal; + + my @default = ( + '', + map $_->Name, @{$templates->ItemsArrayRef} + ); + + $m->follow_link_ok( { id => 'admin-global-scrips-create' } ); + ok $m->form_name('CreateScrip'); + my @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0] + ->possible_values; + is_deeply([sort @templates], [sort @default]); + + $m->follow_link_ok( { id => 'admin-queues' } ); + $m->follow_link_ok( { text => 'General' } ); + $m->follow_link_ok( { id => 'page-scrips-create' } ); + + ok $m->form_name('CreateScrip'); + @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0] + ->possible_values; + is_deeply([sort @templates], [sort @default, 'foo']); + +note "make sure we can not apply scrip to queue without required template"; + $m->field('Description' => 'test template'); + $m->select('ScripCondition' => 'On Close'); + $m->select('ScripAction' => 'Notify Ccs'); + $m->select('Template' => 'foo'); + $m->click('Create'); + $m->content_contains("Scrip Created"); + + $m->follow_link_ok( { id => 'page-applies-to' } ); + my ($id) = ($m->content =~ /Modify associated objects for scrip #(\d+)/); + $m->form_name('AddRemoveScrip'); + $m->tick('AddScrip-'.$id, $queue_r->id); + $m->click('Update'); + $m->content_like(qr{No template foo in queue Regression or global}); + +note "unapply the scrip from any queue"; + $m->form_name('AddRemoveScrip'); + $m->tick('RemoveScrip-'.$id, $queue_g->id); + $m->click('Update'); + $m->content_like(qr{Object deleted}); + +note "you can pick any template"; + $m->follow_link_ok( { id => 'page-basics' } ); + ok $m->form_name('ModifyScrip'); + @templates = ($m->find_all_inputs( type => 'option', name => 'Template' ))[0] + ->possible_values; + is_deeply( + [sort @templates], + [sort do { + my $t = RT::Templates->new( RT->SystemUser ); + $t->UnLimit; + ('', $t->DistinctFieldValues('Name')) + }], + ); + +note "go to apply page and apply with template change"; + $m->follow_link_ok( { id => 'page-applies-to' } ); + $m->form_name('AddRemoveScrip'); + $m->field('Template' => 'blank'); + $m->tick('AddScrip-'.$id, $queue_g->id); + $m->tick('AddScrip-'.$id, $queue_r->id); + $m->click('Update'); + $m->content_contains("Template: Template changed from "); + $m->content_contains("Object created"); +} + +note "apply scrip in different stage to different queues"; +{ + $m->follow_link_ok( { id => 'admin-queues' } ); + $m->follow_link_ok( { text => 'General' } ); + $m->follow_link_ok( { id => 'page-scrips-create'}); + + ok $m->form_name('CreateScrip'); + $m->field('Description' => 'test stage'); + $m->select('ScripCondition' => 'On Close'); + $m->select('ScripAction' => 'Notify Ccs'); + $m->select('Template' => 'Blank'); + $m->click('Create'); + $m->content_contains("Scrip Created"); + + my ($sid) = ($m->content =~ /Modify scrip #(\d+)/); + ok $sid, "found scrip id on the page"; + + $m->follow_link_ok({ text => 'Applies to' }); + ok $m->form_name('AddRemoveScrip'); + $m->select('Stage' => 'Batch'); + $m->tick( "AddScrip-$sid" => $queue_r->id ); + $m->click('Update'); + $m->content_contains("Object created"); + + $m->follow_link_ok({ text => 'General' }); + $m->follow_link_ok({ id => 'page-scrips' }); + + my (@matches) = $m->content =~ /test stage/g; + # regression + is scalar @matches, 1, 'scrip mentioned only once'; +} + +undef $m; +done_testing; diff --git a/rt/t/web/search_bulk_update_links.t b/rt/t/web/search_bulk_update_links.t index ffe2efe81..d9b477e03 100644 --- a/rt/t/web/search_bulk_update_links.t +++ b/rt/t/web/search_bulk_update_links.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 47; +use RT::Test tests => 46; my ( $url, $m ) = RT::Test->started_ok; ok( $m->login, 'logged in' ); @@ -79,7 +79,7 @@ $m->content_lacks( 'DeleteLink--', 'no delete link stuff' ); $m->form_name('BulkUpdate'); my @fields = qw/Owner AddRequestor DeleteRequestor AddCc DeleteCc AddAdminCc DeleteAdminCc Subject Priority Queue Status Starts_Date Told_Date Due_Date -Resolved_Date UpdateSubject UpdateContent/; +UpdateSubject UpdateContent/; for my $field ( @fields ) { is( $m->value($field), '', "default $field is empty" ); } diff --git a/rt/t/web/search_ical.t b/rt/t/web/search_ical.t new file mode 100644 index 000000000..094d8a2de --- /dev/null +++ b/rt/t/web/search_ical.t @@ -0,0 +1,196 @@ +use strict; +use warnings; + +use Data::ICal; +use RT::Test tests => 77; + +my $start_obj = RT::Date->new( RT->SystemUser ); +$start_obj->SetToNow; +my $start = $start_obj->iCal( Time => 1); + +my $due_obj = RT::Date->new( RT->SystemUser ); +$due_obj->SetToNow; +$due_obj->AddDays(2); +my $due = $due_obj->iCal( Time => 1); + +diag 'Test iCal with date only'; +{ + my ($baseurl, $agent) = RT::Test->started_ok; + + my $ticket = RT::Ticket->new(RT->SystemUser); + + for ( 1 .. 5 ) { + $ticket->Create( + Subject => 'Ticket ' . $_, + Queue => 'General', + Owner => 'root', + Requestor => 'ical@localhost', + Starts => $start_obj->ISO, + Due => $due_obj->ISO, + ); + } + + ok $agent->login('root', 'password'), 'logged in as root'; + + $agent->get_ok('/Search/Build.html'); + $agent->form_name('BuildQuery'); + $agent->field('idOp', '>'); + $agent->field('ValueOfid', '0'); + $agent->submit('DoSearch'); + $agent->follow_link_ok({id => 'page-results'}); + + for ( 1 .. 5 ) { + $agent->content_contains('Ticket ' . $_); + } + + $agent->follow_link_ok( { text => 'iCal' } ); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + my $ical = Data::ICal->new(data => $agent->content); + + my @entries = $ical->entries; + my $ical_count = @{$entries[0]}; + is( $ical_count, 10, "Got $ical_count ical entries"); + + my $prop_ref = $entries[0]->[0]->properties; + my $start_as_root = RT::Date->new( RT::CurrentUser->new( 'root' ) ); + $start_as_root->Unix( $start_obj->Unix ); + my $start = $start_as_root->ISO( Time => 0, Timezone => 'user' ); + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE\:/, 'Got DATE value'); + + $prop_ref = $entries[0]->[1]->properties; + my $due_as_root = RT::Date->new( RT::CurrentUser->new( 'root' ) ); + $due_as_root->Unix( $due_obj->Unix ); + my $due = $due_as_root->ISO( Time => 0, Timezone => 'user' ); + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE\:/, 'Got DATE value'); +} + +RT::Test->stop_server; + +diag 'Test iCal with date and time with config option'; +{ + RT->Config->Set(TimeInICal =>1); + my ($baseurl, $agent) = RT::Test->started_ok; + + ok $agent->login('root', 'password'), 'logged in as root'; + + $agent->get_ok('/Search/Build.html'); + $agent->form_name('BuildQuery'); + $agent->field('idOp', '>'); + $agent->field('ValueOfid', '0'); + $agent->submit('DoSearch'); + $agent->follow_link_ok({id => 'page-results'}); + + for ( 1 .. 5 ) { + $agent->content_contains('Ticket ' . $_); + } + + my $link = $agent->find_link( text => 'iCal' ); # use $link later + $agent->get_ok($link->url); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + my $ical = Data::ICal->new(data => $agent->content); + + my @entries = $ical->entries; + my $ical_count = @{$entries[0]}; + is( $ical_count, 10, "Got $ical_count ical entries"); + + my $prop_ref = $entries[0]->[0]->properties; + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + $prop_ref = $entries[0]->[1]->properties; + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); +} + +RT::Test->stop_server; + +diag 'Test iCal with date and time using query param'; +{ + RT->Config->Set(TimeInICal =>0); + my ($baseurl, $agent) = RT::Test->started_ok; + + ok $agent->login('root', 'password'), 'logged in as root'; + + $agent->get_ok('/Search/Build.html'); + $agent->form_name('BuildQuery'); + $agent->field('idOp', '>'); + $agent->field('ValueOfid', '0'); + $agent->submit('DoSearch'); + $agent->follow_link_ok({id => 'page-results'}); + + for ( 1 .. 5 ) { + $agent->content_contains('Ticket ' . $_); + } + + my $link = $agent->find_link( text => 'iCal' ); + $agent->get_ok($link->url . '?Time=1'); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + my $ical = Data::ICal->new(data => $agent->content); + + my @entries = $ical->entries; + my $ical_count = @{$entries[0]}; + is( $ical_count, 10, "Got $ical_count ical entries"); + + my $prop_ref = $entries[0]->[0]->properties; + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + $prop_ref = $entries[0]->[1]->properties; + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + diag 'Test iCal with date and time in single events'; + + my $url = $link->url . '?SingleEvent=1&Time=1'; + $agent->get_ok($url); + + is( $agent->content_type, 'text/calendar', 'content type is text/calendar' ); + + for ( 1 .. 5 ) { + $agent->content_like(qr/URL\:$baseurl\/Ticket\/Display\.html\?id=$_/); + } + + $ical = Data::ICal->new(data => $agent->content); + + @entries = $ical->entries; + $ical_count = @{$entries[0]}; + + # Only 5 entries in single event mode + is( $ical_count, 5, "Got $ical_count ical entries"); + + $prop_ref = $entries[0]->[0]->properties; + $start =~ s/-//g; + is($prop_ref->{'dtstart'}->[0]->value, $start, "Got start date with time: $start"); + like( $prop_ref->{'dtstart'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); + + $prop_ref = $entries[0]->[1]->properties; + $due =~ s/-//g; + is($prop_ref->{'dtend'}->[0]->value, $due, "Got due date with time: $due"); + like( $prop_ref->{'dtend'}->[0]->as_string, qr/VALUE=DATE-TIME\:/, 'Got DATE-TIME value'); +} diff --git a/rt/t/web/search_rss.t b/rt/t/web/search_rss.t index 9a53a8d94..7f1fdc1c3 100644 --- a/rt/t/web/search_rss.t +++ b/rt/t/web/search_rss.t @@ -39,14 +39,11 @@ my $rss_content = $agent->content; $agent->get_ok($rdf_path); is($agent->content, $rss_content, 'old Results.rdf still works'); -SKIP: { - eval { require XML::Simple; }; - skip 'no XML::Simple found', 6 if $@; - my $rss = XML::Simple::XMLin( $rss_content ); - is( scalar @{ $rss->{item} }, 5, 'item number' ); - for ( 1 .. 5 ) { - is( $rss->{item}[$_-1]{title}, 'Ticket ' . $_, 'title' . $_ ); - } +use XML::Simple; +my $rss = XML::Simple::XMLin( $rss_content ); +is( scalar @{ $rss->{item} }, 5, 'item number' ); +for ( 1 .. 5 ) { + is( $rss->{item}[$_-1]{title}, 'Ticket ' . $_, 'title' . $_ ); } # not login at all diff --git a/rt/t/web/search_simple.t b/rt/t/web/search_simple.t index a1a3ce806..d7c47279f 100644 --- a/rt/t/web/search_simple.t +++ b/rt/t/web/search_simple.t @@ -44,14 +44,14 @@ my $t = RT::Ticket->new(RT->SystemUser); { my ($status, $msg) = $t->AddCustomFieldValue( Field => $cf1->id, - Value => 'Downtown'); + Value => 'Downtown'); ok( $status, "Added CF value - $msg" ); } { my ($status, $msg) = $t->AddCustomFieldValue( Field => $cf2->id, - Value => 'Proxy'); + Value => 'Proxy'); ok( $status, "Added CF value - $msg" ); } diff --git a/rt/t/web/self_service.t b/rt/t/web/self_service.t index adc90d776..7afc008c6 100644 --- a/rt/t/web/self_service.t +++ b/rt/t/web/self_service.t @@ -18,9 +18,8 @@ ok( $user_a && $user_a->id, 'loaded or created user' ); ok( ! $user_a->Privileged, 'user is not privileged' ); # Load Cc group -my $Cc = RT::Group->new( RT->SystemUser ); -my($ok, $msg) = $Cc->LoadSystemRoleGroup( 'Cc' ); -ok($ok, $msg); +my $Cc = RT::System->RoleGroup( 'Cc' ); +ok($Cc->id); RT::Test->add_rights( { Principal => $Cc, Right => ['ShowTicket'] } ); my ($ticket) = RT::Test->create_ticket( diff --git a/rt/t/web/googleish_search.t b/rt/t/web/simple_search.t index a5f834eee..710efb1d1 100644 --- a/rt/t/web/googleish_search.t +++ b/rt/t/web/simple_search.t @@ -21,40 +21,41 @@ ok $two_words_queue && $two_words_queue->id, 'loaded or created a queue'; my $active = "( ".join( " OR ", map "Status = '$_'", RT::Queue->ActiveStatusArray())." )"; my $inactive = "( ".join( " OR ", map "Status = '$_'", RT::Queue->InactiveStatusArray())." )"; - require RT::Search::Googleish; - my $parser = RT::Search::Googleish->new( + require RT::Search::Simple; + my $parser = RT::Search::Simple->new( TicketsObj => $tickets, Argument => '', ); - is $parser->QueryToSQL("foo"), "$active AND ( Subject LIKE 'foo' )", "correct parsing"; + is $parser->QueryToSQL("foo"), "( Subject LIKE 'foo' ) AND $active", "correct parsing"; + is $parser->QueryToSQL("1 foo"), "( Subject LIKE 'foo' AND Subject LIKE '1' ) AND $active", "correct parsing"; is $parser->QueryToSQL("1"), "( Id = 1 )", "correct parsing"; is $parser->QueryToSQL("#1"), "( Id = 1 )", "correct parsing"; - is $parser->QueryToSQL("'1'"), "$active AND ( Subject LIKE '1' )", "correct parsing"; + is $parser->QueryToSQL("'1'"), "( Subject LIKE '1' ) AND $active", "correct parsing"; is $parser->QueryToSQL("foo bar"), - "$active AND ( Subject LIKE 'foo' AND Subject LIKE 'bar' )", + "( Subject LIKE 'foo' AND Subject LIKE 'bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL("'foo bar'"), - "$active AND ( Subject LIKE 'foo bar' )", + "( Subject LIKE 'foo bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL("'foo \\' bar'"), - "$active AND ( Subject LIKE 'foo \\' bar' )", + "( Subject LIKE 'foo \\' bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL('"foo \' bar"'), - "$active AND ( Subject LIKE 'foo \\' bar' )", + "( Subject LIKE 'foo \\' bar' ) AND $active", "correct parsing"; is $parser->QueryToSQL('"\f\o\o"'), - "$active AND ( Subject LIKE '\\\\f\\\\o\\\\o' )", + "( Subject LIKE '\\\\f\\\\o\\\\o' ) AND $active", "correct parsing"; is $parser->QueryToSQL("General"), "( Queue = 'General' ) AND $active", "correct parsing"; - is $parser->QueryToSQL("'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing"; + is $parser->QueryToSQL("'Two Words'"), "( Subject LIKE 'Two Words' ) AND $active", "correct parsing"; is $parser->QueryToSQL("queue:'Two Words'"), "( Queue = 'Two Words' ) AND $active", "correct parsing"; is $parser->QueryToSQL("subject:'Two Words'"), "$active AND ( Subject LIKE 'Two Words' )", "correct parsing"; is $parser->QueryToSQL("me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing"; - is $parser->QueryToSQL("'me'"), "$active AND ( Subject LIKE 'me' )", "correct parsing"; + is $parser->QueryToSQL("'me'"), "( Subject LIKE 'me' ) AND $active", "correct parsing"; is $parser->QueryToSQL("owner:me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing"; is $parser->QueryToSQL("owner:'me'"), "( Owner = 'me' ) AND $active", "correct parsing"; is $parser->QueryToSQL('owner:root@localhost'), "( Owner.EmailAddress = 'root\@localhost' ) AND $active", "Email address as owner"; @@ -164,7 +165,9 @@ for my $quote ( q{'}, q{"} ) { Subject => qq!base${quote}ticket $$!, Queue => 'general', Owner => $user->Name, - Requestor => qq!custom${quote}search\@localhost!, + ( $quote eq q{'} + ? (Requestor => qq!custom${quote}search\@localhost!) + : () ), Content => qq!this is base${quote}ticket with quote inside!, ); ok( $ticket_quote->id, 'created ticket with quote for custom search' ); @@ -188,7 +191,7 @@ for my $quote ( q{'}, q{"} ) { $m->field( q => $q ); $m->submit; my $escape_quote = $quote; - RT::Interface::Web::EscapeUTF8(\$escape_quote); + RT::Interface::Web::EscapeHTML(\$escape_quote); $m->content_contains( "base${escape_quote}ticket", "base${quote}ticket is found" ); } diff --git a/rt/t/web/smime/outgoing.t b/rt/t/web/smime/outgoing.t new file mode 100644 index 000000000..21d2328f2 --- /dev/null +++ b/rt/t/web/smime/outgoing.t @@ -0,0 +1,384 @@ +use strict; +use warnings; + +use RT::Test::SMIME tests => undef; +my $test = 'RT::Test::SMIME'; + +use RT::Action::SendEmail; +use File::Temp qw(tempdir); + +use_ok('RT::Crypt::SMIME'); + +RT::Test::SMIME->import_key('sender@example.com'); + +my $user_email = 'root@example.com'; +{ + my $user = RT::Test->load_or_create_user( + Name => $user_email, EmailAddress => $user_email + ); + ok $user && $user->id, 'loaded or created user'; + RT::Test::SMIME->import_key($user_email, $user); +} + +my $queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => 'sender@example.com', + CommentAddress => 'sender@example.com', +); +ok $queue && $queue->id, 'loaded or created queue'; + +RT::Test->set_rights( + Principal => 'Everyone', + Right => ['CreateTicket', 'ShowTicket', 'SeeQueue', 'ReplyToTicket', 'ModifyTicket'], +); + +my ($baseurl, $m) = RT::Test->started_ok; +ok $m->login, 'logged in'; + +my @variants = ( + {}, + { Sign => 1 }, + { Encrypt => 1 }, + { Sign => 1, Encrypt => 1 }, +); + +# collect emails +my %mail = ( + plain => [], + signed => [], + encrypted => [], + signed_encrypted => [], +); + +diag "check in read-only mode that queue's props influence create/update ticket pages" if $ENV{TEST_VERBOSE}; +{ + foreach my $variant ( @variants ) { + set_queue_crypt_options( %$variant ); + $m->goto_create_ticket( $queue ); + $m->form_name('TicketCreate'); + if ( $variant->{'Encrypt'} ) { + ok $m->value('Encrypt', 2), "encrypt tick box is checked"; + } else { + ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked"; + } + if ( $variant->{'Sign'} ) { + ok $m->value('Sign', 2), "sign tick box is checked"; + } else { + ok !$m->value('Sign', 2), "sign tick box is unchecked"; + } + } + + # to avoid encryption/signing during create + set_queue_crypt_options(); + + my $ticket = RT::Ticket->new( $RT::SystemUser ); + my ($id) = $ticket->Create( + Subject => 'test', + Queue => $queue->id, + Requestor => $user_email, + ); + ok $id, 'ticket created'; + + foreach my $variant ( @variants ) { + set_queue_crypt_options( %$variant ); + $m->goto_ticket( $id ); + $m->follow_link_ok({text => 'Reply'}, '-> reply'); + $m->form_number(3); + if ( $variant->{'Encrypt'} ) { + ok $m->value('Encrypt', 2), "encrypt tick box is checked"; + } else { + ok !$m->value('Encrypt', 2), "encrypt tick box is unchecked"; + } + if ( $variant->{'Sign'} ) { + ok $m->value('Sign', 2), "sign tick box is checked"; + } else { + ok !$m->value('Sign', 2), "sign tick box is unchecked"; + } + } +} + +# create a ticket for each combination +foreach my $queue_set ( @variants ) { + set_queue_crypt_options( %$queue_set ); + foreach my $ticket_set ( @variants ) { + create_a_ticket( %$ticket_set ); + } +} + +my $tid; +{ + my $ticket = RT::Ticket->new( $RT::SystemUser ); + ($tid) = $ticket->Create( + Subject => 'test', + Queue => $queue->id, + Requestor => $user_email, + ); + ok $tid, 'ticket created'; +} + +# again for each combination add a reply message +foreach my $queue_set ( @variants ) { + set_queue_crypt_options( %$queue_set ); + foreach my $ticket_set ( @variants ) { + update_ticket( $tid, %$ticket_set ); + } +} + + +# ------------------------------------------------------------------------------ +# now delete all keys from the keyring and put back secret/pub pair for rt-test@ +# and only public key for sender@ so we can verify signatures and decrypt +# like we are on another side recieving emails +# ------------------------------------------------------------------------------ + +my $keyring = $test->keyring_path; +unlink $_ foreach glob( $keyring ."/*" ); +RT::Test::SMIME->import_key('sender@example.com.crt'); +RT::Test::SMIME->import_key($user_email); + +$queue = RT::Test->load_or_create_queue( + Name => 'Regression', + CorrespondAddress => $user_email, + CommentAddress => $user_email, +); +ok $queue && $queue->id, 'changed props of the queue'; + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'plain'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($mail); + is ($status >> 8, 0, "The mail gateway exited normally"); + ok ($id, "got id of a newly created ticket - $id"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + ok !$msg->GetHeader('X-RT-Privacy'), "RT's outgoing mail has no crypto"; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted', + "RT's outgoing mail looks not encrypted"; + ok !$msg->GetHeader('X-RT-Incoming-Signature'), + "RT's outgoing mail looks not signed"; + + like $txn->Content, qr/Some content/, "RT's mail includes copy of ticket text"; +} + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($mail); + is ($status >> 8, 0, "The mail gateway exited normally"); + ok ($id, "got id of a newly created ticket - $id"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is $msg->GetHeader('X-RT-Privacy'), 'SMIME', + "RT's outgoing mail has crypto" or exit 0; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Not encrypted', + "RT's outgoing mail looks not encrypted"; + like $msg->GetHeader('X-RT-Incoming-Signature'), + qr/<sender\@example\.com>/, + "RT's outgoing mail looks signed"; + + like $attachments[0]->Content, qr/Some content/, + "RT's mail includes copy of ticket text"; +} + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'encrypted'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($mail); + is ($status >> 8, 0, "The mail gateway exited normally"); + ok ($id, "got id of a newly created ticket - $id"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is $msg->GetHeader('X-RT-Privacy'), 'SMIME', + "RT's outgoing mail has crypto"; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success', + "RT's outgoing mail looks encrypted"; + ok !$msg->GetHeader('X-RT-Incoming-Signature'), + "RT's outgoing mail looks not signed"; + + like $attachments[0]->Content, qr/Some content/, + "RT's mail includes copy of ticket text"; +} + +foreach my $mail ( map cleanup_headers($_), @{ $mail{'signed_encrypted'} } ) { + my ($status, $id) = RT::Test->send_via_mailgate($mail); + is ($status >> 8, 0, "The mail gateway exited normally"); + ok ($id, "got id of a newly created ticket - $id"); + + my $tick = RT::Ticket->new( $RT::SystemUser ); + $tick->Load( $id ); + ok ($tick->id, "loaded ticket #$id"); + + my $txn = $tick->Transactions->First; + my ($msg, @attachments) = @{$txn->Attachments->ItemsArrayRef}; + + is $msg->GetHeader('X-RT-Privacy'), 'SMIME', + "RT's outgoing mail has crypto"; + is $msg->GetHeader('X-RT-Incoming-Encryption'), 'Success', + "RT's outgoing mail looks encrypted"; + like $msg->GetHeader('X-RT-Incoming-Signature'), + qr/<sender\@example.com>/, + "RT's outgoing mail looks signed"; + + like $attachments[0]->Content, qr/Some content/, + "RT's mail includes copy of ticket text"; +} + +sub create_a_ticket { + my %args = (@_); + + RT::Test->clean_caught_mails; + + describe_options('creating a ticket: ', %args); + + $m->goto_create_ticket( $queue ); + $m->form_name('TicketCreate'); + $m->field( Subject => 'test' ); + $m->field( Requestors => $user_email ); + $m->field( Content => 'Some content' ); + + foreach ( qw(Sign Encrypt) ) { + if ( $args{ $_ } ) { + $m->tick( $_ => 1 ); + } else { + $m->untick( $_ => 1 ); + } + } + + $m->submit; + is $m->status, 200, "request successful"; + + unlike($m->content, qr/unable to sign outgoing email messages/); + + $m->get_ok('/'); # ensure that the mail has been processed + + my @mail = RT::Test->fetch_caught_mails; + check_text_emails( \%args, @mail ); +} + +sub update_ticket { + my $tid = shift; + my %args = (@_); + + RT::Test->clean_caught_mails; + + describe_options('updating ticket #'. $tid .': ', %args); + + ok $m->goto_ticket( $tid ), "UI -> ticket #$tid"; + $m->follow_link_ok( { text => 'Reply' }, 'ticket -> reply' ); + $m->form_number(3); + $m->field( UpdateContent => 'Some content' ); + + foreach ( qw(Sign Encrypt) ) { + if ( $args{ $_ } ) { + $m->tick( $_ => 1 ); + } else { + $m->untick( $_ => 1 ); + } + } + + $m->click('SubmitTicket'); + is $m->status, 200, "request successful"; + $m->content_like(qr/Correspondence added/, 'Correspondence added');# or diag $m->content; + + $m->get_ok('/'); # ensure that the mail has been processed + + my @mail = RT::Test->fetch_caught_mails; + check_text_emails( \%args, @mail ); +} + +undef $m; +done_testing; + +sub check_text_emails { + my %args = %{ shift @_ }; + my @mail = @_; + + describe_options('testing that we got at least one mail: ', %args); + + ok scalar @mail, "got some mail"; + for my $mail (@mail) { + if ( $args{'Encrypt'} ) { + unlike $mail, qr/Some content/, "outgoing email was encrypted"; + } else { + like $mail, qr/Some content/, "outgoing email was not encrypted"; + } + + if ( $args{'Encrypt'} ) { + like $mail, qr/application\/(?:x-)?pkcs7-mime/, 'outgoing email was processed'; + } elsif ( $args{'Sign'} ) { + like $mail, qr/(?:x-)?pkcs7-signature/, 'outgoing email was processed'; + } else { + unlike $mail, qr/smime/, 'outgoing email was not processed'; + } + } + if ( $args{'Sign'} && $args{'Encrypt'} ) { + push @{ $mail{'signed_encrypted'} }, @mail; + } elsif ( $args{'Sign'} ) { + push @{ $mail{'signed'} }, @mail; + } elsif ( $args{'Encrypt'} ) { + push @{ $mail{'encrypted'} }, @mail; + } else { + push @{ $mail{'plain'} }, @mail; + } +} + +sub cleanup_headers { + my $mail = shift; + # strip id from subject to create new ticket + $mail =~ s/^(Subject:)\s*\[.*?\s+#\d+\]\s*/$1 /m; + # strip several headers + foreach my $field ( qw(Message-ID RT-Originator RT-Ticket X-RT-Loop-Prevention) ) { + $mail =~ s/^$field:.*?\n(?! |\t)//gmsi; + } + return $mail; +} + +sub set_queue_crypt_options { + my %args = @_; + + describe_options('setting queue options: ', %args); + + $m->get_ok("/Admin/Queues/Modify.html?id=". $queue->id); + $m->form_with_fields('Sign', 'Encrypt'); + foreach my $opt ('Sign', 'Encrypt') { + if ( $args{$opt} ) { + $m->tick($opt => 1); + } else { + $m->untick($opt => 1); + } + } + $m->submit; +} + +sub describe_options { + return unless $ENV{'TEST_VERBOSE'}; + + my $msg = shift; + my %args = @_; + if ( $args{'Encrypt'} && $args{'Sign'} ) { + $msg .= 'encrypt and sign'; + } + elsif ( $args{'Sign'} ) { + $msg .= 'sign'; + } + elsif ( $args{'Encrypt'} ) { + $msg .= 'encrypt'; + } + else { + $msg .= 'no encrypt and no sign'; + } + diag $msg; +} + diff --git a/rt/t/web/squish.t b/rt/t/web/squish.t index ff43e74fb..9d1c01b1f 100644 --- a/rt/t/web/squish.t +++ b/rt/t/web/squish.t @@ -5,18 +5,18 @@ use RT::Test tests => 26; RT->Config->Set( DevelMode => 0 ); RT->Config->Set( WebDefaultStylesheet => 'aileron' ); -RT->Config->Set( MasonLocalComponentRoot => RT::Test::get_abs_relocatable_dir('html') ); +RT->Config->Set( LocalStaticPath => RT::Test::get_abs_relocatable_dir('static') ); my ( $url, $m ) = RT::Test->started_ok; $m->login; diag "test squished files with devel mode disabled"; -$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! }, +$m->follow_link_ok( { url_regex => qr!aileron/squished-([a-f0-9]{32})\.css! }, 'follow squished css' ); $m->content_like( qr!/\*\* End of .*?.css \*/!, 'squished css' ); -$m->content_lacks( 'text-decoration: underline !important;', - 'no print.css by default' ); +$m->content_lacks( 'counteract the titlebox', + 'no mobile.css by default' ); $m->back; my ($js_link) = @@ -29,16 +29,16 @@ RT::Test->stop_server; diag "test squished files with customized files and devel mode disabled"; RT->AddJavaScript( 'not-by-default.js' ); -RT->AddStyleSheets( 'print.css' ); +RT->AddStyleSheets( 'mobile.css' ); ( $url, $m ) = RT::Test->started_ok; $m->login; -$m->follow_link_ok( { url_regex => qr!aileron-squished-([a-f0-9]{32})\.css! }, +$m->follow_link_ok( { url_regex => qr!aileron/squished-([a-f0-9]{32})\.css! }, 'follow squished css' ); $m->content_like( qr!/\*\* End of .*?.css \*/!, 'squished css' ); -$m->content_contains( 'text-decoration: underline !important;', - 'has print.css' ); +$m->content_contains( 'counteract the titlebox', + 'has mobile.css' ); $m->back; ($js_link) = diff --git a/rt/t/web/html/NoAuth/js/not-by-default.js b/rt/t/web/static/js/not-by-default.js index 568f670ee..568f670ee 100644 --- a/rt/t/web/html/NoAuth/js/not-by-default.js +++ b/rt/t/web/static/js/not-by-default.js diff --git a/rt/t/web/template.t b/rt/t/web/template.t index 4a2e6c13a..1a02dc98d 100644 --- a/rt/t/web/template.t +++ b/rt/t/web/template.t @@ -17,7 +17,7 @@ ok( RT::Test->set_rights( ok $m->login('user_a', 'password'), 'logged in as user A'; # get to the templates screen -$m->follow_link( text => 'Configuration' ); +$m->follow_link( text => 'Admin' ); $m->title_is(q{RT Administration}, 'admin screen'); $m->follow_link( text => 'Global' ); diff --git a/rt/t/web/ticket-create-utf8.t b/rt/t/web/ticket-create-utf8.t index 107e41d71..ebb2d5eab 100644 --- a/rt/t/web/ticket-create-utf8.t +++ b/rt/t/web/ticket-create-utf8.t @@ -32,7 +32,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { $m->submit; $m->content_like( - qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, + qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); @@ -50,7 +50,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { $m->submit; $m->content_like( - qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, + qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); $m->content_contains( @@ -73,7 +73,7 @@ foreach my $test_str ( $ru_test, $l1_test ) { $m->submit; $m->content_like( - qr{<td\s+class="message-header-value"[^>]*>\s*\Q$test_str\E\s*</td>}i, + qr{<td\s+class="message-header-value\s*"[^>]*>\s*\Q$test_str\E\s*</td>}i, 'header on the page' ); $m->content_contains( diff --git a/rt/t/web/ticket_forward.t b/rt/t/web/ticket_forward.t index adf4d6f69..439242d48 100644 --- a/rt/t/web/ticket_forward.t +++ b/rt/t/web/ticket_forward.t @@ -36,19 +36,21 @@ diag "Forward Ticket" if $ENV{TEST_VERBOSE}; $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', - Cc => 'rt-cc@example.com', + To => '"Foo" <rt-foo@example.com>, rt-too@example.com', + Cc => 'rt-cc@example.com', + Bcc => 'root', }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); $m->content_contains( - 'Forwarded Ticket to rt-test, rt-to@example.com, rt-cc@example.com', + 'Forwarded Ticket to Foo <rt-foo@example.com>, <rt-too@example.com>, <rt-cc@example.com>, root (Enoch Root)', 'txn msg' ); my ($mail) = RT::Test->fetch_caught_mails; like( $mail, qr!Subject: test forward!, 'Subject field' ); - like( $mail, qr!To: rt-test, rt-to\@example.com!, 'To field' ); + like( $mail, qr!To: .*?rt-foo\@example.com!i, 'To field' ); + like( $mail, qr!To: .*?rt-too\@example.com!i, 'To field' ); like( $mail, qr!Cc: rt-cc\@example.com!i, 'Cc field' ); + like( $mail, qr!Bcc: root\@localhost!i, 'Bcc field' ); like( $mail, qr!This is a forward of ticket!, 'content' ); like( $mail, qr!this is an attachment!, 'att content' ); like( $mail, qr!$att_name!, 'att file name' ); @@ -60,22 +62,22 @@ diag "Forward Transaction" if $ENV{TEST_VERBOSE}; $m->submit_form( form_name => 'ForwardMessage', fields => { - To => 'rt-test, rt-to@example.com', + To => 'rt-to@example.com, rt-too@example.com', Cc => 'rt-cc@example.com', - Bcc => 'rt-bcc@example.com' + Bcc => 'root' }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); $m->content_like( -qr/Forwarded Transaction #\d+ to rt-test, rt-to\@example.com, rt-cc\@example.com, rt-bcc\@example.com/, +qr/Forwarded .*?Transaction #\d+.*? to <rt-to\@example\.com>, <rt-too\@example\.com>, <rt-cc\@example\.com>, root (Enoch Root)/, 'txn msg' ); my ($mail) = RT::Test->fetch_caught_mails; like( $mail, qr!Subject: test forward!, 'Subject field' ); - like( $mail, qr!To: rt-test, rt-to\@example.com!, 'To field' ); + like( $mail, qr!To: .*rt-to\@example.com!i, 'To field' ); + like( $mail, qr!To: .*rt-too\@example.com!i, 'To field' ); like( $mail, qr!Cc: rt-cc\@example.com!i, 'Cc field' ); - like( $mail, qr!Bcc: rt-bcc\@example.com!i, 'Bcc field' ); + like( $mail, qr!Bcc: root\@localhost!i, 'Bcc field' ); like( $mail, qr!This is a forward of transaction!, 'content' ); like( $mail, qr!$att_name!, 'att file name' ); like( $mail, qr!this is an attachment!, 'att content' ); @@ -93,9 +95,8 @@ diag "Forward Ticket without content" if $ENV{TEST_VERBOSE}; fields => { To => 'rt-test@example.com', }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); my ($mail) = RT::Test->fetch_caught_mails; - like( $mail, qr/Subject: Fwd: \[example\.com #\d\] test forward without content/, 'Subject field' ); + like( $mail, qr/Subject: \[example\.com #\d\] Fwd: test forward without content/, 'Subject field' ); like( $mail, qr/To: rt-test\@example\.com/, 'To field' ); like( $mail, qr/This is a forward of ticket #\d/, 'content' ); } @@ -107,7 +108,7 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO $m->form_name('TicketCreate'); my $attach = $m->current_form->find_input('Attach'); - $attach->filename("awesome.patch"); + $attach->filename('awesome.pátch'); $attach->headers('Content-Type' => 'text/x-diff'); $m->set_fields( Subject => 'test forward, empty content but attachments', @@ -122,8 +123,8 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO Attach => RT::Test::get_relocatable_file('bpslogo.png', '..', 'data'), # an image! ); $m->submit; - $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); - $m->content_like( qr/awesome\.patch/, 'uploaded patch file' ); + $m->content_like( qr/Ticket \d+ created/i, 'created the ticket' ); + $m->content_like( qr/awesome.p\%C3\%A1tch/, 'uploaded patch file' ); $m->content_like( qr/text\/x-diff/, 'uploaded patch file content type' ); $m->content_like( qr/bpslogo\.png/, 'uploaded image file' ); $m->content_like( qr/image\/png/, 'uploaded image file content type' ); @@ -137,13 +138,12 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); - $m->content_like( qr/Forwarded Transaction #\d+ to rt-test\@example\.com/, 'txn msg' ); + $m->content_like( qr/Forwarded .*?Transaction #\d+.*? to <rt-test\@example\.com>/, 'txn msg' ); my ($mail) = RT::Test->fetch_caught_mails; like( $mail, qr/Subject: test forward, empty content but attachments/, 'Subject field' ); like( $mail, qr/To: rt-test\@example.com/, 'To field' ); like( $mail, qr/This is a forward of transaction/, 'content' ); - like( $mail, qr/awesome\.patch/, 'att file name' ); + like( $mail, qr/filename\*\=\"UTF\-8\'\'awesome.p\%C3\%A1tch\"/, 'att file name' ); like( $mail, qr/this is an attachment/, 'att content' ); like( $mail, qr/text\/x-diff/, 'att content type' ); like( $mail, qr/bpslogo\.png/, 'att image file name' ); @@ -153,7 +153,7 @@ diag "Forward Transaction with attachments but empty content" if $ENV{TEST_VERBO diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_VERBOSE}; { my $mime = MIME::Entity->build( - From => 'test@example.com', + From => '"Tést" <test@example.com>', Subject => 'attachments for everyone', Type => 'multipart/mixed', ); @@ -195,9 +195,8 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); - $m->content_like( qr/Forwarded Transaction #\d+ to rt-test\@example\.com/, 'txn msg' ); - + $m->content_like( qr/Forwarded .*?Transaction #\d+.*? to <rt-test\@example\.com>/, 'txn msg' ); + # Forward ticket $m->follow_link_ok( { text => 'Forward', n => 1 }, 'follow 1st Forward' ); $m->submit_form( @@ -207,15 +206,15 @@ diag "Forward Transaction with attachments but no 'content' part" if $ENV{TEST_V }, button => 'ForwardAndReturn' ); - $m->content_contains( 'Sent email successfully', 'sent mail msg' ); - $m->content_like( qr/Forwarded Ticket to rt-test\@example\.com/, 'txn msg' ); + $m->content_like( qr/Forwarded Ticket to <rt-test\@example\.com>/, 'txn msg' ); my ($forward_txn, $forward_ticket) = RT::Test->fetch_caught_mails; - my $tag = qr/Fwd: \[example\.com #\d+\]/; + my $tag = qr/\[example\.com #\d+\] Fwd:/; like( $forward_txn, qr/Subject: $tag attachments for everyone/, 'Subject field is from txn' ); like( $forward_txn, qr/This is a forward of transaction/, 'forward description' ); like( $forward_ticket, qr/Subject: $tag test forward, attachments but no "content"/, 'Subject field is from ticket' ); like( $forward_ticket, qr/This is a forward of ticket/, 'forward description' ); + like( $forward_ticket, qr/From: \=\?UTF-8\?.* \<test\@example\.com\>/i ); for my $mail ($forward_txn, $forward_ticket) { like( $mail, qr/To: rt-test\@example.com/, 'To field' ); @@ -259,7 +258,26 @@ diag "Forward Ticket Template with a Subject: line" if $ENV{TEST_VERBOSE}; ); my ($mail) = RT::Test->fetch_caught_mails; - like($mail, qr/Subject: OVERRIDING SUBJECT/); + like($mail, qr/Subject: \[example.com #\d+\] OVERRIDING SUBJECT/); +} + +diag "Forward Transaction with non-ascii subject" if $ENV{TEST_VERBOSE}; +{ + $m->follow_link_ok( { text => 'Forward', n => 2 }, 'follow 2nd Forward' ); + my $subject = Encode::decode("UTF-8", 'test non-ascii äöü'); + $m->submit_form( + form_name => 'ForwardMessage', + fields => { + Subject => $subject, + To => 'rt-to@example.com', + }, + button => 'ForwardAndReturn' + ); + my ($mail) = RT::Test->fetch_caught_mails; + if ( $mail =~ /Subject: (.+)/ ) { + like( Encode::decode("UTF-8", RT::I18N::DecodeMIMEWordsToUTF8( $1, 'Subject' )), qr/$subject/, 'non-ascii subject' ); + } + $m->content_contains( $subject, 'non-ascii subject got displayed correctly' ); } undef $m; diff --git a/rt/t/web/ticket_links.t b/rt/t/web/ticket_links.t index efb615107..994630efd 100644 --- a/rt/t/web/ticket_links.t +++ b/rt/t/web/ticket_links.t @@ -52,7 +52,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { $m->submit; $m->content_like(qr/Ticket \d+ created/, 'created ticket'); - $m->content_contains("Can't link to a deleted ticket"); + $m->content_contains("Linking to a deleted ticket is not allowed"); $id = RT::Test->last_ticket->id; } @@ -75,7 +75,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { $m->field( "$type-$id", "$deleted_id $active_id $inactive_id" ); } $m->submit; - $m->content_contains("Can't link to a deleted ticket"); + $m->content_contains("Linking to a deleted ticket is not allowed"); if ( $c eq 'base' ) { $m->content_like( @@ -165,7 +165,7 @@ for my $type ( "DependsOn", "MemberOf", "RefersTo" ) { $m->content_lacks('hello test reminder subject'); if ($type eq 'RefersTo') { $m->text_contains("$baseurl/test_ticket_reference"); - $m->text_contains("Article " . $article->Id . ': test article'); + $m->text_contains("Article #" . $article->Id . ': test article'); } } } diff --git a/rt/t/web/ticket_modify_all.t b/rt/t/web/ticket_modify_all.t index 6d19b28e4..6b85d98cf 100644 --- a/rt/t/web/ticket_modify_all.t +++ b/rt/t/web/ticket_modify_all.t @@ -1,13 +1,15 @@ use strict; use warnings; -use RT::Test tests => 22; +use RT::Test tests => undef; my $ticket = RT::Test->create_ticket( Subject => 'test bulk update', Queue => 1, ); +RT->Config->Set(AutocompleteOwners => 1); + my ( $url, $m ) = RT::Test->started_ok; ok( $m->login, 'logged in' ); @@ -19,18 +21,12 @@ $m->submit_form( button => 'SubmitTicket', ); -$m->content_contains("Message recorded", 'updated ticket'); +$m->content_contains("Comments added", 'updated ticket'); $m->content_lacks("this is update content", 'textarea is clear'); $m->get_ok($url . '/Ticket/Display.html?id=' . $ticket->id ); $m->content_contains("this is update content", 'updated content in display page'); -# NOTE http://issues.bestpractical.com/Ticket/Display.html?id=18284 -RT::Test->stop_server; -RT->Config->Set(AutocompleteOwners => 1); -($url, $m) = RT::Test->started_ok; -$m->login; - $m->get_ok($url . '/Ticket/ModifyAll.html?id=' . $ticket->id); $m->form_name('TicketModifyAll'); @@ -57,10 +53,18 @@ $m->field('Told_Date' => "2015-01-01 00:00:00"); $m->click('SubmitTicket'); $m->text_contains("Last Contact: (Thu Jan 01 00:00:00 2015)", 'told date successfully updated'); -$m->form_name('TicketModifyAll'); -$m->field('Due_Date' => "2016-01-01 00:00:00"); -$m->click('SubmitTicket'); -$m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated'); +for my $unset ("0", "-", " ") { + $m->form_name('TicketModifyAll'); + $m->field('Due_Date' => "2016-01-01 00:00:00"); + $m->click('SubmitTicket'); + $m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated'); + + $m->form_name('TicketModifyAll'); + $m->field('Due_Date' => $unset); + $m->click('SubmitTicket'); + $m->text_contains("Due: (Not set)", "due date successfully cleared with '$unset'"); + $m->warning_like(qr/Couldn't parse date '-'/) if $unset eq "-"; +} $m->get( $url . '/Ticket/ModifyAll.html?id=' . $ticket->id ); $m->form_name('TicketModifyAll'); @@ -76,8 +80,9 @@ $m->field(WatcherTypeEmail => 'Requestor'); $m->field(WatcherAddressEmail => 'root@localhost'); $m->click('SubmitTicket'); $m->text_contains( - "root is already a Requestor for this ticket", + "root is already a Requestor", 'no duplicate watchers', ); -# XXX TODO test other parts, i.e. links +undef $m; +done_testing; diff --git a/rt/t/web/ticket_modify_people.t b/rt/t/web/ticket_modify_people.t index 750be3f2c..cefbf915b 100644 --- a/rt/t/web/ticket_modify_people.t +++ b/rt/t/web/ticket_modify_people.t @@ -1,7 +1,7 @@ use strict; use warnings; -use RT::Test tests => 23; +use RT::Test tests => 25; my $root = RT::Test->load_or_create_user( Name => 'root' ); my $group_foo = RT::Group->new($RT::SystemUser); @@ -80,7 +80,7 @@ ok( $m->reload; ok( $m->find_link( - text => 'Enoch Root', + text => 'root (Enoch Root)', url_regex => qr!/Admin/Users/Modify\.html!, ), 'got link to modify user' @@ -108,6 +108,16 @@ ok( 'got link to modify group' ); +$m->submit_form_ok({ + with_fields => { + WatcherTypeEmail1 => 'Cc', + WatcherAddressEmail1 => '"Foo Bar" <foo@example.com>', + }, + button => 'SubmitTicket', +}, "Added email with phrase as watcher"); + +my $foo = RT::Test->load_or_create_user( EmailAddress => 'foo@example.com' ); +is $foo->RealName, "Foo Bar", "RealName matches"; # TODO test Add|Delete people diff --git a/rt/t/web/ticket_owner.t b/rt/t/web/ticket_owner.t index 81508534a..782e68f8d 100644 --- a/rt/t/web/ticket_owner.t +++ b/rt/t/web/ticket_owner.t @@ -2,7 +2,7 @@ use strict; use warnings; -use RT::Test nodata => 1, tests => 105; +use RT::Test nodata => 1, tests => undef; my $queue = RT::Test->load_or_create_queue( Name => 'Regression' ); ok $queue && $queue->id, 'loaded or created queue'; @@ -10,12 +10,18 @@ ok $queue && $queue->id, 'loaded or created queue'; my $user_a = RT::Test->load_or_create_user( Name => 'user_a', Password => 'password', ); -ok $user_a && $user_a->id, 'loaded or created user'; +ok $user_a && $user_a->id, 'loaded or created user: ' . $user_a->Name; my $user_b = RT::Test->load_or_create_user( Name => 'user_b', Password => 'password', ); -ok $user_b && $user_b->id, 'loaded or created user'; +ok $user_b && $user_b->id, 'loaded or created user: ' . $user_b->Name; + +# To give ReassignTicket +my $user_c = RT::Test->load_or_create_user( + Name => 'user_c', Password => 'password', +); +ok $user_c && $user_c->id, 'loaded or created user: ' . $user_c->Name; my ($baseurl, $agent_a) = RT::Test->started_ok; @@ -360,6 +366,7 @@ ok( ] }, { Principal => $user_b, Right => [qw(SeeQueue ShowTicket OwnTicket)] }, + { Principal => $user_c, Right => [qw(SeeQueue ShowTicket ReassignTicket)] }, ), 'set rights' ); @@ -383,10 +390,12 @@ diag fields => { Owner => $user_a->id }, button => 'SubmitTicket', ); - $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' ); + like($agent_a->dom->at('.transaction.people .description')->all_text, + qr/user_a\s*-\s*Taken/, 'got user_a Taken message' ); $agent_b->goto_ticket($id); - $agent_b->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message for user b ' ); + like($agent_b->dom->at('.transaction.people .description')->all_text, + qr/user_a\s*-\s*Taken/, 'got user_a Taken message for user b' ); } diag @@ -410,9 +419,106 @@ diag $agent_a->content_contains( 'Owner changed from Nobody to user_a', 'got set message in Basics' ); $agent_a->goto_ticket($id); - $agent_a->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message' ); + like($agent_a->dom->at('.transaction.people .description')->all_text, + qr/user_a\s*-\s*Taken/, 'got user_a Taken message' ); $agent_b->goto_ticket($id); - $agent_b->content_like( qr/user_a\s+-\s+Taken/, 'got user_a Taken message for user b ' ); + like($agent_b->dom->at('.transaction.people .description')->all_text, + qr/user_a\s*-\s*Taken/, 'got user_a Taken message for user b' ); +} + +my $agent_c = RT::Test::Web->new; +ok $agent_c->login('user_c', 'password'), 'logged in as user C'; + +diag "user can assign ticket to new owner with ReassignTicket right"; +{ + my $ticket = RT::Ticket->new($user_a); + my ( $id, $txn, $msg ) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + ); + ok $id, 'created a ticket #' . $id or diag "error: $msg"; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; + + $agent_c->goto_ticket($id); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; + + $agent_a->goto_ticket($id); + $agent_a->content_lacks('Taken', 'no Taken'); + $agent_a->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' ); + $agent_a->submit_form( + form_name => 'TicketModify', + fields => { Owner => $user_a->id }, + ); + $agent_a->content_contains( 'Owner changed from Nobody to user_a', + 'got set message in Basics' ); + $agent_a->goto_ticket($id); + like($agent_a->dom->at('.transaction.people .description')->all_text, + qr{user_a\s*-\s*Taken}, 'got user_a Taken message' ); + + $agent_c->goto_ticket($id); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; + $agent_c->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' ); + my $form = $agent_c->form_name('TicketModify'); + is $form->value('Owner'), $user_a->id, 'correct owner selected'; + + ok grep($_ == $user_b->id, $form->find_input('Owner')->possible_values), + 'user B is listed as potential owner'; + $agent_c->select('Owner', $user_b->id); + $agent_c->submit; + $agent_c->content_contains( 'Owner changed from user_a to user_b', + 'got set message in Basics' ); + $agent_c->goto_ticket($id); + $agent_c->content_like( qr{Owner forcibly changed}, 'got owner forcibly changed message' ); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; +} + +ok( + RT::Test->add_rights( + { Principal => $user_c, Right => [qw(OwnTicket)] }, + ), + 'add rights' +); +diag "user can take/steal ticket with ReassignTicket+OwnTicket right"; +{ + my $ticket = RT::Ticket->new($user_a); + my ( $id, $txn, $msg ) = $ticket->Create( + Queue => $queue->id, + Subject => 'test', + ); + ok $id, 'created a ticket #' . $id or diag "error: $msg"; + is $ticket->Owner, RT->Nobody->id, 'correct owner'; + + $agent_c->goto_ticket($id); + ok( ($agent_c->find_all_links( text => 'Take' ))[0], 'has Take link' ); + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; + + $agent_a->goto_ticket($id); + $agent_a->content_lacks('Taken', 'no Taken'); + $agent_a->follow_link_ok( { text => 'Basics' }, 'Ticket -> Basics' ); + $agent_a->submit_form( + form_name => 'TicketModify', + fields => { Owner => $user_a->id }, + ); + $agent_a->content_contains( 'Owner changed from Nobody to user_a', + 'got set message in Basics' ); + $agent_a->goto_ticket($id); + like($agent_a->dom->at('.transaction.people .description')->all_text, + qr{user_a\s*-\s*Taken}, 'got user_a Taken message' ); + + $agent_c->goto_ticket($id); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok( ($agent_c->find_all_links( text => 'Steal' ))[0], 'has Steal link' ); + $agent_c->follow_link_ok( { text => 'Steal' }, 'Ticket -> Steal' ); + $agent_c->content_contains( 'Owner changed from user_a to user_c', 'steal message' ); + ok !($agent_c->find_all_links( text => 'Take' ))[0], 'no Take link'; + ok !($agent_c->find_all_links( text => 'Steal' ))[0], 'no Steal link'; } + +undef $agent_a; +undef $agent_b; +undef $agent_c; +done_testing; diff --git a/rt/t/web/ticket_preserve_basics.t b/rt/t/web/ticket_preserve_basics.t new file mode 100644 index 000000000..145941407 --- /dev/null +++ b/rt/t/web/ticket_preserve_basics.t @@ -0,0 +1,110 @@ +use strict; +use warnings; + +use RT::Test tests => undef; + +my $ticket = RT::Test->create_ticket( + Subject => 'test ticket basics', + Queue => 1, +); + +my ( $url, $m ) = RT::Test->started_ok; +ok( $m->login, 'logged in' ); + +my $root = RT::Test->load_or_create_user( Name => 'root' ); + +# Failing test where the time units are not preserved when you +# click 'Add more files' on Display +my @form_tries = ( + {Subject => "hello rt"}, + {Status => "open"}, + {Owner => $root->id}, + + ( + map +{ + "Time$_" => undef, + "Time$_-TimeUnits" => 'hours', + }, qw/Estimated Worked Left/ + ), + ( + map +{ + "Time$_" => '1', + "Time$_-TimeUnits" => 'hours', + }, qw/Estimated Worked Left/ + ), + + {InitialPriority => "10"}, + {FinalPriority => "10"}, +); + +for my $try (@form_tries) { + $m->goto_create_ticket(1); + $m->form_name('TicketCreate'); + $m->set_fields(%$try); + $m->click('AddMoreAttach'); + $m->form_name('TicketCreate'); + for my $field (keys %$try) { + is( + $m->value($field), + defined($try->{$field}) ? $try->{$field} : '', + "field $field is the same after the form was submitted" + ); + } +} + +# Test for time unit preservation in Jumbo +for my $try (@form_tries) { + my $jumbo_ticket = RT::Test->create_ticket( + Subject => 'test jumbo ticket basics', + Queue => 1, + ); + + local($try->{Priority}) = delete local($try->{InitialPriority}) + if exists $try->{InitialPriority}; + + $m->get( $url . "/Ticket/ModifyAll.html?id=" . $jumbo_ticket->id ); + $m->form_name('TicketModifyAll'); + $m->set_fields(%$try); + $m->click('AddMoreAttach'); + $m->form_name('TicketModifyAll'); + for my $field (keys %$try) { + is( + $m->value($field), + defined($try->{$field}) ? $try->{$field} : '', + "field $field is the same after the Jumbo form was submitted" + ); + } +} + +my $cf = RT::Test->load_or_create_custom_field( + Name => 'CF1', + Type => 'Freeform', + Pattern => '.', # mandatory + Queue => 'General', +); + +# More time unit testing by a failing CF validation +$m->get_ok($url.'/Admin/CustomFields/Objects.html?id='.$cf->id); +$m->form_with_fields('UpdateObjs'); +$m->tick('AddCustomField-'.$cf->id => '0'); # Make CF global +$m->click('UpdateObjs'); +$m->text_contains('Object created', 'CF applied globally'); + +# Test for preservation when a ticket is submitted and CF validation fails +for my $try (@form_tries) { + $m->goto_create_ticket(1); + $m->form_name('TicketCreate'); + $m->set_fields(%$try); + $m->submit(); + $m->form_name('TicketCreate'); + for my $field (keys %$try) { + is( + $m->value($field), + defined($try->{$field}) ? $try->{$field} : '', + "field $field is the same after the form was submitted" + ); + } +} + +undef $m; +done_testing(); diff --git a/rt/t/web/ticket_txn_content.t b/rt/t/web/ticket_txn_content.t index c0cae976c..096d78e31 100644 --- a/rt/t/web/ticket_txn_content.t +++ b/rt/t/web/ticket_txn_content.t @@ -27,14 +27,14 @@ sub follow_parent_with_headers_link { my $m = shift; my $link = $m->find_link(@_)->url; $link =~ s{/(\d+)$}{"/" . ($1-1)}e; # get the parent attach - $m->get_ok($baseurl . $link); + $m->get_ok($link); } sub follow_with_headers_link { my $m = shift; my $link = $m->find_link(@_)->url; $link =~ s{/\d+/(\d+)/.+$}{/WithHeaders/$1}; # frob into a with headers url - $m->get_ok($baseurl . $link); + $m->get_ok($link); } for my $type ( 'text/plain', 'text/html' ) { diff --git a/rt/t/web/user_update.t b/rt/t/web/user_update.t index 54139d797..7be088b0a 100644 --- a/rt/t/web/user_update.t +++ b/rt/t/web/user_update.t @@ -8,7 +8,7 @@ ok( $m->login(), 'logged in' ); $m->follow_link_ok({text => 'About me'}); $m->submit_form_ok({ with_fields => { Lang => 'ja'} }, "Change to Japanese"); -$m->text_contains("Lang changed from (no value) to 'ja'"); +$m->text_contains(Encode::decode("UTF-8","Langは「(値なし)」から「'ja'」に変更されました")); $m->text_contains(Encode::decode("UTF-8","実名"), "Page content is japanese"); # we only changed one field, and it wasn't the default, so this feedback is @@ -19,9 +19,7 @@ $m->content_lacks("That is already the current value"); $m->submit_form_ok({ with_fields => { Lang => 'en_us'} }, "Change back to english"); -# This message shows up in Japanese -# $m->text_contains("Lang changed from 'ja' to 'en_us'"); -$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「'en_us'」に変更されました")); +$m->text_contains("Lang changed from 'ja' to 'en_us'"); $m->text_contains("Real Name", "Page content is english"); # Check for a lack of spurious updates @@ -30,11 +28,11 @@ $m->content_lacks("That is already the current value"); # Ensure that we can change the language back to the default. $m->submit_form_ok({ with_fields => { Lang => 'ja'} }, "Back briefly to Japanese"); -$m->text_contains("Lang changed from 'en_us' to 'ja'"); +$m->text_contains(Encode::decode("UTF-8","Langは「'en_us'」から「'ja'」に変更されました")); $m->text_contains(Encode::decode("UTF-8","実名"), "Page content is japanese"); $m->submit_form_ok({ with_fields => { Lang => ''} }, "And set to the default"); -$m->text_contains(Encode::decode("UTF-8","Langは「'ja'」から「(値なし)」に変更されました")); +$m->text_contains("Lang changed from 'ja' to (no value)"); $m->text_contains("Real Name", "Page content is english"); undef $m; diff --git a/rt/t/web/walk.t b/rt/t/web/walk.t index 97aa36e12..2f7272739 100644 --- a/rt/t/web/walk.t +++ b/rt/t/web/walk.t @@ -53,7 +53,7 @@ my @links = ( '/Admin/Groups/Modify.html?id=' . $group->id, '/Admin/Queues/Modify.html?id=' . $queue->id, '/Admin/CustomFields/Modify.html?id=' . $cf->id, - '/Admin/Global/Scrip.html?id=1', + '/Admin/Scrips/Modify.html?id=1', '/Admin/Global/Template.html?Template=1', '/Admin/Articles/Classes/Modify.html?id=' . $class->id, '/Search/Build.html?Query=id<10', |